TimeGrid Views
A TimeGrid is an editable spreadsheet-style entry grid: rows × navigable time-period columns, with a single numeric value typed into each cell. It’s the classic “type your hours/amounts against periods” surface — timesheets (task × day → hours), budgets (account × month → amount), demand forecasts (product × week → quantity).
Unlike a TimelineHeatmap (read-only, shaded) or a
computed data_method board, every cell is a real record of the
view’s model. The grid reads them with the standard record query and creates / updates /
deletes them as you type — no read model, no data_method. It’s ordinary record CRUD laid
out as a matrix, with period navigation (prev / today / next) and running row/column totals.
- Rows — one per distinct value of a relation field on the model (
row.field). New rows are added from an in-view picker of that relation’s records. - Columns — a date field (
period.field) bucketed into day / week / month (period.bucket) over a fixedwindow, navigable by whole windows. - Cell — a single numeric measure (
measure.field). Typing a value upserts one record for that (row, period); clearing it deletes the record.
Any extra dimension (whose timesheet? which warehouse’s forecast?) is not a column — it belongs in the action’s default filter (e.g. a “my timesheets” filter scoping to the current user), applied server-side like any other view.
- data_type: UiView name: timesheet_grid_view identifier: timesheet_grid_view type: TimeGrid model: Timesheet # the fact model — one record per non-empty cell arch: - row: field: task # relation on the model; each value is a row add_label: Add task # placeholder for the row picker period: field: date # date field bucketed into the columns bucket: day # day | week | month window: 7 # number of periods visible at once (7 days = a week) measure: field: hours # the numeric field typed into cellsLocked cells
Section titled “Locked cells”A cell is read-only when any record in it is frozen by the model’s
_controlled_edits rules, and its tooltip shows that rule’s
message. There is nothing to declare in the view — the grid reads the same rules the
backend enforces on write and that List and Form views already render read-only from, so a
freeze is described once, on the model, and every surface agrees.
Put TimeGrid in the action’s modes (usually alongside List and Form) so users can
switch between the grid and a plain list:
- data_type: WindowAction name: Weekly Grid identifier: timesheet_grid_action model: Timesheet default_view: timesheet_grid_view modes: TimeGrid, List, Form search_view: timesheet_search_view action_ctx: view: filters: [my_timesheets_filter] # scope to the current user, applied server-side views: - - R - - timesheet_grid_view - timesheet_list_view - timesheet_form_viewThe component is generic — only the arch changes per use case:
| Use case | row.field | period | measure.field |
|---|---|---|---|
| Timesheets | task | date · day · window 7 | hours |
| Budgeting | account | period · month · window 12 | amount |
| Forecasting | product | date · week · window 13 | quantity |
- A cell shows the sum of its records; editing reconciles the cell to the single typed value. Cells with multiple underlying records are read-only in the grid (edit them on the record) so nothing is silently merged.
- The grid participates in the standard search/filter system, so the action’s default filter (and any the user applies) scope which rows load — there is no Browse panel or grouping, since the row/period axes are fixed by the arch.