import { useEffect, useMemo, useRef, useState } from "react"; import { Link, useSearchParams } from "react-router-dom"; import { ArrowLeft, ChevronLeft, ChevronRight, Layout, Edit3, Grid, Download, Table, Info, Image, } from "react-feather"; import { request } from "../lib/api"; import { BASE_W } from "../lib/report"; import { ensureSections, flattenSections } from "../lib/sections"; import { buildSessionQuery, getSessionId, setStoredSessionId } from "../lib/session"; import { APP_VERSION } from "../lib/version"; import type { JobsheetSection, Session } from "../types/session"; import { ReportPageCanvas } from "../components/ReportPageCanvas"; import { InfoMenu } from "../components/InfoMenu"; export default function ReportViewerPage() { const [searchParams] = useSearchParams(); const sessionId = getSessionId(searchParams.toString()); const [session, setSession] = useState(null); const [sections, setSections] = useState([]); const [pageIndex, setPageIndex] = useState(0); const [scale, setScale] = useState(1); const [error, setError] = useState(""); const stageRef = useRef(null); useEffect(() => { if (!sessionId) { setError("No active session found. Return to upload to continue."); return; } setStoredSessionId(sessionId); }, [sessionId]); useEffect(() => { const handleResize = () => { if (!stageRef.current) return; const width = stageRef.current.clientWidth; if (width > 0) setScale(width / BASE_W); }; handleResize(); window.addEventListener("resize", handleResize); return () => window.removeEventListener("resize", handleResize); }, []); useEffect(() => { if (!sessionId) return; async function load() { try { const data = await request(`/sessions/${sessionId}`); setSession(data); const sectionResp = await request<{ sections: JobsheetSection[] }>( `/sessions/${sessionId}/sections`, ); const loaded = ensureSections(sectionResp.sections); setSections(loaded); } catch (err) { const message = err instanceof Error ? err.message : "Failed to load session."; setError(message); } } load(); }, [sessionId]); const flatPages = useMemo( () => flattenSections(ensureSections(sections)), [sections], ); const totalPages = useMemo(() => { if (flatPages.length > 0) return flatPages.length; return Math.max(1, session?.page_count ?? 0); }, [flatPages.length, session?.page_count]); useEffect(() => { setPageIndex((idx) => Math.min(Math.max(0, idx), totalPages - 1)); }, [totalPages]); useEffect(() => { const handler = (event: KeyboardEvent) => { if (event.key === "ArrowRight") { setPageIndex((idx) => Math.min(totalPages - 1, idx + 1)); } if (event.key === "ArrowLeft") { setPageIndex((idx) => Math.max(0, idx - 1)); } }; window.addEventListener("keydown", handler); return () => window.removeEventListener("keydown", handler); }, [totalPages]); const page = flatPages[pageIndex]?.page ?? null; const sectionLabel = flatPages[pageIndex]?.sectionTitle ? `Section ${flatPages[pageIndex].sectionIndex + 1} - ${flatPages[pageIndex].sectionTitle}` : flatPages[pageIndex] ? `Section ${flatPages[pageIndex].sectionIndex + 1}` : ""; const template = page?.template; const sessionQuery = buildSessionQuery(sessionId || ""); const editReportQuery = useMemo(() => { if (!sessionId) return ""; const params = new URLSearchParams(); params.set("session", sessionId); params.set("page", String(pageIndex + 1)); return `?${params.toString()}`; }, [sessionId, pageIndex]); const viewerMeta = useMemo(() => { if (!session) return "Loading..."; const selected = session.selected_photo_ids?.length ?? 0; const docs = session.uploads?.documents?.length ?? 0; const dataFiles = session.uploads?.data_files?.length ?? 0; const hasEdits = flatPages.length > 0; return ( `Selected example photos: ${selected} - Documents: ${docs} - Data files: ${dataFiles}` + (hasEdits ? " - Edited pages loaded" : " - No saved edits yet") ); }, [flatPages.length, session]); return (
Company logo

RepEx - Report Express

Report Viewer

Back