Add small pic to recipes and update the scehma.sql

This commit is contained in:
dvirlabs 2025-12-05 11:08:40 +02:00
parent 984e10682b
commit 02e4a5d7fa
8 changed files with 51 additions and 18 deletions

View File

@ -55,7 +55,7 @@ def list_recipes_db() -> List[Dict[str, Any]]:
cur.execute(
"""
SELECT id, name, meal_type, time_minutes,
tags, ingredients, steps
tags, ingredients, steps, image
FROM recipes
ORDER BY id
"""
@ -68,7 +68,7 @@ def list_recipes_db() -> List[Dict[str, Any]]:
def update_recipe_db(recipe_id: int, recipe_data: Dict[str, Any]) -> Optional[Dict[str, Any]]:
"""
עדכון מתכון קיים לפי id.
recipe_data: name, meal_type, time_minutes, tags, ingredients, steps
recipe_data: name, meal_type, time_minutes, tags, ingredients, steps, image
"""
conn = get_conn()
try:
@ -81,9 +81,10 @@ def update_recipe_db(recipe_id: int, recipe_data: Dict[str, Any]) -> Optional[Di
time_minutes = %s,
tags = %s,
ingredients = %s,
steps = %s
steps = %s,
image = %s
WHERE id = %s
RETURNING id, name, meal_type, time_minutes, tags, ingredients, steps
RETURNING id, name, meal_type, time_minutes, tags, ingredients, steps, image
""",
(
recipe_data["name"],
@ -92,6 +93,7 @@ def update_recipe_db(recipe_id: int, recipe_data: Dict[str, Any]) -> Optional[Di
json.dumps(recipe_data.get("tags", [])),
json.dumps(recipe_data.get("ingredients", [])),
json.dumps(recipe_data.get("steps", [])),
recipe_data.get("image"),
recipe_id,
),
)
@ -129,9 +131,9 @@ def create_recipe_db(recipe_data: Dict[str, Any]) -> Dict[str, Any]:
with conn.cursor() as cur:
cur.execute(
"""
INSERT INTO recipes (name, meal_type, time_minutes, tags, ingredients, steps)
VALUES (%s, %s, %s, %s, %s, %s)
RETURNING id, name, meal_type, time_minutes, tags, ingredients, steps
INSERT INTO recipes (name, meal_type, time_minutes, tags, ingredients, steps, image)
VALUES (%s, %s, %s, %s, %s, %s, %s)
RETURNING id, name, meal_type, time_minutes, tags, ingredients, steps, image
""",
(
recipe_data["name"],
@ -140,6 +142,7 @@ def create_recipe_db(recipe_data: Dict[str, Any]) -> Dict[str, Any]:
json.dumps(recipe_data.get("tags", [])),
json.dumps(recipe_data.get("ingredients", [])),
json.dumps(recipe_data.get("steps", [])),
recipe_data.get("image"),
),
)
row = cur.fetchone()

View File

@ -24,6 +24,7 @@ class RecipeBase(BaseModel):
tags: List[str] = []
ingredients: List[str] = []
steps: List[str] = []
image: Optional[str] = None # Base64-encoded image or image URL
class RecipeCreate(RecipeBase):
@ -65,6 +66,7 @@ def list_recipes():
tags=r["tags"] or [],
ingredients=r["ingredients"] or [],
steps=r["steps"] or [],
image=r.get("image"),
)
for r in rows
]
@ -86,6 +88,7 @@ def create_recipe(recipe_in: RecipeCreate):
tags=row["tags"] or [],
ingredients=row["ingredients"] or [],
steps=row["steps"] or [],
image=row.get("image"),
)
@app.put("/recipes/{recipe_id}", response_model=Recipe)
@ -105,6 +108,7 @@ def update_recipe(recipe_id: int, recipe_in: RecipeUpdate):
tags=row["tags"] or [],
ingredients=row["ingredients"] or [],
steps=row["steps"] or [],
image=row.get("image"),
)
@ -136,6 +140,7 @@ def random_recipe(
tags=r["tags"] or [],
ingredients=r["ingredients"] or [],
steps=r["steps"] or [],
image=r.get("image"),
)
for r in rows
]

View File

@ -7,7 +7,8 @@ CREATE TABLE IF NOT EXISTS recipes (
tags JSONB NOT NULL DEFAULT '[]', -- ["מהיר", "בריא"]
ingredients JSONB NOT NULL DEFAULT '[]', -- ["ביצה", "עגבניה", "מלח"]
steps JSONB NOT NULL DEFAULT '[]' -- ["לחתוך", "לבשל", ...]
steps JSONB NOT NULL DEFAULT '[]', -- ["לחתוך", "לבשל", ...]
image TEXT -- Base64-encoded image or image URL
);
-- Optional: index for filters

View File

@ -0,0 +1,25 @@
<svg width="200" height="200" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
<!-- Background -->
<rect width="200" height="200" fill="#e0e0e0"/>
<!-- Food plate icon -->
<circle cx="100" cy="100" r="70" fill="#c0c0c0" stroke="#999" stroke-width="2"/>
<!-- Fork -->
<g transform="translate(60, 80)">
<line x1="0" y1="0" x2="0" y2="35" stroke="#666" stroke-width="2" stroke-linecap="round"/>
<line x1="-8" y1="5" x2="-8" y2="35" stroke="#666" stroke-width="2" stroke-linecap="round"/>
<line x1="8" y1="5" x2="8" y2="35" stroke="#666" stroke-width="2" stroke-linecap="round"/>
<circle cx="0" cy="0" r="3" fill="#666"/>
</g>
<!-- Knife -->
<g transform="translate(130, 80)">
<line x1="0" y1="0" x2="0" y2="35" stroke="#666" stroke-width="2" stroke-linecap="round"/>
<polygon points="0,0 -6,8 6,8" fill="#666"/>
<circle cx="0" cy="2" r="3" fill="#999"/>
</g>
<!-- "No Image" text -->
<text x="100" y="155" font-family="Arial, sans-serif" font-size="14" fill="#666" text-anchor="middle" font-weight="bold">No Image</text>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -1,3 +1,5 @@
import placeholderImage from "../assets/placeholder.svg";
function RecipeDetails({ recipe, onEditClick, onDeleteClick, onShowDeleteModal }) {
if (!recipe) {
return (
@ -14,11 +16,9 @@ function RecipeDetails({ recipe, onEditClick, onDeleteClick, onShowDeleteModal }
return (
<section className="panel recipe-card">
{/* Recipe Image */}
{recipe.image && (
<div className="recipe-image-container">
<img src={recipe.image} alt={recipe.name} className="recipe-image" />
</div>
)}
<div className="recipe-image-container">
<img src={recipe.image || placeholderImage} alt={recipe.name} className="recipe-image" />
</div>
<header className="recipe-header">
<div>

View File

@ -1,4 +1,5 @@
import { useState } from "react";
import placeholderImage from "../assets/placeholder.svg";
function RecipeSearchList({
allRecipes,
@ -181,11 +182,9 @@ function RecipeSearchList({
}
onClick={() => onSelect(r)}
>
{r.image && (
<div className="recipe-list-image">
<img src={r.image} alt={r.name} />
</div>
)}
<div className="recipe-list-image">
<img src={r.image || placeholderImage} alt={r.name} />
</div>
<div className="recipe-list-main">
<div className="recipe-list-name">{r.name}</div>
<div className="recipe-list-meta">