-
Notifications
You must be signed in to change notification settings - Fork 0
Kanban Board
A Kanban board per entity, opt-in through the schema. The list stays the default; the board appears as an extra tab on the list head once a schema declares it.
A tool whose records move between statuses — audit findings, action items, tickets, pupils' permission slips — is often faster to read as a board than as a table. The board is not a replacement for the list view, it is a second way to set one field on a record. The list view remains the default for browsing, sorting, exporting and bulk actions; the board is the path of least resistance for moving a record from one status to another.
If your records do not carry a status-like enum field, the board has nothing to draw columns from and you do not need it.
Add a view.board declaration to the schema. Without it the view does not exist, the same
posture as DASHBOARD and WIZARD:
export const SCHEMA = {
// …
view: {
board: {
columnField: 'status', // enum field that becomes the columns
cardFields: ['owner', 'due', 'effort'], // optional: up to three fields on each card
limit: 50, // optional: per-column card limit (default 50)
},
},
}columnField must point at an existing enum field. The columns take their values and their
order straight from the schema's values, so the first entry in values always sits at the
left end regardless of where the records sit in the data block.
Records whose value is empty or no longer in values land in a small Unassigned reservoir
at the right. A card that belongs to no column would otherwise be invisible, and pretending the
first column catches them would silently mis-categorise.
The school-trip demo (docs/demos/school-trip/)
is the runnable worked example. Its view.board turns the three consent states into columns
and shows the contact path and payment state on each card:
view: {
board: {
columnField: 'consent', // ausstehend → liegt vor → verweigert
cardFields: ['guardian', 'phone', 'payment'], // wer, wie erreichbar, ob bezahlt
},
}The default in cardFields if you do not set it: the first three non-computed, non-attachment,
non-title, non-column fields in the schema. effort, owner, priority and the like come out
automatically when the field shape is right.
A card drag from one column to another sets the enum field through the normal change path. The
same mutate function the edit form uses, the same write that an undo undoes, the same entry
that the change log picks up. From the recipient's point of view there is no difference between
"set status to done via the form" and "drag card into the done column" — both produce the
same audit-trail entry, both respond to Ctrl/Cmd+Z in the same way, both ask the AI assistant
the same question about a proposed change.
The pending move lives in the session, not in the file. An aborted move is not an undo because
nothing has been written yet; only after Enter (or releasing the mouse on a valid drop target)
does the change hit the data block.
| Input | What it does |
|---|---|
| Mouse drag | Pick up a card, drop it on a column. |
| Touch drag | Same gesture, native HTML5 drag-and-drop, works on iPadOS 15.4+. |
Tab / Shift+Tab
|
Move focus between cards. |
← / →
|
Move focus to the adjacent column. |
↑ / ↓
|
Move focus within a column. |
Enter |
Commit the pending move. |
Esc |
Abort the pending move. |
A focused card responds to the arrow keys to step between columns, Enter to commit, Esc to
abort. The board is the same single-file build as the rest of the app — no extra dependency,
no second library to vendor in.
A single column with more than limit cards (default 50) does not silently clip. A banner sits
above the column naming the limit and the actual count, and the column shows only the first
limit cards. The full set is one click away in the List view — the board does not pretend
to be a complete picture when it isn't.
If your domain regularly runs a single column past limit, raise the number in the schema. If
your domain regularly runs multiple columns past it, the table view is the better tool and the
board should not be the default.
A file exported through Export a read-only copy (Sidebar → Exchange
or settings.readOnly: true in DEFAULT_SETTINGS) renders the board but disables dragging. The
gesture would just be a write surface pretending otherwise. The cards remain visible and
searchable, the keyboard navigation stays in place, the change log stays readable — only the
move-from-column gesture is suppressed.
-
columnFieldmust be an enum. A text or number field has no defined column set; the schema is rejected at load time, with the field named in the message. -
Cards show
titleFieldpluscardFields. A computed field whosecomputereturns a number renders like any other; a date renders throughentity.formatDate; an enum gets the same pill styling as the table. -
Global search and field filters still apply. The board renders the same filtered set the
table would, so a search for
Lieferant: Müllernarrows the cards in every column the same way it would narrow the table rows. - Bulk select lives only in the table. The board is for one-card moves; multi-card actions belong in the list view.
Tell the agent what you want and include the status field in the answer to "which fields". A prompt like this is enough:
Build me an action-item tracker based on openToolbox (https://github.com/m-dohmen/openToolbox). One record is an "action item" — title, owner, due date, status (open / in progress / done), area. Title is the headline, table shows title, owner, due, status. An item is done when status is "done". I'd like a board view grouped by status. Switch the AI assistant on.
The agent adds the view.board declaration, picks a sensible column field from what it
declared, and writes one file. The board works on first run; npm test covers the drag, the
undo, the change-log entry.
The shipped school-trip demo is the closest reference implementation:
examples/school-trip.domain.js ·
Live demo ·
Screenshots: list and board side by side
For the schema reference itself — every field, every rule that breaks a single-file build — see Building Your Own Tool.