Skip to content

Commit 9704404

Browse files
committed
feat: more options for data management on settings
1 parent ba0376b commit 9704404

3 files changed

Lines changed: 100 additions & 12 deletions

File tree

‎backend/routers/media.py‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2344,6 +2344,16 @@ async def manually_collect(
23442344
return {"status": "ok", "message": "Added to collection"}
23452345

23462346

2347+
@router.delete("/collect/all")
2348+
async def clear_collection(
2349+
db: AsyncSession = Depends(get_db),
2350+
current_user: User = Depends(get_current_user),
2351+
):
2352+
await db.execute(delete(Collection).where(Collection.user_id == current_user.id))
2353+
await db.commit()
2354+
return {"status": "ok"}
2355+
2356+
23472357
@router.delete("/collect")
23482358
async def manually_uncollect(
23492359
tmdb_id: int | None = Query(None),

‎backend/routers/ratings.py‎

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from fastapi import APIRouter, Depends, Query, HTTPException
55
from pydantic import BaseModel, Field
66
from sqlalchemy.ext.asyncio import AsyncSession
7-
from sqlalchemy import select, desc
7+
from sqlalchemy import select, desc, delete
88

99
from db import get_db
1010
from models.media import Media
@@ -51,6 +51,16 @@ def format_rating(rating: Rating, media: Media) -> dict:
5151
}
5252

5353

54+
@router.delete("/all")
55+
async def clear_all_ratings(
56+
db: AsyncSession = Depends(get_db),
57+
current_user: User = Depends(get_current_user),
58+
):
59+
await db.execute(delete(Rating).where(Rating.user_id == current_user.id))
60+
await db.commit()
61+
return {"status": "ok"}
62+
63+
5464
@router.post("")
5565
async def submit_rating(
5666
body: RatingIn,

‎frontend/src/pages/settings.astro‎

Lines changed: 79 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1435,18 +1435,34 @@ if (me.totp_enabled) {
14351435
<!-- Data Management -->
14361436
<section>
14371437
<h2 class="section-headline mb-4 border-b border-zinc-800 pb-2">Data Management</h2>
1438-
<div class="bg-zinc-900/40 border border-zinc-800 rounded-2xl p-6 flex flex-col md:flex-row items-center justify-between gap-6">
1439-
<div>
1440-
<h3 class="text-lg font-bold text-zinc-100 mb-1">Clear Watch History</h3>
1441-
<p class="text-zinc-400 text-sm">This will permanently delete all your watch history and progress. Your collection and ratings will not be affected.</p>
1438+
<div class="flex flex-col gap-4">
1439+
<div class="bg-zinc-900/40 border border-zinc-800 rounded-2xl p-6 flex flex-col md:flex-row items-center justify-between gap-6">
1440+
<div>
1441+
<h3 class="text-lg font-bold text-zinc-100 mb-1">Clear Watch History</h3>
1442+
<p class="text-zinc-400 text-sm">Permanently deletes all watch history and progress. Your collection and ratings will not be affected.</p>
1443+
</div>
1444+
<button type="button" id="clear-history-btn" class="px-6 py-3 bg-red-600/10 hover:bg-red-600/20 text-red-500 border border-red-500/30 font-bold rounded-xl transition-all cursor-pointer whitespace-nowrap">
1445+
Clear History
1446+
</button>
1447+
</div>
1448+
<div class="bg-zinc-900/40 border border-zinc-800 rounded-2xl p-6 flex flex-col md:flex-row items-center justify-between gap-6">
1449+
<div>
1450+
<h3 class="text-lg font-bold text-zinc-100 mb-1">Clear Ratings</h3>
1451+
<p class="text-zinc-400 text-sm">Permanently deletes all your ratings. Your watch history and collection will not be affected.</p>
1452+
</div>
1453+
<button type="button" id="clear-ratings-btn" class="px-6 py-3 bg-red-600/10 hover:bg-red-600/20 text-red-500 border border-red-500/30 font-bold rounded-xl transition-all cursor-pointer whitespace-nowrap">
1454+
Clear Ratings
1455+
</button>
1456+
</div>
1457+
<div class="bg-zinc-900/40 border border-zinc-800 rounded-2xl p-6 flex flex-col md:flex-row items-center justify-between gap-6">
1458+
<div>
1459+
<h3 class="text-lg font-bold text-zinc-100 mb-1">Clear Collection</h3>
1460+
<p class="text-zinc-400 text-sm">Permanently removes all items from your collection. Your watch history and ratings will not be affected.</p>
1461+
</div>
1462+
<button type="button" id="clear-collection-btn" class="px-6 py-3 bg-red-600/10 hover:bg-red-600/20 text-red-500 border border-red-500/30 font-bold rounded-xl transition-all cursor-pointer whitespace-nowrap">
1463+
Clear Collection
1464+
</button>
14421465
</div>
1443-
<button
1444-
type="button"
1445-
id="clear-history-btn"
1446-
class="px-6 py-3 bg-red-600/10 hover:bg-red-600/20 text-red-500 border border-red-500/30 font-bold rounded-xl transition-all cursor-pointer whitespace-nowrap"
1447-
>
1448-
Clear History
1449-
</button>
14501466
</div>
14511467
</section>
14521468

@@ -2819,6 +2835,58 @@ if (me.totp_enabled) {
28192835
};
28202836
}
28212837

2838+
const clearRatingsBtn = document.getElementById('clear-ratings-btn') as HTMLButtonElement | null;
2839+
if (clearRatingsBtn) {
2840+
clearRatingsBtn.onclick = async () => {
2841+
if (!confirm('Are you sure you want to clear all your ratings? This cannot be undone.')) return;
2842+
clearRatingsBtn.disabled = true;
2843+
clearRatingsBtn.textContent = 'Clearing...';
2844+
try {
2845+
const res = await fetch(`/api/proxy/ratings/all`, {
2846+
method: 'DELETE',
2847+
headers: { 'Authorization': `Bearer ${token}` }
2848+
});
2849+
if (res.ok) {
2850+
showMessage('Ratings cleared successfully!');
2851+
} else {
2852+
const data = await res.json();
2853+
throw new Error(data.detail || 'Failed to clear ratings');
2854+
}
2855+
} catch (e: any) {
2856+
showMessage(e.message, 'error');
2857+
} finally {
2858+
clearRatingsBtn.disabled = false;
2859+
clearRatingsBtn.textContent = 'Clear Ratings';
2860+
}
2861+
};
2862+
}
2863+
2864+
const clearCollectionBtn = document.getElementById('clear-collection-btn') as HTMLButtonElement | null;
2865+
if (clearCollectionBtn) {
2866+
clearCollectionBtn.onclick = async () => {
2867+
if (!confirm('Are you sure you want to clear your entire collection? This cannot be undone.')) return;
2868+
clearCollectionBtn.disabled = true;
2869+
clearCollectionBtn.textContent = 'Clearing...';
2870+
try {
2871+
const res = await fetch(`/api/proxy/media/collect/all`, {
2872+
method: 'DELETE',
2873+
headers: { 'Authorization': `Bearer ${token}` }
2874+
});
2875+
if (res.ok) {
2876+
showMessage('Collection cleared successfully!');
2877+
} else {
2878+
const data = await res.json();
2879+
throw new Error(data.detail || 'Failed to clear collection');
2880+
}
2881+
} catch (e: any) {
2882+
showMessage(e.message, 'error');
2883+
} finally {
2884+
clearCollectionBtn.disabled = false;
2885+
clearCollectionBtn.textContent = 'Clear Collection';
2886+
}
2887+
};
2888+
}
2889+
28222890
if (deleteBtn && modal && cancelBtn && confirmBtn) {
28232891
deleteBtn.onclick = () => {
28242892
modal.classList.remove('hidden');

0 commit comments

Comments
 (0)