feat(i18n): Lokalisierung - Phase 3 abgeschlossen (LoadingModal, AuthModal, SetupModal)
Lokalisierte Komponenten (10/alle): - LoadingModal: Simple loading spinner mit 'Processing' und 'Please wait...' - AuthModal: Login-Dialog mit Benutzername/Passwort - SetupModal: Initial Setup Wizard mit Toggle für Auth-Aktivierung Neue Translation Keys: - loadingModal.processing, pleaseWait - authModal.title, description, username.*, password.*, error, actions.* - setupModal.title, description, username.*, password.*, confirmPassword.*, enableAuth.*, errors.*, actions.* Technische Details: - Konditionale Beschreibungen basierend auf enableAuth-Status - Fehler-Messages mit t() für i18n - Alle Labels, Placeholders und Button-Texte lokalisiert
This commit is contained in:
@@ -6,12 +6,14 @@ import { Input } from './ui/input';
|
||||
import { useAuth } from './AuthProvider';
|
||||
import { Lock, User, AlertCircle } from 'lucide-react';
|
||||
import { useRegisterModal } from './modal/ModalStackProvider';
|
||||
import { useTranslation } from '@/lib/i18n/useTranslation';
|
||||
|
||||
interface AuthModalProps {
|
||||
isOpen: boolean;
|
||||
}
|
||||
|
||||
export function AuthModal({ isOpen }: AuthModalProps) {
|
||||
const { t } = useTranslation('authModal');
|
||||
useRegisterModal(isOpen, { id: 'auth-modal', allowEscape: false, onClose: () => null });
|
||||
const { login } = useAuth();
|
||||
const [username, setUsername] = useState('');
|
||||
@@ -27,7 +29,7 @@ export function AuthModal({ isOpen }: AuthModalProps) {
|
||||
const success = await login(username, password);
|
||||
|
||||
if (!success) {
|
||||
setError('Invalid username or password');
|
||||
setError(t('error'));
|
||||
}
|
||||
|
||||
setIsLoading(false);
|
||||
@@ -42,27 +44,27 @@ export function AuthModal({ isOpen }: AuthModalProps) {
|
||||
<div className="flex items-center justify-center p-6 border-b border-border">
|
||||
<div className="flex items-center gap-3">
|
||||
<Lock className="h-8 w-8 text-primary" />
|
||||
<h2 className="text-2xl font-bold text-card-foreground">Authentication Required</h2>
|
||||
<h2 className="text-2xl font-bold text-card-foreground">{t('title')}</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="p-6">
|
||||
<p className="text-muted-foreground text-center mb-6">
|
||||
Please enter your credentials to access the application.
|
||||
{t('description')}
|
||||
</p>
|
||||
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
<div>
|
||||
<label htmlFor="username" className="block text-sm font-medium text-foreground mb-2">
|
||||
Username
|
||||
{t('username.label')}
|
||||
</label>
|
||||
<div className="relative">
|
||||
<User className="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-muted-foreground" />
|
||||
<Input
|
||||
id="username"
|
||||
type="text"
|
||||
placeholder="Enter your username"
|
||||
placeholder={t('username.placeholder')}
|
||||
value={username}
|
||||
onChange={(e) => setUsername(e.target.value)}
|
||||
disabled={isLoading}
|
||||
@@ -74,14 +76,14 @@ export function AuthModal({ isOpen }: AuthModalProps) {
|
||||
|
||||
<div>
|
||||
<label htmlFor="password" className="block text-sm font-medium text-foreground mb-2">
|
||||
Password
|
||||
{t('password.label')}
|
||||
</label>
|
||||
<div className="relative">
|
||||
<Lock className="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-muted-foreground" />
|
||||
<Input
|
||||
id="password"
|
||||
type="password"
|
||||
placeholder="Enter your password"
|
||||
placeholder={t('password.placeholder')}
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
disabled={isLoading}
|
||||
@@ -103,7 +105,7 @@ export function AuthModal({ isOpen }: AuthModalProps) {
|
||||
disabled={isLoading || !username.trim() || !password.trim()}
|
||||
className="w-full"
|
||||
>
|
||||
{isLoading ? 'Signing In...' : 'Sign In'}
|
||||
{isLoading ? t('actions.signingIn') : t('actions.signIn')}
|
||||
</Button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import { Loader2 } from 'lucide-react';
|
||||
import { useRegisterModal } from './modal/ModalStackProvider';
|
||||
import { useTranslation } from '@/lib/i18n/useTranslation';
|
||||
|
||||
interface LoadingModalProps {
|
||||
isOpen: boolean;
|
||||
@@ -9,6 +10,7 @@ interface LoadingModalProps {
|
||||
}
|
||||
|
||||
export function LoadingModal({ isOpen, action }: LoadingModalProps) {
|
||||
const { t } = useTranslation('loadingModal');
|
||||
useRegisterModal(isOpen, { id: 'loading-modal', allowEscape: false, onClose: () => null });
|
||||
if (!isOpen) return null;
|
||||
|
||||
@@ -22,13 +24,13 @@ export function LoadingModal({ isOpen, action }: LoadingModalProps) {
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<h3 className="text-lg font-semibold text-card-foreground mb-2">
|
||||
Processing
|
||||
{t('processing')}
|
||||
</h3>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{action}
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground mt-2">
|
||||
Please wait...
|
||||
{t('pleaseWait')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -6,6 +6,7 @@ import { Input } from './ui/input';
|
||||
import { Toggle } from './ui/toggle';
|
||||
import { Lock, User, Shield, AlertCircle } from 'lucide-react';
|
||||
import { useRegisterModal } from './modal/ModalStackProvider';
|
||||
import { useTranslation } from '@/lib/i18n/useTranslation';
|
||||
|
||||
interface SetupModalProps {
|
||||
isOpen: boolean;
|
||||
@@ -13,6 +14,7 @@ interface SetupModalProps {
|
||||
}
|
||||
|
||||
export function SetupModal({ isOpen, onComplete }: SetupModalProps) {
|
||||
const { t } = useTranslation('setupModal');
|
||||
useRegisterModal(isOpen, { id: 'setup-modal', allowEscape: true, onClose: () => null });
|
||||
const [username, setUsername] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
@@ -28,7 +30,7 @@ export function SetupModal({ isOpen, onComplete }: SetupModalProps) {
|
||||
|
||||
// Only validate passwords if authentication is enabled
|
||||
if (enableAuth && password !== confirmPassword) {
|
||||
setError('Passwords do not match');
|
||||
setError(t('errors.passwordMismatch'));
|
||||
setIsLoading(false);
|
||||
return;
|
||||
}
|
||||
@@ -71,11 +73,11 @@ export function SetupModal({ isOpen, onComplete }: SetupModalProps) {
|
||||
}
|
||||
} else {
|
||||
const errorData = await response.json() as { error: string };
|
||||
setError(errorData.error ?? 'Failed to setup authentication');
|
||||
setError(errorData.error ?? t('errors.setupFailed'));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Setup error:', error);
|
||||
setError('Failed to setup authentication');
|
||||
setError(t('errors.setupFailed'));
|
||||
}
|
||||
|
||||
setIsLoading(false);
|
||||
@@ -90,27 +92,27 @@ export function SetupModal({ isOpen, onComplete }: SetupModalProps) {
|
||||
<div className="flex items-center justify-center p-6 border-b border-border">
|
||||
<div className="flex items-center gap-3">
|
||||
<Shield className="h-8 w-8 text-success" />
|
||||
<h2 className="text-2xl font-bold text-card-foreground">Setup Authentication</h2>
|
||||
<h2 className="text-2xl font-bold text-card-foreground">{t('title')}</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="p-6">
|
||||
<p className="text-muted-foreground text-center mb-6">
|
||||
Set up authentication to secure your application. This will be required for future access.
|
||||
{t('description')}
|
||||
</p>
|
||||
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
<div>
|
||||
<label htmlFor="setup-username" className="block text-sm font-medium text-foreground mb-2">
|
||||
Username
|
||||
{t('username.label')}
|
||||
</label>
|
||||
<div className="relative">
|
||||
<User className="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-muted-foreground" />
|
||||
<Input
|
||||
id="setup-username"
|
||||
type="text"
|
||||
placeholder="Choose a username"
|
||||
placeholder={t('username.placeholder')}
|
||||
value={username}
|
||||
onChange={(e) => setUsername(e.target.value)}
|
||||
disabled={isLoading}
|
||||
@@ -123,14 +125,14 @@ export function SetupModal({ isOpen, onComplete }: SetupModalProps) {
|
||||
|
||||
<div>
|
||||
<label htmlFor="setup-password" className="block text-sm font-medium text-foreground mb-2">
|
||||
Password
|
||||
{t('password.label')}
|
||||
</label>
|
||||
<div className="relative">
|
||||
<Lock className="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-muted-foreground" />
|
||||
<Input
|
||||
id="setup-password"
|
||||
type="password"
|
||||
placeholder="Choose a password"
|
||||
placeholder={t('password.placeholder')}
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
disabled={isLoading}
|
||||
@@ -143,14 +145,14 @@ export function SetupModal({ isOpen, onComplete }: SetupModalProps) {
|
||||
|
||||
<div>
|
||||
<label htmlFor="confirm-password" className="block text-sm font-medium text-foreground mb-2">
|
||||
Confirm Password
|
||||
{t('confirmPassword.label')}
|
||||
</label>
|
||||
<div className="relative">
|
||||
<Lock className="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-muted-foreground" />
|
||||
<Input
|
||||
id="confirm-password"
|
||||
type="password"
|
||||
placeholder="Confirm your password"
|
||||
placeholder={t('confirmPassword.placeholder')}
|
||||
value={confirmPassword}
|
||||
onChange={(e) => setConfirmPassword(e.target.value)}
|
||||
disabled={isLoading}
|
||||
@@ -164,11 +166,11 @@ export function SetupModal({ isOpen, onComplete }: SetupModalProps) {
|
||||
<div className="p-4 border border-border rounded-lg bg-muted/30">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h4 className="font-medium text-foreground mb-1">Enable Authentication</h4>
|
||||
<h4 className="font-medium text-foreground mb-1">{t('enableAuth.title')}</h4>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{enableAuth
|
||||
? 'Authentication will be required on every page load'
|
||||
: 'Authentication will be optional (can be enabled later in settings)'
|
||||
? t('enableAuth.descriptionEnabled')
|
||||
: t('enableAuth.descriptionDisabled')
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
@@ -176,7 +178,7 @@ export function SetupModal({ isOpen, onComplete }: SetupModalProps) {
|
||||
checked={enableAuth}
|
||||
onCheckedChange={setEnableAuth}
|
||||
disabled={isLoading}
|
||||
label="Enable authentication"
|
||||
label={t('enableAuth.label')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -196,7 +198,7 @@ export function SetupModal({ isOpen, onComplete }: SetupModalProps) {
|
||||
}
|
||||
className="w-full"
|
||||
>
|
||||
{isLoading ? 'Setting Up...' : 'Complete Setup'}
|
||||
{isLoading ? t('actions.settingUp') : t('actions.completeSetup')}
|
||||
</Button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -64,6 +64,57 @@ export const deMessages: NestedMessages = {
|
||||
serverBackOnline: 'Server ist wieder online! Seite wird neu geladen...',
|
||||
},
|
||||
},
|
||||
loadingModal: {
|
||||
processing: 'Verarbeite',
|
||||
pleaseWait: 'Bitte warten...',
|
||||
},
|
||||
authModal: {
|
||||
title: 'Authentifizierung erforderlich',
|
||||
description: 'Bitte geben Sie Ihre Anmeldedaten ein, um auf die Anwendung zuzugreifen.',
|
||||
username: {
|
||||
label: 'Benutzername',
|
||||
placeholder: 'Benutzername eingeben',
|
||||
},
|
||||
password: {
|
||||
label: 'Passwort',
|
||||
placeholder: 'Passwort eingeben',
|
||||
},
|
||||
error: 'Ungültiger Benutzername oder Passwort',
|
||||
actions: {
|
||||
signIn: 'Anmelden',
|
||||
signingIn: 'Anmeldung läuft...',
|
||||
},
|
||||
},
|
||||
setupModal: {
|
||||
title: 'Authentifizierung einrichten',
|
||||
description: 'Richten Sie die Authentifizierung ein, um Ihre Anwendung zu sichern. Diese wird für zukünftige Zugriffe erforderlich sein.',
|
||||
username: {
|
||||
label: 'Benutzername',
|
||||
placeholder: 'Wählen Sie einen Benutzernamen',
|
||||
},
|
||||
password: {
|
||||
label: 'Passwort',
|
||||
placeholder: 'Wählen Sie ein Passwort',
|
||||
},
|
||||
confirmPassword: {
|
||||
label: 'Passwort bestätigen',
|
||||
placeholder: 'Bestätigen Sie Ihr Passwort',
|
||||
},
|
||||
enableAuth: {
|
||||
title: 'Authentifizierung aktivieren',
|
||||
descriptionEnabled: 'Authentifizierung wird bei jedem Seitenladevorgang erforderlich sein',
|
||||
descriptionDisabled: 'Authentifizierung wird optional sein (kann später in den Einstellungen aktiviert werden)',
|
||||
label: 'Authentifizierung aktivieren',
|
||||
},
|
||||
errors: {
|
||||
passwordMismatch: 'Passwörter stimmen nicht überein',
|
||||
setupFailed: 'Fehler beim Einrichten der Authentifizierung',
|
||||
},
|
||||
actions: {
|
||||
completeSetup: 'Einrichtung abschließen',
|
||||
settingUp: 'Wird eingerichtet...',
|
||||
},
|
||||
},
|
||||
layout: {
|
||||
title: 'PVE Skriptverwaltung',
|
||||
tagline: 'Verwalte und starte lokale Proxmox-Hilfsskripte mit Live-Ausgabe',
|
||||
|
||||
@@ -298,4 +298,55 @@ export const enMessages: NestedMessages = {
|
||||
passwordMismatch: 'Passwords do not match',
|
||||
},
|
||||
},
|
||||
loadingModal: {
|
||||
processing: 'Processing',
|
||||
pleaseWait: 'Please wait...',
|
||||
},
|
||||
authModal: {
|
||||
title: 'Authentication Required',
|
||||
description: 'Please enter your credentials to access the application.',
|
||||
username: {
|
||||
label: 'Username',
|
||||
placeholder: 'Enter your username',
|
||||
},
|
||||
password: {
|
||||
label: 'Password',
|
||||
placeholder: 'Enter your password',
|
||||
},
|
||||
error: 'Invalid username or password',
|
||||
actions: {
|
||||
signIn: 'Sign In',
|
||||
signingIn: 'Signing In...',
|
||||
},
|
||||
},
|
||||
setupModal: {
|
||||
title: 'Setup Authentication',
|
||||
description: 'Set up authentication to secure your application. This will be required for future access.',
|
||||
username: {
|
||||
label: 'Username',
|
||||
placeholder: 'Choose a username',
|
||||
},
|
||||
password: {
|
||||
label: 'Password',
|
||||
placeholder: 'Choose a password',
|
||||
},
|
||||
confirmPassword: {
|
||||
label: 'Confirm Password',
|
||||
placeholder: 'Confirm your password',
|
||||
},
|
||||
enableAuth: {
|
||||
title: 'Enable Authentication',
|
||||
descriptionEnabled: 'Authentication will be required on every page load',
|
||||
descriptionDisabled: 'Authentication will be optional (can be enabled later in settings)',
|
||||
label: 'Enable authentication',
|
||||
},
|
||||
errors: {
|
||||
passwordMismatch: 'Passwords do not match',
|
||||
setupFailed: 'Failed to setup authentication',
|
||||
},
|
||||
actions: {
|
||||
completeSetup: 'Complete Setup',
|
||||
settingUp: 'Setting Up...',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user