Files
Art-gallery/client/src/components/DebugSearchResultsModal.tsx
T
Danila KhodjaefandCursor 0972b5df99 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>
2026-06-21 13:14:06 +03:00

102 lines
3.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useEffect } from 'react';
import { debugImageProxyUrl, type DebugImageSearchManyResult, type DebugImageSearchResultItem } from '../api/client';
import './DebugSearchResultsModal.css';
interface Props {
open: boolean;
title: string;
data: DebugImageSearchManyResult | null;
loading: boolean;
error: string | null;
applyingUrl: string | null;
onClose: () => void;
onSelect: (item: DebugImageSearchResultItem) => void;
}
export default function DebugSearchResultsModal({
open,
title,
data,
loading,
error,
applyingUrl,
onClose,
onSelect,
}: Props) {
useEffect(() => {
if (!open) return;
const onKeyDown = (e: KeyboardEvent) => {
if (e.key === 'Escape') onClose();
};
window.addEventListener('keydown', onKeyDown);
return () => window.removeEventListener('keydown', onKeyDown);
}, [open, onClose]);
if (!open) return null;
return (
<div
className="debug-search-modal-overlay"
role="dialog"
aria-modal="true"
aria-label={title}
onClick={onClose}
>
<div className="debug-search-modal" onClick={(e) => e.stopPropagation()}>
<header className="debug-search-modal-header">
<div>
<h3>{title}</h3>
{data?.query && <p className="debug-search-modal-query">{data.query}</p>}
</div>
<button type="button" className="debug-search-modal-close" onClick={onClose} aria-label="Close">
×
</button>
</header>
{loading && <p className="debug-search-modal-status">Loading results</p>}
{error && <p className="debug-search-modal-error">{error}</p>}
{!loading && !error && data && data.results.length === 0 && (
<p className="debug-search-modal-status">No images found.</p>
)}
{!loading && data && data.results.length > 0 && (
<>
<p className="debug-search-modal-hint">Click an image to replace the current one.</p>
<div className="debug-search-modal-grid">
{data.results.map((item, index) => {
const busy = applyingUrl === item.imageUrl;
return (
<button
key={`${item.imageUrl}-${index}`}
type="button"
className={`debug-search-modal-item${busy ? ' debug-search-modal-item-busy' : ''}`}
disabled={!!applyingUrl}
onClick={() => onSelect(item)}
title="Use this image"
>
<img
src={debugImageProxyUrl(item.thumbUrl || item.imageUrl, {
searchUrl: data.searchUrl,
source: item.source,
})}
alt={`Result ${index + 1}`}
loading="lazy"
onError={(e) => {
(e.target as HTMLImageElement).src = '/placeholder-art.svg';
}}
/>
<span className="debug-search-modal-item-index">{index + 1}</span>
{busy && <span className="debug-search-modal-item-busy-label">Saving</span>}
</button>
);
})}
</div>
</>
)}
</div>
</div>
);
}