Saved views #
Classification: Restricted (describes source code structure).
The review list’s tabs are saved views: a filter, a grouping of at most two
levels, a sort and columns, under a name. Search text is never part of one.
This page is how tofu-web reads, draws and writes them. The decisions behind
it are in the saved-views handoff (D1–D22); the list core is
@bonsai/utils/extraction-list and the stored shape is bonsapi’s
extraction_view.
Where they come from #
| Kind | Source | Who changes it |
|---|---|---|
All (all) |
Built in (config/extraction-views/all.yaml, generated into views.gen.ts). Never stored. |
Nobody: fixed first, never hidden, renamed or saved over. |
Defaults (ap, ar, expense, bank-statement, not-done) |
Org views bonsapi seeds before the organization’s first read or write of views, from the same YAML, in each file’s order. name stays null until renamed, so the tab shows the translated label (i18n/view-labels.ts). |
Owners and admins. |
| Other org views | POST /api/v1/organization/extraction-views |
Owners and admins (manageOrganizationViews). |
| Entity views | POST /api/v1/extraction-views?entity_id= |
entity:extraction_view:write (Managers), owners and admins. |
| Suggestions | SUGGESTED_VIEWS, never stored |
Offered by New view as a starting point. |
A default whose type the entity turned off is absent from the bar, not
hidden: DEFAULT_VIEW_TOGGLE in domain/extraction-views.ts maps each
default key to its entity toggle (AP and AR follow the bill and invoice
toggles; there is no credit-note toggle; Not done is always shown).
Reading #
data/queries/extraction-views.ts reads GET /api/v1/extraction-views once
per entity and normalises it in select (extractionViewsOf): each
definition is checked with isValidSavedView, valid ones are indexed by key
in the default order (org views by position, then the entity’s), and the
ones that no longer validate are kept apart. Those never reach the bar; More
lists them under “Can’t be shown”, and a link to one opens All (D15).
tabsFor builds the bar after All: the person’s own {order, hidden} wins,
views it does not name follow in the default order, hidden ones go to More.
The person has one {order, hidden} per organization; a save
(PUT /api/v1/users/me/extraction-view-tabs?entity_id=) replaces the keys of
the organization’s views and that entity’s, and other entities’ keys keep
their places.
?view and the list
#
The review route carries ?view=<key>; no ?view is All. The container
resolves it with resolveActiveView, a union with an explicit unresolved
arm: while the views load, a key other than all draws the list’s skeleton,
never All’s rows first (no wrong-default flicker). A key the views do not
have, or a failed read, opens All with a notice.
Every way into the review keeps ?view (D16): commands that navigate within
the review keep the current search, and the ones that arrive from elsewhere
(select_entity, the summary, an upload finishing, the bare entity path) ask
viewToCarry in state/extraction-list.store.ts, which carries an org
view’s key to another entity and an entity view’s only within its own. The
entity upload page keeps view in its search, because the list stays beside
it.
The reader’s changes (filter, grouping, sort, columns, search) live in the
list store, per view key, and are never in the address. changedParts
compares the view as saved with the view as shown, ignoring search; the
changed strip offers Reset and, for those who may, Save to view or Save as
new view.
Relative dates resolve on the reader’s calendar day. useListDay ticks at
local midnight and when the tab becomes visible again, and the day reaches
toServerParams through listRequestsOf.
Counts #
All and the defaults show counts (D4) from one request: the window grouped by
type, then status (listRequestsOf().counts). countFrom folds statuses into
bands and sums each default’s types over the bands its filter keeps. A view
whose filter reads anything but types and bands gets no count; the header
shows the active view’s own total.
Commands #
| Command | Where | Notes |
|---|---|---|
apply_list_view |
features/extraction-list/commands/extraction-list.ts |
Data-driven by key; one palette row per view; ←/→ step with replace. |
reset_list_view, set_list_columns, copy_extraction_view_link |
features/extraction-list/commands/extraction-views.ts |
The reader’s own view; reads. |
hide_extraction_view_tab, show_extraction_view_tab, set_extraction_view_tabs |
same | The person’s tabs, queued per organisation. Only hide and show are offered to the assistant. |
create_, save_, rename_, move_, delete_, restore_extraction_view |
features/entity-setting/commands/views.ts |
Entity Settings commands (defineEntitySettingCommand), shared by the list’s tab menu and Settings › Views. |
the same with _organization_ and restore_default_extraction_views |
features/organization-setting/commands/views.ts |
Owners and admins; each confirmation counts the entities it reaches. |
Shared writes are confirm_first with a confirmDetail. A save sends the
view’s updated_at; a 409 (“someone else saved first”) is reported as a
conflict. Delete’s Undo puts the stored view back under the same key with a
PUT, so links and tab orders keep working. Commands refuse all with a
message that says why. Names and descriptions of the bonsapi-backed commands
come from extractionViewTools in @bonsai/mcp-tool.
Not in v1 #
Table/gallery switch, empty groups, phone card fields, drag to reorder (Move up / Move down instead), and a description per view.