// Recognition plugin — the operator's view of the trust (recognition) graph.
//
// TRQP recognition is a per-DID query against the upstream trust registry (not
// a listable set), so this surfaces the configured-registry status plus a
// lookup tool: enter an issuer / community DID and see whether this community
// recognises it. That recognition verdict is what decides whether a third-party
// invitation issuer is trusted (M2).
import { useState } from "react";
import { useMutation, useQuery } from "@tanstack/react-query";
import { Check, Network, X } from "lucide-react";
import {
checkRecognition,
fetchDiagnostics,
type RecognitionCheck,
} from "@/lib/api";
import { useToast } from "@/lib/toast";
export function Recognition() {
const toast = useToast();
const [did, setDid] = useState("");
const diagnostics = useQuery({
queryKey: ["diagnostics"],
queryFn: fetchDiagnostics,
});
const lookup = useMutation<RecognitionCheck, Error, string>({
mutationFn: (d: string) => checkRecognition(d),
onError: (e) => toast.pushFromError(e),
});
const result = lookup.data;
return (
<div className="page">
<header className="page-header">
<h2>
<Network size={20} strokeWidth={1.75} /> Recognition
</h2>
<p className="muted">
The trust (recognition) graph decides which foreign issuers and
communities this community trusts — including which third parties may
issue invitations that auto-admit. Recognition is queried per-DID
against the trust registry.
</p>
</header>
<section className="card">
<h3>Trust registry</h3>
{diagnostics.isPending && <p className="muted">Loading…</p>}
{diagnostics.data && (
<dl>
<dt>Status</dt>
<dd>
<code>{diagnostics.data.registry_status}</code>
</dd>
</dl>
)}
</section>
<section className="card">
<h3>Check recognition</h3>
<form
onSubmit={(e) => {
e.preventDefault();
if (did.trim()) lookup.mutate(did.trim());
}}
>
<label className="field">
<span className="field-label">Issuer / community DID</span>
<input
type="text"
value={did}
onChange={(e) => setDid(e.target.value)}
placeholder="did:webvh:… or did:key:…"
autoComplete="off"
spellCheck={false}
/>
</label>
<button
type="submit"
className="btn primary"
disabled={!did.trim() || lookup.isPending}
>
{lookup.isPending ? "Checking…" : "Check"}
</button>
</form>
{result && (
<p style={{ marginTop: 12 }}>
{result.recognised ? (
<span>
<Check
size={16}
strokeWidth={1.75}
className="status-icon ok"
aria-label="Recognised"
/>{" "}
<strong>Recognised</strong> — <code>{result.did}</code> is
trusted by this community.
</span>
) : (
<span>
<X
size={16}
strokeWidth={1.75}
aria-label="Not recognised"
/>{" "}
<strong>Not recognised</strong> — <code>{result.did}</code> is
not in the recognition graph
{result.registryConfigured ? "" : " (no trust registry configured)"}.
</span>
)}
{result.error && (
<span className="muted">
{" "}
(registry error: {result.error})
</span>
)}
</p>
)}
</section>
</div>
);
}