feat(profile): set a display name and greet with it on the dashboard

Settings: new Profil card with display name and Steam ID. Registration: optional display name. The dashboard falls back to the email prefix when no display name is set.
This commit is contained in:
lobo committed 2026-09-24 10:45:56 +02:00
1 parent 6773040d59
commit 7b676f0a1e
7 files changed
+90 -13

No files matched your search

+22 -6
View File
@@ -20,6 +20,7 @@ import {usePreferences} from '../context/PreferencesContext.jsx';
const BACKEND_ERROR_KEY = {
STEAM_ID_ALREADY_IN_USE: 'REGISTRATION.ERROR_STEAM_ID_IN_USE',
DISPLAY_NAME_ALREADY_IN_USE: 'SETTINGS.ERROR_DISPLAY_NAME_IN_USE',
};
const SUPPORTED_LANGUAGES = ['en', 'de'];
@@ -49,7 +50,7 @@ export default function SettingsPage() {
formState: {errors, isSubmitting},
} = useForm({
resolver: zodResolver(settingsSchema),
defaultValues: {useDarkTheme: true, language: 'en', steamId: ''},
defaultValues: {useDarkTheme: true, language: 'en', steamId: '', displayName: ''},
});
useEffect(() => {
@@ -86,6 +87,7 @@ export default function SettingsPage() {
useDarkTheme: !!data.settings?.useDarkTheme,
language: data.settings?.language || 'en',
steamId: data.steamId ?? '',
displayName: data.displayName ?? '',
});
} catch (err) {
console.error('Settings load error:', err);
@@ -146,6 +148,7 @@ export default function SettingsPage() {
useDarkTheme: next,
language: i18n.language,
steamId: profile?.steamId ?? null,
displayName: profile?.displayName ?? null,
});
};
@@ -155,17 +158,19 @@ export default function SettingsPage() {
useDarkTheme: isDarkMode,
language: next,
steamId: profile?.steamId ?? null,
displayName: profile?.displayName ?? null,
});
};
const onSubmitSteamId = async (values) => {
const onSubmitProfile = async (values) => {
const result = await persistSettings({
useDarkTheme: isDarkMode,
language: i18n.language,
steamId: values.steamId,
displayName: values.displayName,
});
if (result.ok) {
setProfile((p) => (p ? {...p, steamId: values.steamId} : p));
setProfile((p) => (p ? {...p, steamId: values.steamId, displayName: values.displayName} : p));
}
};
@@ -240,10 +245,21 @@ export default function SettingsPage() {
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2"><Gamepad2 className="size-4 text-primary"/>{t('SETTINGS.STEAM')}</CardTitle>
<CardTitle className="flex items-center gap-2"><Gamepad2 className="size-4 text-primary"/>{t('SETTINGS.PROFILE')}</CardTitle>
</CardHeader>
<CardContent>
<form onSubmit={handleSubmit(onSubmitSteamId)} className="space-y-4" noValidate>
<form onSubmit={handleSubmit(onSubmitProfile)} className="space-y-4" noValidate>
<FormField id="displayName" label={t('SETTINGS.DISPLAY_NAME')} error={errors.displayName} t={t}>
<Input
id="displayName"
type="text"
maxLength={24}
placeholder={t('SETTINGS.DISPLAY_NAME_PLACEHOLDER')}
aria-invalid={!!errors.displayName}
{...register('displayName')}
/>
<p className="text-xs text-muted-foreground">{t('SETTINGS.DISPLAY_NAME_HINT')}</p>
</FormField>
<FormField id="steamId" label={t('SETTINGS.STEAM_ID')} error={errors.steamId} t={t}>
<Input
id="steamId"
@@ -256,7 +272,7 @@ export default function SettingsPage() {
</FormField>
<Button type="submit" disabled={isSubmitting}>
{isSubmitting ? <Loader2 className="animate-spin"/> : <Save/>}
{t('SETTINGS.SAVE_STEAM_ID')}
{t('SETTINGS.SAVE_PROFILE')}
</Button>
</form>
</CardContent>