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

@@ -16,7 +16,8 @@ interface EmotionPickerProps {
}
export default function EmotionPicker({ onSelect }: EmotionPickerProps) {
const { slug } = useParams<{ slug: string }>();
const { token: slug } = useParams<{ token: string }>();
const eventKey = slug ?? '';
const navigate = useNavigate();
const [emotions, setEmotions] = useState<Emotion[]>([]);
const [loading, setLoading] = useState(true);
@@ -33,7 +34,7 @@ export default function EmotionPicker({ onSelect }: EmotionPickerProps) {
];
useEffect(() => {
if (!slug) return;
if (!eventKey) return;
async function fetchEmotions() {
try {
@@ -41,7 +42,7 @@ export default function EmotionPicker({ onSelect }: EmotionPickerProps) {
setError(null);
// Try API first
const response = await fetch(`/api/v1/events/${slug}/emotions`);
const response = await fetch(`/api/v1/events/${encodeURIComponent(eventKey)}/emotions`);
if (response.ok) {
const data = await response.json();
setEmotions(Array.isArray(data) ? data : fallbackEmotions);
@@ -60,14 +61,15 @@ export default function EmotionPicker({ onSelect }: EmotionPickerProps) {
}
fetchEmotions();
}, [slug]);
}, [eventKey]);
const handleEmotionSelect = (emotion: Emotion) => {
if (onSelect) {
onSelect(emotion);
} else {
// Default: Navigate to tasks with emotion filter
navigate(`/e/${slug}/tasks?emotion=${emotion.slug}`);
if (!eventKey) return;
navigate(`/e/${encodeURIComponent(eventKey)}/tasks?emotion=${emotion.slug}`);
}
};
@@ -139,11 +141,14 @@ export default function EmotionPicker({ onSelect }: EmotionPickerProps) {
<Button
variant="ghost"
className="w-full text-sm text-gray-600 dark:text-gray-300 hover:text-pink-600 hover:bg-pink-50 dark:hover:bg-gray-800 border-t border-gray-200 dark:border-gray-700 pt-3 mt-3"
onClick={() => navigate(`/e/${slug}/tasks`)}
onClick={() => {
if (!eventKey) return;
navigate(`/e/${encodeURIComponent(eventKey)}/tasks`);
}}
>
Überspringen und Aufgabe wählen
</Button>
</div>
</div>
);
}
}