feat: implement tenant OAuth flow and guest achievements

This commit is contained in:
2025-09-25 08:32:37 +02:00
parent ef6203c603
commit b22d91ed32
84 changed files with 5984 additions and 1399 deletions

View File

@@ -1,13 +1,104 @@
import React from 'react';
import { Page } from './_util';
import React, { useState, useEffect } from 'react';
import { useParams, useNavigate } from 'react-router-dom';
import { useEventData } from '../hooks/useEventData';
import { useGuestIdentity } from '../context/GuestIdentityContext';
import { Input } from '@/components/ui/input';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { Label } from '@/components/ui/label';
import Header from '../components/Header';
export default function ProfileSetupPage() {
const { slug } = useParams<{ slug: string }>();
const nav = useNavigate();
const { event, loading, error } = useEventData();
const { name: storedName, setName: persistName, hydrated } = useGuestIdentity();
const [name, setName] = useState(storedName);
const [submitting, setSubmitting] = useState(false);
useEffect(() => {
if (!slug) {
nav('/');
return;
}
}, [slug, nav]);
useEffect(() => {
if (hydrated) {
setName(storedName);
}
}, [hydrated, storedName]);
function handleChange(value: string) {
setName(value);
}
function submitName() {
if (!slug) return;
const trimmedName = name.trim();
if (!trimmedName) return;
setSubmitting(true);
try {
persistName(trimmedName);
nav(`/e/${slug}`);
} catch (e) {
console.error('Fehler beim Speichern des Namens:', e);
setSubmitting(false);
}
}
if (loading) {
return (
<div className="flex justify-center items-center h-32">
<div className="text-lg">Lade Event...</div>
</div>
);
}
if (error || !event) {
return (
<div className="text-center p-4">
<p className="text-red-600 mb-4">{error || 'Event nicht gefunden.'}</p>
<Button onClick={() => nav('/')}>Zurück zur Startseite</Button>
</div>
);
}
return (
<Page title="Profil erstellen">
<input placeholder="Dein Name" style={{ width: '100%', padding: 10, border: '1px solid #ddd', borderRadius: 8 }} />
<div style={{ height: 12 }} />
<button style={{ padding: '10px 16px', borderRadius: 8, background: '#111827', color: 'white' }}>Starten</button>
</Page>
<div className="min-h-screen bg-gradient-to-b from-pink-50 to-purple-50 flex flex-col">
<Header slug={slug!} />
<div className="flex-1 flex flex-col justify-center items-center px-4 py-8">
<Card className="w-full max-w-md">
<CardHeader className="text-center space-y-2">
<CardTitle className="text-2xl font-bold text-gray-900">{event.name}</CardTitle>
<CardDescription className="text-lg text-gray-600">
Fange den schoensten Moment ein!
</CardDescription>
</CardHeader>
<CardContent className="space-y-4 p-6">
<div className="space-y-2">
<Label htmlFor="name" className="text-sm font-medium">Dein Name (z.B. Anna)</Label>
<Input
id="name"
value={name}
onChange={(e) => handleChange(e.target.value)}
placeholder="Dein Name"
className="text-lg"
disabled={submitting || !hydrated}
autoComplete="name"
/>
</div>
<Button
className="w-full bg-gradient-to-r from-pink-500 to-pink-600 hover:from-pink-600 hover:to-pink-700 text-white py-3 text-base font-semibold rounded-xl"
onClick={submitName}
disabled={submitting || !name.trim() || !hydrated}
>
{submitting ? 'Speichere...' : "Let's go!"}
</Button>
</CardContent>
</Card>
</div>
</div>
);
}