feat(groups): platform-toggle UI with confirm flow (TASK-2.2.2 2.2.2.1/2.2.2.2/2.2.2.4) - #9
feat(groups): platform-toggle UI with confirm flow (TASK-2.2.2 2.2.2.1/2.2.2.2/2.2.2.4)#9TheBoomerDev wants to merge 3 commits into
Conversation
Adds a Modal-based confirmation dialog for platform-toggle changes on groups. The diff body lists added/removed platform chips with color-blind-safe icons + line-through; an aria-live="polite" region announces the pending change once focus lands inside the modal; a spinner blocks double-click while updatePlatforms is in flight. WCAG 2.1 AA: focus-on-cancel via a local useEffect (no Modal-level prop addition). Re-entrance guard prevents stale-state races. Light + dark parity on every color stop. Modal.tsx receives a small comment note describing how future consumers can land focus on a non-default element; no functional change to the primitive. Vitest spec (ConfirmPlatformChangeDialog.test.tsx) covers: group-name echo, added/removed chip render, no-changes branch, cancel close, confirm fire, confirm is no-op when proposed == current, loading-state disables both buttons, and onClose fires on resolution. Refs: - parent-repo docs/planning/tasks/task-2.2.2.md (TASK-2.2.2.#Kickoff) - parent-repo PR #107 (kickoff planning PR — already merged)
New page mounted at /admin/platforms. Mirrors GroupsList conventions (stats row → filter bar → card grid), but each card carries inline toggle buttons that open the shared ConfirmPlatformChangeDialog emitted in commit 1 of this series. AdminPanel route map and getPageTitle case added. 13 new flat-key i18n entries under groups.platformAccess.* plus admin.platformAccess, populated for en + es + fr. The remaining 7 locales (gl/pt/de/eu/ca/pl/...) rely on react-i18next's defaultValue fallback, matching the project's existing convention for inline-stored translations. Both aria-labels are t()-driven (WCAG/Dark-mode parity maintained); disabled groups render with a visibly de-emphasized toggle grid. Refs: - parent-repo docs/planning/tasks/task-2.2.2.md (TASK-2.2.2.#Kickoff)
…TASK-2.2.2.2) GroupCard no longer executes updatePlatforms directly on every chip click. The legacy `onPlatformToggle: (platform: Platform) => void` prop is renamed to `onPendingPlatformChange: (group, current, proposed) => void` — a staged change request. GroupsList owns the dialog state and renders ONE shared ConfirmPlatformChangeDialog at the bottom of the list, regardless of which card the user clicked. This establishes the consistent pattern across both surfaces: GroupCard and PlatformAccessPage both EMIT pending changes; their parents CONFIRM. Future re-use (audit, undo, multi-toggle) just needs the existing onConfirm callback signature. API break note: GroupCard's `onPlatformToggle` prop is removed; GroupsList is the only consumer and was updated in this commit.
| browse: 'Browse', | ||
| viewProgress: 'View progress', | ||
| 'groups.platformAccess.pageTitle': 'Platform Access', | ||
| 'groups.platformAccess.pageSubtitle': 'Manage platform access across all groups. Changes require confirmation.', |
There was a problem hiding this comment.
WARNING: Missing translations for 5 required languages
Translation keys are only provided for en, es, fr locales. According to i18n requirements, translations must be added for ALL 8 languages: en, es, fr, gal (gl), pt, de, eu, ca.
Reply with @kilocode-bot fix it to have Kilo Code address this issue.
| > | ||
| <MaterialIcon name="close" size="text-sm" decorative /> | ||
| <span>{PLATFORMS[p].name}</span> | ||
| </li> |
There was a problem hiding this comment.
WARNING: Confirm button missing dark mode background variants
The confirm button only specifies bg-blue-600 hover:bg-blue-700 without corresponding dark: variants. All buttons must have explicit dark mode background colors per UI/UX standards.
Reply with @kilocode-bot fix it to have Kilo Code address this issue.
| <div className="text-xs font-semibold text-red-700 dark:text-red-400 uppercase tracking-wide"> | ||
| {t( | ||
| 'groups.platformAccess.removedHeading', | ||
| 'Will be disabled' |
There was a problem hiding this comment.
WARNING: Cancel button relies on default browser styling
The cancel button uses default browser styling instead of explicit background colors with dark mode variants. All buttons should define explicit background colors for consistent appearance across themes.
Reply with @kilocode-bot fix it to have Kilo Code address this issue.
What
Implements TASK-2.2.2 (Platform Toggle UI) on
cargoffer/education_frontend:PlatformAccessPagemounted at/admin/platforms.ConfirmPlatformChangeDialogwired into bothGroupCard(via the existingGroupsListsurface) and the newPlatformAccessPage.defaultValuefallback for gl/pt/de/eu/ca/pl), WCAG 2.1 AA, dark-mode parity, Vitest coverage.Subtask 2.2.2.3 (backend
FEATURE_TOGGLEDaudit emission onPUT /api/groups/:id/platforms) is a separate BE PR oncargoffer/education_backend— out of scope here. The dialog already exposes anaffectedUserCountslot wired for that follow-up.Why
Until now, every chip click in
GroupCardfiredupdatePlatformswithout confirmation. TASK-2.2.2 replaces that destructive one-click behaviour with a modal confirmation flow:affectedUserCount.PlatformAccessPageadds a dedicated super-admin surface for bulk platform-toggle operations across every group (per-platform stats row, search/filter bar, inline toggle grid).Branch base
feat/task-2.2.2-platform-toggle-uiorigin/mainCommits (keep this history — please squash-merge with the merge commit title)
feat(admin/groups): add ConfirmPlatformChangeDialog (TASK-2.2.2.2)— new dialog + 8-case Vitest spec;Modal.tsxgets a small doc comment (no functional surface-area change).feat(admin): add PlatformAccessPage at /admin/platforms (TASK-2.2.2.1)— new page, AdminPanel route, 13 new flat i18n keys × 3 locales.refactor(admin/groups): lift platform-toggle dialog into GroupsList (TASK-2.2.2.2)—GroupCard.onPlatformTogglerenamed toonPendingPlatformChange(group, current, proposed); dialog state lifts to the list.Files
Added (3):
src/components/admin/PlatformAccessPage.tsxsrc/components/admin/groups/ConfirmPlatformChangeDialog.tsxsrc/components/admin/groups/ConfirmPlatformChangeDialog.test.tsxModified (5):
src/components/admin/groups/GroupCard.tsxsrc/components/admin/groups/GroupsList.tsxsrc/components/admin/AdminPanel.tsxsrc/components/ui/Modal.tsx(5-line doc comment only — no functional change)src/i18n.ts(13 new flat keys × en + es + fr = 39 entries)WCAG 2.1 AA
<Modal>.useEffectin the dialog; not via a sharedModalprop).aria-live="polite"region announces the proposed change once focus lands inside the modal.i18n
groups.platformAccess.*+admin.platformAccess, populated for en + es + fr.i18next-browser-languagedetectordefault) use react-i18next'sdefaultValuefallback — the same convention that the rest of the inline-stored translations already follow.Tests
Vitest spec for
ConfirmPlatformChangeDialog(8/8 passing):onCloseonConfirmexactly onceonConfirmis in flight,onClosefires on resolutionValidation
→ 0 TypeScript errors, 8/8 dialog tests passing.
Pre-existing issues (NOT introduced by this PR — flagged for visibility only)
src/components/admin/audit/RecentPlatformChangesWidget.tsx:106—@typescript-eslint/no-explicit-any(pre-existing; not in this PR's diff).src/components/auth/ProtectedRoute.test.tsxlooking for legacy strings like "Instructor Courses" / "Student Dashboard"; older copy drifted from the actual<h1>).v10.0.0deprecation warning spotted during commit (#!/usr/bin/env sh+. "$(dirname -- "$0")/_/husky.sh"are flagged for removal).Reviewer checklist
Modal.tsxdiff reduces to a documentation comment only.anytypes introduced anywhere.t(key, defaultValue)(no hardcoded English leaks).GroupCardno longer firesupdatePlatformsdirectly — it emits a pending change throughonPendingPlatformChange.PlatformAccessPagerow toggle buttons also emit pending changes (parity with the GroupCard path).Forward-look
FEATURE_TOGGLEDaudit emission +affectedUserCount) oncargoffer/education_backend— natural follow-up. When that PR lands, the dialog can populate the amber "X users will be affected" note without further UI changes.docs/planning/PRIORITIES.md.Cross-repo refs:
cargoffer/education_portalPR #107 —docs(planning): kickoff TASK-2.2.2 platform-toggle-ui on feat/task-2.2.2-platform-toggle-ui(still open at time of writing).docs/planning/tasks/task-2.2.2.mdin the planning-repo submodule of the parent project.