'use client' import { useEffect, useRef, useState } from 'react' import { authFetch, getApiBase } from '../../lib/auth' import type { Row } from './page' type Preview = { revision: string; server_id: string; row: Row } export default function ResolveIdentityLink({ row, accounts, onClose, onSaved }: { row: Row; accounts: { id: string; name: string }[]; onClose: () => void; onSaved: () => void }) { const dialog = useRef(null) const controller = useRef(null) const [chosen, setChosen] = useState('') const [preview, setPreview] = useState(null) const [busy, setBusy] = useState(false) const [saving, setSaving] = useState(false) const [error, setError] = useState('') useEffect(() => { const previous = document.activeElement as HTMLElement | null const overflow = document.body.style.overflow document.body.style.overflow = 'hidden' dialog.current?.showModal() return () => { controller.current?.abort() document.body.style.overflow = overflow previous?.focus() } }, []) const submit = async (confirm: boolean) => { if (!chosen || busy || saving || (confirm && !preview?.row.can_confirm)) return const abort = new AbortController() controller.current = abort setError('') if (confirm) setSaving(true) else { setBusy(true); setPreview(null) } try { const response = await authFetch(`${getApiBase()}/admin/identities/resolve/${confirm ? 'confirm' : 'check'}`, { method: 'POST', signal: abort.signal, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ user_id: row.user.id, jellyfin_user_id: chosen, ...(confirm ? { revision: preview?.revision } : {}) }), }) const data = await response.json().catch(() => ({})) if (!response.ok) throw new Error(response.status === 401 ? 'Your session has ended. Sign in again.' : typeof data.detail === 'string' ? data.detail : 'Could not check the account links. Try again.') if (!abort.signal.aborted) { if (confirm) onSaved() else setPreview(data) } } catch (err) { if (!abort.signal.aborted) { setError(err instanceof Error ? err.message : 'Could not resolve the link.') setPreview(null) } } finally { if (!abort.signal.aborted) { setBusy(false); setSaving(false) } } } return { event.preventDefault(); if (!saving) onClose() }}>

Resolve missing link

Link {row.user.username} (Magent {row.user.id}) to their Jellyfin account. Review the IDs below to confirm this is the same person.

{error &&

{error}

} {busy &&

Checking live IDs and whether another Magent account already owns this identity.

} {preview &&

{preview.row.can_confirm ? 'Ready to confirm' : 'This link needs attention'}

Jellyfin server {preview.server_id ?? 'Unavailable'}

Jellyfin
{preview.row.jellyfin?.name ?? 'Account not found'}{preview.row.candidate_jellyfin_id}
Seerr
{preview.row.seerr.length ? preview.row.seerr.map((account) => `${account.name} (ID ${account.id})`).join(', ') : 'No matching Jellyfin ID. Check this user’s Jellyfin account link in Seerr, then check again.'}
Jellystat
{preview.row.jellystat.id ?? 'Not verified'}{preview.row.jellystat.state === 'matched' ? 'Same Jellyfin ID verified' : preview.row.jellystat.state === 'missing' ? 'This ID is missing from Jellystat. Check its Jellyfin sync, then check again.' : 'Could not verify this ID. Check the Jellystat connection and try again.'}
{preview.row.issues.length > 0 &&
    {preview.row.issues.map((issue) =>
  • {issue}
  • )}
} {preview.row.state === 'unavailable' &&

A required service is unavailable. Restore its connection and check again.

}

Saving stores the verified Jellyfin and Seerr links in Magent, with your administrator name and confirmation time. All platform IDs and duplicate ownership are checked again before saving.

}
}