Übersetzung Teil 1
This commit is contained in:
@@ -2,6 +2,7 @@ import React, { useEffect, useRef, useState } from 'react';
|
||||
import { Check, ChevronDown } from 'lucide-react';
|
||||
import { countryOptions, getCountryLabel, getCountryOption } from '../data/countries';
|
||||
import CountryFlag from './CountryFlag';
|
||||
import { useTranslation } from '../i18n/LanguageContext';
|
||||
|
||||
const emptyStatus = {
|
||||
message: '',
|
||||
@@ -17,6 +18,7 @@ export default function ProfileTab({
|
||||
onDelete,
|
||||
onSessionChange
|
||||
}) {
|
||||
const t = useTranslation();
|
||||
const [formValues, setFormValues] = useState(profile);
|
||||
const [status, setStatus] = useState(emptyStatus);
|
||||
const [saving, setSaving] = useState(false);
|
||||
@@ -87,14 +89,14 @@ export default function ProfileTab({
|
||||
|
||||
setStatus({
|
||||
message: result.emailUpdateRequested
|
||||
? 'Profil gespeichert. Die neue Mailadresse muss ggf. noch per E-Mail bestätigt werden.'
|
||||
: 'Profil gespeichert.',
|
||||
? t('profile.saved_email')
|
||||
: t('profile.saved'),
|
||||
error: ''
|
||||
});
|
||||
} else {
|
||||
setStatus({
|
||||
message: '',
|
||||
error: result?.error?.message || 'Profil konnte nicht gespeichert werden.'
|
||||
error: result?.error?.message || t('profile.error_save')
|
||||
});
|
||||
}
|
||||
|
||||
@@ -102,9 +104,7 @@ export default function ProfileTab({
|
||||
};
|
||||
|
||||
const handleDelete = async () => {
|
||||
const confirmed = window.confirm(
|
||||
'Profil wirklich löschen?\n\nDabei werden alle Tests dieses Benutzers sowie vorhandene Analysepläne dauerhaft entfernt.'
|
||||
);
|
||||
const confirmed = window.confirm(t('profile.delete_confirm'));
|
||||
|
||||
if (!confirmed) return;
|
||||
|
||||
@@ -115,13 +115,13 @@ export default function ProfileTab({
|
||||
|
||||
if (result?.ok) {
|
||||
setStatus({
|
||||
message: 'Profil und alle Tests wurden gelöscht. Das Konto bleibt bestehen und kann neu eingerichtet werden.',
|
||||
message: t('profile.deleted'),
|
||||
error: ''
|
||||
});
|
||||
} else {
|
||||
setStatus({
|
||||
message: '',
|
||||
error: result?.error?.message || 'Profil konnte nicht gelöscht werden.'
|
||||
error: result?.error?.message || t('profile.error_delete')
|
||||
});
|
||||
}
|
||||
|
||||
@@ -133,44 +133,44 @@ export default function ProfileTab({
|
||||
<div className="max-w-4xl mx-auto">
|
||||
<div className="bg-white dark:bg-gray-900 rounded-2xl shadow-lg border border-gray-200 dark:border-gray-800 overflow-hidden">
|
||||
<div className="px-6 py-5 border-b border-gray-200 dark:border-gray-800">
|
||||
<p className="text-xs uppercase tracking-[0.25em] text-sky-700 dark:text-sky-300">Benutzerprofil</p>
|
||||
<h1 className="text-3xl font-bold mt-2">Profil verwalten</h1>
|
||||
<p className="text-xs uppercase tracking-[0.25em] text-sky-700 dark:text-sky-300">{t('profile.title_label')}</p>
|
||||
<h1 className="text-3xl font-bold mt-2">{t('profile.title')}</h1>
|
||||
<p className="mt-2 text-sm text-gray-600 dark:text-gray-300">
|
||||
Name, Vorname, Mailadresse, Land und die Sichtbarkeit deiner Testfreigaben werden hier gepflegt.
|
||||
{t('profile.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{loading ? (
|
||||
<div className="px-6 py-10 text-sm text-gray-600 dark:text-gray-300">
|
||||
Profil wird geladen...
|
||||
{t('profile.loading')}
|
||||
</div>
|
||||
) : (
|
||||
<form onSubmit={handleSubmit} className="px-6 py-6 space-y-6">
|
||||
<div className="grid md:grid-cols-2 gap-5">
|
||||
<label className="block">
|
||||
<span className="text-sm font-semibold text-gray-700 dark:text-gray-200">Vorname</span>
|
||||
<span className="text-sm font-semibold text-gray-700 dark:text-gray-200">{t('profile.first_name')}</span>
|
||||
<input
|
||||
type="text"
|
||||
value={formValues.firstName || ''}
|
||||
onChange={updateField('firstName')}
|
||||
className="mt-1 w-full px-4 py-3 rounded-lg border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 focus:outline-none focus:ring-2 focus:ring-sky-500"
|
||||
placeholder="Max"
|
||||
placeholder={t('profile.first_name_placeholder')}
|
||||
/>
|
||||
</label>
|
||||
|
||||
<label className="block">
|
||||
<span className="text-sm font-semibold text-gray-700 dark:text-gray-200">Nachname</span>
|
||||
<span className="text-sm font-semibold text-gray-700 dark:text-gray-200">{t('profile.last_name')}</span>
|
||||
<input
|
||||
type="text"
|
||||
value={formValues.lastName || ''}
|
||||
onChange={updateField('lastName')}
|
||||
className="mt-1 w-full px-4 py-3 rounded-lg border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 focus:outline-none focus:ring-2 focus:ring-sky-500"
|
||||
placeholder="Mustermann"
|
||||
placeholder={t('profile.last_name_placeholder')}
|
||||
/>
|
||||
</label>
|
||||
|
||||
<label className="block md:col-span-2">
|
||||
<span className="text-sm font-semibold text-gray-700 dark:text-gray-200">Mailadresse</span>
|
||||
<span className="text-sm font-semibold text-gray-700 dark:text-gray-200">{t('profile.email')}</span>
|
||||
<input
|
||||
type="email"
|
||||
value={formValues.email || session?.user?.email || ''}
|
||||
@@ -180,12 +180,12 @@ export default function ProfileTab({
|
||||
placeholder="you@example.com"
|
||||
/>
|
||||
<span className="mt-2 block text-xs text-gray-500 dark:text-gray-400">
|
||||
Wenn du die Mailadresse änderst, wird dir eine Bestätigung an die neue Adresse gesendet.
|
||||
{t('profile.email_hint')}
|
||||
</span>
|
||||
</label>
|
||||
|
||||
<label className="block">
|
||||
<span className="text-sm font-semibold text-gray-700 dark:text-gray-200">Land</span>
|
||||
<span className="text-sm font-semibold text-gray-700 dark:text-gray-200">{t('profile.country')}</span>
|
||||
<div ref={countryDropdownRef} className="relative mt-1">
|
||||
<button
|
||||
type="button"
|
||||
@@ -195,11 +195,11 @@ export default function ProfileTab({
|
||||
<span className="inline-flex items-center gap-3">
|
||||
<CountryFlag
|
||||
countryCode={selectedCountry?.code || formValues.country}
|
||||
title={selectedCountry?.name || getCountryLabel(formValues.country) || 'Land auswählen'}
|
||||
title={selectedCountry?.name || getCountryLabel(formValues.country) || t('profile.country_none')}
|
||||
className="h-5 w-7 rounded-[3px] shadow-sm ring-1 ring-black/10"
|
||||
/>
|
||||
<span className="font-medium">
|
||||
{selectedCountry?.name || getCountryLabel(formValues.country) || 'Land auswählen'}
|
||||
{selectedCountry?.name || getCountryLabel(formValues.country) || t('profile.country_none')}
|
||||
</span>
|
||||
</span>
|
||||
<ChevronDown
|
||||
@@ -222,10 +222,10 @@ export default function ProfileTab({
|
||||
<span className="inline-flex items-center gap-3">
|
||||
<CountryFlag
|
||||
countryCode=""
|
||||
title="Kein Land ausgewählt"
|
||||
title={t('profile.country_none')}
|
||||
className="h-5 w-7 rounded-[3px] shadow-sm"
|
||||
/>
|
||||
<span>Kein Land ausgewählt</span>
|
||||
<span>{t('profile.country_none')}</span>
|
||||
</span>
|
||||
{!formValues.country && <Check className="h-4 w-4" aria-hidden="true" />}
|
||||
</button>
|
||||
@@ -278,35 +278,52 @@ export default function ProfileTab({
|
||||
)}
|
||||
</div>
|
||||
<span className="mt-2 block text-xs text-gray-500 dark:text-gray-400">
|
||||
Die ausgewählte Flagge wird später auch im Kopfbereich neben deiner Mailadresse angezeigt.
|
||||
{t('profile.country_hint')}
|
||||
</span>
|
||||
</label>
|
||||
|
||||
<div className="block">
|
||||
<span className="text-sm font-semibold text-gray-700 dark:text-gray-200">Ranglistenplatz</span>
|
||||
<div className="mt-1 rounded-lg border border-dashed border-amber-300 bg-amber-50 px-4 py-3 dark:border-amber-900/70 dark:bg-amber-950/30">
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<span className="text-sm font-medium text-amber-800 dark:text-amber-200">
|
||||
Platzhalter
|
||||
</span>
|
||||
<span className="rounded-full bg-white px-3 py-1 text-sm font-semibold text-amber-700 shadow-sm dark:bg-gray-900 dark:text-amber-300">
|
||||
# -
|
||||
</span>
|
||||
</div>
|
||||
<p className="mt-2 text-xs text-amber-700/80 dark:text-amber-200/80">
|
||||
Hier kann spater der Ranglistenplatz des Benutzers angezeigt werden.
|
||||
</p>
|
||||
<label className="block">
|
||||
<span className="text-sm font-semibold text-gray-700 dark:text-gray-200">{t('profile.display')}</span>
|
||||
<select
|
||||
value={formValues.language || 'de'}
|
||||
onChange={updateField('language')}
|
||||
className="mt-1 w-full px-4 py-3 rounded-lg border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 text-sm text-gray-800 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-sky-500"
|
||||
>
|
||||
<option value="de">Deutsch</option>
|
||||
<option value="en">Englisch</option>
|
||||
<option value="it">Italienisch</option>
|
||||
<option value="es">Spanisch</option>
|
||||
<option value="pt">Portugiesisch</option>
|
||||
</select>
|
||||
<span className="mt-2 block text-xs text-gray-500 dark:text-gray-400">
|
||||
{t('profile.display_hint')}
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div className="block">
|
||||
<span className="text-sm font-semibold text-gray-700 dark:text-gray-200">{t('profile.rank')}</span>
|
||||
<div className="mt-1 rounded-lg border border-dashed border-amber-300 bg-amber-50 px-4 py-3 dark:border-amber-900/70 dark:bg-amber-950/30">
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<span className="text-sm font-medium text-amber-800 dark:text-amber-200">
|
||||
{t('profile.rank_placeholder')}
|
||||
</span>
|
||||
<span className="rounded-full bg-white px-3 py-1 text-sm font-semibold text-amber-700 shadow-sm dark:bg-gray-900 dark:text-amber-300">
|
||||
# -
|
||||
</span>
|
||||
</div>
|
||||
<p className="mt-2 text-xs text-amber-700/80 dark:text-amber-200/80">
|
||||
{t('profile.rank_hint')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="rounded-xl border border-sky-200 dark:border-sky-900/70 bg-sky-50 dark:bg-sky-950/30 p-5">
|
||||
<div className="flex items-start justify-between gap-4 flex-wrap">
|
||||
<div>
|
||||
<h2 className="text-lg font-semibold text-gray-900 dark:text-gray-100">Tests sichtbar</h2>
|
||||
<h2 className="text-lg font-semibold text-gray-900 dark:text-gray-100">{t('profile.tests_visible_title')}</h2>
|
||||
<p className="mt-2 text-sm text-gray-600 dark:text-gray-300 max-w-2xl">
|
||||
Ist dieser Schalter aktiv, funktionieren deine Freigabelinks. Wenn du ihn deaktivierst,
|
||||
bleiben vorhandene Links gespeichert, sind aber nicht mehr öffentlich sichtbar.
|
||||
{t('profile.tests_visible_desc')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -318,7 +335,7 @@ export default function ProfileTab({
|
||||
className="h-4 w-4 rounded border-gray-300 text-sky-600 focus:ring-sky-500"
|
||||
/>
|
||||
<span className="text-sm font-semibold text-gray-800 dark:text-gray-100">
|
||||
{formValues.testsVisible ? 'Sichtbar' : 'Nicht sichtbar'}
|
||||
{formValues.testsVisible ? t('profile.visible') : t('profile.not_visible')}
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
@@ -338,7 +355,7 @@ export default function ProfileTab({
|
||||
|
||||
<div className="flex items-center justify-between gap-4 flex-wrap">
|
||||
<div className="text-sm text-gray-500 dark:text-gray-400">
|
||||
Konto-ID: <span className="font-mono">{session?.user?.id || '—'}</span>
|
||||
{t('profile.account_id')}: <span className="font-mono">{session?.user?.id || '—'}</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-3 flex-wrap">
|
||||
@@ -348,7 +365,7 @@ export default function ProfileTab({
|
||||
disabled={deleting || saving}
|
||||
className="px-5 py-3 rounded-lg border border-rose-300 bg-white text-rose-700 font-semibold hover:bg-rose-50 transition disabled:opacity-60 disabled:cursor-not-allowed dark:border-rose-800 dark:bg-gray-900 dark:text-rose-300 dark:hover:bg-rose-950/40"
|
||||
>
|
||||
{deleting ? 'Löscht...' : 'Profil löschen'}
|
||||
{deleting ? t('profile.deleting') : t('profile.delete')}
|
||||
</button>
|
||||
|
||||
<button
|
||||
@@ -356,7 +373,7 @@ export default function ProfileTab({
|
||||
disabled={saving || deleting}
|
||||
className="px-5 py-3 rounded-lg bg-sky-600 text-white font-semibold hover:bg-sky-700 transition disabled:opacity-60 disabled:cursor-not-allowed"
|
||||
>
|
||||
{saving ? 'Speichert...' : 'Profil speichern'}
|
||||
{saving ? t('profile.saving') : t('profile.save')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user