Pipeline
DealCard
One draggable deal on the deals board, plus DealCardPreview, the tilted copy shown under the cursor while dragging.
Guidelines#
Use it for deal columns (see DealColumn). 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 itsorganizationandprospects.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 istext-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 byformatDealAmount(Intl currency, no decimals); - the contact with a 12px
Usericon: 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, formattedMMM d.
- the amount in
- A deal with status
wonand no contact shows an amber "No contact" flag withTriangleAlert(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.
Facts#
| Group | Pipeline |
|---|---|
| Product | Max |
| 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"; |
API#
TypeScript declarations emitted from src/features/deals/ui/components/deal-card.tsx (the module also exports DealCard, DealCardPreview).
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#
The code of the preview above: plain React.createElement against window.DigitalCrew, with realistic data.
<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>