9bca4b608a
Backend:
- New POST /api/batches/{id}/export-837: regenerate X12 837 files
for a list of claim_ids into a ZIP using HCPF file naming standards,
with a unique interchange/group control number per export. Wire
the clearhouse Loop 1000A (NM1*41 + PER) and per-payer receiver
(NM1*40) blocks so the serializer no longer falls back to
CYCLONE / RECEIVER placeholders.
- /api/parse-837 and /api/parse-835 now surface the server-side
batch_id in both JSON and NDJSON response shapes so the frontend
can hit batch-scoped endpoints without an extra listBatches
round-trip.
- Filename helpers and the 837 serializer updated to match the new
HCPF envelope; tests cover batch export, parse batch_id, and the
serializer's control-number uniqueness guarantee.
Frontend:
- New shared components: ClaimCard, ClaimCard837, DominantKpiCard,
EditorialNote, ExportBar, TickerTape, and a charts/ set
(BarChart, HBarChart, SegmentedBar, AgingBars).
- New useBatchExport hook driving ExportBar's download flow against
the new endpoint.
- ClaimDrawer, Lane, and Layout migrated from raw CSS-variable
colors to Tailwind theme tokens (bg-card, text-foreground,
border/60, etc.) for consistency with the rest of the instrument
chrome; the active tab indicator gains a subtle accent glow.
- Upload, Inbox, Batches, BatchDiff, Reconciliation, and Acks pages
reworked to compose the new shared components and consume the new
batch-scoped API surface (notably ExportBar wired into Batches).
Tooling / Docs:
- Add audit-uiux.mjs and a docs/goodclaim.x12 sample fixture.
- Update ClaimDrawer testids and add coverage for the new
components and the useBatchExport hook.
Rolls up into the v0.2.0 release tag.
77 lines
2.8 KiB
TypeScript
77 lines
2.8 KiB
TypeScript
import React from "react";
|
|
import type { ClaimDetail } from "@/types";
|
|
|
|
type RawSegmentsPanelProps = {
|
|
rawSegments: ClaimDetail["rawSegments"];
|
|
};
|
|
|
|
/**
|
|
* Format a single segment by joining its elements with `*` (the X12
|
|
* element separator). Trailing/leading whitespace inside individual
|
|
* elements is preserved — these strings come straight off the parser
|
|
* and any trimming would change the visible byte-for-byte content.
|
|
*/
|
|
function formatSegment(elements: string[]): string {
|
|
return elements.join("*");
|
|
}
|
|
|
|
/**
|
|
* Raw-segments debug section of the claim detail drawer (SP4).
|
|
*
|
|
* Renders the exact X12 segment strings the parser saw, collapsed by
|
|
* default behind a native <details>/<summary> so the user can opt in
|
|
* to a wall of monospace when they're debugging. Element separator is
|
|
* `*` and segment terminator is implicit at the end of each line —
|
|
* matches what the parser produces. This is a read-only diagnostic;
|
|
* the plan keeps it intentionally simple (no copy button for v1).
|
|
*/
|
|
export function RawSegmentsPanel({ rawSegments }: RawSegmentsPanelProps) {
|
|
return (
|
|
<section
|
|
className="flex flex-col gap-3 px-6 py-4"
|
|
data-testid="raw-segments-panel"
|
|
>
|
|
<h3
|
|
data-testid="section-label"
|
|
className="eyebrow text-muted-foreground"
|
|
>
|
|
Raw Segments ({rawSegments.length})
|
|
</h3>
|
|
|
|
<details className="group rounded-lg border border-border/60 bg-muted/30 overflow-hidden transition-colors hover:border-border">
|
|
<summary
|
|
className="cursor-pointer select-none px-4 py-2.5 text-sm text-muted-foreground hover:text-foreground transition-colors [&::-webkit-details-marker]:hidden flex items-center gap-2"
|
|
data-testid="raw-segments-summary"
|
|
>
|
|
<span className="inline-block h-1.5 w-1.5 rounded-full bg-muted-foreground group-open:bg-accent transition-colors" />
|
|
{rawSegments.length === 0
|
|
? `Show raw segments`
|
|
: `Show ${rawSegments.length} raw segment${rawSegments.length === 1 ? "" : "s"}`}
|
|
</summary>
|
|
|
|
{rawSegments.length === 0 ? (
|
|
<p
|
|
data-testid="raw-segments-empty"
|
|
className="px-4 pb-3 text-sm text-muted-foreground"
|
|
>
|
|
No segments
|
|
</p>
|
|
) : (
|
|
<pre
|
|
data-testid="raw-segments-pre"
|
|
className="overflow-x-auto whitespace-pre-wrap border-t border-border/40 px-4 pb-3 pt-3 text-[12px] leading-relaxed text-foreground font-mono"
|
|
style={{ fontFamily: "var(--m-font-mono)" }}
|
|
>
|
|
{rawSegments.map((segment, i) => (
|
|
<React.Fragment key={i}>
|
|
{i > 0 ? "\n" : null}
|
|
<span data-testid="raw-segment-line">{formatSegment(segment)}</span>
|
|
</React.Fragment>
|
|
))}
|
|
</pre>
|
|
)}
|
|
</details>
|
|
</section>
|
|
);
|
|
}
|