feat: add cookbook nesting and auto-filtering capabilities
Enables cookbooks to include other cookbooks and automatically organize content based on tags. This allows users to create hierarchical cookbook structures and maintain collections that automatically update as new content is added. Key features: - Cookbook nesting: Include child cookbooks within parent cookbooks - Auto-filtering by cookbook tags: Automatically include cookbooks matching specified tags - Auto-filtering by recipe tags: Automatically add recipes matching specified tags - Enhanced cookbook management UI with tag support - Comprehensive test coverage for new functionality Database schema updated with CookbookInclusion and CookbookTag tables. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -28,6 +28,7 @@ model User {
|
||||
sharedRecipes RecipeShare[]
|
||||
refreshTokens RefreshToken[]
|
||||
verificationTokens VerificationToken[]
|
||||
mealPlans MealPlan[]
|
||||
|
||||
@@index([email])
|
||||
@@index([provider, providerId])
|
||||
@@ -103,6 +104,7 @@ model Recipe {
|
||||
tags RecipeTag[]
|
||||
cookbooks CookbookRecipe[]
|
||||
sharedWith RecipeShare[]
|
||||
meals MealRecipe[]
|
||||
|
||||
@@index([title])
|
||||
@@index([cuisine])
|
||||
@@ -185,9 +187,10 @@ model RecipeImage {
|
||||
}
|
||||
|
||||
model Tag {
|
||||
id String @id @default(cuid())
|
||||
name String @unique
|
||||
recipes RecipeTag[]
|
||||
id String @id @default(cuid())
|
||||
name String @unique
|
||||
recipes RecipeTag[]
|
||||
cookbooks CookbookTag[]
|
||||
}
|
||||
|
||||
model RecipeTag {
|
||||
@@ -202,6 +205,18 @@ model RecipeTag {
|
||||
@@index([tagId])
|
||||
}
|
||||
|
||||
model CookbookTag {
|
||||
cookbookId String
|
||||
tagId String
|
||||
|
||||
cookbook Cookbook @relation(fields: [cookbookId], references: [id], onDelete: Cascade)
|
||||
tag Tag @relation(fields: [tagId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@id([cookbookId, tagId])
|
||||
@@index([cookbookId])
|
||||
@@index([tagId])
|
||||
}
|
||||
|
||||
model RecipeShare {
|
||||
id String @id @default(cuid())
|
||||
recipeId String
|
||||
@@ -217,18 +232,22 @@ model RecipeShare {
|
||||
}
|
||||
|
||||
model Cookbook {
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
description String?
|
||||
coverImageUrl String?
|
||||
userId String? // Cookbook owner
|
||||
autoFilterCategories String[] @default([]) // Auto-add recipes matching these categories
|
||||
autoFilterTags String[] @default([]) // Auto-add recipes matching these tags
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
description String?
|
||||
coverImageUrl String?
|
||||
userId String? // Cookbook owner
|
||||
autoFilterCategories String[] @default([]) // Auto-add recipes matching these categories
|
||||
autoFilterTags String[] @default([]) // Auto-add recipes matching these tags
|
||||
autoFilterCookbookTags String[] @default([]) // Auto-add cookbooks matching these tags
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
user User? @relation(fields: [userId], references: [id], onDelete: SetNull)
|
||||
recipes CookbookRecipe[]
|
||||
user User? @relation(fields: [userId], references: [id], onDelete: SetNull)
|
||||
recipes CookbookRecipe[]
|
||||
tags CookbookTag[]
|
||||
includedCookbooks CookbookInclusion[] @relation("ParentCookbook")
|
||||
includedIn CookbookInclusion[] @relation("ChildCookbook")
|
||||
|
||||
@@index([name])
|
||||
@@index([userId])
|
||||
@@ -247,3 +266,70 @@ model CookbookRecipe {
|
||||
@@index([cookbookId])
|
||||
@@index([recipeId])
|
||||
}
|
||||
|
||||
model CookbookInclusion {
|
||||
id String @id @default(cuid())
|
||||
parentCookbookId String
|
||||
childCookbookId String
|
||||
addedAt DateTime @default(now())
|
||||
|
||||
parentCookbook Cookbook @relation("ParentCookbook", fields: [parentCookbookId], references: [id], onDelete: Cascade)
|
||||
childCookbook Cookbook @relation("ChildCookbook", fields: [childCookbookId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@unique([parentCookbookId, childCookbookId])
|
||||
@@index([parentCookbookId])
|
||||
@@index([childCookbookId])
|
||||
}
|
||||
|
||||
model MealPlan {
|
||||
id String @id @default(cuid())
|
||||
userId String?
|
||||
date DateTime // The day this meal plan is for (stored at midnight UTC)
|
||||
notes String? @db.Text // Optional notes for the entire day
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
user User? @relation(fields: [userId], references: [id], onDelete: SetNull)
|
||||
meals Meal[]
|
||||
|
||||
@@unique([userId, date]) // One meal plan per user per day
|
||||
@@index([userId])
|
||||
@@index([date])
|
||||
@@index([userId, date])
|
||||
}
|
||||
|
||||
model Meal {
|
||||
id String @id @default(cuid())
|
||||
mealPlanId String
|
||||
mealType MealType
|
||||
order Int // Order within the same meal type (for multi-recipe meals)
|
||||
servings Int? // Servings for this specific meal (can override recipe default)
|
||||
notes String? @db.Text // Meal-specific notes
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
mealPlan MealPlan @relation(fields: [mealPlanId], references: [id], onDelete: Cascade)
|
||||
recipe MealRecipe?
|
||||
|
||||
@@index([mealPlanId])
|
||||
@@index([mealType])
|
||||
}
|
||||
|
||||
model MealRecipe {
|
||||
mealId String @id
|
||||
recipeId String
|
||||
|
||||
meal Meal @relation(fields: [mealId], references: [id], onDelete: Cascade)
|
||||
recipe Recipe @relation(fields: [recipeId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@index([recipeId])
|
||||
}
|
||||
|
||||
enum MealType {
|
||||
BREAKFAST
|
||||
LUNCH
|
||||
DINNER
|
||||
SNACK
|
||||
DESSERT
|
||||
OTHER
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user