import { ChangeEvent, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { Download, ExternalLink, ImagePlus, Loader2, Sparkles, UploadCloud } from 'lucide-react'; import { motion, AnimatePresence } from 'framer-motion'; import { checkDreamWeaverHealth, fetchDreamWeaverResult, getDreamWeaverStatus, submitDreamWeaverJob, type DreamWeaverHealth, type DreamWeaverJobResponse, } from '@/shared/lib/dreamWeaverApi'; import styles from './ReimaginePanel.module.css'; type RoomType = 'bedroom' | 'living_room' | 'bathroom' | 'kitchen' | 'dining_room' | 'office'; type Phase = 'idle' | 'ready' | 'generating' | 'result' | 'error'; interface ReimagineResult { url: string; jobId: string; blob: Blob; } interface ReimaginePanelProps { propertyId: string; roomImageUrl?: string; onResultSaved?: (url: string) => void; } const ROOM_TYPES: { id: RoomType; label: string }[] = [ { id: 'bedroom', label: 'Bedroom' }, { id: 'living_room', label: 'Living Room' }, { id: 'bathroom', label: 'Bathroom' }, { id: 'kitchen', label: 'Kitchen' }, { id: 'dining_room', label: 'Dining Room' }, { id: 'office', label: 'Office' }, ]; const DEFAULT_PROMPT = 'Modern luxury staging, warm practical lighting, premium materials, uncluttered real estate brochure finish'; export function ReimaginePanel({ propertyId, roomImageUrl, onResultSaved }: ReimaginePanelProps) { const fileInputRef = useRef(null); const [phase, setPhase] = useState('idle'); const [health, setHealth] = useState(null); const [roomType, setRoomType] = useState('bedroom'); const [keywords, setKeywords] = useState(DEFAULT_PROMPT); const [sourceFile, setSourceFile] = useState(null); const [sourcePreview, setSourcePreview] = useState(roomImageUrl ?? null); const [job, setJob] = useState(null); const [result, setResult] = useState(null); const [error, setError] = useState(null); const [pollCount, setPollCount] = useState(0); useEffect(() => { let alive = true; checkDreamWeaverHealth() .then((value) => { if (!alive) return; setHealth(value); setPhase('ready'); }) .catch((err) => { if (!alive) return; setError(err instanceof Error ? err.message : 'Dream Weaver health check failed.'); setPhase('error'); }); return () => { alive = false; }; }, []); useEffect(() => { return () => { if (sourcePreview?.startsWith('blob:')) URL.revokeObjectURL(sourcePreview); if (result?.url.startsWith('blob:')) URL.revokeObjectURL(result.url); }; }, [sourcePreview, result]); const canGenerate = Boolean(sourceFile) && phase !== 'generating'; const healthLabel = useMemo(() => { if (!health) return 'Checking Dream Weaver'; if (health.online && health.routeMounted) return 'Dream Weaver online'; return health.detail || health.status || 'Dream Weaver unavailable'; }, [health]); const handleFileChange = (event: ChangeEvent) => { const file = event.target.files?.[0] ?? null; if (!file) return; if (sourcePreview?.startsWith('blob:')) URL.revokeObjectURL(sourcePreview); setSourceFile(file); setSourcePreview(URL.createObjectURL(file)); setResult(null); setError(null); setPhase('ready'); }; const handleGenerate = useCallback(async () => { if (!sourceFile) { setError('Upload a room image before generating.'); setPhase('error'); return; } setPhase('generating'); setError(null); setPollCount(0); try { const submitted = await submitDreamWeaverJob({ image: sourceFile, roomType, keywords }); setJob(submitted); for (let attempt = 1; attempt <= 180; attempt += 1) { setPollCount(attempt); const status = await getDreamWeaverStatus(submitted); if (status.status === 'failed') { throw new Error(status.error || 'Dream Weaver generation failed.'); } if (status.ready || status.status === 'complete') { const blob = await fetchDreamWeaverResult(submitted.job_id, status.result_url ?? submitted.result_url); const url = URL.createObjectURL(blob); setResult({ url, jobId: submitted.job_id, blob }); setPhase('result'); return; } await new Promise((resolve) => window.setTimeout(resolve, 2000)); } throw new Error('Dream Weaver timed out before returning an image.'); } catch (err) { setError(err instanceof Error ? err.message : 'Dream Weaver request failed.'); setPhase('error'); } }, [keywords, roomType, sourceFile]); const handleDownload = () => { if (!result) return; const anchor = document.createElement('a'); anchor.href = result.url; anchor.download = `${propertyId}-reimagine-${result.jobId}.png`; anchor.click(); }; return (
Source Room

Reimagine this space

{healthLabel}
{ROOM_TYPES.map((room) => ( ))}