Add safe user preview for invites
Magent CI/CD / verify (push) Successful in 10m52s
Magent CI/CD / deploy-prod (push) Skipped
Magent CI/CD / deploy-beta (push) Successful in 17s

This commit is contained in:
2026-09-01 16:51:32 +12:00
parent 976d24217b
commit b3c41f6dea
2 changed files with 34 additions and 4 deletions
+5
View File
@@ -1174,6 +1174,11 @@ export default function AdminInviteManagementPage() {
title="Invites" title="Invites"
subtitle="Create access links, apply account profiles, deliver invitations, and see what needs attention." subtitle="Create access links, apply account profiles, deliver invitations, and see what needs attention."
rail={inviteManagementRail} rail={inviteManagementRail}
actions={
<button type="button" className="ghost-button" onClick={() => router.push('/profile/invites?view=user')}>
View as user
</button>
}
> >
<section className="admin-section"> <section className="admin-section">
{error && <div className="error-banner">{error}</div>} {error && <div className="error-banner">{error}</div>}
+29 -4
View File
@@ -1,7 +1,7 @@
'use client' 'use client'
import { useEffect, useMemo, useState } from 'react'
import { useRouter } from 'next/navigation' import { useRouter } from 'next/navigation'
import { useEffect, useMemo, useState } from 'react'
import { authFetch, clearToken, getApiBase, getToken } from '../../lib/auth' import { authFetch, clearToken, getApiBase, getToken } from '../../lib/auth'
type ProfileInfo = { type ProfileInfo = {
@@ -97,6 +97,7 @@ export default function ProfileInvitesPage() {
const [inviteManagedByMaster, setInviteManagedByMaster] = useState(false) const [inviteManagedByMaster, setInviteManagedByMaster] = useState(false)
const [masterInviteTemplate, setMasterInviteTemplate] = useState<OwnedInvitesResponse['master_invite']>(null) const [masterInviteTemplate, setMasterInviteTemplate] = useState<OwnedInvitesResponse['master_invite']>(null)
const [loading, setLoading] = useState(true) const [loading, setLoading] = useState(true)
const [viewAsUser, setViewAsUser] = useState(false)
const signupBaseUrl = useMemo(() => { const signupBaseUrl = useMemo(() => {
if (typeof window === 'undefined') return '/signup' if (typeof window === 'undefined') return '/signup'
@@ -135,6 +136,7 @@ export default function ProfileInvitesPage() {
} }
useEffect(() => { useEffect(() => {
setViewAsUser(new URLSearchParams(window.location.search).get('view') === 'user')
if (!getToken()) { if (!getToken()) {
router.push('/login') router.push('/login')
return return
@@ -307,6 +309,11 @@ export default function ProfileInvitesPage() {
const canManageInvites = profile?.role === 'admin' || inviteAccessEnabled const canManageInvites = profile?.role === 'admin' || inviteAccessEnabled
const enterUserView = () => {
setViewAsUser(true)
router.replace('/profile/invites?view=user')
}
if (loading) { if (loading) {
return <main className="card">Loading invite tools...</main> return <main className="card">Loading invite tools...</main>
} }
@@ -315,17 +322,35 @@ export default function ProfileInvitesPage() {
<main className="card"> <main className="card">
<div className="user-directory-panel-header profile-page-header"> <div className="user-directory-panel-header profile-page-header">
<div> <div>
<h1>My invites</h1> <h1>{viewAsUser ? 'Invites' : 'My invites'}</h1>
<p className="lede">Create invite links, email them directly, and track who you have invited.</p> <p className="lede">
{viewAsUser
? 'Preview the invite workspace exactly as a permitted non-admin user sees it.'
: 'Create invite links, email them directly, and track who you have invited.'}
</p>
</div> </div>
<div className="admin-inline-actions"> <div className="admin-inline-actions">
{profile?.role === 'admin' && !viewAsUser ? (
<button type="button" className="ghost-button" onClick={enterUserView}>
View as user
</button>
) : null}
{profile?.role === 'admin' && viewAsUser ? (
<button type="button" onClick={() => router.push('/admin/invites')}>
Return to admin view
</button>
) : null}
<button type="button" className="ghost-button" onClick={() => router.push('/profile')}> <button type="button" className="ghost-button" onClick={() => router.push('/profile')}>
Back to profile Back to profile
</button> </button>
</div> </div>
</div> </div>
{profile ? ( {profile && viewAsUser ? (
<div className="status-banner">
<strong>User view preview.</strong> The non-admin presentation is active. Your admin permissions and invite data remain unchanged.
</div>
) : profile ? (
<div className="status-banner"> <div className="status-banner">
Signed in as <strong>{profile.username}</strong> ({profile.role}). Signed in as <strong>{profile.username}</strong> ({profile.role}).
</div> </div>