diff --git a/frontend/app/page.tsx b/frontend/app/page.tsx index c114e49..5929678 100644 --- a/frontend/app/page.tsx +++ b/frontend/app/page.tsx @@ -5,6 +5,7 @@ import { StatusBar } from '@/components/ui/StatusBar'; import { Header } from '@/components/passenger/Header'; import { TripCard } from '@/components/passenger/TripCard'; import { LostItemModal } from '@/components/passenger/LostItemModal'; +import { MyReports } from '@/components/passenger/MyReports'; import { BottomNav, type NavTab } from '@/components/ui/BottomNav'; import { Toast } from '@/components/ui/Toast'; import { mockUser, formatRelativeTime } from '@/lib/mock-data'; @@ -298,6 +299,9 @@ export default function PassengerApp() { ))} + + {/* What this passenger reported, and how each one ended */} + ); }; diff --git a/frontend/components/passenger/MyReports.tsx b/frontend/components/passenger/MyReports.tsx new file mode 100644 index 0000000..55533cd --- /dev/null +++ b/frontend/components/passenger/MyReports.tsx @@ -0,0 +1,73 @@ +'use client'; + +import { ITEM_CATEGORY_CONFIG } from '@/lib/types'; +import { NOTIFICATION_STATUS_CONFIG, UI_LABELS } from '@/lib/labels'; +import { formatRelativeTime } from '@/lib/mock-data'; +import { useDemoReports } from '@/lib/hooks'; + +/** + * What this passenger reported, and how each one ended. + * + * The crew's answer used to reach the passenger as a four-second toast and + * nothing else: look away, or have the phone asleep, and there was no way to + * find out afterwards what the crew said. This is the record that outlives the + * toast — same data, read back from the handover. + * + * Renders nothing before the first report, rather than an empty box explaining + * its own emptiness on a screen the passenger opened to see their trips. + */ +export function MyReports() { + const reports = useDemoReports(); + + if (reports.length === 0) return null; + + return ( +
+

{UI_LABELS.myReports.title}

+ +
+ ); +} diff --git a/frontend/lib/hooks/index.ts b/frontend/lib/hooks/index.ts index c45a533..db09855 100644 --- a/frontend/lib/hooks/index.ts +++ b/frontend/lib/hooks/index.ts @@ -12,3 +12,5 @@ export { useReportLostItem, useApiHealth, } from './useApi'; + +export { useDemoReports } from './useDemoReports'; diff --git a/frontend/lib/hooks/useDemoReports.ts b/frontend/lib/hooks/useDemoReports.ts new file mode 100644 index 0000000..3f58e79 --- /dev/null +++ b/frontend/lib/hooks/useDemoReports.ts @@ -0,0 +1,28 @@ +'use client'; + +import { useState, useEffect } from 'react'; +import { readReports, subscribeReports } from '../demo-bus'; +import type { StaffNotification } from '../types'; + +/** + * The reports filed from this browser, with whatever the crew answered. + * + * Empty until mount by design: localStorage does not exist on the server, so + * reading it during render would make the first paint disagree with the HTML + * Next sent. The subscription then keeps the list live — a crew answer landing + * in the other tab updates it without a reload. + */ +export function useDemoReports(): StaffNotification[] { + const [reports, setReports] = useState([]); + + useEffect(() => { + // Loading from an external system, which is the case the + // set-state-in-effect rule's own docs call legitimate — there is no way to + // express "read storage on mount" without it. + // eslint-disable-next-line react-hooks/set-state-in-effect + setReports(readReports()); + return subscribeReports(setReports); + }, []); + + return reports; +} diff --git a/frontend/lib/labels.ts b/frontend/lib/labels.ts index 07f27c1..242c0e2 100644 --- a/frontend/lib/labels.ts +++ b/frontend/lib/labels.ts @@ -159,6 +159,13 @@ export const UI_LABELS = { step3: 'Abholung koordinieren', }, + // The passenger's own reports and how each one ended + myReports: { + title: 'Meine Verlustmeldungen', + staffNote: 'Rückmeldung des Personals', + reportedAt: 'Gemeldet', + }, + // Time time: { justNow: 'gerade eben',