mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-11 13:52:20 +00:00
fix(onboarding): derive the module map only from a questionnaire that was finished
The only gate on the derivation was Q1 being set, so one answer was enough to turn the whole questionnaire into a module map. Two ways that landed wrong and they compound. Somebody answers who the record is for, closes the tab, and later finishes the legacy four-step wizard, which posts to this same endpoint and knows nothing about these questions: the five unanswered ones each read as their conservative default, eleven surfaces are written off, and the once-only marker makes it unrepeatable. Or any client patches one answer and posts the confirm straight back. The gate now asks whether every question has an answer or a deliberate pass. A half-answered flow derives nothing, is not stamped complete, and keeps its confirm step pending, so finishing the questions later still does the work. The legacy arm is untouched: a caller with no setup row still answers the fixed acknowledgement it always did.
This commit is contained in:
1 parent
7a1f3fe32d
commit
cc6e6ca221
6 files changed
+165
-12
No files matched your search
@@ -11,6 +11,7 @@ import {
|
||||
mergeDerivedModulePreferences,
|
||||
} from "@/lib/modules/registry";
|
||||
import {
|
||||
everyOnboardingQuestionSettled,
|
||||
readHeldUnitPreferences,
|
||||
resolveOnboardingSteps,
|
||||
type HeldUnitPreferences,
|
||||
@@ -32,8 +33,9 @@ import { onboardingCompleteSchema } from "@/lib/validations/onboarding";
|
||||
* flow, and the two live side by side rather than one replacing the other.
|
||||
* The legacy half is unchanged: the optional profile fields, the completion
|
||||
* stamp on `User.onboardingCompletedAt`, and the cleared pending cookie. The
|
||||
* needs half runs only for a record that actually answered the questions
|
||||
* (`needs.recordTarget` is set) and is what turns the answers into a module
|
||||
* needs half runs only for a record that actually FINISHED the questions —
|
||||
* every one of them answered or deliberately passed — and is what turns them
|
||||
* into a module
|
||||
* map — ONCE, guarded by `OnboardingRecord.modulesDerivedAt`, because a
|
||||
* re-derivation would re-apply the questionnaire over decisions taken in
|
||||
* Settings since. `POST /api/onboarding/restart` clears that marker when the
|
||||
@@ -129,6 +131,21 @@ async function completeNeedsFlow(userId: string, held: HeldUnitPreferences) {
|
||||
});
|
||||
return toOnboardingStateDto(record, held);
|
||||
}
|
||||
if (!everyOnboardingQuestionSettled(steps)) {
|
||||
// The confirm screen is the only caller that may derive, and it is reached
|
||||
// only once every question has an answer or a deliberate pass. Q1 alone is
|
||||
// not enough: an abandoned flow would have every remaining question read
|
||||
// as its conservative default, switch off the surfaces that default
|
||||
// implies, and latch the result so nobody could re-derive it. This route
|
||||
// is also the LEGACY wizard's endpoint, and that wizard knows nothing
|
||||
// about these questions — so a person who answered Q1, left, and later
|
||||
// finished the old wizard must not have a module map derived for them.
|
||||
annotate({
|
||||
action: { name: "onboarding.needs.complete" },
|
||||
meta: { outcome: "incomplete" },
|
||||
});
|
||||
return toOnboardingStateDto(record, held);
|
||||
}
|
||||
|
||||
let derived = false;
|
||||
if (record.modulesDerivedAt === null) {
|
||||
|
||||
Reference in new issue
Block a user