Compare commits

...

2 commits

Author SHA1 Message Date
1710d5d91d remove email from authcodes 2025-06-17 09:44:30 +02:00
bc5c2075f4 some changes 2025-06-16 21:28:24 +02:00
10 changed files with 59 additions and 67 deletions

View file

@ -1,5 +1,5 @@
import { AuthService } from '../../../auth/authService.ts' import { AuthService } from '../../../auth/authService.ts'
import { useEffect, useState, useRef, MouseEvent } from 'react' import { useEffect, useState, useRef, MouseEvent, useCallback } from 'react'
import { SignupCode } from '../../../auth/signupCode.ts' import { SignupCode } from '../../../auth/signupCode.ts'
import { Temporal } from '@js-temporal/polyfill' import { Temporal } from '@js-temporal/polyfill'
import Button from '../../../../components/buttons/Button.tsx' import Button from '../../../../components/buttons/Button.tsx'
@ -12,25 +12,24 @@ export default function SignupCodesManagementPage({ authService }: SignupCodesMa
const [codes, setCodes] = useState<SignupCode[]>([]) const [codes, setCodes] = useState<SignupCode[]>([])
const [code, setCode] = useState('') const [code, setCode] = useState('')
const [name, setName] = useState('') const [name, setName] = useState('')
const [email, setEmail] = useState('')
const [isLoading, setIsLoading] = useState(false) const [isLoading, setIsLoading] = useState(false)
const [error, setError] = useState<string | null>(null) const [error, setError] = useState<string | null>(null)
const dialogRef = useRef<HTMLDialogElement>(null) const dialogRef = useRef<HTMLDialogElement>(null)
const [tooltipPosition, setTooltipPosition] = useState<{ x: number; y: number } | null>(null) const [tooltipPosition, setTooltipPosition] = useState<{ x: number; y: number } | null>(null)
const [activeCode, setActiveCode] = useState<string | null>(null) const [activeCode, setActiveCode] = useState<string | null>(null)
const fetchCodes = async () => { const fetchCodes = useCallback(async () => {
try { try {
setCodes(await authService.listSignupCodes()) setCodes(await authService.listSignupCodes())
} catch (err) { } catch (err) {
console.error('Failed to fetch signup codes:', err) console.error('Failed to fetch signup codes:', err)
} }
} }, [authService])
useEffect(() => { useEffect(() => {
const timeoutId = setTimeout(fetchCodes) const timeoutId = setTimeout(fetchCodes)
return () => clearTimeout(timeoutId) return () => clearTimeout(timeoutId)
}, [authService]) }, [authService, fetchCodes])
const handleCreateCode = async (e: React.FormEvent) => { const handleCreateCode = async (e: React.FormEvent) => {
e.preventDefault() e.preventDefault()
@ -38,12 +37,11 @@ export default function SignupCodesManagementPage({ authService }: SignupCodesMa
setError(null) setError(null)
try { try {
await authService.createSignupCode(code, email, name) await authService.createSignupCode(code, name)
setCode('') setCode('')
setName('') setName('')
setEmail('')
dialogRef.current?.close() dialogRef.current?.close()
fetchCodes() // Refresh the table fetchCodes()
} catch (err) { } catch (err) {
setError(err instanceof Error ? err.message : 'Failed to create signup code') setError(err instanceof Error ? err.message : 'Failed to create signup code')
} finally { } finally {
@ -116,7 +114,6 @@ export default function SignupCodesManagementPage({ authService }: SignupCodesMa
<thead> <thead>
<tr> <tr>
<th className="text-left">Code</th> <th className="text-left">Code</th>
<th className="text-left">Email</th>
<th className="text-left">Redeemed By</th> <th className="text-left">Redeemed By</th>
<th className="text-left">Expires On</th> <th className="text-left">Expires On</th>
</tr> </tr>
@ -134,7 +131,6 @@ export default function SignupCodesManagementPage({ authService }: SignupCodesMa
{code.code} {code.code}
</button> </button>
</td> </td>
<td>{code.email}</td>
<td>{code.redeemedBy || 'Not redeemed'}</td> <td>{code.redeemedBy || 'Not redeemed'}</td>
<td>{formatDate(code.expiresOn)}</td> <td>{formatDate(code.expiresOn)}</td>
</tr> </tr>
@ -191,19 +187,6 @@ export default function SignupCodesManagementPage({ authService }: SignupCodesMa
/> />
</div> </div>
<div className="mb-4">
<label htmlFor="email" className="block text-sm font-medium text-gray-700 mb-1">
Email
</label>
<input
type="email"
id="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
className="w-full px-3 py-2 border border-gray-300 rounded-md"
/>
</div>
<div className="flex justify-end space-x-2"> <div className="flex justify-end space-x-2">
<Button type="button" onClick={closeDialog} secondary> <Button type="button" onClick={closeDialog} secondary>
Cancel Cancel

View file

@ -19,7 +19,12 @@ export class AuthService {
dispatchMessage('auth:logged-in', null) dispatchMessage('auth:logged-in', null)
} }
async signup(username: string, password: string, signupCode: string, rememberMe: boolean = false) { async signup(
username: string,
password: string,
signupCode: string,
rememberMe: boolean = false,
) {
const res = await this.client.POST('/auth/register', { const res = await this.client.POST('/auth/register', {
body: { username, password, signupCode, email: null, rememberMe }, body: { username, password, signupCode, email: null, rememberMe },
credentials: 'include', credentials: 'include',
@ -39,9 +44,9 @@ export class AuthService {
dispatchMessage('auth:logged-out', null) dispatchMessage('auth:logged-out', null)
} }
async createSignupCode(code: string, email: string, name: string) { async createSignupCode(code: string, name: string) {
const res = await this.client.POST('/auth/signup-codes', { const res = await this.client.POST('/auth/signup-codes', {
body: { code, email, name }, body: { code, name },
credentials: 'include', credentials: 'include',
}) })

View file

@ -1,7 +1,7 @@
import { useUser } from '../../user/user.ts' import { useUser } from '../../user/user.ts'
import NavButton from '../../../components/buttons/NavButton.tsx' import NavButton from '../../../components/buttons/NavButton.tsx'
import { useLocation } from 'react-router-dom' import { useLocation } from 'react-router-dom'
import { useTranslations } from '../../i18n/useTranslations.ts' import { useTranslations } from '../../i18n/translations.ts'
export default function AuthNavButtons() { export default function AuthNavButtons() {
const { t } = useTranslations() const { t } = useTranslations()

View file

@ -8,7 +8,7 @@ import { useUser } from '../../user/user.ts'
import NavBar from '../../../components/NavBar.tsx' import NavBar from '../../../components/NavBar.tsx'
import NavButton from '../../../components/buttons/NavButton.tsx' import NavButton from '../../../components/buttons/NavButton.tsx'
import LinkButton from '../../../components/buttons/LinkButton.tsx' import LinkButton from '../../../components/buttons/LinkButton.tsx'
import { useTranslations } from '../../i18n/useTranslations.ts' import { useTranslations } from '../../i18n/translations.ts'
interface LoginPageProps { interface LoginPageProps {
authService: AuthService authService: AuthService

View file

@ -8,7 +8,7 @@ import { AuthService } from '../authService.ts'
import LinkButton from '../../../components/buttons/LinkButton.tsx' import LinkButton from '../../../components/buttons/LinkButton.tsx'
import NavBar from '../../../components/NavBar.tsx' import NavBar from '../../../components/NavBar.tsx'
import NavButton from '../../../components/buttons/NavButton.tsx' import NavButton from '../../../components/buttons/NavButton.tsx'
import { useTranslations } from '../../i18n/useTranslations.ts' import { useTranslations } from '../../i18n/translations.ts'
const SignupCodeKey = 'signupCode' const SignupCodeKey = 'signupCode'

View file

@ -1,24 +0,0 @@
export interface Translations {
'auth.login.cta': string
'auth.login.register_instead': string
'auth.password.label': string
'auth.register.cta': string
'auth.register.login_instead': string
'auth.remember_me.label': string
'auth.username.label': string
'misc.loading': string
'nav.admin': string
'nav.home': string
'nav.login': string
'nav.logout': string
'nav.register': string
'post.add_media.cta': string
'post.editor.placeholder': string
'post.public.label': string
'post.submit.cta': string
}
export type TranslationKey = keyof Translations

View file

@ -0,0 +1,41 @@
import en from './translations/en.json' assert { type: 'json' }
interface Translation {
'auth.login.cta': string
'auth.login.register_instead': string
'auth.password.label': string
'auth.register.cta': string
'auth.register.login_instead': string
'auth.remember_me.label': string
'auth.username.label': string
'misc.loading': string
'nav.admin': string
'nav.home': string
'nav.login': string
'nav.logout': string
'nav.register': string
'post.add_media.cta': string
'post.editor.placeholder': string
'post.public.label': string
'post.submit.cta': string
}
export type TranslationKey = keyof Translation
export interface UseTranslations {
t: <K extends TranslationKey>(key: K) => Translation[K]
}
export function useTranslations(): UseTranslations {
// TODO somehow handle other languages (reactively)
const texts = en as Translation
function getText<K extends TranslationKey>(key: K): Translation[K] {
return texts[key] ?? key
}
return { t: getText }
}

View file

@ -1,13 +0,0 @@
import { TranslationKey, Translations } from './translationKeys.ts'
import en from './en.json' assert { type: 'json' }
export function useTranslations() {
// TODO somehow handle other languages (reactively)
const texts = en as Translations
function getText<K extends TranslationKey>(key: K): Translations[K] {
return texts[key] ?? key
}
return { t: getText }
}

View file

@ -3,7 +3,7 @@ import FancyTextEditor, { TextInputKeyDownEvent } from './inputs/FancyTextEditor
import Button from './buttons/Button.tsx' import Button from './buttons/Button.tsx'
import { openFileDialog } from '../utils/openFileDialog.ts' import { openFileDialog } from '../utils/openFileDialog.ts'
import makePica from 'pica' import makePica from 'pica'
import { useTranslations } from '../app/i18n/useTranslations.ts' import { useTranslations } from '../app/i18n/translations.ts'
interface NewPostWidgetProps { interface NewPostWidgetProps {
onSubmit: ( onSubmit: (