import { useState } from 'react' import { CheckCircle2, Download, ShieldAlert, ShieldCheck } from 'lucide-react' import { useAdverseImpact, useApproveCurationProposal, useCurationProposals, useVerifyCurationProposal, } from '../lib/queries' import { api } from '../lib/api' import { Badge, Panel } from '../components/ui' import { flagRatio } from '../lib/format' import { useAuth } from '../lib/useAuth' import type { AnalysisSection, CurationProposalView, ImpactRow } from '../lib/types' export function Compliance() { const report = useAdverseImpact() const { principal } = useAuth() const isOperator = principal?.role === 'operator' async function downloadExport() { const text = await api.adverseImpactExport() const url = URL.createObjectURL(new Blob([text], { type: 'text/plain' })) const a = document.createElement('a') a.href = url a.download = 'adverse_impact_report.txt' a.click() URL.revokeObjectURL(url) } return (
Adverse Impact and AEDT Compliance

Four fifths analysis over recorded decisions. The platform supplies this data for an independent audit run by the deploying employer.

{report.isLoading &&

Loading the report.

} {report.isError && (

The adverse impact report is unavailable. It is read only by the compliance role and requires the compliance database connection. Detail: {(report.error as Error).message}

)} {report.data && ( <>
)} {/* Teaching curation is a PLATFORM surface: it writes the global skill ontology every tenant is scored against, so amendment 6 moved it behind the operator plane and its endpoints now answer 404 to everyone else. Rendering it for a tenant is what the reported defect looked like, a panel full of controls that refuse. The rule is that a platform surface is ABSENT for a tenant, not inactive, so the whole panel is omitted rather than disabled. The 404 at the API is the enforcement; this only stops the interface offering a journey that ends nowhere. */} {isOperator && }
) } // CurationProposals surfaces the WS8 teaching curation proposals and the two step human in the loop gate. // Approve is the first step and stages an inert ontology edge that changes no score. Verify is a second, // distinct step that activates the edge for future scores. Nothing is ever auto applied, and neither step // touches the engine math or any already served score. function CurationProposals() { const proposals = useCurationProposals() const approve = useApproveCurationProposal() const verify = useVerifyCurationProposal() // verified tracks the proposal ids the user has just verified this session, so the row reads as activated // immediately rather than only after the list refreshes. approvedThisSession lets a freshly approved // proposal show the Verify action before the pending list drops it. const [approvedThisSession, setApprovedThisSession] = useState([]) const [verified, setVerified] = useState>({}) async function onApprove(p: CurationProposalView) { const res = await approve.mutateAsync(p.id) setApprovedThisSession((prev) => [...prev.filter((x) => x.id !== p.id), { ...p, status: res.status }]) } async function onVerify(id: string) { await verify.mutateAsync(id) setVerified((prev) => ({ ...prev, [id]: true })) } const pending = proposals.data ?? [] // Show the still pending proposals plus any approved this session that are not yet verified, so both steps // are visible in one list without a page reload. const approvedIds = new Set(approvedThisSession.map((p) => p.id)) const rows = [ ...pending.filter((p) => !approvedIds.has(p.id)), ...approvedThisSession, ] return ( {proposals.isError && (

The curation surface is unavailable. It is gated to the compliance or operator role and requires the migrator database connection. Detail: {(proposals.error as Error).message}

)} {!proposals.isError && rows.length === 0 && (

No pending curation proposals.

)} {rows.length > 0 && ( {rows.map((p) => { const isApproved = p.status === 'approved' || approvedIds.has(p.id) const isVerified = verified[p.id] return ( ) })}
Kind Substitute skill For required skill Strength Evidence Action
{p.kind.replace(/_/g, ' ')} {p.from_skill_id.slice(0, 8)} {p.to_skill_id.slice(0, 8)} {p.proposed_strength || 'n a'} {p.evidence_successes} of {p.evidence_observations} {isVerified ? ( Verified, live ) : isApproved ? ( ) : ( )}
)}
) } function Meta({ k, v, wide }: { k: string; v: string; wide?: boolean }) { return (
{k}
{v}
) } function Section({ section }: { section: AnalysisSection }) { return (
) } function Cut({ title, rows }: { title: string; rows: ImpactRow[] }) { return (
{title}
{rows.length === 0 ? (

No data.

) : ( {rows.map((r) => ( ))}
Group Selected Rate Impact ratio Status
{r.group} {r.selected} of {r.considered} {r.rate} {r.impact_ratio} {r.flagged ? ( Below 0.80 ) : ( ok )}
)}
) }