Add admin features, responsive UI, user reviews, visit stats, and channel-colored markers
- Admin: video management with Google Maps match status, manual restaurant mapping, restaurant remap on name change - Admin: user management tab with favorites/reviews detail - Admin: channel deletion fix for IDs with slashes - Frontend: responsive mobile layout (map top, list bottom, 2-row header) - Frontend: channel-colored map markers with legend - Frontend: my reviews list, favorites toggle, visit counter overlay - Frontend: force light mode for dark theme devices - Backend: visit tracking (site_visits table), user reviews endpoint - Backend: bulk transcript/extract streaming, geocode key fixes - Nginx config for production deployment Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -95,3 +95,74 @@ def list_my_reviews(
|
||||
):
|
||||
"""List current user's reviews."""
|
||||
return review.get_user_reviews(current_user["sub"], limit=limit, offset=offset)
|
||||
|
||||
|
||||
# --- Favorites ---
|
||||
|
||||
@router.get("/restaurants/{restaurant_id}/favorite")
|
||||
def get_favorite_status(
|
||||
restaurant_id: str,
|
||||
current_user: dict = Depends(get_current_user),
|
||||
):
|
||||
"""Check if current user has favorited this restaurant."""
|
||||
from core.db import conn
|
||||
with conn() as c:
|
||||
cur = c.cursor()
|
||||
cur.execute(
|
||||
"SELECT id FROM user_favorites WHERE user_id = :u AND restaurant_id = :r",
|
||||
{"u": current_user["sub"], "r": restaurant_id},
|
||||
)
|
||||
return {"favorited": cur.fetchone() is not None}
|
||||
|
||||
|
||||
@router.post("/restaurants/{restaurant_id}/favorite")
|
||||
def toggle_favorite(
|
||||
restaurant_id: str,
|
||||
current_user: dict = Depends(get_current_user),
|
||||
):
|
||||
"""Toggle favorite. Returns new state."""
|
||||
from core.db import conn
|
||||
user_id = current_user["sub"]
|
||||
with conn() as c:
|
||||
cur = c.cursor()
|
||||
cur.execute(
|
||||
"SELECT id FROM user_favorites WHERE user_id = :u AND restaurant_id = :r",
|
||||
{"u": user_id, "r": restaurant_id},
|
||||
)
|
||||
row = cur.fetchone()
|
||||
if row:
|
||||
cur.execute("DELETE FROM user_favorites WHERE id = :fid", {"fid": row[0]})
|
||||
return {"favorited": False}
|
||||
else:
|
||||
cur.execute(
|
||||
"INSERT INTO user_favorites (user_id, restaurant_id) VALUES (:u, :r)",
|
||||
{"u": user_id, "r": restaurant_id},
|
||||
)
|
||||
return {"favorited": True}
|
||||
|
||||
|
||||
@router.get("/users/me/favorites")
|
||||
def list_my_favorites(
|
||||
current_user: dict = Depends(get_current_user),
|
||||
):
|
||||
"""List current user's favorite restaurants."""
|
||||
from core.db import conn
|
||||
sql = """
|
||||
SELECT r.id, r.name, r.address, r.region, r.latitude, r.longitude,
|
||||
r.cuisine_type, r.price_range, r.google_place_id,
|
||||
r.business_status, r.rating, r.rating_count,
|
||||
f.created_at
|
||||
FROM user_favorites f
|
||||
JOIN restaurants r ON r.id = f.restaurant_id
|
||||
WHERE f.user_id = :u
|
||||
ORDER BY f.created_at DESC
|
||||
"""
|
||||
with conn() as c:
|
||||
cur = c.cursor()
|
||||
cur.execute(sql, {"u": current_user["sub"]})
|
||||
cols = [d[0].lower() for d in cur.description]
|
||||
rows = [dict(zip(cols, row)) for row in cur.fetchall()]
|
||||
for r in rows:
|
||||
if r.get("created_at"):
|
||||
r["created_at"] = r["created_at"].isoformat()
|
||||
return rows
|
||||
|
||||
Reference in New Issue
Block a user