# DealCard

> One draggable deal on the deals board, plus `DealCardPreview`, the tilted copy shown under the cursor while dragging.

## Facts

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

## Guidelines

**Use it for** deal columns (see [`DealColumn`](/design/components/deal-column.md)). Render it inside a dnd-kit `DndContext`. It registers with `useDraggable` under `id: deal.id` and `data: { type: "deal", deal, columnStageId }`.

**What you provide**:
- `deal: DealWithRelationsDto`: the deal row plus its `organization` and `prospects`.
- `columnStageId`: the column the card is in.
- `onOpen(deal)`: called when the card is clicked.

`DealCardPreview` takes only `deal`.

**Anatomy**:
- The body is `bg-card rounded-lg border p-3 shadow-xs`. The name is `text-sm font-medium`, truncated.
- A metadata row follows (`mt-1.5 text-xs text-muted-foreground`, `gap-x-3 gap-y-1`, wrapping):
  - the amount in `text-foreground font-medium`, formatted by `formatDealAmount` (Intl currency, no decimals);
  - the contact with a 12px `User` icon: the primary contact, else the first one, shown as full name, then first + last name, then email;
  - the organization with `Building2`;
  - the close date with `CalendarDays`, formatted `MMM d`.
- A deal with status `won` and no contact shows an amber "No contact" flag with `TriangleAlert` (`text-amber-600 dark:text-amber-500`). Its title tells the user to open the deal and link the person.

**Behaviour**: the card is `cursor-grab touch-none`. While it is dragged, the original goes to opacity 0 and the overlay carries it. `DealCardPreview` is `w-[260px] rotate-2`.

**Don't** format money yourself, and never add different currencies together.

## API

```ts
export { formatCurrencySums, formatDealAmount } from "./deal-amount";
type DealCardProps = {
    deal: DealWithRelationsDto;
    columnStageId: string;
    onOpen: (deal: DealWithRelationsDto) => void;
};
export declare function DealCard({ deal, columnStageId, onOpen }: DealCardProps): JSX.Element;
/** Static clone rendered inside the DragOverlay. */
export declare function DealCardPreview({ deal }: {
    deal: DealWithRelationsDto;
}): 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";
  // DealWithRelationsDto: the deal row plus its organization and contacts.
  function deal(id, name, amount, currency, closeDate, status, org, contact) {
    return {
      id: id, workspace_id: WS, pipeline_id: PIPE, stage_id: "stage-" + status, name: name,
      amount: amount, currency: currency, close_date: closeDate, probability: null, status: status,
      won_at: status === "won" ? "2026-09-22T14:05:00Z" : null, lost_at: null, lost_reason: null,
      owner_id: null, organization_id: org ? org.id : null, description: null, custom_fields: null,
      source: "manual", created_by: null, created_at: "2026-08-30T09:00:00Z", updated_at: "2026-09-22T14:05:00Z",
      organization: org,
      prospects: contact ? [{
        deal_id: id, prospect_id: contact.id, workspace_id: WS, role: "Decision maker", is_primary: true,
        created_at: "2026-08-30T09:00:00Z",
        prospect: { id: contact.id, full_name: contact.name, first_name: null, last_name: null, email: null, title: contact.title, photo_url: null, organization_id: org ? org.id : null }
      }] : []
    };
  }
  var NORTHWIND = { id: "org-northwind", name: "Northwind Traders", primary_domain: "northwind.com" };
  var CONTOSO = { id: "org-contoso", name: "Contoso Logistics", primary_domain: "contoso.io" };
  var DEALS = [
    deal("d1", "Northwind Traders — DACH pilot", 48000, "EUR", "2026-10-15", "open", NORTHWIND, { id: "pr-1", name: "Claire Dubois", title: "CFO" }),
    deal("d2", "Contoso Logistics — annual plan", 96000, "EUR", "2026-09-22", "won", CONTOSO, null),
    deal("d3", "Fabrikam onboarding", null, "EUR", null, "open", null, { id: "pr-3", name: "Jonas Weber", title: "Head of Sales" })
  ];
  function App() {
    return h(DC.DndContext, null,
      h("div", { className: "flex flex-wrap items-start gap-8" },
        h("div", { className: "flex w-[280px] flex-col gap-2" },
          DEALS.map(function (d) {
            return h(DC.DealCard, { key: d.id, deal: d, columnStageId: d.stage_id, onOpen: function () {} });
          })),
        h("div", { className: "flex flex-col gap-3 pt-2" },
          h("p", { className: "text-xs font-medium text-muted-foreground" }, "While dragging (DealCardPreview)"),
          h(DC.DealCardPreview, { deal: DEALS[0] }))));
  }
  ReactDOM.createRoot(document.getElementById("root")).render(h(DC.TooltipProvider, { delayDuration: 80 }, h(App)));
})();
</script>
```

## More in Pipeline

- [DealColumn](/design/components/deal-column.md): 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.
- [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.
