CrudHandler
CrudHandler is the primary React component for CRUD pages. It connects to a BaseCrudView endpoint and renders the list table, the Add form, and the detail view — all driven by the backend.
import { CrudHandler } from '@zango-core/crud/table';
Default CRUD Page
The simplest usage — pass the endpoint and an optional header title:
const Patients = () => (
<CrudHandler
api_endpoint="/patients/"
headerProps={{ title: "Patients" }}
/>
);
This renders a full list with search, sort, pagination, Add button, and a default detail drawer.
Custom Detail Drawer
Override the detail drawer with your own component while keeping the default table:
import { CrudHandler } from '@zango-core/crud/table';
const PatientDetail = ({ selectedRow, data, workflowDetails, open, onClose }) => {
if (!open) return null;
return (
<div>
<h2>{data?.title}</h2>
{Object.entries(data?.general_details?.fields || {}).map(([k, f]) => (
<div key={k}>
<strong>{f.display_name}:</strong> {f.value}
</div>
))}
<button onClick={onClose}>Close</button>
</div>
);
};
const Patients = () => (
<CrudHandler
api_endpoint="/patients/"
customDrawerDetail={PatientDetail}
/>
);
Detail Drawer Props
| Prop | Description |
|---|---|
selectedRow | The raw row data from the table |
data | Full detail response from the backend |
data.title | Record title |
data.general_details.fields | Object of { display_name, value } pairs |
workflowDetails | Workflow status and transitions (if workflow is enabled) |
open | Whether the drawer is open |
onClose | Callback to close the drawer |
Full-Page Detail View (Navigate on Row Click)
For primary entities where a drawer isn't enough — navigate to a full detail page on row click:
import { useNavigate } from 'react-router-dom';
import { CrudHandler, TableBody } from '@zango-core/crud/table';
// Pass a component ref — not JSX
const NavigateTableBody = () => <TableBody defaultDetailView="navigate" />;
const PatientDetail = ({ data, generalDetails, objectUuid }) => {
const navigate = useNavigate();
if (!data) return null;
return (
<div>
<button onClick={() => navigate(-1)}>← Back</button>
<h1>{data.title}</h1>
{Object.entries(generalDetails?.fields || {}).map(([k, f]) => (
<div key={k}>
<strong>{f.display_name}:</strong> {f.value}
</div>
))}
</div>
);
};
const Patients = () => (
<CrudHandler
api_endpoint="/patients/"
enableDetailViewRoute={true}
customMainDetail={PatientDetail}
customTableBody={NavigateTableBody}
/>
);
Full-Page Detail Props
| Prop | Description |
|---|---|
data | Full detail response from the backend |
generalDetails | camelCase alias for data.general_details |
objectUuid | UUID of the current record |
workflowDetails | Workflow status and transitions |
Custom List Layout (Kanban, Calendar, Cards)
Swap the table body for a custom layout while keeping all the CRUD wiring:
import { CrudHandler, TableBody } from '@zango-core/crud/table';
const KanbanBody = () => (
<TableBody
customTableBody={({ rows }) => (
<div style={{ display: 'flex', gap: 16 }}>
{rows.map(row => (
<div key={row.id} className="kanban-card">{row.cells[0].value}</div>
))}
</div>
)}
/>
);
const Tasks = () => (
<CrudHandler
api_endpoint="/tasks/"
customTableBody={KanbanBody}
/>
);
Props Reference
| Prop | Type | Description |
|---|---|---|
api_endpoint | string | The URL of your BaseCrudView endpoint |
headerProps | object | { title: string } — page header |
customDrawerDetail | component | Custom drawer detail component |
customMainDetail | component | Full-page detail component (use with enableDetailViewRoute) |
customTableBody | component ref | Custom table body component |
enableDetailViewRoute | boolean | Enable route-based navigation for detail view |
AppBuilder Route Config
After creating your React component, register it in AppBuilder:
Default CRUD page (no React component needed):
{
"page_type": "crud",
"extra_params": { "api_endpoint": "/patients/" }
}
Custom component page:
{
"page_type": "custom",
"component": "Patients"
}
The component value must exactly match the export name in pages/index.js.