screentinker/frontend
ScreenTinker caa9fd0f40 feat(workspaces): mutation UI for members (slice 2B)
Completes P2 user-management. Adds the full admin surface for managing
workspace membership: invite modal, role change, member remove, cancel
pending invite. All admin-gated client-side via can_admin from /me,
server-gated via canAdminWorkspace.

Component additions:
- NEW workspace-members-invite-modal.js (~115 LOC). Mirrors
  workspace-rename-modal.js pattern (imperative open + listeners + close
  + esc/click-outside/enter). Two key differences: onSuccess callback
  instead of window.location.reload (allows targeted re-render of
  pending-invites section), and mapError callback so the parent's
  mapMutationError is the single regex-to-i18n source of truth (instead
  of duplicating in the modal).
- workspace-members.js: header invite button (can_admin gated), per-row
  affordances (role select + remove on direct members, cancel on invited
  rows, none on via_org rows), exported mapMutationError mapper,
  re-render on both success AND error for role-select to resync state
  when the server rejects.
- 4 api.js helpers (inviteWorkspaceMember, cancelWorkspaceInvite,
  updateWorkspaceMemberRole, removeWorkspaceMember).
- 24 i18n keys under members.modal.*, members.button.*,
  members.confirm.*, members.error.*, members.success.*
- CSS for .member-actions family (action buttons + role select + hover
  states).

UX decisions:
- Direct-member rows: role <select> replaces role text in same column;
  remove button right of detail
- via_org rows: no actions cell (server would 403; UI respects boundary)
- Invited rows: cancel button only (handoff rule was over-broad -
  cancel-invite IS a valid mutation on invited rows, refined during 2B
  survey)
- Role select fires on change, no Save button (matches teams.js pattern;
  mitigations for accidental clicks noted in handoff if reports come in)
- Mutations re-fetch + re-render rather than optimistic updates -
  simpler, no state-drift bugs, endpoints respond fast
- /invites endpoint skipped entirely when !can_admin (saves a request;
  server still enforces)

Verification: 21/21 Playwright assertions PASS across 6 cases (invite
happy path, invite collision, role change, remove member, last-admin
block, cancel invite). Test infrastructure stashed at
~/Documents/screentinker-2b-playwright-2026-05.py.

Closes P2 (user-management feature). Slice 1+3 backend landed c4fbd2b,
2A read-only view landed 8db171d, 2C accept-invite handler landed
399af54, 2B mutation UI landed here.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 14:45:34 -05:00
..
assets Landing: replace iframe mock with dashboard screenshot 2026-04-21 19:47:13 -05:00
compare chore(discord): update Discord invite link 2026-05-14 12:26:20 -05:00
css feat(workspaces): mutation UI for members (slice 2B) 2026-05-17 14:45:34 -05:00
guides chore(discord): update Discord invite link 2026-05-14 12:26:20 -05:00
js feat(workspaces): mutation UI for members (slice 2B) 2026-05-17 14:45:34 -05:00
legal docs(privacy): disclose error and diagnostic telemetry from players 2026-05-15 15:31:21 -05:00
index.html feat(teams): temporarily disable Teams API while feature is redesigned 2026-05-12 13:30:55 -05:00
landing.html fix(landing): replace broken Custom pricing card with enterprise contact form 2026-05-14 13:52:24 -05:00
manifest.json Initial open source release 2026-04-08 12:14:53 -05:00
robots.txt SEO: add meta tags, sitemap, robots.txt, comparison pages, guides, internal linking 2026-04-28 20:54:32 -05:00
sitemap.xml SEO: add meta tags, sitemap, robots.txt, comparison pages, guides, internal linking 2026-04-28 20:54:32 -05:00
sw-admin.js fix(frontend): workspace switcher (Phase 3 MVP) + SW network-first migration + platform_admin accessible_workspaces expansion + static render CSS cleanup. The switcher adds a sidebar dropdown for users who are members of multiple workspaces, renders as static text with a 'Workspace' label for single-workspace users, and muted 'No workspace' for zero. Uses existing /api/auth/me's accessible_workspaces and POST /api/auth/switch-workspace endpoints. Platform admin / superadmin users now see all workspaces in accessible_workspaces (closing the known regression from 88d91b1) via a LEFT JOIN that preserves workspace_role semantics (null = acting-as, role string = direct member). No cap on the list - deliberate for now, revisit at 50+ workspaces. SW fix bumps rd-admin-v1 -> rd-admin-v2 and switches fetch strategy from cache-first to network-first so the server's existing Cache-Control: no-cache + ETag headers actually get respected; preserves offline fallback. Static render CSS drops the bordered-box chrome that was making single-workspace users think the static text was clickable. Includes test fixture user switcher-test@local.test (credentials in fixture SQL header). Surfaced by semetra22 / Discord report about 'screens jumbled up' post-migration; root cause was the missing workspace switcher UI making devices in non-active workspaces appear missing. 2026-05-12 10:55:09 -05:00