From 866b910ae9863924a7a299fb4fc432a2238e2e19 Mon Sep 17 00:00:00 2001 From: Matt Date: Mon, 25 May 2026 16:40:28 +0200 Subject: [PATCH] feat(reports-p7): subtitle override field in dashboard builder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - DashboardReportBuilder gains an optional Subtitle input alongside Title. Persisted in the config payload sent to /api/v1/reports/runs + /api/v1/reports/generate + threaded through the preview payload's useMemo dep list so live preview reflects the override. - Cover-page brand picker (admin-only) — deferred. Today the renderer uses the active port's brand kit; cross-port branding swap needs a permission gate, port-pick UI, and a renderer override and is queued for a follow-up. Subtitle alone covers the most common ad-hoc need (custom cover-page subtext like "Board pack — March 2026"). Verified: tsc clean, 1493/1493 vitest. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../builders/dashboard-report-builder.tsx | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/components/reports/builders/dashboard-report-builder.tsx b/src/components/reports/builders/dashboard-report-builder.tsx index 10708809..e344b5b0 100644 --- a/src/components/reports/builders/dashboard-report-builder.tsx +++ b/src/components/reports/builders/dashboard-report-builder.tsx @@ -50,6 +50,7 @@ interface Props { export function DashboardReportBuilder({ portSlug, initialFrom, initialTo }: Props) { const router = useRouter(); const [title, setTitle] = useState(`Report - ${new Date().toLocaleDateString(undefined)}`); + const [subtitle, setSubtitle] = useState(''); const [selected, setSelected] = useState( PDF_DASHBOARD_WIDGETS.map((w) => w.id), ); @@ -69,11 +70,12 @@ export function DashboardReportBuilder({ portSlug, initialFrom, initialTo }: Pro config: { kind: 'dashboard' as const, widgetIds: selected, + ...(subtitle.trim() ? { subtitle: subtitle.trim() } : {}), ...(dateFrom ? { dateFrom } : {}), ...(dateTo ? { dateTo } : {}), }, }), - [title, selected, dateFrom, dateTo], + [title, subtitle, selected, dateFrom, dateTo], ); function toggle(id: PdfDashboardWidgetId) { @@ -102,6 +104,7 @@ export function DashboardReportBuilder({ portSlug, initialFrom, initialTo }: Pro config: { kind: 'dashboard', widgetIds: selected, + ...(subtitle.trim() ? { subtitle: subtitle.trim() } : {}), ...(dateFrom ? { dateFrom } : {}), ...(dateTo ? { dateTo } : {}), }, @@ -142,6 +145,8 @@ export function DashboardReportBuilder({ portSlug, initialFrom, initialTo }: Pro config: { kind: 'dashboard', widgetIds: selected, + title: title.trim() || 'Report', + ...(subtitle.trim() ? { subtitle: subtitle.trim() } : {}), ...(dateFrom ? { dateFrom } : {}), ...(dateTo ? { dateTo } : {}), }, @@ -193,6 +198,18 @@ export function DashboardReportBuilder({ portSlug, initialFrom, initialTo }: Pro className="max-w-md" /> +
+ + setSubtitle(e.target.value)} + className="max-w-md" + placeholder="e.g. Board pack — March 2026" + /> +