Files
whattoplay/src/client/features/discover/components/game-discover-card.tsx

80 lines
2.2 KiB
TypeScript

import { formatPlaytime, gameStateColors } from "@/features/games/schema"
import { Badge } from "@/shared/components/ui/badge"
import type { Game } from "@/shared/db/schema"
interface GameDiscoverCardProps {
game: Game
}
function getSteamHeaderImage(sourceId: string): string {
return `https://cdn.akamai.steamstatic.com/steam/apps/${sourceId}/header.jpg`
}
function parseJsonArray(text: string | null): string[] {
if (!text) return []
try {
return JSON.parse(text)
} catch {
return []
}
}
export function GameDiscoverCard({ game }: GameDiscoverCardProps) {
const imageUrl =
game.source === "steam" ? getSteamHeaderImage(game.source_id) : null
const dotColor =
game.game_state !== "not_set" ? gameStateColors[game.game_state] : null
const genres = parseJsonArray(game.genres).slice(0, 3)
const rating =
game.aggregated_rating != null ? Math.round(game.aggregated_rating) : null
return (
<div className="flex h-full flex-col overflow-hidden rounded-xl border bg-card shadow-lg">
{imageUrl && (
<img
src={imageUrl}
alt={game.title}
className="w-full flex-1 object-cover min-h-0"
/>
)}
<div className="shrink-0 p-4">
<div className="flex items-center gap-2">
<h3 className="truncate text-lg font-bold">{game.title}</h3>
<Badge variant="secondary" className="shrink-0">
{game.source}
</Badge>
{dotColor && (
<span
className={`inline-block h-2.5 w-2.5 shrink-0 rounded-full ${dotColor}`}
/>
)}
{rating != null && (
<Badge variant="outline" className="ml-auto shrink-0">
{rating}%
</Badge>
)}
</div>
{genres.length > 0 && (
<div className="mt-1.5 flex flex-wrap gap-1">
{genres.map((g) => (
<Badge key={g} variant="secondary" className="text-[10px]">
{g}
</Badge>
))}
</div>
)}
{game.summary && (
<p className="mt-1.5 line-clamp-2 text-xs text-muted-foreground">
{game.summary}
</p>
)}
{!game.summary && game.playtime_hours > 0 && (
<p className="mt-1 text-sm text-muted-foreground">
{formatPlaytime(game.playtime_hours)} played
</p>
)}
</div>
</div>
)
}