From ce6d6be95a28663f52e7717a329982c90ba80060 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Thu, 14 May 2026 11:57:02 +0200 Subject: [PATCH] feat(doctor-report): PDF timestamps render in user displayTimezone MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit v1.4.25 W7 phase 3.3 — closes proposal §3 symptom 8. renderDoctorReportPdfBytes now accepts an optional userTz in its DoctorReportRenderOptions. The PDF route resolves the patient's stored timezone via the v1.4.25 W7 resolver and threads it through makeFormatters so date/time rows render in the user's zone. A US patient generating a report at home in Boston now sees Eastern time rows, not Berlin. Existing Berlin-region users see no behaviour change — the User.timezone default is Europe/Berlin and the formatter falls back when userTz is empty. Client-side caller (settings dialog) is untouched in this commit — a follow-up wave wires the auth-context value through the dialog component. --- src/app/api/doctor-report/pdf/route.ts | 8 ++++++-- src/lib/doctor-report-pdf-core.ts | 12 ++++++++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/app/api/doctor-report/pdf/route.ts b/src/app/api/doctor-report/pdf/route.ts index 5b8588998..665067735 100644 --- a/src/app/api/doctor-report/pdf/route.ts +++ b/src/app/api/doctor-report/pdf/route.ts @@ -14,6 +14,7 @@ import { renderDoctorReportPdfBytes } from "@/lib/doctor-report-pdf-core"; import { getServerTranslator } from "@/lib/i18n/server-translator"; import { parseLocaleFromAcceptLanguage } from "@/lib/format-locale"; import { locales, type Locale } from "@/lib/i18n/config"; +import { resolveUserTimezone } from "@/lib/tz/resolver"; /** * Server-rendered PDF doctor report. @@ -58,10 +59,13 @@ export const POST = apiHandler(async (request: NextRequest) => { } } - const data = await collectDoctorReportData(user.id, range, { practiceName }); + const [data, userTz] = await Promise.all([ + collectDoctorReportData(user.id, range, { practiceName }), + resolveUserTimezone(user.id), + ]); const { t } = getServerTranslator(locale); - const pdfBytes = renderDoctorReportPdfBytes(data, { t, locale }); + const pdfBytes = renderDoctorReportPdfBytes(data, { t, locale, userTz }); await auditLog("doctor-report.pdf.generate", { userId: user.id, diff --git a/src/lib/doctor-report-pdf-core.ts b/src/lib/doctor-report-pdf-core.ts index 02b32e440..21826fa09 100644 --- a/src/lib/doctor-report-pdf-core.ts +++ b/src/lib/doctor-report-pdf-core.ts @@ -26,6 +26,14 @@ export interface DoctorReportRenderOptions { * tests; defaults to `new Date()`. */ now?: Date; + /** + * v1.4.25 W7 — per-user display timezone. When omitted the report + * renders timestamps in Europe/Berlin (legacy contract). Server + * callers pass `resolveUserTimezone(user.id)`; client callers pass + * the value from auth context so a US user's PDF carries + * Eastern-time rows even when generated in the browser. + */ + userTz?: string; } /** @@ -137,8 +145,8 @@ export function buildDoctorReportPdfDocument( data: DoctorReportData, options: DoctorReportRenderOptions, ): jsPDF { - const { t, locale, now = new Date() } = options; - const formatters = makeFormatters(locale); + const { t, locale, now = new Date(), userTz } = options; + const formatters = makeFormatters(locale, userTz); const num = (value: number, decimals = 1) => formatters.number(value, decimals); const fmtDate = (iso: string) => formatters.date(iso);