Unify user management and add reviewed identity repairs
This commit is contained in:
@@ -6,6 +6,7 @@ import Link from 'next/link'
|
||||
import { authFetch, clearToken, getApiBase, getToken } from '../lib/auth'
|
||||
import AdminShell from '../ui/AdminShell'
|
||||
import './users.css'
|
||||
import IdentityReviewPanel from '../admin/identities/IdentityReviewPanel'
|
||||
|
||||
type AdminUser = {
|
||||
id: number
|
||||
@@ -82,6 +83,17 @@ const normalizeStats = (stats: any): UserStats => ({
|
||||
|
||||
export default function UsersPage() {
|
||||
const router = useRouter()
|
||||
const [view, setView] = useState('directory')
|
||||
useEffect(() => {
|
||||
const update = () => setView(new URLSearchParams(window.location.search).get('view') === 'identities' ? 'identities' : 'directory')
|
||||
update()
|
||||
window.addEventListener('popstate', update)
|
||||
return () => window.removeEventListener('popstate', update)
|
||||
}, [])
|
||||
const changeView = (next: string) => {
|
||||
setView(next)
|
||||
window.history.pushState(null, '', next === 'identities' ? '/users?view=identities' : '/users')
|
||||
}
|
||||
const [controlsOpen, setControlsOpen] = useState(false)
|
||||
const controlsDialog = useRef<HTMLDialogElement>(null)
|
||||
const controlsTrigger = useRef<HTMLButtonElement>(null)
|
||||
@@ -350,8 +362,8 @@ export default function UsersPage() {
|
||||
|
||||
return (
|
||||
<AdminShell
|
||||
title="Users"
|
||||
subtitle="Directory, access status, and request activity."
|
||||
title="User management"
|
||||
subtitle="Accounts, access, request activity and verified service links."
|
||||
actions={<button ref={controlsTrigger} type="button" className="ghost-button" aria-haspopup="dialog" aria-expanded={controlsOpen} aria-controls="user-management-dialog" onClick={() => setControlsOpen(true)}>Manage users</button>}
|
||||
>
|
||||
<dialog id="user-management-dialog" ref={controlsDialog} className="user-management-dialog" aria-labelledby="user-management-title" onCancel={() => setControlsOpen(false)} onClose={() => setControlsOpen(false)}>
|
||||
@@ -362,7 +374,7 @@ export default function UsersPage() {
|
||||
{inviteStatus && <p className="status-banner" role="status">{inviteStatus}</p>}
|
||||
<div className="user-management-grid">
|
||||
<section className="user-management-panel"><h3>Directory actions</h3><p>Review linked accounts, manage invitations or refresh the list.</p>
|
||||
<div className="user-management-action"><Link className="ghost-button" href="/admin/identities" aria-describedby="identity-help">Review account links ↗</Link><p id="identity-help">Compare and confirm each user's Magent, Jellyfin, Jellystat and Seerr IDs.</p></div>
|
||||
<div className="user-management-action"><Link className="ghost-button" href="/users?view=identities" aria-describedby="identity-help">Review account links ↗</Link><p id="identity-help">Compare and confirm each user's Magent, Jellyfin, Jellystat and Seerr IDs.</p></div>
|
||||
<div className="user-management-action"><Link className="ghost-button" href="/admin/invites" aria-describedby="invitation-help">Manage invitations ↗</Link><p id="invitation-help">Create invitations, review issued links and set invitation defaults.</p></div>
|
||||
<div className="user-management-action"><button type="button" className="ghost-button" onClick={() => void loadUsers()} disabled={controlsBusy} aria-describedby="reload-help">{refreshing ? 'Refreshing…' : 'Refresh user list'}</button><p id="reload-help">Reload account status and request totals from Magent. Your search stays in place.</p></div>
|
||||
</section>
|
||||
@@ -376,7 +388,11 @@ export default function UsersPage() {
|
||||
<details className="user-management-summary"><summary>Directory totals</summary>{usersRail}</details>
|
||||
</div>
|
||||
</dialog>
|
||||
<section className="admin-section users-directory-centered">
|
||||
<nav className="identity-selection" aria-label="User management sections">
|
||||
<button type="button" className="ghost-button" aria-pressed={view === 'directory'} onClick={() => changeView('directory')}>User directory</button>
|
||||
<button type="button" className="ghost-button" aria-pressed={view === 'identities'} onClick={() => changeView('identities')}>Account links & repairs</button>
|
||||
</nav>
|
||||
{view === 'identities' ? <IdentityReviewPanel /> : <section className="admin-section users-directory-centered">
|
||||
{!controlsOpen && error && <p className="error-banner" role="alert">{error}</p>}
|
||||
{!controlsOpen && jellyseerrSyncStatus && <p className="status-banner" role="status">{jellyseerrSyncStatus}</p>}
|
||||
{!controlsOpen && inviteStatus && <p className="status-banner" role="status">{inviteStatus}</p>}
|
||||
@@ -472,7 +488,7 @@ export default function UsersPage() {
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
</section>}
|
||||
</AdminShell>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user