import { useEffect, useMemo, useState } from 'react'; import type { DemoLocale } from './types'; type MarketplaceScene = { id: 'query' | 'rank' | 'match'; durationMs: number; }; type MarketplaceFeatureDemoProps = { locale: DemoLocale; isBusy: boolean; }; const COPY = { en: { title: 'Marketplace Demo', busy: 'Running', hidden: 'Hidden', play: 'Play', pause: 'Pause', replay: 'Replay', hide: 'Hide', show: 'Show', videoName: 'marketplace_demo.webm', step: 'Step', lanes: { query: 'Query', rank: 'Rank', match: 'Match', }, captions: { query: 'Collect search and filter intent', rank: 'Score records by relevance', match: 'Render product + store cards', }, words: { query: ['Vegetables', 'Taipei', 'Under200'], rank: ['Parse', 'Score', 'Sort'], match: ['Product', 'Store', 'Stats'], streamWords: ['Searching', 'Filtering', 'Ranking', 'Matching'], queryImage: 'https://upload.wikimedia.org/wikipedia/commons/c/c2/Fresh_Vegetables_2.jpg', }, }, 'zh-TW': { title: '市集導覽', busy: '執行中', hidden: '已隱藏', play: '播放', pause: '暫停', replay: '重播', hide: '隱藏', show: '顯示', videoName: '市集導覽影片.webm', step: '步驟', lanes: { query: '查詢', rank: '排序', match: '結果', }, captions: { query: '收集搜尋與篩選條件', rank: '依相關性計分排序', match: '輸出商品與店家卡片', }, words: { query: ['蔬菜', '台北', '200內'], rank: ['解析', '計分', '排序'], match: ['商品', '店家', '統計'], streamWords: ['搜尋中', '篩選中', '排序中', '配對中'], queryImage: 'https://upload.wikimedia.org/wikipedia/commons/c/c2/Fresh_Vegetables_2.jpg', }, }, } as const; const SCENES: MarketplaceScene[] = [ { id: 'query', durationMs: 1650 }, { id: 'rank', durationMs: 1750 }, { id: 'match', durationMs: 1700 }, ]; export default function MarketplaceFeatureDemo({ locale, isBusy }: MarketplaceFeatureDemoProps) { const copy = COPY[locale]; const scenes = useMemo(() => SCENES, []); const [sceneIndex, setSceneIndex] = useState(0); const [elapsedMs, setElapsedMs] = useState(0); const [isPlaying, setIsPlaying] = useState(true); const [isCollapsed, setIsCollapsed] = useState(false); const [reducedMotion, setReducedMotion] = useState(false); useEffect(() => { const media = window.matchMedia('(prefers-reduced-motion: reduce)'); const apply = () => setReducedMotion(media.matches); apply(); if (media.addEventListener) { media.addEventListener('change', apply); return () => media.removeEventListener('change', apply); } media.addListener(apply); return () => media.removeListener(apply); }, []); useEffect(() => { if (reducedMotion) setIsPlaying(false); }, [reducedMotion]); useEffect(() => { setSceneIndex(0); setElapsedMs(0); setIsCollapsed(false); if (!reducedMotion) setIsPlaying(true); }, [locale, reducedMotion]); useEffect(() => { if (!isBusy) return; setIsCollapsed(true); setIsPlaying(false); }, [isBusy]); useEffect(() => { if (!isPlaying || isCollapsed || isBusy || reducedMotion) return; const timer = window.setInterval(() => { setElapsedMs((prev) => { const scene = scenes[sceneIndex]; if (!scene) return 0; const next = prev + 120; if (next < scene.durationMs) return next; setSceneIndex((current) => (current + 1) % scenes.length); return 0; }); }, 120); return () => window.clearInterval(timer); }, [isPlaying, isCollapsed, isBusy, reducedMotion, scenes, sceneIndex]); const activeScene = scenes[sceneIndex] ?? scenes[0]; const totalDuration = scenes.reduce((sum, scene) => sum + scene.durationMs, 0); const previousDuration = scenes.slice(0, sceneIndex).reduce((sum, scene) => sum + scene.durationMs, 0); const progress = totalDuration > 0 ? Math.round(((previousDuration + elapsedMs) / totalDuration) * 100) : 0; const sceneCaption = copy.captions[activeScene.id]; const streamOffset = Math.floor(elapsedMs / 420); const activeStreamWords = copy.words.streamWords.map( (_word, index) => copy.words.streamWords[(streamOffset + index) % copy.words.streamWords.length] ); const replay = () => { setSceneIndex(0); setElapsedMs(0); if (!reducedMotion && !isBusy) setIsPlaying(true); }; if (isCollapsed) { return (

{isBusy ? copy.busy : copy.hidden}

{!isBusy ? ( ) : null}
); } return (

{copy.title}

{copy.step} {sceneIndex + 1}/{scenes.length}
{copy.videoName}
1 {copy.lanes.query}
{copy.words.query.map((item) => ( {item} ))}
{locale
{activeStreamWords[0]}
{activeStreamWords[1]}

{locale === 'zh-TW' ? '查詢與價格區間已套用。' : 'Query text and price range applied.'}

2 {copy.lanes.rank}
{copy.words.rank.map((item) => ( {item} ))}
{activeStreamWords[2]}
3 {copy.lanes.match}
{copy.words.match.map((item) => (
{item} {item === copy.words.match[0] ? '6' : item === copy.words.match[1] ? '4' : '42'}
))}

{locale === 'zh-TW' ? '已輸出排序卡片與統計摘要。' : 'Ranked cards and stats summary rendered.'}

); }