Product components · @jengacore/settlement-window
SettlementWindow
Finance Portal: open → closed → files sent → settled stepper.
Finance Portal
Settlement windowsw_1041
- Open04 Oct 00:00done
- Closed04 Oct 17:00done
- Files sent04 Oct 17:12current
- Settled—not reached
Install
terminal
npx shadcn@latest add @jengacore/settlement-windowWithout the namespace configured: npx shadcn@latest add https://ui.jengacore.com/r/<new-york-v4|base-nova>/settlement-window.json
Registry dependencies
npm packages
lucide-react
Source
components/jc/settlement-window.tsx
import * as React from "react"
import { Check } from "lucide-react"
import { IdChip } from "@/components/jc/id-chip"
import { cn } from "@/lib/utils"
export type WindowStage = "open" | "closed" | "files_sent" | "settled"
const STAGES: { key: WindowStage; label: string }[] = [
{ key: "open", label: "Open" },
{ key: "closed", label: "Closed" },
{ key: "files_sent", label: "Files sent" },
{ key: "settled", label: "Settled" },
]
/**
* Life cycle of a Mojaloop settlement window, at the top of the window detail page.
* Done = success icon, current = mint ground, future = muted.
*/
export function SettlementWindow({
windowId,
stage,
times = {},
className,
}: {
windowId: string
stage: WindowStage
/** Formatted times per reached stage, e.g. { open: "05 Oct 00:00" }. */
times?: Partial<Record<WindowStage, string>>
className?: string
}) {
const current = STAGES.findIndex((s) => s.key === stage)
return (
<section data-slot="settlement-window" className={cn("grid gap-3", className)} aria-label="Settlement window">
<div className="flex items-center gap-2">
<span className="eyebrow">Settlement window</span>
<IdChip id={windowId} />
</div>
<ol className="grid grid-cols-2 overflow-hidden rounded-lg border border-border bg-card sm:grid-cols-4 contrast:border-input">
{STAGES.map((s, i) => {
const done = i < current || stage === "settled"
const isCurrent = i === current && stage !== "settled"
return (
<li
key={s.key}
aria-current={isCurrent ? "step" : undefined}
className={cn(
"flex items-center gap-2.5 border-border px-4 py-3 not-last:border-r",
isCurrent && "bg-secondary text-secondary-foreground",
!done && !isCurrent && "text-muted-foreground"
)}
>
<span
className={cn(
"grid size-5 shrink-0 place-items-center rounded-full border font-mono text-[10.5px]",
done && "border-success bg-success text-primary-foreground",
isCurrent && "border-2 border-primary",
!done && !isCurrent && "border-border contrast:border-input"
)}
aria-hidden
>
{done ? <Check className="size-3 stroke-[2.5]" /> : i + 1}
</span>
<span className="grid">
<span className="text-[13.5px] font-medium">{s.label}</span>
<span className="font-mono text-[11px] text-muted-foreground">{times[s.key] ?? (done || isCurrent ? "" : "—")}</span>
</span>
<span className="sr-only">{done ? "done" : isCurrent ? "current" : "not reached"}</span>
</li>
)
})}
</ol>
</section>
)
}