import { Pencil, Trash2 } from "lucide-react"; import { type FC, useState } from "react"; import type { GameEntry } from "@/api/gen/model/gameEntry"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Card } from "@/components/ui/card"; import { m } from "@/paraglide/messages"; /** * Display label for a store badge. Steam and custom keep their localized strings; every other store * (lutris, heroic, epic, …) is a proper noun shown capitalized, so new providers surface correctly * without a translation per store. */ function storeLabel(store: string): string { switch (store) { case "custom": return m.library_store_custom(); case "steam": return m.library_store_steam(); default: return store.charAt(0).toUpperCase() + store.slice(1); } } export interface GameCardProps { game: GameEntry; onEdit: () => void; onDelete: () => void; deleting: boolean; } /** * A poster tile. The cover prefers the 2:3 portrait capsule; on a load error it * falls back to the wide header, then to a text placeholder. Custom entries get * edit/delete affordances. */ export const GameCard: FC = ({ game, onEdit, onDelete, deleting, }) => { const isCustom = game.store === "custom"; // Track which sources have failed so the can step down portrait → header → placeholder. const [failed, setFailed] = useState>({}); const candidates = [game.art.portrait, game.art.header].filter( (u): u is string => !!u && !failed[u], ); const src = candidates[0]; return (
{src ? ( {game.title} setFailed((prev) => ({ ...prev, [src]: true }))} /> ) : (
{game.title}
)}
{storeLabel(game.store)}
{isCustom && (
)}
{game.title}
); };