Add influence rework, image checkup, debug mode, and fetched paintings.

Support artist and movement influence links with web discovery, a developer checkup table with gallery/detail thumbnails, and debug image search with fix-it workflow.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Danila Khodjaef
2026-06-20 10:29:43 +03:00
co-authored by Cursor
parent 0ece1195fa
commit bf7db9b25e
246 changed files with 2429 additions and 301 deletions
+77
View File
@@ -4,13 +4,16 @@ import MovementBands from '../components/MovementBands';
import VirtualGallery from '../components/VirtualGallery';
import PaintingDetailView from '../components/PaintingDetail';
import ArtistBio from '../components/ArtistBio';
import CheckupPage from '../pages/CheckupPage';
import { api } from '../api/client';
import type { TimelineData, Artist, ArtistDetail, Painting, PaintingDetail } from '../types';
import { sortArtistPaintingsChronological } from '../utils/paintingUtils';
import { readDebugMode, writeDebugMode } from '../utils/debugMode';
import './HomePage.css';
type View =
| { type: 'timeline' }
| { type: 'checkup' }
| { type: 'gallery'; artistId: number; data: ArtistDetail }
| { type: 'painting'; paintingId: number; data: PaintingDetail; returnTo: View }
| { type: 'bio'; artistId: number; data: ArtistDetail; returnTo: View };
@@ -28,6 +31,7 @@ export default function HomePage() {
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [detailArtistPaintings, setDetailArtistPaintings] = useState<Painting[]>([]);
const [debugMode, setDebugMode] = useState(readDebugMode);
const detailReturnToRef = useRef<View>({ type: 'timeline' });
useEffect(() => {
@@ -76,6 +80,51 @@ export default function HomePage() {
setViewEnd(end);
};
const toggleDebugMode = () => {
setDebugMode((prev) => {
const next = !prev;
writeDebugMode(next);
return next;
});
};
const handlePaintingImageFixed = useCallback(async (paintingId: number) => {
const data = await api.getPainting(paintingId);
setView((current) =>
current.type === 'painting' && current.paintingId === paintingId
? { ...current, data }
: current
);
setDetailArtistPaintings((list) =>
list.map((p) =>
p.id === paintingId
? { ...p, image_path: data.painting.image_path, thumbnail_path: data.painting.thumbnail_path }
: p
)
);
if (gallerySession?.artistId === data.painting.artist_id) {
setGallerySession((session) =>
session
? {
...session,
data: {
...session.data,
paintings: session.data.paintings.map((p) =>
p.id === paintingId
? {
...p,
image_path: data.painting.image_path,
thumbnail_path: data.painting.thumbnail_path,
}
: p
),
},
}
: session
);
}
}, [gallerySession]);
const handleArtistClick = async (artistId: number) => {
try {
const data = await api.getArtist(artistId);
@@ -182,6 +231,9 @@ export default function HomePage() {
const artistData = await api.getArtist(view.data.painting.artist_id);
setView({ type: 'bio', artistId: artistData.artist.id, data: artistData, returnTo: view });
}}
onInfluenceArtistClick={handleArtistClick}
debugMode={debugMode}
onPaintingImageFixed={handlePaintingImageFixed}
/>
</div>
)}
@@ -198,9 +250,34 @@ export default function HomePage() {
</div>
)}
{view.type === 'checkup' && (
<CheckupPage
onBack={() => setView({ type: 'timeline' })}
onOpenPainting={handlePaintingClick}
/>
)}
{view.type === 'timeline' && (
<div className="home-page">
<header className="site-header">
<div className="site-dev-tools">
<button
type="button"
className={`debug-mode-toggle${debugMode ? ' debug-mode-toggle-active' : ''}`}
onClick={toggleDebugMode}
title="Toggle developer image audit mode on painting details"
>
Debug mode{debugMode ? ': ON' : ''}
</button>
<button
type="button"
className="checkup-link-btn"
onClick={() => setView({ type: 'checkup' })}
title="Open painting image checkup table"
>
Checkup
</button>
</div>
<h1>Virtual Art Gallery</h1>
<p className="site-subtitle">Watch art movements branch forward through time each flowing from what came before</p>
</header>