Skip to content

[Rebrand][WIP] RE-107: /users/edit account settings — page chrome + safe DS swaps (1/N)#73446

Draft
levadadenys wants to merge 14 commits into
stagingfrom
denys/re/users-edit-rebrand
Draft

[Rebrand][WIP] RE-107: /users/edit account settings — page chrome + safe DS swaps (1/N)#73446
levadadenys wants to merge 14 commits into
stagingfrom
denys/re/users-edit-rebrand

Conversation

@levadadenys

@levadadenys levadadenys commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

WIP — multi-PR migration. /users/edit (account settings) is large and
mostly auth-/compliance-sensitive, so it's being moved to the design system in
pieces. This branch collects the slices done so far.

Summary

Migrates the account-settings page (/users/edit, devise/registrations/edit)
to the design system, incrementally. Each flow keeps its server/CSRF wiring
intact and only swaps the UI to DSCO/MUI. Legacy BaseDialog / SystemDialog
sources are not edited — each dialog uses the component-library Modal at
its call site instead.

Page chrome + safe swaps

  • AccountEditHeader (back link + title) → DSCO Link + MUI Typography.
  • DeleteAccountHelpers links → DSCO Link.
  • LtiRosterSyncSettings → MUI Typography + Button.
  • MigrateToMultiAuth → DSCO NotificationBanner.

Full flows

  • Add Password — DSCO TextField, MUI Button/Typography.
  • Change User Type (confirmation modal) — DSCO Modal + TextField +
    SimpleDropdown + Link. (The HAML trigger — the user-type select + "Update
    Account Type" button — is still legacy; see below.)
  • Add/Update Parent Email — DSCO Modal + TextField + RadioButton + Link.
  • Change EmailBaseDialog shell → DSCO Modal.
  • Delete Account — section (MUI error Button), DeleteAccountDialog (DSCO
    Modal/Checkbox/TextField/FontAwesomeV6Icon), gate dialogs
    AdminAccountDialog + PersonalLoginDialog → DSCO Modal.

Always-visible elements

  • Manage Linked Accounts — gray BootstrapButtons → MUI Button
    (Connect contained-primary / Disconnect outlined-secondary); colors → tokens.
  • Manage Other Sessions — legacy HAML .btn-warning section → React
    ExpireOtherSessions (MUI button submitting a native Rails DELETE form + CSRF).

Recent visible fixes

  • Section headers (Account/School Information) now use the regular text color (--text-neutral-primary), not teal.
  • Delete Account “managed by teacher” notice folded into the React DeleteAccount (MUI Typography), removing the last .danger HAML there.
  • Manage Linked Accounts / Manage Other Sessions buttons → outlined secondary.

Now migrated (this round)

  • For Parents and Guardians → React: black section header (MUI Typography),
    body2 text; Update/Remove are DSCO Links keeping their ids so the existing
    controllers attach unchanged.
  • Account Type → React ChangeUserTypeSection: black header, DSCO
    SimpleDropdown, outlined-secondary confirm button. ChangeUserTypeController
    keeps submitUserTypeChange/modal (auth path unchanged); a new handleConfirm
    drives submit-vs-modal.
  • Section headers now use the regular text color (--text-neutral-primary),
    not teal; the Delete Account header stays the danger red
    (--text-error-primary).

Out of scope

The green success flash banner ("Successfully logged out…") is rendered by
the global layouts/application.html.haml (site-wide), so it's not part of this
page — worth a separate ticket for DS flash banners.

Links

  • Jira: RE-107

Testing story

  • Unit: all affected suites green (~120 tests) — accounts header/password/
    migrate, change-user-type, add-parent-email, change-email, delete-account,
    manage-linked-accounts, expire-other-sessions. pre-commit (incl. haml-lint),
    yarn typecheck, yarn build all clean.
  • Test notes: AddParentEmailModalTest matches email fields by anchored-regex
    name (DSCO TextField puts error text in the field <label>; parent-email
    label is a prefix of confirmed-email label); ChangeEmailModalTest asserts the
    save button's MUI loading state, not a specific spinner; ManageLinkedAccountsTest
    queries connect/disconnect buttons by button[type="submit"]. Coverage unchanged.
  • Manual: verified visually in ?brand=codeai-next (pink audit). The static
    page now shows DSCO for the header, account info, linked-accounts buttons, and
    the sessions button; the modals are exercised on click. Remaining legacy bits
    are the HAML items listed above.

levadadenys and others added 14 commits June 24, 2026 14:52
First slice of the /users/edit (account settings) rebrand — low-risk,
presentational pieces, steering clear of the auth/compliance-sensitive dialogs
and shared primitives (BaseDialog/Notification/SystemDialog), which are later PRs.

- New AccountEditHeader (DSCO Link back link + MUI Typography title) mounted in
  place of the legacy HAML back anchor + h1.text-black.
- DeleteAccountHelpers: the two help links -> DSCO Link.
- LtiRosterSyncSettings: section heading -> MUI Typography, .btn button -> MUI
  Button (the toggle was already DSCO).
- MigrateToMultiAuth: legacy Notification banner -> DSCO Alert.

Tests: new AccountEditHeaderTest + MigrateToMultiAuthTest; DeleteAccountTest
still green. Lint + build clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Convert the Add Password flow end-to-end to the design system, and switch the
multi-auth migration notice from Alert to the richer NotificationBanner (per
review — title/description/action fits the notice-with-CTA shape better).

AddPasswordForm:
- SystemDialog Field + raw <input type=password> -> DSCO TextField
  (inputType=password, label, errorMessage). Old SystemDialog source untouched.
- BootstrapButton -> MUI Button; <h2>/hint -> MUI Typography.
- inline styles + color.js -> add-password-form.module.scss with semantic
  tokens (error text -> --text-error-primary).
The controller passes password values through the handleSubmit(pw, confirm)
callback and submits the hidden Rails form, so input markup is free to change.

MigrateToMultiAuth: Alert -> NotificationBanner (variant info, filled), notice
as title, details as description, migrate button as an MUI Button action.

Tests: AddPasswordFormTest (16) + MigrateToMultiAuthTest green; lint, typecheck,
build clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Convert the Change User Type modal flow (student -> teacher confirmation) to the
design system. The HAML trigger (user-type select + button) and the jQuery
ChangeUserTypeController are untouched — the controller still mounts this modal
via createReactRoot and submits the hidden Rails form.

ChangeUserTypeModal:
- BaseDialog + SystemDialog Header/ConfirmCancelFooter -> DSCO Modal
  (title, description, customContent, primary/secondary buttons, customBottom
  status). Cancel keeps i18n.cancel(); Esc/X close routes through handleClose,
  which no-ops while saving (preserves the old `uncloseable` behavior).
- color.js wrapper -> change-user-type-modal.module.scss.

ChangeUserTypeForm:
- SystemDialog Field + raw <input>/<select> -> DSCO TextField (email) and
  SimpleDropdown (opt-in); privacy <a> -> DSCO Link; inline styles ->
  change-user-type-form.module.scss. Email autofocus + focus-on-error now reach
  the input via a root-ref querySelector (TextField doesn't forward a ref).

Legacy BaseDialog/SystemDialog sources left untouched per review.

Tests: ChangeUserTypeModalTest/FormTest/ControllerTest all green (33); lint,
typecheck, build clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Convert the student Add/Update Parent Email modal to the design system. The
AddParentEmailController/RemoveParentEmailController and the hidden Rails forms
are untouched — the controller still mounts this modal and submits the form.

AddParentEmailModal:
- BaseDialog + SystemDialog Header/Field/ConfirmCancelFooter -> DSCO Modal
  (title, subtitle as description, customContent, primary/secondary buttons,
  customBottom status). Esc/X routes through handleClose, no-op while saving.
- two email Fields -> DSCO TextField; opt-in radios -> DSCO RadioButton;
  privacy <a> -> DSCO Link; opt-in heading -> MUI Typography.
- color.js inline styles -> add-parent-email-modal.module.scss with semantic
  tokens (--borders-neutral-strong, --background-neutral-secondary).
- focus-on-error reaches the email input via a content-ref querySelector.

Test: query the email fields by anchored-regex name — the DSCO TextField puts
its error text inside the field <label> (so it joins the accessible name), and
the parent-email label is a prefix of the confirmed-email label. 11 modal +
13 controller tests green; lint, typecheck, build clean.

Legacy BaseDialog/SystemDialog sources left untouched per review.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Finish migrating the Change Email flow. The form (ChangeEmailForm.tsx) was
already DSCO (TextField, RadioButtonsGroup, Link); this swaps the modal shell
and the last legacy color.

ChangeEmailModal:
- BaseDialog + manual CloseButton/title/button-row -> DSCO Modal (title,
  customContent, primary/secondary buttons; Esc/X via handleClose, no-op while
  saving). Drops the bespoke CloseButton + MUI Typography/Button scaffolding.
- unknown-error Alert kept, moved into customContent.
- modalStyle.module.scss trimmed to just the Alert spacing.

formStyle.module.scss: error text $light_negative_500 -> var(--text-error-primary);
drop the legacy `@import 'color'`.

Test: the save button's loading indicator now renders via the Modal's own MUI
context (MUI CircularProgress) rather than the app theme's FontAwesome spinner,
so the saving assertion checks the MUI loading state instead of the fa-spinner
class. 55 tests green; typecheck, lint, build clean.

Legacy BaseDialog source left untouched per review.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Convert the core Delete Account flow. The delete request path ($.ajax DELETE
/users) and the gate dialogs (PersonalLoginDialog, AdminAccountDialog) are
unchanged; those two gate dialogs are migrated in a follow-up.

DeleteAccount (page section):
- BootstrapButton (danger) -> MUI Button color="error"; <h2> -> MUI Typography;
  color.js inline styles -> delete-account.module.scss with semantic tokens
  (--text-error-primary, --borders-error-primary for the danger divider).

DeleteAccountDialog:
- BaseDialog + SystemDialog Header/Field/ConfirmCancelFooter -> DSCO Modal
  (title, customContent, primary[error]/secondary buttons, customBottom delete
  error). Renders null when !isOpen (DSCO Modal has no isOpen prop).
- raw checkboxes -> DSCO Checkbox (ReactNode labels); password/verification
  Field+input -> DSCO TextField; legacy FontAwesome -> FontAwesomeV6Icon;
  color.js -> delete-account-dialog.module.scss.

Tests: DeleteAccountTest (14) green; typecheck, lint, build clean.
Legacy BaseDialog/SystemDialog sources left untouched per review.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Finish the Delete Account flow by migrating the two gate dialogs that precede
the delete confirmation.

AdminAccountDialog:
- BaseDialog + SystemDialog Header/ConfirmCancelFooter -> DSCO Modal (title,
  customContent warning, primary/secondary buttons). Renders null when !isOpen.
- color.js -> admin-account-dialog.module.scss (--text-error-primary).

PersonalLoginDialog:
- BaseDialog + Header/ConfirmCancelFooter -> DSCO Modal; legacy
  legacySharedComponents/Button (help link) -> MUI Button (href, new tab);
  body paragraphs -> MUI Typography; color.js -> personal-login-dialog.module.scss.
- GlobalEditionWrapper default export preserved.

Tests: DeleteAccountTest (14, exercises both gate dialogs) green; typecheck,
lint, build clean. Legacy BaseDialog/SystemDialog sources untouched per review.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Address the most visible legacy gap on /users/edit: the Manage Linked Accounts
table's gray Connect/Disconnect buttons.

ManageLinkedAccounts:
- BootstrapButton -> MUI Button (Connect: contained primary; Disconnect:
  outlined secondary), still type=submit inside the native connect/disconnect
  POST forms.
- <h2> -> MUI Typography; color.js (light_gray, red) -> semantic tokens
  (--text-neutral-secondary, --text-error-primary) in the existing inline styles.
- Drops the now-unused color/fontConstants imports.

The table layout (shared tableLayoutStyles) and the LTI AccountUnlinkWarningModal
(a hidden modal) are unchanged; the unlink modal is a follow-up.

Test: ManageLinkedAccountsTest queries the connect/disconnect buttons by
button[type="submit"] instead of the BootstrapButton component name (16 green);
typecheck, lint, build clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replace the legacy HAML "Manage Other Sessions" section (orange .btn-warning
submit) with a React ExpireOtherSessions component mounted at #expire-other-sessions.

- MUI Typography heading/description + MUI Button, submitting a native Rails
  DELETE form (hidden _method=delete + RailsAuthenticityToken CSRF) to the
  expire path. Heading/description/button text + action come from the mount
  point's data-* attributes so localization stays Rails-side (matching
  AccountEditHeader).
- edit.js mounts it; the HAML form_for/.btn-warning is removed.

Test: ExpireOtherSessionsTest (renders heading/description/button; form posts
DELETE to the expire path). typecheck, lint (incl. haml-lint), build clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ts + sessions

Per review, make the Manage Linked Accounts Connect/Disconnect buttons and the
Manage Other Sessions button all outlined secondary (was contained primary for
Connect / the sessions button).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…teal

The Account/School Information section headers were colored var(--brand-teal-50)
(a primitive that ignores theming) / var(--text-brand-teal-primary). Per review,
section headers should use the regular typography color, so they now use
var(--text-neutral-primary) (theming correctly instead of staying teal).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…llback

The teacher-managed delete notice was the only remaining legacy HAML in the
delete section (.danger hr + h2 + p). Fold it into the React DeleteAccount
component: the mount point now always renders, and DeleteAccount shows either
the delete UI or, when canDelete is false, the managed-account notice (MUI
Typography, danger-tokened header + divider).

- HAML: single #delete-account mount with data-can-delete + data-managed-note;
  the if/else .danger block is removed.
- edit.js passes canDelete + managedNote from the mount point.

Test: DeleteAccountTest gains a no-permission case (15 green); typecheck, lint
(incl. haml-lint), build clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…L -> React)

Replace the legacy HAML "For Parents and Guardians" section with a React
ForParentsAndGuardians component: black section header (MUI Typography +
shared sectionHeader, --text-neutral-primary) and body2 Typography text, per
review.

The Update/Remove links are DSCO Links that keep their original ids
(#add-parent-email-link / #remove-parent-email-link) and the displayed email
keeps #displayed-parent-email, so the existing AddParentEmailController /
RemoveParentEmailController attach unchanged. The component is mounted before
those controllers are constructed; the hidden Rails forms are untouched. Text
is passed via data-* so localization stays Rails-side.

Test: ForParentsAndGuardiansTest (heading/intro/email/note + Update link id;
Remove link only when a parent email exists). typecheck, lint (incl. haml-lint),
build clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the legacy HAML change-user-type trigger (native <select> + gray .btn,
driven by ChangeUserTypeController's jQuery DOM wiring) with a React
ChangeUserTypeSection: black section header (MUI Typography), DSCO
SimpleDropdown, and an outlined-secondary MUI confirm button.

The auth submission is unchanged: ChangeUserTypeController keeps
submitUserTypeChange / handleSuccess / the email-confirmation modal. Its
constructor no longer wires the DOM; a new handleConfirm(selectedType) sets the
hidden user_type field and either opens the modal (-> teacher) or submits
directly (-> student). edit.js renders the section with onConfirm=handleConfirm.

- HAML: the form keeps its hidden fields (+ a new hidden user_type); the visible
  select/button/heading become a #change-user-type mount point.
- Section shows saving/error state around the direct-submit promise.

Tests: new ChangeUserTypeSectionTest (5) covers the UI (disabled-until-changed,
onConfirm, saving/error); ChangeUserTypeControllerTest rewritten to cover
handleConfirm + submitUserTypeChange + modal show/hide (was DOM-coupled).
ChangeUserTypeModalTest still green. typecheck, lint (incl. haml-lint), build clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant