@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
|
@@ -168,8 +168,13 @@ function useDealList(options = {}) {
|
|
|
168
168
|
const [stages, setStages] = useState2([]);
|
|
169
169
|
const [loading, setLoading] = useState2(true);
|
|
170
170
|
const [error, setError] = useState2(null);
|
|
171
|
-
const [
|
|
171
|
+
const [internalPage, setInternalPage] = useState2(0);
|
|
172
172
|
const pipelineId = options.pipelineId ?? "pipeline-1";
|
|
173
|
+
const pageIndex = options.pageIndex ?? internalPage;
|
|
174
|
+
const pageSize = options.pageSize ?? options.limit ?? 50;
|
|
175
|
+
const [sort] = options.sorting ?? [];
|
|
176
|
+
const sortBy = sort?.id;
|
|
177
|
+
const sortDirection = sort ? sort.desc ? "desc" : "asc" : undefined;
|
|
173
178
|
const fetchData = useCallback(async () => {
|
|
174
179
|
setLoading(true);
|
|
175
180
|
setError(null);
|
|
@@ -181,8 +186,10 @@ function useDealList(options = {}) {
|
|
|
181
186
|
stageId: options.stageId,
|
|
182
187
|
status: options.status === "all" ? undefined : options.status,
|
|
183
188
|
search: options.search,
|
|
184
|
-
limit:
|
|
185
|
-
offset:
|
|
189
|
+
limit: pageSize,
|
|
190
|
+
offset: pageIndex * pageSize,
|
|
191
|
+
sortBy: sortBy === "name" || sortBy === "value" || sortBy === "status" || sortBy === "expectedCloseDate" || sortBy === "updatedAt" ? sortBy : undefined,
|
|
192
|
+
sortDirection
|
|
186
193
|
}),
|
|
187
194
|
crm.getDealsByStage({ projectId, pipelineId }),
|
|
188
195
|
crm.getPipelineStages({ pipelineId })
|
|
@@ -202,8 +209,10 @@ function useDealList(options = {}) {
|
|
|
202
209
|
options.stageId,
|
|
203
210
|
options.status,
|
|
204
211
|
options.search,
|
|
205
|
-
|
|
206
|
-
|
|
212
|
+
pageIndex,
|
|
213
|
+
pageSize,
|
|
214
|
+
sortBy,
|
|
215
|
+
sortDirection
|
|
207
216
|
]);
|
|
208
217
|
useEffect(() => {
|
|
209
218
|
fetchData();
|
|
@@ -231,10 +240,12 @@ function useDealList(options = {}) {
|
|
|
231
240
|
loading,
|
|
232
241
|
error,
|
|
233
242
|
stats,
|
|
234
|
-
page,
|
|
243
|
+
page: pageIndex + 1,
|
|
244
|
+
pageIndex,
|
|
245
|
+
pageSize,
|
|
235
246
|
refetch: fetchData,
|
|
236
|
-
nextPage: () =>
|
|
237
|
-
prevPage: () =>
|
|
247
|
+
nextPage: options.pageIndex === undefined ? () => setInternalPage((page) => page + 1) : undefined,
|
|
248
|
+
prevPage: options.pageIndex === undefined ? () => pageIndex > 0 && setInternalPage((page) => page - 1) : undefined
|
|
238
249
|
};
|
|
239
250
|
}
|
|
240
251
|
|
|
@@ -971,11 +982,305 @@ function DealActionsModal({
|
|
|
971
982
|
}, undefined, true, undefined, this);
|
|
972
983
|
}
|
|
973
984
|
|
|
974
|
-
// src/ui/
|
|
985
|
+
// src/ui/tables/DealListTab.tsx
|
|
975
986
|
import {
|
|
976
987
|
Button as Button3,
|
|
988
|
+
DataTable,
|
|
989
|
+
LoaderBlock
|
|
990
|
+
} from "@contractspec/lib.design-system";
|
|
991
|
+
import { useContractTable } from "@contractspec/lib.presentation-runtime-react";
|
|
992
|
+
import { Badge } from "@contractspec/lib.ui-kit-web/ui/badge";
|
|
993
|
+
import { HStack, VStack } from "@contractspec/lib.ui-kit-web/ui/stack";
|
|
994
|
+
import { Text } from "@contractspec/lib.ui-kit-web/ui/text";
|
|
995
|
+
import * as React from "react";
|
|
996
|
+
import { jsxDEV as jsxDEV5 } from "react/jsx-dev-runtime";
|
|
997
|
+
"use client";
|
|
998
|
+
function formatCurrency4(value, currency = "USD") {
|
|
999
|
+
return new Intl.NumberFormat("en-US", {
|
|
1000
|
+
style: "currency",
|
|
1001
|
+
currency,
|
|
1002
|
+
minimumFractionDigits: 0,
|
|
1003
|
+
maximumFractionDigits: 0
|
|
1004
|
+
}).format(value);
|
|
1005
|
+
}
|
|
1006
|
+
function statusVariant(status) {
|
|
1007
|
+
switch (status) {
|
|
1008
|
+
case "WON":
|
|
1009
|
+
return "default";
|
|
1010
|
+
case "LOST":
|
|
1011
|
+
return "destructive";
|
|
1012
|
+
case "STALE":
|
|
1013
|
+
return "outline";
|
|
1014
|
+
default:
|
|
1015
|
+
return "secondary";
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
function DealListDataTable({
|
|
1019
|
+
deals,
|
|
1020
|
+
totalItems,
|
|
1021
|
+
pageIndex,
|
|
1022
|
+
pageSize,
|
|
1023
|
+
sorting,
|
|
1024
|
+
loading,
|
|
1025
|
+
onSortingChange,
|
|
1026
|
+
onPaginationChange,
|
|
1027
|
+
onDealClick
|
|
1028
|
+
}) {
|
|
1029
|
+
const controller = useContractTable({
|
|
1030
|
+
data: deals,
|
|
1031
|
+
columns: [
|
|
1032
|
+
{
|
|
1033
|
+
id: "deal",
|
|
1034
|
+
header: "Deal",
|
|
1035
|
+
label: "Deal",
|
|
1036
|
+
accessor: (deal) => deal.name,
|
|
1037
|
+
cell: ({ item }) => /* @__PURE__ */ jsxDEV5(VStack, {
|
|
1038
|
+
gap: "xs",
|
|
1039
|
+
children: [
|
|
1040
|
+
/* @__PURE__ */ jsxDEV5(Text, {
|
|
1041
|
+
className: "font-medium text-sm",
|
|
1042
|
+
children: item.name
|
|
1043
|
+
}, undefined, false, undefined, this),
|
|
1044
|
+
/* @__PURE__ */ jsxDEV5(Text, {
|
|
1045
|
+
className: "text-muted-foreground text-xs",
|
|
1046
|
+
children: item.companyId ?? "Unassigned company"
|
|
1047
|
+
}, undefined, false, undefined, this)
|
|
1048
|
+
]
|
|
1049
|
+
}, undefined, true, undefined, this),
|
|
1050
|
+
size: 240,
|
|
1051
|
+
minSize: 180,
|
|
1052
|
+
canSort: true,
|
|
1053
|
+
canPin: true,
|
|
1054
|
+
canResize: true
|
|
1055
|
+
},
|
|
1056
|
+
{
|
|
1057
|
+
id: "value",
|
|
1058
|
+
header: "Value",
|
|
1059
|
+
label: "Value",
|
|
1060
|
+
accessorKey: "value",
|
|
1061
|
+
cell: ({ item }) => formatCurrency4(item.value, item.currency),
|
|
1062
|
+
align: "right",
|
|
1063
|
+
size: 140,
|
|
1064
|
+
canSort: true,
|
|
1065
|
+
canResize: true
|
|
1066
|
+
},
|
|
1067
|
+
{
|
|
1068
|
+
id: "status",
|
|
1069
|
+
header: "Status",
|
|
1070
|
+
label: "Status",
|
|
1071
|
+
accessorKey: "status",
|
|
1072
|
+
cell: ({ value }) => /* @__PURE__ */ jsxDEV5(Badge, {
|
|
1073
|
+
variant: statusVariant(value),
|
|
1074
|
+
children: String(value)
|
|
1075
|
+
}, undefined, false, undefined, this),
|
|
1076
|
+
size: 130,
|
|
1077
|
+
canSort: true,
|
|
1078
|
+
canHide: true,
|
|
1079
|
+
canPin: true,
|
|
1080
|
+
canResize: true
|
|
1081
|
+
},
|
|
1082
|
+
{
|
|
1083
|
+
id: "expectedCloseDate",
|
|
1084
|
+
header: "Expected Close",
|
|
1085
|
+
label: "Expected Close",
|
|
1086
|
+
accessor: (deal) => deal.expectedCloseDate?.toISOString() ?? "",
|
|
1087
|
+
cell: ({ item }) => item.expectedCloseDate?.toLocaleDateString() ?? "Not scheduled",
|
|
1088
|
+
size: 170,
|
|
1089
|
+
canSort: true,
|
|
1090
|
+
canHide: true,
|
|
1091
|
+
canResize: true
|
|
1092
|
+
},
|
|
1093
|
+
{
|
|
1094
|
+
id: "updatedAt",
|
|
1095
|
+
header: "Updated",
|
|
1096
|
+
label: "Updated",
|
|
1097
|
+
accessor: (deal) => deal.updatedAt.toISOString(),
|
|
1098
|
+
cell: ({ item }) => item.updatedAt.toLocaleDateString(),
|
|
1099
|
+
size: 140,
|
|
1100
|
+
canSort: true,
|
|
1101
|
+
canHide: true,
|
|
1102
|
+
canResize: true
|
|
1103
|
+
},
|
|
1104
|
+
{
|
|
1105
|
+
id: "actions",
|
|
1106
|
+
header: "Actions",
|
|
1107
|
+
label: "Actions",
|
|
1108
|
+
accessor: (deal) => deal.id,
|
|
1109
|
+
cell: ({ item }) => /* @__PURE__ */ jsxDEV5(Button3, {
|
|
1110
|
+
variant: "ghost",
|
|
1111
|
+
size: "sm",
|
|
1112
|
+
onPress: () => onDealClick?.(item.id),
|
|
1113
|
+
children: "Actions"
|
|
1114
|
+
}, undefined, false, undefined, this),
|
|
1115
|
+
size: 120,
|
|
1116
|
+
canSort: false,
|
|
1117
|
+
canHide: false,
|
|
1118
|
+
canPin: false,
|
|
1119
|
+
canResize: false
|
|
1120
|
+
}
|
|
1121
|
+
],
|
|
1122
|
+
executionMode: "server",
|
|
1123
|
+
selectionMode: "multiple",
|
|
1124
|
+
totalItems,
|
|
1125
|
+
state: {
|
|
1126
|
+
sorting,
|
|
1127
|
+
pagination: {
|
|
1128
|
+
pageIndex,
|
|
1129
|
+
pageSize
|
|
1130
|
+
}
|
|
1131
|
+
},
|
|
1132
|
+
onSortingChange,
|
|
1133
|
+
onPaginationChange,
|
|
1134
|
+
initialState: {
|
|
1135
|
+
columnVisibility: { updatedAt: false },
|
|
1136
|
+
columnPinning: { left: ["deal", "status"], right: [] }
|
|
1137
|
+
},
|
|
1138
|
+
renderExpandedContent: (deal) => /* @__PURE__ */ jsxDEV5(VStack, {
|
|
1139
|
+
gap: "sm",
|
|
1140
|
+
className: "py-2",
|
|
1141
|
+
children: [
|
|
1142
|
+
/* @__PURE__ */ jsxDEV5(HStack, {
|
|
1143
|
+
justify: "between",
|
|
1144
|
+
children: [
|
|
1145
|
+
/* @__PURE__ */ jsxDEV5(Text, {
|
|
1146
|
+
className: "font-medium text-sm",
|
|
1147
|
+
children: "Owner"
|
|
1148
|
+
}, undefined, false, undefined, this),
|
|
1149
|
+
/* @__PURE__ */ jsxDEV5(Text, {
|
|
1150
|
+
className: "text-muted-foreground text-sm",
|
|
1151
|
+
children: deal.ownerId
|
|
1152
|
+
}, undefined, false, undefined, this)
|
|
1153
|
+
]
|
|
1154
|
+
}, undefined, true, undefined, this),
|
|
1155
|
+
/* @__PURE__ */ jsxDEV5(HStack, {
|
|
1156
|
+
justify: "between",
|
|
1157
|
+
children: [
|
|
1158
|
+
/* @__PURE__ */ jsxDEV5(Text, {
|
|
1159
|
+
className: "font-medium text-sm",
|
|
1160
|
+
children: "Contact"
|
|
1161
|
+
}, undefined, false, undefined, this),
|
|
1162
|
+
/* @__PURE__ */ jsxDEV5(Text, {
|
|
1163
|
+
className: "text-muted-foreground text-sm",
|
|
1164
|
+
children: deal.contactId ?? "No linked contact"
|
|
1165
|
+
}, undefined, false, undefined, this)
|
|
1166
|
+
]
|
|
1167
|
+
}, undefined, true, undefined, this),
|
|
1168
|
+
deal.wonSource ? /* @__PURE__ */ jsxDEV5(HStack, {
|
|
1169
|
+
justify: "between",
|
|
1170
|
+
children: [
|
|
1171
|
+
/* @__PURE__ */ jsxDEV5(Text, {
|
|
1172
|
+
className: "font-medium text-sm",
|
|
1173
|
+
children: "Won Source"
|
|
1174
|
+
}, undefined, false, undefined, this),
|
|
1175
|
+
/* @__PURE__ */ jsxDEV5(Text, {
|
|
1176
|
+
className: "text-muted-foreground text-sm",
|
|
1177
|
+
children: deal.wonSource
|
|
1178
|
+
}, undefined, false, undefined, this)
|
|
1179
|
+
]
|
|
1180
|
+
}, undefined, true, undefined, this) : null,
|
|
1181
|
+
deal.lostReason ? /* @__PURE__ */ jsxDEV5(HStack, {
|
|
1182
|
+
justify: "between",
|
|
1183
|
+
children: [
|
|
1184
|
+
/* @__PURE__ */ jsxDEV5(Text, {
|
|
1185
|
+
className: "font-medium text-sm",
|
|
1186
|
+
children: "Lost Reason"
|
|
1187
|
+
}, undefined, false, undefined, this),
|
|
1188
|
+
/* @__PURE__ */ jsxDEV5(Text, {
|
|
1189
|
+
className: "text-muted-foreground text-sm",
|
|
1190
|
+
children: deal.lostReason
|
|
1191
|
+
}, undefined, false, undefined, this)
|
|
1192
|
+
]
|
|
1193
|
+
}, undefined, true, undefined, this) : null,
|
|
1194
|
+
deal.notes ? /* @__PURE__ */ jsxDEV5(VStack, {
|
|
1195
|
+
gap: "xs",
|
|
1196
|
+
children: [
|
|
1197
|
+
/* @__PURE__ */ jsxDEV5(Text, {
|
|
1198
|
+
className: "font-medium text-sm",
|
|
1199
|
+
children: "Notes"
|
|
1200
|
+
}, undefined, false, undefined, this),
|
|
1201
|
+
/* @__PURE__ */ jsxDEV5(Text, {
|
|
1202
|
+
className: "text-muted-foreground text-sm",
|
|
1203
|
+
children: deal.notes
|
|
1204
|
+
}, undefined, false, undefined, this)
|
|
1205
|
+
]
|
|
1206
|
+
}, undefined, true, undefined, this) : null
|
|
1207
|
+
]
|
|
1208
|
+
}, undefined, true, undefined, this),
|
|
1209
|
+
getCanExpand: () => true
|
|
1210
|
+
});
|
|
1211
|
+
return /* @__PURE__ */ jsxDEV5(DataTable, {
|
|
1212
|
+
controller,
|
|
1213
|
+
title: "All Deals",
|
|
1214
|
+
description: "Server-mode table using the shared ContractSpec controller.",
|
|
1215
|
+
loading,
|
|
1216
|
+
toolbar: /* @__PURE__ */ jsxDEV5(HStack, {
|
|
1217
|
+
gap: "sm",
|
|
1218
|
+
className: "flex-wrap",
|
|
1219
|
+
children: [
|
|
1220
|
+
/* @__PURE__ */ jsxDEV5(Text, {
|
|
1221
|
+
className: "text-muted-foreground text-sm",
|
|
1222
|
+
children: [
|
|
1223
|
+
"Selected ",
|
|
1224
|
+
controller.selectedRowIds.length
|
|
1225
|
+
]
|
|
1226
|
+
}, undefined, true, undefined, this),
|
|
1227
|
+
/* @__PURE__ */ jsxDEV5(Text, {
|
|
1228
|
+
className: "text-muted-foreground text-sm",
|
|
1229
|
+
children: [
|
|
1230
|
+
totalItems,
|
|
1231
|
+
" total deals"
|
|
1232
|
+
]
|
|
1233
|
+
}, undefined, true, undefined, this)
|
|
1234
|
+
]
|
|
1235
|
+
}, undefined, true, undefined, this),
|
|
1236
|
+
footer: `Page ${controller.pageIndex + 1} of ${controller.pageCount}`,
|
|
1237
|
+
emptyState: /* @__PURE__ */ jsxDEV5("div", {
|
|
1238
|
+
className: "rounded-md border border-dashed p-8 text-center text-muted-foreground text-sm",
|
|
1239
|
+
children: "No deals found"
|
|
1240
|
+
}, undefined, false, undefined, this)
|
|
1241
|
+
}, undefined, false, undefined, this);
|
|
1242
|
+
}
|
|
1243
|
+
function DealListTab({
|
|
1244
|
+
onDealClick
|
|
1245
|
+
}) {
|
|
1246
|
+
const [sorting, setSorting] = React.useState([
|
|
1247
|
+
{ id: "value", desc: true }
|
|
1248
|
+
]);
|
|
1249
|
+
const [pagination, setPagination] = React.useState({
|
|
1250
|
+
pageIndex: 0,
|
|
1251
|
+
pageSize: 3
|
|
1252
|
+
});
|
|
1253
|
+
const { data, loading } = useDealList({
|
|
1254
|
+
pageIndex: pagination.pageIndex,
|
|
1255
|
+
pageSize: pagination.pageSize,
|
|
1256
|
+
sorting
|
|
1257
|
+
});
|
|
1258
|
+
if (loading && !data) {
|
|
1259
|
+
return /* @__PURE__ */ jsxDEV5(LoaderBlock, {
|
|
1260
|
+
label: "Loading deals..."
|
|
1261
|
+
}, undefined, false, undefined, this);
|
|
1262
|
+
}
|
|
1263
|
+
return /* @__PURE__ */ jsxDEV5(DealListDataTable, {
|
|
1264
|
+
deals: data?.deals ?? [],
|
|
1265
|
+
totalItems: data?.total ?? 0,
|
|
1266
|
+
pageIndex: pagination.pageIndex,
|
|
1267
|
+
pageSize: pagination.pageSize,
|
|
1268
|
+
sorting,
|
|
1269
|
+
loading,
|
|
1270
|
+
onSortingChange: (nextSorting) => {
|
|
1271
|
+
setSorting(nextSorting);
|
|
1272
|
+
setPagination((current) => ({ ...current, pageIndex: 0 }));
|
|
1273
|
+
},
|
|
1274
|
+
onPaginationChange: setPagination,
|
|
1275
|
+
onDealClick
|
|
1276
|
+
}, undefined, false, undefined, this);
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
// src/ui/CrmDashboard.tsx
|
|
1280
|
+
import {
|
|
1281
|
+
Button as Button4,
|
|
977
1282
|
ErrorState,
|
|
978
|
-
LoaderBlock,
|
|
1283
|
+
LoaderBlock as LoaderBlock2,
|
|
979
1284
|
StatCard,
|
|
980
1285
|
StatCardGroup
|
|
981
1286
|
} from "@contractspec/lib.design-system";
|
|
@@ -985,10 +1290,10 @@ import {
|
|
|
985
1290
|
TabsList,
|
|
986
1291
|
TabsTrigger
|
|
987
1292
|
} from "@contractspec/lib.ui-kit-web/ui/tabs";
|
|
988
|
-
import { useCallback as useCallback3, useState as
|
|
989
|
-
import { jsxDEV as
|
|
1293
|
+
import { useCallback as useCallback3, useState as useState7 } from "react";
|
|
1294
|
+
import { jsxDEV as jsxDEV6 } from "react/jsx-dev-runtime";
|
|
990
1295
|
"use client";
|
|
991
|
-
function
|
|
1296
|
+
function formatCurrency5(value, currency = "USD") {
|
|
992
1297
|
return new Intl.NumberFormat("en-US", {
|
|
993
1298
|
style: "currency",
|
|
994
1299
|
currency,
|
|
@@ -997,9 +1302,9 @@ function formatCurrency4(value, currency = "USD") {
|
|
|
997
1302
|
}).format(value);
|
|
998
1303
|
}
|
|
999
1304
|
function CrmDashboard() {
|
|
1000
|
-
const [isCreateModalOpen, setIsCreateModalOpen] =
|
|
1001
|
-
const [selectedDeal, setSelectedDeal] =
|
|
1002
|
-
const [isDealActionsOpen, setIsDealActionsOpen] =
|
|
1305
|
+
const [isCreateModalOpen, setIsCreateModalOpen] = useState7(false);
|
|
1306
|
+
const [selectedDeal, setSelectedDeal] = useState7(null);
|
|
1307
|
+
const [isDealActionsOpen, setIsDealActionsOpen] = useState7(false);
|
|
1003
1308
|
const { data, dealsByStage, stages, loading, error, stats, refetch } = useDealList();
|
|
1004
1309
|
const mutations = useDealMutations({
|
|
1005
1310
|
onSuccess: () => {
|
|
@@ -1017,32 +1322,32 @@ function CrmDashboard() {
|
|
|
1017
1322
|
await mutations.moveDeal({ dealId, stageId: toStageId });
|
|
1018
1323
|
}, [mutations]);
|
|
1019
1324
|
if (loading && !data) {
|
|
1020
|
-
return /* @__PURE__ */
|
|
1325
|
+
return /* @__PURE__ */ jsxDEV6(LoaderBlock2, {
|
|
1021
1326
|
label: "Loading CRM..."
|
|
1022
1327
|
}, undefined, false, undefined, this);
|
|
1023
1328
|
}
|
|
1024
1329
|
if (error) {
|
|
1025
|
-
return /* @__PURE__ */
|
|
1330
|
+
return /* @__PURE__ */ jsxDEV6(ErrorState, {
|
|
1026
1331
|
title: "Failed to load CRM",
|
|
1027
1332
|
description: error.message,
|
|
1028
1333
|
onRetry: refetch,
|
|
1029
1334
|
retryLabel: "Retry"
|
|
1030
1335
|
}, undefined, false, undefined, this);
|
|
1031
1336
|
}
|
|
1032
|
-
return /* @__PURE__ */
|
|
1337
|
+
return /* @__PURE__ */ jsxDEV6("div", {
|
|
1033
1338
|
className: "space-y-6",
|
|
1034
1339
|
children: [
|
|
1035
|
-
/* @__PURE__ */
|
|
1340
|
+
/* @__PURE__ */ jsxDEV6("div", {
|
|
1036
1341
|
className: "flex items-center justify-between",
|
|
1037
1342
|
children: [
|
|
1038
|
-
/* @__PURE__ */
|
|
1343
|
+
/* @__PURE__ */ jsxDEV6("h2", {
|
|
1039
1344
|
className: "font-bold text-2xl",
|
|
1040
1345
|
children: "CRM Pipeline"
|
|
1041
1346
|
}, undefined, false, undefined, this),
|
|
1042
|
-
/* @__PURE__ */
|
|
1347
|
+
/* @__PURE__ */ jsxDEV6(Button4, {
|
|
1043
1348
|
onClick: () => setIsCreateModalOpen(true),
|
|
1044
1349
|
children: [
|
|
1045
|
-
/* @__PURE__ */
|
|
1350
|
+
/* @__PURE__ */ jsxDEV6("span", {
|
|
1046
1351
|
className: "mr-2",
|
|
1047
1352
|
children: "+"
|
|
1048
1353
|
}, undefined, false, undefined, this),
|
|
@@ -1051,60 +1356,60 @@ function CrmDashboard() {
|
|
|
1051
1356
|
}, undefined, true, undefined, this)
|
|
1052
1357
|
]
|
|
1053
1358
|
}, undefined, true, undefined, this),
|
|
1054
|
-
stats && /* @__PURE__ */
|
|
1359
|
+
stats && /* @__PURE__ */ jsxDEV6(StatCardGroup, {
|
|
1055
1360
|
children: [
|
|
1056
|
-
/* @__PURE__ */
|
|
1361
|
+
/* @__PURE__ */ jsxDEV6(StatCard, {
|
|
1057
1362
|
label: "Total Pipeline",
|
|
1058
|
-
value:
|
|
1363
|
+
value: formatCurrency5(stats.totalValue),
|
|
1059
1364
|
hint: `${stats.total} deals`
|
|
1060
1365
|
}, undefined, false, undefined, this),
|
|
1061
|
-
/* @__PURE__ */
|
|
1366
|
+
/* @__PURE__ */ jsxDEV6(StatCard, {
|
|
1062
1367
|
label: "Open Deals",
|
|
1063
|
-
value:
|
|
1368
|
+
value: formatCurrency5(stats.openValue),
|
|
1064
1369
|
hint: `${stats.openCount} active`
|
|
1065
1370
|
}, undefined, false, undefined, this),
|
|
1066
|
-
/* @__PURE__ */
|
|
1371
|
+
/* @__PURE__ */ jsxDEV6(StatCard, {
|
|
1067
1372
|
label: "Won",
|
|
1068
|
-
value:
|
|
1373
|
+
value: formatCurrency5(stats.wonValue),
|
|
1069
1374
|
hint: `${stats.wonCount} closed`
|
|
1070
1375
|
}, undefined, false, undefined, this),
|
|
1071
|
-
/* @__PURE__ */
|
|
1376
|
+
/* @__PURE__ */ jsxDEV6(StatCard, {
|
|
1072
1377
|
label: "Lost",
|
|
1073
1378
|
value: stats.lostCount,
|
|
1074
1379
|
hint: "deals lost"
|
|
1075
1380
|
}, undefined, false, undefined, this)
|
|
1076
1381
|
]
|
|
1077
1382
|
}, undefined, true, undefined, this),
|
|
1078
|
-
/* @__PURE__ */
|
|
1383
|
+
/* @__PURE__ */ jsxDEV6(Tabs, {
|
|
1079
1384
|
defaultValue: "pipeline",
|
|
1080
1385
|
className: "w-full",
|
|
1081
1386
|
children: [
|
|
1082
|
-
/* @__PURE__ */
|
|
1387
|
+
/* @__PURE__ */ jsxDEV6(TabsList, {
|
|
1083
1388
|
children: [
|
|
1084
|
-
/* @__PURE__ */
|
|
1389
|
+
/* @__PURE__ */ jsxDEV6(TabsTrigger, {
|
|
1085
1390
|
value: "pipeline",
|
|
1086
1391
|
children: [
|
|
1087
|
-
/* @__PURE__ */
|
|
1392
|
+
/* @__PURE__ */ jsxDEV6("span", {
|
|
1088
1393
|
className: "mr-2",
|
|
1089
1394
|
children: "\uD83D\uDCCA"
|
|
1090
1395
|
}, undefined, false, undefined, this),
|
|
1091
1396
|
"Pipeline"
|
|
1092
1397
|
]
|
|
1093
1398
|
}, undefined, true, undefined, this),
|
|
1094
|
-
/* @__PURE__ */
|
|
1399
|
+
/* @__PURE__ */ jsxDEV6(TabsTrigger, {
|
|
1095
1400
|
value: "list",
|
|
1096
1401
|
children: [
|
|
1097
|
-
/* @__PURE__ */
|
|
1402
|
+
/* @__PURE__ */ jsxDEV6("span", {
|
|
1098
1403
|
className: "mr-2",
|
|
1099
1404
|
children: "\uD83D\uDCCB"
|
|
1100
1405
|
}, undefined, false, undefined, this),
|
|
1101
1406
|
"All Deals"
|
|
1102
1407
|
]
|
|
1103
1408
|
}, undefined, true, undefined, this),
|
|
1104
|
-
/* @__PURE__ */
|
|
1409
|
+
/* @__PURE__ */ jsxDEV6(TabsTrigger, {
|
|
1105
1410
|
value: "metrics",
|
|
1106
1411
|
children: [
|
|
1107
|
-
/* @__PURE__ */
|
|
1412
|
+
/* @__PURE__ */ jsxDEV6("span", {
|
|
1108
1413
|
className: "mr-2",
|
|
1109
1414
|
children: "\uD83D\uDCC8"
|
|
1110
1415
|
}, undefined, false, undefined, this),
|
|
@@ -1113,34 +1418,33 @@ function CrmDashboard() {
|
|
|
1113
1418
|
}, undefined, true, undefined, this)
|
|
1114
1419
|
]
|
|
1115
1420
|
}, undefined, true, undefined, this),
|
|
1116
|
-
/* @__PURE__ */
|
|
1421
|
+
/* @__PURE__ */ jsxDEV6(TabsContent, {
|
|
1117
1422
|
value: "pipeline",
|
|
1118
1423
|
className: "min-h-[400px]",
|
|
1119
|
-
children: /* @__PURE__ */
|
|
1424
|
+
children: /* @__PURE__ */ jsxDEV6(CrmPipelineBoard, {
|
|
1120
1425
|
dealsByStage,
|
|
1121
1426
|
stages,
|
|
1122
1427
|
onDealClick: handleDealClick,
|
|
1123
1428
|
onDealMove: handleDealMove
|
|
1124
1429
|
}, undefined, false, undefined, this)
|
|
1125
1430
|
}, undefined, false, undefined, this),
|
|
1126
|
-
/* @__PURE__ */
|
|
1431
|
+
/* @__PURE__ */ jsxDEV6(TabsContent, {
|
|
1127
1432
|
value: "list",
|
|
1128
1433
|
className: "min-h-[400px]",
|
|
1129
|
-
children: /* @__PURE__ */
|
|
1130
|
-
data,
|
|
1434
|
+
children: /* @__PURE__ */ jsxDEV6(DealListTab, {
|
|
1131
1435
|
onDealClick: handleDealClick
|
|
1132
1436
|
}, undefined, false, undefined, this)
|
|
1133
1437
|
}, undefined, false, undefined, this),
|
|
1134
|
-
/* @__PURE__ */
|
|
1438
|
+
/* @__PURE__ */ jsxDEV6(TabsContent, {
|
|
1135
1439
|
value: "metrics",
|
|
1136
1440
|
className: "min-h-[400px]",
|
|
1137
|
-
children: /* @__PURE__ */
|
|
1441
|
+
children: /* @__PURE__ */ jsxDEV6(MetricsTab, {
|
|
1138
1442
|
stats
|
|
1139
1443
|
}, undefined, false, undefined, this)
|
|
1140
1444
|
}, undefined, false, undefined, this)
|
|
1141
1445
|
]
|
|
1142
1446
|
}, undefined, true, undefined, this),
|
|
1143
|
-
/* @__PURE__ */
|
|
1447
|
+
/* @__PURE__ */ jsxDEV6(CreateDealModal, {
|
|
1144
1448
|
isOpen: isCreateModalOpen,
|
|
1145
1449
|
onClose: () => setIsCreateModalOpen(false),
|
|
1146
1450
|
onSubmit: async (input) => {
|
|
@@ -1149,7 +1453,7 @@ function CrmDashboard() {
|
|
|
1149
1453
|
stages,
|
|
1150
1454
|
isLoading: mutations.createState.loading
|
|
1151
1455
|
}, undefined, false, undefined, this),
|
|
1152
|
-
/* @__PURE__ */
|
|
1456
|
+
/* @__PURE__ */ jsxDEV6(DealActionsModal, {
|
|
1153
1457
|
isOpen: isDealActionsOpen,
|
|
1154
1458
|
deal: selectedDeal,
|
|
1155
1459
|
stages,
|
|
@@ -1172,112 +1476,30 @@ function CrmDashboard() {
|
|
|
1172
1476
|
]
|
|
1173
1477
|
}, undefined, true, undefined, this);
|
|
1174
1478
|
}
|
|
1175
|
-
function DealListTab({ data, onDealClick }) {
|
|
1176
|
-
if (!data?.deals.length) {
|
|
1177
|
-
return /* @__PURE__ */ jsxDEV5("div", {
|
|
1178
|
-
className: "flex h-64 items-center justify-center text-muted-foreground",
|
|
1179
|
-
children: "No deals found"
|
|
1180
|
-
}, undefined, false, undefined, this);
|
|
1181
|
-
}
|
|
1182
|
-
return /* @__PURE__ */ jsxDEV5("div", {
|
|
1183
|
-
className: "rounded-lg border border-border",
|
|
1184
|
-
children: /* @__PURE__ */ jsxDEV5("table", {
|
|
1185
|
-
className: "w-full",
|
|
1186
|
-
children: [
|
|
1187
|
-
/* @__PURE__ */ jsxDEV5("thead", {
|
|
1188
|
-
className: "border-border border-b bg-muted/30",
|
|
1189
|
-
children: /* @__PURE__ */ jsxDEV5("tr", {
|
|
1190
|
-
children: [
|
|
1191
|
-
/* @__PURE__ */ jsxDEV5("th", {
|
|
1192
|
-
className: "px-4 py-3 text-left font-medium text-sm",
|
|
1193
|
-
children: "Deal"
|
|
1194
|
-
}, undefined, false, undefined, this),
|
|
1195
|
-
/* @__PURE__ */ jsxDEV5("th", {
|
|
1196
|
-
className: "px-4 py-3 text-left font-medium text-sm",
|
|
1197
|
-
children: "Value"
|
|
1198
|
-
}, undefined, false, undefined, this),
|
|
1199
|
-
/* @__PURE__ */ jsxDEV5("th", {
|
|
1200
|
-
className: "px-4 py-3 text-left font-medium text-sm",
|
|
1201
|
-
children: "Status"
|
|
1202
|
-
}, undefined, false, undefined, this),
|
|
1203
|
-
/* @__PURE__ */ jsxDEV5("th", {
|
|
1204
|
-
className: "px-4 py-3 text-left font-medium text-sm",
|
|
1205
|
-
children: "Expected Close"
|
|
1206
|
-
}, undefined, false, undefined, this),
|
|
1207
|
-
/* @__PURE__ */ jsxDEV5("th", {
|
|
1208
|
-
className: "px-4 py-3 text-left font-medium text-sm",
|
|
1209
|
-
children: "Actions"
|
|
1210
|
-
}, undefined, false, undefined, this)
|
|
1211
|
-
]
|
|
1212
|
-
}, undefined, true, undefined, this)
|
|
1213
|
-
}, undefined, false, undefined, this),
|
|
1214
|
-
/* @__PURE__ */ jsxDEV5("tbody", {
|
|
1215
|
-
className: "divide-y divide-border",
|
|
1216
|
-
children: data.deals.map((deal) => /* @__PURE__ */ jsxDEV5("tr", {
|
|
1217
|
-
className: "hover:bg-muted/50",
|
|
1218
|
-
children: [
|
|
1219
|
-
/* @__PURE__ */ jsxDEV5("td", {
|
|
1220
|
-
className: "px-4 py-3",
|
|
1221
|
-
children: /* @__PURE__ */ jsxDEV5("div", {
|
|
1222
|
-
className: "font-medium",
|
|
1223
|
-
children: deal.name
|
|
1224
|
-
}, undefined, false, undefined, this)
|
|
1225
|
-
}, undefined, false, undefined, this),
|
|
1226
|
-
/* @__PURE__ */ jsxDEV5("td", {
|
|
1227
|
-
className: "px-4 py-3 font-mono",
|
|
1228
|
-
children: formatCurrency4(deal.value, deal.currency)
|
|
1229
|
-
}, undefined, false, undefined, this),
|
|
1230
|
-
/* @__PURE__ */ jsxDEV5("td", {
|
|
1231
|
-
className: "px-4 py-3",
|
|
1232
|
-
children: /* @__PURE__ */ jsxDEV5("span", {
|
|
1233
|
-
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"}`,
|
|
1234
|
-
children: deal.status
|
|
1235
|
-
}, undefined, false, undefined, this)
|
|
1236
|
-
}, undefined, false, undefined, this),
|
|
1237
|
-
/* @__PURE__ */ jsxDEV5("td", {
|
|
1238
|
-
className: "px-4 py-3 text-muted-foreground",
|
|
1239
|
-
children: deal.expectedCloseDate?.toLocaleDateString() ?? "-"
|
|
1240
|
-
}, undefined, false, undefined, this),
|
|
1241
|
-
/* @__PURE__ */ jsxDEV5("td", {
|
|
1242
|
-
className: "px-4 py-3",
|
|
1243
|
-
children: /* @__PURE__ */ jsxDEV5(Button3, {
|
|
1244
|
-
variant: "ghost",
|
|
1245
|
-
size: "sm",
|
|
1246
|
-
onPress: () => onDealClick?.(deal.id),
|
|
1247
|
-
children: "Actions"
|
|
1248
|
-
}, undefined, false, undefined, this)
|
|
1249
|
-
}, undefined, false, undefined, this)
|
|
1250
|
-
]
|
|
1251
|
-
}, deal.id, true, undefined, this))
|
|
1252
|
-
}, undefined, false, undefined, this)
|
|
1253
|
-
]
|
|
1254
|
-
}, undefined, true, undefined, this)
|
|
1255
|
-
}, undefined, false, undefined, this);
|
|
1256
|
-
}
|
|
1257
1479
|
function MetricsTab({
|
|
1258
1480
|
stats
|
|
1259
1481
|
}) {
|
|
1260
1482
|
if (!stats)
|
|
1261
1483
|
return null;
|
|
1262
|
-
return /* @__PURE__ */
|
|
1484
|
+
return /* @__PURE__ */ jsxDEV6("div", {
|
|
1263
1485
|
className: "space-y-6",
|
|
1264
|
-
children: /* @__PURE__ */
|
|
1486
|
+
children: /* @__PURE__ */ jsxDEV6("div", {
|
|
1265
1487
|
className: "rounded-xl border border-border bg-card p-6",
|
|
1266
1488
|
children: [
|
|
1267
|
-
/* @__PURE__ */
|
|
1489
|
+
/* @__PURE__ */ jsxDEV6("h3", {
|
|
1268
1490
|
className: "mb-4 font-semibold text-lg",
|
|
1269
1491
|
children: "Pipeline Overview"
|
|
1270
1492
|
}, undefined, false, undefined, this),
|
|
1271
|
-
/* @__PURE__ */
|
|
1493
|
+
/* @__PURE__ */ jsxDEV6("dl", {
|
|
1272
1494
|
className: "grid gap-4 sm:grid-cols-3",
|
|
1273
1495
|
children: [
|
|
1274
|
-
/* @__PURE__ */
|
|
1496
|
+
/* @__PURE__ */ jsxDEV6("div", {
|
|
1275
1497
|
children: [
|
|
1276
|
-
/* @__PURE__ */
|
|
1498
|
+
/* @__PURE__ */ jsxDEV6("dt", {
|
|
1277
1499
|
className: "text-muted-foreground text-sm",
|
|
1278
1500
|
children: "Win Rate"
|
|
1279
1501
|
}, undefined, false, undefined, this),
|
|
1280
|
-
/* @__PURE__ */
|
|
1502
|
+
/* @__PURE__ */ jsxDEV6("dd", {
|
|
1281
1503
|
className: "font-semibold text-2xl",
|
|
1282
1504
|
children: [
|
|
1283
1505
|
stats.total > 0 ? (stats.wonCount / stats.total * 100).toFixed(0) : 0,
|
|
@@ -1286,25 +1508,25 @@ function MetricsTab({
|
|
|
1286
1508
|
}, undefined, true, undefined, this)
|
|
1287
1509
|
]
|
|
1288
1510
|
}, undefined, true, undefined, this),
|
|
1289
|
-
/* @__PURE__ */
|
|
1511
|
+
/* @__PURE__ */ jsxDEV6("div", {
|
|
1290
1512
|
children: [
|
|
1291
|
-
/* @__PURE__ */
|
|
1513
|
+
/* @__PURE__ */ jsxDEV6("dt", {
|
|
1292
1514
|
className: "text-muted-foreground text-sm",
|
|
1293
1515
|
children: "Avg Deal Size"
|
|
1294
1516
|
}, undefined, false, undefined, this),
|
|
1295
|
-
/* @__PURE__ */
|
|
1517
|
+
/* @__PURE__ */ jsxDEV6("dd", {
|
|
1296
1518
|
className: "font-semibold text-2xl",
|
|
1297
|
-
children:
|
|
1519
|
+
children: formatCurrency5(stats.total > 0 ? stats.totalValue / stats.total : 0)
|
|
1298
1520
|
}, undefined, false, undefined, this)
|
|
1299
1521
|
]
|
|
1300
1522
|
}, undefined, true, undefined, this),
|
|
1301
|
-
/* @__PURE__ */
|
|
1523
|
+
/* @__PURE__ */ jsxDEV6("div", {
|
|
1302
1524
|
children: [
|
|
1303
|
-
/* @__PURE__ */
|
|
1525
|
+
/* @__PURE__ */ jsxDEV6("dt", {
|
|
1304
1526
|
className: "text-muted-foreground text-sm",
|
|
1305
1527
|
children: "Conversion"
|
|
1306
1528
|
}, undefined, false, undefined, this),
|
|
1307
|
-
/* @__PURE__ */
|
|
1529
|
+
/* @__PURE__ */ jsxDEV6("dd", {
|
|
1308
1530
|
className: "font-semibold text-2xl",
|
|
1309
1531
|
children: [
|
|
1310
1532
|
stats.wonCount,
|