@contractspec/example.crm-pipeline 1.46.1 → 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 +106 -31
- package/.turbo/turbo-build.log +109 -34
- package/CHANGELOG.md +44 -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.operation.d.ts +6 -6
- 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/contact.entity.d.ts +32 -32
- package/dist/entities/index.js.map +1 -1
- package/dist/entities/task.entity.d.ts +43 -43
- package/dist/events/contact.event.d.ts +8 -8
- package/dist/events/contact.event.js +1 -1
- package/dist/events/deal.event.d.ts +5 -5
- package/dist/events/deal.event.js +1 -1
- package/dist/events/task.event.d.ts +2 -2
- 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 +14 -1
- 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,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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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import * as react_jsx_runtime3 from "react/jsx-runtime";
|
|
2
|
+
|
|
3
|
+
//#region src/ui/modals/DealActionsModal.d.ts
|
|
4
|
+
interface Deal {
|
|
5
|
+
id: string;
|
|
6
|
+
name: string;
|
|
7
|
+
value: number;
|
|
8
|
+
currency: string;
|
|
9
|
+
stageId: string;
|
|
10
|
+
status: 'OPEN' | 'WON' | 'LOST' | 'STALE';
|
|
11
|
+
}
|
|
12
|
+
interface WinDealInput {
|
|
13
|
+
dealId: string;
|
|
14
|
+
wonSource?: string;
|
|
15
|
+
notes?: string;
|
|
16
|
+
}
|
|
17
|
+
interface LoseDealInput {
|
|
18
|
+
dealId: string;
|
|
19
|
+
lostReason: string;
|
|
20
|
+
notes?: string;
|
|
21
|
+
}
|
|
22
|
+
interface MoveDealInput {
|
|
23
|
+
dealId: string;
|
|
24
|
+
stageId: string;
|
|
25
|
+
}
|
|
26
|
+
interface DealActionsModalProps {
|
|
27
|
+
isOpen: boolean;
|
|
28
|
+
deal: Deal | null;
|
|
29
|
+
stages: {
|
|
30
|
+
id: string;
|
|
31
|
+
name: string;
|
|
32
|
+
}[];
|
|
33
|
+
onClose: () => void;
|
|
34
|
+
onWin: (input: WinDealInput) => Promise<void>;
|
|
35
|
+
onLose: (input: LoseDealInput) => Promise<void>;
|
|
36
|
+
onMove: (input: MoveDealInput) => Promise<void>;
|
|
37
|
+
isLoading?: boolean;
|
|
38
|
+
}
|
|
39
|
+
declare function DealActionsModal({
|
|
40
|
+
isOpen,
|
|
41
|
+
deal,
|
|
42
|
+
stages,
|
|
43
|
+
onClose,
|
|
44
|
+
onWin,
|
|
45
|
+
onLose,
|
|
46
|
+
onMove,
|
|
47
|
+
isLoading
|
|
48
|
+
}: DealActionsModalProps): react_jsx_runtime3.JSX.Element | null;
|
|
49
|
+
//#endregion
|
|
50
|
+
export { Deal, DealActionsModal, LoseDealInput, MoveDealInput, WinDealInput };
|
|
51
|
+
//# sourceMappingURL=DealActionsModal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DealActionsModal.d.ts","names":[],"sources":["../../../src/ui/modals/DealActionsModal.tsx"],"sourcesContent":[],"mappings":";;;UAYiB,IAAA;;;EAAA,KAAA,EAAI,MAAA;EASJ,QAAA,EAAA,MAAY;EAMZ,OAAA,EAAA,MAAA;EAMA,MAAA,EAAA,MAAA,GAAa,KAAA,GAAA,MAAA,GAAA,OAAA;AAG7B;AAMO,UArBS,YAAA,CAqBT;EAGS,MAAA,EAAA,MAAA;EAAiB,SAAA,CAAA,EAAA,MAAA;EAChB,KAAA,CAAA,EAAA,MAAA;;AACA,UApBD,aAAA,CAoBC;EAAkB,MAAA,EAAA,MAAA;EAAO,UAAA,EAAA,MAAA;EAa3B,KAAA,CAAA,EAAA,MAAA;;AAEd,UA7Be,aAAA,CA6Bf;EACA,MAAA,EAAA,MAAA;EACA,OAAA,EAAA,MAAA;;UAxBQ,qBAAA,CA0BR;EACA,MAAA,EAAA,OAAA;EACA,IAAA,EA1BM,IA0BN,GAAA,IAAA;EACC,MAAA,EAAA;IAAqB,EAAA,EAAA,MAAA;IAAA,IAAA,EAAA,MAAA;;;iBAxBP,iBAAiB;kBAChB,kBAAkB;kBAClB,kBAAkB;;;iBAapB,gBAAA;;;;;;;;;GASb,wBAAqB,kBAAA,CAAA,GAAA,CAAA,OAAA"}
|
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { Button } from "@contractspec/lib.design-system";
|
|
5
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
|
|
7
|
+
//#region src/ui/modals/DealActionsModal.tsx
|
|
8
|
+
/**
|
|
9
|
+
* DealActionsModal - Actions for a specific deal (Win, Lose, Move)
|
|
10
|
+
*
|
|
11
|
+
* Wires to WinDealContract, LoseDealContract, MoveDealContract
|
|
12
|
+
* via useDealMutations hook.
|
|
13
|
+
*/
|
|
14
|
+
function formatCurrency(value, currency) {
|
|
15
|
+
return new Intl.NumberFormat("en-US", {
|
|
16
|
+
style: "currency",
|
|
17
|
+
currency,
|
|
18
|
+
minimumFractionDigits: 0,
|
|
19
|
+
maximumFractionDigits: 0
|
|
20
|
+
}).format(value);
|
|
21
|
+
}
|
|
22
|
+
function DealActionsModal({ isOpen, deal, stages, onClose, onWin, onLose, onMove, isLoading = false }) {
|
|
23
|
+
const [mode, setMode] = useState("menu");
|
|
24
|
+
const [wonSource, setWonSource] = useState("");
|
|
25
|
+
const [lostReason, setLostReason] = useState("");
|
|
26
|
+
const [notes, setNotes] = useState("");
|
|
27
|
+
const [selectedStageId, setSelectedStageId] = useState("");
|
|
28
|
+
const [error, setError] = useState(null);
|
|
29
|
+
const resetForm = () => {
|
|
30
|
+
setMode("menu");
|
|
31
|
+
setWonSource("");
|
|
32
|
+
setLostReason("");
|
|
33
|
+
setNotes("");
|
|
34
|
+
setSelectedStageId("");
|
|
35
|
+
setError(null);
|
|
36
|
+
};
|
|
37
|
+
const handleClose = () => {
|
|
38
|
+
resetForm();
|
|
39
|
+
onClose();
|
|
40
|
+
};
|
|
41
|
+
const handleWin = async () => {
|
|
42
|
+
if (!deal) return;
|
|
43
|
+
setError(null);
|
|
44
|
+
try {
|
|
45
|
+
await onWin({
|
|
46
|
+
dealId: deal.id,
|
|
47
|
+
wonSource: wonSource.trim() || void 0,
|
|
48
|
+
notes: notes.trim() || void 0
|
|
49
|
+
});
|
|
50
|
+
handleClose();
|
|
51
|
+
} catch (err) {
|
|
52
|
+
setError(err instanceof Error ? err.message : "Failed to mark deal as won");
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
const handleLose = async () => {
|
|
56
|
+
if (!deal) return;
|
|
57
|
+
setError(null);
|
|
58
|
+
if (!lostReason.trim()) {
|
|
59
|
+
setError("Please provide a reason for losing the deal");
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
try {
|
|
63
|
+
await onLose({
|
|
64
|
+
dealId: deal.id,
|
|
65
|
+
lostReason: lostReason.trim(),
|
|
66
|
+
notes: notes.trim() || void 0
|
|
67
|
+
});
|
|
68
|
+
handleClose();
|
|
69
|
+
} catch (err) {
|
|
70
|
+
setError(err instanceof Error ? err.message : "Failed to mark deal as lost");
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
const handleMove = async () => {
|
|
74
|
+
if (!deal) return;
|
|
75
|
+
setError(null);
|
|
76
|
+
if (!selectedStageId) {
|
|
77
|
+
setError("Please select a stage");
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
if (selectedStageId === deal.stageId) {
|
|
81
|
+
setError("Deal is already in this stage");
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
try {
|
|
85
|
+
await onMove({
|
|
86
|
+
dealId: deal.id,
|
|
87
|
+
stageId: selectedStageId
|
|
88
|
+
});
|
|
89
|
+
handleClose();
|
|
90
|
+
} catch (err) {
|
|
91
|
+
setError(err instanceof Error ? err.message : "Failed to move deal");
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
if (!isOpen || !deal) return null;
|
|
95
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
96
|
+
className: "fixed inset-0 z-50 flex items-center justify-center",
|
|
97
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
98
|
+
className: "bg-background/80 absolute inset-0 backdrop-blur-sm",
|
|
99
|
+
onClick: handleClose,
|
|
100
|
+
role: "button",
|
|
101
|
+
tabIndex: 0,
|
|
102
|
+
onKeyDown: (e) => {
|
|
103
|
+
if (e.key === "Enter" || e.key === " ") handleClose();
|
|
104
|
+
},
|
|
105
|
+
"aria-label": "Close modal"
|
|
106
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
107
|
+
className: "bg-card border-border relative z-10 w-full max-w-md rounded-xl border p-6 shadow-xl",
|
|
108
|
+
children: [
|
|
109
|
+
/* @__PURE__ */ jsxs("div", {
|
|
110
|
+
className: "border-border mb-4 border-b pb-4",
|
|
111
|
+
children: [
|
|
112
|
+
/* @__PURE__ */ jsx("h2", {
|
|
113
|
+
className: "text-xl font-semibold",
|
|
114
|
+
children: deal.name
|
|
115
|
+
}),
|
|
116
|
+
/* @__PURE__ */ jsx("p", {
|
|
117
|
+
className: "text-primary text-lg font-medium",
|
|
118
|
+
children: formatCurrency(deal.value, deal.currency)
|
|
119
|
+
}),
|
|
120
|
+
/* @__PURE__ */ jsx("span", {
|
|
121
|
+
className: `mt-2 inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${deal.status === "WON" ? "bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400" : deal.status === "LOST" ? "bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400" : "bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400"}`,
|
|
122
|
+
children: deal.status
|
|
123
|
+
})
|
|
124
|
+
]
|
|
125
|
+
}),
|
|
126
|
+
mode === "menu" && /* @__PURE__ */ jsxs("div", {
|
|
127
|
+
className: "space-y-3",
|
|
128
|
+
children: [
|
|
129
|
+
deal.status === "OPEN" && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
130
|
+
/* @__PURE__ */ jsxs(Button, {
|
|
131
|
+
className: "w-full justify-start",
|
|
132
|
+
variant: "ghost",
|
|
133
|
+
onPress: () => setMode("win"),
|
|
134
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
135
|
+
className: "mr-2",
|
|
136
|
+
children: "🏆"
|
|
137
|
+
}), " Mark as Won"]
|
|
138
|
+
}),
|
|
139
|
+
/* @__PURE__ */ jsxs(Button, {
|
|
140
|
+
className: "w-full justify-start",
|
|
141
|
+
variant: "ghost",
|
|
142
|
+
onPress: () => setMode("lose"),
|
|
143
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
144
|
+
className: "mr-2",
|
|
145
|
+
children: "❌"
|
|
146
|
+
}), " Mark as Lost"]
|
|
147
|
+
}),
|
|
148
|
+
/* @__PURE__ */ jsxs(Button, {
|
|
149
|
+
className: "w-full justify-start",
|
|
150
|
+
variant: "ghost",
|
|
151
|
+
onPress: () => {
|
|
152
|
+
setSelectedStageId(deal.stageId);
|
|
153
|
+
setMode("move");
|
|
154
|
+
},
|
|
155
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
156
|
+
className: "mr-2",
|
|
157
|
+
children: "➡️"
|
|
158
|
+
}), " Move to Stage"]
|
|
159
|
+
})
|
|
160
|
+
] }),
|
|
161
|
+
deal.status !== "OPEN" && /* @__PURE__ */ jsxs("p", {
|
|
162
|
+
className: "text-muted-foreground py-4 text-center",
|
|
163
|
+
children: [
|
|
164
|
+
"This deal is already ",
|
|
165
|
+
deal.status.toLowerCase(),
|
|
166
|
+
". No actions available."
|
|
167
|
+
]
|
|
168
|
+
}),
|
|
169
|
+
/* @__PURE__ */ jsx("div", {
|
|
170
|
+
className: "border-border border-t pt-3",
|
|
171
|
+
children: /* @__PURE__ */ jsx(Button, {
|
|
172
|
+
className: "w-full",
|
|
173
|
+
variant: "outline",
|
|
174
|
+
onPress: handleClose,
|
|
175
|
+
children: "Close"
|
|
176
|
+
})
|
|
177
|
+
})
|
|
178
|
+
]
|
|
179
|
+
}),
|
|
180
|
+
mode === "win" && /* @__PURE__ */ jsxs("div", {
|
|
181
|
+
className: "space-y-4",
|
|
182
|
+
children: [
|
|
183
|
+
/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("label", {
|
|
184
|
+
htmlFor: "won-source",
|
|
185
|
+
className: "text-muted-foreground mb-1 block text-sm font-medium",
|
|
186
|
+
children: "How did you win this deal?"
|
|
187
|
+
}), /* @__PURE__ */ jsxs("select", {
|
|
188
|
+
id: "won-source",
|
|
189
|
+
value: wonSource,
|
|
190
|
+
onChange: (e) => setWonSource(e.target.value),
|
|
191
|
+
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",
|
|
192
|
+
children: [
|
|
193
|
+
/* @__PURE__ */ jsx("option", {
|
|
194
|
+
value: "",
|
|
195
|
+
children: "Select a source..."
|
|
196
|
+
}),
|
|
197
|
+
/* @__PURE__ */ jsx("option", {
|
|
198
|
+
value: "referral",
|
|
199
|
+
children: "Referral"
|
|
200
|
+
}),
|
|
201
|
+
/* @__PURE__ */ jsx("option", {
|
|
202
|
+
value: "cold_outreach",
|
|
203
|
+
children: "Cold Outreach"
|
|
204
|
+
}),
|
|
205
|
+
/* @__PURE__ */ jsx("option", {
|
|
206
|
+
value: "inbound",
|
|
207
|
+
children: "Inbound Lead"
|
|
208
|
+
}),
|
|
209
|
+
/* @__PURE__ */ jsx("option", {
|
|
210
|
+
value: "upsell",
|
|
211
|
+
children: "Upsell"
|
|
212
|
+
}),
|
|
213
|
+
/* @__PURE__ */ jsx("option", {
|
|
214
|
+
value: "other",
|
|
215
|
+
children: "Other"
|
|
216
|
+
})
|
|
217
|
+
]
|
|
218
|
+
})] }),
|
|
219
|
+
/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("label", {
|
|
220
|
+
htmlFor: "win-notes",
|
|
221
|
+
className: "text-muted-foreground mb-1 block text-sm font-medium",
|
|
222
|
+
children: "Notes (optional)"
|
|
223
|
+
}), /* @__PURE__ */ jsx("textarea", {
|
|
224
|
+
id: "win-notes",
|
|
225
|
+
value: notes,
|
|
226
|
+
onChange: (e) => setNotes(e.target.value),
|
|
227
|
+
placeholder: "Any additional notes about the win...",
|
|
228
|
+
rows: 3,
|
|
229
|
+
className: "border-input bg-background focus:ring-ring w-full rounded-md border px-3 py-2 text-sm focus:ring-2 focus:outline-none"
|
|
230
|
+
})] }),
|
|
231
|
+
error && /* @__PURE__ */ jsx("div", {
|
|
232
|
+
className: "bg-destructive/10 text-destructive rounded-md p-3 text-sm",
|
|
233
|
+
children: error
|
|
234
|
+
}),
|
|
235
|
+
/* @__PURE__ */ jsxs("div", {
|
|
236
|
+
className: "flex justify-end gap-3 pt-2",
|
|
237
|
+
children: [/* @__PURE__ */ jsx(Button, {
|
|
238
|
+
variant: "ghost",
|
|
239
|
+
onPress: () => setMode("menu"),
|
|
240
|
+
disabled: isLoading,
|
|
241
|
+
children: "Back"
|
|
242
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
243
|
+
onPress: handleWin,
|
|
244
|
+
disabled: isLoading,
|
|
245
|
+
children: isLoading ? "Processing..." : "🏆 Confirm Win"
|
|
246
|
+
})]
|
|
247
|
+
})
|
|
248
|
+
]
|
|
249
|
+
}),
|
|
250
|
+
mode === "lose" && /* @__PURE__ */ jsxs("div", {
|
|
251
|
+
className: "space-y-4",
|
|
252
|
+
children: [
|
|
253
|
+
/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("label", {
|
|
254
|
+
htmlFor: "lost-reason",
|
|
255
|
+
className: "text-muted-foreground mb-1 block text-sm font-medium",
|
|
256
|
+
children: "Why was this deal lost? *"
|
|
257
|
+
}), /* @__PURE__ */ jsxs("select", {
|
|
258
|
+
id: "lost-reason",
|
|
259
|
+
value: lostReason,
|
|
260
|
+
onChange: (e) => setLostReason(e.target.value),
|
|
261
|
+
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",
|
|
262
|
+
children: [
|
|
263
|
+
/* @__PURE__ */ jsx("option", {
|
|
264
|
+
value: "",
|
|
265
|
+
children: "Select a reason..."
|
|
266
|
+
}),
|
|
267
|
+
/* @__PURE__ */ jsx("option", {
|
|
268
|
+
value: "price",
|
|
269
|
+
children: "Price too high"
|
|
270
|
+
}),
|
|
271
|
+
/* @__PURE__ */ jsx("option", {
|
|
272
|
+
value: "competitor",
|
|
273
|
+
children: "Lost to competitor"
|
|
274
|
+
}),
|
|
275
|
+
/* @__PURE__ */ jsx("option", {
|
|
276
|
+
value: "no_budget",
|
|
277
|
+
children: "No budget"
|
|
278
|
+
}),
|
|
279
|
+
/* @__PURE__ */ jsx("option", {
|
|
280
|
+
value: "no_decision",
|
|
281
|
+
children: "No decision made"
|
|
282
|
+
}),
|
|
283
|
+
/* @__PURE__ */ jsx("option", {
|
|
284
|
+
value: "timing",
|
|
285
|
+
children: "Bad timing"
|
|
286
|
+
}),
|
|
287
|
+
/* @__PURE__ */ jsx("option", {
|
|
288
|
+
value: "product_fit",
|
|
289
|
+
children: "Product not a fit"
|
|
290
|
+
}),
|
|
291
|
+
/* @__PURE__ */ jsx("option", {
|
|
292
|
+
value: "other",
|
|
293
|
+
children: "Other"
|
|
294
|
+
})
|
|
295
|
+
]
|
|
296
|
+
})] }),
|
|
297
|
+
/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("label", {
|
|
298
|
+
htmlFor: "lose-notes",
|
|
299
|
+
className: "text-muted-foreground mb-1 block text-sm font-medium",
|
|
300
|
+
children: "Notes (optional)"
|
|
301
|
+
}), /* @__PURE__ */ jsx("textarea", {
|
|
302
|
+
id: "lose-notes",
|
|
303
|
+
value: notes,
|
|
304
|
+
onChange: (e) => setNotes(e.target.value),
|
|
305
|
+
placeholder: "Any additional details...",
|
|
306
|
+
rows: 3,
|
|
307
|
+
className: "border-input bg-background focus:ring-ring w-full rounded-md border px-3 py-2 text-sm focus:ring-2 focus:outline-none"
|
|
308
|
+
})] }),
|
|
309
|
+
error && /* @__PURE__ */ jsx("div", {
|
|
310
|
+
className: "bg-destructive/10 text-destructive rounded-md p-3 text-sm",
|
|
311
|
+
children: error
|
|
312
|
+
}),
|
|
313
|
+
/* @__PURE__ */ jsxs("div", {
|
|
314
|
+
className: "flex justify-end gap-3 pt-2",
|
|
315
|
+
children: [/* @__PURE__ */ jsx(Button, {
|
|
316
|
+
variant: "ghost",
|
|
317
|
+
onPress: () => setMode("menu"),
|
|
318
|
+
disabled: isLoading,
|
|
319
|
+
children: "Back"
|
|
320
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
321
|
+
variant: "destructive",
|
|
322
|
+
onPress: handleLose,
|
|
323
|
+
disabled: isLoading,
|
|
324
|
+
children: isLoading ? "Processing..." : "❌ Confirm Loss"
|
|
325
|
+
})]
|
|
326
|
+
})
|
|
327
|
+
]
|
|
328
|
+
}),
|
|
329
|
+
mode === "move" && /* @__PURE__ */ jsxs("div", {
|
|
330
|
+
className: "space-y-4",
|
|
331
|
+
children: [
|
|
332
|
+
/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("label", {
|
|
333
|
+
htmlFor: "move-stage",
|
|
334
|
+
className: "text-muted-foreground mb-1 block text-sm font-medium",
|
|
335
|
+
children: "Move to Stage"
|
|
336
|
+
}), /* @__PURE__ */ jsx("select", {
|
|
337
|
+
id: "move-stage",
|
|
338
|
+
value: selectedStageId,
|
|
339
|
+
onChange: (e) => setSelectedStageId(e.target.value),
|
|
340
|
+
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",
|
|
341
|
+
children: stages.map((stage) => /* @__PURE__ */ jsxs("option", {
|
|
342
|
+
value: stage.id,
|
|
343
|
+
children: [stage.name, stage.id === deal.stageId ? " (current)" : ""]
|
|
344
|
+
}, stage.id))
|
|
345
|
+
})] }),
|
|
346
|
+
error && /* @__PURE__ */ jsx("div", {
|
|
347
|
+
className: "bg-destructive/10 text-destructive rounded-md p-3 text-sm",
|
|
348
|
+
children: error
|
|
349
|
+
}),
|
|
350
|
+
/* @__PURE__ */ jsxs("div", {
|
|
351
|
+
className: "flex justify-end gap-3 pt-2",
|
|
352
|
+
children: [/* @__PURE__ */ jsx(Button, {
|
|
353
|
+
variant: "ghost",
|
|
354
|
+
onPress: () => setMode("menu"),
|
|
355
|
+
disabled: isLoading,
|
|
356
|
+
children: "Back"
|
|
357
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
358
|
+
onPress: handleMove,
|
|
359
|
+
disabled: isLoading,
|
|
360
|
+
children: isLoading ? "Moving..." : "➡️ Move Deal"
|
|
361
|
+
})]
|
|
362
|
+
})
|
|
363
|
+
]
|
|
364
|
+
})
|
|
365
|
+
]
|
|
366
|
+
})]
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
//#endregion
|
|
371
|
+
export { DealActionsModal };
|
|
372
|
+
//# sourceMappingURL=DealActionsModal.js.map
|