From d6dd07e0bdd24734cc859bba701421e64593bc69 Mon Sep 17 00:00:00 2001 From: Mattias Tall Date: Tue, 26 May 2026 11:07:32 +0200 Subject: [PATCH] Add delete button to taste profile tags in Stats MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Backend: DELETE /stats/taste/{tag} removes the row from user_tag_affinity - API: deleteTasteTag(tag) helper - Stats UI: × button on each tag chip, faint by default, full opacity on hover; invalidates stats query so the tag disappears immediately Co-Authored-By: Claude Sonnet 4.6 --- backend/routers/stats.py | 17 +++++++++++++++-- frontend/src/api/index.js | 1 + frontend/src/pages/Stats.jsx | 20 +++++++++++++++++--- 3 files changed, 33 insertions(+), 5 deletions(-) diff --git a/backend/routers/stats.py b/backend/routers/stats.py index 39db272..fdbe428 100644 --- a/backend/routers/stats.py +++ b/backend/routers/stats.py @@ -1,10 +1,10 @@ -from fastapi import APIRouter, Depends +from fastapi import APIRouter, Depends, HTTPException from sqlalchemy.orm import Session from sqlalchemy import text from ..auth_utils import get_current_user from ..database import get_db -from ..models import User +from ..models import User, UserTagAffinity router = APIRouter() @@ -142,3 +142,16 @@ def get_stats( "top_categories": [dict(r) for r in top_categories], "taste_profile": [dict(r) for r in taste_profile], } + + +@router.delete("/taste/{tag}", status_code=204) +def delete_taste_tag( + tag: str, + db: Session = Depends(get_db), + current_user: User = Depends(get_current_user), +): + row = db.query(UserTagAffinity).filter_by(user_id=current_user.id, tag=tag).first() + if not row: + raise HTTPException(status_code=404, detail="Tag not found") + db.delete(row) + db.commit() diff --git a/frontend/src/api/index.js b/frontend/src/api/index.js index 588022f..d0466fa 100644 --- a/frontend/src/api/index.js +++ b/frontend/src/api/index.js @@ -129,6 +129,7 @@ export const getCommunityShelf = () => api.get("/discovery/community"); // Stats export const getStats = () => api.get("/stats"); +export const deleteTasteTag = (tag) => api.delete(`/stats/taste/${encodeURIComponent(tag)}`); // Admin export const getAdminUsers = () => api.get("/admin/users"); diff --git a/frontend/src/pages/Stats.jsx b/frontend/src/pages/Stats.jsx index 8775eea..6d46c8c 100644 --- a/frontend/src/pages/Stats.jsx +++ b/frontend/src/pages/Stats.jsx @@ -1,5 +1,5 @@ -import { useQuery } from "@tanstack/react-query"; -import { getStats } from "../api"; +import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; +import { getStats, deleteTasteTag } from "../api"; import { Link } from "react-router-dom"; function fmt(seconds) { @@ -22,12 +22,18 @@ function StatCard({ label, value, sub }) { } export default function Stats() { + const qc = useQueryClient(); const { data, isLoading } = useQuery({ queryKey: ["stats"], queryFn: () => getStats().then(r => r.data), staleTime: 5 * 60_000, }); + const deleteTag = useMutation({ + mutationFn: (tag) => deleteTasteTag(tag), + onSuccess: () => qc.invalidateQueries({ queryKey: ["stats"] }), + }); + if (isLoading) { return (
@@ -178,7 +184,7 @@ export default function Stats() { {t.tag} + ); })}