Examples

Simple List Example

A minimal example of a list page with basic CRUD operations.

Complete Example

1. DataSource Definition

// src/lib/server/ds/defs/EntityDS.ts
import type { DataSource } from '@wayvo-ai/core/common';
import { DefaultAttribute, DefaultDataSource, DefaultFullAccess } from '@wayvo-ai/core/ds';

export interface Entity {
  id: string;
  name: string;
  description?: string;
  createdAt: string;
}

export const EntityDataSource: DataSource<Entity> = {
  ...DefaultDataSource,
  id: 'Entity',
  tableName: 'entities',
  attributes: [
    {
      ...DefaultAttribute,
      code: 'id',
      name: 'ID',
      type: 'Text',
      column: 'id',
      maxLength: 40,
      primary: true,
      defaultValue: 'ULID',
    },
    {
      ...DefaultAttribute,
      code: 'name',
      name: 'Name',
      type: 'Text',
      column: 'name',
      maxLength: 120,
      optional: false,
    },
    {
      ...DefaultAttribute,
      code: 'description',
      name: 'Description',
      type: 'Text',
      column: 'description',
      maxLength: 500,
    },
    {
      ...DefaultAttribute,
      code: 'createdAt',
      name: 'Created At',
      type: 'Date',
      column: 'created_at',
      optional: false,
    },
  ],
  access: [
    {
      ...DefaultFullAccess,
      roleCode: 'all_users',
    },
  ],
};

2. Type Definition

// src/lib/common/ds/types/Entity.ts
export interface Entity {
  id: string;
  name: string;
  description?: string;
  createdAt: string;
}

3. Store Hook

// src/app/(secure)/entities/hooks/use-store.ts
import { useStore } from '@wayvo-ai/core/client';
import type { Entity } from '@/lib/common/ds/types/Entity';

export function useEntityStore() {
  return useStore<Entity>({
    datasourceId: 'Entity',
    page: 'entity-page',
    alias: 'entity-list',
    limit: 20,
    includeCount: true,
    autoQuery: true,
    sort: { createdAt: -1 },
  });
}

4. Table Columns

// src/app/(secure)/entities/hooks/use-table-columns.tsx
import type { Store } from '@wayvo-ai/core/common';
import type { Entity } from '@/lib/common/ds/types/Entity';
import type { AccessorKeyColumnDef } from '@tanstack/react-table';
import { HeaderCell, TableCell } from '@wayvo-ai/core/ui';
import { useMemo } from 'react';

export default function useTableColumns(store: Store<Entity>): AccessorKeyColumnDef<Entity>[] {
  return useMemo(
    () => [
      {
        accessorKey: 'name',
        header: (props) => <HeaderCell {...props} type="Text" store={store} accessorKey="name" title="Name" />,
        cell: (props) => <TableCell type="Text" attributeCode="name" {...props} />,
      },
      {
        accessorKey: 'description',
        header: (props) => (
          <HeaderCell {...props} type="Text" store={store} accessorKey="description" title="Description" />
        ),
        cell: (props) => <TableCell type="Text" attributeCode="description" {...props} />,
      },
    ],
    [store],
  );
}

5. Smart Search Columns

// src/app/(secure)/entities/hooks/smart-search-columns.ts
import type { Column } from '@wayvo-ai/core/ui/smart-search';
import type { Entity } from '@/lib/common/ds/types/Entity';

export default function useSmartSearchColumns(): Column<Entity>[] {
  return [
    {
      key: 'name',
      label: 'Name',
      type: 'Text',
      defaultOperator: 'is',
    },
    {
      key: 'description',
      label: 'Description',
      type: 'Text',
      defaultOperator: 'contains',
    },
  ];
}

6. Edit Form

// src/app/(secure)/entities/components/edit-form.tsx
'use client';

import { TextInput } from '@wayvo-ai/core/ui';
import { useCurrentRowSync } from '@wayvo-ai/core/ui';
import type { Store } from '@wayvo-ai/core/common';
import type { Entity } from '@/lib/common/ds/types/Entity';

export function EditForm({ store }: { store: Store<Entity> }) {
  const row = useCurrentRowSync(store);

  if (!row) return null;

  return (
    <div className="grid gap-4 p-4">
      <TextInput
        label="Name"
        value={row.name || ''}
        onChange={(value) => store.setValue('name', value)}
        required
      />
      <TextInput
        label="Description"
        value={row.description || ''}
        onChange={(value) => store.setValue('description', value)}
      />
    </div>
  );
}

7. Page Content

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

import { PageLayoutTemplate } from '@wayvo-ai/core/ui';
import { FileText } from 'lucide-react';
import { useEntityStore } from './hooks/use-store';
import useTableColumns from './hooks/use-table-columns';
import useSmartSearchColumns from './hooks/smart-search-columns';
import { EditForm } from './components/edit-form';

export default function EntityPageContent() {
  const store = useEntityStore();
  const tableColumns = useTableColumns(store);
  const smartSearchColumns = useSmartSearchColumns();

  return (
    <PageLayoutTemplate
      title="Entities"
      subTitle="Manage your entities"
      icon={<FileText className="h-12 w-12 text-muted-foreground" />}
      store={store}
      smartSearchColumns={smartSearchColumns}
      tableColumns={tableColumns}
      pageId="entity-page"
      itemId="entity"
      editForm={<EditForm store={store} />}
      getDefaultRow={() => ({})}
      addNewButtonText="Add Entity"
    />
  );
}

8. Page Entry Point

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

import { PageShell } from '@wayvo-ai/core/ui';
import dynamic from 'next/dynamic';

const PageContent = dynamic(() => import('./page-content'), { ssr: false });

export default function EntityPage() {
  return (
    <PageShell title="Entities" noPadding>
      <PageContent />
    </PageShell>
  );
}

What This Provides

  • ✅ List view with pagination
  • ✅ Search and filtering
  • ✅ Add/Edit dialog
  • ✅ Automatic data synchronization
  • ✅ Type-safe throughout

Next Steps

Previous
Common Mistakes