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 */}
{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}
);
}