import Link from "next/link"; import * as React from "react"; import { Badge } from "@/components/ui/badge"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Separator } from "@/components/ui/separator"; import { Table, TableBody, TableCell, TableRow } from "@/components/ui/table"; import { cn } from "@/lib/utils"; import type { ArtworkWithRelations } from "@/types/Artwork"; function fmtDate(value?: Date | string | null) { if (!value) return "—"; const d = typeof value === "string" ? new Date(value) : value; if (Number.isNaN(d.getTime())) return "—"; return new Intl.DateTimeFormat("de-DE", { year: "numeric", month: "short", day: "2-digit", hour: "2-digit", minute: "2-digit", }).format(d); } function fmtBool(value?: boolean | null) { if (value === true) return "Yes"; if (value === false) return "No"; return "—"; } function fmtNum(value?: number | null, digits = 0) { if (value === null || value === undefined) return "—"; return new Intl.NumberFormat("de-DE", { maximumFractionDigits: digits, minimumFractionDigits: digits, }).format(value); } function fmtBytes(bytes?: number | null) { if (!bytes && bytes !== 0) return "—"; const units = ["B", "KB", "MB", "GB", "TB"]; let v = bytes; let i = 0; while (v >= 1024 && i < units.length - 1) { v /= 1024; i++; } return `${fmtNum(v, i === 0 ? 0 : 2)} ${units[i]}`; } function KVTable({ rows }: { rows: Array<{ k: string; v: React.ReactNode }> }) { return ( {rows.map((r) => ( {r.k} {r.v} ))}
); } function StatusPill({ label, variant, }: { label: string; variant?: "default" | "secondary" | "destructive" | "outline"; }) { return ( {label} ); } export default function ArtworkDetails({ artwork, className, }: { artwork: ArtworkWithRelations; className?: string; }) { const meta = artwork.metadata ?? null; // Your schema: Artwork has `fileId` + relation `file: FileData` // but depending on your `ArtworkWithRelations` type, `file` may be optional. const file = (artwork as any).file ?? null; const flags = [ artwork.published ? : , artwork.nsfw ? : , artwork.needsWork ? : , artwork.setAsHeader ? : null, ].filter(Boolean); return (
Artwork details Read-only technical information and metadata
{flags}
{/* Core */}
Core
{artwork.id} }, { k: "Slug", v: {artwork.slug} }, { k: "Sort index", v: fmtNum(artwork.sortIndex ?? 0) }, { k: "Sort key", v: artwork.sortKey != null ? fmtNum(artwork.sortKey) : "—" }, { k: "Created", v: fmtDate(artwork.createdAt as any) }, { k: "Updated", v: fmtDate(artwork.updatedAt as any) }, { k: "Creation date", v: fmtDate(artwork.creationDate as any) }, { k: "Creation (month/year)", v: artwork.month || artwork.year ? `${artwork.month ? fmtNum(artwork.month) : "—"} / ${artwork.year ? fmtNum(artwork.year) : "—"}` : "—", }, { k: "Color status", v: (
{artwork.colorStatus ?? "—"} {artwork.colorsGeneratedAt ? ( generated {fmtDate(artwork.colorsGeneratedAt as any)} ) : null}
{artwork.colorError ? (
{artwork.colorError}
) : null}
), }, { k: "OKLab", v: artwork.okLabL != null || artwork.okLabA != null || artwork.okLabB != null ? (
L {fmtNum(artwork.okLabL, 3)} a {fmtNum(artwork.okLabA, 3)} b {fmtNum(artwork.okLabB, 3)}
) : ( "—" ), }, { k: "Relations", v: (
{(artwork.categories?.length ?? 0)} categories {(artwork.tags?.length ?? 0)} tags {(artwork.colors?.length ?? 0)} colors {(artwork.variants?.length ?? 0)} variants
), }, ]} />
{/* Metadata */}
Artwork metadata
{!meta ? None : null}
{meta ? ( {meta.id} }, ]} /> ) : (
No metadata available for this artwork.
)}
{/* File data */}
File
{!file ? Missing relation : null}
{file ? ( {file.id} }, { k: "File key", v: {file.fileKey} }, { k: "Original name", v: {file.originalFile} }, { k: "Stored name", v: file.name ?? "—" }, { k: "MIME type", v: file.fileType ?? "—" }, { k: "Size", v: fmtBytes(file.fileSize) }, { k: "Uploaded", v: fmtDate(file.uploadDate as any) }, ]} /> ) : (
This component expects the artwork query to include the file relation.
)}
{/* Variants (optional but helpful) */} {artwork.variants?.length ? ( <>
Variants
{artwork.variants .slice() .sort((a: any, b: any) => (a.type ?? "").localeCompare(b.type ?? "")) .map((v: any) => (
{v.type ?? "variant"} {v.width && v.height ? `${fmtNum(v.width)}×${fmtNum(v.height)} px` : "—"}
{v.mimeType ?? "—"} {v.fileExtension ? `(${v.fileExtension})` : ""}
{v.sizeBytes ? fmtBytes(v.sizeBytes) : "—"} {v.url ? ( Open ) : null}
))}
) : null}
); }