@contractspec/example.crm-pipeline 3.7.6 → 3.7.10
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 +45 -42
- package/AGENTS.md +51 -33
- package/CHANGELOG.md +36 -0
- package/README.md +67 -148
- package/dist/browser/docs/crm-pipeline.docblock.js +1 -1
- package/dist/browser/docs/index.js +1 -1
- package/dist/browser/events/contact.event.js +1 -1
- package/dist/browser/events/deal.event.js +1 -1
- package/dist/browser/events/index.js +3 -3
- package/dist/browser/events/task.event.js +1 -1
- package/dist/browser/handlers/crm.handlers.js +13 -2
- package/dist/browser/handlers/index.js +13 -2
- package/dist/browser/index.js +680 -447
- package/dist/browser/ui/CrmDashboard.js +574 -352
- package/dist/browser/ui/CrmDealCard.js +5 -5
- package/dist/browser/ui/CrmPipelineBoard.js +13 -13
- package/dist/browser/ui/hooks/index.js +21 -10
- package/dist/browser/ui/hooks/useDealList.js +20 -9
- package/dist/browser/ui/hooks/useDealMutations.js +1 -1
- package/dist/browser/ui/index.js +683 -450
- package/dist/browser/ui/modals/CreateDealModal.js +12 -12
- package/dist/browser/ui/modals/DealActionsModal.js +21 -21
- package/dist/browser/ui/modals/index.js +33 -33
- package/dist/browser/ui/renderers/index.js +140 -118
- package/dist/browser/ui/renderers/pipeline.markdown.js +13 -2
- package/dist/browser/ui/renderers/pipeline.renderer.js +108 -97
- package/dist/browser/ui/tables/DealListTab.js +390 -0
- package/dist/deal/index.d.ts +2 -2
- package/dist/docs/crm-pipeline.docblock.js +1 -1
- package/dist/docs/index.js +1 -1
- package/dist/events/contact.event.js +1 -1
- package/dist/events/deal.event.js +1 -1
- package/dist/events/index.js +3 -3
- package/dist/events/task.event.js +1 -1
- package/dist/handlers/crm.handlers.d.ts +2 -0
- package/dist/handlers/crm.handlers.js +13 -2
- package/dist/handlers/index.d.ts +2 -2
- package/dist/handlers/index.js +13 -2
- package/dist/index.d.ts +3 -3
- package/dist/index.js +680 -447
- package/dist/node/docs/crm-pipeline.docblock.js +1 -1
- package/dist/node/docs/index.js +1 -1
- package/dist/node/events/contact.event.js +1 -1
- package/dist/node/events/deal.event.js +1 -1
- package/dist/node/events/index.js +3 -3
- package/dist/node/events/task.event.js +1 -1
- package/dist/node/handlers/crm.handlers.js +13 -2
- package/dist/node/handlers/index.js +13 -2
- package/dist/node/index.js +680 -447
- package/dist/node/ui/CrmDashboard.js +574 -352
- package/dist/node/ui/CrmDealCard.js +5 -5
- package/dist/node/ui/CrmPipelineBoard.js +13 -13
- package/dist/node/ui/hooks/index.js +21 -10
- package/dist/node/ui/hooks/useDealList.js +20 -9
- package/dist/node/ui/hooks/useDealMutations.js +1 -1
- package/dist/node/ui/index.js +683 -450
- package/dist/node/ui/modals/CreateDealModal.js +12 -12
- package/dist/node/ui/modals/DealActionsModal.js +21 -21
- package/dist/node/ui/modals/index.js +33 -33
- package/dist/node/ui/renderers/index.js +140 -118
- package/dist/node/ui/renderers/pipeline.markdown.js +13 -2
- package/dist/node/ui/renderers/pipeline.renderer.js +108 -97
- package/dist/node/ui/tables/DealListTab.js +390 -0
- package/dist/operations/index.d.ts +1 -1
- package/dist/ui/CrmDashboard.js +574 -352
- package/dist/ui/CrmDealCard.js +5 -5
- package/dist/ui/CrmPipelineBoard.js +13 -13
- package/dist/ui/hooks/index.d.ts +2 -2
- package/dist/ui/hooks/index.js +21 -10
- package/dist/ui/hooks/useDealList.d.ts +8 -2
- package/dist/ui/hooks/useDealList.js +20 -9
- package/dist/ui/hooks/useDealMutations.d.ts +9 -0
- package/dist/ui/hooks/useDealMutations.js +1 -1
- package/dist/ui/index.d.ts +3 -3
- package/dist/ui/index.js +683 -450
- package/dist/ui/modals/CreateDealModal.js +12 -12
- package/dist/ui/modals/DealActionsModal.js +21 -21
- package/dist/ui/modals/index.js +33 -33
- package/dist/ui/renderers/index.d.ts +1 -1
- package/dist/ui/renderers/index.js +140 -118
- package/dist/ui/renderers/pipeline.markdown.js +13 -2
- package/dist/ui/renderers/pipeline.renderer.d.ts +1 -1
- package/dist/ui/renderers/pipeline.renderer.js +108 -97
- package/dist/ui/tables/DealListTab.d.ts +20 -0
- package/dist/ui/tables/DealListTab.js +391 -0
- package/dist/ui/tables/DealListTab.smoke.test.d.ts +1 -0
- package/package.json +29 -14
- package/src/crm-pipeline.feature.ts +86 -86
- package/src/deal/deal.enum.ts +8 -8
- package/src/deal/deal.operation.ts +255 -255
- package/src/deal/deal.schema.ts +92 -92
- package/src/deal/deal.test-spec.ts +48 -48
- package/src/deal/index.ts +17 -19
- package/src/docs/crm-pipeline.docblock.ts +44 -44
- package/src/entities/company.entity.ts +52 -52
- package/src/entities/contact.entity.ts +67 -67
- package/src/entities/deal.entity.ts +134 -134
- package/src/entities/index.ts +27 -27
- package/src/entities/task.entity.ts +105 -105
- package/src/events/contact.event.ts +22 -22
- package/src/events/deal.event.ts +77 -77
- package/src/events/task.event.ts +19 -19
- package/src/example.ts +32 -32
- package/src/handlers/crm.handlers.ts +375 -357
- package/src/handlers/deal.handlers.ts +179 -179
- package/src/handlers/index.ts +18 -19
- package/src/handlers/mock-data.ts +167 -167
- package/src/index.ts +11 -11
- package/src/operations/index.ts +16 -16
- package/src/presentations/dashboard.presentation.ts +45 -45
- package/src/presentations/pipeline.presentation.ts +90 -90
- package/src/seeders/index.ts +26 -26
- package/src/shared/overlay-types.ts +23 -23
- package/src/ui/CrmDashboard.tsx +210 -279
- package/src/ui/CrmDealCard.tsx +64 -64
- package/src/ui/CrmPipelineBoard.tsx +105 -105
- package/src/ui/hooks/index.ts +3 -3
- package/src/ui/hooks/useDealList.ts +113 -85
- package/src/ui/hooks/useDealMutations.ts +151 -150
- package/src/ui/index.ts +5 -10
- package/src/ui/modals/CreateDealModal.tsx +217 -217
- package/src/ui/modals/DealActionsModal.tsx +390 -390
- package/src/ui/overlays/demo-overlays.ts +43 -43
- package/src/ui/renderers/index.ts +4 -3
- package/src/ui/renderers/pipeline.markdown.ts +165 -165
- package/src/ui/renderers/pipeline.renderer.tsx +17 -16
- package/src/ui/tables/DealListTab.smoke.test.tsx +149 -0
- package/src/ui/tables/DealListTab.tsx +276 -0
- package/tsconfig.json +7 -8
- package/tsdown.config.js +7 -3
package/dist/ui/CrmDashboard.js
CHANGED
|
@@ -1,18 +1,181 @@
|
|
|
1
1
|
// @bun
|
|
2
|
+
// src/ui/CrmDealCard.tsx
|
|
3
|
+
import { jsxDEV } from "react/jsx-dev-runtime";
|
|
4
|
+
"use client";
|
|
5
|
+
function formatCurrency(value, currency) {
|
|
6
|
+
return new Intl.NumberFormat("en-US", {
|
|
7
|
+
style: "currency",
|
|
8
|
+
currency,
|
|
9
|
+
minimumFractionDigits: 0,
|
|
10
|
+
maximumFractionDigits: 0
|
|
11
|
+
}).format(value);
|
|
12
|
+
}
|
|
13
|
+
function CrmDealCard({ deal, onClick }) {
|
|
14
|
+
const daysUntilClose = deal.expectedCloseDate ? Math.ceil((deal.expectedCloseDate.getTime() - Date.now()) / (1000 * 60 * 60 * 24)) : null;
|
|
15
|
+
return /* @__PURE__ */ jsxDEV("div", {
|
|
16
|
+
onClick,
|
|
17
|
+
className: "cursor-pointer rounded-lg border border-border bg-card p-3 shadow-sm transition-shadow hover:shadow-md",
|
|
18
|
+
role: "button",
|
|
19
|
+
tabIndex: 0,
|
|
20
|
+
onKeyDown: (e) => {
|
|
21
|
+
if (e.key === "Enter" || e.key === " ")
|
|
22
|
+
onClick?.();
|
|
23
|
+
},
|
|
24
|
+
children: [
|
|
25
|
+
/* @__PURE__ */ jsxDEV("h4", {
|
|
26
|
+
className: "font-medium leading-snug",
|
|
27
|
+
children: deal.name
|
|
28
|
+
}, undefined, false, undefined, this),
|
|
29
|
+
/* @__PURE__ */ jsxDEV("div", {
|
|
30
|
+
className: "mt-2 font-semibold text-lg text-primary",
|
|
31
|
+
children: formatCurrency(deal.value, deal.currency)
|
|
32
|
+
}, undefined, false, undefined, this),
|
|
33
|
+
/* @__PURE__ */ jsxDEV("div", {
|
|
34
|
+
className: "mt-3 flex items-center justify-between text-muted-foreground text-xs",
|
|
35
|
+
children: [
|
|
36
|
+
daysUntilClose !== null && /* @__PURE__ */ jsxDEV("span", {
|
|
37
|
+
className: daysUntilClose < 0 ? "text-red-500" : daysUntilClose <= 7 ? "text-yellow-600 dark:text-yellow-500" : "",
|
|
38
|
+
children: daysUntilClose < 0 ? `${Math.abs(daysUntilClose)}d overdue` : daysUntilClose === 0 ? "Due today" : `${daysUntilClose}d left`
|
|
39
|
+
}, undefined, false, undefined, this),
|
|
40
|
+
/* @__PURE__ */ jsxDEV("span", {
|
|
41
|
+
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"}`,
|
|
42
|
+
children: deal.status
|
|
43
|
+
}, undefined, false, undefined, this)
|
|
44
|
+
]
|
|
45
|
+
}, undefined, true, undefined, this)
|
|
46
|
+
]
|
|
47
|
+
}, undefined, true, undefined, this);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// src/ui/CrmPipelineBoard.tsx
|
|
51
|
+
import { useState } from "react";
|
|
52
|
+
import { jsxDEV as jsxDEV2 } from "react/jsx-dev-runtime";
|
|
53
|
+
"use client";
|
|
54
|
+
function formatCurrency2(value) {
|
|
55
|
+
if (value >= 1e6)
|
|
56
|
+
return `$${(value / 1e6).toFixed(1)}M`;
|
|
57
|
+
if (value >= 1000)
|
|
58
|
+
return `$${(value / 1000).toFixed(0)}K`;
|
|
59
|
+
return `$${value}`;
|
|
60
|
+
}
|
|
61
|
+
function CrmPipelineBoard({
|
|
62
|
+
dealsByStage,
|
|
63
|
+
stages,
|
|
64
|
+
onDealClick,
|
|
65
|
+
onDealMove
|
|
66
|
+
}) {
|
|
67
|
+
const [quickMoveOpen, setQuickMoveOpen] = useState(null);
|
|
68
|
+
const sortedStages = [...stages].sort((a, b) => a.position - b.position);
|
|
69
|
+
const handleQuickMove = (dealId, toStageId) => {
|
|
70
|
+
onDealMove?.(dealId, toStageId);
|
|
71
|
+
setQuickMoveOpen(null);
|
|
72
|
+
};
|
|
73
|
+
return /* @__PURE__ */ jsxDEV2("div", {
|
|
74
|
+
className: "flex gap-4 overflow-x-auto pb-4",
|
|
75
|
+
children: sortedStages.map((stage) => {
|
|
76
|
+
const deals = dealsByStage[stage.id] ?? [];
|
|
77
|
+
const stageValue = deals.reduce((sum, d) => sum + d.value, 0);
|
|
78
|
+
return /* @__PURE__ */ jsxDEV2("div", {
|
|
79
|
+
className: "flex w-72 flex-shrink-0 flex-col rounded-lg bg-muted/30",
|
|
80
|
+
children: [
|
|
81
|
+
/* @__PURE__ */ jsxDEV2("div", {
|
|
82
|
+
className: "flex items-center justify-between border-border border-b px-3 py-2",
|
|
83
|
+
children: [
|
|
84
|
+
/* @__PURE__ */ jsxDEV2("div", {
|
|
85
|
+
children: [
|
|
86
|
+
/* @__PURE__ */ jsxDEV2("h3", {
|
|
87
|
+
className: "font-medium",
|
|
88
|
+
children: stage.name
|
|
89
|
+
}, undefined, false, undefined, this),
|
|
90
|
+
/* @__PURE__ */ jsxDEV2("p", {
|
|
91
|
+
className: "text-muted-foreground text-xs",
|
|
92
|
+
children: [
|
|
93
|
+
deals.length,
|
|
94
|
+
" deals \xB7 ",
|
|
95
|
+
formatCurrency2(stageValue)
|
|
96
|
+
]
|
|
97
|
+
}, undefined, true, undefined, this)
|
|
98
|
+
]
|
|
99
|
+
}, undefined, true, undefined, this),
|
|
100
|
+
/* @__PURE__ */ jsxDEV2("span", {
|
|
101
|
+
className: "flex h-6 w-6 items-center justify-center rounded-full bg-muted font-medium text-xs",
|
|
102
|
+
children: deals.length
|
|
103
|
+
}, undefined, false, undefined, this)
|
|
104
|
+
]
|
|
105
|
+
}, undefined, true, undefined, this),
|
|
106
|
+
/* @__PURE__ */ jsxDEV2("div", {
|
|
107
|
+
className: "flex flex-1 flex-col gap-2 p-2",
|
|
108
|
+
children: deals.length === 0 ? /* @__PURE__ */ jsxDEV2("div", {
|
|
109
|
+
className: "flex h-24 items-center justify-center rounded-md border-2 border-muted-foreground/20 border-dashed text-muted-foreground text-xs",
|
|
110
|
+
children: "No deals"
|
|
111
|
+
}, undefined, false, undefined, this) : deals.map((deal) => /* @__PURE__ */ jsxDEV2("div", {
|
|
112
|
+
className: "group relative",
|
|
113
|
+
children: [
|
|
114
|
+
/* @__PURE__ */ jsxDEV2(CrmDealCard, {
|
|
115
|
+
deal,
|
|
116
|
+
onClick: () => onDealClick?.(deal.id)
|
|
117
|
+
}, undefined, false, undefined, this),
|
|
118
|
+
deal.status === "OPEN" && onDealMove && /* @__PURE__ */ jsxDEV2("div", {
|
|
119
|
+
className: "absolute top-1 right-1 opacity-0 transition-opacity group-hover:opacity-100",
|
|
120
|
+
children: [
|
|
121
|
+
/* @__PURE__ */ jsxDEV2("button", {
|
|
122
|
+
type: "button",
|
|
123
|
+
onClick: (e) => {
|
|
124
|
+
e.stopPropagation();
|
|
125
|
+
setQuickMoveOpen(quickMoveOpen === deal.id ? null : deal.id);
|
|
126
|
+
},
|
|
127
|
+
className: "flex h-6 w-6 items-center justify-center rounded border border-border bg-background text-xs shadow-sm hover:bg-muted",
|
|
128
|
+
title: "Quick move",
|
|
129
|
+
children: "\u27A1\uFE0F"
|
|
130
|
+
}, undefined, false, undefined, this),
|
|
131
|
+
quickMoveOpen === deal.id && /* @__PURE__ */ jsxDEV2("div", {
|
|
132
|
+
className: "absolute top-7 right-0 z-20 min-w-[140px] rounded-lg border border-border bg-card py-1 shadow-lg",
|
|
133
|
+
children: [
|
|
134
|
+
/* @__PURE__ */ jsxDEV2("p", {
|
|
135
|
+
className: "px-3 py-1 font-medium text-muted-foreground text-xs",
|
|
136
|
+
children: "Move to:"
|
|
137
|
+
}, undefined, false, undefined, this),
|
|
138
|
+
sortedStages.filter((s) => s.id !== deal.stageId).map((s) => /* @__PURE__ */ jsxDEV2("button", {
|
|
139
|
+
type: "button",
|
|
140
|
+
onClick: (e) => {
|
|
141
|
+
e.stopPropagation();
|
|
142
|
+
handleQuickMove(deal.id, s.id);
|
|
143
|
+
},
|
|
144
|
+
className: "w-full px-3 py-1.5 text-left text-sm hover:bg-muted",
|
|
145
|
+
children: s.name
|
|
146
|
+
}, s.id, false, undefined, this))
|
|
147
|
+
]
|
|
148
|
+
}, undefined, true, undefined, this)
|
|
149
|
+
]
|
|
150
|
+
}, undefined, true, undefined, this)
|
|
151
|
+
]
|
|
152
|
+
}, deal.id, true, undefined, this))
|
|
153
|
+
}, undefined, false, undefined, this)
|
|
154
|
+
]
|
|
155
|
+
}, stage.id, true, undefined, this);
|
|
156
|
+
})
|
|
157
|
+
}, undefined, false, undefined, this);
|
|
158
|
+
}
|
|
159
|
+
|
|
2
160
|
// src/ui/hooks/useDealList.ts
|
|
3
|
-
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
4
161
|
import { useTemplateRuntime } from "@contractspec/lib.example-shared-ui";
|
|
162
|
+
import { useCallback, useEffect, useMemo, useState as useState2 } from "react";
|
|
5
163
|
"use client";
|
|
6
164
|
function useDealList(options = {}) {
|
|
7
165
|
const { handlers, projectId } = useTemplateRuntime();
|
|
8
166
|
const { crm } = handlers;
|
|
9
|
-
const [data, setData] =
|
|
10
|
-
const [dealsByStage, setDealsByStage] =
|
|
11
|
-
const [stages, setStages] =
|
|
12
|
-
const [loading, setLoading] =
|
|
13
|
-
const [error, setError] =
|
|
14
|
-
const [
|
|
167
|
+
const [data, setData] = useState2(null);
|
|
168
|
+
const [dealsByStage, setDealsByStage] = useState2({});
|
|
169
|
+
const [stages, setStages] = useState2([]);
|
|
170
|
+
const [loading, setLoading] = useState2(true);
|
|
171
|
+
const [error, setError] = useState2(null);
|
|
172
|
+
const [internalPage, setInternalPage] = useState2(0);
|
|
15
173
|
const pipelineId = options.pipelineId ?? "pipeline-1";
|
|
174
|
+
const pageIndex = options.pageIndex ?? internalPage;
|
|
175
|
+
const pageSize = options.pageSize ?? options.limit ?? 50;
|
|
176
|
+
const [sort] = options.sorting ?? [];
|
|
177
|
+
const sortBy = sort?.id;
|
|
178
|
+
const sortDirection = sort ? sort.desc ? "desc" : "asc" : undefined;
|
|
16
179
|
const fetchData = useCallback(async () => {
|
|
17
180
|
setLoading(true);
|
|
18
181
|
setError(null);
|
|
@@ -24,8 +187,10 @@ function useDealList(options = {}) {
|
|
|
24
187
|
stageId: options.stageId,
|
|
25
188
|
status: options.status === "all" ? undefined : options.status,
|
|
26
189
|
search: options.search,
|
|
27
|
-
limit:
|
|
28
|
-
offset:
|
|
190
|
+
limit: pageSize,
|
|
191
|
+
offset: pageIndex * pageSize,
|
|
192
|
+
sortBy: sortBy === "name" || sortBy === "value" || sortBy === "status" || sortBy === "expectedCloseDate" || sortBy === "updatedAt" ? sortBy : undefined,
|
|
193
|
+
sortDirection
|
|
29
194
|
}),
|
|
30
195
|
crm.getDealsByStage({ projectId, pipelineId }),
|
|
31
196
|
crm.getPipelineStages({ pipelineId })
|
|
@@ -45,8 +210,10 @@ function useDealList(options = {}) {
|
|
|
45
210
|
options.stageId,
|
|
46
211
|
options.status,
|
|
47
212
|
options.search,
|
|
48
|
-
|
|
49
|
-
|
|
213
|
+
pageIndex,
|
|
214
|
+
pageSize,
|
|
215
|
+
sortBy,
|
|
216
|
+
sortDirection
|
|
50
217
|
]);
|
|
51
218
|
useEffect(() => {
|
|
52
219
|
fetchData();
|
|
@@ -74,35 +241,37 @@ function useDealList(options = {}) {
|
|
|
74
241
|
loading,
|
|
75
242
|
error,
|
|
76
243
|
stats,
|
|
77
|
-
page,
|
|
244
|
+
page: pageIndex + 1,
|
|
245
|
+
pageIndex,
|
|
246
|
+
pageSize,
|
|
78
247
|
refetch: fetchData,
|
|
79
|
-
nextPage: () =>
|
|
80
|
-
prevPage: () =>
|
|
248
|
+
nextPage: options.pageIndex === undefined ? () => setInternalPage((page) => page + 1) : undefined,
|
|
249
|
+
prevPage: options.pageIndex === undefined ? () => pageIndex > 0 && setInternalPage((page) => page - 1) : undefined
|
|
81
250
|
};
|
|
82
251
|
}
|
|
83
252
|
|
|
84
253
|
// src/ui/hooks/useDealMutations.ts
|
|
85
|
-
import { useCallback as useCallback2, useState as useState2 } from "react";
|
|
86
254
|
import { useTemplateRuntime as useTemplateRuntime2 } from "@contractspec/lib.example-shared-ui";
|
|
255
|
+
import { useCallback as useCallback2, useState as useState3 } from "react";
|
|
87
256
|
function useDealMutations(options = {}) {
|
|
88
257
|
const { handlers, projectId } = useTemplateRuntime2();
|
|
89
258
|
const { crm } = handlers;
|
|
90
|
-
const [createState, setCreateState] =
|
|
259
|
+
const [createState, setCreateState] = useState3({
|
|
91
260
|
loading: false,
|
|
92
261
|
error: null,
|
|
93
262
|
data: null
|
|
94
263
|
});
|
|
95
|
-
const [moveState, setMoveState] =
|
|
264
|
+
const [moveState, setMoveState] = useState3({
|
|
96
265
|
loading: false,
|
|
97
266
|
error: null,
|
|
98
267
|
data: null
|
|
99
268
|
});
|
|
100
|
-
const [winState, setWinState] =
|
|
269
|
+
const [winState, setWinState] = useState3({
|
|
101
270
|
loading: false,
|
|
102
271
|
error: null,
|
|
103
272
|
data: null
|
|
104
273
|
});
|
|
105
|
-
const [loseState, setLoseState] =
|
|
274
|
+
const [loseState, setLoseState] = useState3({
|
|
106
275
|
loading: false,
|
|
107
276
|
error: null,
|
|
108
277
|
data: null
|
|
@@ -179,167 +348,9 @@ function useDealMutations(options = {}) {
|
|
|
179
348
|
};
|
|
180
349
|
}
|
|
181
350
|
|
|
182
|
-
// src/ui/CrmDealCard.tsx
|
|
183
|
-
import { jsxDEV } from "react/jsx-dev-runtime";
|
|
184
|
-
"use client";
|
|
185
|
-
function formatCurrency(value, currency) {
|
|
186
|
-
return new Intl.NumberFormat("en-US", {
|
|
187
|
-
style: "currency",
|
|
188
|
-
currency,
|
|
189
|
-
minimumFractionDigits: 0,
|
|
190
|
-
maximumFractionDigits: 0
|
|
191
|
-
}).format(value);
|
|
192
|
-
}
|
|
193
|
-
function CrmDealCard({ deal, onClick }) {
|
|
194
|
-
const daysUntilClose = deal.expectedCloseDate ? Math.ceil((deal.expectedCloseDate.getTime() - Date.now()) / (1000 * 60 * 60 * 24)) : null;
|
|
195
|
-
return /* @__PURE__ */ jsxDEV("div", {
|
|
196
|
-
onClick,
|
|
197
|
-
className: "border-border bg-card cursor-pointer rounded-lg border p-3 shadow-sm transition-shadow hover:shadow-md",
|
|
198
|
-
role: "button",
|
|
199
|
-
tabIndex: 0,
|
|
200
|
-
onKeyDown: (e) => {
|
|
201
|
-
if (e.key === "Enter" || e.key === " ")
|
|
202
|
-
onClick?.();
|
|
203
|
-
},
|
|
204
|
-
children: [
|
|
205
|
-
/* @__PURE__ */ jsxDEV("h4", {
|
|
206
|
-
className: "leading-snug font-medium",
|
|
207
|
-
children: deal.name
|
|
208
|
-
}, undefined, false, undefined, this),
|
|
209
|
-
/* @__PURE__ */ jsxDEV("div", {
|
|
210
|
-
className: "text-primary mt-2 text-lg font-semibold",
|
|
211
|
-
children: formatCurrency(deal.value, deal.currency)
|
|
212
|
-
}, undefined, false, undefined, this),
|
|
213
|
-
/* @__PURE__ */ jsxDEV("div", {
|
|
214
|
-
className: "text-muted-foreground mt-3 flex items-center justify-between text-xs",
|
|
215
|
-
children: [
|
|
216
|
-
daysUntilClose !== null && /* @__PURE__ */ jsxDEV("span", {
|
|
217
|
-
className: daysUntilClose < 0 ? "text-red-500" : daysUntilClose <= 7 ? "text-yellow-600 dark:text-yellow-500" : "",
|
|
218
|
-
children: daysUntilClose < 0 ? `${Math.abs(daysUntilClose)}d overdue` : daysUntilClose === 0 ? "Due today" : `${daysUntilClose}d left`
|
|
219
|
-
}, undefined, false, undefined, this),
|
|
220
|
-
/* @__PURE__ */ jsxDEV("span", {
|
|
221
|
-
className: `rounded px-1.5 py-0.5 text-xs font-medium ${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"}`,
|
|
222
|
-
children: deal.status
|
|
223
|
-
}, undefined, false, undefined, this)
|
|
224
|
-
]
|
|
225
|
-
}, undefined, true, undefined, this)
|
|
226
|
-
]
|
|
227
|
-
}, undefined, true, undefined, this);
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
// src/ui/CrmPipelineBoard.tsx
|
|
231
|
-
import { useState as useState3 } from "react";
|
|
232
|
-
import { jsxDEV as jsxDEV2 } from "react/jsx-dev-runtime";
|
|
233
|
-
"use client";
|
|
234
|
-
function formatCurrency2(value) {
|
|
235
|
-
if (value >= 1e6)
|
|
236
|
-
return `$${(value / 1e6).toFixed(1)}M`;
|
|
237
|
-
if (value >= 1000)
|
|
238
|
-
return `$${(value / 1000).toFixed(0)}K`;
|
|
239
|
-
return `$${value}`;
|
|
240
|
-
}
|
|
241
|
-
function CrmPipelineBoard({
|
|
242
|
-
dealsByStage,
|
|
243
|
-
stages,
|
|
244
|
-
onDealClick,
|
|
245
|
-
onDealMove
|
|
246
|
-
}) {
|
|
247
|
-
const [quickMoveOpen, setQuickMoveOpen] = useState3(null);
|
|
248
|
-
const sortedStages = [...stages].sort((a, b) => a.position - b.position);
|
|
249
|
-
const handleQuickMove = (dealId, toStageId) => {
|
|
250
|
-
onDealMove?.(dealId, toStageId);
|
|
251
|
-
setQuickMoveOpen(null);
|
|
252
|
-
};
|
|
253
|
-
return /* @__PURE__ */ jsxDEV2("div", {
|
|
254
|
-
className: "flex gap-4 overflow-x-auto pb-4",
|
|
255
|
-
children: sortedStages.map((stage) => {
|
|
256
|
-
const deals = dealsByStage[stage.id] ?? [];
|
|
257
|
-
const stageValue = deals.reduce((sum, d) => sum + d.value, 0);
|
|
258
|
-
return /* @__PURE__ */ jsxDEV2("div", {
|
|
259
|
-
className: "bg-muted/30 flex w-72 flex-shrink-0 flex-col rounded-lg",
|
|
260
|
-
children: [
|
|
261
|
-
/* @__PURE__ */ jsxDEV2("div", {
|
|
262
|
-
className: "border-border flex items-center justify-between border-b px-3 py-2",
|
|
263
|
-
children: [
|
|
264
|
-
/* @__PURE__ */ jsxDEV2("div", {
|
|
265
|
-
children: [
|
|
266
|
-
/* @__PURE__ */ jsxDEV2("h3", {
|
|
267
|
-
className: "font-medium",
|
|
268
|
-
children: stage.name
|
|
269
|
-
}, undefined, false, undefined, this),
|
|
270
|
-
/* @__PURE__ */ jsxDEV2("p", {
|
|
271
|
-
className: "text-muted-foreground text-xs",
|
|
272
|
-
children: [
|
|
273
|
-
deals.length,
|
|
274
|
-
" deals \xB7 ",
|
|
275
|
-
formatCurrency2(stageValue)
|
|
276
|
-
]
|
|
277
|
-
}, undefined, true, undefined, this)
|
|
278
|
-
]
|
|
279
|
-
}, undefined, true, undefined, this),
|
|
280
|
-
/* @__PURE__ */ jsxDEV2("span", {
|
|
281
|
-
className: "bg-muted flex h-6 w-6 items-center justify-center rounded-full text-xs font-medium",
|
|
282
|
-
children: deals.length
|
|
283
|
-
}, undefined, false, undefined, this)
|
|
284
|
-
]
|
|
285
|
-
}, undefined, true, undefined, this),
|
|
286
|
-
/* @__PURE__ */ jsxDEV2("div", {
|
|
287
|
-
className: "flex flex-1 flex-col gap-2 p-2",
|
|
288
|
-
children: deals.length === 0 ? /* @__PURE__ */ jsxDEV2("div", {
|
|
289
|
-
className: "border-muted-foreground/20 text-muted-foreground flex h-24 items-center justify-center rounded-md border-2 border-dashed text-xs",
|
|
290
|
-
children: "No deals"
|
|
291
|
-
}, undefined, false, undefined, this) : deals.map((deal) => /* @__PURE__ */ jsxDEV2("div", {
|
|
292
|
-
className: "group relative",
|
|
293
|
-
children: [
|
|
294
|
-
/* @__PURE__ */ jsxDEV2(CrmDealCard, {
|
|
295
|
-
deal,
|
|
296
|
-
onClick: () => onDealClick?.(deal.id)
|
|
297
|
-
}, undefined, false, undefined, this),
|
|
298
|
-
deal.status === "OPEN" && onDealMove && /* @__PURE__ */ jsxDEV2("div", {
|
|
299
|
-
className: "absolute top-1 right-1 opacity-0 transition-opacity group-hover:opacity-100",
|
|
300
|
-
children: [
|
|
301
|
-
/* @__PURE__ */ jsxDEV2("button", {
|
|
302
|
-
type: "button",
|
|
303
|
-
onClick: (e) => {
|
|
304
|
-
e.stopPropagation();
|
|
305
|
-
setQuickMoveOpen(quickMoveOpen === deal.id ? null : deal.id);
|
|
306
|
-
},
|
|
307
|
-
className: "bg-background border-border hover:bg-muted flex h-6 w-6 items-center justify-center rounded border text-xs shadow-sm",
|
|
308
|
-
title: "Quick move",
|
|
309
|
-
children: "\u27A1\uFE0F"
|
|
310
|
-
}, undefined, false, undefined, this),
|
|
311
|
-
quickMoveOpen === deal.id && /* @__PURE__ */ jsxDEV2("div", {
|
|
312
|
-
className: "bg-card border-border absolute top-7 right-0 z-20 min-w-[140px] rounded-lg border py-1 shadow-lg",
|
|
313
|
-
children: [
|
|
314
|
-
/* @__PURE__ */ jsxDEV2("p", {
|
|
315
|
-
className: "text-muted-foreground px-3 py-1 text-xs font-medium",
|
|
316
|
-
children: "Move to:"
|
|
317
|
-
}, undefined, false, undefined, this),
|
|
318
|
-
sortedStages.filter((s) => s.id !== deal.stageId).map((s) => /* @__PURE__ */ jsxDEV2("button", {
|
|
319
|
-
type: "button",
|
|
320
|
-
onClick: (e) => {
|
|
321
|
-
e.stopPropagation();
|
|
322
|
-
handleQuickMove(deal.id, s.id);
|
|
323
|
-
},
|
|
324
|
-
className: "hover:bg-muted w-full px-3 py-1.5 text-left text-sm",
|
|
325
|
-
children: s.name
|
|
326
|
-
}, s.id, false, undefined, this))
|
|
327
|
-
]
|
|
328
|
-
}, undefined, true, undefined, this)
|
|
329
|
-
]
|
|
330
|
-
}, undefined, true, undefined, this)
|
|
331
|
-
]
|
|
332
|
-
}, deal.id, true, undefined, this))
|
|
333
|
-
}, undefined, false, undefined, this)
|
|
334
|
-
]
|
|
335
|
-
}, stage.id, true, undefined, this);
|
|
336
|
-
})
|
|
337
|
-
}, undefined, false, undefined, this);
|
|
338
|
-
}
|
|
339
|
-
|
|
340
351
|
// src/ui/modals/CreateDealModal.tsx
|
|
341
|
-
import { useState as useState4 } from "react";
|
|
342
352
|
import { Button, Input } from "@contractspec/lib.design-system";
|
|
353
|
+
import { useState as useState4 } from "react";
|
|
343
354
|
import { jsxDEV as jsxDEV3 } from "react/jsx-dev-runtime";
|
|
344
355
|
"use client";
|
|
345
356
|
var CURRENCIES = ["USD", "EUR", "GBP", "CAD"];
|
|
@@ -398,7 +409,7 @@ function CreateDealModal({
|
|
|
398
409
|
className: "fixed inset-0 z-50 flex items-center justify-center",
|
|
399
410
|
children: [
|
|
400
411
|
/* @__PURE__ */ jsxDEV3("div", {
|
|
401
|
-
className: "bg-background/80
|
|
412
|
+
className: "absolute inset-0 bg-background/80 backdrop-blur-sm",
|
|
402
413
|
onClick: onClose,
|
|
403
414
|
role: "button",
|
|
404
415
|
tabIndex: 0,
|
|
@@ -409,10 +420,10 @@ function CreateDealModal({
|
|
|
409
420
|
"aria-label": "Close modal"
|
|
410
421
|
}, undefined, false, undefined, this),
|
|
411
422
|
/* @__PURE__ */ jsxDEV3("div", {
|
|
412
|
-
className: "
|
|
423
|
+
className: "relative z-10 w-full max-w-md rounded-xl border border-border bg-card p-6 shadow-xl",
|
|
413
424
|
children: [
|
|
414
425
|
/* @__PURE__ */ jsxDEV3("h2", {
|
|
415
|
-
className: "mb-4 text-xl
|
|
426
|
+
className: "mb-4 font-semibold text-xl",
|
|
416
427
|
children: "Create New Deal"
|
|
417
428
|
}, undefined, false, undefined, this),
|
|
418
429
|
/* @__PURE__ */ jsxDEV3("form", {
|
|
@@ -423,7 +434,7 @@ function CreateDealModal({
|
|
|
423
434
|
children: [
|
|
424
435
|
/* @__PURE__ */ jsxDEV3("label", {
|
|
425
436
|
htmlFor: "deal-name",
|
|
426
|
-
className: "
|
|
437
|
+
className: "mb-1 block font-medium text-muted-foreground text-sm",
|
|
427
438
|
children: "Deal Name *"
|
|
428
439
|
}, undefined, false, undefined, this),
|
|
429
440
|
/* @__PURE__ */ jsxDEV3(Input, {
|
|
@@ -443,7 +454,7 @@ function CreateDealModal({
|
|
|
443
454
|
children: [
|
|
444
455
|
/* @__PURE__ */ jsxDEV3("label", {
|
|
445
456
|
htmlFor: "deal-value",
|
|
446
|
-
className: "
|
|
457
|
+
className: "mb-1 block font-medium text-muted-foreground text-sm",
|
|
447
458
|
children: "Value *"
|
|
448
459
|
}, undefined, false, undefined, this),
|
|
449
460
|
/* @__PURE__ */ jsxDEV3(Input, {
|
|
@@ -463,7 +474,7 @@ function CreateDealModal({
|
|
|
463
474
|
children: [
|
|
464
475
|
/* @__PURE__ */ jsxDEV3("label", {
|
|
465
476
|
htmlFor: "deal-currency",
|
|
466
|
-
className: "
|
|
477
|
+
className: "mb-1 block font-medium text-muted-foreground text-sm",
|
|
467
478
|
children: "Currency"
|
|
468
479
|
}, undefined, false, undefined, this),
|
|
469
480
|
/* @__PURE__ */ jsxDEV3("select", {
|
|
@@ -471,7 +482,7 @@ function CreateDealModal({
|
|
|
471
482
|
value: currency,
|
|
472
483
|
onChange: (e) => setCurrency(e.target.value),
|
|
473
484
|
disabled: isLoading,
|
|
474
|
-
className: "
|
|
485
|
+
className: "h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring disabled:opacity-50",
|
|
475
486
|
children: CURRENCIES.map((c) => /* @__PURE__ */ jsxDEV3("option", {
|
|
476
487
|
value: c,
|
|
477
488
|
children: c
|
|
@@ -485,7 +496,7 @@ function CreateDealModal({
|
|
|
485
496
|
children: [
|
|
486
497
|
/* @__PURE__ */ jsxDEV3("label", {
|
|
487
498
|
htmlFor: "deal-stage",
|
|
488
|
-
className: "
|
|
499
|
+
className: "mb-1 block font-medium text-muted-foreground text-sm",
|
|
489
500
|
children: "Pipeline Stage *"
|
|
490
501
|
}, undefined, false, undefined, this),
|
|
491
502
|
/* @__PURE__ */ jsxDEV3("select", {
|
|
@@ -493,7 +504,7 @@ function CreateDealModal({
|
|
|
493
504
|
value: stageId,
|
|
494
505
|
onChange: (e) => setStageId(e.target.value),
|
|
495
506
|
disabled: isLoading,
|
|
496
|
-
className: "
|
|
507
|
+
className: "h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring disabled:opacity-50",
|
|
497
508
|
children: stages.map((stage) => /* @__PURE__ */ jsxDEV3("option", {
|
|
498
509
|
value: stage.id,
|
|
499
510
|
children: stage.name
|
|
@@ -505,7 +516,7 @@ function CreateDealModal({
|
|
|
505
516
|
children: [
|
|
506
517
|
/* @__PURE__ */ jsxDEV3("label", {
|
|
507
518
|
htmlFor: "deal-close-date",
|
|
508
|
-
className: "
|
|
519
|
+
className: "mb-1 block font-medium text-muted-foreground text-sm",
|
|
509
520
|
children: "Expected Close Date"
|
|
510
521
|
}, undefined, false, undefined, this),
|
|
511
522
|
/* @__PURE__ */ jsxDEV3(Input, {
|
|
@@ -518,7 +529,7 @@ function CreateDealModal({
|
|
|
518
529
|
]
|
|
519
530
|
}, undefined, true, undefined, this),
|
|
520
531
|
error && /* @__PURE__ */ jsxDEV3("div", {
|
|
521
|
-
className: "bg-destructive/10
|
|
532
|
+
className: "rounded-md bg-destructive/10 p-3 text-destructive text-sm",
|
|
522
533
|
children: error
|
|
523
534
|
}, undefined, false, undefined, this),
|
|
524
535
|
/* @__PURE__ */ jsxDEV3("div", {
|
|
@@ -547,8 +558,8 @@ function CreateDealModal({
|
|
|
547
558
|
}
|
|
548
559
|
|
|
549
560
|
// src/ui/modals/DealActionsModal.tsx
|
|
550
|
-
import { useState as useState5 } from "react";
|
|
551
561
|
import { Button as Button2 } from "@contractspec/lib.design-system";
|
|
562
|
+
import { useState as useState5 } from "react";
|
|
552
563
|
import { jsxDEV as jsxDEV4, Fragment } from "react/jsx-dev-runtime";
|
|
553
564
|
"use client";
|
|
554
565
|
function formatCurrency3(value, currency) {
|
|
@@ -649,7 +660,7 @@ function DealActionsModal({
|
|
|
649
660
|
className: "fixed inset-0 z-50 flex items-center justify-center",
|
|
650
661
|
children: [
|
|
651
662
|
/* @__PURE__ */ jsxDEV4("div", {
|
|
652
|
-
className: "bg-background/80
|
|
663
|
+
className: "absolute inset-0 bg-background/80 backdrop-blur-sm",
|
|
653
664
|
onClick: handleClose,
|
|
654
665
|
role: "button",
|
|
655
666
|
tabIndex: 0,
|
|
@@ -660,21 +671,21 @@ function DealActionsModal({
|
|
|
660
671
|
"aria-label": "Close modal"
|
|
661
672
|
}, undefined, false, undefined, this),
|
|
662
673
|
/* @__PURE__ */ jsxDEV4("div", {
|
|
663
|
-
className: "
|
|
674
|
+
className: "relative z-10 w-full max-w-md rounded-xl border border-border bg-card p-6 shadow-xl",
|
|
664
675
|
children: [
|
|
665
676
|
/* @__PURE__ */ jsxDEV4("div", {
|
|
666
|
-
className: "
|
|
677
|
+
className: "mb-4 border-border border-b pb-4",
|
|
667
678
|
children: [
|
|
668
679
|
/* @__PURE__ */ jsxDEV4("h2", {
|
|
669
|
-
className: "text-xl
|
|
680
|
+
className: "font-semibold text-xl",
|
|
670
681
|
children: deal.name
|
|
671
682
|
}, undefined, false, undefined, this),
|
|
672
683
|
/* @__PURE__ */ jsxDEV4("p", {
|
|
673
|
-
className: "
|
|
684
|
+
className: "font-medium text-lg text-primary",
|
|
674
685
|
children: formatCurrency3(deal.value, deal.currency)
|
|
675
686
|
}, undefined, false, undefined, this),
|
|
676
687
|
/* @__PURE__ */ jsxDEV4("span", {
|
|
677
|
-
className: `mt-2 inline-flex rounded-full px-2 py-0.5 text-xs
|
|
688
|
+
className: `mt-2 inline-flex rounded-full px-2 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"}`,
|
|
678
689
|
children: deal.status
|
|
679
690
|
}, undefined, false, undefined, this)
|
|
680
691
|
]
|
|
@@ -726,7 +737,7 @@ function DealActionsModal({
|
|
|
726
737
|
]
|
|
727
738
|
}, undefined, true, undefined, this),
|
|
728
739
|
deal.status !== "OPEN" && /* @__PURE__ */ jsxDEV4("p", {
|
|
729
|
-
className: "
|
|
740
|
+
className: "py-4 text-center text-muted-foreground",
|
|
730
741
|
children: [
|
|
731
742
|
"This deal is already ",
|
|
732
743
|
deal.status.toLowerCase(),
|
|
@@ -751,14 +762,14 @@ function DealActionsModal({
|
|
|
751
762
|
children: [
|
|
752
763
|
/* @__PURE__ */ jsxDEV4("label", {
|
|
753
764
|
htmlFor: "won-source",
|
|
754
|
-
className: "
|
|
765
|
+
className: "mb-1 block font-medium text-muted-foreground text-sm",
|
|
755
766
|
children: "How did you win this deal?"
|
|
756
767
|
}, undefined, false, undefined, this),
|
|
757
768
|
/* @__PURE__ */ jsxDEV4("select", {
|
|
758
769
|
id: "won-source",
|
|
759
770
|
value: wonSource,
|
|
760
771
|
onChange: (e) => setWonSource(e.target.value),
|
|
761
|
-
className: "
|
|
772
|
+
className: "h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring",
|
|
762
773
|
children: [
|
|
763
774
|
/* @__PURE__ */ jsxDEV4("option", {
|
|
764
775
|
value: "",
|
|
@@ -792,7 +803,7 @@ function DealActionsModal({
|
|
|
792
803
|
children: [
|
|
793
804
|
/* @__PURE__ */ jsxDEV4("label", {
|
|
794
805
|
htmlFor: "win-notes",
|
|
795
|
-
className: "
|
|
806
|
+
className: "mb-1 block font-medium text-muted-foreground text-sm",
|
|
796
807
|
children: "Notes (optional)"
|
|
797
808
|
}, undefined, false, undefined, this),
|
|
798
809
|
/* @__PURE__ */ jsxDEV4("textarea", {
|
|
@@ -801,12 +812,12 @@ function DealActionsModal({
|
|
|
801
812
|
onChange: (e) => setNotes(e.target.value),
|
|
802
813
|
placeholder: "Any additional notes about the win...",
|
|
803
814
|
rows: 3,
|
|
804
|
-
className: "
|
|
815
|
+
className: "w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring"
|
|
805
816
|
}, undefined, false, undefined, this)
|
|
806
817
|
]
|
|
807
818
|
}, undefined, true, undefined, this),
|
|
808
819
|
error && /* @__PURE__ */ jsxDEV4("div", {
|
|
809
|
-
className: "bg-destructive/10
|
|
820
|
+
className: "rounded-md bg-destructive/10 p-3 text-destructive text-sm",
|
|
810
821
|
children: error
|
|
811
822
|
}, undefined, false, undefined, this),
|
|
812
823
|
/* @__PURE__ */ jsxDEV4("div", {
|
|
@@ -834,14 +845,14 @@ function DealActionsModal({
|
|
|
834
845
|
children: [
|
|
835
846
|
/* @__PURE__ */ jsxDEV4("label", {
|
|
836
847
|
htmlFor: "lost-reason",
|
|
837
|
-
className: "
|
|
848
|
+
className: "mb-1 block font-medium text-muted-foreground text-sm",
|
|
838
849
|
children: "Why was this deal lost? *"
|
|
839
850
|
}, undefined, false, undefined, this),
|
|
840
851
|
/* @__PURE__ */ jsxDEV4("select", {
|
|
841
852
|
id: "lost-reason",
|
|
842
853
|
value: lostReason,
|
|
843
854
|
onChange: (e) => setLostReason(e.target.value),
|
|
844
|
-
className: "
|
|
855
|
+
className: "h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring",
|
|
845
856
|
children: [
|
|
846
857
|
/* @__PURE__ */ jsxDEV4("option", {
|
|
847
858
|
value: "",
|
|
@@ -883,7 +894,7 @@ function DealActionsModal({
|
|
|
883
894
|
children: [
|
|
884
895
|
/* @__PURE__ */ jsxDEV4("label", {
|
|
885
896
|
htmlFor: "lose-notes",
|
|
886
|
-
className: "
|
|
897
|
+
className: "mb-1 block font-medium text-muted-foreground text-sm",
|
|
887
898
|
children: "Notes (optional)"
|
|
888
899
|
}, undefined, false, undefined, this),
|
|
889
900
|
/* @__PURE__ */ jsxDEV4("textarea", {
|
|
@@ -892,12 +903,12 @@ function DealActionsModal({
|
|
|
892
903
|
onChange: (e) => setNotes(e.target.value),
|
|
893
904
|
placeholder: "Any additional details...",
|
|
894
905
|
rows: 3,
|
|
895
|
-
className: "
|
|
906
|
+
className: "w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring"
|
|
896
907
|
}, undefined, false, undefined, this)
|
|
897
908
|
]
|
|
898
909
|
}, undefined, true, undefined, this),
|
|
899
910
|
error && /* @__PURE__ */ jsxDEV4("div", {
|
|
900
|
-
className: "bg-destructive/10
|
|
911
|
+
className: "rounded-md bg-destructive/10 p-3 text-destructive text-sm",
|
|
901
912
|
children: error
|
|
902
913
|
}, undefined, false, undefined, this),
|
|
903
914
|
/* @__PURE__ */ jsxDEV4("div", {
|
|
@@ -926,14 +937,14 @@ function DealActionsModal({
|
|
|
926
937
|
children: [
|
|
927
938
|
/* @__PURE__ */ jsxDEV4("label", {
|
|
928
939
|
htmlFor: "move-stage",
|
|
929
|
-
className: "
|
|
940
|
+
className: "mb-1 block font-medium text-muted-foreground text-sm",
|
|
930
941
|
children: "Move to Stage"
|
|
931
942
|
}, undefined, false, undefined, this),
|
|
932
943
|
/* @__PURE__ */ jsxDEV4("select", {
|
|
933
944
|
id: "move-stage",
|
|
934
945
|
value: selectedStageId,
|
|
935
946
|
onChange: (e) => setSelectedStageId(e.target.value),
|
|
936
|
-
className: "
|
|
947
|
+
className: "h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring",
|
|
937
948
|
children: stages.map((stage) => /* @__PURE__ */ jsxDEV4("option", {
|
|
938
949
|
value: stage.id,
|
|
939
950
|
children: [
|
|
@@ -945,7 +956,7 @@ function DealActionsModal({
|
|
|
945
956
|
]
|
|
946
957
|
}, undefined, true, undefined, this),
|
|
947
958
|
error && /* @__PURE__ */ jsxDEV4("div", {
|
|
948
|
-
className: "bg-destructive/10
|
|
959
|
+
className: "rounded-md bg-destructive/10 p-3 text-destructive text-sm",
|
|
949
960
|
children: error
|
|
950
961
|
}, undefined, false, undefined, this),
|
|
951
962
|
/* @__PURE__ */ jsxDEV4("div", {
|
|
@@ -972,12 +983,305 @@ function DealActionsModal({
|
|
|
972
983
|
}, undefined, true, undefined, this);
|
|
973
984
|
}
|
|
974
985
|
|
|
975
|
-
// src/ui/
|
|
976
|
-
import { useCallback as useCallback3, useState as useState6 } from "react";
|
|
986
|
+
// src/ui/tables/DealListTab.tsx
|
|
977
987
|
import {
|
|
978
988
|
Button as Button3,
|
|
989
|
+
DataTable,
|
|
990
|
+
LoaderBlock
|
|
991
|
+
} from "@contractspec/lib.design-system";
|
|
992
|
+
import { useContractTable } from "@contractspec/lib.presentation-runtime-react";
|
|
993
|
+
import { Badge } from "@contractspec/lib.ui-kit-web/ui/badge";
|
|
994
|
+
import { HStack, VStack } from "@contractspec/lib.ui-kit-web/ui/stack";
|
|
995
|
+
import { Text } from "@contractspec/lib.ui-kit-web/ui/text";
|
|
996
|
+
import * as React from "react";
|
|
997
|
+
import { jsxDEV as jsxDEV5 } from "react/jsx-dev-runtime";
|
|
998
|
+
"use client";
|
|
999
|
+
function formatCurrency4(value, currency = "USD") {
|
|
1000
|
+
return new Intl.NumberFormat("en-US", {
|
|
1001
|
+
style: "currency",
|
|
1002
|
+
currency,
|
|
1003
|
+
minimumFractionDigits: 0,
|
|
1004
|
+
maximumFractionDigits: 0
|
|
1005
|
+
}).format(value);
|
|
1006
|
+
}
|
|
1007
|
+
function statusVariant(status) {
|
|
1008
|
+
switch (status) {
|
|
1009
|
+
case "WON":
|
|
1010
|
+
return "default";
|
|
1011
|
+
case "LOST":
|
|
1012
|
+
return "destructive";
|
|
1013
|
+
case "STALE":
|
|
1014
|
+
return "outline";
|
|
1015
|
+
default:
|
|
1016
|
+
return "secondary";
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
function DealListDataTable({
|
|
1020
|
+
deals,
|
|
1021
|
+
totalItems,
|
|
1022
|
+
pageIndex,
|
|
1023
|
+
pageSize,
|
|
1024
|
+
sorting,
|
|
1025
|
+
loading,
|
|
1026
|
+
onSortingChange,
|
|
1027
|
+
onPaginationChange,
|
|
1028
|
+
onDealClick
|
|
1029
|
+
}) {
|
|
1030
|
+
const controller = useContractTable({
|
|
1031
|
+
data: deals,
|
|
1032
|
+
columns: [
|
|
1033
|
+
{
|
|
1034
|
+
id: "deal",
|
|
1035
|
+
header: "Deal",
|
|
1036
|
+
label: "Deal",
|
|
1037
|
+
accessor: (deal) => deal.name,
|
|
1038
|
+
cell: ({ item }) => /* @__PURE__ */ jsxDEV5(VStack, {
|
|
1039
|
+
gap: "xs",
|
|
1040
|
+
children: [
|
|
1041
|
+
/* @__PURE__ */ jsxDEV5(Text, {
|
|
1042
|
+
className: "font-medium text-sm",
|
|
1043
|
+
children: item.name
|
|
1044
|
+
}, undefined, false, undefined, this),
|
|
1045
|
+
/* @__PURE__ */ jsxDEV5(Text, {
|
|
1046
|
+
className: "text-muted-foreground text-xs",
|
|
1047
|
+
children: item.companyId ?? "Unassigned company"
|
|
1048
|
+
}, undefined, false, undefined, this)
|
|
1049
|
+
]
|
|
1050
|
+
}, undefined, true, undefined, this),
|
|
1051
|
+
size: 240,
|
|
1052
|
+
minSize: 180,
|
|
1053
|
+
canSort: true,
|
|
1054
|
+
canPin: true,
|
|
1055
|
+
canResize: true
|
|
1056
|
+
},
|
|
1057
|
+
{
|
|
1058
|
+
id: "value",
|
|
1059
|
+
header: "Value",
|
|
1060
|
+
label: "Value",
|
|
1061
|
+
accessorKey: "value",
|
|
1062
|
+
cell: ({ item }) => formatCurrency4(item.value, item.currency),
|
|
1063
|
+
align: "right",
|
|
1064
|
+
size: 140,
|
|
1065
|
+
canSort: true,
|
|
1066
|
+
canResize: true
|
|
1067
|
+
},
|
|
1068
|
+
{
|
|
1069
|
+
id: "status",
|
|
1070
|
+
header: "Status",
|
|
1071
|
+
label: "Status",
|
|
1072
|
+
accessorKey: "status",
|
|
1073
|
+
cell: ({ value }) => /* @__PURE__ */ jsxDEV5(Badge, {
|
|
1074
|
+
variant: statusVariant(value),
|
|
1075
|
+
children: String(value)
|
|
1076
|
+
}, undefined, false, undefined, this),
|
|
1077
|
+
size: 130,
|
|
1078
|
+
canSort: true,
|
|
1079
|
+
canHide: true,
|
|
1080
|
+
canPin: true,
|
|
1081
|
+
canResize: true
|
|
1082
|
+
},
|
|
1083
|
+
{
|
|
1084
|
+
id: "expectedCloseDate",
|
|
1085
|
+
header: "Expected Close",
|
|
1086
|
+
label: "Expected Close",
|
|
1087
|
+
accessor: (deal) => deal.expectedCloseDate?.toISOString() ?? "",
|
|
1088
|
+
cell: ({ item }) => item.expectedCloseDate?.toLocaleDateString() ?? "Not scheduled",
|
|
1089
|
+
size: 170,
|
|
1090
|
+
canSort: true,
|
|
1091
|
+
canHide: true,
|
|
1092
|
+
canResize: true
|
|
1093
|
+
},
|
|
1094
|
+
{
|
|
1095
|
+
id: "updatedAt",
|
|
1096
|
+
header: "Updated",
|
|
1097
|
+
label: "Updated",
|
|
1098
|
+
accessor: (deal) => deal.updatedAt.toISOString(),
|
|
1099
|
+
cell: ({ item }) => item.updatedAt.toLocaleDateString(),
|
|
1100
|
+
size: 140,
|
|
1101
|
+
canSort: true,
|
|
1102
|
+
canHide: true,
|
|
1103
|
+
canResize: true
|
|
1104
|
+
},
|
|
1105
|
+
{
|
|
1106
|
+
id: "actions",
|
|
1107
|
+
header: "Actions",
|
|
1108
|
+
label: "Actions",
|
|
1109
|
+
accessor: (deal) => deal.id,
|
|
1110
|
+
cell: ({ item }) => /* @__PURE__ */ jsxDEV5(Button3, {
|
|
1111
|
+
variant: "ghost",
|
|
1112
|
+
size: "sm",
|
|
1113
|
+
onPress: () => onDealClick?.(item.id),
|
|
1114
|
+
children: "Actions"
|
|
1115
|
+
}, undefined, false, undefined, this),
|
|
1116
|
+
size: 120,
|
|
1117
|
+
canSort: false,
|
|
1118
|
+
canHide: false,
|
|
1119
|
+
canPin: false,
|
|
1120
|
+
canResize: false
|
|
1121
|
+
}
|
|
1122
|
+
],
|
|
1123
|
+
executionMode: "server",
|
|
1124
|
+
selectionMode: "multiple",
|
|
1125
|
+
totalItems,
|
|
1126
|
+
state: {
|
|
1127
|
+
sorting,
|
|
1128
|
+
pagination: {
|
|
1129
|
+
pageIndex,
|
|
1130
|
+
pageSize
|
|
1131
|
+
}
|
|
1132
|
+
},
|
|
1133
|
+
onSortingChange,
|
|
1134
|
+
onPaginationChange,
|
|
1135
|
+
initialState: {
|
|
1136
|
+
columnVisibility: { updatedAt: false },
|
|
1137
|
+
columnPinning: { left: ["deal", "status"], right: [] }
|
|
1138
|
+
},
|
|
1139
|
+
renderExpandedContent: (deal) => /* @__PURE__ */ jsxDEV5(VStack, {
|
|
1140
|
+
gap: "sm",
|
|
1141
|
+
className: "py-2",
|
|
1142
|
+
children: [
|
|
1143
|
+
/* @__PURE__ */ jsxDEV5(HStack, {
|
|
1144
|
+
justify: "between",
|
|
1145
|
+
children: [
|
|
1146
|
+
/* @__PURE__ */ jsxDEV5(Text, {
|
|
1147
|
+
className: "font-medium text-sm",
|
|
1148
|
+
children: "Owner"
|
|
1149
|
+
}, undefined, false, undefined, this),
|
|
1150
|
+
/* @__PURE__ */ jsxDEV5(Text, {
|
|
1151
|
+
className: "text-muted-foreground text-sm",
|
|
1152
|
+
children: deal.ownerId
|
|
1153
|
+
}, undefined, false, undefined, this)
|
|
1154
|
+
]
|
|
1155
|
+
}, undefined, true, undefined, this),
|
|
1156
|
+
/* @__PURE__ */ jsxDEV5(HStack, {
|
|
1157
|
+
justify: "between",
|
|
1158
|
+
children: [
|
|
1159
|
+
/* @__PURE__ */ jsxDEV5(Text, {
|
|
1160
|
+
className: "font-medium text-sm",
|
|
1161
|
+
children: "Contact"
|
|
1162
|
+
}, undefined, false, undefined, this),
|
|
1163
|
+
/* @__PURE__ */ jsxDEV5(Text, {
|
|
1164
|
+
className: "text-muted-foreground text-sm",
|
|
1165
|
+
children: deal.contactId ?? "No linked contact"
|
|
1166
|
+
}, undefined, false, undefined, this)
|
|
1167
|
+
]
|
|
1168
|
+
}, undefined, true, undefined, this),
|
|
1169
|
+
deal.wonSource ? /* @__PURE__ */ jsxDEV5(HStack, {
|
|
1170
|
+
justify: "between",
|
|
1171
|
+
children: [
|
|
1172
|
+
/* @__PURE__ */ jsxDEV5(Text, {
|
|
1173
|
+
className: "font-medium text-sm",
|
|
1174
|
+
children: "Won Source"
|
|
1175
|
+
}, undefined, false, undefined, this),
|
|
1176
|
+
/* @__PURE__ */ jsxDEV5(Text, {
|
|
1177
|
+
className: "text-muted-foreground text-sm",
|
|
1178
|
+
children: deal.wonSource
|
|
1179
|
+
}, undefined, false, undefined, this)
|
|
1180
|
+
]
|
|
1181
|
+
}, undefined, true, undefined, this) : null,
|
|
1182
|
+
deal.lostReason ? /* @__PURE__ */ jsxDEV5(HStack, {
|
|
1183
|
+
justify: "between",
|
|
1184
|
+
children: [
|
|
1185
|
+
/* @__PURE__ */ jsxDEV5(Text, {
|
|
1186
|
+
className: "font-medium text-sm",
|
|
1187
|
+
children: "Lost Reason"
|
|
1188
|
+
}, undefined, false, undefined, this),
|
|
1189
|
+
/* @__PURE__ */ jsxDEV5(Text, {
|
|
1190
|
+
className: "text-muted-foreground text-sm",
|
|
1191
|
+
children: deal.lostReason
|
|
1192
|
+
}, undefined, false, undefined, this)
|
|
1193
|
+
]
|
|
1194
|
+
}, undefined, true, undefined, this) : null,
|
|
1195
|
+
deal.notes ? /* @__PURE__ */ jsxDEV5(VStack, {
|
|
1196
|
+
gap: "xs",
|
|
1197
|
+
children: [
|
|
1198
|
+
/* @__PURE__ */ jsxDEV5(Text, {
|
|
1199
|
+
className: "font-medium text-sm",
|
|
1200
|
+
children: "Notes"
|
|
1201
|
+
}, undefined, false, undefined, this),
|
|
1202
|
+
/* @__PURE__ */ jsxDEV5(Text, {
|
|
1203
|
+
className: "text-muted-foreground text-sm",
|
|
1204
|
+
children: deal.notes
|
|
1205
|
+
}, undefined, false, undefined, this)
|
|
1206
|
+
]
|
|
1207
|
+
}, undefined, true, undefined, this) : null
|
|
1208
|
+
]
|
|
1209
|
+
}, undefined, true, undefined, this),
|
|
1210
|
+
getCanExpand: () => true
|
|
1211
|
+
});
|
|
1212
|
+
return /* @__PURE__ */ jsxDEV5(DataTable, {
|
|
1213
|
+
controller,
|
|
1214
|
+
title: "All Deals",
|
|
1215
|
+
description: "Server-mode table using the shared ContractSpec controller.",
|
|
1216
|
+
loading,
|
|
1217
|
+
toolbar: /* @__PURE__ */ jsxDEV5(HStack, {
|
|
1218
|
+
gap: "sm",
|
|
1219
|
+
className: "flex-wrap",
|
|
1220
|
+
children: [
|
|
1221
|
+
/* @__PURE__ */ jsxDEV5(Text, {
|
|
1222
|
+
className: "text-muted-foreground text-sm",
|
|
1223
|
+
children: [
|
|
1224
|
+
"Selected ",
|
|
1225
|
+
controller.selectedRowIds.length
|
|
1226
|
+
]
|
|
1227
|
+
}, undefined, true, undefined, this),
|
|
1228
|
+
/* @__PURE__ */ jsxDEV5(Text, {
|
|
1229
|
+
className: "text-muted-foreground text-sm",
|
|
1230
|
+
children: [
|
|
1231
|
+
totalItems,
|
|
1232
|
+
" total deals"
|
|
1233
|
+
]
|
|
1234
|
+
}, undefined, true, undefined, this)
|
|
1235
|
+
]
|
|
1236
|
+
}, undefined, true, undefined, this),
|
|
1237
|
+
footer: `Page ${controller.pageIndex + 1} of ${controller.pageCount}`,
|
|
1238
|
+
emptyState: /* @__PURE__ */ jsxDEV5("div", {
|
|
1239
|
+
className: "rounded-md border border-dashed p-8 text-center text-muted-foreground text-sm",
|
|
1240
|
+
children: "No deals found"
|
|
1241
|
+
}, undefined, false, undefined, this)
|
|
1242
|
+
}, undefined, false, undefined, this);
|
|
1243
|
+
}
|
|
1244
|
+
function DealListTab({
|
|
1245
|
+
onDealClick
|
|
1246
|
+
}) {
|
|
1247
|
+
const [sorting, setSorting] = React.useState([
|
|
1248
|
+
{ id: "value", desc: true }
|
|
1249
|
+
]);
|
|
1250
|
+
const [pagination, setPagination] = React.useState({
|
|
1251
|
+
pageIndex: 0,
|
|
1252
|
+
pageSize: 3
|
|
1253
|
+
});
|
|
1254
|
+
const { data, loading } = useDealList({
|
|
1255
|
+
pageIndex: pagination.pageIndex,
|
|
1256
|
+
pageSize: pagination.pageSize,
|
|
1257
|
+
sorting
|
|
1258
|
+
});
|
|
1259
|
+
if (loading && !data) {
|
|
1260
|
+
return /* @__PURE__ */ jsxDEV5(LoaderBlock, {
|
|
1261
|
+
label: "Loading deals..."
|
|
1262
|
+
}, undefined, false, undefined, this);
|
|
1263
|
+
}
|
|
1264
|
+
return /* @__PURE__ */ jsxDEV5(DealListDataTable, {
|
|
1265
|
+
deals: data?.deals ?? [],
|
|
1266
|
+
totalItems: data?.total ?? 0,
|
|
1267
|
+
pageIndex: pagination.pageIndex,
|
|
1268
|
+
pageSize: pagination.pageSize,
|
|
1269
|
+
sorting,
|
|
1270
|
+
loading,
|
|
1271
|
+
onSortingChange: (nextSorting) => {
|
|
1272
|
+
setSorting(nextSorting);
|
|
1273
|
+
setPagination((current) => ({ ...current, pageIndex: 0 }));
|
|
1274
|
+
},
|
|
1275
|
+
onPaginationChange: setPagination,
|
|
1276
|
+
onDealClick
|
|
1277
|
+
}, undefined, false, undefined, this);
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1280
|
+
// src/ui/CrmDashboard.tsx
|
|
1281
|
+
import {
|
|
1282
|
+
Button as Button4,
|
|
979
1283
|
ErrorState,
|
|
980
|
-
LoaderBlock,
|
|
1284
|
+
LoaderBlock as LoaderBlock2,
|
|
981
1285
|
StatCard,
|
|
982
1286
|
StatCardGroup
|
|
983
1287
|
} from "@contractspec/lib.design-system";
|
|
@@ -987,9 +1291,10 @@ import {
|
|
|
987
1291
|
TabsList,
|
|
988
1292
|
TabsTrigger
|
|
989
1293
|
} from "@contractspec/lib.ui-kit-web/ui/tabs";
|
|
990
|
-
import {
|
|
1294
|
+
import { useCallback as useCallback3, useState as useState7 } from "react";
|
|
1295
|
+
import { jsxDEV as jsxDEV6 } from "react/jsx-dev-runtime";
|
|
991
1296
|
"use client";
|
|
992
|
-
function
|
|
1297
|
+
function formatCurrency5(value, currency = "USD") {
|
|
993
1298
|
return new Intl.NumberFormat("en-US", {
|
|
994
1299
|
style: "currency",
|
|
995
1300
|
currency,
|
|
@@ -998,9 +1303,9 @@ function formatCurrency4(value, currency = "USD") {
|
|
|
998
1303
|
}).format(value);
|
|
999
1304
|
}
|
|
1000
1305
|
function CrmDashboard() {
|
|
1001
|
-
const [isCreateModalOpen, setIsCreateModalOpen] =
|
|
1002
|
-
const [selectedDeal, setSelectedDeal] =
|
|
1003
|
-
const [isDealActionsOpen, setIsDealActionsOpen] =
|
|
1306
|
+
const [isCreateModalOpen, setIsCreateModalOpen] = useState7(false);
|
|
1307
|
+
const [selectedDeal, setSelectedDeal] = useState7(null);
|
|
1308
|
+
const [isDealActionsOpen, setIsDealActionsOpen] = useState7(false);
|
|
1004
1309
|
const { data, dealsByStage, stages, loading, error, stats, refetch } = useDealList();
|
|
1005
1310
|
const mutations = useDealMutations({
|
|
1006
1311
|
onSuccess: () => {
|
|
@@ -1018,32 +1323,32 @@ function CrmDashboard() {
|
|
|
1018
1323
|
await mutations.moveDeal({ dealId, stageId: toStageId });
|
|
1019
1324
|
}, [mutations]);
|
|
1020
1325
|
if (loading && !data) {
|
|
1021
|
-
return /* @__PURE__ */
|
|
1326
|
+
return /* @__PURE__ */ jsxDEV6(LoaderBlock2, {
|
|
1022
1327
|
label: "Loading CRM..."
|
|
1023
1328
|
}, undefined, false, undefined, this);
|
|
1024
1329
|
}
|
|
1025
1330
|
if (error) {
|
|
1026
|
-
return /* @__PURE__ */
|
|
1331
|
+
return /* @__PURE__ */ jsxDEV6(ErrorState, {
|
|
1027
1332
|
title: "Failed to load CRM",
|
|
1028
1333
|
description: error.message,
|
|
1029
1334
|
onRetry: refetch,
|
|
1030
1335
|
retryLabel: "Retry"
|
|
1031
1336
|
}, undefined, false, undefined, this);
|
|
1032
1337
|
}
|
|
1033
|
-
return /* @__PURE__ */
|
|
1338
|
+
return /* @__PURE__ */ jsxDEV6("div", {
|
|
1034
1339
|
className: "space-y-6",
|
|
1035
1340
|
children: [
|
|
1036
|
-
/* @__PURE__ */
|
|
1341
|
+
/* @__PURE__ */ jsxDEV6("div", {
|
|
1037
1342
|
className: "flex items-center justify-between",
|
|
1038
1343
|
children: [
|
|
1039
|
-
/* @__PURE__ */
|
|
1040
|
-
className: "text-2xl
|
|
1344
|
+
/* @__PURE__ */ jsxDEV6("h2", {
|
|
1345
|
+
className: "font-bold text-2xl",
|
|
1041
1346
|
children: "CRM Pipeline"
|
|
1042
1347
|
}, undefined, false, undefined, this),
|
|
1043
|
-
/* @__PURE__ */
|
|
1348
|
+
/* @__PURE__ */ jsxDEV6(Button4, {
|
|
1044
1349
|
onClick: () => setIsCreateModalOpen(true),
|
|
1045
1350
|
children: [
|
|
1046
|
-
/* @__PURE__ */
|
|
1351
|
+
/* @__PURE__ */ jsxDEV6("span", {
|
|
1047
1352
|
className: "mr-2",
|
|
1048
1353
|
children: "+"
|
|
1049
1354
|
}, undefined, false, undefined, this),
|
|
@@ -1052,60 +1357,60 @@ function CrmDashboard() {
|
|
|
1052
1357
|
}, undefined, true, undefined, this)
|
|
1053
1358
|
]
|
|
1054
1359
|
}, undefined, true, undefined, this),
|
|
1055
|
-
stats && /* @__PURE__ */
|
|
1360
|
+
stats && /* @__PURE__ */ jsxDEV6(StatCardGroup, {
|
|
1056
1361
|
children: [
|
|
1057
|
-
/* @__PURE__ */
|
|
1362
|
+
/* @__PURE__ */ jsxDEV6(StatCard, {
|
|
1058
1363
|
label: "Total Pipeline",
|
|
1059
|
-
value:
|
|
1364
|
+
value: formatCurrency5(stats.totalValue),
|
|
1060
1365
|
hint: `${stats.total} deals`
|
|
1061
1366
|
}, undefined, false, undefined, this),
|
|
1062
|
-
/* @__PURE__ */
|
|
1367
|
+
/* @__PURE__ */ jsxDEV6(StatCard, {
|
|
1063
1368
|
label: "Open Deals",
|
|
1064
|
-
value:
|
|
1369
|
+
value: formatCurrency5(stats.openValue),
|
|
1065
1370
|
hint: `${stats.openCount} active`
|
|
1066
1371
|
}, undefined, false, undefined, this),
|
|
1067
|
-
/* @__PURE__ */
|
|
1372
|
+
/* @__PURE__ */ jsxDEV6(StatCard, {
|
|
1068
1373
|
label: "Won",
|
|
1069
|
-
value:
|
|
1374
|
+
value: formatCurrency5(stats.wonValue),
|
|
1070
1375
|
hint: `${stats.wonCount} closed`
|
|
1071
1376
|
}, undefined, false, undefined, this),
|
|
1072
|
-
/* @__PURE__ */
|
|
1377
|
+
/* @__PURE__ */ jsxDEV6(StatCard, {
|
|
1073
1378
|
label: "Lost",
|
|
1074
1379
|
value: stats.lostCount,
|
|
1075
1380
|
hint: "deals lost"
|
|
1076
1381
|
}, undefined, false, undefined, this)
|
|
1077
1382
|
]
|
|
1078
1383
|
}, undefined, true, undefined, this),
|
|
1079
|
-
/* @__PURE__ */
|
|
1384
|
+
/* @__PURE__ */ jsxDEV6(Tabs, {
|
|
1080
1385
|
defaultValue: "pipeline",
|
|
1081
1386
|
className: "w-full",
|
|
1082
1387
|
children: [
|
|
1083
|
-
/* @__PURE__ */
|
|
1388
|
+
/* @__PURE__ */ jsxDEV6(TabsList, {
|
|
1084
1389
|
children: [
|
|
1085
|
-
/* @__PURE__ */
|
|
1390
|
+
/* @__PURE__ */ jsxDEV6(TabsTrigger, {
|
|
1086
1391
|
value: "pipeline",
|
|
1087
1392
|
children: [
|
|
1088
|
-
/* @__PURE__ */
|
|
1393
|
+
/* @__PURE__ */ jsxDEV6("span", {
|
|
1089
1394
|
className: "mr-2",
|
|
1090
1395
|
children: "\uD83D\uDCCA"
|
|
1091
1396
|
}, undefined, false, undefined, this),
|
|
1092
1397
|
"Pipeline"
|
|
1093
1398
|
]
|
|
1094
1399
|
}, undefined, true, undefined, this),
|
|
1095
|
-
/* @__PURE__ */
|
|
1400
|
+
/* @__PURE__ */ jsxDEV6(TabsTrigger, {
|
|
1096
1401
|
value: "list",
|
|
1097
1402
|
children: [
|
|
1098
|
-
/* @__PURE__ */
|
|
1403
|
+
/* @__PURE__ */ jsxDEV6("span", {
|
|
1099
1404
|
className: "mr-2",
|
|
1100
1405
|
children: "\uD83D\uDCCB"
|
|
1101
1406
|
}, undefined, false, undefined, this),
|
|
1102
1407
|
"All Deals"
|
|
1103
1408
|
]
|
|
1104
1409
|
}, undefined, true, undefined, this),
|
|
1105
|
-
/* @__PURE__ */
|
|
1410
|
+
/* @__PURE__ */ jsxDEV6(TabsTrigger, {
|
|
1106
1411
|
value: "metrics",
|
|
1107
1412
|
children: [
|
|
1108
|
-
/* @__PURE__ */
|
|
1413
|
+
/* @__PURE__ */ jsxDEV6("span", {
|
|
1109
1414
|
className: "mr-2",
|
|
1110
1415
|
children: "\uD83D\uDCC8"
|
|
1111
1416
|
}, undefined, false, undefined, this),
|
|
@@ -1114,34 +1419,33 @@ function CrmDashboard() {
|
|
|
1114
1419
|
}, undefined, true, undefined, this)
|
|
1115
1420
|
]
|
|
1116
1421
|
}, undefined, true, undefined, this),
|
|
1117
|
-
/* @__PURE__ */
|
|
1422
|
+
/* @__PURE__ */ jsxDEV6(TabsContent, {
|
|
1118
1423
|
value: "pipeline",
|
|
1119
1424
|
className: "min-h-[400px]",
|
|
1120
|
-
children: /* @__PURE__ */
|
|
1425
|
+
children: /* @__PURE__ */ jsxDEV6(CrmPipelineBoard, {
|
|
1121
1426
|
dealsByStage,
|
|
1122
1427
|
stages,
|
|
1123
1428
|
onDealClick: handleDealClick,
|
|
1124
1429
|
onDealMove: handleDealMove
|
|
1125
1430
|
}, undefined, false, undefined, this)
|
|
1126
1431
|
}, undefined, false, undefined, this),
|
|
1127
|
-
/* @__PURE__ */
|
|
1432
|
+
/* @__PURE__ */ jsxDEV6(TabsContent, {
|
|
1128
1433
|
value: "list",
|
|
1129
1434
|
className: "min-h-[400px]",
|
|
1130
|
-
children: /* @__PURE__ */
|
|
1131
|
-
data,
|
|
1435
|
+
children: /* @__PURE__ */ jsxDEV6(DealListTab, {
|
|
1132
1436
|
onDealClick: handleDealClick
|
|
1133
1437
|
}, undefined, false, undefined, this)
|
|
1134
1438
|
}, undefined, false, undefined, this),
|
|
1135
|
-
/* @__PURE__ */
|
|
1439
|
+
/* @__PURE__ */ jsxDEV6(TabsContent, {
|
|
1136
1440
|
value: "metrics",
|
|
1137
1441
|
className: "min-h-[400px]",
|
|
1138
|
-
children: /* @__PURE__ */
|
|
1442
|
+
children: /* @__PURE__ */ jsxDEV6(MetricsTab, {
|
|
1139
1443
|
stats
|
|
1140
1444
|
}, undefined, false, undefined, this)
|
|
1141
1445
|
}, undefined, false, undefined, this)
|
|
1142
1446
|
]
|
|
1143
1447
|
}, undefined, true, undefined, this),
|
|
1144
|
-
/* @__PURE__ */
|
|
1448
|
+
/* @__PURE__ */ jsxDEV6(CreateDealModal, {
|
|
1145
1449
|
isOpen: isCreateModalOpen,
|
|
1146
1450
|
onClose: () => setIsCreateModalOpen(false),
|
|
1147
1451
|
onSubmit: async (input) => {
|
|
@@ -1150,7 +1454,7 @@ function CrmDashboard() {
|
|
|
1150
1454
|
stages,
|
|
1151
1455
|
isLoading: mutations.createState.loading
|
|
1152
1456
|
}, undefined, false, undefined, this),
|
|
1153
|
-
/* @__PURE__ */
|
|
1457
|
+
/* @__PURE__ */ jsxDEV6(DealActionsModal, {
|
|
1154
1458
|
isOpen: isDealActionsOpen,
|
|
1155
1459
|
deal: selectedDeal,
|
|
1156
1460
|
stages,
|
|
@@ -1173,113 +1477,31 @@ function CrmDashboard() {
|
|
|
1173
1477
|
]
|
|
1174
1478
|
}, undefined, true, undefined, this);
|
|
1175
1479
|
}
|
|
1176
|
-
function DealListTab({ data, onDealClick }) {
|
|
1177
|
-
if (!data?.deals.length) {
|
|
1178
|
-
return /* @__PURE__ */ jsxDEV5("div", {
|
|
1179
|
-
className: "text-muted-foreground flex h-64 items-center justify-center",
|
|
1180
|
-
children: "No deals found"
|
|
1181
|
-
}, undefined, false, undefined, this);
|
|
1182
|
-
}
|
|
1183
|
-
return /* @__PURE__ */ jsxDEV5("div", {
|
|
1184
|
-
className: "border-border rounded-lg border",
|
|
1185
|
-
children: /* @__PURE__ */ jsxDEV5("table", {
|
|
1186
|
-
className: "w-full",
|
|
1187
|
-
children: [
|
|
1188
|
-
/* @__PURE__ */ jsxDEV5("thead", {
|
|
1189
|
-
className: "border-border bg-muted/30 border-b",
|
|
1190
|
-
children: /* @__PURE__ */ jsxDEV5("tr", {
|
|
1191
|
-
children: [
|
|
1192
|
-
/* @__PURE__ */ jsxDEV5("th", {
|
|
1193
|
-
className: "px-4 py-3 text-left text-sm font-medium",
|
|
1194
|
-
children: "Deal"
|
|
1195
|
-
}, undefined, false, undefined, this),
|
|
1196
|
-
/* @__PURE__ */ jsxDEV5("th", {
|
|
1197
|
-
className: "px-4 py-3 text-left text-sm font-medium",
|
|
1198
|
-
children: "Value"
|
|
1199
|
-
}, undefined, false, undefined, this),
|
|
1200
|
-
/* @__PURE__ */ jsxDEV5("th", {
|
|
1201
|
-
className: "px-4 py-3 text-left text-sm font-medium",
|
|
1202
|
-
children: "Status"
|
|
1203
|
-
}, undefined, false, undefined, this),
|
|
1204
|
-
/* @__PURE__ */ jsxDEV5("th", {
|
|
1205
|
-
className: "px-4 py-3 text-left text-sm font-medium",
|
|
1206
|
-
children: "Expected Close"
|
|
1207
|
-
}, undefined, false, undefined, this),
|
|
1208
|
-
/* @__PURE__ */ jsxDEV5("th", {
|
|
1209
|
-
className: "px-4 py-3 text-left text-sm font-medium",
|
|
1210
|
-
children: "Actions"
|
|
1211
|
-
}, undefined, false, undefined, this)
|
|
1212
|
-
]
|
|
1213
|
-
}, undefined, true, undefined, this)
|
|
1214
|
-
}, undefined, false, undefined, this),
|
|
1215
|
-
/* @__PURE__ */ jsxDEV5("tbody", {
|
|
1216
|
-
className: "divide-border divide-y",
|
|
1217
|
-
children: data.deals.map((deal) => /* @__PURE__ */ jsxDEV5("tr", {
|
|
1218
|
-
className: "hover:bg-muted/50",
|
|
1219
|
-
children: [
|
|
1220
|
-
/* @__PURE__ */ jsxDEV5("td", {
|
|
1221
|
-
className: "px-4 py-3",
|
|
1222
|
-
children: /* @__PURE__ */ jsxDEV5("div", {
|
|
1223
|
-
className: "font-medium",
|
|
1224
|
-
children: deal.name
|
|
1225
|
-
}, undefined, false, undefined, this)
|
|
1226
|
-
}, undefined, false, undefined, this),
|
|
1227
|
-
/* @__PURE__ */ jsxDEV5("td", {
|
|
1228
|
-
className: "px-4 py-3 font-mono",
|
|
1229
|
-
children: formatCurrency4(deal.value, deal.currency)
|
|
1230
|
-
}, undefined, false, undefined, this),
|
|
1231
|
-
/* @__PURE__ */ jsxDEV5("td", {
|
|
1232
|
-
className: "px-4 py-3",
|
|
1233
|
-
children: /* @__PURE__ */ jsxDEV5("span", {
|
|
1234
|
-
className: `inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${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"}`,
|
|
1235
|
-
children: deal.status
|
|
1236
|
-
}, undefined, false, undefined, this)
|
|
1237
|
-
}, undefined, false, undefined, this),
|
|
1238
|
-
/* @__PURE__ */ jsxDEV5("td", {
|
|
1239
|
-
className: "text-muted-foreground px-4 py-3",
|
|
1240
|
-
children: deal.expectedCloseDate?.toLocaleDateString() ?? "-"
|
|
1241
|
-
}, undefined, false, undefined, this),
|
|
1242
|
-
/* @__PURE__ */ jsxDEV5("td", {
|
|
1243
|
-
className: "px-4 py-3",
|
|
1244
|
-
children: /* @__PURE__ */ jsxDEV5(Button3, {
|
|
1245
|
-
variant: "ghost",
|
|
1246
|
-
size: "sm",
|
|
1247
|
-
onPress: () => onDealClick?.(deal.id),
|
|
1248
|
-
children: "Actions"
|
|
1249
|
-
}, undefined, false, undefined, this)
|
|
1250
|
-
}, undefined, false, undefined, this)
|
|
1251
|
-
]
|
|
1252
|
-
}, deal.id, true, undefined, this))
|
|
1253
|
-
}, undefined, false, undefined, this)
|
|
1254
|
-
]
|
|
1255
|
-
}, undefined, true, undefined, this)
|
|
1256
|
-
}, undefined, false, undefined, this);
|
|
1257
|
-
}
|
|
1258
1480
|
function MetricsTab({
|
|
1259
1481
|
stats
|
|
1260
1482
|
}) {
|
|
1261
1483
|
if (!stats)
|
|
1262
1484
|
return null;
|
|
1263
|
-
return /* @__PURE__ */
|
|
1485
|
+
return /* @__PURE__ */ jsxDEV6("div", {
|
|
1264
1486
|
className: "space-y-6",
|
|
1265
|
-
children: /* @__PURE__ */
|
|
1266
|
-
className: "border-border bg-card
|
|
1487
|
+
children: /* @__PURE__ */ jsxDEV6("div", {
|
|
1488
|
+
className: "rounded-xl border border-border bg-card p-6",
|
|
1267
1489
|
children: [
|
|
1268
|
-
/* @__PURE__ */
|
|
1269
|
-
className: "mb-4 text-lg
|
|
1490
|
+
/* @__PURE__ */ jsxDEV6("h3", {
|
|
1491
|
+
className: "mb-4 font-semibold text-lg",
|
|
1270
1492
|
children: "Pipeline Overview"
|
|
1271
1493
|
}, undefined, false, undefined, this),
|
|
1272
|
-
/* @__PURE__ */
|
|
1494
|
+
/* @__PURE__ */ jsxDEV6("dl", {
|
|
1273
1495
|
className: "grid gap-4 sm:grid-cols-3",
|
|
1274
1496
|
children: [
|
|
1275
|
-
/* @__PURE__ */
|
|
1497
|
+
/* @__PURE__ */ jsxDEV6("div", {
|
|
1276
1498
|
children: [
|
|
1277
|
-
/* @__PURE__ */
|
|
1499
|
+
/* @__PURE__ */ jsxDEV6("dt", {
|
|
1278
1500
|
className: "text-muted-foreground text-sm",
|
|
1279
1501
|
children: "Win Rate"
|
|
1280
1502
|
}, undefined, false, undefined, this),
|
|
1281
|
-
/* @__PURE__ */
|
|
1282
|
-
className: "text-2xl
|
|
1503
|
+
/* @__PURE__ */ jsxDEV6("dd", {
|
|
1504
|
+
className: "font-semibold text-2xl",
|
|
1283
1505
|
children: [
|
|
1284
1506
|
stats.total > 0 ? (stats.wonCount / stats.total * 100).toFixed(0) : 0,
|
|
1285
1507
|
"%"
|
|
@@ -1287,26 +1509,26 @@ function MetricsTab({
|
|
|
1287
1509
|
}, undefined, true, undefined, this)
|
|
1288
1510
|
]
|
|
1289
1511
|
}, undefined, true, undefined, this),
|
|
1290
|
-
/* @__PURE__ */
|
|
1512
|
+
/* @__PURE__ */ jsxDEV6("div", {
|
|
1291
1513
|
children: [
|
|
1292
|
-
/* @__PURE__ */
|
|
1514
|
+
/* @__PURE__ */ jsxDEV6("dt", {
|
|
1293
1515
|
className: "text-muted-foreground text-sm",
|
|
1294
1516
|
children: "Avg Deal Size"
|
|
1295
1517
|
}, undefined, false, undefined, this),
|
|
1296
|
-
/* @__PURE__ */
|
|
1297
|
-
className: "text-2xl
|
|
1298
|
-
children:
|
|
1518
|
+
/* @__PURE__ */ jsxDEV6("dd", {
|
|
1519
|
+
className: "font-semibold text-2xl",
|
|
1520
|
+
children: formatCurrency5(stats.total > 0 ? stats.totalValue / stats.total : 0)
|
|
1299
1521
|
}, undefined, false, undefined, this)
|
|
1300
1522
|
]
|
|
1301
1523
|
}, undefined, true, undefined, this),
|
|
1302
|
-
/* @__PURE__ */
|
|
1524
|
+
/* @__PURE__ */ jsxDEV6("div", {
|
|
1303
1525
|
children: [
|
|
1304
|
-
/* @__PURE__ */
|
|
1526
|
+
/* @__PURE__ */ jsxDEV6("dt", {
|
|
1305
1527
|
className: "text-muted-foreground text-sm",
|
|
1306
1528
|
children: "Conversion"
|
|
1307
1529
|
}, undefined, false, undefined, this),
|
|
1308
|
-
/* @__PURE__ */
|
|
1309
|
-
className: "text-2xl
|
|
1530
|
+
/* @__PURE__ */ jsxDEV6("dd", {
|
|
1531
|
+
className: "font-semibold text-2xl",
|
|
1310
1532
|
children: [
|
|
1311
1533
|
stats.wonCount,
|
|
1312
1534
|
" / ",
|