mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-10 13:22:14 +00:00
fix(onboarding): leave a trail for the three settings columns the flow writes
The setup flow writes a record's module map, its glucose unit and its unit preference. All three have a dedicated settings route that records an audit row, and the flow recorded none, so "why did my modules change" had no answer in the activity panel. Restarting the flow did write a row, which made the gap look like an oversight rather than a decision, because it was one. Same event names as the dedicated routes, since it is the same column and the panel should group them. The module row names the keys that actually moved and the domains kept for their content.
This commit is contained in:
1 parent
07f1b6ce92
commit
b10a3ef22e
5 files changed
+82
-5
No files matched your search
@@ -27,9 +27,11 @@ import { apiHandler, requireAuth } from "@/lib/api-handler";
|
||||
import {
|
||||
apiError,
|
||||
apiSuccess,
|
||||
getClientIp,
|
||||
returnAllZodIssues,
|
||||
safeJson,
|
||||
} from "@/lib/api-response";
|
||||
import { auditLog } from "@/lib/auth/audit";
|
||||
import { prisma, toJson } from "@/lib/db";
|
||||
import { annotate } from "@/lib/logging/context";
|
||||
import { applyOnboardingAnswer } from "@/lib/onboarding/needs-apply";
|
||||
@@ -146,6 +148,32 @@ export const PATCH = apiHandler(async (request: NextRequest) => {
|
||||
unitPreference: unitData.unitPreference ?? user.unitPreference,
|
||||
});
|
||||
|
||||
// Both columns have a dedicated settings route that writes an audit row, and
|
||||
// the trail should not depend on which surface the person changed them from.
|
||||
// Same event names, so the activity panel groups them.
|
||||
if (unitData.glucoseUnit !== undefined) {
|
||||
await auditLog("user.glucose-unit.update", {
|
||||
userId: user.id,
|
||||
ipAddress: getClientIp(request),
|
||||
details: {
|
||||
previous: user.glucoseUnit,
|
||||
next: unitData.glucoseUnit,
|
||||
source: "onboarding",
|
||||
},
|
||||
});
|
||||
}
|
||||
if (unitData.unitPreference !== undefined) {
|
||||
await auditLog("user.unit-preference.update", {
|
||||
userId: user.id,
|
||||
ipAddress: getClientIp(request),
|
||||
details: {
|
||||
previous: user.unitPreference,
|
||||
next: unitData.unitPreference,
|
||||
source: "onboarding",
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
annotate({
|
||||
action: { name: "onboarding.answer.save" },
|
||||
meta: {
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
import { prisma, toJson } from "@/lib/db";
|
||||
import { apiHandler, requireAuth } from "@/lib/api-handler";
|
||||
import { annotate } from "@/lib/logging/context";
|
||||
import { apiSuccess, apiError, safeJson } from "@/lib/api-response";
|
||||
import {
|
||||
apiSuccess,
|
||||
apiError,
|
||||
getClientIp,
|
||||
safeJson,
|
||||
} from "@/lib/api-response";
|
||||
import { auditLog } from "@/lib/auth/audit";
|
||||
import { setOnboardingPendingCookie } from "@/lib/auth/session";
|
||||
import { normalisePrefs } from "@/lib/modules/gate";
|
||||
import { modulesHoldingRecordData } from "@/lib/modules/domain-data";
|
||||
@@ -98,6 +104,7 @@ export const POST = apiHandler(async (request: NextRequest) => {
|
||||
const onboarding = await completeNeedsFlow(
|
||||
user.id,
|
||||
readHeldUnitPreferences(user),
|
||||
request,
|
||||
);
|
||||
|
||||
return apiSuccess({ completed: true, ...(onboarding ? { onboarding } : {}) });
|
||||
@@ -111,7 +118,11 @@ export const POST = apiHandler(async (request: NextRequest) => {
|
||||
* needs flow — a legacy wizard completion then answers exactly the body it
|
||||
* always did.
|
||||
*/
|
||||
async function completeNeedsFlow(userId: string, held: HeldUnitPreferences) {
|
||||
async function completeNeedsFlow(
|
||||
userId: string,
|
||||
held: HeldUnitPreferences,
|
||||
request: NextRequest,
|
||||
) {
|
||||
const record = await prisma.onboardingRecord.findUnique({
|
||||
where: { userId },
|
||||
select: ONBOARDING_RECORD_SELECT,
|
||||
@@ -195,6 +206,22 @@ async function completeNeedsFlow(userId: string, held: HeldUnitPreferences) {
|
||||
...(defaults.cycleTracking ? { cycleTrackingEnabled: true } : {}),
|
||||
});
|
||||
|
||||
// The same trail the dedicated modules route writes, under the same event
|
||||
// name, because it is the same column and the activity panel is where
|
||||
// somebody goes to ask why their modules changed. The flow was writing
|
||||
// three audited settings columns and leaving no trail at all, which made
|
||||
// the answer to that question "nothing did it".
|
||||
const stored = normalisePrefs(row?.modulePreferencesJson);
|
||||
const changed = [
|
||||
...Object.keys(merged).filter((key) => merged[key] !== stored[key]),
|
||||
...(defaults.cycleTracking ? ["cycleTrackingEnabled"] : []),
|
||||
];
|
||||
await auditLog("user.modules.update", {
|
||||
userId,
|
||||
ipAddress: getClientIp(request),
|
||||
details: { changed, keptForData: [...holdsData], source: "onboarding" },
|
||||
});
|
||||
|
||||
keptForData = [...holdsData];
|
||||
derived = true;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user