Form modal doctrine + refactor roadmap#104
Conversation
Forms now follow a single reusable model: every form opens as an overlay modal above the dimmed interface (never a new page), built from one shared shell and field partial. Create and edit are distinct forms (one per action, no instance.pk branching), forms never scroll vertically (large forms split into a multi-step modal), the user always sees a step indicator or a required-fields completion meter, required fields are marked and counted, and every field carries a mandatory always-visible helper. The retired single-column / two-column page-form and standalone delete-page patterns are documented as superseded; deletion becomes a confirmation modal. Updates the Components pointer and the CHANGELOG accordingly. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Turn the global #itemDrawer container and the includes/modal_form.html shell from a right-side Bootstrap offcanvas into a centered Bootstrap modal. The swap region id (drawer-form-content) and the shared HtmxFormMixin flow are kept identical, so every list trigger and all 28 entity form templates switch to modals with no per-entity change. - base.html: #itemDrawer becomes `.modal` (centered, scrollable body via flex column + max-height), JS uses bootstrap.Modal and hidden.bs.modal while keeping the Back-button / history integration and the formSaved table-refresh path. CSS rescoped from offcanvas to modal chrome. - includes/modal_form.html: modal header / body / footer, adds a modal_form_actions block for footer overrides (e.g. delete). - swot_detail.html: hide the modal via bootstrap.Modal.getInstance. First step of the modal form refactor; declarative steps, progress tracking and per-field helpers land per form next. Full test suite green (1917 passed). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Progress - commit
|
Introduce core.modal_forms with Step and SteppedFormMixin: a form declares an ordered list of Step(title, icon, fields) as the single source of truth for its grouping and ordering. One step renders single-step (completion meter), two or more render as a multi-step wizard. The declared steps must cover every visible field exactly once, validated at instantiation (unknown / duplicate / uncovered field raises ImproperlyConfigured) so a field never silently drops from a form. Also adds includes/form_field.html, the single reusable field renderer (label / control / helper / error), with the helper shown whenever help_text is set per the Forms doctrine. No form consumes the engine yet; this is the reusable foundation for the per-form migration. Adds the PR progress-tracking working rule to CLAUDE.md. 7 new unit tests, ruff clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Progress - commit
|
Wire the declarative engine into the shared shell and prove it end to end
on the Role create/edit forms.
Presentation (reusable, zero per-entity JS):
- includes/modal_form.html: dual mode. When the form is stepped it
auto-renders the progress header (includes/form_progress.html) and the
fields (includes/form_steps.html) and shows a Back/Next/Save footer;
otherwise it keeps the legacy modal_form_fields block and Cancel/Save,
so unmigrated forms are untouched.
- includes/form_progress.html: stepper (multi-step) or required-fields
completion meter (single-step).
- includes/form_steps.html: one section per step, or all fields for a
single step; renders each via includes/form_field.html.
- base.html: generic attribute-driven JS (step nav, per-step required
gating, live meter, focus) and brand CSS for the stepper / meter.
Role pilot:
- RoleBaseForm(SteppedFormMixin, ScopedFormMixin, ModelForm) declares two
steps (Identity / Scope & status) and a helper on every field; thin
RoleCreateForm / RoleUpdateForm subclasses (one form per action).
- role_form_modal.html reduced to its header icon.
- FR translations for the new helper / step / control strings.
Also fixes multiline Django comments ({# #} cannot span lines and were
rendering as visible text) by switching them to {% comment %} blocks.
Verified in the browser: stepper, per-step validation gating, light and
dark themes, save + list refresh; legacy Activity modal still renders as a
plain centered modal. Full suite green (1924), ruff clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Progress - commit
|
Second pilot, single-step this time, to exercise the completion meter and the server-side error path: - RequirementMappingBaseForm(SteppedFormMixin, ModelForm) with one declarative step and a helper on every field; thin RequirementMappingCreateForm / RequirementMappingUpdateForm subclasses. - Mapping create/update views use the split forms and now set proper modal titles (New mapping / Edit mapping) - they were previously empty. - mapping_form_modal.html reduced to its header icon. - FR translations for the six field helpers. Verified in the browser: live completion meter (0/3 -> 3/3), server-side validation re-render keeping the modal open with input intact (duplicate mapping non-field error), create + list refresh, and edit pre-fill. Full suite green (1924), ruff clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Progress - commit
|
Batch 1 of the context app rollout. Each form is split into Create / Update subclasses over a shared stepped base, with two declarative steps (Identity + Assessment & status / Scope & status) and a helper on every field; the modal templates are reduced to their header icon. FR translations added for the new helpers and the "Assessment & status" step. Verified in the browser: the Issue create modal renders the two-step stepper with all per-field helpers. Full suite green (1924), ruff clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Progress - commit
|
Split into StakeholderCreateForm / StakeholderUpdateForm over a shared stepped base with three declarative steps (Identity / Contact / Assessment & status) and a helper on every field; template reduced to the header icon. FR translations added. Full suite green (1924), ruff clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Progress - commit (Stakeholder)Migrated the context Stakeholder form: Full suite green (1924), ruff clean. 6 / 28 migrated. Remaining context: Scope (16 fields), Objective (17), Indicator (15), Predefined indicator (14) - the large/threshold-heavy ones, next. |
Batch 2 of the context rollout: - Objective, Indicator and Predefined indicator forms split into Create / Update subclasses over a shared stepped base, three declarative steps each (Identity / Measurement or Format & thresholds / Scope & status or Review & status) and a helper on every field. - The Indicator update view keeps its dynamic get_form_class (predefined vs standard) and the matching template selection, now returning the Update variants. - Modal templates reduced to their header icon; FR translations added. Scope is intentionally left on the legacy centered modal: its bespoke icon picker must first become a reusable widget before it can join the declarative engine. Verified in the browser: the Objective create modal renders the three steps (Identity / Measurement / Scope & status) with all per-field helpers. Full suite green (1924), ruff clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Progress - commit
|
Extract the Scope icon picker (preview button, clear button, searchable Bootstrap-icon grid popover) into context.widgets.IconPickerWidget with a widget template and generic, data-attribute-driven JS + CSS in base.html, so it initialises anywhere - including after an HTMX swap into the modal. The Scope form then joins the declarative engine as a four-step form (Identity / Boundaries / Sites & people / Dates & tags) with a helper on every field; its custom __init__ (parent/site/manager querysets) is kept, and the icon field is now a visible, auto-rendered control. Template reduced to the header icon; FR translations added. This completes the context app: 8/8 forms on the modal engine. Verified in the browser: the Scope create modal renders the four steps; the icon picker opens its grid inside the modal, search works, and selecting an icon updates the preview and closes the popover. Full suite green (1924), ruff clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Progress - commit
|
A step entry can now be a list of cells rendered side by side instead of
always full width, with an optional per-cell width ("auto" or a 1-12
Bootstrap span). Field-coverage validation flattens rows via
Step.field_names(), so the exactly-once rule still holds. Rendering goes
through a new includes/form_row.html; single-cell rows stay plain
full-width fields, so every already-migrated form is unchanged.
Applied to the Scope form: the icon picker and name now share one row
([("icon", "auto"), "name"]). Documented in the Forms doctrine.
Verified in the browser: Scope step 1 shows icon + name on a single line,
each with its label and helper. 2 new unit tests (9 total in
test_modal_forms); full suite green (1926), ruff clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Progress - commit (multi-column rows in the engine)Acting on the review feedback (icon + name should share a line): the declarative engine now supports multi-column rows.
Verified in the browser (see attached layout): icon + name on a single line in Scope step 1. 2 new unit tests (9 in |
Use the new multi-column rows to densify every migrated context form and the compliance Mapping form (paired short selects, date pairs, threshold pairs, contact email/phone, included/excluded sites, etc.), so steps read without scrolling. Fix the Scope icon + name row reported as misaligned: the icon cell was `col-auto` and grew to the width of its helper (which fell back to the model's long help_text once removed from the form), leaving a big gap. The icon picker is a self-evident adornment, so it now carries no helper (empty help_text overrides the model's) and its button height matches the inputs. Forms doctrine refined accordingly (helper mandatory on data fields; self-evident adornment controls may omit it). Verified in the browser: Scope step 1 shows icon + name cleanly aligned on one line with no stray gap. Full suite green (1926), ruff clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Progress - layout polish + form optimizationAddressed the alignment feedback and optimized the already-migrated forms. Icon + name row fix: the icon cell ( Layout optimization: applied multi-column rows to every migrated context form + the Mapping form — paired short selects (type/category), date pairs, threshold min/max, contact email/phone, included/excluded sites, etc. Steps now read denser without scrolling. Verified in the browser: Scope step 1 icon + name aligned cleanly on one line, no stray gap. Full suite green (1926), ruff clean. No new forms migrated this commit (refinement) — still 10/28; remaining: assets (5), compliance (4), risks (8). |
The stepper already labels the current step, so the repeated section heading (icon + title) above the fields was redundant and ate vertical space. Remove it from form_steps.html and delete the now-unused .mform-step-head CSS. Single-step forms were unaffected (no header). Verified in the browser: the Scope modal goes straight from the stepper to the fields. Full suite green (1926). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Progress - remove redundant per-step headerPer feedback: the stepper already names the current step, so the section heading (icon + title, e.g. "IDENTITÉ") above the fields was a duplicate that wasted vertical space. Removed it from Verified in the browser: the Scope modal now goes straight from the stepper to the fields. Full suite green (1926). |
Batch 1 of the assets rollout: - AssetGroupBaseForm + Create/Update subclasses: two steps (Identity / Scope & status), [type, owner] paired on a row, a helper per field. - SiteBaseForm + Create/Update subclasses: two steps (Identity / Location & tags), [type, status] paired; its custom __init__ (parent site tree choices) is preserved. - Both view pairs now set modal titles (New/Edit), previously empty. - Modal templates reduced to their header icon; FR translations added. The Supplier form is deferred: its logo upload needs a reusable image-upload widget (like the icon picker) before it can auto-render. Verified in the browser: the Site create modal renders its two steps with the [type, status] row and per-field helpers. Full suite green (1926), ruff clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Progress - assets batch 1 (Asset group, Site)
Verified: Site create modal renders its two steps with the paired row and per-field helpers (browser). Full suite green (1926), ruff clean. Forms table: Asset group, Site ticked — 12/28 migrated. Supplier deferred (logo upload needs a reusable image-upload widget, like the icon picker). Remaining assets: essential asset, support asset (both large, multi-step) + Supplier. Then compliance (4) and risks (8). |
Assets batch 2 (the two large asset forms): - EssentialAssetBaseForm + Create/Update: four steps (Identity / Security needs / Continuity & data / Relations & status); CIA levels on a 3-col row, MTD/RTO/RPO on a row, data_classification + personal_data paired. - SupportAssetBaseForm + Create/Update: four steps (Identity / Hardware & network / Lifecycle / Relations & status); hardware and date fields paired across rows. - Helper on every field; both view pairs now set modal titles (were empty); templates reduced to the header icon. - FR translations added using the existing "bien" vocabulary (matching the app); the asset helper strings are shared between the two forms. Verified in the browser: the Essential asset create modal renders the four steps with the paired Type/Category and Owner/Custodian rows and per-field helpers. Full suite green (1926), ruff clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Progress - assets batch 2 (Essential asset, Support asset)The two large asset forms, four steps each with dense column rows.
Verified: Essential asset create modal renders the four steps with paired Type/Category and Owner/Custodian rows and per-field helpers (browser). Full suite green (1926), ruff clean. Forms table: Essential asset, Support asset ticked — 14/28 migrated; assets 4/5 (Supplier deferred: logo upload widget needed). Remaining: Supplier, compliance (4), risks (8). |
…complete) Generalise the supplier logo upload into context.widgets.ImageUploadWidget (square preview + camera button + clear) with generic data-attribute JS in base.html that resizes the chosen file to a 128px PNG data-URI client-side (reusing resizeImageFile, now included globally), so it works inside the modal with no multipart submit. The logo / logo_resized field pair is consolidated into a single `logo` data-URI field; save() and the full-page supplier template are updated accordingly. The Supplier form joins the declarative engine as a four-step form (Identity / Contact / Contract / Scope & status), with the logo and name on one row and a helper on every field; modal titles set. Engine: the shell now also renders form.hidden_fields, so any hidden field submits even when no step lists it. This completes the assets app: 5/5 forms on the modal engine. Verified in the browser: the Supplier create modal renders the four steps, the logo widget sits inline with the name, and its file picker / resize / clear wiring is live (resizeImageFile available). Full suite green (1926), ruff clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Progress - reusable ImageUploadWidget + Supplier (assets complete)The Supplier blocker (logo upload) is resolved with a reusable image-upload widget, mirroring how the icon picker unblocked Scope.
Verified in the browser: Supplier create modal renders the four steps; logo widget sits inline with the name and its picker/resize/clear wiring is live. Forms table: Supplier ticked — assets complete (5/5). 15/28 overall. Full suite green (1926), ruff clean. Remaining: compliance (4), risks (8). |
Submitting a multi-step form failed with "An invalid form control with name=... is not focusable": the browser tried to validate/focus a constrained field (e.g. contact_email, website) on a hidden step, which aborted the submit. Multi-step forms now carry `novalidate`; validation is gated per step in JS and enforced server-side (the source of truth), which re-renders the modal on error. Verified in the browser: a supplier is created end to end through the four steps. Full suite green (1926). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fix - multi-step submit (reported on supplier save)
Fix: multi-step forms now carry Verified end to end in the browser: created a supplier (SUPP-1) through all four steps; modal closed and the list refreshed. Full suite green (1926). |
Context
This PR establishes the form doctrine (rewrite of the
Formssection of the brand guidelines) and acts as the tracking point for the refactor: every create / edit / delete form moves to a single, maximally reusable modal model.Decisions made:
Form(Python): single source of truth, near-empty entity templates.The diff in this PR = doctrine in
brand-guidelines.md+ aCHANGELOGentry. The checklist below tracks the implementation to come (not included here).Checklist
Shared building blocks (infra)
smconfirmation,mdsingle-step,lgrich / multi-step)#itemDrawer/#drawer-form-contentoffcanvas and its CSS/JSStep model (declarative on the Form)
Form:(title, icon, [fields])Progress and navigation
aria-current="step")aria-label)Cancel | Back | Next,Next->Saveon the last stepCreate vs Edit
XBaseForm+ thinXCreateForm/XUpdateFormsubclassesServer contract
#hx-live) of the error count after submitCross-cutting
prefers-reduced-motionhonoured (.fw-popentrance)_()/{% trans %}), no duplicatemsgidDocumentation
brand-guidelines.md)/styleguidepage (live component rendering)README.mdandCHANGELOG.mdup to date at each stepTo decide later
FormComplete list of forms to adapt
context/templates/context/scope_form_modal.htmlcontext/templates/context/issue_form_modal.htmlcontext/templates/context/stakeholder_form_modal.htmlcontext/templates/context/objective_form_modal.htmlcontext/templates/context/role_form_modal.htmlcontext/templates/context/activity_form_modal.htmlcontext/templates/context/swot_form_modal.htmlcontext/templates/context/indicator_form_modal.htmlcontext/templates/context/indicator_predefined_form_modal.htmlassets/templates/assets/essential_asset_form_modal.htmlassets/templates/assets/support_asset_form_modal.htmlassets/templates/assets/group_form_modal.htmlassets/templates/assets/supplier_form_modal.htmlassets/templates/assets/site_form_modal.htmlcompliance/templates/compliance/framework_form_modal.htmlcompliance/templates/compliance/finding_form_modal.htmlcompliance/templates/compliance/assessment_form_modal.htmlcompliance/templates/compliance/action_plan_form_modal.htmlcompliance/templates/compliance/mapping_form_modal.htmlcompliance/templates/compliance/assessment_result_form_modal.htmlrisks/templates/risks/risk_form_modal.htmlrisks/templates/risks/iso27005_risk_form_modal.htmlrisks/templates/risks/assessment_form_modal.htmlrisks/templates/risks/treatment_plan_form_modal.htmlrisks/templates/risks/treatment_action_form_modal.htmlrisks/templates/risks/acceptance_form_modal.htmlrisks/templates/risks/threat_form_modal.htmlrisks/templates/risks/vulnerability_form_modal.htmlTotal: 28 forms (9 context, 5 assets, 6 compliance, 8 risks).
🤖 Generated with Claude Code