| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470 |
- import { defineCatalog } from "@json-render/core";
- import { schema } from "@json-render/react/schema";
- import { z } from "zod";
- /**
- * Dashboard Catalog
- *
- * Components map directly to shadcn/ui components.
- * Actions correspond to real API endpoints in /api/v1/*
- */
- export const dashboardCatalog = defineCatalog(schema, {
- components: {
- // Layout
- Stack: {
- props: z.object({
- direction: z.enum(["horizontal", "vertical"]).nullable(),
- gap: z.enum(["sm", "md", "lg"]).nullable(),
- }),
- slots: ["default"],
- description: "Flex layout container",
- example: { direction: "vertical", gap: "md" },
- },
- Accordion: {
- props: z.object({
- type: z.enum(["single", "multiple"]).nullable(),
- }),
- slots: ["default"],
- description: "Collapsible accordion container",
- },
- AccordionItem: {
- props: z.object({
- value: z.string(),
- title: z.string(),
- }),
- slots: ["default"],
- description: "Accordion item with trigger and content",
- },
- // Form
- Button: {
- props: z.object({
- label: z.string(),
- variant: z
- .enum(["default", "secondary", "destructive", "outline", "ghost"])
- .nullable(),
- action: z.string(),
- actionParams: z.record(z.string(), z.unknown()).nullable(),
- disabled: z.boolean().nullable(),
- }),
- description:
- "Clickable button. Use actionParams to pass parameters to the action (e.g., { limit: 5, sort: 'newest' })",
- example: { label: "Save", variant: "default", action: "formSubmit" },
- },
- Input: {
- props: z.object({
- label: z.string().nullable(),
- valuePath: z.string(),
- placeholder: z.string().nullable(),
- type: z.enum(["text", "email", "password", "number", "tel"]).nullable(),
- }),
- description: "Text input field",
- example: {
- label: "Email",
- valuePath: "/form/email",
- placeholder: "you@example.com",
- type: "email",
- },
- },
- Form: {
- props: z.object({
- submitAction: z.string(),
- submitActionParams: z.record(z.string(), z.unknown()).nullable(),
- }),
- slots: ["default"],
- description:
- "Form container that enables Enter key submission. Wrap form inputs (Input, Select, Checkbox, etc.) and a submit Button inside this component.",
- },
- // Display
- Badge: {
- props: z.object({
- text: z.string(),
- variant: z
- .enum(["default", "secondary", "destructive", "outline"])
- .nullable(),
- }),
- description: "Status badge",
- example: { text: "Active", variant: "default" },
- },
- Alert: {
- props: z.object({
- variant: z.enum(["default", "destructive"]).nullable(),
- title: z.string(),
- description: z.string().nullable(),
- }),
- description: "Alert message",
- },
- Separator: {
- props: z.object({}),
- description: "Visual divider",
- },
- Avatar: {
- props: z.object({
- src: z.string().nullable(),
- alt: z.string().nullable(),
- fallback: z.string(),
- }),
- description: "User avatar image with fallback initials",
- },
- Checkbox: {
- props: z.object({
- label: z.string().nullable(),
- valuePath: z.string(),
- defaultChecked: z.boolean().nullable(),
- }),
- description: "Checkbox input",
- },
- Dialog: {
- props: z.object({
- trigger: z.string(),
- title: z.string(),
- description: z.string().nullable(),
- }),
- slots: ["default"],
- description: "Modal dialog with trigger button",
- },
- Drawer: {
- props: z.object({
- trigger: z.string(),
- title: z.string(),
- description: z.string().nullable(),
- side: z.enum(["top", "bottom", "left", "right"]).nullable(),
- }),
- slots: ["default"],
- description: "Slide-out drawer panel",
- },
- DropdownMenu: {
- props: z.object({
- trigger: z.string(),
- items: z.array(
- z.object({
- label: z.string(),
- action: z.string().nullable(),
- actionParams: z.record(z.string(), z.unknown()).nullable(),
- }),
- ),
- }),
- description: "Dropdown menu with action items",
- },
- Label: {
- props: z.object({
- text: z.string(),
- htmlFor: z.string().nullable(),
- }),
- description: "Form label",
- },
- Pagination: {
- props: z.object({
- currentPage: z.number(),
- totalPages: z.number(),
- onPageChange: z.string().nullable(),
- }),
- description: "Page navigation",
- },
- Popover: {
- props: z.object({
- trigger: z.string(),
- }),
- slots: ["default"],
- description: "Popover with trigger",
- },
- Progress: {
- props: z.object({
- value: z.number(),
- max: z.number().nullable(),
- }),
- description: "Progress bar",
- },
- RadioGroup: {
- props: z.object({
- valuePath: z.string(),
- options: z.array(
- z.object({
- value: z.string(),
- label: z.string(),
- }),
- ),
- defaultValue: z.string().nullable(),
- }),
- description: "Radio button group",
- },
- Select: {
- props: z.object({
- valuePath: z.string(),
- placeholder: z.string().nullable(),
- options: z.array(
- z.object({
- value: z.string(),
- label: z.string(),
- }),
- ),
- }),
- description: "Dropdown select input",
- },
- Skeleton: {
- props: z.object({
- width: z.string().nullable(),
- height: z.string().nullable(),
- }),
- description: "Loading placeholder",
- },
- Spinner: {
- props: z.object({
- size: z.enum(["sm", "md", "lg"]).nullable(),
- }),
- description: "Loading spinner",
- },
- Switch: {
- props: z.object({
- label: z.string().nullable(),
- valuePath: z.string(),
- defaultChecked: z.boolean().nullable(),
- }),
- description: "Toggle switch",
- },
- Tabs: {
- props: z.object({
- defaultValue: z.string().nullable(),
- tabs: z.array(
- z.object({
- value: z.string(),
- label: z.string(),
- }),
- ),
- }),
- slots: ["default"],
- description: "Tabbed content container",
- },
- TabContent: {
- props: z.object({
- value: z.string(),
- }),
- slots: ["default"],
- description: "Content for a specific tab",
- },
- Textarea: {
- props: z.object({
- label: z.string().nullable(),
- valuePath: z.string(),
- placeholder: z.string().nullable(),
- rows: z.number().nullable(),
- }),
- description: "Multi-line text input",
- },
- Tooltip: {
- props: z.object({
- content: z.string(),
- }),
- slots: ["default"],
- description: "Tooltip on hover",
- },
- Table: {
- props: z.object({
- statePath: z.string(),
- columns: z.array(
- z.object({
- key: z.string(),
- label: z.string(),
- }),
- ),
- rowActions: z
- .array(
- z.object({
- label: z.string(),
- action: z.string(),
- variant: z
- .enum([
- "default",
- "secondary",
- "destructive",
- "outline",
- "ghost",
- ])
- .nullable(),
- }),
- )
- .nullable(),
- emptyMessage: z.string().nullable(),
- }),
- description: "Data table with optional row actions (delete, edit, etc.)",
- example: {
- statePath: "/customers/data",
- columns: [
- { key: "name", label: "Name" },
- { key: "email", label: "Email" },
- ],
- },
- },
- // Typography
- Heading: {
- props: z.object({
- text: z.string(),
- level: z.enum(["h1", "h2", "h3", "h4"]).nullable(),
- }),
- description: "Section heading",
- example: { text: "Dashboard", level: "h1" },
- },
- Text: {
- props: z.object({
- content: z.string(),
- muted: z.boolean().nullable(),
- }),
- description: "Text content",
- example: { content: "Welcome back! Here is your overview." },
- },
- // Charts
- BarChart: {
- props: z.object({
- title: z.string().nullable(),
- statePath: z.string(),
- xKey: z.string(),
- yKey: z.string(),
- aggregate: z.enum(["sum", "count", "avg"]).nullable(),
- color: z.string().nullable(),
- height: z.number().nullable(),
- }),
- description:
- "Bar chart visualization. statePath points to array of objects, xKey is the category/group field, yKey is the numeric value field. Use aggregate='count' to count items grouped by xKey (yKey becomes the count). For dates, xKey values are auto-formatted.",
- },
- LineChart: {
- props: z.object({
- title: z.string().nullable(),
- statePath: z.string(),
- xKey: z.string(),
- yKey: z.string(),
- aggregate: z.enum(["sum", "count", "avg"]).nullable(),
- color: z.string().nullable(),
- height: z.number().nullable(),
- }),
- description:
- "Line chart visualization. statePath points to array of objects, xKey is the x-axis field, yKey is the numeric value field. Use aggregate='count' to count items grouped by xKey. For dates, xKey values are auto-formatted.",
- },
- },
- actions: {
- // Customers - use these for customer-related widgets
- viewCustomers: {
- params: z.object({
- status: z.enum(["active", "inactive"]).nullable(),
- limit: z.number().nullable(),
- sort: z.enum(["newest", "oldest"]).nullable(),
- }),
- description:
- "Fetch customers from GET /api/v1/customers. Supports ?limit=N&sort=newest|oldest. Data available at 'customers.data'",
- },
- refreshCustomers: {
- params: z.object({
- limit: z.number().nullable(),
- sort: z.enum(["newest", "oldest"]).nullable(),
- }),
- description:
- "Refresh customers from GET /api/v1/customers. Supports ?limit=N&sort=newest|oldest. Data available at 'customers.data'",
- },
- createCustomer: {
- params: z.object({
- name: z.string(),
- email: z.string(),
- phone: z.string().nullable(),
- }),
- description: "Create new customer via POST /api/v1/customers",
- },
- deleteCustomer: {
- params: z.object({
- customerId: z.string(),
- }),
- description: "Delete a customer via DELETE /api/v1/customers/:id",
- },
- // Invoices - use these for invoice-related widgets
- viewInvoices: {
- params: z.object({
- status: z.enum(["draft", "sent", "paid", "overdue"]).nullable(),
- }),
- description:
- "Fetch invoices from GET /api/v1/invoices. Data available at 'invoices.data'",
- },
- refreshInvoices: {
- params: z.object({}),
- description:
- "Refresh invoices from GET /api/v1/invoices. Data available at 'invoices.data'",
- },
- createInvoice: {
- params: z.object({
- customerId: z.string(),
- dueDate: z.string(),
- }),
- description: "Create new invoice via POST /api/v1/invoices",
- },
- sendInvoice: {
- params: z.object({
- invoiceId: z.string(),
- }),
- description: "Send invoice to customer",
- },
- markInvoicePaid: {
- params: z.object({
- invoiceId: z.string(),
- }),
- description: "Mark invoice as paid",
- },
- // Expenses - use these for expense-related widgets
- viewExpenses: {
- params: z.object({
- status: z.enum(["pending", "approved", "rejected"]).nullable(),
- }),
- description:
- "Fetch expenses from GET /api/v1/expenses. Data available at 'expenses.data'",
- },
- refreshExpenses: {
- params: z.object({}),
- description:
- "Refresh expenses from GET /api/v1/expenses. Data available at 'expenses.data'",
- },
- createExpense: {
- params: z.object({
- vendor: z.string(),
- category: z.string(),
- amount: z.number(),
- description: z.string().nullable(),
- }),
- description: "Create new expense via POST /api/v1/expenses",
- },
- approveExpense: {
- params: z.object({
- expenseId: z.string(),
- }),
- description: "Approve expense",
- },
- },
- });
|