feat: post-signup onboarding wizard (dietary/allergen prefs, notifications, skippable) (v0.77.0)
New accounts land on a 3-step wizard after signup — welcome, dietary preferences & allergens, notification opt-in — skippable at every step, shown once via a users.onboardingCompletedAt gate in the (app) layout. Existing accounts are backfilled as already-onboarded. Also gives the allergen-preferences step a real write path: user_allergens previously only had a GDPR-export read. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
ALTER TABLE "users" ADD COLUMN "onboarding_completed_at" timestamp;--> statement-breakpoint
|
||||
ALTER TABLE "users" ADD COLUMN "dietary_tags" jsonb DEFAULT '{}'::jsonb;--> statement-breakpoint
|
||||
-- Backfill existing accounts as already onboarded — the wizard is only for
|
||||
-- users who sign up after this migration, not everyone who predates it.
|
||||
UPDATE "users" SET "onboarding_completed_at" = "created_at" WHERE "onboarding_completed_at" IS NULL;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -449,6 +449,13 @@
|
||||
"when": 1784885406472,
|
||||
"tag": "0063_sharp_stone_men",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 64,
|
||||
"version": "7",
|
||||
"when": 1784885885146,
|
||||
"tag": "0064_thick_drax",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -4,11 +4,25 @@ import {
|
||||
timestamp,
|
||||
boolean,
|
||||
integer,
|
||||
jsonb,
|
||||
pgEnum,
|
||||
primaryKey,
|
||||
} from "drizzle-orm/pg-core";
|
||||
import { relations } from "drizzle-orm";
|
||||
|
||||
// Mirrors recipes.ts's DietaryTags shape — duplicated rather than imported to
|
||||
// avoid a users.ts <-> recipes.ts circular import (recipes.ts already
|
||||
// references users.ts for the authorId FK).
|
||||
export type UserDietaryTags = {
|
||||
vegan?: boolean;
|
||||
vegetarian?: boolean;
|
||||
glutenFree?: boolean;
|
||||
dairyFree?: boolean;
|
||||
nutFree?: boolean;
|
||||
halal?: boolean;
|
||||
kosher?: boolean;
|
||||
};
|
||||
|
||||
export const userRoleEnum = pgEnum("user_role", ["user", "moderator", "admin"]);
|
||||
export const tierEnum = pgEnum("tier", ["free", "pro", "family"]);
|
||||
export const unitPrefEnum = pgEnum("unit_pref", ["metric", "imperial"]);
|
||||
@@ -55,6 +69,11 @@ export const users = pgTable("users", {
|
||||
unitPref: unitPrefEnum("unit_pref").notNull().default("metric"),
|
||||
locale: text("locale").notNull().default("en"),
|
||||
twoFactorEnabled: boolean("two_factor_enabled").notNull().default(false),
|
||||
// Null = hasn't been through (or finished/skipped) the post-signup
|
||||
// onboarding wizard yet. Set once, either on completion or on skip — both
|
||||
// count as "done," so the wizard never shows twice.
|
||||
onboardingCompletedAt: timestamp("onboarding_completed_at"),
|
||||
dietaryTags: jsonb("dietary_tags").$type<UserDietaryTags>().default({}),
|
||||
createdAt: timestamp("created_at").notNull().defaultNow(),
|
||||
updatedAt: timestamp("updated_at").notNull().defaultNow(),
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user