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.
This commit is contained in:
@@ -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 (
|
||||
<tr
|
||||
key={reported.id}
|
||||
@@ -292,30 +313,34 @@ export default function ReportedPhotos() {
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap">
|
||||
<div className="flex items-center gap-4">
|
||||
<label className="flex items-center gap-2 cursor-pointer">
|
||||
<input
|
||||
type="radio"
|
||||
name={`decision-${reported.id}`}
|
||||
value="keep"
|
||||
checked={decisions[reported.id] === 'keep'}
|
||||
onChange={() => handleDecisionChange(reported.id, 'keep')}
|
||||
className="w-4 h-4 text-green-600 focus:ring-green-500"
|
||||
/>
|
||||
<span className="text-sm text-gray-700">Keep</span>
|
||||
</label>
|
||||
<label className="flex items-center gap-2 cursor-pointer">
|
||||
<input
|
||||
type="radio"
|
||||
name={`decision-${reported.id}`}
|
||||
value="remove"
|
||||
checked={decisions[reported.id] === 'remove'}
|
||||
onChange={() => handleDecisionChange(reported.id, 'remove')}
|
||||
className="w-4 h-4 text-red-600 focus:ring-red-500"
|
||||
/>
|
||||
<span className="text-sm text-gray-700">Remove</span>
|
||||
</label>
|
||||
</div>
|
||||
{canMakeDecision ? (
|
||||
<div className="flex items-center gap-4">
|
||||
<label className="flex items-center gap-2 cursor-pointer">
|
||||
<input
|
||||
type="radio"
|
||||
name={`decision-${reported.id}`}
|
||||
value="keep"
|
||||
checked={decisions[reported.id] === 'keep'}
|
||||
onChange={() => handleDecisionChange(reported.id, 'keep')}
|
||||
className="w-4 h-4 text-green-600 focus:ring-green-500"
|
||||
/>
|
||||
<span className="text-sm text-gray-700">Keep</span>
|
||||
</label>
|
||||
<label className="flex items-center gap-2 cursor-pointer">
|
||||
<input
|
||||
type="radio"
|
||||
name={`decision-${reported.id}`}
|
||||
value="remove"
|
||||
checked={decisions[reported.id] === 'remove'}
|
||||
onChange={() => handleDecisionChange(reported.id, 'remove')}
|
||||
className="w-4 h-4 text-red-600 focus:ring-red-500"
|
||||
/>
|
||||
<span className="text-sm text-gray-700">Remove</span>
|
||||
</label>
|
||||
</div>
|
||||
) : (
|
||||
<span className="text-sm text-gray-500 italic">-</span>
|
||||
)}
|
||||
</td>
|
||||
<td className="px-6 py-4">
|
||||
{isReviewed || isDismissed ? (
|
||||
|
||||
Reference in New Issue
Block a user