package savedrecipe import "time" // UserSavedRecipe is a user's bookmark referencing a catalog recipe. // Display fields are populated by joining dishes + recipes. type UserSavedRecipe struct { ID string `json:"id"` UserID string `json:"-"` RecipeID string `json:"recipe_id"` SavedAt time.Time `json:"saved_at"` // Display data — joined from dishes + recipes. DishName string `json:"title"` // dish name used as display title Description *string `json:"description"` ImageURL *string `json:"image_url"` CuisineSlug *string `json:"cuisine_slug"` Tags []string `json:"tags"` Difficulty *string `json:"difficulty"` PrepTimeMin *int `json:"prep_time_min"` CookTimeMin *int `json:"cook_time_min"` Servings *int `json:"servings"` CaloriesPerServing *float64 `json:"calories_per_serving"` ProteinPerServing *float64 `json:"protein_per_serving"` FatPerServing *float64 `json:"fat_per_serving"` CarbsPerServing *float64 `json:"carbs_per_serving"` Ingredients []RecipeIngredient `json:"ingredients"` Steps []RecipeStep `json:"steps"` } // RecipeIngredient is a single ingredient row. type RecipeIngredient struct { Name string `json:"name"` Amount float64 `json:"amount"` UnitCode *string `json:"unit_code"` IsOptional bool `json:"is_optional"` } // RecipeStep is a single step row. type RecipeStep struct { StepNumber int `json:"number"` Description string `json:"description"` TimerSeconds *int `json:"timer_seconds"` } // SaveRequest is the body for POST /saved-recipes. // When recipe_id is provided, the existing catalog recipe is bookmarked. // Otherwise a new dish+recipe is created from the supplied fields. type SaveRequest struct { RecipeID string `json:"recipe_id"` // optional: bookmark existing recipe Title string `json:"title"` Description string `json:"description"` Cuisine string `json:"cuisine"` Difficulty string `json:"difficulty"` PrepTimeMin int `json:"prep_time_min"` CookTimeMin int `json:"cook_time_min"` Servings int `json:"servings"` ImageURL string `json:"image_url"` // Ingredients / Steps / Tags / Nutrition are JSONB for backward compatibility // with the recommendation flow that sends the full Gemini response. Ingredients interface{} `json:"ingredients"` Steps interface{} `json:"steps"` Tags interface{} `json:"tags"` Nutrition interface{} `json:"nutrition_per_serving"` Source string `json:"source"` } // ErrNotFound is returned when a saved recipe does not exist for the given user. var ErrNotFound = errorString("saved recipe not found") type errorString string func (e errorString) Error() string { return string(e) }