S5: onboarding → minne + smaksignaler (user_stated)
This commit is contained in:
@@ -6,6 +6,7 @@ import {
|
||||
pgTable,
|
||||
text,
|
||||
timestamp,
|
||||
uniqueIndex,
|
||||
uuid,
|
||||
} from "drizzle-orm/pg-core";
|
||||
import {
|
||||
@@ -47,6 +48,7 @@ export const memoryItems = pgTable(
|
||||
index("memory_items_user_idx").on(t.userId, t.kind),
|
||||
index("memory_items_household_idx").on(t.householdId, t.kind),
|
||||
index("memory_items_key_idx").on(t.key),
|
||||
uniqueIndex("memory_items_user_key_unique").on(t.userId, t.key),
|
||||
],
|
||||
);
|
||||
|
||||
@@ -59,13 +61,17 @@ export const tasteSignals = pgTable(
|
||||
.notNull()
|
||||
.references(() => users.id, { onDelete: "cascade" }),
|
||||
axis: tasteAxisEnum("axis").notNull(),
|
||||
target: text("target"),
|
||||
direction: doublePrecision("direction").notNull(),
|
||||
strength: doublePrecision("strength").notNull().default(0.5),
|
||||
origin: signalOriginEnum("origin").notNull(),
|
||||
refRecipeId: uuid("ref_recipe_id").references(() => recipes.id, { onDelete: "set null" }),
|
||||
createdAt: createdAt(),
|
||||
},
|
||||
(t) => [index("taste_signals_user_idx").on(t.userId, t.axis)],
|
||||
(t) => [
|
||||
index("taste_signals_user_idx").on(t.userId, t.axis),
|
||||
uniqueIndex("taste_signals_user_axis_target_unique").on(t.userId, t.axis, t.target),
|
||||
],
|
||||
);
|
||||
|
||||
/** Food Memory (spec §39): långsiktiga matminnen kopplade till högtider och betyg. */
|
||||
|
||||
@@ -320,6 +320,21 @@ export function renderMemorySummary(
|
||||
if (typeof v.spicePreference === "string") {
|
||||
return `Spice preference: ${String(v.spicePreference)}`;
|
||||
}
|
||||
if (typeof v.goal === "string") {
|
||||
return `Goal: ${String(v.goal)}`;
|
||||
}
|
||||
if (typeof v.primaryGoal === "string") {
|
||||
return `Primary goal: ${String(v.primaryGoal)}`;
|
||||
}
|
||||
if (typeof v.allergen === "string") {
|
||||
return `Allergy: ${String(v.allergen)}`;
|
||||
}
|
||||
if (typeof v.avoidIngredientId === "string") {
|
||||
return `Avoids ingredient: ${String(v.avoidIngredientId)}`;
|
||||
}
|
||||
if (typeof v.spiceLevelMax === "number") {
|
||||
return `Max spice level: ${v.spiceLevelMax}`;
|
||||
}
|
||||
}
|
||||
return item.summarySv;
|
||||
}
|
||||
|
||||
@@ -599,6 +599,8 @@ export interface TasteSignal {
|
||||
direction: number;
|
||||
strength: number;
|
||||
origin: SignalOrigin;
|
||||
/** Axis-specific target, e.g. a cuisine slug or canonical ingredient id. */
|
||||
target?: string;
|
||||
refRecipeId?: string;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
@@ -477,6 +477,8 @@ export const TASTE_AXES = [
|
||||
"sweetness",
|
||||
"herbs",
|
||||
"umami",
|
||||
"cuisine",
|
||||
"ingredient_avoid",
|
||||
] as const;
|
||||
export type TasteAxis = (typeof TASTE_AXES)[number];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user