Add debug Remove entry, Show more auto-picker, and update docs.

DELETE /api/paintings/:id removes works and image files with gallery refresh and catalog navigation; Show more opens the search modal on load; documentation updated for migrate schema and debug workflow.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Danila Khodjaef
2026-06-22 22:32:23 +03:00
co-authored by Cursor
parent b2cae284ac
commit f542c689c9
31 changed files with 396 additions and 30 deletions
+42 -3
View File
@@ -1,4 +1,4 @@
import { useEffect, useRef, useState, type ChangeEvent, type SyntheticEvent } from 'react';
import { useCallback, useEffect, useRef, useState, type ChangeEvent, type SyntheticEvent } from 'react';
import type { InfluenceLink, Painting, PaintingDetail } from '../types';
import { api, debugImageProxyUrl, imageUrl, paintingImageUrl, type DebugImageSearchResult, type DebugImageSearchResultItem, type FixPaintingImageResult } from '../api/client';
import DebugSearchResultsModal from './DebugSearchResultsModal';
@@ -15,6 +15,7 @@ interface Props {
onArtistBio: () => void;
onInfluenceArtistClick?: (artistId: number) => void;
debugMode?: boolean;
debugShowMore?: boolean;
onPaintingImageFixed?: (
paintingId: number,
fixResult: FixPaintingImageResult
@@ -23,6 +24,7 @@ interface Props {
paintingId: number,
flags: { checked: boolean; fixed: boolean }
) => void | Promise<void>;
onPaintingRemoved?: (paintingId: number, artistId: number) => void | Promise<void>;
}
function influenceKey(inf: InfluenceLink, index: number): string {
@@ -188,8 +190,10 @@ export default function PaintingDetailView({
onArtistBio,
onInfluenceArtistClick,
debugMode = false,
debugShowMore = false,
onPaintingImageFixed,
onPaintingCheckupFlagsUpdated,
onPaintingRemoved,
}: Props) {
const { painting, influencedBy, influenced, annotations = [] } = data;
const [fullscreen, setFullscreen] = useState(false);
@@ -206,6 +210,7 @@ export default function PaintingDetailView({
const [applyingUrl, setApplyingUrl] = useState<string | null>(null);
const [clearing, setClearing] = useState(false);
const [uploading, setUploading] = useState(false);
const [removing, setRemoving] = useState(false);
const [activeAnnotationId, setActiveAnnotationId] = useState<number | null>(null);
const uploadInputRef = useRef<HTMLInputElement>(null);
@@ -230,6 +235,13 @@ export default function PaintingDetailView({
setMoreResults(null);
setMoreError(null);
setActiveAnnotationId(null);
setRemoving(false);
setFixing(false);
setClearing(false);
setUploading(false);
setMarkingChecked(false);
setApplyingUrl(null);
setMoreLoading(false);
}, [painting.id]);
useEffect(() => {
@@ -298,7 +310,7 @@ export default function PaintingDetailView({
}
};
const handleOpenMore = async () => {
const handleOpenMore = useCallback(async () => {
setMoreOpen(true);
setMoreLoading(true);
setMoreError(null);
@@ -311,7 +323,12 @@ export default function PaintingDetailView({
} finally {
setMoreLoading(false);
}
};
}, [painting.id]);
useEffect(() => {
if (!debugMode || !debugShowMore) return;
void handleOpenMore();
}, [debugMode, debugShowMore, painting.id, handleOpenMore]);
const handleSelectMoreResult = async (item: DebugImageSearchResultItem) => {
if (fixing || applyingUrl) return;
@@ -381,6 +398,18 @@ export default function PaintingDetailView({
}
};
const handleRemoveEntry = async () => {
if (removing || fixing || clearing || uploading || !onPaintingRemoved) return;
setRemoving(true);
setDebugError(null);
try {
await onPaintingRemoved(painting.id, painting.artist_id);
} catch (err) {
setDebugError(err instanceof Error ? err.message : 'Could not remove painting.');
setRemoving(false);
}
};
useEffect(() => {
if (fullscreen) return;
@@ -614,6 +643,16 @@ export default function PaintingDetailView({
onChange={handleUploadFile}
/>
</div>
<div className="debug-action-buttons debug-action-buttons-danger">
<button
type="button"
className="debug-remove-btn"
onClick={handleRemoveEntry}
disabled={removing || fixing || clearing || uploading}
>
{removing ? '…' : 'Remove entry'}
</button>
</div>
</aside>
)}