Mapping made_by to displayname

This commit is contained in:
dvirlabs 2025-12-08 09:08:32 +02:00
parent 53ca792988
commit fa5ba578bb
10 changed files with 72 additions and 30 deletions

View File

@ -54,10 +54,12 @@ def list_recipes_db() -> List[Dict[str, Any]]:
with conn.cursor() as cur:
cur.execute(
"""
SELECT id, name, meal_type, time_minutes,
tags, ingredients, steps, image, made_by, user_id
FROM recipes
ORDER BY id
SELECT r.id, r.name, r.meal_type, r.time_minutes,
r.tags, r.ingredients, r.steps, r.image, r.made_by, r.user_id,
u.display_name as owner_display_name
FROM recipes r
LEFT JOIN users u ON r.user_id = u.id
ORDER BY r.id
"""
)
rows = cur.fetchall()
@ -163,19 +165,21 @@ def get_recipes_by_filters_db(
conn = get_conn()
try:
query = """
SELECT id, name, meal_type, time_minutes,
tags, ingredients, steps, image, made_by, user_id
FROM recipes
SELECT r.id, r.name, r.meal_type, r.time_minutes,
r.tags, r.ingredients, r.steps, r.image, r.made_by, r.user_id,
u.display_name as owner_display_name
FROM recipes r
LEFT JOIN users u ON r.user_id = u.id
WHERE 1=1
"""
params: List = []
if meal_type:
query += " AND meal_type = %s"
query += " AND r.meal_type = %s"
params.append(meal_type.lower())
if max_time:
query += " AND time_minutes <= %s"
query += " AND r.time_minutes <= %s"
params.append(max_time)
with conn.cursor() as cur:

View File

@ -51,6 +51,7 @@ class RecipeCreate(RecipeBase):
class Recipe(RecipeBase):
id: int
user_id: Optional[int] = None # Recipe owner ID
owner_display_name: Optional[str] = None # Owner's display name for filtering
class RecipeUpdate(RecipeBase):
@ -132,6 +133,7 @@ def list_recipes():
steps=r["steps"] or [],
image=r.get("image"),
user_id=r.get("user_id"),
owner_display_name=r.get("owner_display_name"),
)
for r in rows
]
@ -157,6 +159,7 @@ def create_recipe(recipe_in: RecipeCreate, current_user: dict = Depends(get_curr
steps=row["steps"] or [],
image=row.get("image"),
user_id=row.get("user_id"),
owner_display_name=current_user.get("display_name"),
)
@app.put("/recipes/{recipe_id}", response_model=Recipe)
@ -192,6 +195,7 @@ def update_recipe(recipe_id: int, recipe_in: RecipeUpdate, current_user: dict =
steps=row["steps"] or [],
image=row.get("image"),
user_id=row.get("user_id"),
owner_display_name=current_user.get("display_name"),
)
@ -239,6 +243,7 @@ def random_recipe(
steps=r["steps"] or [],
image=r.get("image"),
user_id=r.get("user_id"),
owner_display_name=r.get("owner_display_name"),
)
for r in rows
]
@ -288,6 +293,17 @@ def register(user: UserRegister):
detail="האימייל כבר רשום במערכת"
)
# Check if display_name already exists
print(f"[REGISTER] Checking if display_name exists...")
from user_db_utils import get_user_by_display_name
existing_display_name = get_user_by_display_name(user.display_name)
if existing_display_name:
print(f"[REGISTER] Display name already exists")
raise HTTPException(
status_code=400,
detail="שם התצוגה כבר קיים במערכת"
)
# Hash password and create user
print(f"[REGISTER] Hashing password...")
password_hash = hash_password(user.password)

View File

@ -6,7 +6,7 @@ CREATE TABLE IF NOT EXISTS users (
password_hash TEXT NOT NULL,
first_name TEXT,
last_name TEXT,
display_name TEXT NOT NULL,
display_name TEXT UNIQUE NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

View File

@ -84,3 +84,19 @@ def get_user_by_id(user_id: int):
finally:
cur.close()
conn.close()
def get_user_by_display_name(display_name: str):
"""Get user by display name"""
conn = get_db_connection()
cur = conn.cursor(cursor_factory=RealDictCursor)
try:
cur.execute(
"SELECT id, username, email, display_name, created_at FROM users WHERE display_name = %s",
(display_name,)
)
user = cur.fetchone()
return dict(user) if user else None
finally:
cur.close()
conn.close()

View File

@ -27,7 +27,7 @@ function App() {
const [filterMealType, setFilterMealType] = useState("");
const [filterMaxTime, setFilterMaxTime] = useState("");
const [filterTags, setFilterTags] = useState([]);
const [filterMadeBy, setFilterMadeBy] = useState("");
const [filterOwner, setFilterOwner] = useState("");
// Random recipe filters
const [mealTypeFilter, setMealTypeFilter] = useState("");
@ -126,8 +126,8 @@ function App() {
}
}
// Filter by made_by
if (filterMadeBy && (!recipe.made_by || recipe.made_by !== filterMadeBy)) {
// Filter by made_by (username)
if (filterOwner && (!recipe.made_by || recipe.made_by !== filterOwner)) {
return false;
}
@ -343,8 +343,8 @@ function App() {
onMaxTimeChange={setFilterMaxTime}
filterTags={filterTags}
onTagsChange={setFilterTags}
filterMadeBy={filterMadeBy}
onMadeByChange={setFilterMadeBy}
filterOwner={filterOwner}
onOwnerChange={setFilterOwner}
/>
</section>

View File

@ -35,8 +35,8 @@ function RecipeDetails({ recipe, onEditClick, onDeleteClick, onShowDeleteModal,
<p className="recipe-subtitle">
{translateMealType(recipe.meal_type)} · {recipe.time_minutes} דקות הכנה
</p>
{recipe.made_by && (
<h4 className="recipe-made-by">המתכון של: {recipe.made_by}</h4>
{(recipe.owner_display_name || recipe.made_by) && (
<h4 className="recipe-made-by">המתכון של: {recipe.owner_display_name || recipe.made_by}</h4>
)}
</div>
<div className="pill-row">

View File

@ -14,8 +14,8 @@ function RecipeSearchList({
onMaxTimeChange,
filterTags,
onTagsChange,
filterMadeBy,
onMadeByChange,
filterOwner,
onOwnerChange,
}) {
const [expandFilters, setExpandFilters] = useState(false);
@ -27,8 +27,14 @@ function RecipeSearchList({
// Extract unique meal types from ALL recipes (not filtered)
const mealTypes = Array.from(new Set(allRecipes.map((r) => r.meal_type))).sort();
// Extract unique made_by from ALL recipes (not filtered)
const allMadeBy = Array.from(new Set(allRecipes.map((r) => r.made_by).filter(Boolean))).sort();
// Extract unique made_by (username) from ALL recipes and map to display names
const madeByMap = new Map();
allRecipes.forEach((r) => {
if (r.made_by && r.owner_display_name) {
madeByMap.set(r.made_by, r.owner_display_name);
}
});
const allMadeBy = Array.from(madeByMap.keys()).sort();
// Extract max time for slider from ALL recipes (not filtered) - add 10 to make it comfortable to select max time recipes
const maxTimeInRecipes = Math.max(...allRecipes.map((r) => r.time_minutes), 0);
@ -47,10 +53,10 @@ function RecipeSearchList({
onMealTypeChange("");
onMaxTimeChange("");
onTagsChange([]);
onMadeByChange("");
onOwnerChange("");
};
const hasActiveFilters = searchQuery || filterMealType || filterMaxTime || filterTags.length > 0 || filterMadeBy;
const hasActiveFilters = searchQuery || filterMealType || filterMaxTime || filterTags.length > 0 || filterOwner;
return (
<section className="panel secondary recipe-search-list">
@ -165,18 +171,18 @@ function RecipeSearchList({
<label className="filter-label">המתכונים של:</label>
<div className="filter-options">
<button
className={`filter-btn ${filterMadeBy === "" ? "active" : ""}`}
onClick={() => onMadeByChange("")}
className={`filter-btn ${filterOwner === "" ? "active" : ""}`}
onClick={() => onOwnerChange("")}
>
הכל
</button>
{allMadeBy.map((person) => (
{allMadeBy.map((madeBy) => (
<button
key={person}
className={`filter-btn ${filterMadeBy === person ? "active" : ""}`}
onClick={() => onMadeByChange(person)}
key={madeBy}
className={`filter-btn ${filterOwner === madeBy ? "active" : ""}`}
onClick={() => onOwnerChange(madeBy)}
>
{person}
{madeByMap.get(madeBy) || madeBy}
</button>
))}
</div>