Files
member-console/internal/embeds/templates/partials/ui_section_header.html
T
cgalo5758 f8a3478f2a Rebuild the entitlement set Rules surface as a staged batch
The Rules section is one record table grouped by kind, Limit then
Boolean, on fixed columns, edited in place: Edit opens a row's controls
in their columns, Add rule opens a dense row above the table, and every
change is staged into a tray that lists the deltas with Undo and applies
them as one rule-change act. The reduction policy is a column of the
rule beside its limit. History shows counts only. Group rows are a quiet
heading rather than a divider, the maintainer's pick from four rounds of
outside-model ideation.

Dense rows align to the top and render each error under its control in
every form family (design D16), replacing the below-row error block; the
forms library gains the batch form (rows plus one tray) and the RowField
dense and label-hidden options. Migration 00019 records the governing
reduction policy on effect rows.

Archive staged-rule-changes with its spec updates (entitlement-set-
management, entitlement-set-history, entitlements, form-library,
form-conventions, ui-quality-gate). Screens accepted 2026-09-19.
2026-09-19 19:46:09 -05:00

50 lines
4.7 KiB
HTML

{{- /* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Commercial */ -}}
{{- /* SPDX-FileCopyrightText: 2025-2026 Christian Galo */ -}}
{{/* Page anatomy: in-page section headers (docs/design-system.md §6; spec
page-anatomy "Section headers are uniform"). Pipeline:
server.SectionHeader. sectionHeader titles a section outside the box
its content sits in (h2.h5, optional muted count or one action).
sectionSummary is the same header as the <summary> of a native
<details>, for a secondary section that opens on demand; the count
rides in parentheses so the closed disclosure still says how much it
holds. */}}
{{ define "sectionHeader" }}<div class="d-flex align-items-center justify-content-between gap-3 mt-4 mb-2 app-section-header">
<h2 class="h5 mb-0">{{ .Title }}{{ if .Help }} {{ template "helpIcon" (helpIcon .Title .Help) }}{{ end }}</h2>
{{/* The action's weight follows its role (form-conventions "Button
weight follows the button's role"; design D19). An opener that
toggles a collapsed sub-record panel (Action.Toggle names the
panel's id) is tertiary: outline-secondary, with the plus glyph
that rotates into a cross while the panel is open. It carries no
pressed or filled state; the glyph is the whole open/closed signal
(maintainer, 2026-09-03: the pressed look "looks pressable as
opposed to untoggleable"). A link marked Filled is the section's
one filled call to action, which on a list page is its "New ..."
link to the record's create page (design D20); every other link
navigates and is outline-secondary. */}}
{{ if .Action }}{{ if .Action.Toggle }}<button type="button" class="btn btn-sm btn-outline-secondary" data-bs-toggle="collapse" data-bs-target="#{{ .Action.Toggle }}" aria-controls="{{ .Action.Toggle }}" aria-expanded="{{ if .Action.Expanded }}true{{ else }}false{{ end }}">{{ template "openerIcon" }}{{ .Action.Label }}</button>{{ else if .Action.Post }}<button type="button" class="btn btn-sm btn-outline-secondary" hx-post="{{ .Action.Post }}" hx-vals='{{ .Action.Vals }}' hx-include="{{ .Action.Include }}" hx-target="{{ .Action.Target }}" hx-swap="outerHTML" hx-sync="this:replace">{{ template "openerIcon" }}{{ .Action.Label }}</button>{{ else }}<a href="{{ .Action.URL }}" class="btn btn-sm {{ if .Action.Filled }}btn-primary{{ else }}btn-outline-secondary{{ end }}">{{ .Action.Label }}</a>{{ end }}{{ else if .Count }}<span class="text-body-secondary small">{{ .Count }}</span>{{ else if .Key }}<span class="text-body-secondary small">Key <code class="text-muted small">{{ .Key }}</code></span>{{ end }}
</div>{{ end }}
{{/* openerIcon is the leading glyph of every panel opener, on this part's
Toggle actions and on the openers written out by hand (the
organization composite's Issue grant and Extend). A plus that rotates
45 degrees into a cross while its button reports aria-expanded="true"
(app.css keys the rotation on that attribute), so the control states
whether the panel it owns is open without changing weight. Adapted
from Material Design's speed-dial FAB, whose plus rotates into a
close glyph when the dial opens, and Carbon's accordion, which flips
its chevron while a section is expanded. An inline SVG for the same
reason as the external-destination marker (chrome-conventions): a
character like U+002B would take the surrounding font's own weight
and a U+00D7 cross reads as a different mark. */}}
{{ define "openerIcon" }}<svg class="app-opener-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" aria-hidden="true" focusable="false"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>{{ end }}
{{/* chevronIcon marks a linked card (page-anatomy "A linked card is marked
by a glyph and the row's hover"; overview-consistency D3, round 2): the
at-a-glance tiles are a.card links, and this glyph at the card's
trailing edge says "go into" the way a list row's disclosure chevron
does, unlike the external-destination arrow (.external-link-icon),
which says "leaves the console". An inline SVG for the same reason as
openerIcon: a character glyph would take the surrounding font's own
weight. */}}
{{ define "chevronIcon" }}<svg class="app-chevron-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><polyline points="9 6 15 12 9 18"/></svg>{{ end }}
{{ define "sectionSummary" }}<summary class="app-section-header mb-2"><h2 class="h5 d-inline mb-0">{{ .Title }}{{ if .Count }} ({{ .Count }}){{ end }}</h2></summary>{{ end }}