feat(gallery): always-visible feedback indicators on grid tiles; fallback image rendering in lightbox/hero; auto-auth from shared-link token; fix external photo resolver\n\n- GridGallery: bottom-left icons for like/rated/comment on every tile\n- Hero layout grid: added same indicators (non-intrusive icons)\n- Lightbox/Hero: add fallbackSrc to display thumbnail if original fails\n- GalleryAuth: auto-store token from /gallery/:slug/:token and hydrate event\n- Backend gallery photo route: use resolvePhotoFilePath for external-media\n\nfix(admin): move photo feedback badges to bottom-right on admin grid tiles\n\nfix(dashboard): add missing i18n keys for activity types + fallback to formatter\n\nfix(admin/feedback): correct thumbnail URL base + robust date parsing\n\nRefs: #19

This commit is contained in:
2025-09-15 22:55:35 +02:00
parent 4c7b49a5f6
commit 6948aaa92a
13 changed files with 143 additions and 50 deletions
+7 -7
View File
@@ -268,13 +268,13 @@ export const AdminDashboard: React.FC = () => {
categoryName: activity.metadata?.category_name || ''
};
// Check if translation exists
const translated = t(translationKey, params);
if (typeof translated === 'string') {
return translated;
// Translate; if key missing i18n returns the key string itself
const translated = t(translationKey, params) as string;
if (!translated || translated === translationKey) {
// Fallback: format a readable English message
return adminService.formatActivityMessage(activity);
}
// Fallback to unknown activity if translation not found
return t('admin.activities.unknown') as string;
return translated;
};
return (
@@ -302,4 +302,4 @@ export const AdminDashboard: React.FC = () => {
);
};
AdminDashboard.displayName = 'AdminDashboard';
AdminDashboard.displayName = 'AdminDashboard';
+14 -4
View File
@@ -17,7 +17,7 @@ import {
Trash2
} from 'lucide-react';
import { toast } from 'react-toastify';
import { format } from 'date-fns';
import { format, parseISO } from 'date-fns';
import { Button, Card, Loading } from '../../components/common';
import { AdminAuthenticatedImage } from '../../components/admin/AdminAuthenticatedImage';
@@ -254,7 +254,7 @@ export const EventFeedbackPage: React.FC = () => {
{item.photo_id && (
<div className="w-16 h-16 overflow-hidden rounded">
<AdminAuthenticatedImage
src={`/api/admin/photos/${id}/thumbnail/${item.photo_id}`}
src={`/admin/photos/${id}/thumbnail/${item.photo_id}`}
alt={item.filename || 'Photo'}
className="w-16 h-16 object-cover rounded"
/>
@@ -290,7 +290,12 @@ export const EventFeedbackPage: React.FC = () => {
<p className="text-sm text-neutral-700">{item.comment_text}</p>
)}
<p className="text-xs text-neutral-500 mt-1">
{format(new Date(item.created_at), 'PPpp')}
{(() => {
const d = typeof item.created_at === 'string'
? parseISO(item.created_at)
: new Date(item.created_at);
return isNaN(d.getTime()) ? t('common.unknownDate', 'Unknown date') : format(d, 'PPpp');
})()}
</p>
</div>
<div className="flex items-center gap-2">
@@ -492,7 +497,12 @@ export const EventFeedbackPage: React.FC = () => {
<p className="text-sm text-neutral-700">{comment.comment_text}</p>
<p className="text-xs text-neutral-500 mt-1">
{comment.guest_name} {comment.filename}
{format(new Date(comment.created_at), 'PP')}
{(() => {
const d = typeof comment.created_at === 'string'
? parseISO(comment.created_at)
: new Date(comment.created_at);
return isNaN(d.getTime()) ? t('common.unknownDate', 'Unknown date') : format(d, 'PP');
})()}
</p>
</div>
))}