refactor: structure YMB as enterprise React app

This commit is contained in:
Zhou Weidong committed 2026-11-10 15:35:16 +08:00
1 parent e575ebd948
commit eb3eb8dfc6
18 files changed
+182 -20

No files matched your search

+10
View File
@@ -0,0 +1,10 @@
// Author: fruit.com.cn
import { useState } from 'react';
import { MediaCard } from '../components/MediaCard';
import { SectionHeading } from '../components/SectionHeading';
import { videos } from '../data/catalog';
import { useAppStore } from '../app/store';
import { searchCatalog } from '../services/catalogService';
export function HomePage() { const { locale } = useAppStore(); const [genre, setGenre] = useState(locale.genres[0]); const [toast, setToast] = useState(''); const items = searchCatalog('', genre, locale.genres.indexOf(genre)); const play = () => { setToast(locale.watch); setTimeout(() => setToast(''), 1400); }; return <><section className="hero-card"><div className="hero-copy"><small>YMB ORIGINAL</small><h1>{locale.hero}<br /><em>{locale.accent}</em></h1><p>{locale.description}</p><button className="primary-button" onClick={play}>▶ {locale.watch}</button></div><div className="hero-art"><img src="/assets/hero.jpg" alt="YMB drama" /></div></section><section><SectionHeading title={locale.picks} /><div className="chip-row">{locale.genres.map((item) => <button className={`chip ${genre === item ? 'active' : ''}`} key={item} onClick={() => setGenre(item)}>{item}</button>)}</div><div className="video-grid">{items.map((video) => <MediaCard key={video.id} video={video} />)}</div></section><section><SectionHeading title={locale.shortTitle} action="Explore →" /><div className="shorts-row">{videos.slice(1, 7).map((video) => <MediaCard key={video.id} video={video} compact />)}</div></section>{toast && <div className="toast">{toast}</div>}</>;
}
+7
View File
@@ -0,0 +1,7 @@
// Author: fruit.com.cn
import { MediaCard } from '../components/MediaCard';
import { SectionHeading } from '../components/SectionHeading';
import { videos } from '../data/catalog';
import { useAppStore } from '../app/store';
export function ListingPage({ type }) { const { locale } = useAppStore(); const title = type === 'shorts' ? locale.shortTitle : type === 'subscriptions' ? locale.channels : type === 'history' ? locale.history : locale.library; return <><SectionHeading title={title} /><div className="video-grid">{videos.map((video) => <MediaCard key={video.id} video={video} />)}</div></>; }
+8
View File
@@ -0,0 +1,8 @@
// Author: fruit.com.cn
import { useSearchParams } from 'react-router-dom';
import { MediaCard } from '../components/MediaCard';
import { SectionHeading } from '../components/SectionHeading';
import { useAppStore } from '../app/store';
import { searchCatalog } from '../services/catalogService';
export function SearchPage() { const { locale } = useAppStore(); const [params] = useSearchParams(); const query = params.get('q') || ''; const results = searchCatalog(query, '', 0); return <><SectionHeading title={query ? `Search results for “${query}”` : locale.picks} /><div className="video-grid">{results.map((video) => <MediaCard key={video.id} video={video} />)}</div></>; }
+7
View File
@@ -0,0 +1,7 @@
// Author: fruit.com.cn
import { useParams } from 'react-router-dom';
import { videos } from '../data/catalog';
import { MediaCard } from '../components/MediaCard';
import { useAppStore } from '../app/store';
export function WatchPage() { const { id } = useParams(); const { locale } = useAppStore(); const video = videos.find((item) => item.id === id) || videos[0]; return <div className="watch-layout"><div><div className="player"><img src={`/assets/${video.image}`} alt={video.title} /><button>▶</button></div><h1 className="watch-title">{video.title}</h1><p className="watch-subtitle">{video.channel} · {video.views}</p></div><aside className="up-next"><h2>{locale.picks}</h2>{videos.slice(0, 4).map((item) => <MediaCard key={item.id} video={item} />)}</aside></div>; }