jengacore.shadcn registry · Tailwind v4 · Radix and Base UI
Components
Product components · @jengacore/settlement-window

SettlementWindow

Finance Portal: open → closed → files sent → settled stepper.

Finance Portal
Settlement windowsw_1041
  1. Open04 Oct 00:00done
  2. Closed04 Oct 17:00done
  3. Files sent04 Oct 17:12current
  4. Settled—not reached

Install

terminal
npx shadcn@latest add @jengacore/settlement-window

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