Examples

CRUD Form Example

Complete CRUD form example with dialog, validation, and error handling.

Complete Example

Dialog Component

// src/app/(secure)/entities/components/entity-dialog.tsx
'use client';

import { useCallback } from 'react';
import { Popup, useCurrentRowSync, useIsStoreDirty, useIsStorePosting, showSuccess, showError } from '@wayvo-ai/core/ui';
import { TextInput, TextareaInput } from '@wayvo-ai/core/ui';
import type { Store } from '@wayvo-ai/core/common';
import type { Entity } from '@/lib/common/ds/types/Entity';

interface Props {
  open: boolean;
  onOpenChange: (open: boolean) => void;
  store: Store<Entity>;
  editingRow?: Entity | null;
}

export function EntityDialog({ open, onOpenChange, store, editingRow }: Props) {
  const row = useCurrentRowSync(store);
  const isDirty = useIsStoreDirty(store);
  const isPosting = useIsStorePosting(store);

  const handleSave = useCallback(async () => {
    try {
      if (!row?.name?.trim()) {
        showError('Name is required');
        return;
      }

      const success = await store.save();
      if (success) {
        showSuccess(editingRow ? 'Entity updated' : 'Entity created');
        onOpenChange(false);
      }
    } catch (error) {
      showError('Failed to save entity');
    }
  }, [store, row, editingRow, onOpenChange]);

  const handleClose = useCallback(() => {
    store.resetStore();
    onOpenChange(false);
  }, [store, onOpenChange]);

  if (!open) return null;

  return (
    <Popup
      title={editingRow ? 'Edit Entity' : 'Add Entity'}
      onClose={handleClose}
      width={520}
      height={430}
      footer={
        <>
          <Button variant="outline" onClick={handleClose} disabled={isPosting}>
            Cancel
          </Button>
          <Button onClick={handleSave} disabled={isPosting || !isDirty || !row}>
            {isPosting && <Loader2 className="mr-2 size-4 animate-spin" />}
            Save
          </Button>
        </>
      }
    >
      {row && (
        <div className="grid gap-4">
          <TextInput
            label="Name"
            value={row.name || ''}
            onChange={(value) => store.setValue('name', value)}
            required
          />
          <TextareaInput
            label="Description"
            value={row.description || ''}
            onChange={(value) => store.setValue('description', value)}
            rows={4}
          />
        </div>
      )}
    </Popup>
  );
}

Usage in Page Content

// src/app/(secure)/entities/page-content.tsx
'use client';

import { useState, useCallback } from 'react';
import { PageLayoutTemplate } from '@wayvo-ai/core/ui';
import { useEntityStore } from './hooks/use-store';
import useTableColumns from './hooks/use-table-columns';
import useSmartSearchColumns from './hooks/smart-search-columns';
import { EntityDialog } from './components/entity-dialog';
import type { Entity } from '@/lib/common/ds/types/Entity';

export default function EntityPageContent() {
  const store = useEntityStore();
  const tableColumns = useTableColumns(store);
  const smartSearchColumns = useSmartSearchColumns();
  const [isDialogOpen, setIsDialogOpen] = useState(false);
  const [editingRow, setEditingRow] = useState<Entity | null>(null);

  const handleAdd = useCallback(() => {
    store.createNew({ partialRecord: { status: 'draft' } });
    setEditingRow(null);
    setIsDialogOpen(true);
  }, [store]);

  const handleEdit = useCallback((row: Entity) => {
    store.setCurrentRow(row);
    setEditingRow(row);
    setIsDialogOpen(true);
  }, [store]);

  return (
    <>
      <PageLayoutTemplate
        title="Entities"
        store={store}
        smartSearchColumns={smartSearchColumns}
        tableColumns={tableColumns}
        pageId="entity-page"
        itemId="entity"
        onRowClick={handleEdit}
        addNewButtonText="Add Entity"
        onAddNew={handleAdd}
      />
      <EntityDialog
        open={isDialogOpen}
        onOpenChange={setIsDialogOpen}
        store={store}
        editingRow={editingRow}
      />
    </>
  );
}

Key Features

  • ✅ Form validation
  • ✅ Loading states
  • ✅ Error handling
  • ✅ Success feedback
  • ✅ Proper dialog initialization

Next Steps

Previous
Simple List