Files
cyclone/src/components/ClaimDrawer/RawSegmentsPanel.tsx
T
Tyler 9bca4b608a feat(release): v0.2.0 — batch 837 export, ClaimCard, theme tokens
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.
2026-06-22 11:01:58 -06:00

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>
);
}