Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
5.5 KiB
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 (<Svg>, <Rect>, <Path>, <Line>) — 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 <ReportDocument /> (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)