Skip to content

Kanban Board

Michael Dohmen edited this page Aug 28, 2026 · 1 revision

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.

When to use 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.

Activating the board

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.

Worked example: school trip

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.

How a drag becomes a write

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.

Keyboard, mouse and touch

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.

When the column is too full

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.

Read-only copies

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.

Relation to the rest of the schema

  • columnField must 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 titleField plus cardFields. A computed field whose compute returns a number renders like any other; a date renders through entity.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üller narrows 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.

Building a domain that uses the board

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.

Clone this wiki locally