Redesign 3D gallery as single hall per artist with exit navigation.

Restore React client source, add hall-to-hall navigation via painting influences grouped by movement, and update documentation.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Danila Khodjaef
2026-06-19 10:10:13 +03:00
co-authored by Cursor
parent 44d3d4359a
commit 08f99d7a29
25 changed files with 2954 additions and 218 deletions
+71
View File
@@ -0,0 +1,71 @@
import type { Artist } from '../types';
import { imageUrl } from '../api/client';
import './ArtistBio.css';
interface Props {
artist: Artist & { movement_name?: string };
onBack: () => void;
onEnterGallery: () => void;
}
export default function ArtistBio({ artist, onBack, onEnterGallery }: Props) {
const lifespan =
artist.birth_year && artist.death_year
? `${artist.birth_year} ${artist.death_year}`
: artist.birth_year
? `b. ${artist.birth_year}`
: '';
return (
<div className="artist-bio">
<header className="bio-header">
<button className="back-btn" onClick={onBack}> Back</button>
<h1>{artist.name}</h1>
<button className="gallery-btn" onClick={onEnterGallery}>Enter Gallery</button>
</header>
<div className="bio-content">
<div className="bio-portrait">
<img
src={imageUrl(artist.portrait_path)}
alt={artist.name}
onError={(e) => {
(e.target as HTMLImageElement).src = '/placeholder-portrait.svg';
}}
/>
</div>
<div className="bio-text">
<div className="bio-meta">
{lifespan && <span className="bio-lifespan">{lifespan}</span>}
{artist.movement_name && (
<span className="bio-movement">{artist.movement_name}</span>
)}
</div>
{artist.bio_short && (
<p className="bio-summary">{artist.bio_short}</p>
)}
{artist.bio_full && (
<div className="bio-full">
{artist.bio_full.split('\n').map((para, i) => (
<p key={i}>{para}</p>
))}
</div>
)}
{!artist.bio_full && !artist.bio_short && (
<p className="bio-empty">Biographical information not yet available.</p>
)}
{artist.wikipedia_title && (
<p className="bio-source">
Information sourced from Wikipedia article: {artist.wikipedia_title}
</p>
)}
</div>
</div>
</div>
);
}