83 lines
2.5 KiB
TypeScript
83 lines
2.5 KiB
TypeScript
import { Badge } from "@/shared/components/ui/badge"
|
|
import { useGame } from "@/shared/db/hooks"
|
|
import type { Game } from "@/shared/db/schema"
|
|
import { t } from "@/shared/i18n"
|
|
import { ExternalLink } from "lucide-react"
|
|
import { formatPlaytime } from "../schema"
|
|
import { FavoriteButton } from "./favorite-button"
|
|
import { GameStateSelect } from "./game-state-select"
|
|
import { StarRating } from "./star-rating"
|
|
|
|
function getSteamHeaderImage(sourceId: string): string {
|
|
return `https://cdn.akamai.steamstatic.com/steam/apps/${sourceId}/header.jpg`
|
|
}
|
|
|
|
interface GameDetailProps {
|
|
gameId: string
|
|
}
|
|
|
|
export function GameDetail({ gameId }: GameDetailProps) {
|
|
const { game, loading, reload } = useGame(gameId)
|
|
|
|
if (loading) return null
|
|
|
|
if (!game) {
|
|
return <p className="py-8 text-center text-muted-foreground">{t("game.notFound")}</p>
|
|
}
|
|
|
|
return <GameDetailContent game={game} onUpdate={reload} />
|
|
}
|
|
|
|
function GameDetailContent({ game, onUpdate }: { game: Game; onUpdate: () => void }) {
|
|
const imageUrl = game.source === "steam" ? getSteamHeaderImage(game.source_id) : null
|
|
|
|
return (
|
|
<div className="space-y-4">
|
|
{imageUrl && (
|
|
<img
|
|
src={imageUrl}
|
|
alt={game.title}
|
|
className="w-full rounded-xl object-cover aspect-video"
|
|
/>
|
|
)}
|
|
|
|
<div className="flex items-start justify-between gap-2">
|
|
<div className="min-w-0 flex-1">
|
|
<div className="flex items-center gap-2">
|
|
<h2 className="text-xl font-bold">{game.title}</h2>
|
|
<Badge variant="secondary" className="shrink-0">
|
|
{game.source}
|
|
</Badge>
|
|
</div>
|
|
<div className="mt-1 flex items-center gap-3 text-sm text-muted-foreground">
|
|
{game.playtime_hours > 0 && <span>{formatPlaytime(game.playtime_hours)}</span>}
|
|
{game.last_played && (
|
|
<span>
|
|
{t("game.lastPlayed")}: {game.last_played}
|
|
</span>
|
|
)}
|
|
</div>
|
|
</div>
|
|
<FavoriteButton gameId={game.id} isFavorite={game.is_favorite} onChange={onUpdate} />
|
|
</div>
|
|
|
|
<div className="flex items-center justify-between">
|
|
<StarRating gameId={game.id} rating={game.rating} onChange={onUpdate} />
|
|
<GameStateSelect gameId={game.id} state={game.game_state} onChange={onUpdate} />
|
|
</div>
|
|
|
|
{game.url && (
|
|
<a
|
|
href={game.url}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="inline-flex items-center gap-2 rounded-lg border px-4 py-2 text-sm hover:bg-muted"
|
|
>
|
|
<ExternalLink className="h-4 w-4" />
|
|
{t("game.openStore")}
|
|
</a>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|