feat(report): page shell with two-column layout and PDF download action

This commit is contained in:
fahed
2026-04-28 14:42:31 +03:00
parent 553928a3a9
commit 640538bcbd
+92
View File
@@ -0,0 +1,92 @@
import React, { useState, useCallback } from 'react';
import { pdf } from '@react-pdf/renderer';
import type { MuseumRecord } from '../../types';
import { DEFAULT_CONFIG, computeReportData } from './reportHelpers';
import type { ReportConfig } from './reportHelpers';
import { ReportDocument } from './ReportDocument';
import ReportForm from './ReportForm';
import ReportPreview from './ReportPreview';
import { getUniqueMuseums, getUniqueChannels } from '../../services/dataService';
interface Props {
data: MuseumRecord[];
}
export default function ReportPage({ data }: Props) {
const [config, setConfig] = useState<ReportConfig>(DEFAULT_CONFIG);
const [generating, setGenerating] = useState(false);
const allMuseums = getUniqueMuseums(data);
const allChannels = getUniqueChannels(data);
const patch = useCallback((p: Partial<ReportConfig>) => setConfig(c => ({ ...c, ...p })), []);
const handleGenerate = async () => {
setGenerating(true);
try {
const reportData = computeReportData(data, config);
const blob = await pdf(<ReportDocument data={reportData} />).toBlob();
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
const slug = (config.clientName || 'report').toLowerCase().replace(/\s+/g, '-');
a.href = url;
a.download = `hihala-${slug}-${config.startDate.slice(0, 7)}.pdf`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
} catch (err) {
console.error('PDF generation failed:', err);
alert('Failed to generate PDF. Please try again.');
} finally {
setGenerating(false);
}
};
return (
<div className="report-page">
<div className="report-header">
<h1 className="report-title">Report Builder</h1>
<p className="report-sub">Configure and download a client-ready PDF report.</p>
</div>
<div className="report-body">
<div className="report-form-col">
<ReportForm config={config} onChange={patch} allMuseums={allMuseums} allChannels={allChannels} />
</div>
<div className="report-preview-col">
<div className="report-preview-sticky">
<ReportPreview config={config} />
</div>
</div>
</div>
<div className="report-footer-bar">
<button
type="button"
className="report-generate-btn"
onClick={handleGenerate}
disabled={generating}
>
{generating ? (
<>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="report-spin" aria-hidden="true">
<path d="M21 12a9 9 0 1 1-6.219-8.56"/>
</svg>
Generating
</>
) : (
<>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
<polyline points="7 10 12 15 17 10"/>
<line x1="12" y1="15" x2="12" y2="3"/>
</svg>
Download PDF
</>
)}
</button>
</div>
</div>
);
}