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

@@ -12,7 +12,7 @@ type StatsResponse = {
latest_photo_at?: string;
};
export function usePollStats(slug: string | null | undefined) {
export function usePollStats(eventKey: string | null | undefined) {
const [data, setData] = useState<EventStats>({ onlineGuests: 0, tasksSolved: 0, latestPhotoAt: null });
const [loading, setLoading] = useState(true);
const timer = useRef<number | null>(null);
@@ -20,11 +20,11 @@ export function usePollStats(slug: string | null | undefined) {
typeof document !== 'undefined' ? document.visibilityState === 'visible' : true
);
const canPoll = Boolean(slug);
const canPoll = Boolean(eventKey);
async function fetchOnce(activeSlug: string) {
async function fetchOnce(activeKey: string) {
try {
const res = await fetch(`/api/v1/events/${encodeURIComponent(activeSlug)}/stats`, {
const res = await fetch(`/api/v1/events/${encodeURIComponent(activeKey)}/stats`, {
headers: { 'Cache-Control': 'no-store' },
});
if (res.status === 304) return;
@@ -52,16 +52,16 @@ export function usePollStats(slug: string | null | undefined) {
}
setLoading(true);
const activeSlug = String(slug);
fetchOnce(activeSlug);
const activeKey = String(eventKey);
fetchOnce(activeKey);
if (timer.current) window.clearInterval(timer.current);
if (visible) {
timer.current = window.setInterval(() => fetchOnce(activeSlug), 10_000);
timer.current = window.setInterval(() => fetchOnce(activeKey), 10_000);
}
return () => {
if (timer.current) window.clearInterval(timer.current);
};
}, [slug, visible, canPoll]);
}, [eventKey, visible, canPoll]);
return { ...data, loading };
}