Guest PWA vollständig lokalisiert
This commit is contained in:
@@ -7,14 +7,19 @@ import { Alert, AlertDescription } from '@/components/ui/alert';
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Html5Qrcode } from 'html5-qrcode';
|
||||
import { readGuestName } from '../context/GuestIdentityContext';
|
||||
import { useTranslation } from '../i18n/useTranslation';
|
||||
|
||||
type LandingErrorKey = 'eventClosed' | 'network' | 'camera';
|
||||
|
||||
export default function LandingPage() {
|
||||
const nav = useNavigate();
|
||||
const { t } = useTranslation();
|
||||
const [eventCode, setEventCode] = useState('');
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [errorKey, setErrorKey] = useState<LandingErrorKey | null>(null);
|
||||
const [isScanning, setIsScanning] = useState(false);
|
||||
const [scanner, setScanner] = useState<Html5Qrcode | null>(null);
|
||||
const errorMessage = errorKey ? t(`landing.errors.${errorKey}`) : null;
|
||||
|
||||
function extractEventKey(raw: string): string {
|
||||
const trimmed = raw.trim();
|
||||
@@ -48,11 +53,11 @@ export default function LandingPage() {
|
||||
const normalized = extractEventKey(provided);
|
||||
if (!normalized) return;
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
setErrorKey(null);
|
||||
try {
|
||||
const res = await fetch(`/api/v1/events/${encodeURIComponent(normalized)}`);
|
||||
if (!res.ok) {
|
||||
setError('Event nicht gefunden oder geschlossen.');
|
||||
setErrorKey('eventClosed');
|
||||
return;
|
||||
}
|
||||
const data = await res.json();
|
||||
@@ -65,7 +70,7 @@ export default function LandingPage() {
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Join request failed', e);
|
||||
setError('Netzwerkfehler. Bitte spaeter erneut versuchen.');
|
||||
setErrorKey('network');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -80,7 +85,7 @@ export default function LandingPage() {
|
||||
setIsScanning(true);
|
||||
} catch (err) {
|
||||
console.error('Scanner start failed', err);
|
||||
setError('Kamera-Zugriff fehlgeschlagen. Bitte erlaube den Zugriff und versuche es erneut.');
|
||||
setErrorKey('camera');
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -92,7 +97,7 @@ export default function LandingPage() {
|
||||
setIsScanning(true);
|
||||
} catch (err) {
|
||||
console.error('Scanner initialisation failed', err);
|
||||
setError('Kamera-Zugriff fehlgeschlagen. Bitte erlaube den Zugriff und versuche es erneut.');
|
||||
setErrorKey('camera');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,22 +128,22 @@ export default function LandingPage() {
|
||||
}, [scanner]);
|
||||
|
||||
return (
|
||||
<Page title="Willkommen bei der Fotobox!">
|
||||
{error && (
|
||||
<Page title={t('landing.pageTitle')}>
|
||||
{errorMessage && (
|
||||
<Alert className="mb-3" variant="destructive">
|
||||
<AlertDescription>{error}</AlertDescription>
|
||||
<AlertDescription>{errorMessage}</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
<div className="space-y-6 pb-20">
|
||||
<div className="text-center space-y-2">
|
||||
<h1 className="text-2xl font-bold text-gray-900">Willkommen bei der Fotobox!</h1>
|
||||
<p className="text-lg text-gray-600">Dein Schluessel zu unvergesslichen Momenten.</p>
|
||||
<h1 className="text-2xl font-bold text-gray-900">{t('landing.headline')}</h1>
|
||||
<p className="text-lg text-gray-600">{t('landing.subheadline')}</p>
|
||||
</div>
|
||||
|
||||
<Card className="mx-auto w-full max-w-md">
|
||||
<CardHeader className="text-center">
|
||||
<CardTitle className="text-xl font-semibold">Event beitreten</CardTitle>
|
||||
<CardDescription>Scanne den QR-Code oder gib den Code manuell ein.</CardDescription>
|
||||
<CardTitle className="text-xl font-semibold">{t('landing.join.title')}</CardTitle>
|
||||
<CardDescription>{t('landing.join.description')}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4 p-6">
|
||||
<div className="flex flex-col items-center space-y-3">
|
||||
@@ -155,20 +160,20 @@ export default function LandingPage() {
|
||||
onClick={isScanning ? stopScanner : startScanner}
|
||||
disabled={loading}
|
||||
>
|
||||
{isScanning ? 'Scanner stoppen' : 'QR-Code scannen'}
|
||||
{isScanning ? t('landing.scan.stop') : t('landing.scan.start')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="border-t border-gray-200 py-2 text-center text-sm text-gray-500">
|
||||
Oder manuell eingeben
|
||||
{t('landing.scan.manualDivider')}
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Input
|
||||
value={eventCode}
|
||||
onChange={(event) => setEventCode(event.target.value)}
|
||||
placeholder="Event-Code eingeben"
|
||||
placeholder={t('landing.input.placeholder')}
|
||||
disabled={loading}
|
||||
/>
|
||||
<Button
|
||||
@@ -176,7 +181,7 @@ export default function LandingPage() {
|
||||
disabled={loading || !eventCode.trim()}
|
||||
onClick={() => join()}
|
||||
>
|
||||
{loading ? 'Pruefe...' : 'Event beitreten'}
|
||||
{loading ? t('landing.join.buttonLoading') : t('landing.join.button')}
|
||||
</Button>
|
||||
</div>
|
||||
</CardContent>
|
||||
|
||||
Reference in New Issue
Block a user