feat(reports-overhaul): sales + operational + custom reports, templates, schedules, exports
End-to-end reports build covering Phases 1, 2, 5, 6, 7 of Initiative 1 in docs/launch-readiness.md. Phases 3 (Marketing) + 4 (Financial) remain deferred per the gap audit at the bottom of that doc. Highlights: - Sales performance report: 7 KPI tiles, pipeline funnel + stage velocity + win-rate-over-time + source conversion + rep leaderboard charts, deal-heat section, 5 detail tables, stage / lead-cat / outcome filters. - Operational report: 7 KPIs, 7 charts (heatmap, status mix, tenancy churn, tenure histogram, signing box plot, occupancy by area, docs in pipeline), 4 tables. Module-OFF banner when tenancies disabled. - Custom (ad-hoc) builder v1: 4 entities (clients, interests, berths, tenancies), column-whitelist composer, date filter, CSV download, save-as-template. Registry-only extension path for the remaining 6 entities documented at src/lib/reports/custom/registry.ts. - Templates: load / modify / save / save-as on Sales / Operational / Custom. ?templateId= URL deep-link hydration via useRef guard. Active-template badge clears when the user drives view-state via wrapped setters; raw setters used on template apply so the badge survives. - Scheduled runs: BullMQ poll fires due schedules, mints report_runs, renders, optionally emails. Recipients optional (zero-recipient schedules archive without sending). PDF-only output for v1. Schedule dialog re-mounts via key prop on schedule.id transitions to avoid setState-in-effect reset patterns. - Server-side PDF endpoint + shared payload renderer (lib/pdf/reports/payload-report.tsx) so client + scheduler share one rendering path. - Shared currency formatter (lib/reports/format-currency.ts) consolidates 5 duplicated formatMoney helpers; fixes hardcoded 'USD' in detail tables; pre-formats money rows so PDF export (which strips column.format callbacks at the JSON boundary) renders consistently with CSV / XLSX. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -7,7 +7,12 @@ import { errorResponse } from '@/lib/errors';
|
||||
import { createReportTemplate, listReportTemplates } from '@/lib/services/report-templates.service';
|
||||
|
||||
const createBodySchema = z.object({
|
||||
kind: z.enum(['dashboard', 'clients', 'berths', 'interests']),
|
||||
// 'sales' + 'operational' don't go through /api/v1/reports/generate;
|
||||
// they're standalone report pages with their own routes. The config
|
||||
// for these kinds is a thin view-state snapshot (date range +
|
||||
// filters) that the report client applies on load. 'custom' is the
|
||||
// ad-hoc composer's saved config — entity + columns + filter.
|
||||
kind: z.enum(['dashboard', 'clients', 'berths', 'interests', 'sales', 'operational', 'custom']),
|
||||
name: z.string().min(1).max(120),
|
||||
description: z.string().max(400).nullable().optional(),
|
||||
// Config is the raw discriminated-union payload; the
|
||||
|
||||
Reference in New Issue
Block a user