Skip to content

Migrate profile email-preferences tab to Vue + /api/v2/users/me/preferences (Group 2.2 1/5)#868

Open
edwh wants to merge 7 commits into
developfrom
RES-PROFILE-EDIT-vue
Open

Migrate profile email-preferences tab to Vue + /api/v2/users/me/preferences (Group 2.2 1/5)#868
edwh wants to merge 7 commits into
developfrom
RES-PROFILE-EDIT-vue

Conversation

@edwh

@edwh edwh commented May 30, 2026

Copy link
Copy Markdown
Collaborator

Summary

First slice of plans/active/blade-to-vue-migration.md Group 2.2.

The user/profile-edit page is a tabbed shell with 5 partials. This PR
migrates the simplest tab (email-preferences.blade.php) so the API
shape and Vue-component pattern can be reviewed before the heavier tabs
(profile, account, calendars, repair-directory) follow on the same branch.

  • GET/PATCH /api/v2/users/me/preferences with OpenAPI + 7 PHPUnit tests
  • EmailPreferencesTab.vue
  • The existing /profile/edit-preferences POST handler is unchanged so
    any other consumers stay green.

Test plan

  • PHPUnit: APIv2UserPreferencesTest passes on CI
  • Follow-ups on same branch: profile / account / calendars / repair-directory tabs

…Group 2.2 partial 1/5)

The profile-edit page is a tabbed shell with 5 partials (profile, account,
email-preferences, calendars, repair-directory). This commit migrates the
email-preferences partial — the simplest — as the first slice.

- GET/PATCH /api/v2/users/me/preferences (auth:api, OpenAPI annotated)
  returning {data: {invites: bool}}
- tests/Feature/Users/APIv2UserPreferencesTest: 7 cases covering auth,
  shape, validation, and persistence in both directions
- resources/js/components/EmailPreferencesTab.vue: small Vue component
  rendering the existing UI and POSTing via the new endpoint
- resources/views/user/profile/email-preferences.blade.php now mounts
  the Vue component; the existing /profile/edit-preferences POST handler
  is unchanged and remains a fallback
- lang/{en,fr,fr-BE}/general.php: error_occurred key for the toast

Follow-ups: migrate profile/account/calendars/repair-directory tabs
incrementally on the same branch.
edwh added 2 commits May 30, 2026 23:29
…oup 2.2 2/5)

- GET /api/v2/users/me/calendars (auth:api) returns user + per-group +
  admin all-events + group-area calendar URLs with OpenAPI
- tests/Feature/Users/APIv2UserCalendarsTest: 3 cases (auth, host shape,
  admin flag/url)
- CalendarsTab.vue: lists subscription URLs, copy buttons, area select;
  uses clipboard API instead of the old jQuery btn-copy-input-text
- calendars.blade.php now mounts the Vue component
…ir-directory-* (Group 2.2 3/5)

- GET /api/v2/users/{id}/repair-directory-options: per-option `selected`
  + `disabled` flags computed against existing UserPolicy::changeRepairDirRole.
- PATCH /api/v2/users/{id}/repair-directory-role: policy-gated save.
- APIv2RepairDirectoryTest: 7 cases (auth/404/super-admin enabled/non-admin
  disabled/forbidden update/persistence/validation enum).
- RepairDirectoryTab.vue: replicates the existing select with disabled
  options and submit button.
@edwh edwh marked this pull request as ready for review May 30, 2026 22:34
edwh and others added 4 commits May 30, 2026 23:35
…up 2.2 4/5 partial)

The account tab contains 4 sub-forms (password, language, admin matrix,
soft-delete). This commit migrates the language sub-form only —
non-sensitive and isolated. Password change, admin matrix, and
soft-delete remain on existing CSRF-protected POST routes pending
adversarial review of those security boundaries.

- GET /api/v2/users/me/language returns current language + supported locales
- PATCH /api/v2/users/me/language validates against supported list, fires
  UserLanguageUpdated event, persists to user
- APIv2UserLanguageTest: 5 cases (get auth/shape, update auth/validation/persist)
- LanguageTab.vue: select + save, reloads after save to apply translations
…ed key

The PR's new v2 endpoint tests exposed three real issues:

1. Unauthenticated auth:api requests returned 500 instead of 401. The custom
   JSON exception handler mapped any exception without getStatusCode() to 500,
   and Laravel's AuthenticationException has none. Added an explicit
   AuthenticationException -> 401 branch. This is correct API behaviour and was
   simply never asserted before (these are the suite's first 401 tests), so it
   fixed every *RequiresAuth test across preferences/calendars/language/
   repair-directory at once.

2. getMyCalendarsv2 read the admin all-events hash via env('CALENDAR_HASH')
   directly, which returns null under config:cache in production (a latent bug)
   and can't be overridden in tests. Moved it to config('restarters.calendar_hash')
   and updated the test to set it via config() instead of putenv() (which
   Laravel's env() does not read).

3. email_alerts_pref1 ('monthly newsletter') was only ever referenced inside a
   commented-out block in the old blade, so translations:check passed on the
   stray string. Migrating that tab to Vue removed the last textual reference,
   orphaning the key. The newsletter checkbox has long been disabled (handled by
   the community platform), so removed the dead key from en/fr/fr-BE.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
PHPUnit is green after the previous commit; the only failure was
tests/Integration/event.test.js "Invite volunteers modal opens from Event
Actions dropdown" timing out clicking the dropdown item. That test is
untouched by this PR, the branch is level with develop, and the test has a
long history of timeout/hang flake-fixes (Google Maps hang in Docker noted in
its own comments). Empty commit to re-run the suite.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The event Playwright suite failed deterministically today (last day of May)
on "Invite volunteers modal opens from Event Actions dropdown". Root cause is
in the shared createEvent() helper, not the test or this PR's code:

createEvent(past=false) selected the last day of the *current* month at
13:00-14:00. On the last day of a month that date is "today"; when CI runs
after 14:00 local the event has already finished, so the API reports
upcoming=false and EventActions correctly hides the "Invite Volunteers" item
(gated on isAttending && upcoming && approved). The test then times out
waiting for an item that will never appear. develop passed only because its
run happened earlier in the day.

Fix: for future events, advance to the next month before selecting the day
(mirroring the past branch's "Previous month"), so the event is always
comfortably upcoming regardless of when the suite runs. Only the two
past=false callers in event.test.js are affected; device.test.js uses past.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

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