From 050bbc81dd13fc8883c2db29c40323c2d9f52ffe Mon Sep 17 00:00:00 2001 From: kraysent Date: Tue, 21 Apr 2026 20:23:44 +0100 Subject: [PATCH 1/4] make crossmatch results page more useful --- src/pages/CrossmatchResults.tsx | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/pages/CrossmatchResults.tsx b/src/pages/CrossmatchResults.tsx index 456fd01..069335f 100644 --- a/src/pages/CrossmatchResults.tsx +++ b/src/pages/CrossmatchResults.tsx @@ -156,10 +156,6 @@ function CrossmatchResults({ ); } - function getStatusLabel(status: RecordCrossmatchStatus): string { - return getResource(`crossmatch.status.${status}`).Title; - } - function getTriageStatusLabel(triageStatus: RecordTriageStatus): string { return getResource(`crossmatch.triage.${triageStatus}`).Title; } @@ -174,7 +170,6 @@ function CrossmatchResults({ return NULL; }, }, - { name: "Status" }, { name: "Manual check status" }, { name: "Candidates", @@ -190,7 +185,6 @@ function CrossmatchResults({ const tableData: Record[] = data?.records.map((record: RecordCrossmatch, index: number) => ({ "Record name": index, - Status: getStatusLabel(record.status), "Manual check status": getTriageStatusLabel(record.triage_status), Candidates: index, })) || []; From 8ce0e03a8d22a73cb121cb26487f79a2ceb24ba7 Mon Sep 17 00:00:00 2001 From: kraysent Date: Tue, 21 Apr 2026 20:26:34 +0100 Subject: [PATCH 2/4] move triage status as a badge --- src/pages/CrossmatchResults.tsx | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/pages/CrossmatchResults.tsx b/src/pages/CrossmatchResults.tsx index 069335f..fd4a6e5 100644 --- a/src/pages/CrossmatchResults.tsx +++ b/src/pages/CrossmatchResults.tsx @@ -127,12 +127,22 @@ function CrossmatchResults({ data, loading, }: CrossmatchResultsProps): ReactElement { + function getTriageStatusLabel(triageStatus: RecordTriageStatus): string { + return getResource(`crossmatch.triage.${triageStatus}`).Title; + } + function getRecordName(record: RecordCrossmatch): ReactElement { const displayName = record.catalogs.designation?.name || record.record_id; + const triageStatusLabel = getTriageStatusLabel(record.triage_status); + const triageBadgeType = record.triage_status === "resolved" ? "success" : "warning"; + return ( - - {displayName} - +
+ + {displayName} + + {triageStatusLabel} +
); } @@ -156,10 +166,6 @@ function CrossmatchResults({ ); } - function getTriageStatusLabel(triageStatus: RecordTriageStatus): string { - return getResource(`crossmatch.triage.${triageStatus}`).Title; - } - const columns: Column[] = [ { name: "Record name", @@ -170,7 +176,6 @@ function CrossmatchResults({ return NULL; }, }, - { name: "Manual check status" }, { name: "Candidates", renderCell: (recordIndex: CellPrimitive) => { @@ -183,9 +188,8 @@ function CrossmatchResults({ ]; const tableData: Record[] = - data?.records.map((record: RecordCrossmatch, index: number) => ({ + data?.records.map((_: RecordCrossmatch, index: number) => ({ "Record name": index, - "Manual check status": getTriageStatusLabel(record.triage_status), Candidates: index, })) || []; From 4462640aecbaba967246d74a2ab00b130cde5176 Mon Sep 17 00:00:00 2001 From: kraysent Date: Tue, 21 Apr 2026 20:32:51 +0100 Subject: [PATCH 3/4] remove status filter --- src/pages/CrossmatchResults.tsx | 45 +++++---------------------------- 1 file changed, 6 insertions(+), 39 deletions(-) diff --git a/src/pages/CrossmatchResults.tsx b/src/pages/CrossmatchResults.tsx index fd4a6e5..9d879b7 100644 --- a/src/pages/CrossmatchResults.tsx +++ b/src/pages/CrossmatchResults.tsx @@ -12,7 +12,6 @@ import { getCrossmatchRecords } from "../clients/admin/sdk.gen"; import type { GetRecordsCrossmatchResponse, RecordCrossmatch, - RecordCrossmatchStatus, RecordTriageStatus, ValidationError, } from "../clients/admin/types.gen"; @@ -27,12 +26,10 @@ import { adminClient } from "../clients/config"; interface CrossmatchFiltersProps { tableName: string | null; - status: RecordCrossmatchStatus | null; triageStatus: string | null; pageSize: number; onApplyFilters: ( tableName: string, - status: string, triageStatus: string, pageSize: number, ) => void; @@ -40,12 +37,10 @@ interface CrossmatchFiltersProps { function CrossmatchFilters({ tableName, - status, triageStatus, pageSize, onApplyFilters, }: CrossmatchFiltersProps): ReactElement { - const [localStatus, setLocalStatus] = useState(status || "all"); const [localTriageStatus, setLocalTriageStatus] = useState( triageStatus ?? "pending", ); @@ -53,19 +48,13 @@ function CrossmatchFilters({ const [localTableName, setLocalTableName] = useState(tableName || ""); useEffect(() => { - setLocalStatus(status || "all"); setLocalTriageStatus(triageStatus ?? "pending"); setLocalPageSize(pageSize); setLocalTableName(tableName || ""); - }, [status, triageStatus, pageSize, tableName]); + }, [triageStatus, pageSize, tableName]); function applyFilters(): void { - onApplyFilters( - localTableName, - localStatus, - localTriageStatus, - localPageSize, - ); + onApplyFilters(localTableName, localTriageStatus, localPageSize); } return ( @@ -78,18 +67,6 @@ function CrossmatchFilters({ onEnter={applyFilters} /> - @@ -198,7 +176,6 @@ function CrossmatchResults({ async function fetcher( tableName: string | null, - status: RecordCrossmatchStatus | null, triageStatus: RecordTriageStatus | null, page: number, pageSize: number, @@ -211,7 +188,6 @@ async function fetcher( client: adminClient, query: { table_name: tableName, - status: status, triage_status: triageStatus, page: page, page_size: pageSize, @@ -237,7 +213,6 @@ export function CrossmatchResultsPage(): ReactElement { const [searchParams, setSearchParams] = useSearchParams(); const tableName = searchParams.get("table_name"); - const status = searchParams.get("status") as RecordCrossmatchStatus | null; const triageStatusParam = searchParams.get("triage_status"); const apiTriageStatus: RecordTriageStatus | null = triageStatusParam === null || triageStatusParam === "" @@ -253,8 +228,8 @@ export function CrossmatchResultsPage(): ReactElement { }, [tableName]); const { data, loading, error } = useDataFetching( - () => fetcher(tableName, status, apiTriageStatus, page, pageSize), - [tableName, status, apiTriageStatus, page, pageSize], + () => fetcher(tableName, apiTriageStatus, page, pageSize), + [tableName, apiTriageStatus, page, pageSize], ); function handlePageChange(newPage: number): void { @@ -265,7 +240,6 @@ export function CrossmatchResultsPage(): ReactElement { function handleApplyFilters( newTableName: string, - newStatus: string, newTriageStatus: string, newPageSize: number, ): void { @@ -277,12 +251,6 @@ export function CrossmatchResultsPage(): ReactElement { newSearchParams.delete("table_name"); } - if (newStatus === "all") { - newSearchParams.delete("status"); - } else { - newSearchParams.set("status", newStatus); - } - if (newTriageStatus === "all") { newSearchParams.set("triage_status", "all"); } else { @@ -318,7 +286,6 @@ export function CrossmatchResultsPage(): ReactElement {

Crossmatch results

Date: Fri, 22 May 2026 18:46:40 +0100 Subject: [PATCH 4/4] add "Mark as new" button to the interface --- src/assets/texts.json | 4 +- src/pages/RecordCrossmatchDetails.tsx | 96 +++++++++++++++++++-------- 2 files changed, 70 insertions(+), 30 deletions(-) diff --git a/src/assets/texts.json b/src/assets/texts.json index 4ff3ef7..8e6604f 100644 --- a/src/assets/texts.json +++ b/src/assets/texts.json @@ -10,6 +10,8 @@ "crossmatch.triage.pending": "Pending", "crossmatch.triage.resolved": "Resolved", "crossmatch.triage.verbose.pending": "Pending manual check", - "crossmatch.triage.verbose.resolved": "Resolved" + "crossmatch.triage.verbose.resolved": "Resolved", + "crossmatch.action.mark_new": "Mark as new", + "crossmatch.action.resolve": "Match to this PGC" } } diff --git a/src/pages/RecordCrossmatchDetails.tsx b/src/pages/RecordCrossmatchDetails.tsx index f0100e4..abf82e1 100644 --- a/src/pages/RecordCrossmatchDetails.tsx +++ b/src/pages/RecordCrossmatchDetails.tsx @@ -18,6 +18,7 @@ import { RecordCrossmatch, PgcCandidate, Schema as AdminSchema, + StatusesPayload, } from "../clients/admin/types.gen"; import { Schema as BackendSchema } from "../clients/backend/types.gen"; import { getResource } from "../resources/resources"; @@ -151,7 +152,7 @@ function OriginalData({ function RecordCrossmatchDetails({ data, }: RecordCrossmatchDetailsProps): ReactElement { - const [resolvingPgc, setResolvingPgc] = useState(null); + const [resolving, setResolving] = useState<"new" | number | null>(null); const [resolveError, setResolveError] = useState(null); const { crossmatch, @@ -174,36 +175,57 @@ function RecordCrossmatchDetails({ `crossmatch.triage.verbose.${crossmatch.triage_status}`, ).Title; + async function submitCrossmatchResolution( + statuses: StatusesPayload, + ): Promise { + const response = await setCrossmatchResults({ + client: adminClient, + body: { statuses }, + }); + + if (response.error || !response.data?.data) { + throw new Error( + typeof response.error === "object" + ? JSON.stringify(response.error) + : String(response.error || "Unknown error"), + ); + } + + window.location.reload(); + } + async function resolveCandidate(pgc: number): Promise { setResolveError(null); - setResolvingPgc(pgc); + setResolving(pgc); try { - const response = await setCrossmatchResults({ - client: adminClient, - body: { - statuses: { - existing: { - record_ids: [crossmatch.record_id], - pgcs: [pgc], - triage_statuses: ["resolved"], - }, - }, + await submitCrossmatchResolution({ + existing: { + record_ids: [crossmatch.record_id], + pgcs: [pgc], + triage_statuses: ["resolved"], }, }); + } catch (err) { + setResolveError(`${err}`); + } finally { + setResolving(null); + } + } - if (response.error || !response.data?.data) { - throw new Error( - typeof response.error === "object" - ? JSON.stringify(response.error) - : String(response.error || "Unknown error"), - ); - } - - window.location.reload(); + async function markAsNew(): Promise { + setResolveError(null); + setResolving("new"); + try { + await submitCrossmatchResolution({ + new: { + record_ids: [crossmatch.record_id], + triage_statuses: ["resolved"], + }, + }); } catch (err) { setResolveError(`${err}`); } finally { - setResolvingPgc(null); + setResolving(null); } } @@ -243,6 +265,19 @@ function RecordCrossmatchDetails({ ? "1 candidate" : `${candidates.length} candidates`}

+ {showResolveControls && ( +
+ +
+ )} @@ -254,14 +289,15 @@ function RecordCrossmatchDetails({ )} + {resolveError && ( +

+ {resolveError} +

+ )} + {candidates.length > 0 && (

Crossmatch Candidates

- {resolveError && ( -

- {resolveError} -

- )} {candidates.map((candidate) => ( resolveCandidate(candidate.pgc)} > - {resolvingPgc === candidate.pgc ? "Resolving…" : "Resolve"} + {resolving === candidate.pgc + ? "Saving…" + : getResource("crossmatch.action.resolve").Title} )}