@contractspec/example.crm-pipeline 3.7.16 → 3.7.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +135 -135
- package/CHANGELOG.md +40 -0
- package/dist/browser/crm-pipeline.feature.js +1 -82
- package/dist/browser/deal/deal.enum.js +1 -18
- package/dist/browser/deal/deal.operation.js +1 -396
- package/dist/browser/deal/deal.schema.js +1 -141
- package/dist/browser/deal/deal.test-spec.js +1 -58
- package/dist/browser/deal/index.js +1 -408
- package/dist/browser/docs/crm-pipeline.docblock.js +5 -49
- package/dist/browser/docs/index.js +5 -49
- package/dist/browser/entities/company.entity.js +1 -52
- package/dist/browser/entities/contact.entity.js +1 -66
- package/dist/browser/entities/deal.entity.js +1 -107
- package/dist/browser/entities/index.js +1 -343
- package/dist/browser/entities/task.entity.js +1 -99
- package/dist/browser/events/contact.event.js +1 -31
- package/dist/browser/events/deal.event.js +1 -101
- package/dist/browser/events/index.js +1 -158
- package/dist/browser/events/task.event.js +1 -28
- package/dist/browser/example.js +1 -39
- package/dist/browser/handlers/crm.handlers.js +2 -171
- package/dist/browser/handlers/deal.handlers.js +1 -293
- package/dist/browser/handlers/index.js +2 -467
- package/dist/browser/handlers/mock-data.js +1 -165
- package/dist/browser/index.js +8 -3461
- package/dist/browser/operations/index.js +1 -407
- package/dist/browser/presentations/dashboard.presentation.js +1 -55
- package/dist/browser/presentations/index.js +1 -290
- package/dist/browser/presentations/pipeline.presentation.js +1 -236
- package/dist/browser/seeders/index.js +1 -22
- package/dist/browser/ui/CrmDashboard.js +1 -1547
- package/dist/browser/ui/CrmDealCard.js +1 -50
- package/dist/browser/ui/CrmPipelineBoard.js +1 -160
- package/dist/browser/ui/hooks/index.js +1 -197
- package/dist/browser/ui/hooks/useDealList.js +1 -95
- package/dist/browser/ui/hooks/useDealMutations.js +1 -100
- package/dist/browser/ui/index.js +4 -2205
- package/dist/browser/ui/modals/CreateDealModal.js +1 -211
- package/dist/browser/ui/modals/DealActionsModal.js +1 -428
- package/dist/browser/ui/modals/index.js +1 -638
- package/dist/browser/ui/overlays/demo-overlays.js +1 -55
- package/dist/browser/ui/overlays/index.js +1 -55
- package/dist/browser/ui/renderers/index.js +4 -849
- package/dist/browser/ui/renderers/pipeline.markdown.js +4 -575
- package/dist/browser/ui/renderers/pipeline.renderer.js +1 -275
- package/dist/browser/ui/tables/DealListTab.js +1 -390
- package/dist/crm-pipeline.feature.js +1 -82
- package/dist/deal/deal.enum.js +1 -18
- package/dist/deal/deal.operation.js +1 -396
- package/dist/deal/deal.schema.js +1 -141
- package/dist/deal/deal.test-spec.js +1 -58
- package/dist/deal/index.js +1 -408
- package/dist/docs/crm-pipeline.docblock.js +5 -49
- package/dist/docs/index.js +5 -49
- package/dist/entities/company.entity.js +1 -52
- package/dist/entities/contact.entity.js +1 -66
- package/dist/entities/deal.entity.js +1 -107
- package/dist/entities/index.js +1 -343
- package/dist/entities/task.entity.js +1 -99
- package/dist/events/contact.event.js +1 -31
- package/dist/events/deal.event.js +1 -101
- package/dist/events/index.js +1 -158
- package/dist/events/task.event.js +1 -28
- package/dist/example.js +1 -39
- package/dist/handlers/crm.handlers.js +2 -171
- package/dist/handlers/deal.handlers.js +1 -293
- package/dist/handlers/index.js +2 -467
- package/dist/handlers/mock-data.js +1 -165
- package/dist/index.js +8 -3461
- package/dist/node/crm-pipeline.feature.js +1 -82
- package/dist/node/deal/deal.enum.js +1 -18
- package/dist/node/deal/deal.operation.js +1 -396
- package/dist/node/deal/deal.schema.js +1 -141
- package/dist/node/deal/deal.test-spec.js +1 -58
- package/dist/node/deal/index.js +1 -408
- package/dist/node/docs/crm-pipeline.docblock.js +5 -49
- package/dist/node/docs/index.js +5 -49
- package/dist/node/entities/company.entity.js +1 -52
- package/dist/node/entities/contact.entity.js +1 -66
- package/dist/node/entities/deal.entity.js +1 -107
- package/dist/node/entities/index.js +1 -343
- package/dist/node/entities/task.entity.js +1 -99
- package/dist/node/events/contact.event.js +1 -31
- package/dist/node/events/deal.event.js +1 -101
- package/dist/node/events/index.js +1 -158
- package/dist/node/events/task.event.js +1 -28
- package/dist/node/example.js +1 -39
- package/dist/node/handlers/crm.handlers.js +2 -171
- package/dist/node/handlers/deal.handlers.js +1 -293
- package/dist/node/handlers/index.js +2 -467
- package/dist/node/handlers/mock-data.js +1 -165
- package/dist/node/index.js +8 -3461
- package/dist/node/operations/index.js +1 -407
- package/dist/node/presentations/dashboard.presentation.js +1 -55
- package/dist/node/presentations/index.js +1 -290
- package/dist/node/presentations/pipeline.presentation.js +1 -236
- package/dist/node/seeders/index.js +1 -22
- package/dist/node/ui/CrmDashboard.js +1 -1547
- package/dist/node/ui/CrmDealCard.js +1 -50
- package/dist/node/ui/CrmPipelineBoard.js +1 -160
- package/dist/node/ui/hooks/index.js +1 -197
- package/dist/node/ui/hooks/useDealList.js +1 -95
- package/dist/node/ui/hooks/useDealMutations.js +1 -100
- package/dist/node/ui/index.js +4 -2205
- package/dist/node/ui/modals/CreateDealModal.js +1 -211
- package/dist/node/ui/modals/DealActionsModal.js +1 -428
- package/dist/node/ui/modals/index.js +1 -638
- package/dist/node/ui/overlays/demo-overlays.js +1 -55
- package/dist/node/ui/overlays/index.js +1 -55
- package/dist/node/ui/renderers/index.js +4 -849
- package/dist/node/ui/renderers/pipeline.markdown.js +4 -575
- package/dist/node/ui/renderers/pipeline.renderer.js +1 -275
- package/dist/node/ui/tables/DealListTab.js +1 -390
- package/dist/operations/index.js +1 -407
- package/dist/presentations/dashboard.presentation.js +1 -55
- package/dist/presentations/index.js +1 -290
- package/dist/presentations/pipeline.presentation.js +1 -236
- package/dist/seeders/index.js +1 -22
- package/dist/ui/CrmDashboard.js +1 -1547
- package/dist/ui/CrmDealCard.js +1 -50
- package/dist/ui/CrmPipelineBoard.js +1 -160
- package/dist/ui/hooks/index.js +1 -197
- package/dist/ui/hooks/useDealList.js +1 -95
- package/dist/ui/hooks/useDealMutations.js +1 -100
- package/dist/ui/index.js +4 -2205
- package/dist/ui/modals/CreateDealModal.js +1 -211
- package/dist/ui/modals/DealActionsModal.js +1 -428
- package/dist/ui/modals/index.js +1 -638
- package/dist/ui/overlays/demo-overlays.js +1 -55
- package/dist/ui/overlays/index.js +1 -55
- package/dist/ui/renderers/index.js +4 -849
- package/dist/ui/renderers/pipeline.markdown.js +4 -575
- package/dist/ui/renderers/pipeline.renderer.js +1 -275
- package/dist/ui/tables/DealListTab.js +1 -390
- package/package.json +16 -16
|
@@ -1,50 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { jsxDEV } from "react/jsx-dev-runtime";
|
|
3
|
-
"use client";
|
|
4
|
-
function formatCurrency(value, currency) {
|
|
5
|
-
return new Intl.NumberFormat("en-US", {
|
|
6
|
-
style: "currency",
|
|
7
|
-
currency,
|
|
8
|
-
minimumFractionDigits: 0,
|
|
9
|
-
maximumFractionDigits: 0
|
|
10
|
-
}).format(value);
|
|
11
|
-
}
|
|
12
|
-
function CrmDealCard({ deal, onClick }) {
|
|
13
|
-
const daysUntilClose = deal.expectedCloseDate ? Math.ceil((deal.expectedCloseDate.getTime() - Date.now()) / (1000 * 60 * 60 * 24)) : null;
|
|
14
|
-
return /* @__PURE__ */ jsxDEV("div", {
|
|
15
|
-
onClick,
|
|
16
|
-
className: "cursor-pointer rounded-lg border border-border bg-card p-3 shadow-sm transition-shadow hover:shadow-md",
|
|
17
|
-
role: "button",
|
|
18
|
-
tabIndex: 0,
|
|
19
|
-
onKeyDown: (e) => {
|
|
20
|
-
if (e.key === "Enter" || e.key === " ")
|
|
21
|
-
onClick?.();
|
|
22
|
-
},
|
|
23
|
-
children: [
|
|
24
|
-
/* @__PURE__ */ jsxDEV("h4", {
|
|
25
|
-
className: "font-medium leading-snug",
|
|
26
|
-
children: deal.name
|
|
27
|
-
}, undefined, false, undefined, this),
|
|
28
|
-
/* @__PURE__ */ jsxDEV("div", {
|
|
29
|
-
className: "mt-2 font-semibold text-lg text-primary",
|
|
30
|
-
children: formatCurrency(deal.value, deal.currency)
|
|
31
|
-
}, undefined, false, undefined, this),
|
|
32
|
-
/* @__PURE__ */ jsxDEV("div", {
|
|
33
|
-
className: "mt-3 flex items-center justify-between text-muted-foreground text-xs",
|
|
34
|
-
children: [
|
|
35
|
-
daysUntilClose !== null && /* @__PURE__ */ jsxDEV("span", {
|
|
36
|
-
className: daysUntilClose < 0 ? "text-red-500" : daysUntilClose <= 7 ? "text-yellow-600 dark:text-yellow-500" : "",
|
|
37
|
-
children: daysUntilClose < 0 ? `${Math.abs(daysUntilClose)}d overdue` : daysUntilClose === 0 ? "Due today" : `${daysUntilClose}d left`
|
|
38
|
-
}, undefined, false, undefined, this),
|
|
39
|
-
/* @__PURE__ */ jsxDEV("span", {
|
|
40
|
-
className: `rounded px-1.5 py-0.5 font-medium text-xs ${deal.status === "WON" ? "bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400" : deal.status === "LOST" ? "bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400" : "bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400"}`,
|
|
41
|
-
children: deal.status
|
|
42
|
-
}, undefined, false, undefined, this)
|
|
43
|
-
]
|
|
44
|
-
}, undefined, true, undefined, this)
|
|
45
|
-
]
|
|
46
|
-
}, undefined, true, undefined, this);
|
|
47
|
-
}
|
|
48
|
-
export {
|
|
49
|
-
CrmDealCard
|
|
50
|
-
};
|
|
1
|
+
import{jsx as z,jsxs as F}from"react/jsx-runtime";function G(h,w){return new Intl.NumberFormat("en-US",{style:"currency",currency:w,minimumFractionDigits:0,maximumFractionDigits:0}).format(h)}function H({deal:h,onClick:w}){let q=h.expectedCloseDate?Math.ceil((h.expectedCloseDate.getTime()-Date.now())/86400000):null;return F("div",{onClick:w,className:"cursor-pointer rounded-lg border border-border bg-card p-3 shadow-sm transition-shadow hover:shadow-md",role:"button",tabIndex:0,onKeyDown:(A)=>{if(A.key==="Enter"||A.key===" ")w?.()},children:[z("h4",{className:"font-medium leading-snug",children:h.name}),z("div",{className:"mt-2 font-semibold text-lg text-primary",children:G(h.value,h.currency)}),F("div",{className:"mt-3 flex items-center justify-between text-muted-foreground text-xs",children:[q!==null&&z("span",{className:q<0?"text-red-500":q<=7?"text-yellow-600 dark:text-yellow-500":"",children:q<0?`${Math.abs(q)}d overdue`:q===0?"Due today":`${q}d left`}),z("span",{className:`rounded px-1.5 py-0.5 font-medium text-xs ${h.status==="WON"?"bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400":h.status==="LOST"?"bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400":"bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400"}`,children:h.status})]})]})}export{H as CrmDealCard};
|
|
@@ -1,160 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { jsxDEV } from "react/jsx-dev-runtime";
|
|
3
|
-
"use client";
|
|
4
|
-
function formatCurrency(value, currency) {
|
|
5
|
-
return new Intl.NumberFormat("en-US", {
|
|
6
|
-
style: "currency",
|
|
7
|
-
currency,
|
|
8
|
-
minimumFractionDigits: 0,
|
|
9
|
-
maximumFractionDigits: 0
|
|
10
|
-
}).format(value);
|
|
11
|
-
}
|
|
12
|
-
function CrmDealCard({ deal, onClick }) {
|
|
13
|
-
const daysUntilClose = deal.expectedCloseDate ? Math.ceil((deal.expectedCloseDate.getTime() - Date.now()) / (1000 * 60 * 60 * 24)) : null;
|
|
14
|
-
return /* @__PURE__ */ jsxDEV("div", {
|
|
15
|
-
onClick,
|
|
16
|
-
className: "cursor-pointer rounded-lg border border-border bg-card p-3 shadow-sm transition-shadow hover:shadow-md",
|
|
17
|
-
role: "button",
|
|
18
|
-
tabIndex: 0,
|
|
19
|
-
onKeyDown: (e) => {
|
|
20
|
-
if (e.key === "Enter" || e.key === " ")
|
|
21
|
-
onClick?.();
|
|
22
|
-
},
|
|
23
|
-
children: [
|
|
24
|
-
/* @__PURE__ */ jsxDEV("h4", {
|
|
25
|
-
className: "font-medium leading-snug",
|
|
26
|
-
children: deal.name
|
|
27
|
-
}, undefined, false, undefined, this),
|
|
28
|
-
/* @__PURE__ */ jsxDEV("div", {
|
|
29
|
-
className: "mt-2 font-semibold text-lg text-primary",
|
|
30
|
-
children: formatCurrency(deal.value, deal.currency)
|
|
31
|
-
}, undefined, false, undefined, this),
|
|
32
|
-
/* @__PURE__ */ jsxDEV("div", {
|
|
33
|
-
className: "mt-3 flex items-center justify-between text-muted-foreground text-xs",
|
|
34
|
-
children: [
|
|
35
|
-
daysUntilClose !== null && /* @__PURE__ */ jsxDEV("span", {
|
|
36
|
-
className: daysUntilClose < 0 ? "text-red-500" : daysUntilClose <= 7 ? "text-yellow-600 dark:text-yellow-500" : "",
|
|
37
|
-
children: daysUntilClose < 0 ? `${Math.abs(daysUntilClose)}d overdue` : daysUntilClose === 0 ? "Due today" : `${daysUntilClose}d left`
|
|
38
|
-
}, undefined, false, undefined, this),
|
|
39
|
-
/* @__PURE__ */ jsxDEV("span", {
|
|
40
|
-
className: `rounded px-1.5 py-0.5 font-medium text-xs ${deal.status === "WON" ? "bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400" : deal.status === "LOST" ? "bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400" : "bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400"}`,
|
|
41
|
-
children: deal.status
|
|
42
|
-
}, undefined, false, undefined, this)
|
|
43
|
-
]
|
|
44
|
-
}, undefined, true, undefined, this)
|
|
45
|
-
]
|
|
46
|
-
}, undefined, true, undefined, this);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// src/ui/CrmPipelineBoard.tsx
|
|
50
|
-
import { useState } from "react";
|
|
51
|
-
import { jsxDEV as jsxDEV2 } from "react/jsx-dev-runtime";
|
|
52
|
-
"use client";
|
|
53
|
-
function formatCurrency2(value) {
|
|
54
|
-
if (value >= 1e6)
|
|
55
|
-
return `$${(value / 1e6).toFixed(1)}M`;
|
|
56
|
-
if (value >= 1000)
|
|
57
|
-
return `$${(value / 1000).toFixed(0)}K`;
|
|
58
|
-
return `$${value}`;
|
|
59
|
-
}
|
|
60
|
-
function CrmPipelineBoard({
|
|
61
|
-
dealsByStage,
|
|
62
|
-
stages,
|
|
63
|
-
onDealClick,
|
|
64
|
-
onDealMove
|
|
65
|
-
}) {
|
|
66
|
-
const [quickMoveOpen, setQuickMoveOpen] = useState(null);
|
|
67
|
-
const sortedStages = [...stages].sort((a, b) => a.position - b.position);
|
|
68
|
-
const handleQuickMove = (dealId, toStageId) => {
|
|
69
|
-
onDealMove?.(dealId, toStageId);
|
|
70
|
-
setQuickMoveOpen(null);
|
|
71
|
-
};
|
|
72
|
-
return /* @__PURE__ */ jsxDEV2("div", {
|
|
73
|
-
className: "flex gap-4 overflow-x-auto pb-4",
|
|
74
|
-
children: sortedStages.map((stage) => {
|
|
75
|
-
const deals = dealsByStage[stage.id] ?? [];
|
|
76
|
-
const stageValue = deals.reduce((sum, d) => sum + d.value, 0);
|
|
77
|
-
return /* @__PURE__ */ jsxDEV2("div", {
|
|
78
|
-
className: "flex w-72 flex-shrink-0 flex-col rounded-lg bg-muted/30",
|
|
79
|
-
children: [
|
|
80
|
-
/* @__PURE__ */ jsxDEV2("div", {
|
|
81
|
-
className: "flex items-center justify-between border-border border-b px-3 py-2",
|
|
82
|
-
children: [
|
|
83
|
-
/* @__PURE__ */ jsxDEV2("div", {
|
|
84
|
-
children: [
|
|
85
|
-
/* @__PURE__ */ jsxDEV2("h3", {
|
|
86
|
-
className: "font-medium",
|
|
87
|
-
children: stage.name
|
|
88
|
-
}, undefined, false, undefined, this),
|
|
89
|
-
/* @__PURE__ */ jsxDEV2("p", {
|
|
90
|
-
className: "text-muted-foreground text-xs",
|
|
91
|
-
children: [
|
|
92
|
-
deals.length,
|
|
93
|
-
" deals · ",
|
|
94
|
-
formatCurrency2(stageValue)
|
|
95
|
-
]
|
|
96
|
-
}, undefined, true, undefined, this)
|
|
97
|
-
]
|
|
98
|
-
}, undefined, true, undefined, this),
|
|
99
|
-
/* @__PURE__ */ jsxDEV2("span", {
|
|
100
|
-
className: "flex h-6 w-6 items-center justify-center rounded-full bg-muted font-medium text-xs",
|
|
101
|
-
children: deals.length
|
|
102
|
-
}, undefined, false, undefined, this)
|
|
103
|
-
]
|
|
104
|
-
}, undefined, true, undefined, this),
|
|
105
|
-
/* @__PURE__ */ jsxDEV2("div", {
|
|
106
|
-
className: "flex flex-1 flex-col gap-2 p-2",
|
|
107
|
-
children: deals.length === 0 ? /* @__PURE__ */ jsxDEV2("div", {
|
|
108
|
-
className: "flex h-24 items-center justify-center rounded-md border-2 border-muted-foreground/20 border-dashed text-muted-foreground text-xs",
|
|
109
|
-
children: "No deals"
|
|
110
|
-
}, undefined, false, undefined, this) : deals.map((deal) => /* @__PURE__ */ jsxDEV2("div", {
|
|
111
|
-
className: "group relative",
|
|
112
|
-
children: [
|
|
113
|
-
/* @__PURE__ */ jsxDEV2(CrmDealCard, {
|
|
114
|
-
deal,
|
|
115
|
-
onClick: () => onDealClick?.(deal.id)
|
|
116
|
-
}, undefined, false, undefined, this),
|
|
117
|
-
deal.status === "OPEN" && onDealMove && /* @__PURE__ */ jsxDEV2("div", {
|
|
118
|
-
className: "absolute top-1 right-1 opacity-0 transition-opacity group-hover:opacity-100",
|
|
119
|
-
children: [
|
|
120
|
-
/* @__PURE__ */ jsxDEV2("button", {
|
|
121
|
-
type: "button",
|
|
122
|
-
onClick: (e) => {
|
|
123
|
-
e.stopPropagation();
|
|
124
|
-
setQuickMoveOpen(quickMoveOpen === deal.id ? null : deal.id);
|
|
125
|
-
},
|
|
126
|
-
className: "flex h-6 w-6 items-center justify-center rounded border border-border bg-background text-xs shadow-sm hover:bg-muted",
|
|
127
|
-
title: "Quick move",
|
|
128
|
-
children: "➡️"
|
|
129
|
-
}, undefined, false, undefined, this),
|
|
130
|
-
quickMoveOpen === deal.id && /* @__PURE__ */ jsxDEV2("div", {
|
|
131
|
-
className: "absolute top-7 right-0 z-20 min-w-[140px] rounded-lg border border-border bg-card py-1 shadow-lg",
|
|
132
|
-
children: [
|
|
133
|
-
/* @__PURE__ */ jsxDEV2("p", {
|
|
134
|
-
className: "px-3 py-1 font-medium text-muted-foreground text-xs",
|
|
135
|
-
children: "Move to:"
|
|
136
|
-
}, undefined, false, undefined, this),
|
|
137
|
-
sortedStages.filter((s) => s.id !== deal.stageId).map((s) => /* @__PURE__ */ jsxDEV2("button", {
|
|
138
|
-
type: "button",
|
|
139
|
-
onClick: (e) => {
|
|
140
|
-
e.stopPropagation();
|
|
141
|
-
handleQuickMove(deal.id, s.id);
|
|
142
|
-
},
|
|
143
|
-
className: "w-full px-3 py-1.5 text-left text-sm hover:bg-muted",
|
|
144
|
-
children: s.name
|
|
145
|
-
}, s.id, false, undefined, this))
|
|
146
|
-
]
|
|
147
|
-
}, undefined, true, undefined, this)
|
|
148
|
-
]
|
|
149
|
-
}, undefined, true, undefined, this)
|
|
150
|
-
]
|
|
151
|
-
}, deal.id, true, undefined, this))
|
|
152
|
-
}, undefined, false, undefined, this)
|
|
153
|
-
]
|
|
154
|
-
}, stage.id, true, undefined, this);
|
|
155
|
-
})
|
|
156
|
-
}, undefined, false, undefined, this);
|
|
157
|
-
}
|
|
158
|
-
export {
|
|
159
|
-
CrmPipelineBoard
|
|
160
|
-
};
|
|
1
|
+
import{jsx as Y,jsxs as E}from"react/jsx-runtime";function V(z,W){return new Intl.NumberFormat("en-US",{style:"currency",currency:W,minimumFractionDigits:0,maximumFractionDigits:0}).format(z)}function H({deal:z,onClick:W}){let F=z.expectedCloseDate?Math.ceil((z.expectedCloseDate.getTime()-Date.now())/86400000):null;return E("div",{onClick:W,className:"cursor-pointer rounded-lg border border-border bg-card p-3 shadow-sm transition-shadow hover:shadow-md",role:"button",tabIndex:0,onKeyDown:(X)=>{if(X.key==="Enter"||X.key===" ")W?.()},children:[Y("h4",{className:"font-medium leading-snug",children:z.name}),Y("div",{className:"mt-2 font-semibold text-lg text-primary",children:V(z.value,z.currency)}),E("div",{className:"mt-3 flex items-center justify-between text-muted-foreground text-xs",children:[F!==null&&Y("span",{className:F<0?"text-red-500":F<=7?"text-yellow-600 dark:text-yellow-500":"",children:F<0?`${Math.abs(F)}d overdue`:F===0?"Due today":`${F}d left`}),Y("span",{className:`rounded px-1.5 py-0.5 font-medium text-xs ${z.status==="WON"?"bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400":z.status==="LOST"?"bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400":"bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400"}`,children:z.status})]})]})}import{useState as q}from"react";import{jsx as K,jsxs as P}from"react/jsx-runtime";function w(z){if(z>=1e6)return`$${(z/1e6).toFixed(1)}M`;if(z>=1000)return`$${(z/1000).toFixed(0)}K`;return`$${z}`}function h({dealsByStage:z,stages:W,onDealClick:F,onDealMove:X}){let[Z,_]=q(null),$=[...W].sort((L,G)=>L.position-G.position),N=(L,G)=>{X?.(L,G),_(null)};return K("div",{className:"flex gap-4 overflow-x-auto pb-4",children:$.map((L)=>{let G=z[L.id]??[],R=G.reduce((A,J)=>A+J.value,0);return P("div",{className:"flex w-72 flex-shrink-0 flex-col rounded-lg bg-muted/30",children:[P("div",{className:"flex items-center justify-between border-border border-b px-3 py-2",children:[P("div",{children:[K("h3",{className:"font-medium",children:L.name}),P("p",{className:"text-muted-foreground text-xs",children:[G.length," deals · ",w(R)]})]}),K("span",{className:"flex h-6 w-6 items-center justify-center rounded-full bg-muted font-medium text-xs",children:G.length})]}),K("div",{className:"flex flex-1 flex-col gap-2 p-2",children:G.length===0?K("div",{className:"flex h-24 items-center justify-center rounded-md border-2 border-muted-foreground/20 border-dashed text-muted-foreground text-xs",children:"No deals"}):G.map((A)=>P("div",{className:"group relative",children:[K(H,{deal:A,onClick:()=>F?.(A.id)}),A.status==="OPEN"&&X&&P("div",{className:"absolute top-1 right-1 opacity-0 transition-opacity group-hover:opacity-100",children:[K("button",{type:"button",onClick:(J)=>{J.stopPropagation(),_(Z===A.id?null:A.id)},className:"flex h-6 w-6 items-center justify-center rounded border border-border bg-background text-xs shadow-sm hover:bg-muted",title:"Quick move",children:"➡️"}),Z===A.id&&P("div",{className:"absolute top-7 right-0 z-20 min-w-[140px] rounded-lg border border-border bg-card py-1 shadow-lg",children:[K("p",{className:"px-3 py-1 font-medium text-muted-foreground text-xs",children:"Move to:"}),$.filter((J)=>J.id!==A.stageId).map((J)=>K("button",{type:"button",onClick:(T)=>{T.stopPropagation(),N(A.id,J.id)},className:"w-full px-3 py-1.5 text-left text-sm hover:bg-muted",children:J.name},J.id))]})]})]},A.id))})]},L.id)})})}export{h as CrmPipelineBoard};
|
|
@@ -1,197 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { useTemplateRuntime } from "@contractspec/lib.example-shared-ui";
|
|
3
|
-
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
4
|
-
"use client";
|
|
5
|
-
function useDealList(options = {}) {
|
|
6
|
-
const { handlers, projectId } = useTemplateRuntime();
|
|
7
|
-
const { crm } = handlers;
|
|
8
|
-
const [data, setData] = useState(null);
|
|
9
|
-
const [dealsByStage, setDealsByStage] = useState({});
|
|
10
|
-
const [stages, setStages] = useState([]);
|
|
11
|
-
const [loading, setLoading] = useState(true);
|
|
12
|
-
const [error, setError] = useState(null);
|
|
13
|
-
const [internalPage, setInternalPage] = useState(0);
|
|
14
|
-
const pipelineId = options.pipelineId ?? "pipeline-1";
|
|
15
|
-
const pageIndex = options.pageIndex ?? internalPage;
|
|
16
|
-
const pageSize = options.pageSize ?? options.limit ?? 50;
|
|
17
|
-
const [sort] = options.sorting ?? [];
|
|
18
|
-
const sortBy = sort?.id;
|
|
19
|
-
const sortDirection = sort ? sort.desc ? "desc" : "asc" : undefined;
|
|
20
|
-
const fetchData = useCallback(async () => {
|
|
21
|
-
setLoading(true);
|
|
22
|
-
setError(null);
|
|
23
|
-
try {
|
|
24
|
-
const [dealsResult, stageDealsResult, stagesResult] = await Promise.all([
|
|
25
|
-
crm.listDeals({
|
|
26
|
-
projectId,
|
|
27
|
-
pipelineId,
|
|
28
|
-
stageId: options.stageId,
|
|
29
|
-
status: options.status === "all" ? undefined : options.status,
|
|
30
|
-
search: options.search,
|
|
31
|
-
limit: pageSize,
|
|
32
|
-
offset: pageIndex * pageSize,
|
|
33
|
-
sortBy: sortBy === "name" || sortBy === "value" || sortBy === "status" || sortBy === "expectedCloseDate" || sortBy === "updatedAt" ? sortBy : undefined,
|
|
34
|
-
sortDirection
|
|
35
|
-
}),
|
|
36
|
-
crm.getDealsByStage({ projectId, pipelineId }),
|
|
37
|
-
crm.getPipelineStages({ pipelineId })
|
|
38
|
-
]);
|
|
39
|
-
setData(dealsResult);
|
|
40
|
-
setDealsByStage(stageDealsResult);
|
|
41
|
-
setStages(stagesResult);
|
|
42
|
-
} catch (err) {
|
|
43
|
-
setError(err instanceof Error ? err : new Error("Unknown error"));
|
|
44
|
-
} finally {
|
|
45
|
-
setLoading(false);
|
|
46
|
-
}
|
|
47
|
-
}, [
|
|
48
|
-
crm,
|
|
49
|
-
projectId,
|
|
50
|
-
pipelineId,
|
|
51
|
-
options.stageId,
|
|
52
|
-
options.status,
|
|
53
|
-
options.search,
|
|
54
|
-
pageIndex,
|
|
55
|
-
pageSize,
|
|
56
|
-
sortBy,
|
|
57
|
-
sortDirection
|
|
58
|
-
]);
|
|
59
|
-
useEffect(() => {
|
|
60
|
-
fetchData();
|
|
61
|
-
}, [fetchData]);
|
|
62
|
-
const stats = useMemo(() => {
|
|
63
|
-
if (!data)
|
|
64
|
-
return null;
|
|
65
|
-
const open = data.deals.filter((d) => d.status === "OPEN");
|
|
66
|
-
const won = data.deals.filter((d) => d.status === "WON");
|
|
67
|
-
const lost = data.deals.filter((d) => d.status === "LOST");
|
|
68
|
-
return {
|
|
69
|
-
total: data.total,
|
|
70
|
-
totalValue: data.totalValue,
|
|
71
|
-
openCount: open.length,
|
|
72
|
-
openValue: open.reduce((sum, d) => sum + d.value, 0),
|
|
73
|
-
wonCount: won.length,
|
|
74
|
-
wonValue: won.reduce((sum, d) => sum + d.value, 0),
|
|
75
|
-
lostCount: lost.length
|
|
76
|
-
};
|
|
77
|
-
}, [data]);
|
|
78
|
-
return {
|
|
79
|
-
data,
|
|
80
|
-
dealsByStage,
|
|
81
|
-
stages,
|
|
82
|
-
loading,
|
|
83
|
-
error,
|
|
84
|
-
stats,
|
|
85
|
-
page: pageIndex + 1,
|
|
86
|
-
pageIndex,
|
|
87
|
-
pageSize,
|
|
88
|
-
refetch: fetchData,
|
|
89
|
-
nextPage: options.pageIndex === undefined ? () => setInternalPage((page) => page + 1) : undefined,
|
|
90
|
-
prevPage: options.pageIndex === undefined ? () => pageIndex > 0 && setInternalPage((page) => page - 1) : undefined
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// src/ui/hooks/useDealMutations.ts
|
|
95
|
-
import { useTemplateRuntime as useTemplateRuntime2 } from "@contractspec/lib.example-shared-ui";
|
|
96
|
-
import { useCallback as useCallback2, useState as useState2 } from "react";
|
|
97
|
-
function useDealMutations(options = {}) {
|
|
98
|
-
const { handlers, projectId } = useTemplateRuntime2();
|
|
99
|
-
const { crm } = handlers;
|
|
100
|
-
const [createState, setCreateState] = useState2({
|
|
101
|
-
loading: false,
|
|
102
|
-
error: null,
|
|
103
|
-
data: null
|
|
104
|
-
});
|
|
105
|
-
const [moveState, setMoveState] = useState2({
|
|
106
|
-
loading: false,
|
|
107
|
-
error: null,
|
|
108
|
-
data: null
|
|
109
|
-
});
|
|
110
|
-
const [winState, setWinState] = useState2({
|
|
111
|
-
loading: false,
|
|
112
|
-
error: null,
|
|
113
|
-
data: null
|
|
114
|
-
});
|
|
115
|
-
const [loseState, setLoseState] = useState2({
|
|
116
|
-
loading: false,
|
|
117
|
-
error: null,
|
|
118
|
-
data: null
|
|
119
|
-
});
|
|
120
|
-
const createDeal = useCallback2(async (input) => {
|
|
121
|
-
setCreateState({ loading: true, error: null, data: null });
|
|
122
|
-
try {
|
|
123
|
-
const result = await crm.createDeal(input, {
|
|
124
|
-
projectId,
|
|
125
|
-
ownerId: "user-1"
|
|
126
|
-
});
|
|
127
|
-
setCreateState({ loading: false, error: null, data: result });
|
|
128
|
-
options.onSuccess?.();
|
|
129
|
-
return result;
|
|
130
|
-
} catch (err) {
|
|
131
|
-
const error = err instanceof Error ? err : new Error("Failed to create deal");
|
|
132
|
-
setCreateState({ loading: false, error, data: null });
|
|
133
|
-
options.onError?.(error);
|
|
134
|
-
return null;
|
|
135
|
-
}
|
|
136
|
-
}, [crm, projectId, options]);
|
|
137
|
-
const moveDeal = useCallback2(async (input) => {
|
|
138
|
-
setMoveState({ loading: true, error: null, data: null });
|
|
139
|
-
try {
|
|
140
|
-
const result = await crm.moveDeal(input);
|
|
141
|
-
setMoveState({ loading: false, error: null, data: result });
|
|
142
|
-
options.onSuccess?.();
|
|
143
|
-
return result;
|
|
144
|
-
} catch (err) {
|
|
145
|
-
const error = err instanceof Error ? err : new Error("Failed to move deal");
|
|
146
|
-
setMoveState({ loading: false, error, data: null });
|
|
147
|
-
options.onError?.(error);
|
|
148
|
-
return null;
|
|
149
|
-
}
|
|
150
|
-
}, [crm, options]);
|
|
151
|
-
const winDeal = useCallback2(async (input) => {
|
|
152
|
-
setWinState({ loading: true, error: null, data: null });
|
|
153
|
-
try {
|
|
154
|
-
const result = await crm.winDeal(input);
|
|
155
|
-
setWinState({ loading: false, error: null, data: result });
|
|
156
|
-
options.onSuccess?.();
|
|
157
|
-
return result;
|
|
158
|
-
} catch (err) {
|
|
159
|
-
const error = err instanceof Error ? err : new Error("Failed to mark deal as won");
|
|
160
|
-
setWinState({ loading: false, error, data: null });
|
|
161
|
-
options.onError?.(error);
|
|
162
|
-
return null;
|
|
163
|
-
}
|
|
164
|
-
}, [crm, options]);
|
|
165
|
-
const loseDeal = useCallback2(async (input) => {
|
|
166
|
-
setLoseState({ loading: true, error: null, data: null });
|
|
167
|
-
try {
|
|
168
|
-
const result = await crm.loseDeal(input);
|
|
169
|
-
setLoseState({ loading: false, error: null, data: result });
|
|
170
|
-
options.onSuccess?.();
|
|
171
|
-
return result;
|
|
172
|
-
} catch (err) {
|
|
173
|
-
const error = err instanceof Error ? err : new Error("Failed to mark deal as lost");
|
|
174
|
-
setLoseState({ loading: false, error, data: null });
|
|
175
|
-
options.onError?.(error);
|
|
176
|
-
return null;
|
|
177
|
-
}
|
|
178
|
-
}, [crm, options]);
|
|
179
|
-
return {
|
|
180
|
-
createDeal,
|
|
181
|
-
moveDeal,
|
|
182
|
-
winDeal,
|
|
183
|
-
loseDeal,
|
|
184
|
-
createState,
|
|
185
|
-
moveState,
|
|
186
|
-
winState,
|
|
187
|
-
loseState,
|
|
188
|
-
isLoading: createState.loading || moveState.loading || winState.loading || loseState.loading
|
|
189
|
-
};
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// src/ui/hooks/index.ts
|
|
193
|
-
"use client";
|
|
194
|
-
export {
|
|
195
|
-
useDealMutations,
|
|
196
|
-
useDealList
|
|
197
|
-
};
|
|
1
|
+
import{useTemplateRuntime as x}from"@contractspec/lib.example-shared-ui";import{useCallback as B,useEffect as C,useMemo as y,useState as V}from"react";function D(A={}){let{handlers:k,projectId:Q}=x(),{crm:G}=k,[J,X]=V(null),[_,Y]=V({}),[$,Z]=V([]),[T,U]=V(!0),[w,E]=V(null),[M,P]=V(0),H=A.pipelineId??"pipeline-1",q=A.pageIndex??M,F=A.pageSize??A.limit??50,[R]=A.sorting??[],O=R?.id,b=R?R.desc?"desc":"asc":void 0,f=B(async()=>{U(!0),E(null);try{let[K,W,v]=await Promise.all([G.listDeals({projectId:Q,pipelineId:H,stageId:A.stageId,status:A.status==="all"?void 0:A.status,search:A.search,limit:F,offset:q*F,sortBy:O==="name"||O==="value"||O==="status"||O==="expectedCloseDate"||O==="updatedAt"?O:void 0,sortDirection:b}),G.getDealsByStage({projectId:Q,pipelineId:H}),G.getPipelineStages({pipelineId:H})]);X(K),Y(W),Z(v)}catch(K){E(K instanceof Error?K:Error("Unknown error"))}finally{U(!1)}},[G,Q,H,A.stageId,A.status,A.search,q,F,O,b]);C(()=>{f()},[f]);let h=y(()=>{if(!J)return null;let K=J.deals.filter((N)=>N.status==="OPEN"),W=J.deals.filter((N)=>N.status==="WON"),v=J.deals.filter((N)=>N.status==="LOST");return{total:J.total,totalValue:J.totalValue,openCount:K.length,openValue:K.reduce((N,L)=>N+L.value,0),wonCount:W.length,wonValue:W.reduce((N,L)=>N+L.value,0),lostCount:v.length}},[J]);return{data:J,dealsByStage:_,stages:$,loading:T,error:w,stats:h,page:q+1,pageIndex:q,pageSize:F,refetch:f,nextPage:A.pageIndex===void 0?()=>P((K)=>K+1):void 0,prevPage:A.pageIndex===void 0?()=>q>0&&P((K)=>K-1):void 0}}import{useTemplateRuntime as I}from"@contractspec/lib.example-shared-ui";import{useCallback as z,useState as j}from"react";function g(A={}){let{handlers:k,projectId:Q}=I(),{crm:G}=k,[J,X]=j({loading:!1,error:null,data:null}),[_,Y]=j({loading:!1,error:null,data:null}),[$,Z]=j({loading:!1,error:null,data:null}),[T,U]=j({loading:!1,error:null,data:null}),w=z(async(H)=>{X({loading:!0,error:null,data:null});try{let q=await G.createDeal(H,{projectId:Q,ownerId:"user-1"});return X({loading:!1,error:null,data:q}),A.onSuccess?.(),q}catch(q){let F=q instanceof Error?q:Error("Failed to create deal");return X({loading:!1,error:F,data:null}),A.onError?.(F),null}},[G,Q,A]),E=z(async(H)=>{Y({loading:!0,error:null,data:null});try{let q=await G.moveDeal(H);return Y({loading:!1,error:null,data:q}),A.onSuccess?.(),q}catch(q){let F=q instanceof Error?q:Error("Failed to move deal");return Y({loading:!1,error:F,data:null}),A.onError?.(F),null}},[G,A]),M=z(async(H)=>{Z({loading:!0,error:null,data:null});try{let q=await G.winDeal(H);return Z({loading:!1,error:null,data:q}),A.onSuccess?.(),q}catch(q){let F=q instanceof Error?q:Error("Failed to mark deal as won");return Z({loading:!1,error:F,data:null}),A.onError?.(F),null}},[G,A]),P=z(async(H)=>{U({loading:!0,error:null,data:null});try{let q=await G.loseDeal(H);return U({loading:!1,error:null,data:q}),A.onSuccess?.(),q}catch(q){let F=q instanceof Error?q:Error("Failed to mark deal as lost");return U({loading:!1,error:F,data:null}),A.onError?.(F),null}},[G,A]);return{createDeal:w,moveDeal:E,winDeal:M,loseDeal:P,createState:J,moveState:_,winState:$,loseState:T,isLoading:J.loading||_.loading||$.loading||T.loading}}export{g as useDealMutations,D as useDealList};
|
|
@@ -1,95 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { useTemplateRuntime } from "@contractspec/lib.example-shared-ui";
|
|
3
|
-
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
4
|
-
"use client";
|
|
5
|
-
function useDealList(options = {}) {
|
|
6
|
-
const { handlers, projectId } = useTemplateRuntime();
|
|
7
|
-
const { crm } = handlers;
|
|
8
|
-
const [data, setData] = useState(null);
|
|
9
|
-
const [dealsByStage, setDealsByStage] = useState({});
|
|
10
|
-
const [stages, setStages] = useState([]);
|
|
11
|
-
const [loading, setLoading] = useState(true);
|
|
12
|
-
const [error, setError] = useState(null);
|
|
13
|
-
const [internalPage, setInternalPage] = useState(0);
|
|
14
|
-
const pipelineId = options.pipelineId ?? "pipeline-1";
|
|
15
|
-
const pageIndex = options.pageIndex ?? internalPage;
|
|
16
|
-
const pageSize = options.pageSize ?? options.limit ?? 50;
|
|
17
|
-
const [sort] = options.sorting ?? [];
|
|
18
|
-
const sortBy = sort?.id;
|
|
19
|
-
const sortDirection = sort ? sort.desc ? "desc" : "asc" : undefined;
|
|
20
|
-
const fetchData = useCallback(async () => {
|
|
21
|
-
setLoading(true);
|
|
22
|
-
setError(null);
|
|
23
|
-
try {
|
|
24
|
-
const [dealsResult, stageDealsResult, stagesResult] = await Promise.all([
|
|
25
|
-
crm.listDeals({
|
|
26
|
-
projectId,
|
|
27
|
-
pipelineId,
|
|
28
|
-
stageId: options.stageId,
|
|
29
|
-
status: options.status === "all" ? undefined : options.status,
|
|
30
|
-
search: options.search,
|
|
31
|
-
limit: pageSize,
|
|
32
|
-
offset: pageIndex * pageSize,
|
|
33
|
-
sortBy: sortBy === "name" || sortBy === "value" || sortBy === "status" || sortBy === "expectedCloseDate" || sortBy === "updatedAt" ? sortBy : undefined,
|
|
34
|
-
sortDirection
|
|
35
|
-
}),
|
|
36
|
-
crm.getDealsByStage({ projectId, pipelineId }),
|
|
37
|
-
crm.getPipelineStages({ pipelineId })
|
|
38
|
-
]);
|
|
39
|
-
setData(dealsResult);
|
|
40
|
-
setDealsByStage(stageDealsResult);
|
|
41
|
-
setStages(stagesResult);
|
|
42
|
-
} catch (err) {
|
|
43
|
-
setError(err instanceof Error ? err : new Error("Unknown error"));
|
|
44
|
-
} finally {
|
|
45
|
-
setLoading(false);
|
|
46
|
-
}
|
|
47
|
-
}, [
|
|
48
|
-
crm,
|
|
49
|
-
projectId,
|
|
50
|
-
pipelineId,
|
|
51
|
-
options.stageId,
|
|
52
|
-
options.status,
|
|
53
|
-
options.search,
|
|
54
|
-
pageIndex,
|
|
55
|
-
pageSize,
|
|
56
|
-
sortBy,
|
|
57
|
-
sortDirection
|
|
58
|
-
]);
|
|
59
|
-
useEffect(() => {
|
|
60
|
-
fetchData();
|
|
61
|
-
}, [fetchData]);
|
|
62
|
-
const stats = useMemo(() => {
|
|
63
|
-
if (!data)
|
|
64
|
-
return null;
|
|
65
|
-
const open = data.deals.filter((d) => d.status === "OPEN");
|
|
66
|
-
const won = data.deals.filter((d) => d.status === "WON");
|
|
67
|
-
const lost = data.deals.filter((d) => d.status === "LOST");
|
|
68
|
-
return {
|
|
69
|
-
total: data.total,
|
|
70
|
-
totalValue: data.totalValue,
|
|
71
|
-
openCount: open.length,
|
|
72
|
-
openValue: open.reduce((sum, d) => sum + d.value, 0),
|
|
73
|
-
wonCount: won.length,
|
|
74
|
-
wonValue: won.reduce((sum, d) => sum + d.value, 0),
|
|
75
|
-
lostCount: lost.length
|
|
76
|
-
};
|
|
77
|
-
}, [data]);
|
|
78
|
-
return {
|
|
79
|
-
data,
|
|
80
|
-
dealsByStage,
|
|
81
|
-
stages,
|
|
82
|
-
loading,
|
|
83
|
-
error,
|
|
84
|
-
stats,
|
|
85
|
-
page: pageIndex + 1,
|
|
86
|
-
pageIndex,
|
|
87
|
-
pageSize,
|
|
88
|
-
refetch: fetchData,
|
|
89
|
-
nextPage: options.pageIndex === undefined ? () => setInternalPage((page) => page + 1) : undefined,
|
|
90
|
-
prevPage: options.pageIndex === undefined ? () => pageIndex > 0 && setInternalPage((page) => page - 1) : undefined
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
export {
|
|
94
|
-
useDealList
|
|
95
|
-
};
|
|
1
|
+
import{useTemplateRuntime as R}from"@contractspec/lib.example-shared-ui";import{useCallback as f,useEffect as h,useMemo as x,useState as H}from"react";function B(q={}){let{handlers:C,projectId:U}=R(),{crm:K}=C,[F,M]=H(null),[T,b]=H({}),[w,L]=H([]),[j,Z]=H(!0),[E,_]=H(null),[P,$]=H(0),N=q.pipelineId??"pipeline-1",J=q.pageIndex??P,O=q.pageSize??q.limit??50,[V]=q.sorting??[],G=V?.id,k=V?V.desc?"desc":"asc":void 0,W=f(async()=>{Z(!0),_(null);try{let[v,Q,X]=await Promise.all([K.listDeals({projectId:U,pipelineId:N,stageId:q.stageId,status:q.status==="all"?void 0:q.status,search:q.search,limit:O,offset:J*O,sortBy:G==="name"||G==="value"||G==="status"||G==="expectedCloseDate"||G==="updatedAt"?G:void 0,sortDirection:k}),K.getDealsByStage({projectId:U,pipelineId:N}),K.getPipelineStages({pipelineId:N})]);M(v),b(Q),L(X)}catch(v){_(v instanceof Error?v:Error("Unknown error"))}finally{Z(!1)}},[K,U,N,q.stageId,q.status,q.search,J,O,G,k]);h(()=>{W()},[W]);let z=x(()=>{if(!F)return null;let v=F.deals.filter((A)=>A.status==="OPEN"),Q=F.deals.filter((A)=>A.status==="WON"),X=F.deals.filter((A)=>A.status==="LOST");return{total:F.total,totalValue:F.totalValue,openCount:v.length,openValue:v.reduce((A,Y)=>A+Y.value,0),wonCount:Q.length,wonValue:Q.reduce((A,Y)=>A+Y.value,0),lostCount:X.length}},[F]);return{data:F,dealsByStage:T,stages:w,loading:j,error:E,stats:z,page:J+1,pageIndex:J,pageSize:O,refetch:W,nextPage:q.pageIndex===void 0?()=>$((v)=>v+1):void 0,prevPage:q.pageIndex===void 0?()=>J>0&&$((v)=>v-1):void 0}}export{B as useDealList};
|
|
@@ -1,100 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { useTemplateRuntime } from "@contractspec/lib.example-shared-ui";
|
|
3
|
-
import { useCallback, useState } from "react";
|
|
4
|
-
function useDealMutations(options = {}) {
|
|
5
|
-
const { handlers, projectId } = useTemplateRuntime();
|
|
6
|
-
const { crm } = handlers;
|
|
7
|
-
const [createState, setCreateState] = useState({
|
|
8
|
-
loading: false,
|
|
9
|
-
error: null,
|
|
10
|
-
data: null
|
|
11
|
-
});
|
|
12
|
-
const [moveState, setMoveState] = useState({
|
|
13
|
-
loading: false,
|
|
14
|
-
error: null,
|
|
15
|
-
data: null
|
|
16
|
-
});
|
|
17
|
-
const [winState, setWinState] = useState({
|
|
18
|
-
loading: false,
|
|
19
|
-
error: null,
|
|
20
|
-
data: null
|
|
21
|
-
});
|
|
22
|
-
const [loseState, setLoseState] = useState({
|
|
23
|
-
loading: false,
|
|
24
|
-
error: null,
|
|
25
|
-
data: null
|
|
26
|
-
});
|
|
27
|
-
const createDeal = useCallback(async (input) => {
|
|
28
|
-
setCreateState({ loading: true, error: null, data: null });
|
|
29
|
-
try {
|
|
30
|
-
const result = await crm.createDeal(input, {
|
|
31
|
-
projectId,
|
|
32
|
-
ownerId: "user-1"
|
|
33
|
-
});
|
|
34
|
-
setCreateState({ loading: false, error: null, data: result });
|
|
35
|
-
options.onSuccess?.();
|
|
36
|
-
return result;
|
|
37
|
-
} catch (err) {
|
|
38
|
-
const error = err instanceof Error ? err : new Error("Failed to create deal");
|
|
39
|
-
setCreateState({ loading: false, error, data: null });
|
|
40
|
-
options.onError?.(error);
|
|
41
|
-
return null;
|
|
42
|
-
}
|
|
43
|
-
}, [crm, projectId, options]);
|
|
44
|
-
const moveDeal = useCallback(async (input) => {
|
|
45
|
-
setMoveState({ loading: true, error: null, data: null });
|
|
46
|
-
try {
|
|
47
|
-
const result = await crm.moveDeal(input);
|
|
48
|
-
setMoveState({ loading: false, error: null, data: result });
|
|
49
|
-
options.onSuccess?.();
|
|
50
|
-
return result;
|
|
51
|
-
} catch (err) {
|
|
52
|
-
const error = err instanceof Error ? err : new Error("Failed to move deal");
|
|
53
|
-
setMoveState({ loading: false, error, data: null });
|
|
54
|
-
options.onError?.(error);
|
|
55
|
-
return null;
|
|
56
|
-
}
|
|
57
|
-
}, [crm, options]);
|
|
58
|
-
const winDeal = useCallback(async (input) => {
|
|
59
|
-
setWinState({ loading: true, error: null, data: null });
|
|
60
|
-
try {
|
|
61
|
-
const result = await crm.winDeal(input);
|
|
62
|
-
setWinState({ loading: false, error: null, data: result });
|
|
63
|
-
options.onSuccess?.();
|
|
64
|
-
return result;
|
|
65
|
-
} catch (err) {
|
|
66
|
-
const error = err instanceof Error ? err : new Error("Failed to mark deal as won");
|
|
67
|
-
setWinState({ loading: false, error, data: null });
|
|
68
|
-
options.onError?.(error);
|
|
69
|
-
return null;
|
|
70
|
-
}
|
|
71
|
-
}, [crm, options]);
|
|
72
|
-
const loseDeal = useCallback(async (input) => {
|
|
73
|
-
setLoseState({ loading: true, error: null, data: null });
|
|
74
|
-
try {
|
|
75
|
-
const result = await crm.loseDeal(input);
|
|
76
|
-
setLoseState({ loading: false, error: null, data: result });
|
|
77
|
-
options.onSuccess?.();
|
|
78
|
-
return result;
|
|
79
|
-
} catch (err) {
|
|
80
|
-
const error = err instanceof Error ? err : new Error("Failed to mark deal as lost");
|
|
81
|
-
setLoseState({ loading: false, error, data: null });
|
|
82
|
-
options.onError?.(error);
|
|
83
|
-
return null;
|
|
84
|
-
}
|
|
85
|
-
}, [crm, options]);
|
|
86
|
-
return {
|
|
87
|
-
createDeal,
|
|
88
|
-
moveDeal,
|
|
89
|
-
winDeal,
|
|
90
|
-
loseDeal,
|
|
91
|
-
createState,
|
|
92
|
-
moveState,
|
|
93
|
-
winState,
|
|
94
|
-
loseState,
|
|
95
|
-
isLoading: createState.loading || moveState.loading || winState.loading || loseState.loading
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
export {
|
|
99
|
-
useDealMutations
|
|
100
|
-
};
|
|
1
|
+
import{useTemplateRuntime as Z}from"@contractspec/lib.example-shared-ui";import{useCallback as A,useState as B}from"react";function x(g={}){let{handlers:Q,projectId:J}=Z(),{crm:y}=Q,[K,E]=B({loading:!1,error:null,data:null}),[N,F]=B({loading:!1,error:null,data:null}),[O,G]=B({loading:!1,error:null,data:null}),[P,H]=B({loading:!1,error:null,data:null}),U=A(async(z)=>{E({loading:!0,error:null,data:null});try{let f=await y.createDeal(z,{projectId:J,ownerId:"user-1"});return E({loading:!1,error:null,data:f}),g.onSuccess?.(),f}catch(f){let q=f instanceof Error?f:Error("Failed to create deal");return E({loading:!1,error:q,data:null}),g.onError?.(q),null}},[y,J,g]),V=A(async(z)=>{F({loading:!0,error:null,data:null});try{let f=await y.moveDeal(z);return F({loading:!1,error:null,data:f}),g.onSuccess?.(),f}catch(f){let q=f instanceof Error?f:Error("Failed to move deal");return F({loading:!1,error:q,data:null}),g.onError?.(q),null}},[y,g]),X=A(async(z)=>{G({loading:!0,error:null,data:null});try{let f=await y.winDeal(z);return G({loading:!1,error:null,data:f}),g.onSuccess?.(),f}catch(f){let q=f instanceof Error?f:Error("Failed to mark deal as won");return G({loading:!1,error:q,data:null}),g.onError?.(q),null}},[y,g]),Y=A(async(z)=>{H({loading:!0,error:null,data:null});try{let f=await y.loseDeal(z);return H({loading:!1,error:null,data:f}),g.onSuccess?.(),f}catch(f){let q=f instanceof Error?f:Error("Failed to mark deal as lost");return H({loading:!1,error:q,data:null}),g.onError?.(q),null}},[y,g]);return{createDeal:U,moveDeal:V,winDeal:X,loseDeal:Y,createState:K,moveState:N,winState:O,loseState:P,isLoading:K.loading||N.loading||O.loading||P.loading}}export{x as useDealMutations};
|