Added opaque join-token support across backend and frontend: new migration/model/service/endpoints, guest controllers now resolve tokens, and the demo seeder seeds a token. Tenant event details list/manage tokens with copy/revoke actions, and the guest PWA uses tokens end-to-end (routing, storage, uploads, achievements, etc.). Docs TODO updated to reflect completed steps.

This commit is contained in:
Codex Agent
2025-10-12 10:32:37 +02:00
parent d04e234ca0
commit 9394c3171e
73 changed files with 3277 additions and 911 deletions

View File

@@ -28,11 +28,12 @@ const TASK_PROGRESS_TARGET = 5;
const TIMER_VIBRATION = [0, 60, 120, 60];
export default function TaskPickerPage() {
const { slug } = useParams<{ slug: string }>();
const { token: slug } = useParams<{ token: string }>();
const eventKey = slug ?? '';
const navigate = useNavigate();
const [searchParams, setSearchParams] = useSearchParams();
const { completedCount, markCompleted, isCompleted } = useGuestTaskProgress(slug);
const { completedCount, markCompleted, isCompleted } = useGuestTaskProgress(eventKey);
const [tasks, setTasks] = React.useState<Task[]>([]);
const [currentTask, setCurrentTask] = React.useState<Task | null>(null);
@@ -48,12 +49,12 @@ export default function TaskPickerPage() {
const initialEmotionRef = React.useRef(false);
const fetchTasks = React.useCallback(async () => {
if (!slug) return;
if (!eventKey) return;
setIsFetching(true);
setLoading(true);
setError(null);
try {
const response = await fetch(`/api/v1/events/${encodeURIComponent(slug)}/tasks`);
const response = await fetch(`/api/v1/events/${encodeURIComponent(eventKey)}/tasks`);
if (!response.ok) throw new Error('Aufgaben konnten nicht geladen werden.');
const payload = await response.json();
if (Array.isArray(payload)) {
@@ -69,7 +70,7 @@ export default function TaskPickerPage() {
setIsFetching(false);
setLoading(false);
}
}, [slug]);
}, [eventKey]);
React.useEffect(() => {
fetchTasks();
@@ -206,8 +207,9 @@ export default function TaskPickerPage() {
};
const handleStartUpload = () => {
if (!currentTask || !slug) return;
navigate(`/e/${encodeURIComponent(slug)}/upload?task=${currentTask.id}&emotion=${currentTask.emotion?.slug || ''}`);
if (!currentTask || !eventKey) return;
if (!eventKey) return;
navigate(`/e/${encodeURIComponent(eventKey)}/upload?task=${currentTask.id}&emotion=${currentTask.emotion?.slug || ''}`);
};
const handleMarkCompleted = () => {