# Settling card

A card from the registry whose write may not have settled yet, so it never reads as done before the service says it is. Around RegistryCard, which draws the card (and its own fallbacks) unchanged.

- `pending`: the card draws under a line saying it is waiting for the service to confirm, and the card is `aria-busy`.
- `failed`: the service's reason, in a degraded panel with Retry when the app can retry, then the card marked "Not applied".
- `settled`: the card alone. Set it only from the service's own confirmation; nothing here shows success before it.

One status region stays in the page for the card's life and its words change with the settlement, so a screen reader hears "Waiting…" when the card turns pending, and the failure is announced when it fails. When the person presses Retry, focus moves to the status line rather than being lost with the button. The card's definition reads the state with `useCardSettlement()` to lock its own actions while pending.

- Registry name: `settling-card` (`@candor/settling-card`)
- Tier: Pro. Installing it needs a Pro, Premium or Team licence key: the person runs `npx candor-ui key <key>` themselves; never ask for the key in chat or write it to a file.
- Docs: https://candor.themethodic.com/docs/settling-card

## Install

```bash
npx candor-ui add settling-card
```

Run `npx candor-ui@latest init` once first (https://candor.themethodic.com/get-started.md). Without the CLI: `npx shadcn@latest add @candor/settling-card`.
It brings in: bidi, card-registry, degraded-panel, locale, status-dot, tokens, use-announcer, utils.
npm packages: lucide-react@^1.67.0.

## Example

```tsx
"use client"

import * as React from "react"
import { CardRegistryProvider, defineCard, isObject, isString } from "@/components/candor/card-registry"
import { SettlingCard, useCardSettlement } from "@/components/candor/settling-card"
import { Button } from "@/components/candor/ui/button"
import { Locked } from "@/components/candor/locked"

/** The card's own action is locked until the service confirms the write. */
function RefundCard({ amount, order }: { amount: string; order: string }) {
  const settlement = useCardSettlement()
  return (
    <div className="flex items-center justify-between gap-3 rounded-lg border bg-card px-3 py-2.5 text-sm">
      <span>
        Refund {amount} on order {order}
      </span>
      <Locked reason={settlement === "pending" ? "Waiting for the service to confirm" : settlement === "failed" ? "Nothing to undo: it wasn’t applied" : undefined}>
        <Button size="xs" variant="outline">
          Undo
        </Button>
      </Locked>
    </div>
  )
}

const cards = {
  refund: defineCard<{ amount: string; order: string }>({
    label: "Refund",
    check: (p) => (isObject(p) && isString(p.amount) && isString(p.order) ? null : "it needs an amount and an order"),
    render: (p) => <RefundCard {...p} />,
  }),
}

export default function Example() {
  const [settlement, setSettlement] = React.useState<"pending" | "settled" | "failed">("pending")
  return (
    <CardRegistryProvider cards={cards}>
      <div className="flex max-w-lg flex-col gap-3">
        <SettlingCard
          name="refund"
          props={{ amount: "£40.00", order: "1042" }}
          summary="Refund £40.00 on order 1042"
          settlement={settlement}
          service="the billing service"
          failure="The card on file has expired."
          onRetry={() => setSettlement("pending")}
        />
        <div className="flex gap-2">
          <Button size="xs" variant="ghost" onClick={() => setSettlement("settled")}>
            The service confirms
          </Button>
          <Button size="xs" variant="ghost" onClick={() => setSettlement("failed")}>
            The service refuses
          </Button>
        </div>
      </div>
    </CardRegistryProvider>
  )
}
```

## <SettlingCard>

Props:

- `name: string`: Which card to draw, from the registry.
- `props: unknown`: The card's data, checked before it draws.
- `summary: string`: The plain-text version, shown when the card can't be.
- `settlement: CardSettlement`: Where the card's write stands: "pending" until the service confirms it, "settled" once it has, "failed" if it refused.
- `service?: string | undefined`: Who must confirm the write, named by the app ("the billing service"). Left out, the line doesn't name one.
- `failure?: string | undefined`: Why the service refused it, in its own words; shown after the sentence saying it wasn't applied.
- `onRetry?: (() => void) | undefined`: Shows Retry on a failed write.
- `className?: string | undefined`: Classes, merged with Candor's.

## Functions

### useCardSettlement

```ts
useCardSettlement()
```

Where the card being drawn stands, for a card definition's own `render`: "pending" while its write waits for the service, "settled" once confirmed, "failed" when refused, and undefined outside a SettlingCard. Lock the card's own actions while it is pending, with a reason, rather than letting them act on something not yet true: `<Locked reason={useCardSettlement() === "pending" ? "Waiting for the service to confirm" : undefined}>`.

## Strings (`settlingCard`)

The fixed words, English by default. Override any of them under `settlingCard` in `<LocaleProvider strings={{ settlingCard: { … } }}>`; the table is exported as `settlingCardStrings`.

- `waiting`: Waiting for {service} to confirm
- `waitingUnnamed`: Waiting for confirmation
- `failed`: {service} didn’t apply this.
- `failedUnnamed`: This wasn’t applied.
- `notApplied`: Not applied

## Types

### CardSettlement

Where a card's write stands: sent and not yet confirmed, confirmed by the service, or refused by it.

```ts
"pending" | "settled" | "failed"
```

## Rules for using Candor UI

- Components take plain typed data. Never pass model output as HTML or render it as a link; messages are typed blocks.
- A control that can't be used says why: wrap it in `<Locked reason="…">`, don't just disable it.
- Don't show a value you don't have: leave it out (it renders as —) rather than guess. An estimate is labelled as one.
- Nothing is pre-selected in a decision a person makes, and the agent is named from your app's data, never from model output.
- Your app's words go in `LocaleProvider`: its fixed strings in `strings`, its nouns in `nouns`; entity kinds and agent roles in `EntityKindsProvider` and `AgentRolesProvider`.
