@contractspec/example.crm-pipeline 1.46.0 → 1.47.0
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$colon$bundle.log +111 -36
- package/.turbo/turbo-build.log +109 -34
- package/CHANGELOG.md +56 -0
- package/dist/crm-pipeline.feature.d.ts +2 -2
- package/dist/crm-pipeline.feature.d.ts.map +1 -1
- package/dist/crm-pipeline.feature.js +9 -2
- package/dist/crm-pipeline.feature.js.map +1 -1
- package/dist/deal/deal.enum.d.ts +3 -3
- package/dist/deal/deal.enum.d.ts.map +1 -1
- package/dist/deal/deal.operation.d.ts +128 -128
- package/dist/deal/deal.operation.d.ts.map +1 -1
- package/dist/deal/deal.schema.d.ts +71 -71
- package/dist/deal/deal.test-spec.d.ts +8 -0
- package/dist/deal/deal.test-spec.d.ts.map +1 -0
- package/dist/deal/deal.test-spec.js +65 -0
- package/dist/deal/deal.test-spec.js.map +1 -0
- package/dist/entities/company.entity.d.ts +28 -28
- package/dist/entities/company.entity.d.ts.map +1 -1
- package/dist/entities/contact.entity.d.ts +32 -32
- package/dist/entities/contact.entity.d.ts.map +1 -1
- package/dist/entities/deal.entity.d.ts +53 -53
- package/dist/entities/index.js.map +1 -1
- package/dist/entities/task.entity.d.ts +43 -43
- package/dist/entities/task.entity.d.ts.map +1 -1
- package/dist/events/contact.event.d.ts +8 -8
- package/dist/events/contact.event.d.ts.map +1 -1
- package/dist/events/contact.event.js +1 -1
- package/dist/events/deal.event.d.ts +30 -30
- package/dist/events/deal.event.js +1 -1
- package/dist/events/task.event.d.ts +8 -8
- package/dist/events/task.event.d.ts.map +1 -1
- package/dist/events/task.event.js +1 -1
- package/dist/example.d.ts +2 -2
- package/dist/example.d.ts.map +1 -1
- package/dist/example.js +4 -2
- package/dist/example.js.map +1 -1
- package/dist/handlers/crm.handlers.d.ts +89 -0
- package/dist/handlers/crm.handlers.d.ts.map +1 -0
- package/dist/handlers/crm.handlers.js +172 -0
- package/dist/handlers/crm.handlers.js.map +1 -0
- package/dist/handlers/deal.handlers.js.map +1 -1
- package/dist/handlers/index.d.ts +2 -1
- package/dist/handlers/index.js +2 -1
- package/dist/handlers/mock-data.js.map +1 -1
- package/dist/index.d.ts +16 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -1
- package/dist/index.js.map +1 -1
- package/dist/presentations/dashboard.presentation.d.ts +3 -4
- package/dist/presentations/dashboard.presentation.d.ts.map +1 -1
- package/dist/presentations/dashboard.presentation.js +8 -5
- package/dist/presentations/dashboard.presentation.js.map +1 -1
- package/dist/presentations/pipeline.presentation.d.ts +5 -6
- package/dist/presentations/pipeline.presentation.d.ts.map +1 -1
- package/dist/presentations/pipeline.presentation.js +12 -9
- package/dist/presentations/pipeline.presentation.js.map +1 -1
- package/dist/seeders/index.d.ts +10 -0
- package/dist/seeders/index.d.ts.map +1 -0
- package/dist/seeders/index.js +47 -0
- package/dist/seeders/index.js.map +1 -0
- package/dist/shared/overlay-types.d.ts +34 -0
- package/dist/shared/overlay-types.d.ts.map +1 -0
- package/dist/shared/overlay-types.js +0 -0
- package/dist/ui/CrmDashboard.d.ts +7 -0
- package/dist/ui/CrmDashboard.d.ts.map +1 -0
- package/dist/ui/CrmDashboard.js +304 -0
- package/dist/ui/CrmDashboard.js.map +1 -0
- package/dist/ui/CrmDealCard.d.ts +15 -0
- package/dist/ui/CrmDealCard.d.ts.map +1 -0
- package/dist/ui/CrmDealCard.js +49 -0
- package/dist/ui/CrmDealCard.js.map +1 -0
- package/dist/ui/CrmPipelineBoard.d.ts +23 -0
- package/dist/ui/CrmPipelineBoard.d.ts.map +1 -0
- package/dist/ui/CrmPipelineBoard.js +98 -0
- package/dist/ui/CrmPipelineBoard.js.map +1 -0
- package/dist/ui/hooks/index.d.ts +3 -0
- package/dist/ui/hooks/index.js +6 -0
- package/dist/ui/hooks/useDealList.d.ts +35 -0
- package/dist/ui/hooks/useDealList.d.ts.map +1 -0
- package/dist/ui/hooks/useDealList.js +94 -0
- package/dist/ui/hooks/useDealList.js.map +1 -0
- package/dist/ui/hooks/useDealMutations.d.ts +26 -0
- package/dist/ui/hooks/useDealMutations.d.ts.map +1 -0
- package/dist/ui/hooks/useDealMutations.js +159 -0
- package/dist/ui/hooks/useDealMutations.js.map +1 -0
- package/dist/ui/index.d.ts +14 -0
- package/dist/ui/index.js +15 -0
- package/dist/ui/modals/CreateDealModal.d.ts +33 -0
- package/dist/ui/modals/CreateDealModal.d.ts.map +1 -0
- package/dist/ui/modals/CreateDealModal.js +183 -0
- package/dist/ui/modals/CreateDealModal.js.map +1 -0
- package/dist/ui/modals/DealActionsModal.d.ts +51 -0
- package/dist/ui/modals/DealActionsModal.d.ts.map +1 -0
- package/dist/ui/modals/DealActionsModal.js +372 -0
- package/dist/ui/modals/DealActionsModal.js.map +1 -0
- package/dist/ui/modals/index.d.ts +3 -0
- package/dist/ui/modals/index.js +4 -0
- package/dist/ui/overlays/demo-overlays.d.ts +19 -0
- package/dist/ui/overlays/demo-overlays.d.ts.map +1 -0
- package/dist/ui/overlays/demo-overlays.js +68 -0
- package/dist/ui/overlays/demo-overlays.js.map +1 -0
- package/dist/ui/overlays/index.d.ts +2 -0
- package/dist/ui/overlays/index.js +3 -0
- package/dist/ui/renderers/index.d.ts +3 -0
- package/dist/ui/renderers/index.js +4 -0
- package/dist/ui/renderers/pipeline.markdown.d.ts +23 -0
- package/dist/ui/renderers/pipeline.markdown.d.ts.map +1 -0
- package/dist/ui/renderers/pipeline.markdown.js +118 -0
- package/dist/ui/renderers/pipeline.markdown.js.map +1 -0
- package/dist/ui/renderers/pipeline.renderer.d.ts +9 -0
- package/dist/ui/renderers/pipeline.renderer.d.ts.map +1 -0
- package/dist/ui/renderers/pipeline.renderer.js +28 -0
- package/dist/ui/renderers/pipeline.renderer.js.map +1 -0
- package/package.json +38 -13
- package/src/crm-pipeline.feature.ts +3 -3
- package/src/deal/deal.test-spec.ts +55 -0
- package/src/example.ts +3 -3
- package/src/handlers/crm.handlers.ts +415 -0
- package/src/handlers/index.ts +3 -0
- package/src/index.ts +1 -0
- package/src/presentations/dashboard.presentation.ts +5 -6
- package/src/presentations/pipeline.presentation.ts +9 -10
- package/src/seeders/index.ts +35 -0
- package/src/shared/overlay-types.ts +39 -0
- package/src/ui/CrmDashboard.tsx +311 -0
- package/src/ui/CrmDealCard.tsx +83 -0
- package/src/ui/CrmPipelineBoard.tsx +136 -0
- package/src/ui/hooks/index.ts +10 -0
- package/src/ui/hooks/useDealList.ts +113 -0
- package/src/ui/hooks/useDealMutations.ts +174 -0
- package/src/ui/index.ts +18 -0
- package/src/ui/modals/CreateDealModal.tsx +239 -0
- package/src/ui/modals/DealActionsModal.tsx +424 -0
- package/src/ui/modals/index.ts +2 -0
- package/src/ui/overlays/demo-overlays.ts +68 -0
- package/src/ui/overlays/index.ts +1 -0
- package/src/ui/renderers/index.ts +6 -0
- package/src/ui/renderers/pipeline.markdown.ts +198 -0
- package/src/ui/renderers/pipeline.renderer.tsx +35 -0
- package/tsconfig.json +1 -1
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CrmPipelineBoard.js","names":[],"sources":["../../src/ui/CrmPipelineBoard.tsx"],"sourcesContent":["'use client';\n\n/**\n * CRM Pipeline Board - Kanban-style deal board\n *\n * Features:\n * - Visual pipeline stages\n * - Deal cards with click actions\n * - Quick move dropdown per card\n * - Drag-and-drop ready (UI only, no lib dependency)\n */\nimport { useState } from 'react';\n// import { Button } from '@contractspec/lib.design-system';\nimport type { Deal } from './hooks/useDealList';\nimport { CrmDealCard } from './CrmDealCard';\n\ninterface CrmPipelineBoardProps {\n dealsByStage: Record<string, Deal[]>;\n stages: { id: string; name: string; position: number }[];\n onDealClick?: (dealId: string) => void;\n onDealMove?: (dealId: string, toStageId: string) => void;\n}\n\nfunction formatCurrency(value: number): string {\n if (value >= 1000000) return `$${(value / 1000000).toFixed(1)}M`;\n if (value >= 1000) return `$${(value / 1000).toFixed(0)}K`;\n return `$${value}`;\n}\n\nexport function CrmPipelineBoard({\n dealsByStage,\n stages,\n onDealClick,\n onDealMove,\n}: CrmPipelineBoardProps) {\n // Track which deal has the quick-move dropdown open\n const [quickMoveOpen, setQuickMoveOpen] = useState<string | null>(null);\n\n // Sort stages by position\n const sortedStages = [...stages].sort((a, b) => a.position - b.position);\n\n const handleQuickMove = (dealId: string, toStageId: string) => {\n onDealMove?.(dealId, toStageId);\n setQuickMoveOpen(null);\n };\n\n return (\n <div className=\"flex gap-4 overflow-x-auto pb-4\">\n {sortedStages.map((stage) => {\n const deals = dealsByStage[stage.id] ?? [];\n const stageValue = deals.reduce((sum, d) => sum + d.value, 0);\n\n return (\n <div\n key={stage.id}\n className=\"bg-muted/30 flex w-72 flex-shrink-0 flex-col rounded-lg\"\n >\n {/* Stage Header */}\n <div className=\"border-border flex items-center justify-between border-b px-3 py-2\">\n <div>\n <h3 className=\"font-medium\">{stage.name}</h3>\n <p className=\"text-muted-foreground text-xs\">\n {deals.length} deals · {formatCurrency(stageValue)}\n </p>\n </div>\n <span className=\"bg-muted flex h-6 w-6 items-center justify-center rounded-full text-xs font-medium\">\n {deals.length}\n </span>\n </div>\n\n {/* Deals Column */}\n <div className=\"flex flex-1 flex-col gap-2 p-2\">\n {deals.length === 0 ? (\n <div className=\"border-muted-foreground/20 text-muted-foreground flex h-24 items-center justify-center rounded-md border-2 border-dashed text-xs\">\n No deals\n </div>\n ) : (\n deals.map((deal) => (\n <div key={deal.id} className=\"group relative\">\n <CrmDealCard\n deal={deal}\n onClick={() => onDealClick?.(deal.id)}\n />\n\n {/* Quick Move Button */}\n {deal.status === 'OPEN' && onDealMove && (\n <div className=\"absolute top-1 right-1 opacity-0 transition-opacity group-hover:opacity-100\">\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n setQuickMoveOpen(\n quickMoveOpen === deal.id ? null : deal.id\n );\n }}\n className=\"bg-background border-border hover:bg-muted flex h-6 w-6 items-center justify-center rounded border text-xs shadow-sm\"\n title=\"Quick move\"\n >\n ➡️\n </button>\n\n {/* Quick Move Dropdown */}\n {quickMoveOpen === deal.id && (\n <div className=\"bg-card border-border absolute top-7 right-0 z-20 min-w-[140px] rounded-lg border py-1 shadow-lg\">\n <p className=\"text-muted-foreground px-3 py-1 text-xs font-medium\">\n Move to:\n </p>\n {sortedStages\n .filter((s) => s.id !== deal.stageId)\n .map((s) => (\n <button\n key={s.id}\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n handleQuickMove(deal.id, s.id);\n }}\n className=\"hover:bg-muted w-full px-3 py-1.5 text-left text-sm\"\n >\n {s.name}\n </button>\n ))}\n </div>\n )}\n </div>\n )}\n </div>\n ))\n )}\n </div>\n </div>\n );\n })}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAuBA,SAAS,eAAe,OAAuB;AAC7C,KAAI,SAAS,IAAS,QAAO,KAAK,QAAQ,KAAS,QAAQ,EAAE,CAAC;AAC9D,KAAI,SAAS,IAAM,QAAO,KAAK,QAAQ,KAAM,QAAQ,EAAE,CAAC;AACxD,QAAO,IAAI;;AAGb,SAAgB,iBAAiB,EAC/B,cACA,QACA,aACA,cACwB;CAExB,MAAM,CAAC,eAAe,oBAAoB,SAAwB,KAAK;CAGvE,MAAM,eAAe,CAAC,GAAG,OAAO,CAAC,MAAM,GAAG,MAAM,EAAE,WAAW,EAAE,SAAS;CAExE,MAAM,mBAAmB,QAAgB,cAAsB;AAC7D,eAAa,QAAQ,UAAU;AAC/B,mBAAiB,KAAK;;AAGxB,QACE,oBAAC;EAAI,WAAU;YACZ,aAAa,KAAK,UAAU;GAC3B,MAAM,QAAQ,aAAa,MAAM,OAAO,EAAE;GAC1C,MAAM,aAAa,MAAM,QAAQ,KAAK,MAAM,MAAM,EAAE,OAAO,EAAE;AAE7D,UACE,qBAAC;IAEC,WAAU;eAGV,qBAAC;KAAI,WAAU;gBACb,qBAAC,oBACC,oBAAC;MAAG,WAAU;gBAAe,MAAM;OAAU,EAC7C,qBAAC;MAAE,WAAU;;OACV,MAAM;OAAO;OAAU,eAAe,WAAW;;OAChD,IACA,EACN,oBAAC;MAAK,WAAU;gBACb,MAAM;OACF;MACH,EAGN,oBAAC;KAAI,WAAU;eACZ,MAAM,WAAW,IAChB,oBAAC;MAAI,WAAU;gBAAmI;OAE5I,GAEN,MAAM,KAAK,SACT,qBAAC;MAAkB,WAAU;iBAC3B,oBAAC;OACO;OACN,eAAe,cAAc,KAAK,GAAG;QACrC,EAGD,KAAK,WAAW,UAAU,cACzB,qBAAC;OAAI,WAAU;kBACb,oBAAC;QACC,MAAK;QACL,UAAU,MAAM;AACd,WAAE,iBAAiB;AACnB,0BACE,kBAAkB,KAAK,KAAK,OAAO,KAAK,GACzC;;QAEH,WAAU;QACV,OAAM;kBACP;SAEQ,EAGR,kBAAkB,KAAK,MACtB,qBAAC;QAAI,WAAU;mBACb,oBAAC;SAAE,WAAU;mBAAsD;UAE/D,EACH,aACE,QAAQ,MAAM,EAAE,OAAO,KAAK,QAAQ,CACpC,KAAK,MACJ,oBAAC;SAEC,MAAK;SACL,UAAU,MAAM;AACd,YAAE,iBAAiB;AACnB,0BAAgB,KAAK,IAAI,EAAE,GAAG;;SAEhC,WAAU;mBAET,EAAE;WARE,EAAE,GASA,CACT;SACA;QAEJ;QA9CA,KAAK,GAgDT,CACN;MAEA;MA3ED,MAAM,GA4EP;IAER;GACE"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Deal as Deal$1, ListDealsOutput as ListDealsOutput$1, Stage } from "../../handlers/crm.handlers.js";
|
|
2
|
+
|
|
3
|
+
//#region src/ui/hooks/useDealList.d.ts
|
|
4
|
+
type Deal = Deal$1;
|
|
5
|
+
type ListDealsOutput = ListDealsOutput$1;
|
|
6
|
+
interface UseDealListOptions {
|
|
7
|
+
pipelineId?: string;
|
|
8
|
+
stageId?: string;
|
|
9
|
+
status?: 'OPEN' | 'WON' | 'LOST' | 'all';
|
|
10
|
+
search?: string;
|
|
11
|
+
limit?: number;
|
|
12
|
+
}
|
|
13
|
+
declare function useDealList(options?: UseDealListOptions): {
|
|
14
|
+
data: ListDealsOutput$1 | null;
|
|
15
|
+
dealsByStage: Record<string, Deal$1[]>;
|
|
16
|
+
stages: Stage[];
|
|
17
|
+
loading: boolean;
|
|
18
|
+
error: Error | null;
|
|
19
|
+
stats: {
|
|
20
|
+
total: number;
|
|
21
|
+
totalValue: number;
|
|
22
|
+
openCount: number;
|
|
23
|
+
openValue: number;
|
|
24
|
+
wonCount: number;
|
|
25
|
+
wonValue: number;
|
|
26
|
+
lostCount: number;
|
|
27
|
+
} | null;
|
|
28
|
+
page: number;
|
|
29
|
+
refetch: () => Promise<void>;
|
|
30
|
+
nextPage: () => void;
|
|
31
|
+
prevPage: () => false | void;
|
|
32
|
+
};
|
|
33
|
+
//#endregion
|
|
34
|
+
export { Deal, ListDealsOutput, UseDealListOptions, useDealList };
|
|
35
|
+
//# sourceMappingURL=useDealList.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useDealList.d.ts","names":[],"sources":["../../../src/ui/hooks/useDealList.ts"],"sourcesContent":[],"mappings":";;;KAiBY,IAAA,GAAO;KACP,eAAA,GAAkB;AADlB,UAGK,kBAAA,CAHa;EAClB,UAAA,CAAA,EAAA,MAAe;EAEV,OAAA,CAAA,EAAA,MAAA;EAQD,MAAA,CAAA,EAAA,MAAW,GAAA,KAAA,GAAA,MAAA,GAAA,KAAA;EAAU,MAAA,CAAA,EAAA,MAAA;;;iBAArB,WAAA,WAAqB"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import "../../handlers/crm.handlers.js";
|
|
4
|
+
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
5
|
+
import { useTemplateRuntime } from "@contractspec/lib.example-shared-ui";
|
|
6
|
+
|
|
7
|
+
//#region src/ui/hooks/useDealList.ts
|
|
8
|
+
/**
|
|
9
|
+
* Hook for fetching and managing deal list data
|
|
10
|
+
*
|
|
11
|
+
* Uses runtime-local database-backed handlers.
|
|
12
|
+
*/
|
|
13
|
+
function useDealList(options = {}) {
|
|
14
|
+
const { handlers, projectId } = useTemplateRuntime();
|
|
15
|
+
const { crm } = handlers;
|
|
16
|
+
const [data, setData] = useState(null);
|
|
17
|
+
const [dealsByStage, setDealsByStage] = useState({});
|
|
18
|
+
const [stages, setStages] = useState([]);
|
|
19
|
+
const [loading, setLoading] = useState(true);
|
|
20
|
+
const [error, setError] = useState(null);
|
|
21
|
+
const [page, setPage] = useState(1);
|
|
22
|
+
const pipelineId = options.pipelineId ?? "pipeline-1";
|
|
23
|
+
const fetchData = useCallback(async () => {
|
|
24
|
+
setLoading(true);
|
|
25
|
+
setError(null);
|
|
26
|
+
try {
|
|
27
|
+
const [dealsResult, stageDealsResult, stagesResult] = await Promise.all([
|
|
28
|
+
crm.listDeals({
|
|
29
|
+
projectId,
|
|
30
|
+
pipelineId,
|
|
31
|
+
stageId: options.stageId,
|
|
32
|
+
status: options.status === "all" ? void 0 : options.status,
|
|
33
|
+
search: options.search,
|
|
34
|
+
limit: options.limit ?? 50,
|
|
35
|
+
offset: (page - 1) * (options.limit ?? 50)
|
|
36
|
+
}),
|
|
37
|
+
crm.getDealsByStage({
|
|
38
|
+
projectId,
|
|
39
|
+
pipelineId
|
|
40
|
+
}),
|
|
41
|
+
crm.getPipelineStages({ pipelineId })
|
|
42
|
+
]);
|
|
43
|
+
setData(dealsResult);
|
|
44
|
+
setDealsByStage(stageDealsResult);
|
|
45
|
+
setStages(stagesResult);
|
|
46
|
+
} catch (err) {
|
|
47
|
+
setError(err instanceof Error ? err : /* @__PURE__ */ new Error("Unknown error"));
|
|
48
|
+
} finally {
|
|
49
|
+
setLoading(false);
|
|
50
|
+
}
|
|
51
|
+
}, [
|
|
52
|
+
crm,
|
|
53
|
+
projectId,
|
|
54
|
+
pipelineId,
|
|
55
|
+
options.stageId,
|
|
56
|
+
options.status,
|
|
57
|
+
options.search,
|
|
58
|
+
options.limit,
|
|
59
|
+
page
|
|
60
|
+
]);
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
fetchData();
|
|
63
|
+
}, [fetchData]);
|
|
64
|
+
return {
|
|
65
|
+
data,
|
|
66
|
+
dealsByStage,
|
|
67
|
+
stages,
|
|
68
|
+
loading,
|
|
69
|
+
error,
|
|
70
|
+
stats: useMemo(() => {
|
|
71
|
+
if (!data) return null;
|
|
72
|
+
const open = data.deals.filter((d) => d.status === "OPEN");
|
|
73
|
+
const won = data.deals.filter((d) => d.status === "WON");
|
|
74
|
+
const lost = data.deals.filter((d) => d.status === "LOST");
|
|
75
|
+
return {
|
|
76
|
+
total: data.total,
|
|
77
|
+
totalValue: data.totalValue,
|
|
78
|
+
openCount: open.length,
|
|
79
|
+
openValue: open.reduce((sum, d) => sum + d.value, 0),
|
|
80
|
+
wonCount: won.length,
|
|
81
|
+
wonValue: won.reduce((sum, d) => sum + d.value, 0),
|
|
82
|
+
lostCount: lost.length
|
|
83
|
+
};
|
|
84
|
+
}, [data]),
|
|
85
|
+
page,
|
|
86
|
+
refetch: fetchData,
|
|
87
|
+
nextPage: () => setPage((p) => p + 1),
|
|
88
|
+
prevPage: () => page > 1 && setPage((p) => p - 1)
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
//#endregion
|
|
93
|
+
export { useDealList };
|
|
94
|
+
//# sourceMappingURL=useDealList.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useDealList.js","names":[],"sources":["../../../src/ui/hooks/useDealList.ts"],"sourcesContent":["'use client';\n\n/**\n * Hook for fetching and managing deal list data\n *\n * Uses runtime-local database-backed handlers.\n */\nimport { useCallback, useEffect, useMemo, useState } from 'react';\nimport { useTemplateRuntime } from '@contractspec/lib.example-shared-ui';\nimport {\n type CrmHandlers,\n type Deal as RuntimeDeal,\n type ListDealsOutput as RuntimeListDealsOutput,\n type Stage,\n} from '../../handlers/crm.handlers';\n\n// Re-export types for convenience\nexport type Deal = RuntimeDeal;\nexport type ListDealsOutput = RuntimeListDealsOutput;\n\nexport interface UseDealListOptions {\n pipelineId?: string;\n stageId?: string;\n status?: 'OPEN' | 'WON' | 'LOST' | 'all';\n search?: string;\n limit?: number;\n}\n\nexport function useDealList(options: UseDealListOptions = {}) {\n const { handlers, projectId } = useTemplateRuntime<{ crm: CrmHandlers }>();\n const { crm } = handlers;\n\n const [data, setData] = useState<ListDealsOutput | null>(null);\n const [dealsByStage, setDealsByStage] = useState<Record<string, Deal[]>>({});\n const [stages, setStages] = useState<Stage[]>([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n const [page, setPage] = useState(1);\n\n const pipelineId = options.pipelineId ?? 'pipeline-1';\n\n const fetchData = useCallback(async () => {\n setLoading(true);\n setError(null);\n\n try {\n const [dealsResult, stageDealsResult, stagesResult] = await Promise.all([\n crm.listDeals({\n projectId,\n pipelineId,\n stageId: options.stageId,\n status: options.status === 'all' ? undefined : options.status,\n search: options.search,\n limit: options.limit ?? 50,\n offset: (page - 1) * (options.limit ?? 50),\n }),\n crm.getDealsByStage({ projectId, pipelineId }),\n crm.getPipelineStages({ pipelineId }),\n ]);\n setData(dealsResult);\n setDealsByStage(stageDealsResult);\n setStages(stagesResult);\n } catch (err) {\n setError(err instanceof Error ? err : new Error('Unknown error'));\n } finally {\n setLoading(false);\n }\n }, [\n crm,\n projectId,\n pipelineId,\n options.stageId,\n options.status,\n options.search,\n options.limit,\n page,\n ]);\n\n useEffect(() => {\n fetchData();\n }, [fetchData]);\n\n // Calculate stats\n const stats = useMemo(() => {\n if (!data) return null;\n const open = data.deals.filter((d: Deal) => d.status === 'OPEN');\n const won = data.deals.filter((d: Deal) => d.status === 'WON');\n const lost = data.deals.filter((d: Deal) => d.status === 'LOST');\n\n return {\n total: data.total,\n totalValue: data.totalValue,\n openCount: open.length,\n openValue: open.reduce((sum: number, d: Deal) => sum + d.value, 0),\n wonCount: won.length,\n wonValue: won.reduce((sum: number, d: Deal) => sum + d.value, 0),\n lostCount: lost.length,\n };\n }, [data]);\n\n return {\n data,\n dealsByStage,\n stages,\n loading,\n error,\n stats,\n page,\n refetch: fetchData,\n nextPage: () => setPage((p) => p + 1),\n prevPage: () => page > 1 && setPage((p) => p - 1),\n };\n}\n"],"mappings":";;;;;;;;;;;;AA4BA,SAAgB,YAAY,UAA8B,EAAE,EAAE;CAC5D,MAAM,EAAE,UAAU,cAAc,oBAA0C;CAC1E,MAAM,EAAE,QAAQ;CAEhB,MAAM,CAAC,MAAM,WAAW,SAAiC,KAAK;CAC9D,MAAM,CAAC,cAAc,mBAAmB,SAAiC,EAAE,CAAC;CAC5E,MAAM,CAAC,QAAQ,aAAa,SAAkB,EAAE,CAAC;CACjD,MAAM,CAAC,SAAS,cAAc,SAAS,KAAK;CAC5C,MAAM,CAAC,OAAO,YAAY,SAAuB,KAAK;CACtD,MAAM,CAAC,MAAM,WAAW,SAAS,EAAE;CAEnC,MAAM,aAAa,QAAQ,cAAc;CAEzC,MAAM,YAAY,YAAY,YAAY;AACxC,aAAW,KAAK;AAChB,WAAS,KAAK;AAEd,MAAI;GACF,MAAM,CAAC,aAAa,kBAAkB,gBAAgB,MAAM,QAAQ,IAAI;IACtE,IAAI,UAAU;KACZ;KACA;KACA,SAAS,QAAQ;KACjB,QAAQ,QAAQ,WAAW,QAAQ,SAAY,QAAQ;KACvD,QAAQ,QAAQ;KAChB,OAAO,QAAQ,SAAS;KACxB,SAAS,OAAO,MAAM,QAAQ,SAAS;KACxC,CAAC;IACF,IAAI,gBAAgB;KAAE;KAAW;KAAY,CAAC;IAC9C,IAAI,kBAAkB,EAAE,YAAY,CAAC;IACtC,CAAC;AACF,WAAQ,YAAY;AACpB,mBAAgB,iBAAiB;AACjC,aAAU,aAAa;WAChB,KAAK;AACZ,YAAS,eAAe,QAAQ,sBAAM,IAAI,MAAM,gBAAgB,CAAC;YACzD;AACR,cAAW,MAAM;;IAElB;EACD;EACA;EACA;EACA,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR;EACD,CAAC;AAEF,iBAAgB;AACd,aAAW;IACV,CAAC,UAAU,CAAC;AAoBf,QAAO;EACL;EACA;EACA;EACA;EACA;EACA,OAvBY,cAAc;AAC1B,OAAI,CAAC,KAAM,QAAO;GAClB,MAAM,OAAO,KAAK,MAAM,QAAQ,MAAY,EAAE,WAAW,OAAO;GAChE,MAAM,MAAM,KAAK,MAAM,QAAQ,MAAY,EAAE,WAAW,MAAM;GAC9D,MAAM,OAAO,KAAK,MAAM,QAAQ,MAAY,EAAE,WAAW,OAAO;AAEhE,UAAO;IACL,OAAO,KAAK;IACZ,YAAY,KAAK;IACjB,WAAW,KAAK;IAChB,WAAW,KAAK,QAAQ,KAAa,MAAY,MAAM,EAAE,OAAO,EAAE;IAClE,UAAU,IAAI;IACd,UAAU,IAAI,QAAQ,KAAa,MAAY,MAAM,EAAE,OAAO,EAAE;IAChE,WAAW,KAAK;IACjB;KACA,CAAC,KAAK,CAAC;EASR;EACA,SAAS;EACT,gBAAgB,SAAS,MAAM,IAAI,EAAE;EACrC,gBAAgB,OAAO,KAAK,SAAS,MAAM,IAAI,EAAE;EAClD"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { CreateDealInput, Deal, LoseDealInput, MoveDealInput, WinDealInput } from "../../handlers/crm.handlers.js";
|
|
2
|
+
|
|
3
|
+
//#region src/ui/hooks/useDealMutations.d.ts
|
|
4
|
+
interface MutationState<T> {
|
|
5
|
+
loading: boolean;
|
|
6
|
+
error: Error | null;
|
|
7
|
+
data: T | null;
|
|
8
|
+
}
|
|
9
|
+
interface UseDealMutationsOptions {
|
|
10
|
+
onSuccess?: () => void;
|
|
11
|
+
onError?: (error: Error) => void;
|
|
12
|
+
}
|
|
13
|
+
declare function useDealMutations(options?: UseDealMutationsOptions): {
|
|
14
|
+
createDeal: (input: CreateDealInput) => Promise<Deal | null>;
|
|
15
|
+
moveDeal: (input: MoveDealInput) => Promise<Deal | null>;
|
|
16
|
+
winDeal: (input: WinDealInput) => Promise<Deal | null>;
|
|
17
|
+
loseDeal: (input: LoseDealInput) => Promise<Deal | null>;
|
|
18
|
+
createState: MutationState<Deal>;
|
|
19
|
+
moveState: MutationState<Deal>;
|
|
20
|
+
winState: MutationState<Deal>;
|
|
21
|
+
loseState: MutationState<Deal>;
|
|
22
|
+
isLoading: boolean;
|
|
23
|
+
};
|
|
24
|
+
//#endregion
|
|
25
|
+
export { MutationState, UseDealMutationsOptions, useDealMutations };
|
|
26
|
+
//# sourceMappingURL=useDealMutations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useDealMutations.d.ts","names":[],"sources":["../../../src/ui/hooks/useDealMutations.ts"],"sourcesContent":[],"mappings":";;;UAoBiB;;EAAA,KAAA,EAER,KAFQ,GAAA,IAAa;EAMb,IAAA,EAHT,CAGS,GAAA,IAAA;AAKjB;AAA0C,UALzB,uBAAA,CAKyB;EAgCxB,SAAA,CAAA,EAAA,GAAA,GAAA,IAAA;EAA0B,OAAA,CAAA,EAAA,CAAA,KAAA,EAnCxB,KAmCwB,EAAA,GAAA,IAAA;;AAyB1B,iBAzDF,gBAAA,CAyDE,OAAA,CAAA,EAzDwB,uBAyDxB,CAAA,EAAA;EAAwB,UAAA,EAAA,CAAA,KAAA,EAzBxB,eAyBwB,EAAA,GAzBN,OAyBM,CAzBE,IAyBF,GAAA,IAAA,CAAA;EAAR,QAAA,EAAA,CAAA,KAAA,EAAhB,aAAgB,EAAA,GAAA,OAAA,CAAQ,IAAR,GAAA,IAAA,CAAA;EAsBhB,OAAA,EAAA,CAAA,KAAA,EAAA,YAAA,EAAA,GAAe,OAAf,CAAuB,IAAvB,GAAA,IAAA,CAAA;EAAuB,QAAA,EAAA,CAAA,KAAA,EAsBvB,aAtBuB,EAAA,GAsBP,OAtBO,CAsBC,IAtBD,GAAA,IAAA,CAAA;EAAR,WAAA,eAAA,KAAA,CAAA;EAsBf,SAAA,eAAA,KAAA,CAAA;EAAwB,QAAA,eAAA,KAAA,CAAA;EAAR,SAAA,eAAA,KAAA,CAAA"}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { useCallback, useState } from "react";
|
|
2
|
+
import { useTemplateRuntime } from "@contractspec/lib.example-shared-ui";
|
|
3
|
+
|
|
4
|
+
//#region src/ui/hooks/useDealMutations.ts
|
|
5
|
+
/**
|
|
6
|
+
* Hook for CRM deal mutations (commands)
|
|
7
|
+
*
|
|
8
|
+
* Uses runtime-local database-backed handlers for:
|
|
9
|
+
* - CreateDealContract
|
|
10
|
+
* - MoveDealContract
|
|
11
|
+
* - WinDealContract
|
|
12
|
+
* - LoseDealContract
|
|
13
|
+
*/
|
|
14
|
+
function useDealMutations(options = {}) {
|
|
15
|
+
const { handlers, projectId } = useTemplateRuntime();
|
|
16
|
+
const { crm } = handlers;
|
|
17
|
+
const [createState, setCreateState] = useState({
|
|
18
|
+
loading: false,
|
|
19
|
+
error: null,
|
|
20
|
+
data: null
|
|
21
|
+
});
|
|
22
|
+
const [moveState, setMoveState] = useState({
|
|
23
|
+
loading: false,
|
|
24
|
+
error: null,
|
|
25
|
+
data: null
|
|
26
|
+
});
|
|
27
|
+
const [winState, setWinState] = useState({
|
|
28
|
+
loading: false,
|
|
29
|
+
error: null,
|
|
30
|
+
data: null
|
|
31
|
+
});
|
|
32
|
+
const [loseState, setLoseState] = useState({
|
|
33
|
+
loading: false,
|
|
34
|
+
error: null,
|
|
35
|
+
data: null
|
|
36
|
+
});
|
|
37
|
+
return {
|
|
38
|
+
createDeal: useCallback(async (input) => {
|
|
39
|
+
setCreateState({
|
|
40
|
+
loading: true,
|
|
41
|
+
error: null,
|
|
42
|
+
data: null
|
|
43
|
+
});
|
|
44
|
+
try {
|
|
45
|
+
const result = await crm.createDeal(input, {
|
|
46
|
+
projectId,
|
|
47
|
+
ownerId: "user-1"
|
|
48
|
+
});
|
|
49
|
+
setCreateState({
|
|
50
|
+
loading: false,
|
|
51
|
+
error: null,
|
|
52
|
+
data: result
|
|
53
|
+
});
|
|
54
|
+
options.onSuccess?.();
|
|
55
|
+
return result;
|
|
56
|
+
} catch (err) {
|
|
57
|
+
const error = err instanceof Error ? err : /* @__PURE__ */ new Error("Failed to create deal");
|
|
58
|
+
setCreateState({
|
|
59
|
+
loading: false,
|
|
60
|
+
error,
|
|
61
|
+
data: null
|
|
62
|
+
});
|
|
63
|
+
options.onError?.(error);
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
}, [
|
|
67
|
+
crm,
|
|
68
|
+
projectId,
|
|
69
|
+
options
|
|
70
|
+
]),
|
|
71
|
+
moveDeal: useCallback(async (input) => {
|
|
72
|
+
setMoveState({
|
|
73
|
+
loading: true,
|
|
74
|
+
error: null,
|
|
75
|
+
data: null
|
|
76
|
+
});
|
|
77
|
+
try {
|
|
78
|
+
const result = await crm.moveDeal(input);
|
|
79
|
+
setMoveState({
|
|
80
|
+
loading: false,
|
|
81
|
+
error: null,
|
|
82
|
+
data: result
|
|
83
|
+
});
|
|
84
|
+
options.onSuccess?.();
|
|
85
|
+
return result;
|
|
86
|
+
} catch (err) {
|
|
87
|
+
const error = err instanceof Error ? err : /* @__PURE__ */ new Error("Failed to move deal");
|
|
88
|
+
setMoveState({
|
|
89
|
+
loading: false,
|
|
90
|
+
error,
|
|
91
|
+
data: null
|
|
92
|
+
});
|
|
93
|
+
options.onError?.(error);
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
}, [crm, options]),
|
|
97
|
+
winDeal: useCallback(async (input) => {
|
|
98
|
+
setWinState({
|
|
99
|
+
loading: true,
|
|
100
|
+
error: null,
|
|
101
|
+
data: null
|
|
102
|
+
});
|
|
103
|
+
try {
|
|
104
|
+
const result = await crm.winDeal(input);
|
|
105
|
+
setWinState({
|
|
106
|
+
loading: false,
|
|
107
|
+
error: null,
|
|
108
|
+
data: result
|
|
109
|
+
});
|
|
110
|
+
options.onSuccess?.();
|
|
111
|
+
return result;
|
|
112
|
+
} catch (err) {
|
|
113
|
+
const error = err instanceof Error ? err : /* @__PURE__ */ new Error("Failed to mark deal as won");
|
|
114
|
+
setWinState({
|
|
115
|
+
loading: false,
|
|
116
|
+
error,
|
|
117
|
+
data: null
|
|
118
|
+
});
|
|
119
|
+
options.onError?.(error);
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
}, [crm, options]),
|
|
123
|
+
loseDeal: useCallback(async (input) => {
|
|
124
|
+
setLoseState({
|
|
125
|
+
loading: true,
|
|
126
|
+
error: null,
|
|
127
|
+
data: null
|
|
128
|
+
});
|
|
129
|
+
try {
|
|
130
|
+
const result = await crm.loseDeal(input);
|
|
131
|
+
setLoseState({
|
|
132
|
+
loading: false,
|
|
133
|
+
error: null,
|
|
134
|
+
data: result
|
|
135
|
+
});
|
|
136
|
+
options.onSuccess?.();
|
|
137
|
+
return result;
|
|
138
|
+
} catch (err) {
|
|
139
|
+
const error = err instanceof Error ? err : /* @__PURE__ */ new Error("Failed to mark deal as lost");
|
|
140
|
+
setLoseState({
|
|
141
|
+
loading: false,
|
|
142
|
+
error,
|
|
143
|
+
data: null
|
|
144
|
+
});
|
|
145
|
+
options.onError?.(error);
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
}, [crm, options]),
|
|
149
|
+
createState,
|
|
150
|
+
moveState,
|
|
151
|
+
winState,
|
|
152
|
+
loseState,
|
|
153
|
+
isLoading: createState.loading || moveState.loading || winState.loading || loseState.loading
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
//#endregion
|
|
158
|
+
export { useDealMutations };
|
|
159
|
+
//# sourceMappingURL=useDealMutations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useDealMutations.js","names":[],"sources":["../../../src/ui/hooks/useDealMutations.ts"],"sourcesContent":["/**\n * Hook for CRM deal mutations (commands)\n *\n * Uses runtime-local database-backed handlers for:\n * - CreateDealContract\n * - MoveDealContract\n * - WinDealContract\n * - LoseDealContract\n */\nimport { useCallback, useState } from 'react';\nimport { useTemplateRuntime } from '@contractspec/lib.example-shared-ui';\nimport type {\n CreateDealInput,\n CrmHandlers,\n Deal,\n LoseDealInput,\n MoveDealInput,\n WinDealInput,\n} from '../../handlers/crm.handlers';\n\nexport interface MutationState<T> {\n loading: boolean;\n error: Error | null;\n data: T | null;\n}\n\nexport interface UseDealMutationsOptions {\n onSuccess?: () => void;\n onError?: (error: Error) => void;\n}\n\nexport function useDealMutations(options: UseDealMutationsOptions = {}) {\n const { handlers, projectId } = useTemplateRuntime<{ crm: CrmHandlers }>();\n const { crm } = handlers;\n\n const [createState, setCreateState] = useState<MutationState<Deal>>({\n loading: false,\n error: null,\n data: null,\n });\n\n const [moveState, setMoveState] = useState<MutationState<Deal>>({\n loading: false,\n error: null,\n data: null,\n });\n\n const [winState, setWinState] = useState<MutationState<Deal>>({\n loading: false,\n error: null,\n data: null,\n });\n\n const [loseState, setLoseState] = useState<MutationState<Deal>>({\n loading: false,\n error: null,\n data: null,\n });\n\n /**\n * Create a new deal\n */\n const createDeal = useCallback(\n async (input: CreateDealInput): Promise<Deal | null> => {\n setCreateState({ loading: true, error: null, data: null });\n try {\n const result = await crm.createDeal(input, {\n projectId,\n ownerId: 'user-1', // Demo user\n });\n setCreateState({ loading: false, error: null, data: result });\n options.onSuccess?.();\n return result;\n } catch (err) {\n const error =\n err instanceof Error ? err : new Error('Failed to create deal');\n setCreateState({ loading: false, error, data: null });\n options.onError?.(error);\n return null;\n }\n },\n [crm, projectId, options]\n );\n\n /**\n * Move a deal to a different stage\n */\n const moveDeal = useCallback(\n async (input: MoveDealInput): Promise<Deal | null> => {\n setMoveState({ loading: true, error: null, data: null });\n try {\n const result = await crm.moveDeal(input);\n setMoveState({ loading: false, error: null, data: result });\n options.onSuccess?.();\n return result;\n } catch (err) {\n const error =\n err instanceof Error ? err : new Error('Failed to move deal');\n setMoveState({ loading: false, error, data: null });\n options.onError?.(error);\n return null;\n }\n },\n [crm, options]\n );\n\n /**\n * Mark a deal as won\n */\n const winDeal = useCallback(\n async (input: WinDealInput): Promise<Deal | null> => {\n setWinState({ loading: true, error: null, data: null });\n try {\n const result = await crm.winDeal(input);\n setWinState({ loading: false, error: null, data: result });\n options.onSuccess?.();\n return result;\n } catch (err) {\n const error =\n err instanceof Error ? err : new Error('Failed to mark deal as won');\n setWinState({ loading: false, error, data: null });\n options.onError?.(error);\n return null;\n }\n },\n [crm, options]\n );\n\n /**\n * Mark a deal as lost\n */\n const loseDeal = useCallback(\n async (input: LoseDealInput): Promise<Deal | null> => {\n setLoseState({ loading: true, error: null, data: null });\n try {\n const result = await crm.loseDeal(input);\n setLoseState({ loading: false, error: null, data: result });\n options.onSuccess?.();\n return result;\n } catch (err) {\n const error =\n err instanceof Error ? err : new Error('Failed to mark deal as lost');\n setLoseState({ loading: false, error, data: null });\n options.onError?.(error);\n return null;\n }\n },\n [crm, options]\n );\n\n return {\n // Mutations\n createDeal,\n moveDeal,\n winDeal,\n loseDeal,\n\n // State\n createState,\n moveState,\n winState,\n loseState,\n\n // Convenience\n isLoading:\n createState.loading ||\n moveState.loading ||\n winState.loading ||\n loseState.loading,\n };\n}\n\n// Note: Types are re-exported from the handlers package\n// Consumers should import types directly from '@contractspec/example.crm-pipeline/handlers'\n"],"mappings":";;;;;;;;;;;;;AA+BA,SAAgB,iBAAiB,UAAmC,EAAE,EAAE;CACtE,MAAM,EAAE,UAAU,cAAc,oBAA0C;CAC1E,MAAM,EAAE,QAAQ;CAEhB,MAAM,CAAC,aAAa,kBAAkB,SAA8B;EAClE,SAAS;EACT,OAAO;EACP,MAAM;EACP,CAAC;CAEF,MAAM,CAAC,WAAW,gBAAgB,SAA8B;EAC9D,SAAS;EACT,OAAO;EACP,MAAM;EACP,CAAC;CAEF,MAAM,CAAC,UAAU,eAAe,SAA8B;EAC5D,SAAS;EACT,OAAO;EACP,MAAM;EACP,CAAC;CAEF,MAAM,CAAC,WAAW,gBAAgB,SAA8B;EAC9D,SAAS;EACT,OAAO;EACP,MAAM;EACP,CAAC;AA6FF,QAAO;EAEL,YA1FiB,YACjB,OAAO,UAAiD;AACtD,kBAAe;IAAE,SAAS;IAAM,OAAO;IAAM,MAAM;IAAM,CAAC;AAC1D,OAAI;IACF,MAAM,SAAS,MAAM,IAAI,WAAW,OAAO;KACzC;KACA,SAAS;KACV,CAAC;AACF,mBAAe;KAAE,SAAS;KAAO,OAAO;KAAM,MAAM;KAAQ,CAAC;AAC7D,YAAQ,aAAa;AACrB,WAAO;YACA,KAAK;IACZ,MAAM,QACJ,eAAe,QAAQ,sBAAM,IAAI,MAAM,wBAAwB;AACjE,mBAAe;KAAE,SAAS;KAAO;KAAO,MAAM;KAAM,CAAC;AACrD,YAAQ,UAAU,MAAM;AACxB,WAAO;;KAGX;GAAC;GAAK;GAAW;GAAQ,CAC1B;EAuEC,UAlEe,YACf,OAAO,UAA+C;AACpD,gBAAa;IAAE,SAAS;IAAM,OAAO;IAAM,MAAM;IAAM,CAAC;AACxD,OAAI;IACF,MAAM,SAAS,MAAM,IAAI,SAAS,MAAM;AACxC,iBAAa;KAAE,SAAS;KAAO,OAAO;KAAM,MAAM;KAAQ,CAAC;AAC3D,YAAQ,aAAa;AACrB,WAAO;YACA,KAAK;IACZ,MAAM,QACJ,eAAe,QAAQ,sBAAM,IAAI,MAAM,sBAAsB;AAC/D,iBAAa;KAAE,SAAS;KAAO;KAAO,MAAM;KAAM,CAAC;AACnD,YAAQ,UAAU,MAAM;AACxB,WAAO;;KAGX,CAAC,KAAK,QAAQ,CACf;EAkDC,SA7Cc,YACd,OAAO,UAA8C;AACnD,eAAY;IAAE,SAAS;IAAM,OAAO;IAAM,MAAM;IAAM,CAAC;AACvD,OAAI;IACF,MAAM,SAAS,MAAM,IAAI,QAAQ,MAAM;AACvC,gBAAY;KAAE,SAAS;KAAO,OAAO;KAAM,MAAM;KAAQ,CAAC;AAC1D,YAAQ,aAAa;AACrB,WAAO;YACA,KAAK;IACZ,MAAM,QACJ,eAAe,QAAQ,sBAAM,IAAI,MAAM,6BAA6B;AACtE,gBAAY;KAAE,SAAS;KAAO;KAAO,MAAM;KAAM,CAAC;AAClD,YAAQ,UAAU,MAAM;AACxB,WAAO;;KAGX,CAAC,KAAK,QAAQ,CACf;EA6BC,UAxBe,YACf,OAAO,UAA+C;AACpD,gBAAa;IAAE,SAAS;IAAM,OAAO;IAAM,MAAM;IAAM,CAAC;AACxD,OAAI;IACF,MAAM,SAAS,MAAM,IAAI,SAAS,MAAM;AACxC,iBAAa;KAAE,SAAS;KAAO,OAAO;KAAM,MAAM;KAAQ,CAAC;AAC3D,YAAQ,aAAa;AACrB,WAAO;YACA,KAAK;IACZ,MAAM,QACJ,eAAe,QAAQ,sBAAM,IAAI,MAAM,8BAA8B;AACvE,iBAAa;KAAE,SAAS;KAAO;KAAO,MAAM;KAAM,CAAC;AACnD,YAAQ,UAAU,MAAM;AACxB,WAAO;;KAGX,CAAC,KAAK,QAAQ,CACf;EAUC;EACA;EACA;EACA;EAGA,WACE,YAAY,WACZ,UAAU,WACV,SAAS,WACT,UAAU;EACb"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { CrmDashboard } from "./CrmDashboard.js";
|
|
2
|
+
import { UseDealListOptions, useDealList } from "./hooks/useDealList.js";
|
|
3
|
+
import { CrmPipelineBoard } from "./CrmPipelineBoard.js";
|
|
4
|
+
import { CrmDealCard } from "./CrmDealCard.js";
|
|
5
|
+
import { CreateDealModal } from "./modals/CreateDealModal.js";
|
|
6
|
+
import { DealActionsModal } from "./modals/DealActionsModal.js";
|
|
7
|
+
import "./modals/index.js";
|
|
8
|
+
import { UseDealMutationsOptions, useDealMutations } from "./hooks/useDealMutations.js";
|
|
9
|
+
import "./hooks/index.js";
|
|
10
|
+
import { crmPipelineReactRenderer } from "./renderers/pipeline.renderer.js";
|
|
11
|
+
import { crmDashboardMarkdownRenderer, crmPipelineMarkdownRenderer } from "./renderers/pipeline.markdown.js";
|
|
12
|
+
import "./renderers/index.js";
|
|
13
|
+
import { crmDemoOverlay, crmOverlays, crmSalesRepOverlay } from "./overlays/demo-overlays.js";
|
|
14
|
+
export { CreateDealModal, CrmDashboard, CrmDealCard, CrmPipelineBoard, DealActionsModal, UseDealListOptions, UseDealMutationsOptions, crmDashboardMarkdownRenderer, crmDemoOverlay, crmOverlays, crmPipelineMarkdownRenderer, crmPipelineReactRenderer, crmSalesRepOverlay, useDealList, useDealMutations };
|
package/dist/ui/index.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { useDealList } from "./hooks/useDealList.js";
|
|
2
|
+
import { useDealMutations } from "./hooks/useDealMutations.js";
|
|
3
|
+
import { CrmDealCard } from "./CrmDealCard.js";
|
|
4
|
+
import { CrmPipelineBoard } from "./CrmPipelineBoard.js";
|
|
5
|
+
import { CreateDealModal } from "./modals/CreateDealModal.js";
|
|
6
|
+
import { DealActionsModal } from "./modals/DealActionsModal.js";
|
|
7
|
+
import { CrmDashboard } from "./CrmDashboard.js";
|
|
8
|
+
import "./modals/index.js";
|
|
9
|
+
import "./hooks/index.js";
|
|
10
|
+
import { crmPipelineReactRenderer } from "./renderers/pipeline.renderer.js";
|
|
11
|
+
import { crmDashboardMarkdownRenderer, crmPipelineMarkdownRenderer } from "./renderers/pipeline.markdown.js";
|
|
12
|
+
import "./renderers/index.js";
|
|
13
|
+
import { crmDemoOverlay, crmOverlays, crmSalesRepOverlay } from "./overlays/demo-overlays.js";
|
|
14
|
+
|
|
15
|
+
export { CreateDealModal, CrmDashboard, CrmDealCard, CrmPipelineBoard, DealActionsModal, crmDashboardMarkdownRenderer, crmDemoOverlay, crmOverlays, crmPipelineMarkdownRenderer, crmPipelineReactRenderer, crmSalesRepOverlay, useDealList, useDealMutations };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import * as react_jsx_runtime2 from "react/jsx-runtime";
|
|
2
|
+
|
|
3
|
+
//#region src/ui/modals/CreateDealModal.d.ts
|
|
4
|
+
interface CreateDealInput {
|
|
5
|
+
name: string;
|
|
6
|
+
value: number;
|
|
7
|
+
currency: string;
|
|
8
|
+
pipelineId: string;
|
|
9
|
+
stageId: string;
|
|
10
|
+
expectedCloseDate?: Date;
|
|
11
|
+
contactId?: string;
|
|
12
|
+
companyId?: string;
|
|
13
|
+
}
|
|
14
|
+
interface CreateDealModalProps {
|
|
15
|
+
isOpen: boolean;
|
|
16
|
+
onClose: () => void;
|
|
17
|
+
onSubmit: (input: CreateDealInput) => Promise<void>;
|
|
18
|
+
stages: {
|
|
19
|
+
id: string;
|
|
20
|
+
name: string;
|
|
21
|
+
}[];
|
|
22
|
+
isLoading?: boolean;
|
|
23
|
+
}
|
|
24
|
+
declare function CreateDealModal({
|
|
25
|
+
isOpen,
|
|
26
|
+
onClose,
|
|
27
|
+
onSubmit,
|
|
28
|
+
stages,
|
|
29
|
+
isLoading
|
|
30
|
+
}: CreateDealModalProps): react_jsx_runtime2.JSX.Element | null;
|
|
31
|
+
//#endregion
|
|
32
|
+
export { CreateDealInput, CreateDealModal };
|
|
33
|
+
//# sourceMappingURL=CreateDealModal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CreateDealModal.d.ts","names":[],"sources":["../../../src/ui/modals/CreateDealModal.tsx"],"sourcesContent":[],"mappings":";;;UAWiB,eAAA;;;EAAA,QAAA,EAAA,MAAA;EAWP,UAAA,EAAA,MAAA;EAWM,OAAA,EAAA,MAAA;EACd,iBAAA,CAAA,EAjBoB,IAiBpB;EACA,SAAA,CAAA,EAAA,MAAA;EACA,SAAA,CAAA,EAAA,MAAA;;UAdQ,oBAAA,CAgBR;EACC,MAAA,EAAA,OAAA;EAAoB,OAAA,EAAA,GAAA,GAAA,IAAA;EAAA,QAAA,EAAA,CAAA,KAAA,EAdH,eAcG,EAAA,GAdiB,OAcjB,CAAA,IAAA,CAAA;;;;;;;iBANP,eAAA;;;;;;GAMb,uBAAoB,kBAAA,CAAA,GAAA,CAAA,OAAA"}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { Button, Input } from "@contractspec/lib.design-system";
|
|
5
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
|
|
7
|
+
//#region src/ui/modals/CreateDealModal.tsx
|
|
8
|
+
/**
|
|
9
|
+
* CreateDealModal - Form for creating a new deal
|
|
10
|
+
*
|
|
11
|
+
* Wires to CreateDealContract via useDealMutations hook.
|
|
12
|
+
*/
|
|
13
|
+
const CURRENCIES = [
|
|
14
|
+
"USD",
|
|
15
|
+
"EUR",
|
|
16
|
+
"GBP",
|
|
17
|
+
"CAD"
|
|
18
|
+
];
|
|
19
|
+
const DEFAULT_PIPELINE_ID = "pipeline-1";
|
|
20
|
+
function CreateDealModal({ isOpen, onClose, onSubmit, stages, isLoading = false }) {
|
|
21
|
+
const [name, setName] = useState("");
|
|
22
|
+
const [value, setValue] = useState("");
|
|
23
|
+
const [currency, setCurrency] = useState("USD");
|
|
24
|
+
const [stageId, setStageId] = useState(stages[0]?.id ?? "");
|
|
25
|
+
const [expectedCloseDate, setExpectedCloseDate] = useState("");
|
|
26
|
+
const [error, setError] = useState(null);
|
|
27
|
+
const handleSubmit = async (e) => {
|
|
28
|
+
e.preventDefault();
|
|
29
|
+
setError(null);
|
|
30
|
+
if (!name.trim()) {
|
|
31
|
+
setError("Deal name is required");
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const numericValue = parseFloat(value);
|
|
35
|
+
if (isNaN(numericValue) || numericValue <= 0) {
|
|
36
|
+
setError("Value must be a positive number");
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
if (!stageId) {
|
|
40
|
+
setError("Please select a pipeline stage");
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
await onSubmit({
|
|
45
|
+
name: name.trim(),
|
|
46
|
+
value: numericValue,
|
|
47
|
+
currency,
|
|
48
|
+
pipelineId: DEFAULT_PIPELINE_ID,
|
|
49
|
+
stageId,
|
|
50
|
+
expectedCloseDate: expectedCloseDate ? new Date(expectedCloseDate) : void 0
|
|
51
|
+
});
|
|
52
|
+
setName("");
|
|
53
|
+
setValue("");
|
|
54
|
+
setCurrency("USD");
|
|
55
|
+
setStageId(stages[0]?.id ?? "");
|
|
56
|
+
setExpectedCloseDate("");
|
|
57
|
+
onClose();
|
|
58
|
+
} catch (err) {
|
|
59
|
+
setError(err instanceof Error ? err.message : "Failed to create deal");
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
if (!isOpen) return null;
|
|
63
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
64
|
+
className: "fixed inset-0 z-50 flex items-center justify-center",
|
|
65
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
66
|
+
className: "bg-background/80 absolute inset-0 backdrop-blur-sm",
|
|
67
|
+
onClick: onClose,
|
|
68
|
+
role: "button",
|
|
69
|
+
tabIndex: 0,
|
|
70
|
+
onKeyDown: (e) => {
|
|
71
|
+
if (e.key === "Enter" || e.key === " ") onClose();
|
|
72
|
+
},
|
|
73
|
+
"aria-label": "Close modal"
|
|
74
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
75
|
+
className: "bg-card border-border relative z-10 w-full max-w-md rounded-xl border p-6 shadow-xl",
|
|
76
|
+
children: [/* @__PURE__ */ jsx("h2", {
|
|
77
|
+
className: "mb-4 text-xl font-semibold",
|
|
78
|
+
children: "Create New Deal"
|
|
79
|
+
}), /* @__PURE__ */ jsxs("form", {
|
|
80
|
+
onSubmit: handleSubmit,
|
|
81
|
+
className: "space-y-4",
|
|
82
|
+
children: [
|
|
83
|
+
/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("label", {
|
|
84
|
+
htmlFor: "deal-name",
|
|
85
|
+
className: "text-muted-foreground mb-1 block text-sm font-medium",
|
|
86
|
+
children: "Deal Name *"
|
|
87
|
+
}), /* @__PURE__ */ jsx(Input, {
|
|
88
|
+
id: "deal-name",
|
|
89
|
+
value: name,
|
|
90
|
+
onChange: (e) => setName(e.target.value),
|
|
91
|
+
placeholder: "e.g., Enterprise License - Acme Corp",
|
|
92
|
+
disabled: isLoading
|
|
93
|
+
})] }),
|
|
94
|
+
/* @__PURE__ */ jsxs("div", {
|
|
95
|
+
className: "flex gap-3",
|
|
96
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
97
|
+
className: "flex-1",
|
|
98
|
+
children: [/* @__PURE__ */ jsx("label", {
|
|
99
|
+
htmlFor: "deal-value",
|
|
100
|
+
className: "text-muted-foreground mb-1 block text-sm font-medium",
|
|
101
|
+
children: "Value *"
|
|
102
|
+
}), /* @__PURE__ */ jsx(Input, {
|
|
103
|
+
id: "deal-value",
|
|
104
|
+
type: "number",
|
|
105
|
+
min: "0",
|
|
106
|
+
step: "0.01",
|
|
107
|
+
value,
|
|
108
|
+
onChange: (e) => setValue(e.target.value),
|
|
109
|
+
placeholder: "50000",
|
|
110
|
+
disabled: isLoading
|
|
111
|
+
})]
|
|
112
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
113
|
+
className: "w-24",
|
|
114
|
+
children: [/* @__PURE__ */ jsx("label", {
|
|
115
|
+
htmlFor: "deal-currency",
|
|
116
|
+
className: "text-muted-foreground mb-1 block text-sm font-medium",
|
|
117
|
+
children: "Currency"
|
|
118
|
+
}), /* @__PURE__ */ jsx("select", {
|
|
119
|
+
id: "deal-currency",
|
|
120
|
+
value: currency,
|
|
121
|
+
onChange: (e) => setCurrency(e.target.value),
|
|
122
|
+
disabled: isLoading,
|
|
123
|
+
className: "border-input bg-background focus:ring-ring h-10 w-full rounded-md border px-3 py-2 text-sm focus:ring-2 focus:outline-none disabled:opacity-50",
|
|
124
|
+
children: CURRENCIES.map((c) => /* @__PURE__ */ jsx("option", {
|
|
125
|
+
value: c,
|
|
126
|
+
children: c
|
|
127
|
+
}, c))
|
|
128
|
+
})]
|
|
129
|
+
})]
|
|
130
|
+
}),
|
|
131
|
+
/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("label", {
|
|
132
|
+
htmlFor: "deal-stage",
|
|
133
|
+
className: "text-muted-foreground mb-1 block text-sm font-medium",
|
|
134
|
+
children: "Pipeline Stage *"
|
|
135
|
+
}), /* @__PURE__ */ jsx("select", {
|
|
136
|
+
id: "deal-stage",
|
|
137
|
+
value: stageId,
|
|
138
|
+
onChange: (e) => setStageId(e.target.value),
|
|
139
|
+
disabled: isLoading,
|
|
140
|
+
className: "border-input bg-background focus:ring-ring h-10 w-full rounded-md border px-3 py-2 text-sm focus:ring-2 focus:outline-none disabled:opacity-50",
|
|
141
|
+
children: stages.map((stage) => /* @__PURE__ */ jsx("option", {
|
|
142
|
+
value: stage.id,
|
|
143
|
+
children: stage.name
|
|
144
|
+
}, stage.id))
|
|
145
|
+
})] }),
|
|
146
|
+
/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("label", {
|
|
147
|
+
htmlFor: "deal-close-date",
|
|
148
|
+
className: "text-muted-foreground mb-1 block text-sm font-medium",
|
|
149
|
+
children: "Expected Close Date"
|
|
150
|
+
}), /* @__PURE__ */ jsx(Input, {
|
|
151
|
+
id: "deal-close-date",
|
|
152
|
+
type: "date",
|
|
153
|
+
value: expectedCloseDate,
|
|
154
|
+
onChange: (e) => setExpectedCloseDate(e.target.value),
|
|
155
|
+
disabled: isLoading
|
|
156
|
+
})] }),
|
|
157
|
+
error && /* @__PURE__ */ jsx("div", {
|
|
158
|
+
className: "bg-destructive/10 text-destructive rounded-md p-3 text-sm",
|
|
159
|
+
children: error
|
|
160
|
+
}),
|
|
161
|
+
/* @__PURE__ */ jsxs("div", {
|
|
162
|
+
className: "flex justify-end gap-3 pt-2",
|
|
163
|
+
children: [/* @__PURE__ */ jsx(Button, {
|
|
164
|
+
type: "button",
|
|
165
|
+
variant: "ghost",
|
|
166
|
+
onPress: onClose,
|
|
167
|
+
disabled: isLoading,
|
|
168
|
+
children: "Cancel"
|
|
169
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
170
|
+
type: "submit",
|
|
171
|
+
disabled: isLoading,
|
|
172
|
+
children: isLoading ? "Creating..." : "Create Deal"
|
|
173
|
+
})]
|
|
174
|
+
})
|
|
175
|
+
]
|
|
176
|
+
})]
|
|
177
|
+
})]
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
//#endregion
|
|
182
|
+
export { CreateDealModal };
|
|
183
|
+
//# sourceMappingURL=CreateDealModal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CreateDealModal.js","names":[],"sources":["../../../src/ui/modals/CreateDealModal.tsx"],"sourcesContent":["'use client';\n\n/**\n * CreateDealModal - Form for creating a new deal\n *\n * Wires to CreateDealContract via useDealMutations hook.\n */\nimport { useState } from 'react';\nimport { Button, Input } from '@contractspec/lib.design-system';\n\n// Local type definition for modal props\nexport interface CreateDealInput {\n name: string;\n value: number;\n currency: string;\n pipelineId: string;\n stageId: string;\n expectedCloseDate?: Date;\n contactId?: string;\n companyId?: string;\n}\n\ninterface CreateDealModalProps {\n isOpen: boolean;\n onClose: () => void;\n onSubmit: (input: CreateDealInput) => Promise<void>;\n stages: { id: string; name: string }[];\n isLoading?: boolean;\n}\n\nconst CURRENCIES = ['USD', 'EUR', 'GBP', 'CAD'];\nconst DEFAULT_PIPELINE_ID = 'pipeline-1';\n\nexport function CreateDealModal({\n isOpen,\n onClose,\n onSubmit,\n stages,\n isLoading = false,\n}: CreateDealModalProps) {\n const [name, setName] = useState('');\n const [value, setValue] = useState('');\n const [currency, setCurrency] = useState('USD');\n const [stageId, setStageId] = useState(stages[0]?.id ?? '');\n const [expectedCloseDate, setExpectedCloseDate] = useState('');\n const [error, setError] = useState<string | null>(null);\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n setError(null);\n\n // Validation\n if (!name.trim()) {\n setError('Deal name is required');\n return;\n }\n\n const numericValue = parseFloat(value);\n if (isNaN(numericValue) || numericValue <= 0) {\n setError('Value must be a positive number');\n return;\n }\n\n if (!stageId) {\n setError('Please select a pipeline stage');\n return;\n }\n\n try {\n await onSubmit({\n name: name.trim(),\n value: numericValue,\n currency,\n pipelineId: DEFAULT_PIPELINE_ID,\n stageId,\n expectedCloseDate: expectedCloseDate\n ? new Date(expectedCloseDate)\n : undefined,\n });\n\n // Reset form\n setName('');\n setValue('');\n setCurrency('USD');\n setStageId(stages[0]?.id ?? '');\n setExpectedCloseDate('');\n onClose();\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to create deal');\n }\n };\n\n if (!isOpen) return null;\n\n return (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center\">\n {/* Backdrop */}\n <div\n className=\"bg-background/80 absolute inset-0 backdrop-blur-sm\"\n onClick={onClose}\n role=\"button\"\n tabIndex={0}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') onClose();\n }}\n aria-label=\"Close modal\"\n />\n\n {/* Modal */}\n <div className=\"bg-card border-border relative z-10 w-full max-w-md rounded-xl border p-6 shadow-xl\">\n <h2 className=\"mb-4 text-xl font-semibold\">Create New Deal</h2>\n\n <form onSubmit={handleSubmit} className=\"space-y-4\">\n {/* Deal Name */}\n <div>\n <label\n htmlFor=\"deal-name\"\n className=\"text-muted-foreground mb-1 block text-sm font-medium\"\n >\n Deal Name *\n </label>\n <Input\n id=\"deal-name\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n placeholder=\"e.g., Enterprise License - Acme Corp\"\n disabled={isLoading}\n />\n </div>\n\n {/* Value & Currency */}\n <div className=\"flex gap-3\">\n <div className=\"flex-1\">\n <label\n htmlFor=\"deal-value\"\n className=\"text-muted-foreground mb-1 block text-sm font-medium\"\n >\n Value *\n </label>\n <Input\n id=\"deal-value\"\n type=\"number\"\n min=\"0\"\n step=\"0.01\"\n value={value}\n onChange={(e) => setValue(e.target.value)}\n placeholder=\"50000\"\n disabled={isLoading}\n />\n </div>\n <div className=\"w-24\">\n <label\n htmlFor=\"deal-currency\"\n className=\"text-muted-foreground mb-1 block text-sm font-medium\"\n >\n Currency\n </label>\n <select\n id=\"deal-currency\"\n value={currency}\n onChange={(e) => setCurrency(e.target.value)}\n disabled={isLoading}\n className=\"border-input bg-background focus:ring-ring h-10 w-full rounded-md border px-3 py-2 text-sm focus:ring-2 focus:outline-none disabled:opacity-50\"\n >\n {CURRENCIES.map((c) => (\n <option key={c} value={c}>\n {c}\n </option>\n ))}\n </select>\n </div>\n </div>\n\n {/* Stage */}\n <div>\n <label\n htmlFor=\"deal-stage\"\n className=\"text-muted-foreground mb-1 block text-sm font-medium\"\n >\n Pipeline Stage *\n </label>\n <select\n id=\"deal-stage\"\n value={stageId}\n onChange={(e) => setStageId(e.target.value)}\n disabled={isLoading}\n className=\"border-input bg-background focus:ring-ring h-10 w-full rounded-md border px-3 py-2 text-sm focus:ring-2 focus:outline-none disabled:opacity-50\"\n >\n {stages.map((stage) => (\n <option key={stage.id} value={stage.id}>\n {stage.name}\n </option>\n ))}\n </select>\n </div>\n\n {/* Expected Close Date */}\n <div>\n <label\n htmlFor=\"deal-close-date\"\n className=\"text-muted-foreground mb-1 block text-sm font-medium\"\n >\n Expected Close Date\n </label>\n <Input\n id=\"deal-close-date\"\n type=\"date\"\n value={expectedCloseDate}\n onChange={(e) => setExpectedCloseDate(e.target.value)}\n disabled={isLoading}\n />\n </div>\n\n {/* Error Message */}\n {error && (\n <div className=\"bg-destructive/10 text-destructive rounded-md p-3 text-sm\">\n {error}\n </div>\n )}\n\n {/* Actions */}\n <div className=\"flex justify-end gap-3 pt-2\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n onPress={onClose}\n disabled={isLoading}\n >\n Cancel\n </Button>\n <Button type=\"submit\" disabled={isLoading}>\n {isLoading ? 'Creating...' : 'Create Deal'}\n </Button>\n </div>\n </form>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|