import { useMemo, useRef, useState } from "react"; import { DUNGEON_REPORT_STATUSES, filterDungeonReports, sessionCanManageDungeonReports, updateDungeonReport, type DungeonReport, type DungeonReportStatus, } from "../app/dungeonReports"; import { useShellStore } from "../app/shellStore"; import { DualDisplayFrame } from "../components/DualDisplayFrame"; import { DUNGEON_CATALOG, dungeonById } from "../game/dungeonCatalog"; import { useMenuController, type MenuAction } from "../input/useMenuController"; import { BrandMark, ControllerLegend, FrontSurface } from "./FrontSurface"; import { ControllerButton } from "./ControllerButton"; import { LazyLocalCompanionPanel } from "./LazyLocalCompanionPanel"; import { useDungeonReports } from "./useDungeonReports"; function ReportCard({ report, canManage, saving, onUpdate, selectedId, select }: { readonly report: DungeonReport; readonly canManage: boolean; readonly saving: boolean; readonly onUpdate: (report: DungeonReport, status: DungeonReportStatus) => void; readonly selectedId: string; readonly select: (id: string) => void; }) { return (

{dungeonById(report.dungeonId)?.title ?? report.dungeonId}

{report.status}

{report.comment}

Submitted by {report.submittedBy} · {report.updatedBy && Updated by {report.updatedBy} · {new Date(report.updatedAt).toLocaleString()}} {canManage && (
{report.status !== "Closed" && onUpdate(report, "Closed")}>Close report}
)}
); } export function DungeonReportsScreen({ onClose }: { readonly onClose: () => void }) { const session = useShellStore((state) => state.session); const { snapshot, loading, error, refresh } = useDungeonReports(); const [status, setStatus] = useState("all"); const [dungeonId, setDungeonId] = useState("all"); const [saving, setSaving] = useState(false); const [updateError, setUpdateError] = useState(""); const [message, setMessage] = useState(""); const busy = useRef(false); const canManage = sessionCanManageDungeonReports(session); const reports = filterDungeonReports(snapshot?.reports ?? [], status, dungeonId); const dungeonOptions = useMemo(() => [...DUNGEON_CATALOG].sort((a, b) => a.title.localeCompare(b.title)), []); const actions = useMemo(() => [ { id: "reports-back", run: onClose, enabled: !saving }, { id: "reports-filter-status", run: () => { const options = ["all", ...DUNGEON_REPORT_STATUSES] as const; setStatus(options[(options.indexOf(status) + 1) % options.length]); } }, { id: "reports-filter-dungeon", run: () => { const options = ["all", ...dungeonOptions.map((dungeon) => dungeon.id)]; setDungeonId(options[(options.indexOf(dungeonId) + 1) % options.length]); } }, { id: "reports-clear-filters", run: () => { setStatus("all"); setDungeonId("all"); } }, { id: "reports-refresh", run: () => void refresh(), enabled: !loading && !saving }, ...(canManage ? reports.flatMap((report) => [ { id: `report-status-${report.id}`, enabled: !saving, run: () => { const nextStatus = DUNGEON_REPORT_STATUSES[(DUNGEON_REPORT_STATUSES.indexOf(report.status) + 1) % DUNGEON_REPORT_STATUSES.length]; void update(report, nextStatus); } }, ...(report.status === "Closed" ? [] : [{ id: `report-close-${report.id}`, enabled: !saving, run: () => void update(report, "Closed") }]), ]) : []), ], [canManage, dungeonId, dungeonOptions, loading, onClose, refresh, reports, saving, session, status]); const controller = useMenuController(actions, { initialId: "reports-back", onBack: saving ? undefined : onClose }); const update = async (report: DungeonReport, nextStatus: DungeonReportStatus) => { if (!session || !canManage || busy.current) return; busy.current = true; setSaving(true); setUpdateError(""); setMessage(""); try { await updateDungeonReport(session, report, nextStatus); setMessage(`Report status updated to ${nextStatus}.`); } catch (failure) { setUpdateError(failure instanceof Error ? failure.message : "The status could not be updated."); } finally { await refresh(); busy.current = false; setSaving(false); } }; return (
Main menu / Dungeon feedback

View Reports

Back to main menu
{ setStatus("all"); setDungeonId("all"); }}>Clear filters void refresh()}> {loading ? "Refreshing..." : "Refresh"}

{reports.length} of {snapshot?.reports.length ?? 0} reports · {canManage ? "GM status controls enabled" : "All players can view reports. GMs manage their status."}

{(error || updateError) &&

{updateError || error}{snapshot && error ? " Showing the last loaded reports." : ""}

} {message &&

{message}

}
{loading && !snapshot ?

Loading reports...

: snapshot && reports.length === 0 ? (

{snapshot.reports.length ? "No reports match these filters." : "No dungeon reports have been submitted yet."}

) : reports.map((report) => void update(item, nextStatus)} selectedId={controller.selectedId} select={controller.select} />)}
} bottom={} /> ); }