Blocks
Blocks are imported from protobase/layout. “Record” below means the record of the RecordCard or CardRow card around a block (see Data); filter is AIP-160 and sort AIP-132, as in the list API. Every block below appears in the ERP example’s sales overview:
/** @jsxImportSource protobase */import { Action, Card, CardRow, Field, Grid, ModalForm, Page, RecordCard, Show, Stat, Table, component, page } from 'protobase/layout'
/** Orders per day for the last `days` days, drawn by protobase.ui.tsx. */export const OrdersPerDay = component<{ days: 7 | 30 | 90 }>('OrdersPerDay')
/** The sales team's start page: what needs attention today, and the leads to follow up. */export const salesOverview = page( 'overview', <Page title="Sales overview" description="What needs attention today: unpaid orders, overdue invoices and new leads."> <Show when="orders.count = 0"> <Card title="No orders yet">Create the first order from Orders; this page fills up as the team sells.</Card> </Show> <Grid columns={4}> <Stat label="Draft orders" resource="orders" filter="status = 'draft'" /> <Stat label="Delivered, not paid" resource="orders" filter="status = 'delivered' AND paid = false" /> <Stat label="Overdue invoices" resource="invoices" filter="status = 'overdue'" /> <Stat label="Open leads" resource="companies" filter="status = 'lead'" /> </Grid> <Grid columns={3}> <RecordCard title="Biggest unpaid delivery" resource="orders" filter="status = 'delivered' AND paid = false" sort="total desc" empty="Every delivered order is paid." actions={<Action name="markPaid" variant="primary" />} > <Grid columns={2}> <Field name="number" /> <Field name="total" /> <Field name="companyId" label="Customer" /> <Field name="companyId.city" label="City" /> </Grid> <Show when="companyId.status = 'dormant'">The customer has gone quiet: call before sending a reminder.</Show> </RecordCard> <RecordCard title="Oldest overdue invoice" resource="invoices" filter="status = 'overdue'" sort="issuedAt" empty="Nothing is overdue." actions={[<Action name="send" label="Send reminder" />, <Action name="recordPayment" variant="primary" />]} > <Grid columns={2}> <Field name="number" /> <Field name="total" /> <Field name="companyId" label="Customer" /> <Field name="dueAt" label="Due" /> </Grid> </RecordCard> <Card title="Orders per day" description="The last 30 days."> <OrdersPerDay days={30} /> </Card> </Grid> <Grid columns={2}> <Table title="Overdue invoices" resource="invoices" filter="status = 'overdue'" sort="issuedAt" columns={['number', 'companyId', 'dueAt', 'total']} pageSize={5} empty="Nothing is overdue." /> <Table title="Latest orders" resource="orders" sort="createdAt desc" columns={['number', 'companyId', 'status', 'total']} pageSize={5} /> </Grid> <CardRow title="New leads" description="Companies that have not ordered yet." resource="companies" filter="status = 'lead'" sort="createdAt desc" limit={8} actions={<ModalForm mode="create" label="Add lead" resource="companies" fields={['name', 'city', 'countryCode', 'email']} values={{ status: 'lead' }} />} > <Field name="name" label={false} /> <Field name="city" /> <Field name="email" /> <ModalForm mode="edit" label="Edit" fields={['name', 'city', 'email', 'phone', 'status']} /> </CardRow> </Page>, { nav: { group: 'Sales', order: 0 }, icon: 'layout-dashboard' },)Layout
Section titled “Layout”The root of every page, and only the root.
| Prop | |
|---|---|
title |
Heading, and the page’s name in the sidebar and breadcrumbs. Required. |
description |
A line under the title. |
actions |
Elements beside the title, such as a ModalForm. |
Columns that collapse to one on a phone. A child’s span (1 to 4) makes it wider.
| Prop | |
|---|---|
columns |
1 to 4; default 2. |
span |
Its own width when it sits in another grid. |
<Grid columns={4}> <Stat label="Draft orders" resource="orders" filter="status = 'draft'" /> ...</Grid>A box with a heading. Text and Links inside it run together as paragraphs.
| Prop | |
|---|---|
title, description |
The heading. |
actions |
Elements in the heading. |
span |
Width in a grid. |
<Card title="No orders yet">Create the first order from Orders; this page fills up as the team sells.</Card>A link in text. A path starting with / stays in the app; other links open as usual (https: in a new tab, mailto: in the mail app). Its text is its children, else the href.
| Prop | |
|---|---|
href |
Required. |
One number or value with a label. Exactly one of value, resource and field.
| Prop | |
|---|---|
label |
Required. |
value |
A fixed value. |
resource, filter |
Counts the matching records the user can see; the number links to that filtered list. |
field |
A field of the record, through relations (plan.name). |
format |
Instead of the view’s format, such as badge. |
description |
A line under the value. |
span |
Width in a grid. |
<Stat label="Delivered, not paid" resource="orders" filter="status = 'delivered' AND paid = false" />A page of records at a time, with arrows to page through the rest. The first column opens the record; relations show the related record’s name.
| Prop | |
|---|---|
resource, filter, sort |
Which records, in which order. resource is required. |
columns |
Field names; default the view’s list columns. |
pageSize |
Rows per page; default 10. |
title, description, actions |
The heading. |
empty |
Text when nothing matches; default “Nothing here yet.”. |
span |
Width in a grid. |
<Table title="Overdue invoices" resource="invoices" filter="status = 'overdue'" sort="issuedAt" columns={['number', 'companyId', 'dueAt', 'total']} pageSize={5} empty="Nothing is overdue." />RecordCard
Section titled “RecordCard”One record and what is inside it: the one recordKey names (the key as the record’s URL has it), else the first that matches filter in sort order. Its children and its heading actions work on that record.
| Prop | |
|---|---|
resource, filter, sort |
Which record. resource is required. |
recordKey |
A fixed record instead. |
title, description, actions |
The heading. |
empty |
Text when no record matches. |
span |
Width in a grid. |
<RecordCard title="Biggest unpaid delivery" resource="orders" filter="status = 'delivered' AND paid = false" sort="total desc" actions={<Action name="markPaid" variant="primary" />}> <Field name="number" /> <Field name="companyId.city" label="City" /></RecordCard>CardRow
Section titled “CardRow”Up to limit records side by side, each a small card made from the children, which work on that card’s record. The heading’s actions do not have a record: use them for a ModalForm mode="create".
| Prop | |
|---|---|
resource, filter, sort |
Which records, in which order. resource is required. |
limit |
How many; default 12. |
title, description, actions |
The heading. |
empty |
Text when nothing matches. |
span |
Width in a grid. |
A label over a value of the record, formatted as on the record page: statuses as badges, relations as the related record’s name (linked), the view’s prefix and decimals.
| Prop | |
|---|---|
name |
A field, through relations (companyId.city). Required. |
label |
Default: the view’s label, else the name in words. false shows the value alone. |
format |
Instead of the view’s format. |
Progress
Section titled “Progress”A bar for value out of max, with both numbers. It turns amber from 90%.
| Prop | |
|---|---|
value |
A number, or the name of a field of the record. Required. |
max |
A number or a field; default 100. |
label, span |
<Progress label="Seats" value="seatsUsed" max="plan.seats" />Behaviour
Section titled “Behaviour”Action
Section titled “Action”A button for a named action of the view, on the record around it.
| Prop | |
|---|---|
name |
The action’s name in the view. Required. |
resource |
Outside a record: the resource whose view declares it. |
label |
Default: the action’s label. |
variant |
primary, secondary (default), danger or ghost. |
ModalForm
Section titled “ModalForm”A button that opens a form in a dialog. create makes a record of resource; edit changes the record around it (or resource with recordKey), sending only the changed fields with the record’s ETag. The editors are the record page’s, and errors from the server show on their fields. It is not shown to a user who may not create or update, and it keeps only the fields they may write.
| Prop | |
|---|---|
mode |
create or edit. Required. |
label |
The button. Required. |
fields |
The fields of the form, writable ones only. Required. |
resource |
Default: the record’s resource. |
recordKey |
Edit this record. |
title |
The dialog’s title; default the label. |
values |
Sent with every create, such as { status: 'lead' } or a parent’s key. |
variant |
As for Action. |
<ModalForm mode="create" label="Add lead" resource="companies" fields={['name', 'city', 'countryCode', 'email']} values={{ status: 'lead' }} />Its children while when holds.
| Prop | |
|---|---|
when |
A condition. Required. |
<Show when="orders.count = 0"> <Card title="No orders yet">...</Card></Show>