Guest PWA vollständig lokalisiert

This commit is contained in:
Codex Agent
2025-10-17 15:00:07 +02:00
parent bd38decc23
commit 25e8f0511b
26 changed files with 1464 additions and 588 deletions

View File

@@ -4,6 +4,7 @@ import { Dialog, DialogContent } from '@/components/ui/dialog';
import { Button } from '@/components/ui/button';
import { Heart, ChevronLeft, ChevronRight, X } from 'lucide-react';
import { likePhoto } from '../services/photosApi';
import { useTranslation } from '../i18n/useTranslation';
type Photo = {
id: number;
@@ -31,6 +32,7 @@ export default function PhotoLightbox({ photos, currentIndex, onClose, onIndexCh
const navigate = useNavigate();
const photoId = params.photoId;
const eventSlug = params.token || slug;
const { t } = useTranslation();
const [standalonePhoto, setStandalonePhoto] = useState<Photo | null>(null);
const [loading, setLoading] = useState(true);
@@ -65,10 +67,10 @@ export default function PhotoLightbox({ photos, currentIndex, onClose, onIndexCh
setStandalonePhoto(location.state.photo);
}
} else {
setError('Foto nicht gefunden');
setError(t('lightbox.errors.notFound'));
}
} catch (err) {
setError('Fehler beim Laden des Fotos');
setError(t('lightbox.errors.loadFailed'));
} finally {
setLoading(false);
}
@@ -78,7 +80,7 @@ export default function PhotoLightbox({ photos, currentIndex, onClose, onIndexCh
} else if (!isStandalone) {
setLoading(false);
}
}, [isStandalone, photoId, eventSlug, standalonePhoto, location.state]);
}, [isStandalone, photoId, eventSlug, standalonePhoto, location.state, t]);
// Update likes when photo changes
React.useEffect(() => {
@@ -149,31 +151,31 @@ export default function PhotoLightbox({ photos, currentIndex, onClose, onIndexCh
if (foundTask) {
setTask({
id: foundTask.id,
title: foundTask.title || `Aufgabe ${taskId}`
title: foundTask.title || t('lightbox.fallbackTitle').replace('{id}', `${taskId}`)
});
} else {
setTask({
id: taskId,
title: `Unbekannte Aufgabe ${taskId}`
title: t('lightbox.unknownTitle').replace('{id}', `${taskId}`)
});
}
} else {
setTask({
id: taskId,
title: `Unbekannte Aufgabe ${taskId}`
title: t('lightbox.unknownTitle').replace('{id}', `${taskId}`)
});
}
} catch (error) {
console.error('Failed to load task:', error);
setTask({
id: taskId,
title: `Unbekannte Aufgabe ${taskId}`
title: t('lightbox.unknownTitle').replace('{id}', `${taskId}`)
});
} finally {
setTaskLoading(false);
}
})();
}, [photo?.task_id, eventSlug]);
}, [photo?.task_id, eventSlug, t]);
async function onLike() {
if (liked || !photo) return;
@@ -251,9 +253,9 @@ export default function PhotoLightbox({ photos, currentIndex, onClose, onIndexCh
{task && (
<div className="absolute bottom-4 left-4 right-4 z-20 bg-black/60 backdrop-blur-sm rounded-xl p-3 border border-white/20 max-w-md">
<div className="text-sm">
<div className="font-semibold mb-1 text-white">Task: {task.title}</div>
<div className="font-semibold mb-1 text-white">{t('lightbox.taskLabel')}: {task.title}</div>
{taskLoading && (
<div className="text-xs opacity-70 text-gray-300">Lade Aufgabe...</div>
<div className="text-xs opacity-70 text-gray-300">{t('lightbox.loadingTask')}</div>
)}
</div>
</div>
@@ -269,7 +271,14 @@ export default function PhotoLightbox({ photos, currentIndex, onClose, onIndexCh
>
<img
src={photo?.file_path || photo?.thumbnail_path}
alt={`Foto ${photo?.id || ''}${photo?.task_title ? ` - ${photo.task_title}` : ''}`}
alt={t('lightbox.photoAlt')
.replace('{id}', `${photo?.id ?? ''}`)
.replace(
'{suffix}',
photo?.task_title
? t('lightbox.photoAltTaskSuffix').replace('{taskTitle}', photo.task_title)
: ''
)}
className="max-h-[80vh] max-w-full object-contain transition-transform duration-200"
onError={(e) => {
console.error('Image load error:', e);
@@ -283,7 +292,7 @@ export default function PhotoLightbox({ photos, currentIndex, onClose, onIndexCh
<div className="absolute top-4 left-4 right-4 z-20 bg-black/60 backdrop-blur-sm rounded-xl p-3 border border-white/20 max-w-md">
<div className="text-sm text-center">
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-white mx-auto mb-1"></div>
<div className="text-xs opacity-70">Lade Aufgabe...</div>
<div className="text-xs opacity-70">{t('lightbox.loadingTask')}</div>
</div>
</div>
)}