mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-10 05:12:14 +00:00
The baseline step submitted what you filled in and ignored the answer. The profile write is field-independent: it stores everything that validated and reports back what it declined. Nobody read that report here, so a value the server refused vanished between the input and the next screen and you moved on believing it was saved. It now says which field did not make it, using the name that sits next to the input you typed into, and keeps going with the rest that did save. When nothing could be saved at all, the step stays put and names the field that blocked it instead of a shrug. The wording and the field-naming come from the same place the account form in Settings uses, so the same refusal reads the same wherever you hit it.
63 lines
2.5 KiB
TypeScript
63 lines
2.5 KiB
TypeScript
/**
|
|
* Naming the profile fields a save declined.
|
|
*
|
|
* `applyProfileUpdate` (`src/lib/auth/profile-update.ts`) writes every
|
|
* field that validated and reports the rest back: `data.rejectedFields`
|
|
* on a partial success, `details.issues` when nothing could be written
|
|
* at all. Both carry the schema key (`heightCm`, `gender`, ...) and a
|
|
* machine-readable code — never a sentence a person should read.
|
|
*
|
|
* Turning that key into words is the client's job, and every screen
|
|
* that writes the profile has to do it the same way, or the same
|
|
* rejection reads differently depending on where the person happened
|
|
* to be standing. This module is that one way: the settings account
|
|
* form and the onboarding baseline step both resolve the key through
|
|
* it, each passing the labels its own inputs already carry.
|
|
*/
|
|
|
|
/** A single rejected-field entry from `applyProfileUpdate`'s wire shape
|
|
* (`details.issues` on failure, `data.rejectedFields` on a partial
|
|
* success) — `path` is the schema field name, never shown verbatim. */
|
|
export interface RejectedProfileField {
|
|
path: string;
|
|
code: string;
|
|
message?: string;
|
|
}
|
|
|
|
/**
|
|
* Maps a rejected field's schema `path` to the same i18n label already
|
|
* shown next to that input on the settings account form. The server
|
|
* only ever knows the field by its schema key — naming it in a
|
|
* person-facing sentence is the client's job, using labels that already
|
|
* exist and are already localized for that form.
|
|
*
|
|
* A screen with different labels for the same fields (the onboarding
|
|
* baseline step calls them by its own names) passes its own map to
|
|
* `describeRejectedProfileField` instead.
|
|
*/
|
|
export const PROFILE_FIELD_LABEL_KEYS: Record<string, string> = {
|
|
email: "auth.email",
|
|
heightCm: "settings.height",
|
|
dateOfBirth: "settings.dateOfBirth",
|
|
gender: "settings.gender",
|
|
fullName: "settings.identity.fullName",
|
|
insurerName: "settings.identity.insurer",
|
|
insuranceNumber: "settings.identity.insuranceNumber",
|
|
};
|
|
|
|
/**
|
|
* Renders the first rejected field's label, falling back to its raw
|
|
* schema key for a field the calling screen has no input for
|
|
* (defensive — every field a form submits is expected in its map).
|
|
*/
|
|
export function describeRejectedProfileField(
|
|
fields: RejectedProfileField[] | undefined,
|
|
t: (key: string) => string,
|
|
labelKeys: Record<string, string> = PROFILE_FIELD_LABEL_KEYS,
|
|
): string | null {
|
|
const first = fields?.[0];
|
|
if (!first) return null;
|
|
const labelKey = labelKeys[first.path];
|
|
return labelKey ? t(labelKey) : first.path;
|
|
}
|