mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-10 21:32:18 +00:00
feat(export): promote Arztbericht hero card and shell on settings export
The doctor-report PDF is HealthLog's flagship "data out" path. The v1.4.16 phase B7 layout treated it as one of five equal cards in the grid; this change lifts it into a hero card at the top of `/settings/export`. The remaining four export cards (Measurements CSV, Medications CSV, Mood CSV, Full JSON backup) move under a dedicated "Weitere Export-Optionen" / "Other export options" sub-heading. The hero reuses the `hero-gradient + glow-purple` visual treatment from the Insights hero strip, so the surface feels consistent across the app. The CTA routes through the existing `<DoctorReportDialog>` + `/api/doctor-report` flow — only the framing changed. Mobile-first: at < sm the CTA falls onto its own row and clears the 44 px WCAG touch floor (`h-11 sm:h-10`); the button carries an `aria-describedby` link to the value statement so screen readers announce the framing alongside the action label.
This commit is contained in:
1 parent
5a6f921928
commit
0af77230c8
4 files changed
+312
-120
No files matched your search
@@ -2,6 +2,7 @@ import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
createMeasurementSchema,
|
||||
getUnitForType,
|
||||
listMeasurementsSchema,
|
||||
validateMeasurementRange,
|
||||
} from "../measurement";
|
||||
|
||||
@@ -173,4 +174,65 @@ describe("measurement validation", () => {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// v1.4.37 W7c — list-view "one row per day" mode for cumulative
|
||||
// types. The schema pins the optional `groupBy` enum + `dayKey`
|
||||
// shape; the route gates each branch on whether the resolved type
|
||||
// is in `CUMULATIVE_HK_TYPES`. Tests below pin the parser surface.
|
||||
describe("listMeasurementsSchema — v1.4.37 W7c groupBy + dayKey", () => {
|
||||
it("accepts an omitted groupBy + dayKey (legacy per-sample list)", () => {
|
||||
const parsed = listMeasurementsSchema.safeParse({});
|
||||
expect(parsed.success).toBe(true);
|
||||
if (parsed.success) {
|
||||
expect(parsed.data.groupBy).toBeUndefined();
|
||||
expect(parsed.data.dayKey).toBeUndefined();
|
||||
}
|
||||
});
|
||||
|
||||
it("accepts groupBy=day", () => {
|
||||
const parsed = listMeasurementsSchema.safeParse({
|
||||
type: "ACTIVITY_STEPS",
|
||||
groupBy: "day",
|
||||
});
|
||||
expect(parsed.success).toBe(true);
|
||||
if (parsed.success) {
|
||||
expect(parsed.data.groupBy).toBe("day");
|
||||
}
|
||||
});
|
||||
|
||||
it("rejects an unknown groupBy value", () => {
|
||||
const parsed = listMeasurementsSchema.safeParse({
|
||||
type: "ACTIVITY_STEPS",
|
||||
groupBy: "week",
|
||||
});
|
||||
expect(parsed.success).toBe(false);
|
||||
});
|
||||
|
||||
it("accepts a well-formed YYYY-MM-DD dayKey", () => {
|
||||
const parsed = listMeasurementsSchema.safeParse({
|
||||
type: "ACTIVITY_STEPS",
|
||||
dayKey: "2026-05-15",
|
||||
});
|
||||
expect(parsed.success).toBe(true);
|
||||
if (parsed.success) {
|
||||
expect(parsed.data.dayKey).toBe("2026-05-15");
|
||||
}
|
||||
});
|
||||
|
||||
it("rejects a malformed dayKey (DD.MM.YYYY)", () => {
|
||||
const parsed = listMeasurementsSchema.safeParse({
|
||||
type: "ACTIVITY_STEPS",
|
||||
dayKey: "15.05.2026",
|
||||
});
|
||||
expect(parsed.success).toBe(false);
|
||||
});
|
||||
|
||||
it("rejects a partial dayKey (missing day component)", () => {
|
||||
const parsed = listMeasurementsSchema.safeParse({
|
||||
type: "ACTIVITY_STEPS",
|
||||
dayKey: "2026-05",
|
||||
});
|
||||
expect(parsed.success).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -272,6 +272,22 @@ export const listMeasurementsSchema = z.object({
|
||||
// when the rollup bucket set is empty for the requested window so
|
||||
// brand-new accounts still see correct data on their first chart.
|
||||
source: z.enum(["rollup"]).optional(),
|
||||
// v1.4.37 W7c — list-view "one row per day" mode for cumulative
|
||||
// types (steps, active energy, distance, flights, daylight). When
|
||||
// `groupBy=day` is set and `type` is a cumulative HK type, the route
|
||||
// returns one synthesised row per user-TZ day with `value` = SUM
|
||||
// and `sampleCount` = number of per-sample rows behind the bucket.
|
||||
// Omitted = legacy per-sample list behaviour (iOS contract stable).
|
||||
groupBy: z.enum(["day"]).optional(),
|
||||
// v1.4.37 W7c — drill-down to per-sample rows for a single day in
|
||||
// the user's IANA timezone. Format `YYYY-MM-DD`; the route resolves
|
||||
// the day boundary against the user's `User.timezone`. Used by the
|
||||
// expandable list row to reveal the chunks that contributed to the
|
||||
// collapsed daily total.
|
||||
dayKey: z
|
||||
.string()
|
||||
.regex(/^\d{4}-\d{2}-\d{2}$/, "dayKey must be YYYY-MM-DD")
|
||||
.optional(),
|
||||
});
|
||||
|
||||
export const createBatchMeasurementSchema = z.object({
|
||||
|
||||
Reference in new issue
Block a user