# Report Builder — Design Spec **Date:** 2026-04-28 **Status:** Approved --- ## Overview A dedicated `/report` page (admin-only) where users configure a client-facing PDF report from scratch. The report is a professional business document — no app interface visible — downloadable as a `.pdf` file via `@react-pdf/renderer`. --- ## Page Structure Two-column layout on desktop, stacked on mobile: - **Left panel (form):** all configuration fields, grouped into sections - **Right panel:** a static document preview mockup (not a live render — too expensive). Shows real text fields (title, client name, period) updating in real time, but charts and metrics are represented as grey placeholder shapes. Gives the user a sense of page structure and section order. - **Footer bar:** "Generate PDF" button (triggers download), estimated page count The route is `/report`, protected the same way `/settings` is (admin only, via `userRole === 'admin'` check in `App.tsx`). --- ## Form Sections ### 1. Client Info | Field | Type | Notes | |---|---|---| | Report title | Text input | e.g. "Q1 2025 Visitor Performance" | | Prepared for (company) | Text input | Shown in report header | | Contact name | Text input | Optional — "Attention: …" line | | Client logo | File upload (PNG/JPG/SVG, max 2MB) | Base64-encoded, embedded in PDF header | | Accent color | Color picker | Defaults to HiHala blue `#2563eb`; used for section headers, borders | ### 2. Data Selection | Field | Type | Notes | |---|---|---| | Period start / end | Date inputs | Defaults to current month | | Museums | Multi-select (same component as dashboard) | Empty = all | | Channels | Multi-select | Empty = all | | VAT | Toggle: Excl / Incl | Defaults to Incl | | Include comparison | Checkbox | Adds previous-year column to metrics table | ### 3. Content Sections (toggleable) Each is a checkbox, all on by default: - Executive summary (3–4 sentences auto-generated from numbers) - Key metrics table (Revenue, Visitors, Tickets, Avg Rev/Visitor, optionally Pilgrim Capture Rate) - Revenue & visitor trend chart - Breakdown by museum - Breakdown by channel - Pilgrim capture rate section (only shown if toggle is on AND data exists) ### 4. Presentation | Field | Type | Notes | |---|---|---| | Language | Toggle: EN / AR | Controls all PDF text and direction | | Confidentiality footer | Select: Confidential / Internal / Public | Shown in page footer | | Page orientation | Toggle: Portrait / Landscape | Portrait default | --- ## PDF Document Design Built with `@react-pdf/renderer`. All layout is code — no DOM capture, no html2canvas. **Page 1 — Cover** - HiHala logo (top-left) + client logo (top-right) - Large report title - "Prepared for: [Company]" / "Attention: [Contact]" - Period label (e.g. "January – March 2025") - Generation date - Accent color bar at bottom **Page 2+ — Content pages** - Shared header: small HiHala wordmark + report title + page number - Sections in the order the user toggled them on - Each section starts with a colored heading bar (accent color) - Confidentiality level in page footer **Chart rendering:** Charts do not use the live Chart.js instances. Instead, `@react-pdf/renderer` draws simplified chart equivalents natively using SVG primitives (``, ``, ``, ``) — no canvas capture needed. This produces crisp vector output at any print resolution. Simplified charts to implement: - **Trend line chart:** SVG polyline over a grid - **Bar chart (museum/channel breakdown):** horizontal SVG bars with labels **Executive summary generation:** Computed from the metrics — a template string filled with actual numbers. Example (EN): > "During [period], [selected museums] recorded [X] visitors and [Y SAR] in revenue, representing a [Z%] change versus the same period last year. The top-performing channel was [channel] with [N%] of total tickets." The same template exists in Arabic (stored in the locale file alongside EN/AR strings already in the codebase). Falls back gracefully if comparison data is absent (omits the change sentence). --- ## Data Flow ``` /report page → user fills form → clicks "Generate PDF" → reads filtered data from already-loaded app state (passed as prop from App.tsx) OR re-fetches if needed (dataService.fetchData()) → applies period + museum + channel filters client-side → computes metrics (reuses existing calculateMetrics()) → passes computed values to (react-pdf component) → pdf.download('hihala-report.pdf') ``` No new API endpoints required. All computation is client-side using existing `dataService` functions. --- ## File Structure ``` src/ components/ Report/ index.tsx — the /report page (form + preview layout) ReportForm.tsx — the left-panel form ReportPreview.tsx — the right-panel static mockup ReportDocument.tsx — the @react-pdf/renderer document reportCharts.tsx — SVG chart primitives for PDF reportHelpers.ts — executive summary generator, data filters ``` --- ## Navigation - Desktop nav: gear icon already links to `/settings`; add a "Report" link (document icon) next to it, admin-only - Mobile bottom nav: add Report icon between Comparison and Settings --- ## Dependencies - `@react-pdf/renderer` — PDF generation - No other new dependencies (reuses existing AltMultiSelect, form inputs, data service) --- ## Out of Scope - Scheduled / emailed reports - Saving report configurations - Non-admin users generating reports - Live chart preview in the right panel (static mockup only)