Unify dialogs and confirmations on one native dialog pattern closes #615 #617

Merged
simon merged 34 commits from issue/mitgliederverwaltung-615 into main 2026-10-23 22:33:44 +02:00
Owner

Description of the implemented changes

The changes were:

  • Bugfixing
  • New Feature
  • Breaking Change
  • Refactoring

Dialog and confirmation components: one dialog pattern, one confirmation rule. Every dialog in the app now renders through <.modal> (a native <dialog> whose close paths all route through on_cancel) and every confirmation through <.confirm_dialog>, with one rule for when a confirmation is simple and when the user types the name: deleting a member, group, data field or all fee cycles is typed; the rest is a simple confirmation that states the consequence in a warning callout. The server re-checks the permission on the record it holds before deleting, data-confirm and hand-built modal markup are gone (pinned at zero by the UI guard), and the legacy server-pushed focus restore is removed in favour of the dialog's own focus return.

What has been changed?

  • Add a native modal dialog that every close path routes through on_cancel, with simple and typed confirmations on top
  • Re-check the destroy permission on the record held in assigns before deleting
  • Show the modal and both confirmation strengths in the overlay section
  • Confirm role deletion in the shared dialog and delete only the role held in assigns
  • Confirm user deletion in the shared dialog and delete only the user held in assigns
  • Confirm member deletion in the shared dialog and delete only the member held in assigns
  • Confirm group deletion by typing its name in the page that asks, never via a URL parameter
  • Confirm data field deletion by its visible name in the edit form
  • Ask for the exit date in the shared form dialog and confirm deactivation with a toast
  • Move the cycle dialogs to the shared dialogs and confirm deleting all cycles by the member's name
  • Confirm a fee type's amount change in the shared dialog and delete the fee type from its form's danger zone
  • Approve at once and confirm a rejection in the shared dialog
  • Drop the server-pushed focus restore that native dialogs replaced
  • Hold data-confirm and raw modal markup at zero with no exceptions left
  • Describe the shared dialogs, the decided confirmation rules and the helpers behind them
  • State every confirmation's consequence in a warning callout, with the final note below it
  • Confirm member deletion by typing the full name, stating the fee cycles deleted along
  • Offer a role's deletion only while no user holds it, and say why otherwise
  • Word delete consequences in the present tense, quote the names and count with real plurals
  • Keep cycle dialog errors at their fields, name each row's delete button and return focus to a control that still exists
  • Report a delete dialog that cannot load its record and name a missing permission instead of a generic error
  • Name the member linked when the delete dialog opens, with UserDeletion owning the dialog like its siblings
  • Share the typed confirmation's state handling and name its assigns after the flow
  • Draw callouts in the body color with a visible ink border and tint
  • Fade a closing dialog out while it is still open, so the backdrop clears in one step in every browser
  • Mark a field with an error invalid and describe it by its message, also in the dialogs
  • Show the permission message when a resource policy refuses a delete
  • Re-count a role's users and a fee type's members when the delete dialog opens, and refuse it when counting fails
  • Ignore a dialog's confirm that arrives after its cancel
  • Drop the wrappers that only mounted the retired focus restore hook
  • Move focus to a named control when a dialog refuses to open, instead of letting it fall to body
  • Show the text to type as its own code block under a body-size instruction

Definition of Done

Code Quality

  • No new technical depths
  • Linting passed
  • Documentation is added were needed

Accessibility

  • New elements are properly defined with html-tags
  • Colour contrast follows WCAG criteria
  • Aria labels are added when needed
  • Everything is accessible by keyboard
  • Tab-Order is comprehensible
  • All interactive elements have a visible focus

Testing

  • Tests for new code are written
  • All tests pass
  • axe-core dev tools show no critical or major issues

Additional Notes

  • The review loop reached its 4-iteration cap; the remaining findings were accepted. A further review of the visual-tuning changes found three issues, all fixed in this branch: a late Enter confirming a cancelled dialog, the result toast not announced to screen readers, and the close animation ignoring reduced motion.
  • The dialog close was verified in Firefox and Chromium: daisyUI's close fade relies on transition: overlay, which Firefox and Safari lack, so the dialog now fades out while still open.
  • Larger commits: the per-page migrations to the shared dialogs (role, user, member, group, data field, deactivation, fee cycles, fee type), the warning-callout commit (28 files), the wording sweep (13 files) and the wrapper removal (617+/632−, re-indentation only).
  • Current main (#611, LiveView tests async) is merged in. #611 made role_live_test.exs async, so the merge commit moves this branch's two log-capturing role tests into the serial module role_live_logging_test.exs, as #611 did for its own log tests.
  • Out of scope, noted for later issues: unifying the danger zones (U5), the "create cycle" page and the cycle status buttons (U9/#540), undoing join request decisions from the history.
## Description of the implemented changes The changes were: - [x] Bugfixing - [x] New Feature - [ ] Breaking Change - [x] Refactoring Dialog and confirmation components: one dialog pattern, one confirmation rule. Every dialog in the app now renders through `<.modal>` (a native `<dialog>` whose close paths all route through `on_cancel`) and every confirmation through `<.confirm_dialog>`, with one rule for when a confirmation is simple and when the user types the name: deleting a member, group, data field or all fee cycles is typed; the rest is a simple confirmation that states the consequence in a warning callout. The server re-checks the permission on the record it holds before deleting, `data-confirm` and hand-built modal markup are gone (pinned at zero by the UI guard), and the legacy server-pushed focus restore is removed in favour of the dialog's own focus return. ## What has been changed? - Add a native modal dialog that every close path routes through on_cancel, with simple and typed confirmations on top - Re-check the destroy permission on the record held in assigns before deleting - Show the modal and both confirmation strengths in the overlay section - Confirm role deletion in the shared dialog and delete only the role held in assigns - Confirm user deletion in the shared dialog and delete only the user held in assigns - Confirm member deletion in the shared dialog and delete only the member held in assigns - Confirm group deletion by typing its name in the page that asks, never via a URL parameter - Confirm data field deletion by its visible name in the edit form - Ask for the exit date in the shared form dialog and confirm deactivation with a toast - Move the cycle dialogs to the shared dialogs and confirm deleting all cycles by the member's name - Confirm a fee type's amount change in the shared dialog and delete the fee type from its form's danger zone - Approve at once and confirm a rejection in the shared dialog - Drop the server-pushed focus restore that native dialogs replaced - Hold data-confirm and raw modal markup at zero with no exceptions left - Describe the shared dialogs, the decided confirmation rules and the helpers behind them - State every confirmation's consequence in a warning callout, with the final note below it - Confirm member deletion by typing the full name, stating the fee cycles deleted along - Offer a role's deletion only while no user holds it, and say why otherwise - Word delete consequences in the present tense, quote the names and count with real plurals - Keep cycle dialog errors at their fields, name each row's delete button and return focus to a control that still exists - Report a delete dialog that cannot load its record and name a missing permission instead of a generic error - Name the member linked when the delete dialog opens, with UserDeletion owning the dialog like its siblings - Share the typed confirmation's state handling and name its assigns after the flow - Draw callouts in the body color with a visible ink border and tint - Fade a closing dialog out while it is still open, so the backdrop clears in one step in every browser - Mark a field with an error invalid and describe it by its message, also in the dialogs - Show the permission message when a resource policy refuses a delete - Re-count a role's users and a fee type's members when the delete dialog opens, and refuse it when counting fails - Ignore a dialog's confirm that arrives after its cancel - Drop the wrappers that only mounted the retired focus restore hook - Move focus to a named control when a dialog refuses to open, instead of letting it fall to body - Show the text to type as its own code block under a body-size instruction ## Definition of Done ### Code Quality - [ ] No new technical depths - [x] Linting passed - [x] Documentation is added were needed ### Accessibility - [ ] New elements are properly defined with html-tags - [ ] Colour contrast follows WCAG criteria - [ ] Aria labels are added when needed - [ ] Everything is accessible by keyboard - [ ] Tab-Order is comprehensible - [ ] All interactive elements have a visible focus ### Testing - [x] Tests for new code are written - [x] All tests pass - [ ] axe-core dev tools show no critical or major issues ## Additional Notes - The review loop reached its 4-iteration cap; the remaining findings were accepted. A further review of the visual-tuning changes found three issues, all fixed in this branch: a late Enter confirming a cancelled dialog, the result toast not announced to screen readers, and the close animation ignoring reduced motion. - The dialog close was verified in Firefox and Chromium: daisyUI's close fade relies on `transition: overlay`, which Firefox and Safari lack, so the dialog now fades out while still open. - Larger commits: the per-page migrations to the shared dialogs (role, user, member, group, data field, deactivation, fee cycles, fee type), the warning-callout commit (28 files), the wording sweep (13 files) and the wrapper removal (617+/632−, re-indentation only). - Current main (#611, LiveView tests async) is merged in. #611 made `role_live_test.exs` async, so the merge commit moves this branch's two log-capturing role tests into the serial module `role_live_logging_test.exs`, as #611 did for its own log tests. - Out of scope, noted for later issues: unifying the danger zones (U5), the "create cycle" page and the cycle status buttons (U9/#540), undoing join request decisions from the history.
daisyUI fades a dialog out after close() and keeps it in the top layer
meanwhile with `transition: overlay`, which Firefox and Safari lack: there
the closed dialog dropped below the sidebar at once and the backdrop
cleared in two steps. The dialog now takes daisyUI's closed look while it
is still open and closes after the fade. It is inert during the fade, so a
late Enter cannot confirm a cancelled dialog, and with reduced motion it
closes at once.
Escape, then Enter while the dialog fades out, could still submit the
deactivation or a cycle dialog after the cancel had reached the server.
The handlers now ignore a submit whose dialog is no longer open.
Merge remote-tracking branch 'origin/main' into issue/mitgliederverwaltung-615
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/promote/production Build is passing
0a25b1b6e3
simon merged commit baa69474fb into main 2026-10-23 22:33:44 +02:00
simon deleted branch issue/mitgliederverwaltung-615 2026-10-23 22:33:44 +02:00
Sign in to join this conversation.
No description provided.