Admin Mode
What an admin user can do, and how the UI gates it.
Who is an admin
Any user whose profiles.role = 'admin'. That is everyone on the FPM (Flooring Pros Marketing) team:
| Person | Title | What they do in the portal |
|---|---|---|
| Dan | CMO / Co-founder | Full admin access. Oversees every portal, owns strategy content, owns the roadmap. |
| Chesney | Account Manager | Adds meeting notes, manages deliverables, responds to client requests, sends weekly recap. |
| Kenan | Head of Fulfillment | Manages deliverable pipeline, marks items ready for review, closes requests. |
| Client Revenue Strategist (CRS) | Currently hiring | Adds game plan updates, scorecard scores, wins, meeting summaries. |
| Aniello | Engineering / Data | Owns the codebase, deployments, integrations and database. Repo lives in his GitHub. |
The admin role is set manually in Supabase by Aniello or Dan. There is no self-serve admin promotion.
The admin overlay
Admin and client see the same per-client URL. The difference is the overlay: when you are logged in as admin, the page shows extra UI on top of the client view:
- The dark
#202020sidebar stays visible on the per-client portal route. Each client row has a pencil icon (visible on hover) for quick edit. - Pencil icons on every Client Portal sub-tab card and on every Client Info card.
- The Connect / Disconnect popup for integrations.
- Both hubs visible: Client Hub, Reporting, and Client Info.
- An admin-only toolbar at the top of the main column with date range picker, Visit Site link, IntegrationPanel, and PDF download.
- An Edit info button on the Client Info hub header that opens the modal at the top of all sections.
Capabilities
| Capability | Where |
|---|---|
| View list of all clients | /dashboard or the dark sidebar (always visible) |
| Switch between clients without leaving the portal view | Sidebar — click any client row |
| Edit any client (one click) | Pencil icon on hover in the sidebar row, or /dashboard/clients/[id]/edit |
| Create a new client | /dashboard/clients/new |
| Open any client portal (admin overlay) | /dashboard/clients/[id] |
| Edit client branding (logo upload, tier badge, booking URL) | /dashboard/clients/[id]/edit — Branding & Identity section |
| Edit client info (29 fields — company, contact, address, domain, brand, social, billing, notes) | Client Info hub → Edit info button or per-card pencil |
| Connect / reconnect integrations | Global Integrations panel + per-client Connect popups |
| Edit Client Portal sections (Snapshot, Scorecard, Game Plan, Deliverables, KPI Reporting, Content & Website, Documents) | Inline pencil button on each portal sub-tab |
| Add meeting notes / weekly recap | Pending UI — for now, edit Game Plan and KPI Reporting sections |
| Invite a client user (sends magic link) | Sidebar action on the client page |
| Download monthly PDF | PDF Download button in the admin toolbar |
| Manage deliverable pipeline | Deliverables sub-tab → editor modal |
| Respond to / close requests | Currently surfaced in Slack — UI inbox is on the roadmap |
Default route after login
Admin lands on /dashboard which renders the client list. Clicking a client navigates to /dashboard/clients/[id].
Inside the client page, the default tab is Client Portal (same as the client view). This is intentional — admins should see exactly what the client sees by default, then switch to internal tabs as needed.
How the UI knows you are admin
userRole is passed down as a prop from the server component /dashboard/clients/[id]/page.tsx:
userRole∈'admin' | 'client'.- Components gate edit buttons with
{isAdmin && <button onClick={onEdit}>...</button>}. - Tabs that are admin-only are conditionally rendered in the
TABSarray.
PUT /api/portal/[clientId], the integration endpoints, the invite endpoint) re-checks the role server-side. If you add a new mutating endpoint, you must add the role check — do not rely on the UI alone.Server-side role check pattern
Every admin-only route looks like this:
const supabase = await createClient()
const { data: { user } } = await supabase.auth.getUser()
if (!user) return new Response('Unauthorized', { status: 401 })
const { data: profile } = await supabase
.from('profiles').select('role').eq('id', user.id).single()
if (profile?.role !== 'admin') return new Response('Forbidden', { status: 403 })
// proceed with mutationViewing as a client
There is no formal impersonation mode yet. Admins see the same Client Portal a client would see; the only difference is the edit pencils and the extra top-level tabs. To preview exactly what a client sees, log out and log in as the client's test account, or hide the pencils with browser dev tools.
A proper "view as client" toggle is on the roadmap.