# DealColumn

> One column of the deals board: a droppable, infinitely scrolling list of `DealCard`s under a header with the stage dot, a count badge and money totals.

## Facts

- **Product**: Max
- **Family**: Pipeline
- **Global**: `window.DigitalCrew.DealColumn`
- **Source**: `max-agent: src/features/deals/ui/components/deal-column.tsx`
- **Import in the app**: `import { DealColumn } from "@/features/deals/ui/components/deal-column";`
- **Live preview**: /design/components/deal-column/preview.html
- **Page**: /design/components/deal-column

## Guidelines

**Use it for** the Kanban view of a deal pipeline. Render it inside the board's `DndContext`. It registers with `useDroppable` under the stage id, with `data: { type: "column", stageId }`.

**What you provide**:
- `stage: DealStageDto`: `label`, and `color` chosen from `STAGE_COLORS`.
- `column: { items, total, page, isInitialLoading, isLoadingMore }`.
- `totals?: { count, sums: [{ currency, amount, weighted }] }`.
- `onLoadMore(stageId)` and `onOpenDeal(deal)`.

**Anatomy**:
- The column is `w-[min(100%,280px)] rounded-xl border bg-muted/20`.
- Header, `px-3 pt-3 pb-2`:
  - a `size-2` dot from `stageColorClasses(color).dot`;
  - the label in `text-sm font-medium`;
  - an outline [`Badge`](/design/components/badge.md) with the accent `badge` classes, showing `totals.count` or else `column.total`;
  - one `text-[11px]` line per currency: "Σ €126,000 · weighted €63,000".
- The list is `gap-2 p-2 pt-0 min-h-24 overflow-y-auto`.
- A drag over the column tints it `bg-primary/5 ring-1 ring-inset ring-primary/20`.

**Behaviour**: scrolling within 120px of the bottom calls `onLoadMore` while `items.length < total`. The first load shows two `h-16` skeletons; loading more shows one.

The default stages are Qualified (blue, 10%), Meeting booked (sky, 25%), Proposal (amber, 50%), Negotiation (orange, 75%), Won (emerald, 100%) and Lost (red, 0%).

**Don't** merge currencies into one total. Each currency keeps its own line.

## API

```ts
type StageTotals = {
    count: number;
    sums: {
        currency: string;
        amount: number;
        weighted: number;
    }[];
};
type DealColumnProps = {
    stage: DealStageDto;
    column: DealColumnState;
    totals: StageTotals | undefined;
    onLoadMore: (stageId: string) => void;
    onOpenDeal: (deal: DealWithRelationsDto) => void;
};
export declare function DealColumn({ stage, column, totals, onLoadMore, onOpenDeal, }: DealColumnProps): JSX.Element;
```

## Example

```html
<div id="root" class="p-6"></div>
<script>
(function () {
  var h = React.createElement, DC = window.DigitalCrew, I = DC.Icons;
  var WS = "0b9f6c1e-3a52-4c1d-9e0a-5d2f7b8c4a10", PIPE = "5e1d2c3b-4a59-4f68-8a7b-6c5d4e3f2a1b";
  function color(name) { return DC.STAGE_COLORS.indexOf(name) >= 0 ? name : DC.STAGE_COLORS[0]; }
  // DealStageDto rows from the default "Sales pipeline" (features/deals/constants.ts).
  function stage(id, key, label, c, type, winProbability, position) {
    return { id: id, workspace_id: WS, pipeline_id: PIPE, key: key, label: label, color: color(c), type: type,
      win_probability: winProbability, position: position, is_system: false,
      created_at: "2026-06-01T08:00:00Z", updated_at: "2026-06-01T08:00:00Z" };
  }
  function deal(id, stageId, status, name, amount, currency, closeDate, orgName, contactName) {
    return {
      id: id, workspace_id: WS, pipeline_id: PIPE, stage_id: stageId, name: name, amount: amount, currency: currency,
      close_date: closeDate, probability: null, status: status, won_at: null, lost_at: null, lost_reason: null,
      owner_id: null, organization_id: orgName ? "org-" + id : null, description: null, custom_fields: null,
      source: "agent", created_by: null, created_at: "2026-09-01T09:00:00Z", updated_at: "2026-09-20T09:00:00Z",
      organization: orgName ? { id: "org-" + id, name: orgName, primary_domain: null } : null,
      prospects: contactName ? [{ deal_id: id, prospect_id: "p-" + id, workspace_id: WS, role: null, is_primary: true, created_at: "2026-09-01T09:00:00Z",
        prospect: { id: "p-" + id, full_name: contactName, first_name: null, last_name: null, email: null, title: null, photo_url: null, organization_id: null } }] : []
    };
  }
  var PROPOSAL = stage("st-proposal", "proposal", "Proposal", "amber", "open", 50, 2);
  var WON = stage("st-won", "won", "Won", "emerald", "won", 100, 4);
  var PROPOSAL_DEALS = [
    deal("a1", PROPOSAL.id, "open", "Northwind Traders — DACH pilot", 48000, "EUR", "2026-10-15", "Northwind Traders", "Claire Dubois"),
    deal("a2", PROPOSAL.id, "open", "Litware — SDR augmentation", 36000, "EUR", "2026-10-30", "Litware", "Amelia Hart"),
    deal("a3", PROPOSAL.id, "open", "Adatum renewal + LinkedIn seats", 42000, "EUR", "2026-11-06", "Adatum Corp", "Lucas Moreau")
  ];
  var WON_DEALS = [
    deal("b1", WON.id, "won", "Contoso Logistics — annual plan", 96000, "EUR", "2026-09-22", "Contoso Logistics", null),
    deal("b2", WON.id, "won", "Tailspin Toys — outbound pod", 24000, "USD", "2026-09-18", "Tailspin Toys", "Priya Nair")
  ];
  function column(items) { return { items: items, total: items.length, page: 1, isInitialLoading: false, isLoadingMore: false }; }
  function App() {
    return h(DC.DndContext, null,
      h("div", { className: "flex items-start gap-3" },
        h(DC.DealColumn, {
          stage: PROPOSAL, column: column(PROPOSAL_DEALS), onLoadMore: function () {}, onOpenDeal: function () {},
          totals: { count: 3, sums: [{ currency: "EUR", amount: 126000, weighted: 63000 }] }
        }),
        h(DC.DealColumn, {
          stage: WON, column: column(WON_DEALS), onLoadMore: function () {}, onOpenDeal: function () {},
          totals: { count: 2, sums: [{ currency: "EUR", amount: 96000, weighted: 96000 }, { currency: "USD", amount: 24000, weighted: 24000 }] }
        })));
  }
  ReactDOM.createRoot(document.getElementById("root")).render(h(DC.TooltipProvider, { delayDuration: 80 }, h(App)));
})();
</script>
```

## More in Pipeline

- [DealCard](/design/components/deal-card.md): One draggable deal on the deals board, plus `DealCardPreview`, the tilted copy shown under the cursor while dragging.
- [MovableCardGrid](/design/components/movable-card-grid.md): A card grid the reader can rearrange.
- [SalesWorkspaceMap](/design/components/sales-workspace-map.md): A read-only React Flow map of how a workspace's lists, signals, campaigns, rules and pipelines connect.
- [StageColors](/design/components/stage-colors.md): The 17-colour palette for pipeline and deal stage accents, with helpers that return static Tailwind classes and a hex value for each colour.
