From 793df1b5cf313c2b66503550259dc75b0c88015d Mon Sep 17 00:00:00 2001 From: pyr0ball Date: Wed, 8 Apr 2026 14:35:02 -0700 Subject: [PATCH] feat: saved recipes, recipe browser, and recipe detail panel - Saved recipes: save/unsave, star rating, notes, tags, collections (migrations 018-020) - Recipe browser: domain/category browsing with pantry match badges, pagination - Recipe detail panel: full directions, ingredient checklist, swap candidates, prep notes - Grocery links: affiliate links for missing ingredients - Nutrition filters and display chips on recipe cards - Bookmark toggle persisted to saved_recipes table - Tier gates on saved recipes (paid) and collections (premium) - Browser telemetry for domain/category click tracking - Cloud compose: CLOUD_DATA_ROOT volume mount for per-user SQLite trees - manage.sh: cf-orch agent sidecar in local stack - README: updated feature list and stack description --- .env.example | 8 + .gitignore | 3 + README.md | 20 +- app/api/endpoints/inventory.py | 31 + app/api/endpoints/recipes.py | 95 ++- app/api/endpoints/saved_recipes.py | 186 ++++ app/api/routes.py | 5 +- app/db/migrations/018_saved_recipes.sql | 14 + app/db/migrations/019_recipe_collections.sql | 16 + app/db/migrations/020_browser_telemetry.sql | 13 + app/db/store.py | 266 +++++- app/models/schemas/inventory.py | 26 + app/models/schemas/recipe.py | 3 + app/models/schemas/saved_recipe.py | 44 + app/services/recipe/assembly_recipes.py | 54 +- app/services/recipe/browser_domains.py | 89 ++ app/services/recipe/grocery_links.py | 73 +- app/services/recipe/llm_recipe.py | 47 +- app/services/recipe/recipe_engine.py | 191 ++++- app/tiers.py | 3 + compose.cloud.yml | 4 + compose.override.yml | 6 + .../src/components/RecipeBrowserPanel.vue | 310 +++++++ frontend/src/components/RecipeDetailPanel.vue | 802 ++++++++++++++++++ frontend/src/components/SaveRecipeModal.vue | 294 +++++++ frontend/src/components/SavedRecipesPanel.vue | 289 +++++++ frontend/src/services/api.ts | 104 +++ frontend/src/stores/inventory.ts | 8 + frontend/src/stores/recipes.ts | 81 ++ frontend/src/stores/savedRecipes.ts | 83 ++ frontend/src/style.css | 6 +- manage.sh | 20 +- 32 files changed, 3111 insertions(+), 83 deletions(-) create mode 100644 app/api/endpoints/saved_recipes.py create mode 100644 app/db/migrations/018_saved_recipes.sql create mode 100644 app/db/migrations/019_recipe_collections.sql create mode 100644 app/db/migrations/020_browser_telemetry.sql create mode 100644 app/models/schemas/saved_recipe.py create mode 100644 app/services/recipe/browser_domains.py create mode 100644 frontend/src/components/RecipeBrowserPanel.vue create mode 100644 frontend/src/components/RecipeDetailPanel.vue create mode 100644 frontend/src/components/SaveRecipeModal.vue create mode 100644 frontend/src/components/SavedRecipesPanel.vue create mode 100644 frontend/src/stores/savedRecipes.ts diff --git a/.env.example b/.env.example index 1723c12..a57f9a5 100644 --- a/.env.example +++ b/.env.example @@ -75,3 +75,11 @@ DEMO_MODE=false # FORGEJO_API_TOKEN= # FORGEJO_REPO=Circuit-Forge/kiwi # FORGEJO_API_URL=https://git.opensourcesolarpunk.com/api/v1 + +# Affiliate links (optional — plain URLs are shown if unset) +# Amazon Associates tag (circuitforge_core.affiliates, retailer="amazon") +# AMAZON_ASSOCIATES_TAG=circuitforge-20 +# Instacart affiliate ID (circuitforge_core.affiliates, retailer="instacart") +# INSTACART_AFFILIATE_ID=circuitforge +# Walmart Impact network affiliate ID (inline, path-based redirect) +# WALMART_AFFILIATE_ID= diff --git a/.gitignore b/.gitignore index 5ffd0c8..ee14ab5 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,6 @@ data/ # Test artifacts (MagicMock sqlite files from pytest)