VVibeFlui

Intermediate Prompts

Use these prompts when a page needs forms, tables, custom renderers, registry handlers, and local CRUD actions.

Form and table prompt

TXT
Create a VibeFlui form-and-table example for inventory products.

Return separate files with path labels:
- schemas/products/product.resource.schema.ts
- components/vibeflui/renderers/products/ProductStatusBadge.tsx
- lib/vibeflui/registry.tsx
- lib/vibeflui/examples/product-crud-actions.ts
- app/products/page.tsx

Requirements:
- resource: products
- title: Product inventory
- identity key: id and param: id
- table columns: sku, name, category, stock, status
- status column uses renderer ProductStatusBadge
- table search enabled
- form mode modal
- form fields: sku, name, category, stock, reorderPoint, status
- category select options hardware, software, service
- status select options draft, active, discontinued
- create, edit, delete enabled
- delete uses confirm true and danger variant
- local data lives in page state
- CRUD functions live in product-crud-actions.ts
- page calls createProductCrudActions(setRows)
- no inline async handlers in <FluiKit>
- registry only imports ProductStatusBadge and registers it
- no JSX inside createRegistry

Expected action helper shape

lib/vibeflui/examples/product-crud-actions.ts

TS
import type { Dispatch, SetStateAction } from "react";
import type { FluiKitSubmitContext } from "@vibeflui/core";

export type ProductRow = {
  id: string;
  sku: string;
  name: string;
  category: string;
  stock: number;
  reorderPoint: number;
  status: string;
};

export function createProductCrudActions(setRows: Dispatch<SetStateAction<ProductRow[]>>) {
  return {
    createProduct: async (values: Record<string, unknown>) => {
      const product = mapValuesToProduct(values);
      setRows((current) => [...current, product]);
      return { status: true, code: 200, message: "Product created" };
    },
    updateProduct: async (values: Record<string, unknown>, context: FluiKitSubmitContext) => {
      const id = String(context.row?.id ?? "");
      const product = mapValuesToProduct(values, id);
      setRows((current) => current.map((row) => (row.id === id ? product : row)));
      return { status: true, code: 200, message: "Product updated" };
    },
    deleteProduct: async (row: Record<string, unknown>) => {
      const id = String(row.id ?? "");
      setRows((current) => current.filter((item) => item.id !== id));
      return { status: true, code: 200, message: "Product deleted" };
    }
  };
}

function mapValuesToProduct(values: Record<string, unknown>, id = crypto.randomUUID()): ProductRow {
  return {
    id,
    sku: String(values.sku ?? ""),
    name: String(values.name ?? ""),
    category: String(values.category ?? "hardware"),
    stock: Number(values.stock ?? 0),
    reorderPoint: Number(values.reorderPoint ?? 0),
    status: String(values.status ?? "draft")
  };
}

List response mapping prompt

Use this when a real backend already returns a specific JSON shape and the AI needs to map it into a VibeFlui table — which key holds the row array, which key holds the total count, and which response fields become which columns.

TXT
Configure a VibeFlui users table against this exact backend response for GET /api/users.
Do not change the response shape; map the schema to it.

Backend response:
{
  "success": true,
  "result": {
    "rows": [
      { "id": "usr_1", "full_name": "Ada Lovelace", "email_address": "ada@example.test", "is_active": true },
      { "id": "usr_2", "full_name": "Alan Turing", "email_address": "alan@example.test", "is_active": false }
    ],
    "count": 2
  }
}

Requirements:
- table.dataPath must point at the array (result.rows)
- table.totalPath must point at the count (result.count)
- table column "full_name" displays as label "Name"
- table column "email_address" displays as label "Email", type email
- table column "is_active" displays as label "Active", rendered as Yes/No (boolean), not the raw true/false value
- identity key and param are both "id"
- explain which response key you used for each of dataPath/totalPath and why

Expected mapping the AI should produce and explain back:

Response keySchema keyPurpose
result.rowstable.dataPath: "result.rows"Array of rows rendered in the table.
result.counttable.totalPath: "result.count"Total row count used for pagination.
full_nametable.columns[].key: "full_name", label: "Name"Displayed column.
email_addresstable.columns[].key: "email_address", label: "Email", type: "email"Displayed column.
is_activetable.columns[].key: "is_active", label: "Active", boolean-formattedDisplayed column — this is the field that is "true/false" per row, not the backend's overall success flag.

Note the difference between success at the top of this response (whether the request succeeded) and is_active inside each row (a business field about that user). Only map fields that should actually be visible to table.columns — do not surface the top-level success flag as a column.

Custom field prompt

TXT
Add a custom Notes field to the products form.

Return separate files:
- components/vibeflui/fields/ProductNotesField.tsx
- lib/vibeflui/registry.tsx
- schemas/products/product.resource.schema.ts

Rules:
- include a file path label before each code block
- field uses type custom and component ProductNotesField
- ProductNotesField is a React component file
- registry imports ProductNotesField and registers it under components
- schema references component by string
- no JSX inside registry