Views Overview
Views define how data is displayed and edited in Fullfinity’s UI.
Which form does a model open in? (priority)
Section titled “Which form does a model open in? (priority)”Opening a record by model — a method returning get_action(model="Board", record_id=self.id)
— resolves the model’s own Form view: lowest priority wins, ties broken by id. The same
resolution answers “Create & Edit” on a relation field: with no form named, the dialog opens
the related model’s default form, so the number you give a secondary form decides what a picker
on someone else’s screen pops up.
Most models ship one form and never think about this. A model with two must say which is its face:
- data_type: UiView identifier: board_quick_create_form_view priority: 10 # not the model's default — the short create form type: Form model: BoardA quick-create form, a kind-specific variant (an address is a kind of contact, a kit is a kind of BOM), a delivery-log view of a message — none of these is what the model should open as. Give them a higher number and leave the real one at the default.
A tie fails the build. check --only views refuses two forms sharing the lowest priority
for one model, because the winner would otherwise be whatever the database returned first —
and the symptom is a record rendering in the wrong form with most of its fields absent, with
nothing raised anywhere.
Priority does not affect a form named explicitly. ctx: {form_identifier: …}, an action’s
views: list, and a relation widget’s view: all name the view they want and are unaffected
— which is how the non-default forms are still reached where they are genuinely wanted.
View Types
Section titled “View Types”The type field is a Selection whose choices are the registered view types. The
engine ships all of these in core’s UiView.type Selection:
| Type | Description | Use Case |
|---|---|---|
| Form | Detail/edit view | Single record editing |
| List | Table view | Browsing multiple records |
| Kanban | Card-based view | Visual workflows |
| Calendar | Timeline view | Date-based data |
| TimeGrid | Editable rows × time-period entry grid | Timesheets, budgets, forecasts (TimeGrid Views). Enterprise. |
| Map | Records plotted on a map | Location/address-based data |
| Gantt | Timeline with dependencies | Scheduling, project tasks (Gantt Views). Enterprise. |
| Pivot | Cross-tab aggregation table | Reporting/analysis (Pivot & Chart) |
| Chart | Aggregated chart of records | Reporting/analysis (Pivot & Chart) |
| TimelineHeatmap | Rows × time-bucket matrix | Load boards, density grids (TimelineHeatmap). Enterprise. |
| FieldDeck | Offline operator worksheet | Field/floor “my queue → do the work → sign off” (FieldDeck Views). Enterprise. |
| Dashboard | Configurable home screen | KPI cards, charts, lists, goals |
| Search | Filters and groupings | Query configuration (Other Views) |
| Wizard | Transient-model form | Multi-step input before an action (Other Views) |
| Designer | Visual report/layout designer | Document design |
| OrgChart | Hierarchy / org tree | Reporting lines, tree structures |
| PortalList | Customer-portal record list | Portal record browsing |
| PortalDetail | Customer-portal record detail | Portal record view |
Tree is also an accepted value (a legacy alias for List); use List. Types marked
Enterprise only render with an active license — see Enterprise view types.
All view types live in core
Section titled “All view types live in core”Every view type is a value of core’s UiView.type Selection — the single source of
truth. Modules never register their own view types: a view type is a rendering contract
owned by the frontend, so there is no per-module extension of the type choices (no
_selection_add onto UiView.type). A bespoke, one-off screen for a module is authored
as a client action (client_action: on a WindowAction), not as a new UiView type.
Enterprise view types
Section titled “Enterprise view types”Some view types are licensed (Enterprise) features. The gate keys on the view type alone (a compiled, tamper-resistant concept) checked against the license state: without an active license the client shows the upgrade prompt instead of the view — renaming the type to dodge the gate just leaves no renderer, so nothing renders. You can author a view of these types in any module; whether it renders depends on the viewer’s license.
These are authorable primitives — real UiView.type values for which you write
standard arch, exactly like Form or List (each has its own reference page):
| Type | Reference | Used for |
|---|---|---|
| Gantt | page | scheduling / project tasks |
| TimeGrid | page | timesheets, budgets, forecasts |
| TimelineHeatmap | page | load boards / density grids |
| FieldDeck | page | field service, maintenance workbench |
| DocumentBrowser | — | documents (DMS) browser |
The Gantt widget (an embeddable Gantt inside a Form) is Enterprise on the same basis.
Enterprise app screens are client actions, not view types
Section titled “Enterprise app screens are client actions, not view types”Several licensed apps ship a bespoke full-screen experience bound to that app’s model
and flow. These are not UiView type values you can author — you cannot set
type: FinancialReport on a UiView. Each is a client action: a client_action:
value on a WindowAction that names the compiled screen the frontend renders. They are
listed here only so you recognise the name; there is no arch to write for them.
| Client action | App |
|---|---|
| FinancialReport, CustomerStatements, BankReconciliation, PaymentMatching | accounting |
| Shopfloor | manufacturing shop floor |
| Scanner | barcode picking |
| PlanningGrid | planning / MPS |
| ShiftScheduler | employee scheduling |
| EcoDiff | engineering change (PLM) |
A view normally sources its data by querying the model’s records. To render
computed / non-record data instead, declare a
data_method in the arch.
Basic Structure
Section titled “Basic Structure”All views share this structure:
- data_type: UiView name: contact_form_view identifier: contact_form_view type: Form model: Contact arch: [...]Fields
Section titled “Fields”| Field | Type | Description |
|---|---|---|
data_type | string | Always "UiView" |
name | string | View name (usually same as identifier) |
identifier | string | Unique identifier |
type | string | View type (Form, List, Kanban, Calendar, Gantt, Pivot, Chart, Search, Wizard, …) |
model | string | Model this view displays |
arch | array/object | View architecture |
inherited_view | string | Parent view identifier (for inheritance) |
Architecture Elements
Section titled “Architecture Elements”The arch contains the view structure:
Layout Elements
Section titled “Layout Elements”| Element | Description |
|---|---|
row | Horizontal container (12-column grid) |
column | Vertical container with span (1-12) |
tab | Tab container with label |
Field Elements
Section titled “Field Elements”| Element | Description |
|---|---|
field | Data field with widget |
statusbar | Status indicator |
actionButton | Button triggering model method |
linkButton | Statistic button with link |
Special Elements
Section titled “Special Elements”| Element | Description |
|---|---|
text | Static text |
divider | Visual separator |
activities | Activity timeline |
Element Structure
Section titled “Element Structure”Each element has:
- type: field name: email properties: widget: TextInput label: Email Address placeholder: Enter email visible: Q(type__eq='company') groups: [admin]Common Properties
Section titled “Common Properties”| Property | Description |
|---|---|
type | Element type (structural, at top level) |
name | Field name (structural, at top level) |
span | Column width 1-12 (structural, at top level) |
properties | All field configuration (widget, visibility, behavior) |
Element Properties
Section titled “Element Properties”All behavioral and display properties go inside properties:
- properties: widget: TextInput visible: Q(status__eq='draft') readonly: Q(status__neq='draft') required: true groups: [sales_manager]| Property | Type | Description |
|---|---|---|
widget | String | Widget type to render |
visible | Boolean/String | Show/hide element. Can be false or Q-expression. Default: true |
readonly | Boolean/String | Make field(s) read-only. Can be true or Q-expression. |
required | Boolean/String | Make field required. Can be true or Q-expression. |
groups | Array | Required group identifiers for access control. |
Note: Structural attributes (type, name, span, content, title, anchor) stay at the
top level. All behavior and display config goes in properties — and this is enforced: a
behavioral key placed at the node level is read by nothing, so it is rejected when the view is
saved and by check --only views rather than shipping as a rule that never fires. See
Element Properties.
Window Actions
Section titled “Window Actions”Actions connect views to navigation:
- data_type: WindowAction identifier: contacts_action name: Contacts model: Contact modes: Kanban,List,Form default_view: contact_kanban_view views: - - R - - contact_kanban_view - contact_list_view - contact_form_view search_view: contact_search_viewTwo things about that block are checked by check --only views (and the pre-commit hook),
because neither fails loudly on its own:
- The explicit
views:command list is what populates the action’s views. A baredefault_view/action_ctx.form_identifierdoes not — an action without aviews:list raises “Unable to locate view(s)” the moment a menu opens it. - Every view the action names must declare the same
model:. A view pointed at another model still composes, and still renders against the records the action loaded — so a wrongmodel:shows up only as fields silently missing from the screen, never as an error.
Menus create navigation structure:
- data_type: UiMenu identifier: contacts_menu name: Contacts parent: sales_menu action: contacts_action sequence: 10View Resolution
Section titled “View Resolution”When loading a view:
- Fetch base view by identifier
- Find all child views (inherited_view)
- Apply inheritance operations
- Return combined view
Dynamic Views
Section titled “Dynamic Views”If no view is defined, Fullfinity auto-generates one from the model registry. This allows navigation to any model without requiring a WindowAction or UiView definition.
URL Pattern
Section titled “URL Pattern”/app/:rootMenu/m/:model # List view/app/:rootMenu/m/:model/:recordId # Form view for specific record/app/:rootMenu/m/:model/new # Form view for new recordExamples
Section titled “Examples”/app/core_menu/m/Currency # Currency list view/app/core_menu/m/Currency/5 # Currency form for record 5/app/core_menu/m/Currency/new # New Currency formGenerated Views
Section titled “Generated Views”| View | Fields Included | Layout |
|---|---|---|
| List | Char, Selection, ManyToOne, OneToOne, Integer, Float, Monetary | Simple table |
| Form | All fields except JSON, File, Binary, OneToMany | 2-column grid with status bar |
| Search | Active filter + group-by for Selection/ManyToOne | Basic filters |
Priority
Section titled “Priority”- If a UiView exists in the database for the model, it is used (with inheritance)
- If no UiView exists, a dynamic view is generated from the model registry
Use Cases
Section titled “Use Cases”- Quick prototyping without creating view definitions
- Admin/debug access to any model
- Action methods returning
get_action(model="ModelName")for navigation
From Action Methods
Section titled “From Action Methods”async def action_view_logs(self): """Open audit logs without a WindowAction.""" return await get_action(model="AuditLog")
async def action_view_record(self): """Open a specific record dynamically.""" return await get_action(model="SomeModel", record_id=self.related_id)File Organization
Section titled “File Organization”Views are stored in modules/<module>/views/:
views/├── actions.yaml # Window actions├── menus.yaml # Menu items├── contact_views.yaml # Contact views└── product_views.yaml # Product viewsNext Steps
Section titled “Next Steps”- Form Views - Detail/edit views
- List Views - Table views
- Kanban Views - Card-based views
- Pivot & Chart Views - Reporting/analysis views
- Other View Types - Calendar, Search, and Wizard views
- Configuration Forms - The settings DSL
- Dashboard Views - Configurable home screen with widgets
- Stats Banner - Rich visual stats for list/kanban views
- Widgets - Available widgets
- View Inheritance - Extending views