tempestweb.presets¶
Ready-made admin-panel screens, assembled from typed records (NavItem, Kpi, Section, TableColumn, FormField) rather than layout widgets. Import from here when the screen is an archetype — shell, dashboard, listing, form, login.
Guide with examples: Ready-made screens (presets) · Admin console.
tempestweb.presets ¶
Ready-made screens for dashboards, admin panels and internal tools.
A preset is a whole screen you describe with data instead of assembling with widgets. You say what is on it — these nav entries, these KPIs, these columns, these fields — and the preset decides the spacing, the type scale, the grid and the responsive behaviour::
from tempestweb.presets import Kpi, NavItem, admin_shell, dashboard_page
def view(app):
return admin_shell(
title="Painel",
nav=[NavItem("Visão geral", "overview"), NavItem("Usuários", "users")],
active=app.state.tab,
on_navigate=lambda value: app.set_state(...),
body=dashboard_page(
title="Visão geral",
kpis=[Kpi("Receita", "R$ 82k", delta="+12%", tone="success")],
),
)
Nothing here measures the viewport. Every breakpoint lives in
client/layouts.js, the stylesheet the client injects at mount: the sidebar
collapses under 1024px, the KPI row reflows, the table scrolls sideways under a
sticky header, and printing drops the chrome. The presets only tag each
container with its layout role (data-tw-layout) so those rules can find it.
That means the same tree is correct at every width, in all three modes, with no
media query of your own — and an inline Style you set still wins over
anything the sheet says.
The presets compose the same public components an app would: they are a shortcut, never a wall. Use one for the shell and hand-build the body, replace a section with your own widget, or stop using them entirely — the widgets underneath are the ones you already know.
FormField
dataclass
¶
One labelled control in a form page.
Attributes:
| Name | Type | Description |
|---|---|---|
label |
str
|
The field's label. |
control |
Widget
|
The input widget itself — any core/tempestweb field. The preset positions it; it never builds or validates it. |
help |
str | None
|
Optional hint shown under the control. |
error |
str | None
|
Optional validation message, shown instead of |
span |
Span
|
|
Source code in tempestweb/presets/models.py
FormSection
dataclass
¶
A group of related fields under one heading.
Attributes:
| Name | Type | Description |
|---|---|---|
title |
str
|
The group heading ("Dados da conta", "Notificações"). |
fields |
list[FormField]
|
The fields in the group, laid out in the responsive form grid. |
subtitle |
str | None
|
Optional line under the heading explaining the group. |
Source code in tempestweb/presets/models.py
Kpi
dataclass
¶
A single headline number on a dashboard.
Attributes:
| Name | Type | Description |
|---|---|---|
label |
str
|
What the number measures ("Receita", "Churn"). |
value |
str
|
The number, already formatted for display ("R$ 82k", "1,8%"). Presets never format numbers: locale and currency are the app's call. |
delta |
str | None
|
Optional change indicator shown next to the value ("+12%"). |
up |
bool
|
Whether |
tone |
Tone
|
The delta's semantic colour. |
Source code in tempestweb/presets/models.py
NavItem
dataclass
¶
One entry in the admin shell's sidebar.
Attributes:
| Name | Type | Description |
|---|---|---|
label |
str
|
The text shown in the sidebar. |
value |
str
|
The value handed to |
badge |
str | None
|
Optional short text rendered as a trailing badge ( |
Source code in tempestweb/presets/models.py
Section
dataclass
¶
A titled block of a dashboard, rendered as a card in the section grid.
Attributes:
| Name | Type | Description |
|---|---|---|
title |
str
|
The section heading. |
body |
Widget
|
The section's content — a chart, a table, any widget. |
subtitle |
str | None
|
Optional supporting line under the heading. |
span |
Span
|
|
Source code in tempestweb/presets/models.py
TableColumn
dataclass
¶
One column of a list page's table.
Attributes:
| Name | Type | Description |
|---|---|---|
label |
str
|
The header text. |
align |
Align
|
How the column's cells are aligned. Numbers usually read better
as |
Source code in tempestweb/presets/models.py
auth_page ¶
auth_page(*, title: str, body: Widget, subtitle: str | None = None, brand: str | None = None, footer: Sequence[Widget] = (), key: str = 'tw-auth') -> Widget
Build a sign-in / sign-up screen around an existing form.
The card is centred vertically and horizontally and capped at a readable width, so the same tree is right on a phone and on a 27" monitor without the form stretching across it.
The form itself is yours — LoginForm/SignupForm from
:mod:tempestweb.components, or any widget. This preset only places it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
title
|
str
|
The heading above the form ("Entrar"). |
required |
body
|
Widget
|
The form widget. |
required |
subtitle
|
str | None
|
Optional line under the heading. |
None
|
brand
|
str | None
|
Optional product name above the heading. |
None
|
footer
|
Sequence[Widget]
|
Widgets under the card (a "Esqueci minha senha" link, a legal note). |
()
|
key
|
str
|
The key prefix for the page's widgets. |
'tw-auth'
|
Returns:
| Type | Description |
|---|---|
Widget
|
The auth page. |
Source code in tempestweb/presets/auth.py
dashboard_page ¶
dashboard_page(*, title: str, kpis: Sequence[Kpi] = (), sections: Sequence[Section] = (), subtitle: str | None = None, actions: Sequence[Widget] = (), key: str = 'tw-dashboard') -> Widget
Build a dashboard: a header, a KPI row and a grid of sections.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
title
|
str
|
The page title. |
required |
kpis
|
Sequence[Kpi]
|
Headline numbers shown above the sections. Empty renders no row. |
()
|
sections
|
Sequence[Section]
|
Titled content blocks. Empty renders no grid. |
()
|
subtitle
|
str | None
|
Optional line under the title. |
None
|
actions
|
Sequence[Widget]
|
Buttons shown opposite the title (a period picker, "Exportar"). |
()
|
key
|
str
|
The key prefix for the page's widgets. |
'tw-dashboard'
|
Returns:
| Type | Description |
|---|---|
Widget
|
The dashboard page. |
Source code in tempestweb/presets/dashboard.py
kpi_grid ¶
Render headline numbers in a grid that reflows with the viewport.
The grid is auto-fit, so four KPIs become two columns on a tablet and one
on a phone with no breakpoint of your own — and adding a fifth KPI does not
require touching a layout.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
kpis
|
Sequence[Kpi]
|
The numbers to show, in order. |
required |
key
|
str
|
The key prefix. |
'tw-kpis'
|
Returns:
| Type | Description |
|---|---|
Widget
|
The KPI grid container. |
Source code in tempestweb/presets/dashboard.py
section_grid ¶
Render titled content blocks as cards in a reflowing grid.
A section with span="full" takes the whole row — what a wide chart or a
table wants — while the rest share the available tracks.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sections
|
Sequence[Section]
|
The sections to render, in order. |
required |
key
|
str
|
The key prefix. |
'tw-sections'
|
Returns:
| Type | Description |
|---|---|
Widget
|
The section grid container. |
Source code in tempestweb/presets/dashboard.py
form_page ¶
form_page(*, title: str, fields: Sequence[FormField] = (), sections: Sequence[FormSection] = (), actions: Sequence[Widget] = (), subtitle: str | None = None, key: str = 'tw-form') -> Widget
Build a form screen: fields in a responsive grid over an action bar.
Pass fields for a flat form or sections for a grouped one; passing
both puts the loose fields first, in their own grid. Field widths come from
the grid, which fits as many columns as the viewport allows and drops to one
on a phone — a field marked span="full" always takes the whole row.
The action bar sits at the end: a right-aligned row on a wide screen, and a
stack on a phone. The stack is reversed (column-reverse in
client/layouts.js), so the last entry in actions renders on top —
put the primary action last and it leads the stack, the way Material stacks
dialog buttons.
See :func:settings_page for the grouped-only variant of this same page.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
title
|
str
|
The page title. |
required |
fields
|
Sequence[FormField]
|
Ungrouped fields, rendered before any sections. |
()
|
sections
|
Sequence[FormSection]
|
Grouped fields, each rendered as a card. |
()
|
actions
|
Sequence[Widget]
|
The submit/cancel buttons. Empty renders no action bar. |
()
|
subtitle
|
str | None
|
Optional line under the title. |
None
|
key
|
str
|
The key prefix for the page's widgets. |
'tw-form'
|
Returns:
| Type | Description |
|---|---|
Widget
|
The form page. |
Source code in tempestweb/presets/forms.py
form_section ¶
Render a titled group of fields as a card.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
section
|
FormSection
|
The group to render. |
required |
key
|
str
|
The widget key prefix. |
required |
Returns:
| Type | Description |
|---|---|
Widget
|
The section card. |
Source code in tempestweb/presets/forms.py
settings_page ¶
settings_page(*, title: str, sections: Sequence[FormSection], actions: Sequence[Widget] = (), subtitle: str | None = None, key: str = 'tw-settings') -> Widget
Build a settings screen — a form page whose fields are always grouped.
Renders identically to :func:form_page; this is a deliberate public
facade over it, not a distinct layout. Two things differ, and neither is
visual:
- the signature —
sectionsis required and loosefieldsare not accepted, because a settings screen always groups its fields; - the key prefix —
tw-settingsrather thantw-form, so the two kinds of screen keep distinct widget keys.
Reach for :func:form_page when the screen is a single flat form.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
title
|
str
|
The page title. |
required |
sections
|
Sequence[FormSection]
|
The setting groups, each rendered as a card. |
required |
actions
|
Sequence[Widget]
|
The save/discard buttons. See :func: |
()
|
subtitle
|
str | None
|
Optional line under the title. |
None
|
key
|
str
|
The key prefix for the page's widgets. |
'tw-settings'
|
Returns:
| Type | Description |
|---|---|
Widget
|
The settings page. |
Source code in tempestweb/presets/forms.py
box ¶
box(role: str, children: Sequence[Widget], *, key: str, attrs: dict[str, str] | None = None, style: Style | None = None) -> Widget
Wrap children in a container tagged with a layout role.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
role
|
str
|
A role from :mod: |
required |
children
|
Sequence[Widget]
|
The container's children, in order. |
required |
key
|
str
|
The widget key, unique among its siblings. |
required |
attrs
|
dict[str, str] | None
|
Extra attributes merged after the role ( |
None
|
style
|
Style | None
|
Optional inline style. Leave it unset for anything the sheet lays out — an inline declaration wins over the sheet's rule. |
None
|
Returns:
| Type | Description |
|---|---|
Widget
|
The tagged container. |
Source code in tempestweb/presets/layout.py
heading ¶
Render a page, section or group heading.
Size, weight and colour come from the stylesheet, not from here. A preset that hard-coded them would pick a colour from one palette and land on a page themed with another — a white title on a white page. The sheet resolves them from the theme's own tokens, so a rebranded app rebrands its headings too.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
text
|
str
|
The heading text. |
required |
key
|
str
|
The widget key. |
required |
level
|
Level
|
Which step of the type scale to use. |
'section'
|
Returns:
| Type | Description |
|---|---|
Widget
|
The heading text, tagged for the sheet. |
Source code in tempestweb/presets/layout.py
muted ¶
Render supporting text (a subtitle, a hint, a help line).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
text
|
str
|
The text. |
required |
key
|
str
|
The widget key. |
required |
Returns:
| Type | Description |
|---|---|
Widget
|
The text, tagged so the sheet gives it the muted treatment. |
Source code in tempestweb/presets/layout.py
page_header ¶
page_header(*, title: str, key: str, subtitle: str | None = None, actions: Sequence[Widget] = ()) -> Widget
Render a page's title block with its action buttons.
The title and the actions sit on one row on a wide screen and stack on a phone — the sheet handles the switch, so nothing here measures the viewport.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
title
|
str
|
The page title. |
required |
key
|
str
|
The key prefix for the header's widgets. |
required |
subtitle
|
str | None
|
Optional line under the title. |
None
|
actions
|
Sequence[Widget]
|
Buttons shown opposite the title ("Novo", "Exportar"). |
()
|
Returns:
| Type | Description |
|---|---|
Widget
|
The header container. |
Source code in tempestweb/presets/layout.py
data_table ¶
data_table(*, columns: Sequence[TableColumn], rows: Sequence[Sequence[Cell]], key: str = 'tw-table') -> Widget
Render a table that scrolls sideways under a header that stays put.
The table is built here rather than delegated to the core's DataTable
because the core resolves row and header backgrounds inline, and inline
beats the stylesheet: zebra striping, row hover and a sticky head would all
be dead rules. These containers carry no inline background, so
client/layouts.js owns the look — and a narrow viewport gets a
horizontally scrolling table instead of a squashed one.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
columns
|
Sequence[TableColumn]
|
The column definitions, in order. |
required |
rows
|
Sequence[Sequence[Cell]]
|
One sequence of cells per row, each aligned with |
required |
key
|
str
|
The key prefix. |
'tw-table'
|
Returns:
| Type | Description |
|---|---|
Widget
|
The scroll container wrapping the table. |
Source code in tempestweb/presets/listing.py
list_page ¶
list_page(*, title: str, columns: Sequence[TableColumn], rows: Sequence[Sequence[Cell]], subtitle: str | None = None, actions: Sequence[Widget] = (), search: str | None = None, on_search: Callable[[str], None] | None = None, search_placeholder: str = 'Buscar…', filters: Sequence[Widget] = (), page: int = 1, page_count: int = 1, on_page: Callable[[int], None] | None = None, empty_title: str = 'Nada por aqui', empty_subtitle: str | None = None, empty_action: Widget | None = None, key: str = 'tw-list') -> Widget
Build the standard admin listing screen.
Header, a toolbar with search and filters, the table, and pagination. When
rows is empty the table is replaced by an empty state — an empty result
is a normal outcome, so it gets a designed screen rather than a bare table
with no lines.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
title
|
str
|
The page title. |
required |
columns
|
Sequence[TableColumn]
|
The table's columns. |
required |
rows
|
Sequence[Sequence[Cell]]
|
The rows to show — already filtered and paged by the app. The preset never slices data: what you pass is what is drawn. |
required |
subtitle
|
str | None
|
Optional line under the title. |
None
|
actions
|
Sequence[Widget]
|
Buttons opposite the title ("Novo usuário"). |
()
|
search
|
str | None
|
The current search text. |
None
|
on_search
|
Callable[[str], None] | None
|
Called with the new text as the user types. Required for the search box to appear. |
None
|
search_placeholder
|
str
|
Placeholder for the search box. |
'Buscar…'
|
filters
|
Sequence[Widget]
|
Extra toolbar widgets (selects, chips, a date range). |
()
|
page
|
int
|
The current 1-based page. |
1
|
page_count
|
int
|
The total number of pages. |
1
|
on_page
|
Callable[[int], None] | None
|
Called with the requested page. Required for pagination. |
None
|
empty_title
|
str
|
Heading of the empty state. |
'Nada por aqui'
|
empty_subtitle
|
str | None
|
Supporting line of the empty state. |
None
|
empty_action
|
Widget | None
|
Optional button in the empty state ("Criar o primeiro"). |
None
|
key
|
str
|
The key prefix for the page's widgets. |
'tw-list'
|
Returns:
| Type | Description |
|---|---|
Widget
|
The list page. |
Source code in tempestweb/presets/listing.py
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 | |
admin_shell ¶
admin_shell(*, title: str, nav: Sequence[NavItem], active: str, on_navigate: Callable[[str], None], body: Widget, brand: str | None = None, actions: Sequence[Widget] = (), footer: Widget | None = None, sidebar_open: bool = False, on_toggle_sidebar: Callable[[], None] | None = None, key: str = 'tw-shell') -> Widget
Build an admin shell around body.
The layout is a grid: the sidebar owns a column and the header a row, so the
content area never needs a margin kept in sync with the sidebar's width.
Below 1024px the sidebar leaves the grid and becomes an overlay — pass
sidebar_open and on_toggle_sidebar to drive it, and the burger button
appears (the sheet hides it on wide screens, where the sidebar is permanent).
Nothing here measures the viewport: every breakpoint lives in
client/layouts.js. The same tree is correct at any width.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
title
|
str
|
The application title, shown in the header. |
required |
nav
|
Sequence[NavItem]
|
The sidebar entries. |
required |
active
|
str
|
The |
required |
on_navigate
|
Callable[[str], None]
|
Called with an entry's |
required |
body
|
Widget
|
The content area — usually a page preset. |
required |
brand
|
str | None
|
Optional product name shown above the nav. |
None
|
actions
|
Sequence[Widget]
|
Widgets pinned to the right of the header (a user menu, a "Sair" button). |
()
|
footer
|
Widget | None
|
Optional widget pinned under the nav (the signed-in user). |
None
|
sidebar_open
|
bool
|
Whether the overlay sidebar is open. Ignored on wide screens, where the sidebar is always visible. |
False
|
on_toggle_sidebar
|
Callable[[], None] | None
|
Called when the burger or the scrim is tapped. Pass
|
None
|
key
|
str
|
The key prefix for the shell's widgets. |
'tw-shell'
|
Returns:
| Type | Description |
|---|---|
Widget
|
The shell widget tree. |
Source code in tempestweb/presets/shell.py
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 | |