| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485 |
- 'use client';
- import { useMemo, useState, type ChangeEvent } from 'react';
- import { Search } from 'lucide-react';
- import type { GameListItem } from '@/types/response/store/game';
- import GameCard from './GameCard';
- type SortKey = 'latest'|'name';
- const SORT_OPTIONS: { value: SortKey; label: string }[] = [
- { value: 'latest', label: '최신순' },
- { value: 'name', label: '이름순' }
- ];
- export default function GamesExplorer({ games }: { games: GameListItem[] })
- {
- const [keyword, setKeyword] = useState('');
- const [sort, setSort] = useState<SortKey>('latest');
- const filtered = useMemo(() => {
- let list = [...games];
- const kw = keyword.trim().toLowerCase();
- if (kw) {
- list = list.filter(g => g.korName.toLowerCase().includes(kw) || (g.engName?.toLowerCase().includes(kw) ?? false));
- }
- if (sort === 'name') {
- list.sort((a, b) => a.korName.localeCompare(b.korName));
- } else {
- list.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
- }
- return list;
- }, [games, keyword, sort]);
- return (
- <section>
- <header className="mb-4 flex flex-wrap items-center justify-between gap-3">
- <h1 className="flex items-center gap-2 text-xl font-bold">
- 게임
- <span className="rounded-full bg-[var(--bg-elevated)] px-2 py-0.5 text-sm font-semibold text-[var(--text-secondary)]">
- {filtered.length.toLocaleString()}
- </span>
- </h1>
- <div className="flex flex-wrap gap-2">
- <select
- value={sort}
- onChange={(e: ChangeEvent<HTMLSelectElement>) => setSort(e.target.value as SortKey)}
- aria-label="정렬"
- className="h-9 rounded-lg border border-[var(--border-default)] bg-[var(--bg-elevated)] px-3 text-sm"
- >
- {SORT_OPTIONS.map(o => <option key={o.value} value={o.value}>{o.label}</option>)}
- </select>
- <form role="search" onSubmit={(e) => e.preventDefault()} className="flex gap-2">
- <input
- type="search"
- value={keyword}
- onChange={(e) => setKeyword(e.target.value)}
- placeholder="게임명 검색"
- aria-label="검색"
- className="h-9 min-w-[200px] rounded-lg border border-[var(--border-default)] bg-[var(--bg-elevated)] px-3 text-sm"
- />
- <button
- type="submit"
- aria-label="검색"
- className="flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-[#FFD700] text-[#333] transition hover:bg-[#FFC400]"
- >
- <Search className="size-4" />
- </button>
- </form>
- </div>
- </header>
- {filtered.length === 0 ? (
- <p className="py-16 text-center text-[var(--text-muted)]">게임이 없습니다.</p>
- ) : (
- <div className="grid grid-cols-2 gap-4 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5">
- {filtered.map(g => <GameCard key={g.id} game={g} />)}
- </div>
- )}
- </section>
- );
- }
|