From 2ef837680e2b3f3260685b962c9c5d7273d0f8bc Mon Sep 17 00:00:00 2001
From: Luca <102960244+Luca-Timo@users.noreply.github.com>
Date: Tue, 2 Jun 2026 15:13:55 +0200
Subject: [PATCH] fix(i18n): honor general_time_format across all time displays
Route 10 surfaces that hardcoded 12-hour date-fns patterns
('h:mm a', 'PPp', 'p', toLocaleTimeString) through useLocalizedDate's
formatDateTime/formatTime so they respect general_date_format +
general_time_format: backup/restore, archives, photo viewer, feedback,
event details, gallery timeline, public quote page, CMS save indicator.
Also pin a lang hint on the business-hours native time inputs (Chrome/Edge
render 24h). Drops now-unused date-fns imports.
---
frontend/src/components/admin/AdminPhotoViewer.tsx | 7 ++++---
.../src/components/admin/FeedbackModerationPanel.tsx | 10 ++--------
frontend/src/components/admin/RestoreWizard.jsx | 7 ++++---
.../gallery/layouts/TimelineGalleryLayout.tsx | 4 +++-
frontend/src/pages/admin/ArchivesPage.tsx | 4 +++-
frontend/src/pages/admin/BackupManagement.tsx | 5 +++--
frontend/src/pages/admin/CMSPage.tsx | 4 ++--
frontend/src/pages/admin/EventDetailsPage.tsx | 4 ++--
frontend/src/pages/admin/EventFeedbackPage.tsx | 6 ++++--
frontend/src/pages/public/QuoteResponsePage.tsx | 4 ++--
10 files changed, 29 insertions(+), 26 deletions(-)
diff --git a/frontend/src/components/admin/AdminPhotoViewer.tsx b/frontend/src/components/admin/AdminPhotoViewer.tsx
index 8acbffe1..00cbfc2a 100644
--- a/frontend/src/components/admin/AdminPhotoViewer.tsx
+++ b/frontend/src/components/admin/AdminPhotoViewer.tsx
@@ -1,6 +1,5 @@
import React, { useState } from 'react';
import { X, ChevronLeft, ChevronRight, Download, Trash2, Tag, Calendar, HardDrive, Eye, MousePointer, MessageSquare, Star, Heart, CheckCircle, XCircle, AlertCircle } from 'lucide-react';
-import { format } from 'date-fns';
import { toast } from 'react-toastify';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
@@ -10,6 +9,7 @@ import { feedbackService, type PhotoFeedback, type FeedbackSummary } from '../..
import { Button } from '../common';
import { AdminAuthenticatedImage } from './AdminAuthenticatedImage';
import { AdminAuthenticatedVideo } from './AdminAuthenticatedVideo';
+import { useLocalizedDate } from '../../hooks/useLocalizedDate';
type AdminFeedbackResponse = {
feedback: PhotoFeedback[];
@@ -38,6 +38,7 @@ export const AdminPhotoViewer: React.FC = ({
const [showCategoryMenu, setShowCategoryMenu] = useState(false);
const [expandedComments, setExpandedComments] = useState(false);
const queryClient = useQueryClient();
+ const { formatDateTime: fmtDateTime } = useLocalizedDate();
const currentPhoto = photos[currentIndex];
const isVideo = currentPhoto
@@ -312,7 +313,7 @@ export const AdminPhotoViewer: React.FC = ({
Uploaded
- {format(new Date(currentPhoto.uploaded_at), 'MMM d, yyyy h:mm a')}
+ {fmtDateTime(currentPhoto.uploaded_at)}
@@ -408,7 +409,7 @@ export const AdminPhotoViewer: React.FC = ({
{comment.guest_name || 'Anonymous'}
- {format(new Date(comment.created_at), 'MMM d, yyyy h:mm a')}
+ {fmtDateTime(comment.created_at)}
diff --git a/frontend/src/components/admin/FeedbackModerationPanel.tsx b/frontend/src/components/admin/FeedbackModerationPanel.tsx
index 23b9e80e..75592e70 100644
--- a/frontend/src/components/admin/FeedbackModerationPanel.tsx
+++ b/frontend/src/components/admin/FeedbackModerationPanel.tsx
@@ -8,7 +8,6 @@ import {
CheckCircle,
User
} from 'lucide-react';
-import { parseISO } from 'date-fns';
import { toast } from 'react-toastify';
import { Card, Loading, Button } from '../common';
@@ -30,7 +29,7 @@ export const FeedbackModerationPanel: React.FC = (
maxItems = 5
}) => {
const { t } = useTranslation();
- const { format } = useLocalizedDate();
+ const { formatDateTime } = useLocalizedDate();
const queryClient = useQueryClient();
const [showAll, setShowAll] = useState(false);
@@ -116,12 +115,7 @@ export const FeedbackModerationPanel: React.FC = (
•
- {format(
- typeof item.created_at === 'string'
- ? parseISO(item.created_at)
- : new Date(item.created_at),
- 'MMM d, h:mm a'
- )}
+ {formatDateTime(item.created_at)}
{item.comment_text || item.comment}
diff --git a/frontend/src/components/admin/RestoreWizard.jsx b/frontend/src/components/admin/RestoreWizard.jsx
index 31d152ab..74f6b047 100644
--- a/frontend/src/components/admin/RestoreWizard.jsx
+++ b/frontend/src/components/admin/RestoreWizard.jsx
@@ -24,14 +24,15 @@ import {
AlertCircle,
ShieldCheck
} from 'lucide-react';
-import { format } from 'date-fns';
import { toast } from 'react-toastify';
import { useQuery, useMutation } from '@tanstack/react-query';
import { Button, Card, Input, Loading } from '../common';
import { api } from '../../config/api';
+import { useLocalizedDate } from '../../hooks/useLocalizedDate';
export const RestoreWizard = ({ onVerifyIntegrity } = {}) => {
const { t } = useTranslation();
+ const { format: fmtDate, formatTime: fmtTime, formatDateTime: fmtDateTime } = useLocalizedDate();
const [currentStep, setCurrentStep] = useState(0);
const steps = [
@@ -331,7 +332,7 @@ export const RestoreWizard = ({ onVerifyIntegrity } = {}) => {
- {format(new Date(backup.created_at), 'PPP')} {t('backup.restore.backup.at')} {format(new Date(backup.created_at), 'p')}
+ {fmtDate(backup.created_at)} {t('backup.restore.backup.at')} {fmtTime(backup.created_at)}
{t('backup.dashboard.backupType', { type: backup.backup_type })} • {formatBytes(backup.total_size || 0)}
@@ -586,7 +587,7 @@ export const RestoreWizard = ({ onVerifyIntegrity } = {}) => {
{t('backup.restore.confirmation.summary.backupDate')}:
- {format(new Date(restoreData.selectedBackup.created_at), 'PPp')}
+ {fmtDateTime(restoreData.selectedBackup.created_at)}
diff --git a/frontend/src/components/gallery/layouts/TimelineGalleryLayout.tsx b/frontend/src/components/gallery/layouts/TimelineGalleryLayout.tsx
index ebaa361a..20cebebc 100644
--- a/frontend/src/components/gallery/layouts/TimelineGalleryLayout.tsx
+++ b/frontend/src/components/gallery/layouts/TimelineGalleryLayout.tsx
@@ -8,6 +8,7 @@ import type { Photo } from '../../../types';
import { FeedbackIdentityModal } from '../../gallery/FeedbackIdentityModal';
import { feedbackService } from '../../../services/feedback.service';
import { useGuestIdentityOptional } from '../../../contexts/GuestIdentityContext';
+import { useLocalizedDate } from '../../../hooks/useLocalizedDate';
export const TimelineGalleryLayout: React.FC
= ({
photos,
@@ -23,6 +24,7 @@ export const TimelineGalleryLayout: React.FC = ({
feedbackOptions
}) => {
const { theme } = useTheme();
+ const { formatTime: fmtTime } = useLocalizedDate();
const [likedIds, setLikedIds] = useState>(new Set());
// Seed from server is_liked on first non-empty payload (#590 follow-up).
// Mount-only so refetches don't clobber in-session optimistic toggles.
@@ -124,7 +126,7 @@ export const TimelineGalleryLayout: React.FC = ({
{/* Time label */}
- {format(parseISO(photo.uploaded_at), 'h:mm a')}
+ {fmtTime(photo.uploaded_at)}
diff --git a/frontend/src/pages/admin/ArchivesPage.tsx b/frontend/src/pages/admin/ArchivesPage.tsx
index 98ec599c..9ff55597 100644
--- a/frontend/src/pages/admin/ArchivesPage.tsx
+++ b/frontend/src/pages/admin/ArchivesPage.tsx
@@ -19,10 +19,12 @@ import { Button, Input, Card, Loading } from '../../components/common';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { archiveService } from '../../services/archive.service';
import { useTranslation } from 'react-i18next';
+import { useLocalizedDate } from '../../hooks/useLocalizedDate';
// import { useNavigate } from 'react-router-dom';
export const ArchivesPage: React.FC = () => {
const { t } = useTranslation();
+ const { formatTime: fmtTime } = useLocalizedDate();
const [searchTerm, setSearchTerm] = useState('');
const [filterType, setFilterType] = useState
('all');
const [sortBy, setSortBy] = useState<'date' | 'size' | 'name'>('date');
@@ -286,7 +288,7 @@ export const ArchivesPage: React.FC = () => {
{formatDate(archive.archivedAt, 'MMM d, yyyy') || t('archives.processing')}
- {formatDate(archive.archivedAt, 'h:mm a')}
+ {archive.archivedAt ? fmtTime(archive.archivedAt) : ''}
diff --git a/frontend/src/pages/admin/BackupManagement.tsx b/frontend/src/pages/admin/BackupManagement.tsx
index 43e89774..1e6532e8 100644
--- a/frontend/src/pages/admin/BackupManagement.tsx
+++ b/frontend/src/pages/admin/BackupManagement.tsx
@@ -15,10 +15,10 @@ import {
} from 'lucide-react';
import { toast } from 'react-toastify';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
-import { format } from 'date-fns';
import { useTranslation } from 'react-i18next';
import { Button, Card, Loading } from '../../components/common';
+import { useLocalizedDate } from '../../hooks/useLocalizedDate';
import { BackupDashboard } from '../../components/admin/BackupDashboard';
import { BackupConfiguration } from '../../components/admin/BackupConfiguration';
import { BackupHistory } from '../../components/admin/BackupHistory';
@@ -33,6 +33,7 @@ export const BackupManagement: React.FC = () => {
const [activeTab, setActiveTab] = useState('dashboard');
const queryClient = useQueryClient();
const { t } = useTranslation();
+ const { formatDateTime: fmtDateTime } = useLocalizedDate();
const tabs = [
{ id: 'dashboard' as const, label: t('backup.tabs.dashboard'), icon: HardDrive },
@@ -122,7 +123,7 @@ export const BackupManagement: React.FC = () => {
<>
- {t('backup.status.lastBackup')}: {format(new Date(backupStatus.lastBackup.created_at), 'PPp')}
+ {t('backup.status.lastBackup')}: {fmtDateTime(backupStatus.lastBackup.created_at)}
>
) : (
diff --git a/frontend/src/pages/admin/CMSPage.tsx b/frontend/src/pages/admin/CMSPage.tsx
index 28359e31..b51116eb 100644
--- a/frontend/src/pages/admin/CMSPage.tsx
+++ b/frontend/src/pages/admin/CMSPage.tsx
@@ -16,7 +16,7 @@ import { useLocalizedDate } from '../../hooks/useLocalizedDate';
export const CMSPage: React.FC = () => {
const { t } = useTranslation();
- const { formatDateTime: fmtDateTime } = useLocalizedDate();
+ const { formatDateTime: fmtDateTime, formatTime: fmtTime } = useLocalizedDate();
const queryClient = useQueryClient();
const [selectedPage, setSelectedPage] = useState('impressum');
const [editingLang, setEditingLang] = useState<'en' | 'de'>('en');
@@ -592,7 +592,7 @@ export const CMSPage: React.FC = () => {
{!hasUnsavedChanges && lastSaved && (
- Saved {new Date(lastSaved).toLocaleTimeString()}
+ Saved {fmtTime(new Date(lastSaved))}
)}
diff --git a/frontend/src/pages/admin/EventDetailsPage.tsx b/frontend/src/pages/admin/EventDetailsPage.tsx
index 7a412233..483327a6 100644
--- a/frontend/src/pages/admin/EventDetailsPage.tsx
+++ b/frontend/src/pages/admin/EventDetailsPage.tsx
@@ -244,7 +244,7 @@ export const EventDetailsPage: React.FC = () => {
const navigate = useNavigate();
const queryClient = useQueryClient();
const { t } = useTranslation();
- const { format } = useLocalizedDate();
+ const { format, formatDateTime: fmtDateTime } = useLocalizedDate();
const { flags } = useFeatureFlags();
// Validate ID parameter
@@ -2330,7 +2330,7 @@ export const EventDetailsPage: React.FC = () => {
{t('events.archivedOn')}
- {event.archived_at && format(safeParseDate(event.archived_at)!, 'PPp')}
+ {event.archived_at && fmtDateTime(safeParseDate(event.archived_at)!)}
diff --git a/frontend/src/pages/admin/EventFeedbackPage.tsx b/frontend/src/pages/admin/EventFeedbackPage.tsx
index 7d8ef126..147d6806 100644
--- a/frontend/src/pages/admin/EventFeedbackPage.tsx
+++ b/frontend/src/pages/admin/EventFeedbackPage.tsx
@@ -25,13 +25,15 @@ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { eventsService } from '../../services/events.service';
import { feedbackService } from '../../services/feedback.service';
import type { PhotoFeedback, FeedbackAnalytics, FeedbackResponse } from '../../services/feedback.service';
+import { useLocalizedDate } from '../../hooks/useLocalizedDate';
export const EventFeedbackPage: React.FC = () => {
const { id } = useParams<{ id: string }>();
const navigate = useNavigate();
const queryClient = useQueryClient();
const { t } = useTranslation();
-
+ const { formatDateTime: fmtDateTime } = useLocalizedDate();
+
const [activeTab, setActiveTab] = useState<'settings' | 'feedback' | 'analytics' | 'moderation'>('settings');
const [feedbackFilter, setFeedbackFilter] = useState({
type: '',
@@ -297,7 +299,7 @@ export const EventFeedbackPage: React.FC = () => {
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');
+ return isNaN(d.getTime()) ? t('common.unknownDate', 'Unknown date') : fmtDateTime(d);
})()}
diff --git a/frontend/src/pages/public/QuoteResponsePage.tsx b/frontend/src/pages/public/QuoteResponsePage.tsx
index 4d651e18..d270d578 100644
--- a/frontend/src/pages/public/QuoteResponsePage.tsx
+++ b/frontend/src/pages/public/QuoteResponsePage.tsx
@@ -31,7 +31,7 @@ import { formatShortDate } from '../../utils/dateShort';
export const QuoteResponsePage: React.FC = () => {
const { t, i18n } = useTranslation();
- const { formatDateTime: fmtDateTime } = useLocalizedDate();
+ const { formatDateTime: fmtDateTime, formatTime: fmtTime } = useLocalizedDate();
const { token } = useParams<{ token: string }>();
const [searchParams, setSearchParams] = useSearchParams();
const [error, setError] = useState
(null);
@@ -309,7 +309,7 @@ export const QuoteResponsePage: React.FC = () => {
? Math.max(0, Math.ceil((lockAt.getTime() - Date.now()) / 60000))
: 0;
return {
- at: lockAt ? lockAt.toLocaleTimeString() : '',
+ at: lockAt ? fmtTime(lockAt) : '',
minutes,
};
})())