Add reviewed resolution for missing user identity links
Magent CI/CD / verify (push) Successful in 10m59s
Magent CI/CD / deploy-prod (push) Skipped
Magent CI/CD / deploy-beta (push) Successful in 1m29s

This commit is contained in:
2026-09-10 15:02:04 +12:00
parent b310e86f80
commit 77f2c1b42a
7 changed files with 231 additions and 6 deletions
+9 -2
View File
@@ -5,9 +5,10 @@ import { useRouter } from 'next/navigation'
import { authFetch, getApiBase } from '../../lib/auth'
import AdminShell from '../../ui/AdminShell'
import './identities.css'
import ResolveIdentityLink from './ResolveIdentityLink'
type Identity = { id: string; name: string }
type Row = {
export type Row = {
user: { id: number; username: string; role: string; auth_provider: string; jellyseerr_user_id: number | null }
jellyfin: Identity | null
candidate_jellyfin_id: string | null
@@ -24,6 +25,7 @@ type Report = {
revision: string; checked_at: string; server_id: string | null
services: Record<string, string>
counts: Record<string, number>
jellyfin_users: Identity[]
rows: Row[]
upstream: { platform: string; id: string; name: string; jellyfin_id: string | null; detail: string }[]
}
@@ -43,6 +45,7 @@ export default function IdentityReviewPage() {
const [query, setQuery] = useState('')
const [filter, setFilter] = useState('all')
const [selected, setSelected] = useState<number[]>([])
const [resolving, setResolving] = useState<Row | null>(null)
const [reviewing, setReviewing] = useState(false)
const controller = useRef<AbortController | null>(null)
const reviewPanel = useRef<HTMLElement | null>(null)
@@ -148,7 +151,7 @@ export default function IdentityReviewPage() {
<div><dt>Jellystat user ID</dt><dd><code>{row.jellystat.id ?? 'Not verified'}</code><span>{statsLabels[row.jellystat.state] ?? row.jellystat.state}</span></dd></div>
</dl>
{row.issues.length > 0 && <ul className="identity-issues">{row.issues.map((issue) => <li key={issue}>{issue}</li>)}</ul>}
{row.state === 'unlinked' && <p className="identity-meta">A matching account is missing in one or more services. This account cannot be confirmed yet.</p>}
{row.state === 'unlinked' && <div className="identity-resolution-entry"><p className="identity-meta">Choose the correct Jellyfin account and check its Seerr and Jellystat links before saving.</p><button type="button" className="ghost-button" disabled={saving || report.services.jellyfin !== 'available'} onClick={() => setResolving(row)}>Resolve missing link</button></div>}
{row.state === 'unavailable' && <p className="identity-meta">A required service could not be checked. Check its connection and run this again.</p>}
{row.confirmed_at && <p className="identity-meta">Last confirmed {new Date(row.confirmed_at).toLocaleString()}</p>}
</article>)}
@@ -156,6 +159,10 @@ export default function IdentityReviewPage() {
{report.upstream.length > 0 && <details className="identity-upstream"><summary>{report.upstream.length} upstream accounts need review</summary><ul>{report.upstream.map((entry) => <li key={`${entry.platform}-${entry.id}`}><strong>{entry.platform}: {entry.name}</strong> · ID <code>{entry.id}</code>{entry.jellyfin_id && <span> · Jellyfin <code>{entry.jellyfin_id}</code></span>}<p>{entry.detail}</p></li>)}</ul></details>}
</>}
</>}
{resolving && report && <ResolveIdentityLink row={resolving} accounts={report.jellyfin_users} onClose={() => setResolving(null)} onSaved={() => {
setResolving(null); setReport(null); setSelected([]); setReviewing(false)
setNotice('Account links confirmed and saved. Run another check to see the updated mappings.')
}} />}
</div>
</AdminShell>
}