Add debug More/Clear/Upload tools for paintings and artist portraits.
Extends the debug panel on painting detail and artist bio with a 20-result search picker, local image upload, and clear-to-empty-frame workflow, plus API routes, artist checkup migration, and documentation. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
b4425445bb
commit
0972b5df99
@@ -348,7 +348,7 @@ export default function CheckupPage({ onBack, onOpenPainting }: Props) {
|
||||
setImageVersionById((prev) => ({ ...prev, [row.id]: (prev[row.id] ?? 0) + 1 }));
|
||||
setRows((list) =>
|
||||
list.map((r) =>
|
||||
r.id === row.id
|
||||
r.id === row.id && updated.imagePath && updated.thumbnailPath
|
||||
? {
|
||||
...r,
|
||||
gallery_file: updated.imagePath,
|
||||
|
||||
@@ -5,7 +5,7 @@ import VirtualGallery from '../components/VirtualGallery';
|
||||
import PaintingDetailView from '../components/PaintingDetail';
|
||||
import ArtistBio from '../components/ArtistBio';
|
||||
import CheckupPage from '../pages/CheckupPage';
|
||||
import { api, type FixPaintingImageResult } from '../api/client';
|
||||
import { api, type FixPaintingImageResult, type FixArtistPortraitResult } from '../api/client';
|
||||
import type { TimelineData, Artist, ArtistDetail, Painting, PaintingDetail } from '../types';
|
||||
import { sortArtistPaintingsChronological } from '../utils/paintingUtils';
|
||||
import { readDebugMode, writeDebugMode } from '../utils/debugMode';
|
||||
@@ -29,6 +29,13 @@ function patchPaintingInArtistDetail(
|
||||
};
|
||||
}
|
||||
|
||||
function patchArtistInArtistDetail(detail: ArtistDetail, patch: Partial<Artist>): ArtistDetail {
|
||||
return {
|
||||
...detail,
|
||||
artist: { ...detail.artist, ...patch },
|
||||
};
|
||||
}
|
||||
|
||||
export default function HomePage() {
|
||||
const [view, setView] = useState<View>({ type: 'timeline' });
|
||||
const [gallerySession, setGallerySession] = useState<{ artistId: number; data: ArtistDetail } | null>(
|
||||
@@ -43,6 +50,7 @@ export default function HomePage() {
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [detailArtistPaintings, setDetailArtistPaintings] = useState<Painting[]>([]);
|
||||
const [imageRevisions, setImageRevisions] = useState<Record<number, number>>({});
|
||||
const [portraitRevisions, setPortraitRevisions] = useState<Record<number, number>>({});
|
||||
const [debugMode, setDebugMode] = useState(readDebugMode);
|
||||
const detailReturnToRef = useRef<View>({ type: 'timeline' });
|
||||
|
||||
@@ -103,8 +111,8 @@ export default function HomePage() {
|
||||
const handlePaintingImageFixed = useCallback(async (paintingId: number, fixResult: FixPaintingImageResult) => {
|
||||
const data = await api.getPainting(paintingId);
|
||||
const patch: Partial<Painting> = {
|
||||
image_path: fixResult.imagePath ?? data.painting.image_path,
|
||||
thumbnail_path: fixResult.thumbnailPath ?? data.painting.thumbnail_path,
|
||||
image_path: fixResult.imagePath,
|
||||
thumbnail_path: fixResult.thumbnailPath,
|
||||
checkup_checked: fixResult.checked ?? true,
|
||||
checkup_fixed: fixResult.fixed ?? true,
|
||||
};
|
||||
@@ -175,6 +183,73 @@ export default function HomePage() {
|
||||
[]
|
||||
);
|
||||
|
||||
const applyArtistPatch = useCallback((artistId: number, patch: Partial<Artist>) => {
|
||||
setArtists((list) => list.map((a) => (a.id === artistId ? { ...a, ...patch } : a)));
|
||||
|
||||
setView((current) => {
|
||||
if (current.type === 'bio' && current.artistId === artistId) {
|
||||
return { ...current, data: patchArtistInArtistDetail(current.data, patch) };
|
||||
}
|
||||
if (current.type === 'gallery' && current.artistId === artistId) {
|
||||
return { ...current, data: patchArtistInArtistDetail(current.data, patch) };
|
||||
}
|
||||
if (current.type === 'painting' && current.data.painting.artist_id === artistId) {
|
||||
return {
|
||||
...current,
|
||||
data: {
|
||||
...current.data,
|
||||
painting: {
|
||||
...current.data.painting,
|
||||
artist_portrait: patch.portrait_path ?? current.data.painting.artist_portrait,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
return current;
|
||||
});
|
||||
|
||||
setGallerySession((session) =>
|
||||
session && session.artistId === artistId
|
||||
? { ...session, data: patchArtistInArtistDetail(session.data, patch) }
|
||||
: session
|
||||
);
|
||||
}, []);
|
||||
|
||||
const handleArtistPortraitFixed = useCallback(
|
||||
async (artistId: number, fixResult: FixArtistPortraitResult) => {
|
||||
const data = await api.getArtist(artistId);
|
||||
const patch: Partial<Artist> = {
|
||||
portrait_path: fixResult.portraitPath,
|
||||
checkup_checked: fixResult.checked ?? true,
|
||||
checkup_fixed: fixResult.fixed ?? true,
|
||||
};
|
||||
setPortraitRevisions((prev) => ({ ...prev, [artistId]: (prev[artistId] ?? 0) + 1 }));
|
||||
applyArtistPatch(artistId, patch);
|
||||
setView((current) =>
|
||||
current.type === 'bio' && current.artistId === artistId
|
||||
? { ...current, data: { ...data, artist: { ...data.artist, ...patch } } }
|
||||
: current
|
||||
);
|
||||
},
|
||||
[applyArtistPatch]
|
||||
);
|
||||
|
||||
const handleArtistCheckupFlagsUpdated = useCallback(
|
||||
async (artistId: number, flags: { checked: boolean; fixed: boolean }) => {
|
||||
const patch: Partial<Artist> = {
|
||||
checkup_checked: flags.checked,
|
||||
checkup_fixed: flags.fixed,
|
||||
};
|
||||
applyArtistPatch(artistId, patch);
|
||||
setView((current) =>
|
||||
current.type === 'bio' && current.artistId === artistId
|
||||
? { ...current, data: patchArtistInArtistDetail(current.data, patch) }
|
||||
: current
|
||||
);
|
||||
},
|
||||
[applyArtistPatch]
|
||||
);
|
||||
|
||||
const handleArtistClick = async (artistId: number) => {
|
||||
try {
|
||||
const data = await api.getArtist(artistId);
|
||||
@@ -305,10 +380,14 @@ export default function HomePage() {
|
||||
<div className="home-overlay">
|
||||
<ArtistBio
|
||||
artist={view.data.artist}
|
||||
debugMode={debugMode}
|
||||
portraitRevision={portraitRevisions[view.data.artist.id]}
|
||||
onBack={() => setView(view.returnTo)}
|
||||
onEnterGallery={() =>
|
||||
setView({ type: 'gallery', artistId: view.artistId, data: view.data })
|
||||
}
|
||||
onArtistPortraitFixed={handleArtistPortraitFixed}
|
||||
onArtistCheckupFlagsUpdated={handleArtistCheckupFlagsUpdated}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
@@ -328,7 +407,7 @@ export default function HomePage() {
|
||||
type="button"
|
||||
className={`debug-mode-toggle${debugMode ? ' debug-mode-toggle-active' : ''}`}
|
||||
onClick={toggleDebugMode}
|
||||
title="Toggle developer image audit mode on painting details"
|
||||
title="Toggle developer image audit mode on painting details and artist bios"
|
||||
>
|
||||
Debug mode{debugMode ? ': ON' : ''}
|
||||
</button>
|
||||
@@ -363,6 +442,7 @@ export default function HomePage() {
|
||||
<MovementBands
|
||||
movements={timelineData.movements}
|
||||
artists={artists}
|
||||
portraitRevisions={portraitRevisions}
|
||||
viewStart={viewStart}
|
||||
viewEnd={viewEnd}
|
||||
absoluteMin={bounds.min}
|
||||
|
||||
Reference in New Issue
Block a user