import { clsx } from "clsx"; import { formatDateLong } from "~/lib/format.js"; import { FieldType } from "@capakraken/shared"; import type { BlueprintFieldDefinition } from "@capakraken/shared"; interface Props { fieldDefs: BlueprintFieldDefinition[]; values: Record; className?: string; } function renderValue(fieldDef: BlueprintFieldDefinition, value: unknown): React.ReactNode { if (value === null || value === undefined || value === "") { return ; } switch (fieldDef.type) { case FieldType.TEXT: case FieldType.TEXTAREA: case FieldType.URL: case FieldType.EMAIL: return {String(value)}; case FieldType.NUMBER: return ( {typeof value === "number" ? value.toLocaleString() : String(value)} ); case FieldType.BOOLEAN: { const bool = value === true || value === "true" || value === 1; return ( {bool ? "Yes" : "No"} ); } case FieldType.DATE: { const dateStr = String(value); const parsed = new Date(dateStr); if (isNaN(parsed.getTime())) { return {dateStr}; } return {formatDateLong(parsed)}; } case FieldType.SELECT: { const strVal = String(value); const option = fieldDef.options?.find((o) => o.value === strVal); return {option?.label ?? strVal}; } case FieldType.MULTI_SELECT: { const rawVals = Array.isArray(value) ? value : [value]; const strVals = rawVals.map((v) => String(v)).filter(Boolean); if (strVals.length === 0) { return ; } const labels = strVals.map((v) => { const option = fieldDef.options?.find((o) => o.value === v); return { value: v, label: option?.label ?? v, color: option?.color }; }); return (
{labels.map(({ value: v, label }) => ( {label} ))}
); } default: return {String(value)}; } } function FieldRow({ fieldDef, value }: { fieldDef: BlueprintFieldDefinition; value: unknown }) { return (
{fieldDef.label}
{renderValue(fieldDef, value)}
{fieldDef.description &&

{fieldDef.description}

}
); } export function DynamicFieldRenderer({ fieldDefs, values, className }: Props) { const sorted = [...fieldDefs].sort((a, b) => a.order - b.order); // Separate grouped and ungrouped fields const ungrouped = sorted.filter((f) => !f.group); const groupMap = new Map(); for (const field of sorted) { if (!field.group) continue; const existing = groupMap.get(field.group) ?? []; existing.push(field); groupMap.set(field.group, existing); } if (sorted.length === 0) { return null; } return (
{ungrouped.length > 0 && (
{ungrouped.map((field) => ( ))}
)} {[...groupMap.entries()].map(([group, fields]) => (

{group}

{fields.map((field) => ( ))}
))}
); }