{# Form body, swappable as a unit. Rendered both as the whole document (via form.html) and as the HTMX swap target on Save/Cancel/Edit. The layout's heading region owns the title / breadcrumb / subtitle; this body only carries the action buttons (which must move with the swap because they change between display/edit modes). #} {% set base = "/web/views/" ~ view.module ~ "/" ~ view.name %} {% set nav_qs = ('?' ~ list_nav_query) if list_nav_query else '' %} {% set rec_url = base ~ "/record/" ~ (record_id if record_id else 'new') ~ nav_qs %} {% set rec_edit_url = base ~ "/record/" ~ record_id ~ "/edit" ~ nav_qs if record_id else '' %} {% set show_activity = ( mode == 'display' and record_id and ( (workflow_context and workflow_context.has_workflow) or (chatter_context and chatter_context.enabled) ) ) %} {% set split_layout = show_activity and not body_only %} {% set stack_activity = show_activity and body_only %} {# HTMX body swap: keep prev/next in the layout heading (top-right). #} {% if body_only and record_pager %}
{% include "_record_pager.html" %}
{% endif %}
{% if split_layout %}
{% endif %} {# Action toolbar — small, right-aligned, mode-aware. #}
{{ mode }}
{% if mode in ("display", "edit") and record_id %} {% for act in header_actions or [] %} {% if act.full_page or (act.method == 'GET' and not act.url.startswith(base)) %} {{ act.label }} {% else %} {% endif %} {% endfor %} {% endif %} {% if mode == "display" and record_id %} {% if access.can_write %} {% endif %} {% if access.can_unlink %} {% endif %} {% elif mode == "edit" and access.can_write %} {% elif mode == "new" and access.can_create %} {% endif %}
{# Whole-form error banner — e.g. an ORM-level rejection during write/create (unique constraint, downstream DB error). Per-field errors get rendered next to the offending input below. #} {% if form_error %}

Could not save:

{{ form_error }}

{% endif %} {# `data-pv-autosave` opts this form into the layout-level autosave/navigation interceptor: editing inputs marks the form dirty, and any subsequent link click triggers a POST to the declared URL before letting the browser follow the link. The attribute is absent in display mode so navigation stays cheap. #}
{% for section in sections %} {# Per-section responsive grid via CSS custom properties — see `.pv-form-grid` in static/tailwind.css. The section emits `--pv-cols` (used from `lg`) and `--pv-cols-md` (used from `md` to ` {% if section.title %} {{ section.title }} {% endif %}
{% for cell in section.cells %} {% set cell_span = cell.colspan or (section_cols if cell.wide else 1) %}
{{ cell.html }}
{% if cell.error %}

{{ cell.error }}

{% endif %}
{% endfor %}
{% endfor %} {% if split_layout %}
{% include "_form_activity_aside.html" %}
{% elif stack_activity %} {% include "_form_activity_aside.html" %} {% endif %}