FAS 1b: Progressive onboarding (3-layer flow)
- Add PROGRESSIVE_ONBOARDING feature flag to KNOWN_FLAGS - Add onboarding_step column to users table (a/b/c) - New backend endpoints: - GET /v1/onboarding/status – check current step + feature flag - POST /v1/onboarding/quick-start – complete Step A (goal + precision) - POST /v1/onboarding/complete-b – complete Step B (diet, allergens, household) - POST /v1/onboarding/complete-c – complete Step C (health profile) - POST /v1/onboarding/skip – GDPR-friendly skip - Refactor mobile onboarding screen into 3 progressive layers - Update auth store with onboardingStep state - Update tab layout to only block on Step A - Update registration to set onboardingStep='a' - Add i18n keys for Step B/C titles across all 12 locales - Add Zod validation schemas (quickStartInputSchema, onboardingStatusSchema) - Add tests for validation and feature flag - Preserve existing /v1/me/onboarding for backward compatibility - Migration: 0004_progressive_onboarding.sql
This commit is contained in:
@@ -39,6 +39,8 @@ export const users = pgTable(
|
||||
locale: text("locale").notNull().default("sv-SE"),
|
||||
precisionMode: precisionModeEnum("precision_mode").notNull().default("simple"),
|
||||
onboardingCompleted: boolean("onboarding_completed").notNull().default(false),
|
||||
/** Progressive onboarding step: a=immediate value, b=after first value, c=completed (spec FAS 1b). */
|
||||
onboardingStep: text("onboarding_step").notNull().default("a"),
|
||||
deletedAt: timestamp("deleted_at", { withTimezone: true }),
|
||||
createdAt: createdAt(),
|
||||
updatedAt: updatedAt(),
|
||||
|
||||
@@ -19,6 +19,7 @@ export const KNOWN_FLAGS = {
|
||||
FOOD_MEMORIES: "food_memories",
|
||||
VOICE_INPUT: "voice_input",
|
||||
AI_RERANK: "ai_rerank",
|
||||
PROGRESSIVE_ONBOARDING: "progressive_onboarding",
|
||||
} as const;
|
||||
export type KnownFlagKey = (typeof KNOWN_FLAGS)[keyof typeof KNOWN_FLAGS];
|
||||
|
||||
|
||||
@@ -83,6 +83,7 @@ export interface User {
|
||||
locale: string;
|
||||
precisionMode: PrecisionMode;
|
||||
onboardingCompleted: boolean;
|
||||
onboardingStep: "a" | "b" | "c";
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
@@ -70,3 +70,20 @@ export const onboardingInputSchema = z.object({
|
||||
.default({ kind: "skip" }),
|
||||
});
|
||||
export type OnboardingInput = z.infer<typeof onboardingInputSchema>;
|
||||
|
||||
/** Progressive onboarding Step A – minimal info for immediate value (FAS 1b). */
|
||||
export const quickStartInputSchema = z.object({
|
||||
primaryGoal: z.enum(GOAL_TYPES).optional(),
|
||||
precisionMode: z.enum(PRECISION_MODES).default("simple"),
|
||||
});
|
||||
export type QuickStartInput = z.infer<typeof quickStartInputSchema>;
|
||||
|
||||
/** Progressive onboarding status response (FAS 1b). */
|
||||
export const onboardingStatusSchema = z.object({
|
||||
step: z.enum(["a", "b", "c"]),
|
||||
onboardingCompleted: z.boolean(),
|
||||
hasHousehold: z.boolean(),
|
||||
hasHealthProfile: z.boolean(),
|
||||
hasPreferences: z.boolean(),
|
||||
});
|
||||
export type OnboardingStatus = z.infer<typeof onboardingStatusSchema>;
|
||||
|
||||
Reference in New Issue
Block a user