feat(doctor-report): PDF timestamps render in user displayTimezone

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.
This commit is contained in:
Marc-André Bombeck committed 2026-05-14 11:57:02 +02:00
1 parent 72e64c816d
commit ce6d6be95a
2 files changed
+16 -4

No files matched your search

+6 -2
View File
@@ -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,
+10 -2
View File
@@ -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);