@contractspec/example.crm-pipeline 3.7.7 → 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/CHANGELOG.md +36 -0
- package/README.md +2 -1
- package/dist/browser/docs/crm-pipeline.docblock.js +1 -1
- package/dist/browser/docs/index.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 +392 -159
- package/dist/browser/ui/CrmDashboard.js +366 -144
- package/dist/browser/ui/hooks/index.js +19 -8
- package/dist/browser/ui/hooks/useDealList.js +19 -8
- package/dist/browser/ui/index.js +391 -158
- package/dist/browser/ui/renderers/index.js +32 -10
- package/dist/browser/ui/renderers/pipeline.markdown.js +13 -2
- package/dist/browser/ui/renderers/pipeline.renderer.js +19 -8
- package/dist/browser/ui/tables/DealListTab.js +390 -0
- package/dist/docs/crm-pipeline.docblock.js +1 -1
- package/dist/docs/index.js +1 -1
- package/dist/handlers/crm.handlers.d.ts +2 -0
- package/dist/handlers/crm.handlers.js +13 -2
- package/dist/handlers/index.js +13 -2
- package/dist/index.js +392 -159
- package/dist/node/docs/crm-pipeline.docblock.js +1 -1
- package/dist/node/docs/index.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 +392 -159
- package/dist/node/ui/CrmDashboard.js +366 -144
- package/dist/node/ui/hooks/index.js +19 -8
- package/dist/node/ui/hooks/useDealList.js +19 -8
- package/dist/node/ui/index.js +391 -158
- package/dist/node/ui/renderers/index.js +32 -10
- package/dist/node/ui/renderers/pipeline.markdown.js +13 -2
- package/dist/node/ui/renderers/pipeline.renderer.js +19 -8
- package/dist/node/ui/tables/DealListTab.js +390 -0
- package/dist/ui/CrmDashboard.js +366 -144
- package/dist/ui/hooks/index.js +19 -8
- package/dist/ui/hooks/useDealList.d.ts +8 -2
- package/dist/ui/hooks/useDealList.js +19 -8
- package/dist/ui/index.js +391 -158
- package/dist/ui/renderers/index.js +32 -10
- package/dist/ui/renderers/pipeline.markdown.js +13 -2
- package/dist/ui/renderers/pipeline.renderer.js +19 -8
- 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 +27 -12
- package/src/docs/crm-pipeline.docblock.ts +1 -1
- package/src/handlers/crm.handlers.ts +18 -1
- package/src/ui/CrmDashboard.tsx +2 -71
- package/src/ui/hooks/useDealList.ts +36 -8
- package/src/ui/tables/DealListTab.smoke.test.tsx +149 -0
- package/src/ui/tables/DealListTab.tsx +276 -0
package/dist/ui/CrmDashboard.js
CHANGED
|
@@ -169,8 +169,13 @@ function useDealList(options = {}) {
|
|
|
169
169
|
const [stages, setStages] = useState2([]);
|
|
170
170
|
const [loading, setLoading] = useState2(true);
|
|
171
171
|
const [error, setError] = useState2(null);
|
|
172
|
-
const [
|
|
172
|
+
const [internalPage, setInternalPage] = useState2(0);
|
|
173
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;
|
|
174
179
|
const fetchData = useCallback(async () => {
|
|
175
180
|
setLoading(true);
|
|
176
181
|
setError(null);
|
|
@@ -182,8 +187,10 @@ function useDealList(options = {}) {
|
|
|
182
187
|
stageId: options.stageId,
|
|
183
188
|
status: options.status === "all" ? undefined : options.status,
|
|
184
189
|
search: options.search,
|
|
185
|
-
limit:
|
|
186
|
-
offset:
|
|
190
|
+
limit: pageSize,
|
|
191
|
+
offset: pageIndex * pageSize,
|
|
192
|
+
sortBy: sortBy === "name" || sortBy === "value" || sortBy === "status" || sortBy === "expectedCloseDate" || sortBy === "updatedAt" ? sortBy : undefined,
|
|
193
|
+
sortDirection
|
|
187
194
|
}),
|
|
188
195
|
crm.getDealsByStage({ projectId, pipelineId }),
|
|
189
196
|
crm.getPipelineStages({ pipelineId })
|
|
@@ -203,8 +210,10 @@ function useDealList(options = {}) {
|
|
|
203
210
|
options.stageId,
|
|
204
211
|
options.status,
|
|
205
212
|
options.search,
|
|
206
|
-
|
|
207
|
-
|
|
213
|
+
pageIndex,
|
|
214
|
+
pageSize,
|
|
215
|
+
sortBy,
|
|
216
|
+
sortDirection
|
|
208
217
|
]);
|
|
209
218
|
useEffect(() => {
|
|
210
219
|
fetchData();
|
|
@@ -232,10 +241,12 @@ function useDealList(options = {}) {
|
|
|
232
241
|
loading,
|
|
233
242
|
error,
|
|
234
243
|
stats,
|
|
235
|
-
page,
|
|
244
|
+
page: pageIndex + 1,
|
|
245
|
+
pageIndex,
|
|
246
|
+
pageSize,
|
|
236
247
|
refetch: fetchData,
|
|
237
|
-
nextPage: () =>
|
|
238
|
-
prevPage: () =>
|
|
248
|
+
nextPage: options.pageIndex === undefined ? () => setInternalPage((page) => page + 1) : undefined,
|
|
249
|
+
prevPage: options.pageIndex === undefined ? () => pageIndex > 0 && setInternalPage((page) => page - 1) : undefined
|
|
239
250
|
};
|
|
240
251
|
}
|
|
241
252
|
|
|
@@ -972,11 +983,305 @@ function DealActionsModal({
|
|
|
972
983
|
}, undefined, true, undefined, this);
|
|
973
984
|
}
|
|
974
985
|
|
|
975
|
-
// src/ui/
|
|
986
|
+
// src/ui/tables/DealListTab.tsx
|
|
976
987
|
import {
|
|
977
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,
|
|
978
1283
|
ErrorState,
|
|
979
|
-
LoaderBlock,
|
|
1284
|
+
LoaderBlock as LoaderBlock2,
|
|
980
1285
|
StatCard,
|
|
981
1286
|
StatCardGroup
|
|
982
1287
|
} from "@contractspec/lib.design-system";
|
|
@@ -986,10 +1291,10 @@ import {
|
|
|
986
1291
|
TabsList,
|
|
987
1292
|
TabsTrigger
|
|
988
1293
|
} from "@contractspec/lib.ui-kit-web/ui/tabs";
|
|
989
|
-
import { useCallback as useCallback3, useState as
|
|
990
|
-
import { jsxDEV as
|
|
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__ */
|
|
1344
|
+
/* @__PURE__ */ jsxDEV6("h2", {
|
|
1040
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,112 +1477,30 @@ 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: "flex h-64 items-center justify-center text-muted-foreground",
|
|
1180
|
-
children: "No deals found"
|
|
1181
|
-
}, undefined, false, undefined, this);
|
|
1182
|
-
}
|
|
1183
|
-
return /* @__PURE__ */ jsxDEV5("div", {
|
|
1184
|
-
className: "rounded-lg border border-border",
|
|
1185
|
-
children: /* @__PURE__ */ jsxDEV5("table", {
|
|
1186
|
-
className: "w-full",
|
|
1187
|
-
children: [
|
|
1188
|
-
/* @__PURE__ */ jsxDEV5("thead", {
|
|
1189
|
-
className: "border-border border-b bg-muted/30",
|
|
1190
|
-
children: /* @__PURE__ */ jsxDEV5("tr", {
|
|
1191
|
-
children: [
|
|
1192
|
-
/* @__PURE__ */ jsxDEV5("th", {
|
|
1193
|
-
className: "px-4 py-3 text-left font-medium text-sm",
|
|
1194
|
-
children: "Deal"
|
|
1195
|
-
}, undefined, false, undefined, this),
|
|
1196
|
-
/* @__PURE__ */ jsxDEV5("th", {
|
|
1197
|
-
className: "px-4 py-3 text-left font-medium text-sm",
|
|
1198
|
-
children: "Value"
|
|
1199
|
-
}, undefined, false, undefined, this),
|
|
1200
|
-
/* @__PURE__ */ jsxDEV5("th", {
|
|
1201
|
-
className: "px-4 py-3 text-left font-medium text-sm",
|
|
1202
|
-
children: "Status"
|
|
1203
|
-
}, undefined, false, undefined, this),
|
|
1204
|
-
/* @__PURE__ */ jsxDEV5("th", {
|
|
1205
|
-
className: "px-4 py-3 text-left font-medium text-sm",
|
|
1206
|
-
children: "Expected Close"
|
|
1207
|
-
}, undefined, false, undefined, this),
|
|
1208
|
-
/* @__PURE__ */ jsxDEV5("th", {
|
|
1209
|
-
className: "px-4 py-3 text-left font-medium text-sm",
|
|
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-y divide-border",
|
|
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 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"}`,
|
|
1235
|
-
children: deal.status
|
|
1236
|
-
}, undefined, false, undefined, this)
|
|
1237
|
-
}, undefined, false, undefined, this),
|
|
1238
|
-
/* @__PURE__ */ jsxDEV5("td", {
|
|
1239
|
-
className: "px-4 py-3 text-muted-foreground",
|
|
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__ */
|
|
1487
|
+
children: /* @__PURE__ */ jsxDEV6("div", {
|
|
1266
1488
|
className: "rounded-xl border border-border bg-card p-6",
|
|
1267
1489
|
children: [
|
|
1268
|
-
/* @__PURE__ */
|
|
1490
|
+
/* @__PURE__ */ jsxDEV6("h3", {
|
|
1269
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__ */
|
|
1503
|
+
/* @__PURE__ */ jsxDEV6("dd", {
|
|
1282
1504
|
className: "font-semibold text-2xl",
|
|
1283
1505
|
children: [
|
|
1284
1506
|
stats.total > 0 ? (stats.wonCount / stats.total * 100).toFixed(0) : 0,
|
|
@@ -1287,25 +1509,25 @@ 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__ */
|
|
1518
|
+
/* @__PURE__ */ jsxDEV6("dd", {
|
|
1297
1519
|
className: "font-semibold text-2xl",
|
|
1298
|
-
children:
|
|
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__ */
|
|
1530
|
+
/* @__PURE__ */ jsxDEV6("dd", {
|
|
1309
1531
|
className: "font-semibold text-2xl",
|
|
1310
1532
|
children: [
|
|
1311
1533
|
stats.wonCount,
|