GameCard.tsx 1.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344
  1. import Link from 'next/link';
  2. import Image from 'next/image';
  3. import type { GameListItem } from '@/types/response/store/game';
  4. function formatDate(iso: string): string
  5. {
  6. const d = new Date(iso);
  7. if (Number.isNaN(d.getTime())) {
  8. return '';
  9. }
  10. return `${d.getFullYear()}.${String(d.getMonth() + 1).padStart(2, '0')}.${String(d.getDate()).padStart(2, '0')}`;
  11. }
  12. export default function GameCard({ game }: { game: GameListItem })
  13. {
  14. return (
  15. <Link
  16. href={`/games/${game.id}`}
  17. className="group flex flex-col overflow-hidden rounded-xl border border-[var(--border-default)] bg-[var(--bg-elevated)] transition hover:shadow-md"
  18. >
  19. <div className="relative aspect-square w-full overflow-hidden bg-[var(--bg-elevated)]">
  20. {game.thumbnail ? (
  21. <Image
  22. src={game.thumbnail}
  23. alt={game.korName}
  24. fill
  25. unoptimized
  26. sizes="(max-width: 640px) 50vw, (max-width: 1024px) 25vw, 20vw"
  27. className="object-cover transition group-hover:scale-105"
  28. />
  29. ) : (
  30. <span className="flex size-full items-center justify-center text-3xl font-bold text-[var(--text-secondary)]">
  31. {game.korName.charAt(0)}
  32. </span>
  33. )}
  34. </div>
  35. <div className="flex flex-col gap-0.5 p-3">
  36. <h3 className="line-clamp-1 font-semibold">{game.korName}</h3>
  37. {game.engName && <p className="line-clamp-1 text-xs text-[var(--text-secondary)]">{game.engName}</p>}
  38. <p className="mt-1 text-[11px] text-[var(--text-muted)]">{formatDate(game.createdAt)} 등록</p>
  39. </div>
  40. </Link>
  41. );
  42. }