feat: add batch-cooking sessions (single "mega recipe" with dish sections)
New AI-generated batch-cooking flow: pick N dinners/lunches + servings, get one unified recipe covering a full prep session — merged shopping list, steps tagged per-dish (a step can advance several dishes at once, e.g. a shared oven bake), and per-dish storage (fridge/freezer) + exact day-of reheat/finishing instructions. Schema: recipes.isBatchCook, recipeSteps.appliesTo (dish names), new recipeBatchDishes table. Batch recipes are excluded from the normal /recipes list and live under their own /batch-cooking section. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
CREATE TABLE "recipe_batch_dishes" (
|
||||
"id" text PRIMARY KEY NOT NULL,
|
||||
"recipe_id" text NOT NULL,
|
||||
"name" text NOT NULL,
|
||||
"description" text,
|
||||
"order" integer DEFAULT 0 NOT NULL,
|
||||
"fridge_days" integer NOT NULL,
|
||||
"freezer_friendly" boolean DEFAULT false NOT NULL,
|
||||
"freezer_note" text,
|
||||
"day_of_instructions" text NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "recipe_steps" ADD COLUMN "applies_to" text[] DEFAULT '{}' NOT NULL;--> statement-breakpoint
|
||||
ALTER TABLE "recipes" ADD COLUMN "is_batch_cook" boolean DEFAULT false NOT NULL;--> statement-breakpoint
|
||||
ALTER TABLE "recipe_batch_dishes" ADD CONSTRAINT "recipe_batch_dishes_recipe_id_recipes_id_fk" FOREIGN KEY ("recipe_id") REFERENCES "public"."recipes"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
CREATE INDEX "recipe_batch_dishes_recipe_idx" ON "recipe_batch_dishes" USING btree ("recipe_id");--> statement-breakpoint
|
||||
CREATE INDEX "recipes_batch_cook_idx" ON "recipes" USING btree ("is_batch_cook");
|
||||
File diff suppressed because it is too large
Load Diff
@@ -204,6 +204,13 @@
|
||||
"when": 1783671061049,
|
||||
"tag": "0028_smiling_ezekiel_stane",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 29,
|
||||
"version": "7",
|
||||
"when": 1783810665920,
|
||||
"tag": "0029_tired_crystal",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -45,12 +45,14 @@ export const recipes = pgTable("recipes", {
|
||||
prepMins: integer("prep_mins"),
|
||||
cookMins: integer("cook_mins"),
|
||||
tags: text("tags").array().notNull().default([]),
|
||||
isBatchCook: boolean("is_batch_cook").notNull().default(false),
|
||||
createdAt: timestamp("created_at").notNull().defaultNow(),
|
||||
updatedAt: timestamp("updated_at").notNull().defaultNow(),
|
||||
}, (t) => [
|
||||
index("recipes_author_idx").on(t.authorId),
|
||||
index("recipes_visibility_idx").on(t.visibility),
|
||||
index("recipes_dietary_tags_gin").using("gin", t.dietaryTags),
|
||||
index("recipes_batch_cook_idx").on(t.isBatchCook),
|
||||
]);
|
||||
|
||||
export const ingredients = pgTable("ingredients", {
|
||||
@@ -82,10 +84,25 @@ export const recipeSteps = pgTable("recipe_steps", {
|
||||
instruction: text("instruction").notNull(),
|
||||
timerSeconds: integer("timer_seconds"),
|
||||
photoUrl: text("photo_url"),
|
||||
appliesTo: text("applies_to").array().notNull().default([]),
|
||||
}, (t) => [
|
||||
index("recipe_steps_recipe_idx").on(t.recipeId),
|
||||
]);
|
||||
|
||||
export const recipeBatchDishes = pgTable("recipe_batch_dishes", {
|
||||
id: text("id").primaryKey(),
|
||||
recipeId: text("recipe_id").notNull().references(() => recipes.id, { onDelete: "cascade" }),
|
||||
name: text("name").notNull(),
|
||||
description: text("description"),
|
||||
order: integer("order").notNull().default(0),
|
||||
fridgeDays: integer("fridge_days").notNull(),
|
||||
freezerFriendly: boolean("freezer_friendly").notNull().default(false),
|
||||
freezerNote: text("freezer_note"),
|
||||
dayOfInstructions: text("day_of_instructions").notNull(),
|
||||
}, (t) => [
|
||||
index("recipe_batch_dishes_recipe_idx").on(t.recipeId),
|
||||
]);
|
||||
|
||||
export const recipePhotos = pgTable("recipe_photos", {
|
||||
id: text("id").primaryKey(),
|
||||
recipeId: text("recipe_id").notNull().references(() => recipes.id, { onDelete: "cascade" }),
|
||||
@@ -128,6 +145,11 @@ export const recipesRelations = relations(recipes, ({ one, many }) => ({
|
||||
notes: many(recipeNotes),
|
||||
childVariations: many(recipeVariations, { relationName: "parent" }),
|
||||
parentVariations: many(recipeVariations, { relationName: "child" }),
|
||||
batchDishes: many(recipeBatchDishes),
|
||||
}));
|
||||
|
||||
export const recipeBatchDishesRelations = relations(recipeBatchDishes, ({ one }) => ({
|
||||
recipe: one(recipes, { fields: [recipeBatchDishes.recipeId], references: [recipes.id] }),
|
||||
}));
|
||||
|
||||
export const recipeIngredientsRelations = relations(recipeIngredients, ({ one }) => ({
|
||||
|
||||
Reference in New Issue
Block a user