Three frames at 375 × 667 (iPhone SE viewport). Mobile is a separate render path —
UsersListMobile.jsx
and UserEditMobile.jsx
— switched at the route level by viewport width. Desktop components are untouched.
Cards replace the 6-column table. Each row collapses to: name + email (primary), role + last-login (secondary), single status badge top-right. Tap-to-edit; deactivated rows have inline "Reactivate".
Changing the role here doesn't change grants. Use Reset to apply the template.
The 9-group grid-cols-2 grant matrix collapses to accordions. Each section shows a "n of m granted · k override" summary so the admin sees deltas without expanding. Toggle switches replace checkboxes (bigger tap target). Sticky Save pinned to the bottom.
Create a new user account. They'll sign in with their Microsoft work account.
Mobile uses a bottom sheet (shadcn Sheet) instead of a centered Dialog — lower thumb-reach Create button, drag-to-dismiss handle, full-width inputs. The chip strip wraps naturally; QB list ID field moves into "Edit" later (rarely set at create time).
Mobile is a separate render path, not a responsive shrink of desktop. Two new files —
UsersListMobile.jsx and
UserEditMobile.jsx — render the views above. Existing
UsersList.jsx + UserEdit.jsx
stay untouched; only the route element wraps them in a viewport switch.
// main/src/pages/admin/index.jsx (NEW — thin switcher)
import useIsMobile from '@/core/useIsMobile' // window.innerWidth < 1024
import UsersList from './UsersList'
import UsersListMobile from './UsersListMobile'
export default function UsersListSwitcher() {
return useIsMobile() ? <UsersListMobile /> : <UsersList />
}
The useIsMobile hook reads
window.matchMedia('(max-width: 1023px)') and
listens for changes — so a desktop user resizing their window past the breakpoint sees the appropriate view.
Since desktop CRSApp is gated behind hidden lg:flex sidebar
anyway, "mobile" here means anything < lg (1024px), matching the existing breakpoint convention.
Shared, unchanged:
api.js — same listUsers / createUser / updateUser / resetGrants callsroleTemplates.js — same default-access mirrorApp.jsx — point to the switcher instead of the desktop componentThree new components:
UsersListMobile.jsx — card-per-user list (Frame 1)UserEditMobile.jsx — accordion sections + sticky save (Frame 2)UserAddSheet.jsx — bottom-sheet variant of Add User (Frame 3) — uses shadcn Sheet from @/components/ui/sheet (already installed)The pattern is reusable for every other admin / settings / form-heavy page that gets the same mobile treatment later. The switcher is a dozen lines; the heavy lift is each page's mobile component.