From 37739699f9ffc46fc42c54216e470ee8169a9d86 Mon Sep 17 00:00:00 2001 From: Arnaud Date: Sun, 19 Jul 2026 21:01:13 +0200 Subject: [PATCH] =?UTF-8?q?fix:=20Admin=20Insights=20actual=20crash=20?= =?UTF-8?q?=E2=80=94=20function=20props=20across=20server/client=20boundar?= =?UTF-8?q?y=20(v0.55.3)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Server logs (thanks to the user pulling them) showed the real error: "Functions cannot be passed directly to Client Components" — the server component page was passing formatShortDate/formatMonth as a `formatDate` prop into TimeSeriesChart ("use client"). Functions aren't serializable across the RSC boundary; the two previous fixes (query hardening, Promise.allSettled) were real improvements but not the actual cause of the reported crash. TimeSeriesChart now takes a plain `dateFormat: "day" | "month"` string and formats internally — BarChart was never affected (its formatValue prop is only ever used via its own default, never passed from the page). Co-Authored-By: Claude Sonnet 5 --- CHANGELOG.md | 5 +++++ apps/web/app/admin/insights/page.tsx | 9 ++------- .../admin/charts/time-series-chart.tsx | 18 ++++++++++++++++-- apps/web/lib/changelog.ts | 9 ++++++++- apps/web/package.json | 2 +- package.json | 2 +- 6 files changed, 33 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3da3e1d..d4ea0d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ All notable changes to Epicure are documented here. This file is mirrored in-app at `/changelog` (and in the admin dashboard) via `apps/web/lib/changelog.ts` — update both together. +## 0.55.3 — 2026-07-19 19:00 + +### Fixed +- Admin Insights page's actual crash, found from server logs: the page (a server component) was passing plain formatter functions as props into its chart components (client components) — React can't serialize functions across that boundary. Replaced with a plain string prop ("day" vs "month") formatted inside the client component instead. + ## 0.55.2 — 2026-07-19 18:20 ### Fixed diff --git a/apps/web/app/admin/insights/page.tsx b/apps/web/app/admin/insights/page.tsx index b1aaddc..394fb24 100644 --- a/apps/web/app/admin/insights/page.tsx +++ b/apps/web/app/admin/insights/page.tsx @@ -34,11 +34,6 @@ function lastNMonths(n: number): string[] { return out; } -function formatMonth(m: string) { - const [y, mo] = m.split("-"); - return new Date(Number(y), Number(mo) - 1, 1).toLocaleDateString(undefined, { month: "short", year: "2-digit" }); -} - export default async function AdminInsightsPage() { const since = new Date(); since.setDate(since.getDate() - DAYS); @@ -128,7 +123,7 @@ export default async function AdminInsightsPage() { Daily, last {DAYS} days - + @@ -168,7 +163,7 @@ export default async function AdminInsightsPage() { Monthly total across all users, last 6 months - + diff --git a/apps/web/components/admin/charts/time-series-chart.tsx b/apps/web/components/admin/charts/time-series-chart.tsx index 3e99ea2..ccc870e 100644 --- a/apps/web/components/admin/charts/time-series-chart.tsx +++ b/apps/web/components/admin/charts/time-series-chart.tsx @@ -5,22 +5,36 @@ import { cn } from "@/lib/utils"; export type TimeSeriesPoint = { date: string; value: number }; +/** "day" expects "YYYY-MM-DD", "month" expects "YYYY-MM" — kept as a plain + * string enum rather than a formatter function prop, since this chart is + * a "use client" component and functions passed from a server component + * parent aren't serializable across that boundary (React throws: "Functions + * cannot be passed directly to Client Components"). */ +function formatPoint(date: string, kind: "day" | "month"): string { + if (kind === "month") { + const [y, m] = date.split("-"); + return new Date(Number(y), Number(m) - 1, 1).toLocaleDateString(undefined, { month: "short", year: "2-digit" }); + } + return new Date(`${date}T00:00:00Z`).toLocaleDateString(undefined, { month: "short", day: "numeric", timeZone: "UTC" }); +} + /** Single-series area/line chart with a hover crosshair+tooltip and a * table-view fallback — hand-rolled SVG, no charting lib. */ export function TimeSeriesChart({ data, formatValue = (n) => String(n), - formatDate = (d) => d, + dateFormat = "day", height = 200, }: { data: TimeSeriesPoint[]; formatValue?: (n: number) => string; - formatDate?: (d: string) => string; + dateFormat?: "day" | "month"; height?: number; }) { const [hovered, setHovered] = useState(null); const [showTable, setShowTable] = useState(false); const svgRef = useRef(null); + const formatDate = (d: string) => formatPoint(d, dateFormat); const width = 600; const paddingBottom = 24; diff --git a/apps/web/lib/changelog.ts b/apps/web/lib/changelog.ts index e389612..fe941dd 100644 --- a/apps/web/lib/changelog.ts +++ b/apps/web/lib/changelog.ts @@ -1,5 +1,5 @@ // Mirrors CHANGELOG.md at the repo root — update both together. -export const APP_VERSION = "0.55.2"; +export const APP_VERSION = "0.55.3"; export type ChangelogEntry = { version: string; @@ -11,6 +11,13 @@ export type ChangelogEntry = { }; export const CHANGELOG: ChangelogEntry[] = [ + { + version: "0.55.3", + date: "2026-07-19 19:00", + fixed: [ + "Admin Insights page's actual crash, found from server logs: the page (a server component) was passing plain formatter functions as props into its chart components (client components) — React can't serialize functions across that boundary. Replaced with a plain string prop (\"day\" vs \"month\") formatted inside the client component instead.", + ], + }, { version: "0.55.2", date: "2026-07-19 18:20", diff --git a/apps/web/package.json b/apps/web/package.json index f085295..b563544 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -1,6 +1,6 @@ { "name": "@epicure/web", - "version": "0.55.2", + "version": "0.55.3", "private": true, "scripts": { "dev": "next dev", diff --git a/package.json b/package.json index fbe58df..9147aa6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "epicure", - "version": "0.55.2", + "version": "0.55.3", "private": true, "scripts": { "dev": "pnpm --filter web dev",