| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196 |
- 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[];
|