import { useState } from "react"; import { Download } from "lucide-react"; import { Badge, type BadgeProps } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { DrillDrawerHeader } from "@/components/drill/DrillDrawerHeader"; import { api } from "@/lib/api"; import { downloadTextFile } from "@/lib/download"; import { fmt } from "@/lib/format"; import type { ClaimDetail } from "@/types"; type ClaimDrawerHeaderProps = { claim: ClaimDetail; onClose: () => void; /** * Optional toast callback for surfacing download failures (network error, * 404 if the claim was deleted out from under us, 422 if the stored * payload can't be regenerated). Defaults to a no-op so consumers that * don't wire a toast still get the happy-path download. */ onError?: (message: string) => void; }; /** * State → Badge variant. The state machine value (see * `cyclone.db.ClaimState`) drives a color-coded chip so the user can * scan the drawer's status at a glance: * * submitted → secondary (neutral, in-flight) * accepted → default (brand-colored, accepted by payer) * matched → success (paired with an ERA) * paid → success (funds received) * denied → destructive (needs attention) * pending → warning (waiting on something) * anything else → muted */ const STATE_VARIANT: Record = { submitted: "secondary", accepted: "default", matched: "success", paid: "success", denied: "destructive", pending: "warning", }; function badgeVariantFor(state: string): BadgeProps["variant"] { return STATE_VARIANT[state] ?? "muted"; } /** * Header band for the claim detail drawer (SP4 → refactored SP21 * Phase 5 Task 5.10). * * The shell is now the shared `DrillDrawerHeader` (same as * `ProviderDrawer` / `AckDrawer`) — eyebrow + title on the left, * close button on the right. The right-side `action` slot carries * the state badge, total billed amount, and the "Download 837" * button, all of which used to live in a custom
block. * * Top-left: instrument-style "Claim" eyebrow + the claim ID. * Top-right: state badge + total billed amount + download button. * The badge color encodes state so the user can read the drawer's * status at a glance without scrolling. */ export function ClaimDrawerHeader({ claim, onClose, onError, }: ClaimDrawerHeaderProps) { // Local "is downloading" flag so the button can show feedback while the // fetch is in flight. The header is rendered above the fold, so a stuck // spinner is the difference between "the click did something" and "did // my click even register?" const [downloading, setDownloading] = useState(false); async function handleDownload() { if (downloading) return; setDownloading(true); try { const { text, filename } = await api.serializeClaim837(claim.id); downloadTextFile(filename, "text/x12", text); } catch (err) { const message = err instanceof Error ? err.message : "Failed to download 837 file."; onError?.(message); } finally { setDownloading(false); } } // The action slot is rendered by DrillDrawerHeader to the left of // the close button. Group the three action pieces (badge, amount, // download) in a single flex row so they read as a unit. const action = (
{claim.stateLabel} {fmt.usdPrecise(claim.billedAmount)}
); return (
); }