import { createCatalog } from "@json-render/core"; import { z } from "zod"; /** * Dashboard component catalog * * This defines the ONLY components that the AI can generate. * It acts as a guardrail - the AI cannot create arbitrary HTML/CSS. * * Note: OpenAI structured output requires all fields to be required. * Use .nullable() instead of .optional() for optional fields. */ export const dashboardCatalog = createCatalog({ name: "dashboard", components: { // Layout Components Card: { props: z.object({ title: z.string().nullable(), description: z.string().nullable(), padding: z.enum(["sm", "md", "lg"]).nullable(), }), hasChildren: true, description: "A card container with optional title", }, Grid: { props: z.object({ columns: z.number().min(1).max(4).nullable(), gap: z.enum(["sm", "md", "lg"]).nullable(), }), hasChildren: true, description: "Grid layout with configurable columns", }, Stack: { props: z.object({ direction: z.enum(["horizontal", "vertical"]).nullable(), gap: z.enum(["sm", "md", "lg"]).nullable(), align: z.enum(["start", "center", "end", "stretch"]).nullable(), }), hasChildren: true, description: "Flex stack for horizontal or vertical layouts", }, // Data Display Components Metric: { props: z.object({ label: z.string(), valuePath: z.string(), format: z.enum(["number", "currency", "percent"]).nullable(), trend: z.enum(["up", "down", "neutral"]).nullable(), trendValue: z.string().nullable(), }), description: "Display a single metric with optional trend indicator", }, Chart: { props: z.object({ type: z.enum(["bar", "line", "pie", "area"]), dataPath: z.string(), title: z.string().nullable(), height: z.number().nullable(), }), description: "Display a chart from array data", }, Table: { props: z.object({ dataPath: z.string(), columns: z.array( z.object({ key: z.string(), label: z.string(), format: z.enum(["text", "currency", "date", "badge"]).nullable(), }), ), }), description: "Display tabular data", }, List: { props: z.object({ dataPath: z.string(), emptyMessage: z.string().nullable(), }), hasChildren: true, description: "Render a list from array data", }, // Interactive Components Button: { props: z.object({ label: z.string(), variant: z.enum(["primary", "secondary", "danger", "ghost"]).nullable(), size: z.enum(["sm", "md", "lg"]).nullable(), action: z.string(), disabled: z.boolean().nullable(), }), description: "Clickable button with action", }, Select: { props: z.object({ label: z.string().nullable(), bindPath: z.string(), options: z.array( z.object({ value: z.string(), label: z.string(), }), ), placeholder: z.string().nullable(), }), description: "Dropdown select input", }, DatePicker: { props: z.object({ label: z.string().nullable(), bindPath: z.string(), placeholder: z.string().nullable(), }), description: "Date picker input", }, // Typography Heading: { props: z.object({ text: z.string(), level: z.enum(["h1", "h2", "h3", "h4"]).nullable(), }), description: "Section heading", }, Text: { props: z.object({ content: z.string(), variant: z.enum(["body", "caption", "label"]).nullable(), color: z .enum(["default", "muted", "success", "warning", "danger"]) .nullable(), }), description: "Text paragraph", }, // Status Components Badge: { props: z.object({ text: z.string(), variant: z .enum(["default", "success", "warning", "danger", "info"]) .nullable(), }), description: "Small status badge", }, Alert: { props: z.object({ type: z.enum(["info", "success", "warning", "error"]), title: z.string(), message: z.string().nullable(), dismissible: z.boolean().nullable(), }), description: "Alert/notification banner", }, // Special Components Divider: { props: z.object({ label: z.string().nullable(), }), description: "Visual divider", }, Empty: { props: z.object({ title: z.string(), description: z.string().nullable(), action: z.string().nullable(), actionLabel: z.string().nullable(), }), description: "Empty state placeholder", }, }, actions: { export_report: { description: "Export the current dashboard to PDF" }, refresh_data: { description: "Refresh all metrics and charts" }, view_details: { description: "View detailed information" }, apply_filter: { description: "Apply the current filter settings" }, }, validation: "strict", }); // Export the component list for the AI prompt export const componentList = dashboardCatalog.componentNames as string[];