From 2c3b2d7a08a142c712e5ae38751e1e366e1b0c90 Mon Sep 17 00:00:00 2001 From: tanyar09 Date: Thu, 20 Nov 2025 14:06:07 -0500 Subject: [PATCH] feat: Enhance reported photos handling with detailed confirmation dialogs This commit improves the user experience in the ReportedPhotos component by adding specific confirmation dialogs for photo removal decisions. It ensures users are aware of the consequences of their actions, particularly when permanently deleting photos. Additionally, the backend has been updated to handle deletion of tag linkages associated with photos, ensuring data integrity. Documentation has been updated to reflect these changes. --- frontend/src/pages/ReportedPhotos.tsx | 93 +++++++++++++++++---------- src/web/api/reported_photos.py | 13 ++-- 2 files changed, 68 insertions(+), 38 deletions(-) diff --git a/frontend/src/pages/ReportedPhotos.tsx b/frontend/src/pages/ReportedPhotos.tsx index 8766b0a..a02f1b0 100644 --- a/frontend/src/pages/ReportedPhotos.tsx +++ b/frontend/src/pages/ReportedPhotos.tsx @@ -86,16 +86,36 @@ export default function ReportedPhotos() { return } - if ( - !confirm( - `Submit ${decisionsList.length} decision(s)?\n\nThis will ${ - decisionsList.filter((d) => d.decision === 'remove').length - } remove photo(s) and ${ - decisionsList.filter((d) => d.decision === 'keep').length - } keep photo(s).` - ) - ) { - return + // Check if there are any 'remove' decisions + const removeDecisions = decisionsList.filter((d) => d.decision === 'remove') + const keepDecisions = decisionsList.filter((d) => d.decision === 'keep') + + // Show specific confirmation for removal + if (removeDecisions.length > 0) { + const removeCount = removeDecisions.length + const photoDetails = removeDecisions + .map((d) => { + const reported = reportedPhotos.find((p) => p.id === d.id) + return reported?.photo_filename || `Photo #${reported?.photo_id || d.id}` + }) + .join('\n - ') + + const confirmMessage = `⚠️ WARNING: You are about to PERMANENTLY REMOVE ${removeCount} photo(s):\n\n - ${photoDetails}\n\nThis will:\n • Delete the photo(s) from the database\n • Delete all faces detected in the photo(s)\n • Delete all encodings related to those faces\n\nThis action CANNOT be undone!\n\nAre you sure you want to proceed?` + + if (!confirm(confirmMessage)) { + return + } + } + + // Show general confirmation if there are also 'keep' decisions + if (keepDecisions.length > 0) { + const confirmMessage = `Submit ${decisionsList.length} decision(s)?\n\nThis will ${ + removeDecisions.length + } remove photo(s) and ${keepDecisions.length} keep photo(s).` + + if (!confirm(confirmMessage)) { + return + } } setSubmitting(true) @@ -220,6 +240,7 @@ export default function ReportedPhotos() { {reportedPhotos.map((reported) => { const isReviewed = reported.status === 'reviewed' const isDismissed = reported.status === 'dismissed' + const canMakeDecision = !isDismissed && (reported.status === 'pending' || reported.status === 'reviewed') return ( -
- - -
+ {canMakeDecision ? ( +
+ + +
+ ) : ( + - + )} {isReviewed || isDismissed ? ( diff --git a/src/web/api/reported_photos.py b/src/web/api/reported_photos.py index 73e9fc1..7955f82 100644 --- a/src/web/api/reported_photos.py +++ b/src/web/api/reported_photos.py @@ -11,7 +11,7 @@ from sqlalchemy import text from sqlalchemy.orm import Session from src.web.db.session import get_auth_db, get_db -from src.web.db.models import Photo +from src.web.db.models import Photo, PhotoTagLinkage from src.web.api.users import get_current_admin_user router = APIRouter(prefix="/reported-photos", tags=["reported-photos"]) @@ -224,14 +224,19 @@ def review_reported_photos( kept_count += 1 # Count as kept since we couldn't remove it continue - # Delete the photo (cascade will delete faces, tags, etc.) + # Delete tag linkages for this photo + main_db.query(PhotoTagLinkage).filter( + PhotoTagLinkage.photo_id == photo.id + ).delete(synchronize_session=False) + + # Delete the photo (cascade will delete faces, etc.) main_db.delete(photo) main_db.commit() - # Update status in auth database + # Update status in auth database to dismissed auth_db.execute(text(""" UPDATE inappropriate_photo_reports - SET status = 'reviewed', + SET status = 'dismissed', reviewed_at = :reviewed_at, reviewed_by = :reviewed_by, review_notes = :review_notes