Files
HealthLog/src/lib/onboarding/tour-progress.ts
T
Marc-André Bombeck 8ff3b51ba3 refactor: remove dead exports and an unused prop
Drop the health-score card's inert onAskCoach prop and its now-dead
pass-through from the hero strip, the buildDashboardBands re-export that no
caller reached through the snapshot module, an unused tour-progress status
type, and an orphan service-worker cache helper.
2026-06-18 22:42:27 +02:00

52 lines
1.9 KiB
TypeScript

/**
* v1.18.6 — resumable module-tour progress.
*
* Pure data + Zod schema for the `users.onboarding_tour_progress_json`
* column. Kept DOM-free + free of Prisma imports so vitest (node env)
* and the OpenAPI registry can both consume it.
*
* The coarse `onboardingTourCompleted` boolean stays the launcher's
* auto-launch gate; this object is the fine-grained resume point so a
* mid-tour reload reopens at the right module. `null` = the user has
* not started the module tour.
*/
import { z } from "zod/v4";
/** Terminal / running status of the module tour. */
export const TOUR_PROGRESS_STATUSES = [
"in_progress",
"completed",
"skipped",
] as const;
/**
* The persisted progress shape. `lastStopId` seeds the resume index;
* `completedStopIds` is informational (analytics + future per-module
* "already seen" hints). Stop ids are bounded strings — the client
* sends a known stop id from `buildTourStops()`, but the server does
* not couple to that closed set (a future stop must not 422 an older
* client and vice-versa), so the field is a length-capped string array
* rather than a hard enum.
*/
export const tourProgressSchema = z.object({
lastStopId: z.string().min(1).max(64).nullable(),
completedStopIds: z.array(z.string().min(1).max(64)).max(64).default([]),
status: z.enum(TOUR_PROGRESS_STATUSES),
updatedAt: z.iso.datetime(),
});
export type TourProgress = z.infer<typeof tourProgressSchema>;
/**
* Parse a value read from the JSON column. Returns `null` for a null /
* absent column AND for any malformed payload — the resume point is a
* non-critical convenience, so a corrupt blob degrades to "start from
* the top" rather than throwing into the auth/me read path.
*/
export function parseTourProgress(value: unknown): TourProgress | null {
if (value == null) return null;
const parsed = tourProgressSchema.safeParse(value);
return parsed.success ? parsed.data : null;
}