feat(frontend): ClaimDetail type + api.getClaimDetail

This commit is contained in:
Tyler
2026-06-20 10:01:40 -06:00
parent b984c01d9a
commit 44e1c4616d
3 changed files with 256 additions and 1 deletions
+125
View File
@@ -418,3 +418,128 @@ export interface Ack {
ackCode: "A" | "E" | "R" | "P";
parsedAt: string;
}
// ---------------------------------------------------------------------------
// SP4 claim detail drawer types (Task 4).
// Mirrors the JSON shape of `GET /api/claims/{claim_id}` 1:1 — camelCase
// keys, numeric money fields (coerced from Decimal on the backend), ISO
// timestamps with a trailing `Z`. Distinct from the parser-aligned
// snake_case types above (e.g. ``BillingProvider``, ``ServiceLine``) which
// describe the EDI parse tree; these describe the UI-ready detail payload.
// The ``ClaimDetail*`` prefix keeps the two flavors side-by-side without
// a TypeScript duplicate-identifier collision.
// ---------------------------------------------------------------------------
export interface ClaimDetailDiagnosis {
code: string;
qualifier: string | null;
}
export interface ClaimDetailServiceLine {
lineNumber: number;
procedureQualifier: string;
procedureCode: string;
modifiers: string[];
charge: number;
units: number | null;
unitType: string | null;
serviceDate: string | null; // ISO date YYYY-MM-DD or null
}
/**
* The backend can return ``{}`` for a missing address (see
* ``store._address_to_ui``); callers that want a populated address must
* branch on ``Object.keys(addr).length === 0`` before reading fields.
*/
export interface ClaimDetailAddress {
line1: string;
line2: string | null;
city: string;
state: string;
zip: string;
}
export interface ClaimDetailBillingProvider {
name: string;
npi: string;
taxId: string;
address: ClaimDetailAddress | Record<string, never>;
}
export interface ClaimDetailSubscriber {
firstName: string;
lastName: string;
memberId: string;
dob: string | null;
gender: string | null;
}
export interface ClaimDetailPayer {
name: string;
id: string;
}
export interface ClaimDetailParties {
billingProvider: ClaimDetailBillingProvider;
subscriber: ClaimDetailSubscriber;
payer: ClaimDetailPayer;
}
export interface ClaimDetailValidationIssue {
rule: string;
severity: "error" | "warning";
message: string;
}
export interface ClaimDetailValidation {
passed: boolean;
errors: ClaimDetailValidationIssue[];
warnings: ClaimDetailValidationIssue[];
}
export interface ClaimDetailMatchedRemittance {
id: string;
totalPaid: number;
/** Mirrors the same `"received" | "reconciled"` mapping as the list endpoint. */
status: string;
receivedAt: string; // ISO Z
}
export interface ClaimDetailStateHistoryEvent {
kind: string;
ts: string; // ISO Z (most-recent-first, capped at 50 by the backend)
batchId: string | null;
remittanceId: string | null;
}
/**
* One claim with the full drawer context: header, state, parties,
* validation, service lines, diagnoses, raw segments, recent
* ``stateHistory`` (most-recent-first, capped at 50), and a populated
* ``matchedRemittance`` block when the claim has been paired with an
* ERA. Mirrors ``CycloneStore.get_claim_detail`` (backend/src/cyclone/store.py)
* and the ``GET /api/claims/{claim_id}`` endpoint contract.
*/
export interface ClaimDetail {
id: string;
batchId: string;
state: string;
stateLabel: string;
billedAmount: number;
patientName: string;
providerNpi: string;
providerName: string;
payerName: string;
payerId: string;
submissionDate: string; // ISO Z
serviceDateFrom: string | null;
serviceDateTo: string | null;
parsedAt: string; // ISO Z
diagnoses: ClaimDetailDiagnosis[];
serviceLines: ClaimDetailServiceLine[];
parties: ClaimDetailParties;
validation: ClaimDetailValidation;
rawSegments: string[][];
matchedRemittance: ClaimDetailMatchedRemittance | null;
stateHistory: ClaimDetailStateHistoryEvent[];
}