@contractspec/example.crm-pipeline 3.7.7 → 3.7.12
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 +72 -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.d.ts +1 -1
- 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 +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 +29 -13
- 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/renderers/pipeline.markdown.ts +1 -1
- package/src/ui/renderers/pipeline.renderer.tsx +1 -1
- package/src/ui/tables/DealListTab.smoke.test.tsx +149 -0
- package/src/ui/tables/DealListTab.tsx +276 -0
package/dist/index.js
CHANGED
|
@@ -551,7 +551,7 @@ var crmPipelineDocBlocks = [
|
|
|
551
551
|
- deal.created, stage.moved, task.completed, contact.updated.
|
|
552
552
|
|
|
553
553
|
## Presentations
|
|
554
|
-
- Pipelines/kanban, deal detail, contact/company profiles, task lists.
|
|
554
|
+
- Pipelines/kanban, deal detail, contact/company profiles, task lists, and a server-mode shared table for the deal list.
|
|
555
555
|
|
|
556
556
|
## Notes
|
|
557
557
|
- Stage definitions should be declarative; enforce via spec and regeneration.
|
|
@@ -1120,6 +1120,13 @@ function rowToDeal(row) {
|
|
|
1120
1120
|
updatedAt: new Date(row.updatedAt)
|
|
1121
1121
|
};
|
|
1122
1122
|
}
|
|
1123
|
+
var DEAL_SORT_COLUMNS = {
|
|
1124
|
+
name: "name",
|
|
1125
|
+
value: "value",
|
|
1126
|
+
status: "status",
|
|
1127
|
+
expectedCloseDate: "expectedCloseDate",
|
|
1128
|
+
updatedAt: "updatedAt"
|
|
1129
|
+
};
|
|
1123
1130
|
function createCrmHandlers(db) {
|
|
1124
1131
|
async function listDeals(input) {
|
|
1125
1132
|
const {
|
|
@@ -1130,7 +1137,9 @@ function createCrmHandlers(db) {
|
|
|
1130
1137
|
ownerId,
|
|
1131
1138
|
search,
|
|
1132
1139
|
limit = 20,
|
|
1133
|
-
offset = 0
|
|
1140
|
+
offset = 0,
|
|
1141
|
+
sortBy = "value",
|
|
1142
|
+
sortDirection = "desc"
|
|
1134
1143
|
} = input;
|
|
1135
1144
|
let whereClause = "WHERE projectId = ?";
|
|
1136
1145
|
const params = [projectId];
|
|
@@ -1158,7 +1167,9 @@ function createCrmHandlers(db) {
|
|
|
1158
1167
|
const total = countResult[0]?.count ?? 0;
|
|
1159
1168
|
const valueResult = (await db.query(`SELECT COALESCE(SUM(value), 0) as total FROM crm_deal ${whereClause}`, params)).rows;
|
|
1160
1169
|
const totalValue = valueResult[0]?.total ?? 0;
|
|
1161
|
-
const
|
|
1170
|
+
const orderByColumn = DEAL_SORT_COLUMNS[sortBy] ?? DEAL_SORT_COLUMNS.value;
|
|
1171
|
+
const orderByDirection = sortDirection === "asc" ? "ASC" : "DESC";
|
|
1172
|
+
const dealRows = (await db.query(`SELECT * FROM crm_deal ${whereClause} ORDER BY ${orderByColumn} ${orderByDirection} LIMIT ? OFFSET ?`, [...params, limit, offset])).rows;
|
|
1162
1173
|
return {
|
|
1163
1174
|
deals: dealRows.map(rowToDeal),
|
|
1164
1175
|
total,
|
|
@@ -1860,8 +1871,13 @@ function useDealList(options = {}) {
|
|
|
1860
1871
|
const [stages, setStages] = useState2([]);
|
|
1861
1872
|
const [loading, setLoading] = useState2(true);
|
|
1862
1873
|
const [error, setError] = useState2(null);
|
|
1863
|
-
const [
|
|
1874
|
+
const [internalPage, setInternalPage] = useState2(0);
|
|
1864
1875
|
const pipelineId = options.pipelineId ?? "pipeline-1";
|
|
1876
|
+
const pageIndex = options.pageIndex ?? internalPage;
|
|
1877
|
+
const pageSize = options.pageSize ?? options.limit ?? 50;
|
|
1878
|
+
const [sort] = options.sorting ?? [];
|
|
1879
|
+
const sortBy = sort?.id;
|
|
1880
|
+
const sortDirection = sort ? sort.desc ? "desc" : "asc" : undefined;
|
|
1865
1881
|
const fetchData = useCallback(async () => {
|
|
1866
1882
|
setLoading(true);
|
|
1867
1883
|
setError(null);
|
|
@@ -1873,8 +1889,10 @@ function useDealList(options = {}) {
|
|
|
1873
1889
|
stageId: options.stageId,
|
|
1874
1890
|
status: options.status === "all" ? undefined : options.status,
|
|
1875
1891
|
search: options.search,
|
|
1876
|
-
limit:
|
|
1877
|
-
offset:
|
|
1892
|
+
limit: pageSize,
|
|
1893
|
+
offset: pageIndex * pageSize,
|
|
1894
|
+
sortBy: sortBy === "name" || sortBy === "value" || sortBy === "status" || sortBy === "expectedCloseDate" || sortBy === "updatedAt" ? sortBy : undefined,
|
|
1895
|
+
sortDirection
|
|
1878
1896
|
}),
|
|
1879
1897
|
crm2.getDealsByStage({ projectId, pipelineId }),
|
|
1880
1898
|
crm2.getPipelineStages({ pipelineId })
|
|
@@ -1894,8 +1912,10 @@ function useDealList(options = {}) {
|
|
|
1894
1912
|
options.stageId,
|
|
1895
1913
|
options.status,
|
|
1896
1914
|
options.search,
|
|
1897
|
-
|
|
1898
|
-
|
|
1915
|
+
pageIndex,
|
|
1916
|
+
pageSize,
|
|
1917
|
+
sortBy,
|
|
1918
|
+
sortDirection
|
|
1899
1919
|
]);
|
|
1900
1920
|
useEffect(() => {
|
|
1901
1921
|
fetchData();
|
|
@@ -1923,10 +1943,12 @@ function useDealList(options = {}) {
|
|
|
1923
1943
|
loading,
|
|
1924
1944
|
error,
|
|
1925
1945
|
stats,
|
|
1926
|
-
page,
|
|
1946
|
+
page: pageIndex + 1,
|
|
1947
|
+
pageIndex,
|
|
1948
|
+
pageSize,
|
|
1927
1949
|
refetch: fetchData,
|
|
1928
|
-
nextPage: () =>
|
|
1929
|
-
prevPage: () =>
|
|
1950
|
+
nextPage: options.pageIndex === undefined ? () => setInternalPage((page) => page + 1) : undefined,
|
|
1951
|
+
prevPage: options.pageIndex === undefined ? () => pageIndex > 0 && setInternalPage((page) => page - 1) : undefined
|
|
1930
1952
|
};
|
|
1931
1953
|
}
|
|
1932
1954
|
|
|
@@ -2663,11 +2685,305 @@ function DealActionsModal({
|
|
|
2663
2685
|
}, undefined, true, undefined, this);
|
|
2664
2686
|
}
|
|
2665
2687
|
|
|
2666
|
-
// src/ui/
|
|
2688
|
+
// src/ui/tables/DealListTab.tsx
|
|
2667
2689
|
import {
|
|
2668
2690
|
Button as Button3,
|
|
2691
|
+
DataTable,
|
|
2692
|
+
LoaderBlock
|
|
2693
|
+
} from "@contractspec/lib.design-system";
|
|
2694
|
+
import { useContractTable } from "@contractspec/lib.presentation-runtime-react";
|
|
2695
|
+
import { Badge } from "@contractspec/lib.ui-kit-web/ui/badge";
|
|
2696
|
+
import { HStack, VStack } from "@contractspec/lib.ui-kit-web/ui/stack";
|
|
2697
|
+
import { Text } from "@contractspec/lib.ui-kit-web/ui/text";
|
|
2698
|
+
import * as React from "react";
|
|
2699
|
+
import { jsxDEV as jsxDEV5 } from "react/jsx-dev-runtime";
|
|
2700
|
+
"use client";
|
|
2701
|
+
function formatCurrency4(value, currency = "USD") {
|
|
2702
|
+
return new Intl.NumberFormat("en-US", {
|
|
2703
|
+
style: "currency",
|
|
2704
|
+
currency,
|
|
2705
|
+
minimumFractionDigits: 0,
|
|
2706
|
+
maximumFractionDigits: 0
|
|
2707
|
+
}).format(value);
|
|
2708
|
+
}
|
|
2709
|
+
function statusVariant(status) {
|
|
2710
|
+
switch (status) {
|
|
2711
|
+
case "WON":
|
|
2712
|
+
return "default";
|
|
2713
|
+
case "LOST":
|
|
2714
|
+
return "destructive";
|
|
2715
|
+
case "STALE":
|
|
2716
|
+
return "outline";
|
|
2717
|
+
default:
|
|
2718
|
+
return "secondary";
|
|
2719
|
+
}
|
|
2720
|
+
}
|
|
2721
|
+
function DealListDataTable({
|
|
2722
|
+
deals,
|
|
2723
|
+
totalItems,
|
|
2724
|
+
pageIndex,
|
|
2725
|
+
pageSize,
|
|
2726
|
+
sorting,
|
|
2727
|
+
loading,
|
|
2728
|
+
onSortingChange,
|
|
2729
|
+
onPaginationChange,
|
|
2730
|
+
onDealClick
|
|
2731
|
+
}) {
|
|
2732
|
+
const controller = useContractTable({
|
|
2733
|
+
data: deals,
|
|
2734
|
+
columns: [
|
|
2735
|
+
{
|
|
2736
|
+
id: "deal",
|
|
2737
|
+
header: "Deal",
|
|
2738
|
+
label: "Deal",
|
|
2739
|
+
accessor: (deal3) => deal3.name,
|
|
2740
|
+
cell: ({ item }) => /* @__PURE__ */ jsxDEV5(VStack, {
|
|
2741
|
+
gap: "xs",
|
|
2742
|
+
children: [
|
|
2743
|
+
/* @__PURE__ */ jsxDEV5(Text, {
|
|
2744
|
+
className: "font-medium text-sm",
|
|
2745
|
+
children: item.name
|
|
2746
|
+
}, undefined, false, undefined, this),
|
|
2747
|
+
/* @__PURE__ */ jsxDEV5(Text, {
|
|
2748
|
+
className: "text-muted-foreground text-xs",
|
|
2749
|
+
children: item.companyId ?? "Unassigned company"
|
|
2750
|
+
}, undefined, false, undefined, this)
|
|
2751
|
+
]
|
|
2752
|
+
}, undefined, true, undefined, this),
|
|
2753
|
+
size: 240,
|
|
2754
|
+
minSize: 180,
|
|
2755
|
+
canSort: true,
|
|
2756
|
+
canPin: true,
|
|
2757
|
+
canResize: true
|
|
2758
|
+
},
|
|
2759
|
+
{
|
|
2760
|
+
id: "value",
|
|
2761
|
+
header: "Value",
|
|
2762
|
+
label: "Value",
|
|
2763
|
+
accessorKey: "value",
|
|
2764
|
+
cell: ({ item }) => formatCurrency4(item.value, item.currency),
|
|
2765
|
+
align: "right",
|
|
2766
|
+
size: 140,
|
|
2767
|
+
canSort: true,
|
|
2768
|
+
canResize: true
|
|
2769
|
+
},
|
|
2770
|
+
{
|
|
2771
|
+
id: "status",
|
|
2772
|
+
header: "Status",
|
|
2773
|
+
label: "Status",
|
|
2774
|
+
accessorKey: "status",
|
|
2775
|
+
cell: ({ value }) => /* @__PURE__ */ jsxDEV5(Badge, {
|
|
2776
|
+
variant: statusVariant(value),
|
|
2777
|
+
children: String(value)
|
|
2778
|
+
}, undefined, false, undefined, this),
|
|
2779
|
+
size: 130,
|
|
2780
|
+
canSort: true,
|
|
2781
|
+
canHide: true,
|
|
2782
|
+
canPin: true,
|
|
2783
|
+
canResize: true
|
|
2784
|
+
},
|
|
2785
|
+
{
|
|
2786
|
+
id: "expectedCloseDate",
|
|
2787
|
+
header: "Expected Close",
|
|
2788
|
+
label: "Expected Close",
|
|
2789
|
+
accessor: (deal3) => deal3.expectedCloseDate?.toISOString() ?? "",
|
|
2790
|
+
cell: ({ item }) => item.expectedCloseDate?.toLocaleDateString() ?? "Not scheduled",
|
|
2791
|
+
size: 170,
|
|
2792
|
+
canSort: true,
|
|
2793
|
+
canHide: true,
|
|
2794
|
+
canResize: true
|
|
2795
|
+
},
|
|
2796
|
+
{
|
|
2797
|
+
id: "updatedAt",
|
|
2798
|
+
header: "Updated",
|
|
2799
|
+
label: "Updated",
|
|
2800
|
+
accessor: (deal3) => deal3.updatedAt.toISOString(),
|
|
2801
|
+
cell: ({ item }) => item.updatedAt.toLocaleDateString(),
|
|
2802
|
+
size: 140,
|
|
2803
|
+
canSort: true,
|
|
2804
|
+
canHide: true,
|
|
2805
|
+
canResize: true
|
|
2806
|
+
},
|
|
2807
|
+
{
|
|
2808
|
+
id: "actions",
|
|
2809
|
+
header: "Actions",
|
|
2810
|
+
label: "Actions",
|
|
2811
|
+
accessor: (deal3) => deal3.id,
|
|
2812
|
+
cell: ({ item }) => /* @__PURE__ */ jsxDEV5(Button3, {
|
|
2813
|
+
variant: "ghost",
|
|
2814
|
+
size: "sm",
|
|
2815
|
+
onPress: () => onDealClick?.(item.id),
|
|
2816
|
+
children: "Actions"
|
|
2817
|
+
}, undefined, false, undefined, this),
|
|
2818
|
+
size: 120,
|
|
2819
|
+
canSort: false,
|
|
2820
|
+
canHide: false,
|
|
2821
|
+
canPin: false,
|
|
2822
|
+
canResize: false
|
|
2823
|
+
}
|
|
2824
|
+
],
|
|
2825
|
+
executionMode: "server",
|
|
2826
|
+
selectionMode: "multiple",
|
|
2827
|
+
totalItems,
|
|
2828
|
+
state: {
|
|
2829
|
+
sorting,
|
|
2830
|
+
pagination: {
|
|
2831
|
+
pageIndex,
|
|
2832
|
+
pageSize
|
|
2833
|
+
}
|
|
2834
|
+
},
|
|
2835
|
+
onSortingChange,
|
|
2836
|
+
onPaginationChange,
|
|
2837
|
+
initialState: {
|
|
2838
|
+
columnVisibility: { updatedAt: false },
|
|
2839
|
+
columnPinning: { left: ["deal", "status"], right: [] }
|
|
2840
|
+
},
|
|
2841
|
+
renderExpandedContent: (deal3) => /* @__PURE__ */ jsxDEV5(VStack, {
|
|
2842
|
+
gap: "sm",
|
|
2843
|
+
className: "py-2",
|
|
2844
|
+
children: [
|
|
2845
|
+
/* @__PURE__ */ jsxDEV5(HStack, {
|
|
2846
|
+
justify: "between",
|
|
2847
|
+
children: [
|
|
2848
|
+
/* @__PURE__ */ jsxDEV5(Text, {
|
|
2849
|
+
className: "font-medium text-sm",
|
|
2850
|
+
children: "Owner"
|
|
2851
|
+
}, undefined, false, undefined, this),
|
|
2852
|
+
/* @__PURE__ */ jsxDEV5(Text, {
|
|
2853
|
+
className: "text-muted-foreground text-sm",
|
|
2854
|
+
children: deal3.ownerId
|
|
2855
|
+
}, undefined, false, undefined, this)
|
|
2856
|
+
]
|
|
2857
|
+
}, undefined, true, undefined, this),
|
|
2858
|
+
/* @__PURE__ */ jsxDEV5(HStack, {
|
|
2859
|
+
justify: "between",
|
|
2860
|
+
children: [
|
|
2861
|
+
/* @__PURE__ */ jsxDEV5(Text, {
|
|
2862
|
+
className: "font-medium text-sm",
|
|
2863
|
+
children: "Contact"
|
|
2864
|
+
}, undefined, false, undefined, this),
|
|
2865
|
+
/* @__PURE__ */ jsxDEV5(Text, {
|
|
2866
|
+
className: "text-muted-foreground text-sm",
|
|
2867
|
+
children: deal3.contactId ?? "No linked contact"
|
|
2868
|
+
}, undefined, false, undefined, this)
|
|
2869
|
+
]
|
|
2870
|
+
}, undefined, true, undefined, this),
|
|
2871
|
+
deal3.wonSource ? /* @__PURE__ */ jsxDEV5(HStack, {
|
|
2872
|
+
justify: "between",
|
|
2873
|
+
children: [
|
|
2874
|
+
/* @__PURE__ */ jsxDEV5(Text, {
|
|
2875
|
+
className: "font-medium text-sm",
|
|
2876
|
+
children: "Won Source"
|
|
2877
|
+
}, undefined, false, undefined, this),
|
|
2878
|
+
/* @__PURE__ */ jsxDEV5(Text, {
|
|
2879
|
+
className: "text-muted-foreground text-sm",
|
|
2880
|
+
children: deal3.wonSource
|
|
2881
|
+
}, undefined, false, undefined, this)
|
|
2882
|
+
]
|
|
2883
|
+
}, undefined, true, undefined, this) : null,
|
|
2884
|
+
deal3.lostReason ? /* @__PURE__ */ jsxDEV5(HStack, {
|
|
2885
|
+
justify: "between",
|
|
2886
|
+
children: [
|
|
2887
|
+
/* @__PURE__ */ jsxDEV5(Text, {
|
|
2888
|
+
className: "font-medium text-sm",
|
|
2889
|
+
children: "Lost Reason"
|
|
2890
|
+
}, undefined, false, undefined, this),
|
|
2891
|
+
/* @__PURE__ */ jsxDEV5(Text, {
|
|
2892
|
+
className: "text-muted-foreground text-sm",
|
|
2893
|
+
children: deal3.lostReason
|
|
2894
|
+
}, undefined, false, undefined, this)
|
|
2895
|
+
]
|
|
2896
|
+
}, undefined, true, undefined, this) : null,
|
|
2897
|
+
deal3.notes ? /* @__PURE__ */ jsxDEV5(VStack, {
|
|
2898
|
+
gap: "xs",
|
|
2899
|
+
children: [
|
|
2900
|
+
/* @__PURE__ */ jsxDEV5(Text, {
|
|
2901
|
+
className: "font-medium text-sm",
|
|
2902
|
+
children: "Notes"
|
|
2903
|
+
}, undefined, false, undefined, this),
|
|
2904
|
+
/* @__PURE__ */ jsxDEV5(Text, {
|
|
2905
|
+
className: "text-muted-foreground text-sm",
|
|
2906
|
+
children: deal3.notes
|
|
2907
|
+
}, undefined, false, undefined, this)
|
|
2908
|
+
]
|
|
2909
|
+
}, undefined, true, undefined, this) : null
|
|
2910
|
+
]
|
|
2911
|
+
}, undefined, true, undefined, this),
|
|
2912
|
+
getCanExpand: () => true
|
|
2913
|
+
});
|
|
2914
|
+
return /* @__PURE__ */ jsxDEV5(DataTable, {
|
|
2915
|
+
controller,
|
|
2916
|
+
title: "All Deals",
|
|
2917
|
+
description: "Server-mode table using the shared ContractSpec controller.",
|
|
2918
|
+
loading,
|
|
2919
|
+
toolbar: /* @__PURE__ */ jsxDEV5(HStack, {
|
|
2920
|
+
gap: "sm",
|
|
2921
|
+
className: "flex-wrap",
|
|
2922
|
+
children: [
|
|
2923
|
+
/* @__PURE__ */ jsxDEV5(Text, {
|
|
2924
|
+
className: "text-muted-foreground text-sm",
|
|
2925
|
+
children: [
|
|
2926
|
+
"Selected ",
|
|
2927
|
+
controller.selectedRowIds.length
|
|
2928
|
+
]
|
|
2929
|
+
}, undefined, true, undefined, this),
|
|
2930
|
+
/* @__PURE__ */ jsxDEV5(Text, {
|
|
2931
|
+
className: "text-muted-foreground text-sm",
|
|
2932
|
+
children: [
|
|
2933
|
+
totalItems,
|
|
2934
|
+
" total deals"
|
|
2935
|
+
]
|
|
2936
|
+
}, undefined, true, undefined, this)
|
|
2937
|
+
]
|
|
2938
|
+
}, undefined, true, undefined, this),
|
|
2939
|
+
footer: `Page ${controller.pageIndex + 1} of ${controller.pageCount}`,
|
|
2940
|
+
emptyState: /* @__PURE__ */ jsxDEV5("div", {
|
|
2941
|
+
className: "rounded-md border border-dashed p-8 text-center text-muted-foreground text-sm",
|
|
2942
|
+
children: "No deals found"
|
|
2943
|
+
}, undefined, false, undefined, this)
|
|
2944
|
+
}, undefined, false, undefined, this);
|
|
2945
|
+
}
|
|
2946
|
+
function DealListTab({
|
|
2947
|
+
onDealClick
|
|
2948
|
+
}) {
|
|
2949
|
+
const [sorting, setSorting] = React.useState([
|
|
2950
|
+
{ id: "value", desc: true }
|
|
2951
|
+
]);
|
|
2952
|
+
const [pagination, setPagination] = React.useState({
|
|
2953
|
+
pageIndex: 0,
|
|
2954
|
+
pageSize: 3
|
|
2955
|
+
});
|
|
2956
|
+
const { data, loading } = useDealList({
|
|
2957
|
+
pageIndex: pagination.pageIndex,
|
|
2958
|
+
pageSize: pagination.pageSize,
|
|
2959
|
+
sorting
|
|
2960
|
+
});
|
|
2961
|
+
if (loading && !data) {
|
|
2962
|
+
return /* @__PURE__ */ jsxDEV5(LoaderBlock, {
|
|
2963
|
+
label: "Loading deals..."
|
|
2964
|
+
}, undefined, false, undefined, this);
|
|
2965
|
+
}
|
|
2966
|
+
return /* @__PURE__ */ jsxDEV5(DealListDataTable, {
|
|
2967
|
+
deals: data?.deals ?? [],
|
|
2968
|
+
totalItems: data?.total ?? 0,
|
|
2969
|
+
pageIndex: pagination.pageIndex,
|
|
2970
|
+
pageSize: pagination.pageSize,
|
|
2971
|
+
sorting,
|
|
2972
|
+
loading,
|
|
2973
|
+
onSortingChange: (nextSorting) => {
|
|
2974
|
+
setSorting(nextSorting);
|
|
2975
|
+
setPagination((current) => ({ ...current, pageIndex: 0 }));
|
|
2976
|
+
},
|
|
2977
|
+
onPaginationChange: setPagination,
|
|
2978
|
+
onDealClick
|
|
2979
|
+
}, undefined, false, undefined, this);
|
|
2980
|
+
}
|
|
2981
|
+
|
|
2982
|
+
// src/ui/CrmDashboard.tsx
|
|
2983
|
+
import {
|
|
2984
|
+
Button as Button4,
|
|
2669
2985
|
ErrorState,
|
|
2670
|
-
LoaderBlock,
|
|
2986
|
+
LoaderBlock as LoaderBlock2,
|
|
2671
2987
|
StatCard,
|
|
2672
2988
|
StatCardGroup
|
|
2673
2989
|
} from "@contractspec/lib.design-system";
|
|
@@ -2677,10 +2993,10 @@ import {
|
|
|
2677
2993
|
TabsList,
|
|
2678
2994
|
TabsTrigger
|
|
2679
2995
|
} from "@contractspec/lib.ui-kit-web/ui/tabs";
|
|
2680
|
-
import { useCallback as useCallback3, useState as
|
|
2681
|
-
import { jsxDEV as
|
|
2996
|
+
import { useCallback as useCallback3, useState as useState7 } from "react";
|
|
2997
|
+
import { jsxDEV as jsxDEV6 } from "react/jsx-dev-runtime";
|
|
2682
2998
|
"use client";
|
|
2683
|
-
function
|
|
2999
|
+
function formatCurrency5(value, currency = "USD") {
|
|
2684
3000
|
return new Intl.NumberFormat("en-US", {
|
|
2685
3001
|
style: "currency",
|
|
2686
3002
|
currency,
|
|
@@ -2689,9 +3005,9 @@ function formatCurrency4(value, currency = "USD") {
|
|
|
2689
3005
|
}).format(value);
|
|
2690
3006
|
}
|
|
2691
3007
|
function CrmDashboard() {
|
|
2692
|
-
const [isCreateModalOpen, setIsCreateModalOpen] =
|
|
2693
|
-
const [selectedDeal, setSelectedDeal] =
|
|
2694
|
-
const [isDealActionsOpen, setIsDealActionsOpen] =
|
|
3008
|
+
const [isCreateModalOpen, setIsCreateModalOpen] = useState7(false);
|
|
3009
|
+
const [selectedDeal, setSelectedDeal] = useState7(null);
|
|
3010
|
+
const [isDealActionsOpen, setIsDealActionsOpen] = useState7(false);
|
|
2695
3011
|
const { data, dealsByStage, stages, loading, error, stats, refetch } = useDealList();
|
|
2696
3012
|
const mutations = useDealMutations({
|
|
2697
3013
|
onSuccess: () => {
|
|
@@ -2709,32 +3025,32 @@ function CrmDashboard() {
|
|
|
2709
3025
|
await mutations.moveDeal({ dealId, stageId: toStageId });
|
|
2710
3026
|
}, [mutations]);
|
|
2711
3027
|
if (loading && !data) {
|
|
2712
|
-
return /* @__PURE__ */
|
|
3028
|
+
return /* @__PURE__ */ jsxDEV6(LoaderBlock2, {
|
|
2713
3029
|
label: "Loading CRM..."
|
|
2714
3030
|
}, undefined, false, undefined, this);
|
|
2715
3031
|
}
|
|
2716
3032
|
if (error) {
|
|
2717
|
-
return /* @__PURE__ */
|
|
3033
|
+
return /* @__PURE__ */ jsxDEV6(ErrorState, {
|
|
2718
3034
|
title: "Failed to load CRM",
|
|
2719
3035
|
description: error.message,
|
|
2720
3036
|
onRetry: refetch,
|
|
2721
3037
|
retryLabel: "Retry"
|
|
2722
3038
|
}, undefined, false, undefined, this);
|
|
2723
3039
|
}
|
|
2724
|
-
return /* @__PURE__ */
|
|
3040
|
+
return /* @__PURE__ */ jsxDEV6("div", {
|
|
2725
3041
|
className: "space-y-6",
|
|
2726
3042
|
children: [
|
|
2727
|
-
/* @__PURE__ */
|
|
3043
|
+
/* @__PURE__ */ jsxDEV6("div", {
|
|
2728
3044
|
className: "flex items-center justify-between",
|
|
2729
3045
|
children: [
|
|
2730
|
-
/* @__PURE__ */
|
|
3046
|
+
/* @__PURE__ */ jsxDEV6("h2", {
|
|
2731
3047
|
className: "font-bold text-2xl",
|
|
2732
3048
|
children: "CRM Pipeline"
|
|
2733
3049
|
}, undefined, false, undefined, this),
|
|
2734
|
-
/* @__PURE__ */
|
|
3050
|
+
/* @__PURE__ */ jsxDEV6(Button4, {
|
|
2735
3051
|
onClick: () => setIsCreateModalOpen(true),
|
|
2736
3052
|
children: [
|
|
2737
|
-
/* @__PURE__ */
|
|
3053
|
+
/* @__PURE__ */ jsxDEV6("span", {
|
|
2738
3054
|
className: "mr-2",
|
|
2739
3055
|
children: "+"
|
|
2740
3056
|
}, undefined, false, undefined, this),
|
|
@@ -2743,60 +3059,60 @@ function CrmDashboard() {
|
|
|
2743
3059
|
}, undefined, true, undefined, this)
|
|
2744
3060
|
]
|
|
2745
3061
|
}, undefined, true, undefined, this),
|
|
2746
|
-
stats && /* @__PURE__ */
|
|
3062
|
+
stats && /* @__PURE__ */ jsxDEV6(StatCardGroup, {
|
|
2747
3063
|
children: [
|
|
2748
|
-
/* @__PURE__ */
|
|
3064
|
+
/* @__PURE__ */ jsxDEV6(StatCard, {
|
|
2749
3065
|
label: "Total Pipeline",
|
|
2750
|
-
value:
|
|
3066
|
+
value: formatCurrency5(stats.totalValue),
|
|
2751
3067
|
hint: `${stats.total} deals`
|
|
2752
3068
|
}, undefined, false, undefined, this),
|
|
2753
|
-
/* @__PURE__ */
|
|
3069
|
+
/* @__PURE__ */ jsxDEV6(StatCard, {
|
|
2754
3070
|
label: "Open Deals",
|
|
2755
|
-
value:
|
|
3071
|
+
value: formatCurrency5(stats.openValue),
|
|
2756
3072
|
hint: `${stats.openCount} active`
|
|
2757
3073
|
}, undefined, false, undefined, this),
|
|
2758
|
-
/* @__PURE__ */
|
|
3074
|
+
/* @__PURE__ */ jsxDEV6(StatCard, {
|
|
2759
3075
|
label: "Won",
|
|
2760
|
-
value:
|
|
3076
|
+
value: formatCurrency5(stats.wonValue),
|
|
2761
3077
|
hint: `${stats.wonCount} closed`
|
|
2762
3078
|
}, undefined, false, undefined, this),
|
|
2763
|
-
/* @__PURE__ */
|
|
3079
|
+
/* @__PURE__ */ jsxDEV6(StatCard, {
|
|
2764
3080
|
label: "Lost",
|
|
2765
3081
|
value: stats.lostCount,
|
|
2766
3082
|
hint: "deals lost"
|
|
2767
3083
|
}, undefined, false, undefined, this)
|
|
2768
3084
|
]
|
|
2769
3085
|
}, undefined, true, undefined, this),
|
|
2770
|
-
/* @__PURE__ */
|
|
3086
|
+
/* @__PURE__ */ jsxDEV6(Tabs, {
|
|
2771
3087
|
defaultValue: "pipeline",
|
|
2772
3088
|
className: "w-full",
|
|
2773
3089
|
children: [
|
|
2774
|
-
/* @__PURE__ */
|
|
3090
|
+
/* @__PURE__ */ jsxDEV6(TabsList, {
|
|
2775
3091
|
children: [
|
|
2776
|
-
/* @__PURE__ */
|
|
3092
|
+
/* @__PURE__ */ jsxDEV6(TabsTrigger, {
|
|
2777
3093
|
value: "pipeline",
|
|
2778
3094
|
children: [
|
|
2779
|
-
/* @__PURE__ */
|
|
3095
|
+
/* @__PURE__ */ jsxDEV6("span", {
|
|
2780
3096
|
className: "mr-2",
|
|
2781
3097
|
children: "\uD83D\uDCCA"
|
|
2782
3098
|
}, undefined, false, undefined, this),
|
|
2783
3099
|
"Pipeline"
|
|
2784
3100
|
]
|
|
2785
3101
|
}, undefined, true, undefined, this),
|
|
2786
|
-
/* @__PURE__ */
|
|
3102
|
+
/* @__PURE__ */ jsxDEV6(TabsTrigger, {
|
|
2787
3103
|
value: "list",
|
|
2788
3104
|
children: [
|
|
2789
|
-
/* @__PURE__ */
|
|
3105
|
+
/* @__PURE__ */ jsxDEV6("span", {
|
|
2790
3106
|
className: "mr-2",
|
|
2791
3107
|
children: "\uD83D\uDCCB"
|
|
2792
3108
|
}, undefined, false, undefined, this),
|
|
2793
3109
|
"All Deals"
|
|
2794
3110
|
]
|
|
2795
3111
|
}, undefined, true, undefined, this),
|
|
2796
|
-
/* @__PURE__ */
|
|
3112
|
+
/* @__PURE__ */ jsxDEV6(TabsTrigger, {
|
|
2797
3113
|
value: "metrics",
|
|
2798
3114
|
children: [
|
|
2799
|
-
/* @__PURE__ */
|
|
3115
|
+
/* @__PURE__ */ jsxDEV6("span", {
|
|
2800
3116
|
className: "mr-2",
|
|
2801
3117
|
children: "\uD83D\uDCC8"
|
|
2802
3118
|
}, undefined, false, undefined, this),
|
|
@@ -2805,34 +3121,33 @@ function CrmDashboard() {
|
|
|
2805
3121
|
}, undefined, true, undefined, this)
|
|
2806
3122
|
]
|
|
2807
3123
|
}, undefined, true, undefined, this),
|
|
2808
|
-
/* @__PURE__ */
|
|
3124
|
+
/* @__PURE__ */ jsxDEV6(TabsContent, {
|
|
2809
3125
|
value: "pipeline",
|
|
2810
3126
|
className: "min-h-[400px]",
|
|
2811
|
-
children: /* @__PURE__ */
|
|
3127
|
+
children: /* @__PURE__ */ jsxDEV6(CrmPipelineBoard, {
|
|
2812
3128
|
dealsByStage,
|
|
2813
3129
|
stages,
|
|
2814
3130
|
onDealClick: handleDealClick,
|
|
2815
3131
|
onDealMove: handleDealMove
|
|
2816
3132
|
}, undefined, false, undefined, this)
|
|
2817
3133
|
}, undefined, false, undefined, this),
|
|
2818
|
-
/* @__PURE__ */
|
|
3134
|
+
/* @__PURE__ */ jsxDEV6(TabsContent, {
|
|
2819
3135
|
value: "list",
|
|
2820
3136
|
className: "min-h-[400px]",
|
|
2821
|
-
children: /* @__PURE__ */
|
|
2822
|
-
data,
|
|
3137
|
+
children: /* @__PURE__ */ jsxDEV6(DealListTab, {
|
|
2823
3138
|
onDealClick: handleDealClick
|
|
2824
3139
|
}, undefined, false, undefined, this)
|
|
2825
3140
|
}, undefined, false, undefined, this),
|
|
2826
|
-
/* @__PURE__ */
|
|
3141
|
+
/* @__PURE__ */ jsxDEV6(TabsContent, {
|
|
2827
3142
|
value: "metrics",
|
|
2828
3143
|
className: "min-h-[400px]",
|
|
2829
|
-
children: /* @__PURE__ */
|
|
3144
|
+
children: /* @__PURE__ */ jsxDEV6(MetricsTab, {
|
|
2830
3145
|
stats
|
|
2831
3146
|
}, undefined, false, undefined, this)
|
|
2832
3147
|
}, undefined, false, undefined, this)
|
|
2833
3148
|
]
|
|
2834
3149
|
}, undefined, true, undefined, this),
|
|
2835
|
-
/* @__PURE__ */
|
|
3150
|
+
/* @__PURE__ */ jsxDEV6(CreateDealModal, {
|
|
2836
3151
|
isOpen: isCreateModalOpen,
|
|
2837
3152
|
onClose: () => setIsCreateModalOpen(false),
|
|
2838
3153
|
onSubmit: async (input) => {
|
|
@@ -2841,7 +3156,7 @@ function CrmDashboard() {
|
|
|
2841
3156
|
stages,
|
|
2842
3157
|
isLoading: mutations.createState.loading
|
|
2843
3158
|
}, undefined, false, undefined, this),
|
|
2844
|
-
/* @__PURE__ */
|
|
3159
|
+
/* @__PURE__ */ jsxDEV6(DealActionsModal, {
|
|
2845
3160
|
isOpen: isDealActionsOpen,
|
|
2846
3161
|
deal: selectedDeal,
|
|
2847
3162
|
stages,
|
|
@@ -2864,112 +3179,30 @@ function CrmDashboard() {
|
|
|
2864
3179
|
]
|
|
2865
3180
|
}, undefined, true, undefined, this);
|
|
2866
3181
|
}
|
|
2867
|
-
function DealListTab({ data, onDealClick }) {
|
|
2868
|
-
if (!data?.deals.length) {
|
|
2869
|
-
return /* @__PURE__ */ jsxDEV5("div", {
|
|
2870
|
-
className: "flex h-64 items-center justify-center text-muted-foreground",
|
|
2871
|
-
children: "No deals found"
|
|
2872
|
-
}, undefined, false, undefined, this);
|
|
2873
|
-
}
|
|
2874
|
-
return /* @__PURE__ */ jsxDEV5("div", {
|
|
2875
|
-
className: "rounded-lg border border-border",
|
|
2876
|
-
children: /* @__PURE__ */ jsxDEV5("table", {
|
|
2877
|
-
className: "w-full",
|
|
2878
|
-
children: [
|
|
2879
|
-
/* @__PURE__ */ jsxDEV5("thead", {
|
|
2880
|
-
className: "border-border border-b bg-muted/30",
|
|
2881
|
-
children: /* @__PURE__ */ jsxDEV5("tr", {
|
|
2882
|
-
children: [
|
|
2883
|
-
/* @__PURE__ */ jsxDEV5("th", {
|
|
2884
|
-
className: "px-4 py-3 text-left font-medium text-sm",
|
|
2885
|
-
children: "Deal"
|
|
2886
|
-
}, undefined, false, undefined, this),
|
|
2887
|
-
/* @__PURE__ */ jsxDEV5("th", {
|
|
2888
|
-
className: "px-4 py-3 text-left font-medium text-sm",
|
|
2889
|
-
children: "Value"
|
|
2890
|
-
}, undefined, false, undefined, this),
|
|
2891
|
-
/* @__PURE__ */ jsxDEV5("th", {
|
|
2892
|
-
className: "px-4 py-3 text-left font-medium text-sm",
|
|
2893
|
-
children: "Status"
|
|
2894
|
-
}, undefined, false, undefined, this),
|
|
2895
|
-
/* @__PURE__ */ jsxDEV5("th", {
|
|
2896
|
-
className: "px-4 py-3 text-left font-medium text-sm",
|
|
2897
|
-
children: "Expected Close"
|
|
2898
|
-
}, undefined, false, undefined, this),
|
|
2899
|
-
/* @__PURE__ */ jsxDEV5("th", {
|
|
2900
|
-
className: "px-4 py-3 text-left font-medium text-sm",
|
|
2901
|
-
children: "Actions"
|
|
2902
|
-
}, undefined, false, undefined, this)
|
|
2903
|
-
]
|
|
2904
|
-
}, undefined, true, undefined, this)
|
|
2905
|
-
}, undefined, false, undefined, this),
|
|
2906
|
-
/* @__PURE__ */ jsxDEV5("tbody", {
|
|
2907
|
-
className: "divide-y divide-border",
|
|
2908
|
-
children: data.deals.map((deal3) => /* @__PURE__ */ jsxDEV5("tr", {
|
|
2909
|
-
className: "hover:bg-muted/50",
|
|
2910
|
-
children: [
|
|
2911
|
-
/* @__PURE__ */ jsxDEV5("td", {
|
|
2912
|
-
className: "px-4 py-3",
|
|
2913
|
-
children: /* @__PURE__ */ jsxDEV5("div", {
|
|
2914
|
-
className: "font-medium",
|
|
2915
|
-
children: deal3.name
|
|
2916
|
-
}, undefined, false, undefined, this)
|
|
2917
|
-
}, undefined, false, undefined, this),
|
|
2918
|
-
/* @__PURE__ */ jsxDEV5("td", {
|
|
2919
|
-
className: "px-4 py-3 font-mono",
|
|
2920
|
-
children: formatCurrency4(deal3.value, deal3.currency)
|
|
2921
|
-
}, undefined, false, undefined, this),
|
|
2922
|
-
/* @__PURE__ */ jsxDEV5("td", {
|
|
2923
|
-
className: "px-4 py-3",
|
|
2924
|
-
children: /* @__PURE__ */ jsxDEV5("span", {
|
|
2925
|
-
className: `inline-flex rounded-full px-2 py-0.5 font-medium text-xs ${deal3.status === "WON" ? "bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400" : deal3.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"}`,
|
|
2926
|
-
children: deal3.status
|
|
2927
|
-
}, undefined, false, undefined, this)
|
|
2928
|
-
}, undefined, false, undefined, this),
|
|
2929
|
-
/* @__PURE__ */ jsxDEV5("td", {
|
|
2930
|
-
className: "px-4 py-3 text-muted-foreground",
|
|
2931
|
-
children: deal3.expectedCloseDate?.toLocaleDateString() ?? "-"
|
|
2932
|
-
}, undefined, false, undefined, this),
|
|
2933
|
-
/* @__PURE__ */ jsxDEV5("td", {
|
|
2934
|
-
className: "px-4 py-3",
|
|
2935
|
-
children: /* @__PURE__ */ jsxDEV5(Button3, {
|
|
2936
|
-
variant: "ghost",
|
|
2937
|
-
size: "sm",
|
|
2938
|
-
onPress: () => onDealClick?.(deal3.id),
|
|
2939
|
-
children: "Actions"
|
|
2940
|
-
}, undefined, false, undefined, this)
|
|
2941
|
-
}, undefined, false, undefined, this)
|
|
2942
|
-
]
|
|
2943
|
-
}, deal3.id, true, undefined, this))
|
|
2944
|
-
}, undefined, false, undefined, this)
|
|
2945
|
-
]
|
|
2946
|
-
}, undefined, true, undefined, this)
|
|
2947
|
-
}, undefined, false, undefined, this);
|
|
2948
|
-
}
|
|
2949
3182
|
function MetricsTab({
|
|
2950
3183
|
stats
|
|
2951
3184
|
}) {
|
|
2952
3185
|
if (!stats)
|
|
2953
3186
|
return null;
|
|
2954
|
-
return /* @__PURE__ */
|
|
3187
|
+
return /* @__PURE__ */ jsxDEV6("div", {
|
|
2955
3188
|
className: "space-y-6",
|
|
2956
|
-
children: /* @__PURE__ */
|
|
3189
|
+
children: /* @__PURE__ */ jsxDEV6("div", {
|
|
2957
3190
|
className: "rounded-xl border border-border bg-card p-6",
|
|
2958
3191
|
children: [
|
|
2959
|
-
/* @__PURE__ */
|
|
3192
|
+
/* @__PURE__ */ jsxDEV6("h3", {
|
|
2960
3193
|
className: "mb-4 font-semibold text-lg",
|
|
2961
3194
|
children: "Pipeline Overview"
|
|
2962
3195
|
}, undefined, false, undefined, this),
|
|
2963
|
-
/* @__PURE__ */
|
|
3196
|
+
/* @__PURE__ */ jsxDEV6("dl", {
|
|
2964
3197
|
className: "grid gap-4 sm:grid-cols-3",
|
|
2965
3198
|
children: [
|
|
2966
|
-
/* @__PURE__ */
|
|
3199
|
+
/* @__PURE__ */ jsxDEV6("div", {
|
|
2967
3200
|
children: [
|
|
2968
|
-
/* @__PURE__ */
|
|
3201
|
+
/* @__PURE__ */ jsxDEV6("dt", {
|
|
2969
3202
|
className: "text-muted-foreground text-sm",
|
|
2970
3203
|
children: "Win Rate"
|
|
2971
3204
|
}, undefined, false, undefined, this),
|
|
2972
|
-
/* @__PURE__ */
|
|
3205
|
+
/* @__PURE__ */ jsxDEV6("dd", {
|
|
2973
3206
|
className: "font-semibold text-2xl",
|
|
2974
3207
|
children: [
|
|
2975
3208
|
stats.total > 0 ? (stats.wonCount / stats.total * 100).toFixed(0) : 0,
|
|
@@ -2978,25 +3211,25 @@ function MetricsTab({
|
|
|
2978
3211
|
}, undefined, true, undefined, this)
|
|
2979
3212
|
]
|
|
2980
3213
|
}, undefined, true, undefined, this),
|
|
2981
|
-
/* @__PURE__ */
|
|
3214
|
+
/* @__PURE__ */ jsxDEV6("div", {
|
|
2982
3215
|
children: [
|
|
2983
|
-
/* @__PURE__ */
|
|
3216
|
+
/* @__PURE__ */ jsxDEV6("dt", {
|
|
2984
3217
|
className: "text-muted-foreground text-sm",
|
|
2985
3218
|
children: "Avg Deal Size"
|
|
2986
3219
|
}, undefined, false, undefined, this),
|
|
2987
|
-
/* @__PURE__ */
|
|
3220
|
+
/* @__PURE__ */ jsxDEV6("dd", {
|
|
2988
3221
|
className: "font-semibold text-2xl",
|
|
2989
|
-
children:
|
|
3222
|
+
children: formatCurrency5(stats.total > 0 ? stats.totalValue / stats.total : 0)
|
|
2990
3223
|
}, undefined, false, undefined, this)
|
|
2991
3224
|
]
|
|
2992
3225
|
}, undefined, true, undefined, this),
|
|
2993
|
-
/* @__PURE__ */
|
|
3226
|
+
/* @__PURE__ */ jsxDEV6("div", {
|
|
2994
3227
|
children: [
|
|
2995
|
-
/* @__PURE__ */
|
|
3228
|
+
/* @__PURE__ */ jsxDEV6("dt", {
|
|
2996
3229
|
className: "text-muted-foreground text-sm",
|
|
2997
3230
|
children: "Conversion"
|
|
2998
3231
|
}, undefined, false, undefined, this),
|
|
2999
|
-
/* @__PURE__ */
|
|
3232
|
+
/* @__PURE__ */ jsxDEV6("dd", {
|
|
3000
3233
|
className: "font-semibold text-2xl",
|
|
3001
3234
|
children: [
|
|
3002
3235
|
stats.wonCount,
|
|
@@ -3066,7 +3299,7 @@ var crmOverlays = [
|
|
|
3066
3299
|
crmSalesRepOverlay
|
|
3067
3300
|
];
|
|
3068
3301
|
// src/ui/renderers/pipeline.markdown.ts
|
|
3069
|
-
function
|
|
3302
|
+
function formatCurrency6(value, currency = "USD") {
|
|
3070
3303
|
return new Intl.NumberFormat("en-US", {
|
|
3071
3304
|
style: "currency",
|
|
3072
3305
|
currency,
|
|
@@ -3093,7 +3326,7 @@ var crmPipelineMarkdownRenderer = {
|
|
|
3093
3326
|
const lines = [
|
|
3094
3327
|
"# CRM Pipeline",
|
|
3095
3328
|
"",
|
|
3096
|
-
`**Total Value**: ${
|
|
3329
|
+
`**Total Value**: ${formatCurrency6(dealsResult.totalValue)}`,
|
|
3097
3330
|
`**Total Deals**: ${dealsResult.total}`,
|
|
3098
3331
|
""
|
|
3099
3332
|
];
|
|
@@ -3101,13 +3334,13 @@ var crmPipelineMarkdownRenderer = {
|
|
|
3101
3334
|
const stageDeals = dealsByStage[stage.id] ?? [];
|
|
3102
3335
|
const stageValue = stageDeals.reduce((sum, d) => sum + d.value, 0);
|
|
3103
3336
|
lines.push(`## ${stage.name}`);
|
|
3104
|
-
lines.push(`_${stageDeals.length} deals \xB7 ${
|
|
3337
|
+
lines.push(`_${stageDeals.length} deals \xB7 ${formatCurrency6(stageValue)}_`);
|
|
3105
3338
|
lines.push("");
|
|
3106
3339
|
if (stageDeals.length === 0) {
|
|
3107
3340
|
lines.push("_No deals_");
|
|
3108
3341
|
} else {
|
|
3109
3342
|
for (const deal3 of stageDeals) {
|
|
3110
|
-
lines.push(`- **${deal3.name}** - ${
|
|
3343
|
+
lines.push(`- **${deal3.name}** - ${formatCurrency6(deal3.value, deal3.currency)}`);
|
|
3111
3344
|
}
|
|
3112
3345
|
}
|
|
3113
3346
|
lines.push("");
|
|
@@ -3147,9 +3380,9 @@ var crmDashboardMarkdownRenderer = {
|
|
|
3147
3380
|
"| Metric | Value |",
|
|
3148
3381
|
"|--------|-------|",
|
|
3149
3382
|
`| Total Deals | ${dealsResult.total} |`,
|
|
3150
|
-
`| Pipeline Value | ${
|
|
3151
|
-
`| Open Deals | ${openDeals.length} (${
|
|
3152
|
-
`| Won Deals | ${wonDeals.length} (${
|
|
3383
|
+
`| Pipeline Value | ${formatCurrency6(dealsResult.totalValue)} |`,
|
|
3384
|
+
`| Open Deals | ${openDeals.length} (${formatCurrency6(openValue)}) |`,
|
|
3385
|
+
`| Won Deals | ${wonDeals.length} (${formatCurrency6(wonValue)}) |`,
|
|
3153
3386
|
`| Lost Deals | ${lostDeals.length} |`,
|
|
3154
3387
|
"",
|
|
3155
3388
|
"## Pipeline Stages",
|
|
@@ -3160,7 +3393,7 @@ var crmDashboardMarkdownRenderer = {
|
|
|
3160
3393
|
for (const stage of stageList.sort((a, b) => a.position - b.position)) {
|
|
3161
3394
|
const stageDeals = openDeals.filter((d) => d.stageId === stage.id);
|
|
3162
3395
|
const stageValue = stageDeals.reduce((sum, d) => sum + d.value, 0);
|
|
3163
|
-
lines.push(`| ${stage.name} | ${stageDeals.length} | ${
|
|
3396
|
+
lines.push(`| ${stage.name} | ${stageDeals.length} | ${formatCurrency6(stageValue)} |`);
|
|
3164
3397
|
}
|
|
3165
3398
|
lines.push("");
|
|
3166
3399
|
lines.push("## Recent Deals");
|
|
@@ -3173,7 +3406,7 @@ var crmDashboardMarkdownRenderer = {
|
|
|
3173
3406
|
lines.push("|------|-------|-------|--------|");
|
|
3174
3407
|
for (const deal3 of recentDeals) {
|
|
3175
3408
|
const stage = stageList.find((s) => s.id === deal3.stageId);
|
|
3176
|
-
lines.push(`| ${deal3.name} | ${
|
|
3409
|
+
lines.push(`| ${deal3.name} | ${formatCurrency6(deal3.value, deal3.currency)} | ${stage?.name ?? "-"} | ${deal3.status} |`);
|
|
3177
3410
|
}
|
|
3178
3411
|
}
|
|
3179
3412
|
return {
|
|
@@ -3185,10 +3418,10 @@ var crmDashboardMarkdownRenderer = {
|
|
|
3185
3418
|
};
|
|
3186
3419
|
|
|
3187
3420
|
// src/ui/renderers/pipeline.renderer.tsx
|
|
3188
|
-
import { jsxDEV as
|
|
3421
|
+
import { jsxDEV as jsxDEV7 } from "react/jsx-dev-runtime";
|
|
3189
3422
|
function CrmPipelineBoardWrapper() {
|
|
3190
3423
|
const { dealsByStage, stages } = useDealList();
|
|
3191
|
-
return /* @__PURE__ */
|
|
3424
|
+
return /* @__PURE__ */ jsxDEV7(CrmPipelineBoard, {
|
|
3192
3425
|
dealsByStage,
|
|
3193
3426
|
stages
|
|
3194
3427
|
}, undefined, false, undefined, this);
|
|
@@ -3202,7 +3435,7 @@ var crmPipelineReactRenderer = {
|
|
|
3202
3435
|
if (desc.source.componentKey !== "CrmPipelineView") {
|
|
3203
3436
|
throw new Error(`Unknown component: ${desc.source.componentKey}`);
|
|
3204
3437
|
}
|
|
3205
|
-
return /* @__PURE__ */
|
|
3438
|
+
return /* @__PURE__ */ jsxDEV7(CrmPipelineBoardWrapper, {}, undefined, false, undefined, this);
|
|
3206
3439
|
}
|
|
3207
3440
|
};
|
|
3208
3441
|
// src/index.ts
|