@elevasis/ui 2.23.0 → 2.24.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/dist/app/index.d.ts +2915 -0
- package/dist/app/index.js +5 -4
- package/dist/{chunk-3HEUGBOT.js → chunk-2WZ635SS.js} +2 -2
- package/dist/{chunk-D3KQAABP.js → chunk-4NWNS7TX.js} +1 -1
- package/dist/{chunk-7PGEGSUM.js → chunk-FUEXGRFR.js} +2 -2
- package/dist/{chunk-6IA2OMAE.js → chunk-HC2KV6BU.js} +9 -0
- package/dist/{chunk-YU6MBDVO.js → chunk-KCJ6VATY.js} +4 -68
- package/dist/{chunk-FXWETLEB.js → chunk-KLFIJDTD.js} +1 -1
- package/dist/{chunk-PXGSJNBH.js → chunk-M2HWJY6O.js} +704 -375
- package/dist/{chunk-N6WLOWOD.js → chunk-MTR6AN2C.js} +3 -12
- package/dist/chunk-OWHQ65EQ.js +211 -0
- package/dist/{chunk-XOTJNW4Q.js → chunk-QIW6OCEI.js} +18 -1
- package/dist/{chunk-GUJUK6EH.js → chunk-QULLZ5PE.js} +172 -2
- package/dist/{chunk-QZJM3RYI.js → chunk-SNHGSCKH.js} +1 -1
- package/dist/{chunk-LVUCBY7X.js → chunk-UDJE54WN.js} +85 -3
- package/dist/{chunk-EPV7NU2E.js → chunk-VGNAV3TH.js} +385 -188
- package/dist/{chunk-PTUOINQ2.js → chunk-YBZT7MJR.js} +3 -3
- package/dist/{chunk-SQ5JGELM.js → chunk-ZDKQNQ4X.js} +19 -1
- package/dist/components/index.d.ts +488 -452
- package/dist/components/index.js +58 -22
- package/dist/components/navigation/index.js +2 -2
- package/dist/features/auth/index.d.ts +463 -377
- package/dist/features/crm/index.d.ts +459 -379
- package/dist/features/crm/index.js +8 -8
- package/dist/features/dashboard/index.js +8 -8
- package/dist/features/delivery/index.d.ts +457 -371
- package/dist/features/delivery/index.js +8 -8
- package/dist/features/lead-gen/index.d.ts +213 -65
- package/dist/features/lead-gen/index.js +9 -8
- package/dist/features/monitoring/index.js +9 -9
- package/dist/features/monitoring/requests/index.js +7 -7
- package/dist/features/operations/index.js +11 -10
- package/dist/features/settings/index.d.ts +463 -377
- package/dist/features/settings/index.js +9 -9
- package/dist/hooks/delivery/index.d.ts +457 -371
- package/dist/hooks/index.d.ts +936 -718
- package/dist/hooks/index.js +7 -7
- package/dist/hooks/published.d.ts +936 -718
- package/dist/hooks/published.js +7 -7
- package/dist/index.d.ts +1313 -1027
- package/dist/index.js +8 -8
- package/dist/initialization/index.d.ts +463 -377
- package/dist/organization/index.d.ts +11 -1
- package/dist/organization/index.js +2 -2
- package/dist/profile/index.d.ts +463 -377
- package/dist/provider/index.d.ts +3132 -169
- package/dist/provider/index.js +6 -6
- package/dist/provider/published.d.ts +3098 -168
- package/dist/provider/published.js +3 -3
- package/dist/supabase/index.d.ts +559 -389
- package/dist/test-utils/index.d.ts +21 -1
- package/dist/test-utils/index.js +13 -4
- package/dist/theme/index.js +2 -2
- package/dist/types/index.d.ts +463 -377
- package/package.json +4 -4
- package/src/test-utils/README.md +2 -0
- /package/dist/{chunk-ZBCTB5CA.js → chunk-EIOJNUPL.js} +0 -0
|
@@ -4,16 +4,18 @@ import { SubshellNavItem } from './chunk-CEWTOKE7.js';
|
|
|
4
4
|
import { SubshellSidebarSection } from './chunk-IIMU5YAJ.js';
|
|
5
5
|
import { FilterBar } from './chunk-PDHTXPSF.js';
|
|
6
6
|
import { CustomModal } from './chunk-KVJ3LFH2.js';
|
|
7
|
-
import { useDealTasksDue, useDealsLookup, useCreateDealTask, useDealsSummary, useDeleteDeal, usePaginationState, useDeals, useTableSort, sortData, useTableSelection, useDealDetail, useCompany } from './chunk-
|
|
7
|
+
import { useExecuteAction, useDealTasksDue, useDealsLookup, useCreateDealTask, useDealsSummary, useDeleteDeal, usePaginationState, useDeals, useTableSort, sortData, useTableSelection, useDealDetail, useCompany } from './chunk-QULLZ5PE.js';
|
|
8
|
+
import { useCrmActions, deriveActions } from './chunk-UDJE54WN.js';
|
|
8
9
|
import { SubshellContentContainer } from './chunk-TKAYX2SP.js';
|
|
9
10
|
import { CenteredErrorState, CardHeader, PageTitleCaption, EmptyState, ActivityTimeline } from './chunk-XUYBOO32.js';
|
|
10
11
|
import { useRouterContext } from './chunk-Q7DJKLEN.js';
|
|
11
12
|
import { PAGE_SIZE_DEFAULT, formatTimeAgo } from './chunk-SGXXJE52.js';
|
|
12
13
|
import { useElevasisServices } from './chunk-IRW7JMQ4.js';
|
|
13
|
-
import {
|
|
14
|
-
import { IconLayoutGrid, IconColumns, IconFileInvoice, IconAddressBook, IconTrophy, IconClockExclamation, IconUser, IconPlus, IconChecklist,
|
|
14
|
+
import { Button, Modal, Stack, NumberInput, Switch, Select, Textarea, TextInput, Group, Alert, Text, Box, Badge, Center, Loader, UnstyledButton, Title, Paper, Table, SimpleGrid, Checkbox, Pagination, ActionIcon, Tabs, Card, Code, Divider, CopyButton, Tooltip } from '@mantine/core';
|
|
15
|
+
import { IconLayoutGrid, IconColumns, IconFileInvoice, IconAddressBook, IconAlertCircle, IconTrophy, IconClockExclamation, IconUser, IconPlus, IconChecklist, IconHistory, IconSearch, IconTargetArrow, IconAlertTriangle, IconArrowLeft, IconFileText, IconTrash, IconX, IconBuilding, IconCheckbox, IconCalendar, IconMail, IconPhone, IconArrowRight, IconNote, IconCheck, IconCopy } from '@tabler/icons-react';
|
|
15
16
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
16
17
|
import { useState, useMemo, useEffect } from 'react';
|
|
18
|
+
import { useForm } from '@mantine/form';
|
|
17
19
|
import { useQuery } from '@tanstack/react-query';
|
|
18
20
|
import { useNavigate } from '@tanstack/react-router';
|
|
19
21
|
|
|
@@ -43,6 +45,282 @@ var SAVED_VIEW_PRESETS = [
|
|
|
43
45
|
urlFilters: { stage: "closed_won" }
|
|
44
46
|
}
|
|
45
47
|
];
|
|
48
|
+
function ActionButton({ action, dealId }) {
|
|
49
|
+
const executeAction = useExecuteAction({ dealId });
|
|
50
|
+
return /* @__PURE__ */ jsx(
|
|
51
|
+
Button,
|
|
52
|
+
{
|
|
53
|
+
variant: "light",
|
|
54
|
+
size: "sm",
|
|
55
|
+
loading: executeAction.isPending,
|
|
56
|
+
onClick: () => executeAction.mutate({ key: action.key }),
|
|
57
|
+
children: action.label
|
|
58
|
+
}
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
function getType(schema) {
|
|
62
|
+
const zodSchema = schema;
|
|
63
|
+
return zodSchema._def?.type ?? zodSchema._def?.typeName ?? "";
|
|
64
|
+
}
|
|
65
|
+
function unwrapField(schema) {
|
|
66
|
+
const type = getType(schema);
|
|
67
|
+
if (type === "optional" || type === "ZodOptional") {
|
|
68
|
+
const inner = schema._def?.innerType;
|
|
69
|
+
if (!inner) return { schema, required: false, nullable: false, defaultValue: void 0 };
|
|
70
|
+
return { ...unwrapField(inner), required: false };
|
|
71
|
+
}
|
|
72
|
+
if (type === "nullable" || type === "ZodNullable") {
|
|
73
|
+
const inner = schema._def?.innerType;
|
|
74
|
+
if (!inner) return { schema, required: true, nullable: true, defaultValue: null };
|
|
75
|
+
return { ...unwrapField(inner), nullable: true };
|
|
76
|
+
}
|
|
77
|
+
if (type === "default" || type === "ZodDefault") {
|
|
78
|
+
const def = schema._def;
|
|
79
|
+
const inner = def?.innerType;
|
|
80
|
+
const defaultValue = typeof def?.defaultValue === "function" ? def.defaultValue() : def?.defaultValue;
|
|
81
|
+
if (!inner) return { schema, required: false, nullable: false, defaultValue };
|
|
82
|
+
return { ...unwrapField(inner), required: false, defaultValue };
|
|
83
|
+
}
|
|
84
|
+
return { schema, required: true, nullable: false, defaultValue: void 0 };
|
|
85
|
+
}
|
|
86
|
+
function getObjectShape(schema) {
|
|
87
|
+
const zodSchema = schema;
|
|
88
|
+
const type = getType(schema);
|
|
89
|
+
if (type !== "object" && type !== "ZodObject") {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
const shape = zodSchema.shape;
|
|
93
|
+
if (!shape || typeof shape !== "object" || Array.isArray(shape)) {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
return shape;
|
|
97
|
+
}
|
|
98
|
+
function getEnumValues(schema) {
|
|
99
|
+
const zodSchema = schema;
|
|
100
|
+
const values = zodSchema.options ?? zodSchema._def?.values ?? Object.values(zodSchema._def?.entries ?? {});
|
|
101
|
+
if (!Array.isArray(values) || values.some((value) => typeof value !== "string")) {
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
return values;
|
|
105
|
+
}
|
|
106
|
+
function labelForField(name) {
|
|
107
|
+
return name.replace(/[_-]+/g, " ").replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/^./, (s) => s.toUpperCase());
|
|
108
|
+
}
|
|
109
|
+
function isLongTextField(name) {
|
|
110
|
+
return ["body", "message", "reply", "replyBody", "emailBody"].includes(name);
|
|
111
|
+
}
|
|
112
|
+
function initialValueFor(field, defaultValue) {
|
|
113
|
+
if (defaultValue !== void 0) {
|
|
114
|
+
if (field.kind === "date" && defaultValue instanceof Date) {
|
|
115
|
+
return defaultValue.toISOString().slice(0, 10);
|
|
116
|
+
}
|
|
117
|
+
return defaultValue;
|
|
118
|
+
}
|
|
119
|
+
switch (field.kind) {
|
|
120
|
+
case "boolean":
|
|
121
|
+
return false;
|
|
122
|
+
case "number":
|
|
123
|
+
return field.required ? 0 : "";
|
|
124
|
+
case "enum":
|
|
125
|
+
return field.required ? field.enumValues?.[0] ?? "" : "";
|
|
126
|
+
case "date":
|
|
127
|
+
case "dateString":
|
|
128
|
+
case "string":
|
|
129
|
+
return "";
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
function describeSchema(schema) {
|
|
133
|
+
if (!schema) {
|
|
134
|
+
return { supported: false, reason: "This action does not define a payload schema." };
|
|
135
|
+
}
|
|
136
|
+
const shape = getObjectShape(schema);
|
|
137
|
+
if (!shape) {
|
|
138
|
+
return { supported: false, reason: "This action requires a payload schema that is not a flat object." };
|
|
139
|
+
}
|
|
140
|
+
const fields = [];
|
|
141
|
+
const initialValues = {};
|
|
142
|
+
for (const [name, rawFieldSchema] of Object.entries(shape)) {
|
|
143
|
+
const { schema: fieldSchema, required, nullable, defaultValue } = unwrapField(rawFieldSchema);
|
|
144
|
+
const type = getType(fieldSchema);
|
|
145
|
+
const zodField = fieldSchema;
|
|
146
|
+
const stringFormat = zodField._def?.format;
|
|
147
|
+
let descriptor = null;
|
|
148
|
+
if (type === "string" || type === "ZodString") {
|
|
149
|
+
descriptor = {
|
|
150
|
+
name,
|
|
151
|
+
label: labelForField(name),
|
|
152
|
+
kind: stringFormat === "date" ? "dateString" : "string",
|
|
153
|
+
schema: rawFieldSchema,
|
|
154
|
+
required,
|
|
155
|
+
nullable
|
|
156
|
+
};
|
|
157
|
+
} else if (type === "number" || type === "ZodNumber") {
|
|
158
|
+
descriptor = { name, label: labelForField(name), kind: "number", schema: rawFieldSchema, required, nullable };
|
|
159
|
+
} else if (type === "boolean" || type === "ZodBoolean") {
|
|
160
|
+
descriptor = { name, label: labelForField(name), kind: "boolean", schema: rawFieldSchema, required, nullable };
|
|
161
|
+
} else if (type === "enum" || type === "ZodEnum") {
|
|
162
|
+
const enumValues = getEnumValues(fieldSchema);
|
|
163
|
+
if (!enumValues) {
|
|
164
|
+
return { supported: false, reason: `Field "${name}" uses an enum shape that is not supported by this form.` };
|
|
165
|
+
}
|
|
166
|
+
descriptor = {
|
|
167
|
+
name,
|
|
168
|
+
label: labelForField(name),
|
|
169
|
+
kind: "enum",
|
|
170
|
+
schema: rawFieldSchema,
|
|
171
|
+
required,
|
|
172
|
+
nullable,
|
|
173
|
+
enumValues
|
|
174
|
+
};
|
|
175
|
+
} else if (type === "date" || type === "ZodDate") {
|
|
176
|
+
descriptor = { name, label: labelForField(name), kind: "date", schema: rawFieldSchema, required, nullable };
|
|
177
|
+
}
|
|
178
|
+
if (!descriptor) {
|
|
179
|
+
return {
|
|
180
|
+
supported: false,
|
|
181
|
+
reason: `Field "${name}" is not supported. CRM action forms currently support only flat string, number, boolean, enum, and date fields.`
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
const field = { ...descriptor, defaultValue: initialValueFor(descriptor, defaultValue) };
|
|
185
|
+
fields.push(field);
|
|
186
|
+
initialValues[name] = field.defaultValue;
|
|
187
|
+
}
|
|
188
|
+
return { supported: true, fields, initialValues };
|
|
189
|
+
}
|
|
190
|
+
function normalizeValue(field, value) {
|
|
191
|
+
const isEmpty = value === "" || value === void 0 || value === null;
|
|
192
|
+
if (isEmpty) {
|
|
193
|
+
if (!field.required) return void 0;
|
|
194
|
+
if (field.nullable) return null;
|
|
195
|
+
return value;
|
|
196
|
+
}
|
|
197
|
+
if (field.kind === "date") {
|
|
198
|
+
const acceptsString = (field.schema._def?.coerce ?? false) === true;
|
|
199
|
+
return acceptsString ? value : new Date(String(value));
|
|
200
|
+
}
|
|
201
|
+
return value;
|
|
202
|
+
}
|
|
203
|
+
function buildPayload(fields, values) {
|
|
204
|
+
return fields.reduce((payload, field) => {
|
|
205
|
+
const value = normalizeValue(field, values[field.name]);
|
|
206
|
+
if (value !== void 0 || field.required) {
|
|
207
|
+
payload[field.name] = value;
|
|
208
|
+
}
|
|
209
|
+
return payload;
|
|
210
|
+
}, {});
|
|
211
|
+
}
|
|
212
|
+
function ActionFormButton({ action, dealId }) {
|
|
213
|
+
const [opened, setOpened] = useState(false);
|
|
214
|
+
const executeAction = useExecuteAction({ dealId });
|
|
215
|
+
const schemaDescription = describeSchema(action.payloadSchema);
|
|
216
|
+
const form = useForm({
|
|
217
|
+
initialValues: schemaDescription.supported ? schemaDescription.initialValues : {}
|
|
218
|
+
});
|
|
219
|
+
const handleClose = () => {
|
|
220
|
+
setOpened(false);
|
|
221
|
+
form.reset();
|
|
222
|
+
form.clearErrors();
|
|
223
|
+
};
|
|
224
|
+
const handleSubmit = form.onSubmit(async (values) => {
|
|
225
|
+
if (!schemaDescription.supported || !action.payloadSchema) return;
|
|
226
|
+
form.clearErrors();
|
|
227
|
+
const payload = buildPayload(schemaDescription.fields, values);
|
|
228
|
+
const parsed = action.payloadSchema.safeParse(payload);
|
|
229
|
+
if (!parsed.success) {
|
|
230
|
+
const formErrors = {};
|
|
231
|
+
for (const issue of parsed.error.issues) {
|
|
232
|
+
const fieldName = issue.path[0];
|
|
233
|
+
if (typeof fieldName === "string" && schemaDescription.fields.some((field) => field.name === fieldName)) {
|
|
234
|
+
formErrors[fieldName] = issue.message;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
form.setErrors(formErrors);
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
await executeAction.mutateAsync({ key: action.key, payload: parsed.data });
|
|
241
|
+
handleClose();
|
|
242
|
+
});
|
|
243
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
244
|
+
/* @__PURE__ */ jsx(Button, { variant: "light", size: "sm", onClick: () => setOpened(true), children: action.label }),
|
|
245
|
+
/* @__PURE__ */ jsx(Modal, { opened, onClose: handleClose, title: action.label, size: "xl", children: schemaDescription.supported ? /* @__PURE__ */ jsx("form", { onSubmit: handleSubmit, children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
|
|
246
|
+
schemaDescription.fields.map((field) => {
|
|
247
|
+
if (field.kind === "number") {
|
|
248
|
+
return /* @__PURE__ */ jsx(
|
|
249
|
+
NumberInput,
|
|
250
|
+
{
|
|
251
|
+
label: field.label,
|
|
252
|
+
required: field.required,
|
|
253
|
+
disabled: executeAction.isPending,
|
|
254
|
+
error: form.errors[field.name],
|
|
255
|
+
...form.getInputProps(field.name)
|
|
256
|
+
},
|
|
257
|
+
field.name
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
if (field.kind === "boolean") {
|
|
261
|
+
return /* @__PURE__ */ jsx(
|
|
262
|
+
Switch,
|
|
263
|
+
{
|
|
264
|
+
label: field.label,
|
|
265
|
+
disabled: executeAction.isPending,
|
|
266
|
+
error: form.errors[field.name],
|
|
267
|
+
...form.getInputProps(field.name, { type: "checkbox" })
|
|
268
|
+
},
|
|
269
|
+
field.name
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
if (field.kind === "enum") {
|
|
273
|
+
return /* @__PURE__ */ jsx(
|
|
274
|
+
Select,
|
|
275
|
+
{
|
|
276
|
+
label: field.label,
|
|
277
|
+
required: field.required,
|
|
278
|
+
disabled: executeAction.isPending,
|
|
279
|
+
data: field.enumValues ?? [],
|
|
280
|
+
error: form.errors[field.name],
|
|
281
|
+
...form.getInputProps(field.name)
|
|
282
|
+
},
|
|
283
|
+
field.name
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
if (field.kind === "string" && isLongTextField(field.name)) {
|
|
287
|
+
return /* @__PURE__ */ jsx(
|
|
288
|
+
Textarea,
|
|
289
|
+
{
|
|
290
|
+
label: field.label,
|
|
291
|
+
required: field.required,
|
|
292
|
+
disabled: executeAction.isPending,
|
|
293
|
+
error: form.errors[field.name],
|
|
294
|
+
autosize: true,
|
|
295
|
+
minRows: 6,
|
|
296
|
+
...form.getInputProps(field.name)
|
|
297
|
+
},
|
|
298
|
+
field.name
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
return /* @__PURE__ */ jsx(
|
|
302
|
+
TextInput,
|
|
303
|
+
{
|
|
304
|
+
label: field.label,
|
|
305
|
+
type: field.kind === "date" || field.kind === "dateString" ? "date" : "text",
|
|
306
|
+
required: field.required,
|
|
307
|
+
disabled: executeAction.isPending,
|
|
308
|
+
error: form.errors[field.name],
|
|
309
|
+
...form.getInputProps(field.name)
|
|
310
|
+
},
|
|
311
|
+
field.name
|
|
312
|
+
);
|
|
313
|
+
}),
|
|
314
|
+
/* @__PURE__ */ jsxs(Group, { justify: "flex-end", gap: "sm", children: [
|
|
315
|
+
/* @__PURE__ */ jsx(Button, { variant: "default", onClick: handleClose, disabled: executeAction.isPending, children: "Cancel" }),
|
|
316
|
+
/* @__PURE__ */ jsx(Button, { type: "submit", loading: executeAction.isPending, children: action.label })
|
|
317
|
+
] })
|
|
318
|
+
] }) }) : /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
|
|
319
|
+
/* @__PURE__ */ jsx(Alert, { color: "yellow", variant: "light", icon: /* @__PURE__ */ jsx(IconAlertCircle, { size: 16 }), title: "Unsupported action form", children: /* @__PURE__ */ jsx(Text, { size: "sm", children: schemaDescription.reason }) }),
|
|
320
|
+
/* @__PURE__ */ jsx(Button, { variant: "default", onClick: handleClose, disabled: executeAction.isPending, children: "Close" })
|
|
321
|
+
] }) })
|
|
322
|
+
] });
|
|
323
|
+
}
|
|
46
324
|
var KIND_ICONS = {
|
|
47
325
|
call: IconPhone,
|
|
48
326
|
email: IconMail,
|
|
@@ -63,7 +341,7 @@ function formatDueLabel(dueAt) {
|
|
|
63
341
|
return `in ${diffDays}d`;
|
|
64
342
|
}
|
|
65
343
|
function TaskRow({ task, onClick }) {
|
|
66
|
-
const
|
|
344
|
+
const KindIcon = KIND_ICONS[task.kind];
|
|
67
345
|
const dueLabel = formatDueLabel(task.dueAt);
|
|
68
346
|
const isOverdue = task.dueAt !== null && new Date(task.dueAt) < /* @__PURE__ */ new Date();
|
|
69
347
|
return /* @__PURE__ */ jsx(
|
|
@@ -84,7 +362,7 @@ function TaskRow({ task, onClick }) {
|
|
|
84
362
|
e.currentTarget.style.backgroundColor = "transparent";
|
|
85
363
|
},
|
|
86
364
|
children: /* @__PURE__ */ jsxs(Group, { gap: "xs", wrap: "nowrap", children: [
|
|
87
|
-
/* @__PURE__ */ jsx(
|
|
365
|
+
/* @__PURE__ */ jsx(KindIcon, { size: 14, style: { color: "var(--color-text-dimmed)", flexShrink: 0 } }),
|
|
88
366
|
/* @__PURE__ */ jsx(Text, { size: "xs", truncate: true, style: { flex: 1, minWidth: 0 }, children: task.title }),
|
|
89
367
|
dueLabel && /* @__PURE__ */ jsx(Badge, { size: "xs", variant: "light", color: isOverdue ? "red" : "gray", style: { flexShrink: 0 }, children: dueLabel })
|
|
90
368
|
] })
|
|
@@ -381,11 +659,35 @@ function useRecentCrmActivity(opts) {
|
|
|
381
659
|
error: query.error
|
|
382
660
|
};
|
|
383
661
|
}
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
662
|
+
|
|
663
|
+
// src/features/crm/pages/shared.ts
|
|
664
|
+
var DEAL_STAGE_COLORS = {
|
|
665
|
+
interested: "blue",
|
|
666
|
+
proposal: "yellow",
|
|
667
|
+
closing: "orange",
|
|
668
|
+
closed_won: "green",
|
|
669
|
+
closed_lost: "red",
|
|
670
|
+
nurturing: "grape"
|
|
671
|
+
};
|
|
672
|
+
var DEAL_STAGE_OPTIONS = [
|
|
673
|
+
{ value: "interested", label: "Interested" },
|
|
674
|
+
{ value: "proposal", label: "Proposal" },
|
|
675
|
+
{ value: "closing", label: "Closing" },
|
|
676
|
+
{ value: "closed_won", label: "Closed Won" },
|
|
677
|
+
{ value: "closed_lost", label: "Closed Lost" },
|
|
678
|
+
{ value: "nurturing", label: "Nurturing" }
|
|
679
|
+
];
|
|
680
|
+
function formatDealStageLabel(stage) {
|
|
681
|
+
if (!stage) return "Unknown";
|
|
682
|
+
return stage.split("_").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
683
|
+
}
|
|
684
|
+
var BOOKING_PAGE_URL = "https://elevasis.io/inbound/book";
|
|
685
|
+
function buildBookingUrl(opts = {}) {
|
|
686
|
+
const params = new URLSearchParams();
|
|
687
|
+
if (opts.dealId) params.set("dealId", opts.dealId);
|
|
688
|
+
if (opts.contactId) params.set("contactId", opts.contactId);
|
|
689
|
+
return params.size > 0 ? `${BOOKING_PAGE_URL}?${params.toString()}` : BOOKING_PAGE_URL;
|
|
690
|
+
}
|
|
389
691
|
var STAGE_LABELS = {
|
|
390
692
|
interested: "Interested",
|
|
391
693
|
proposal: "Proposal",
|
|
@@ -403,143 +705,54 @@ function PipelineFunnelWidget({ onStageClick, getDealValue }) {
|
|
|
403
705
|
return /* @__PURE__ */ jsx(Paper, { withBorder: true, p: "md", children: /* @__PURE__ */ jsx(CenteredErrorState, { error, title: "Failed to load pipeline data" }) });
|
|
404
706
|
}
|
|
405
707
|
const totalDeals = data.reduce((sum, s) => sum + s.count, 0);
|
|
406
|
-
const maxCount = Math.max(...data.map((s) => s.count), 1);
|
|
407
|
-
if (totalDeals === 0) {
|
|
408
|
-
return /* @__PURE__ */ jsxs(Paper, { withBorder: true, p: "md", children: [
|
|
409
|
-
/* @__PURE__ */ jsx(CardHeader, { icon: /* @__PURE__ */ jsx(IconColumns, { size: 16 }), title: "Pipeline" }),
|
|
410
|
-
/* @__PURE__ */ jsx(Center, { h: 200, children: /* @__PURE__ */ jsx(Alert, { icon: /* @__PURE__ */ jsx(IconAlertCircle, {}), color: "gray", variant: "light", children: "No deals in the pipeline yet" }) })
|
|
411
|
-
] });
|
|
412
|
-
}
|
|
413
708
|
return /* @__PURE__ */ jsxs(Paper, { withBorder: true, p: "md", children: [
|
|
414
709
|
/* @__PURE__ */ jsx(
|
|
415
710
|
CardHeader,
|
|
416
711
|
{
|
|
417
712
|
icon: /* @__PURE__ */ jsx(IconColumns, { size: 16 }),
|
|
418
713
|
title: "Pipeline",
|
|
419
|
-
subtitle: `${totalDeals} deal${totalDeals !== 1 ? "s" : ""} total`
|
|
714
|
+
subtitle: totalDeals === 0 ? "No deals in the pipeline yet" : `${totalDeals} deal${totalDeals !== 1 ? "s" : ""} total`
|
|
420
715
|
}
|
|
421
716
|
),
|
|
422
|
-
/* @__PURE__ */ jsx(
|
|
717
|
+
/* @__PURE__ */ jsx(Group, { gap: "lg", wrap: "nowrap", mt: "md", children: PIPELINE_FUNNEL_ORDER.map((stage) => {
|
|
423
718
|
const summary = data.find((s) => s.stage === stage);
|
|
424
719
|
const isEmpty = summary.count === 0;
|
|
425
|
-
const
|
|
426
|
-
|
|
720
|
+
const stageColor = DEAL_STAGE_COLORS[stage] ?? "gray";
|
|
721
|
+
const accent = `var(--mantine-color-${stageColor}-6)`;
|
|
722
|
+
return /* @__PURE__ */ jsxs(
|
|
427
723
|
Box,
|
|
428
724
|
{
|
|
429
725
|
onClick: () => onStageClick(stage),
|
|
430
726
|
style: {
|
|
727
|
+
flex: 1,
|
|
728
|
+
minWidth: 0,
|
|
431
729
|
cursor: "pointer",
|
|
432
|
-
|
|
433
|
-
padding: "6px 8px",
|
|
434
|
-
transition: `background-color var(--duration-fast) var(--easing)`,
|
|
435
|
-
marginBottom: 4
|
|
436
|
-
},
|
|
437
|
-
onMouseEnter: (e) => {
|
|
438
|
-
e.currentTarget.style.backgroundColor = "var(--color-surface-hover)";
|
|
730
|
+
padding: "4px 0"
|
|
439
731
|
},
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
width: `${barWidth}%`,
|
|
453
|
-
borderRadius: 4,
|
|
454
|
-
backgroundColor: isEmpty ? "var(--color-border)" : "color-mix(in srgb, var(--color-primary) 70%, transparent)",
|
|
455
|
-
opacity: isEmpty ? 0.4 : 1,
|
|
456
|
-
transition: `width var(--duration-normal) var(--easing)`
|
|
732
|
+
children: [
|
|
733
|
+
/* @__PURE__ */ jsxs(Group, { gap: 8, wrap: "nowrap", align: "center", mb: 6, children: [
|
|
734
|
+
/* @__PURE__ */ jsx(
|
|
735
|
+
Box,
|
|
736
|
+
{
|
|
737
|
+
style: {
|
|
738
|
+
width: 8,
|
|
739
|
+
height: 8,
|
|
740
|
+
borderRadius: "50%",
|
|
741
|
+
background: accent,
|
|
742
|
+
flexShrink: 0
|
|
743
|
+
}
|
|
457
744
|
}
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
745
|
+
),
|
|
746
|
+
/* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", truncate: true, children: STAGE_LABELS[stage] })
|
|
747
|
+
] }),
|
|
748
|
+
/* @__PURE__ */ jsx(Text, { size: "xl", fw: 600, c: isEmpty ? "dimmed" : void 0, lh: 1, children: summary.count })
|
|
749
|
+
]
|
|
461
750
|
},
|
|
462
751
|
stage
|
|
463
752
|
);
|
|
464
753
|
}) })
|
|
465
754
|
] });
|
|
466
755
|
}
|
|
467
|
-
var MAX_VISIBLE = 5;
|
|
468
|
-
function KindIcon({ kind }) {
|
|
469
|
-
const size = 16;
|
|
470
|
-
switch (kind) {
|
|
471
|
-
case "call":
|
|
472
|
-
return /* @__PURE__ */ jsx(IconPhone, { size });
|
|
473
|
-
case "email":
|
|
474
|
-
return /* @__PURE__ */ jsx(IconMail, { size });
|
|
475
|
-
case "meeting":
|
|
476
|
-
return /* @__PURE__ */ jsx(IconCalendar, { size });
|
|
477
|
-
default:
|
|
478
|
-
return /* @__PURE__ */ jsx(IconCheckbox, { size });
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
function formatDueDate(dueAt) {
|
|
482
|
-
if (!dueAt) return "No due date";
|
|
483
|
-
return new Date(dueAt).toLocaleDateString();
|
|
484
|
-
}
|
|
485
|
-
function TasksDueWidget({ onTaskClick, onSeeAll }) {
|
|
486
|
-
const { data: tasks, isLoading, error } = useDealTasksDue({ window: "today_and_overdue" });
|
|
487
|
-
if (isLoading) {
|
|
488
|
-
return /* @__PURE__ */ jsx(Paper, { withBorder: true, p: "md", children: /* @__PURE__ */ jsx(Center, { p: "xl", children: /* @__PURE__ */ jsx(Loader, {}) }) });
|
|
489
|
-
}
|
|
490
|
-
if (error) {
|
|
491
|
-
return /* @__PURE__ */ jsx(Paper, { withBorder: true, p: "md", children: /* @__PURE__ */ jsx(CenteredErrorState, { error, title: "Failed to load tasks" }) });
|
|
492
|
-
}
|
|
493
|
-
const totalCount = tasks?.length ?? 0;
|
|
494
|
-
const visibleTasks = (tasks ?? []).slice(0, MAX_VISIBLE);
|
|
495
|
-
const hasMore = totalCount > MAX_VISIBLE;
|
|
496
|
-
const seeAllLink = onSeeAll && hasMore ? /* @__PURE__ */ jsxs(Anchor, { size: "sm", onClick: onSeeAll, style: { cursor: "pointer" }, children: [
|
|
497
|
-
"See all (",
|
|
498
|
-
totalCount,
|
|
499
|
-
")"
|
|
500
|
-
] }) : void 0;
|
|
501
|
-
if (totalCount === 0) {
|
|
502
|
-
return /* @__PURE__ */ jsxs(Paper, { withBorder: true, p: "md", children: [
|
|
503
|
-
/* @__PURE__ */ jsx(CardHeader, { icon: /* @__PURE__ */ jsx(IconChecklist, { size: 16 }), title: "Tasks Due" }),
|
|
504
|
-
/* @__PURE__ */ jsx(Center, { h: 120, children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "No tasks due today" }) })
|
|
505
|
-
] });
|
|
506
|
-
}
|
|
507
|
-
return /* @__PURE__ */ jsxs(Paper, { withBorder: true, p: "md", children: [
|
|
508
|
-
/* @__PURE__ */ jsx(
|
|
509
|
-
CardHeader,
|
|
510
|
-
{
|
|
511
|
-
icon: /* @__PURE__ */ jsx(IconChecklist, { size: 16 }),
|
|
512
|
-
title: "Tasks Due",
|
|
513
|
-
subtitle: `${totalCount} task${totalCount !== 1 ? "s" : ""}`,
|
|
514
|
-
rightSection: seeAllLink
|
|
515
|
-
}
|
|
516
|
-
),
|
|
517
|
-
/* @__PURE__ */ jsx(Stack, { gap: "xs", children: visibleTasks.map((task) => /* @__PURE__ */ jsx(
|
|
518
|
-
Box,
|
|
519
|
-
{
|
|
520
|
-
onClick: () => onTaskClick(task.dealId),
|
|
521
|
-
style: {
|
|
522
|
-
cursor: "pointer",
|
|
523
|
-
borderRadius: "var(--mantine-radius-sm)",
|
|
524
|
-
padding: "6px 8px",
|
|
525
|
-
transition: `background-color var(--duration-fast) var(--easing)`
|
|
526
|
-
},
|
|
527
|
-
onMouseEnter: (e) => {
|
|
528
|
-
e.currentTarget.style.backgroundColor = "var(--color-surface-hover)";
|
|
529
|
-
},
|
|
530
|
-
onMouseLeave: (e) => {
|
|
531
|
-
e.currentTarget.style.backgroundColor = "transparent";
|
|
532
|
-
},
|
|
533
|
-
children: /* @__PURE__ */ jsxs(Group, { gap: "sm", wrap: "nowrap", children: [
|
|
534
|
-
/* @__PURE__ */ jsx(Text, { c: "dimmed", style: { flexShrink: 0 }, children: /* @__PURE__ */ jsx(KindIcon, { kind: task.kind }) }),
|
|
535
|
-
/* @__PURE__ */ jsx(Text, { size: "sm", style: { flex: 1, minWidth: 0 }, truncate: true, children: task.title }),
|
|
536
|
-
/* @__PURE__ */ jsx(Badge, { size: "xs", variant: "light", color: "gray", style: { flexShrink: 0 }, children: formatDueDate(task.dueAt) })
|
|
537
|
-
] })
|
|
538
|
-
},
|
|
539
|
-
task.id
|
|
540
|
-
)) })
|
|
541
|
-
] });
|
|
542
|
-
}
|
|
543
756
|
function ActivityKindIcon({ kind }) {
|
|
544
757
|
const size = 16;
|
|
545
758
|
switch (kind) {
|
|
@@ -578,41 +791,28 @@ function ActivityFeedWidget({ onDealClick, limit }) {
|
|
|
578
791
|
/* @__PURE__ */ jsx(Center, { h: 120, children: /* @__PURE__ */ jsx(Alert, { icon: /* @__PURE__ */ jsx(IconAlertCircle, {}), color: "gray", variant: "light", children: "No recent activity" }) })
|
|
579
792
|
] });
|
|
580
793
|
}
|
|
581
|
-
return /* @__PURE__ */
|
|
794
|
+
return /* @__PURE__ */ jsx(Paper, { withBorder: true, p: "md", children: /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
|
|
582
795
|
/* @__PURE__ */ jsx(CardHeader, { icon: /* @__PURE__ */ jsx(IconHistory, { size: 16 }), title: "Recent Activity" }),
|
|
583
|
-
/* @__PURE__ */
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
{
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
},
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
children: /* @__PURE__ */ jsxs(Group, { gap: "sm", wrap: "nowrap", align: "flex-start", children: [
|
|
602
|
-
/* @__PURE__ */ jsx(Text, { c: "dimmed", style: { flexShrink: 0, paddingTop: 2 }, children: /* @__PURE__ */ jsx(ActivityKindIcon, { kind: entry.kind }) }),
|
|
603
|
-
/* @__PURE__ */ jsx(Box, { style: { flex: 1, minWidth: 0 }, children: /* @__PURE__ */ jsxs(Group, { gap: "xs", wrap: "nowrap", children: [
|
|
604
|
-
/* @__PURE__ */ jsx(Text, { size: "sm", fw: 600, truncate: true, style: { flexShrink: 0, maxWidth: 140 }, children: name }),
|
|
605
|
-
/* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", truncate: true, style: { flex: 1, minWidth: 0 }, children: entry.description })
|
|
606
|
-
] }) }),
|
|
607
|
-
/* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", style: { flexShrink: 0, whiteSpace: "nowrap" }, children: formatRelativeTime(entry.occurredAt) })
|
|
608
|
-
] })
|
|
609
|
-
},
|
|
610
|
-
entry.id
|
|
611
|
-
);
|
|
612
|
-
}) })
|
|
613
|
-
] });
|
|
796
|
+
/* @__PURE__ */ jsxs(Table, { highlightOnHover: true, children: [
|
|
797
|
+
/* @__PURE__ */ jsx(Table.Thead, { children: /* @__PURE__ */ jsxs(Table.Tr, { children: [
|
|
798
|
+
/* @__PURE__ */ jsx(Table.Th, { w: 40 }),
|
|
799
|
+
/* @__PURE__ */ jsx(Table.Th, { children: "Name" }),
|
|
800
|
+
/* @__PURE__ */ jsx(Table.Th, { children: "Activity" }),
|
|
801
|
+
/* @__PURE__ */ jsx(Table.Th, { w: 120, children: "When" })
|
|
802
|
+
] }) }),
|
|
803
|
+
/* @__PURE__ */ jsx(Table.Tbody, { children: data.map((entry) => {
|
|
804
|
+
const name = entry.contactName ?? entry.companyName ?? "Unknown";
|
|
805
|
+
return /* @__PURE__ */ jsxs(Table.Tr, { style: { cursor: "pointer" }, onClick: () => onDealClick(entry.dealId), children: [
|
|
806
|
+
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { c: "dimmed", component: "span", children: /* @__PURE__ */ jsx(ActivityKindIcon, { kind: entry.kind }) }) }),
|
|
807
|
+
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", fw: 500, truncate: true, children: name }) }),
|
|
808
|
+
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", truncate: true, children: entry.description }) }),
|
|
809
|
+
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", style: { whiteSpace: "nowrap" }, children: formatRelativeTime(entry.occurredAt) }) })
|
|
810
|
+
] }, entry.id);
|
|
811
|
+
}) })
|
|
812
|
+
] })
|
|
813
|
+
] }) });
|
|
614
814
|
}
|
|
615
|
-
var
|
|
815
|
+
var currencyFormatter = new Intl.NumberFormat("en-US", {
|
|
616
816
|
style: "currency",
|
|
617
817
|
currency: "USD",
|
|
618
818
|
maximumFractionDigits: 0
|
|
@@ -629,7 +829,7 @@ function StatTile({ label, value }) {
|
|
|
629
829
|
function MetricsStrip() {
|
|
630
830
|
const { data } = useCrmQuickMetrics();
|
|
631
831
|
return /* @__PURE__ */ jsx(Paper, { withBorder: true, p: "md", children: /* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 2, sm: 4 }, children: [
|
|
632
|
-
/* @__PURE__ */ jsx(StatTile, { label: "Total Pipeline Value", value:
|
|
832
|
+
/* @__PURE__ */ jsx(StatTile, { label: "Total Pipeline Value", value: currencyFormatter.format(data.totalPipelineValue) }),
|
|
633
833
|
/* @__PURE__ */ jsx(StatTile, { label: "Win Rate", value: formatPercent(data.winRate) }),
|
|
634
834
|
/* @__PURE__ */ jsx(StatTile, { label: "Open Deals", value: String(data.openDeals) }),
|
|
635
835
|
/* @__PURE__ */ jsx(StatTile, { label: "Won This Period", value: String(data.wonDeals) })
|
|
@@ -653,13 +853,8 @@ function CrmOverview({
|
|
|
653
853
|
}
|
|
654
854
|
),
|
|
655
855
|
/* @__PURE__ */ jsx(MetricsStrip, {}),
|
|
656
|
-
/* @__PURE__ */
|
|
657
|
-
|
|
658
|
-
/* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
|
|
659
|
-
/* @__PURE__ */ jsx(TasksDueWidget, { onTaskClick: onDealClick }),
|
|
660
|
-
/* @__PURE__ */ jsx(ActivityFeedWidget, { onDealClick })
|
|
661
|
-
] })
|
|
662
|
-
] })
|
|
856
|
+
/* @__PURE__ */ jsx(PipelineFunnelWidget, { onStageClick, getDealValue }),
|
|
857
|
+
/* @__PURE__ */ jsx(ActivityFeedWidget, { onDealClick })
|
|
663
858
|
] });
|
|
664
859
|
}
|
|
665
860
|
var crmManifest = {
|
|
@@ -669,28 +864,6 @@ var crmManifest = {
|
|
|
669
864
|
icon: IconAddressBook,
|
|
670
865
|
sidebar: CrmSidebar
|
|
671
866
|
};
|
|
672
|
-
|
|
673
|
-
// src/features/crm/pages/shared.ts
|
|
674
|
-
var DEAL_STAGE_COLORS = {
|
|
675
|
-
interested: "blue",
|
|
676
|
-
proposal: "yellow",
|
|
677
|
-
closing: "orange",
|
|
678
|
-
closed_won: "green",
|
|
679
|
-
closed_lost: "red",
|
|
680
|
-
nurturing: "grape"
|
|
681
|
-
};
|
|
682
|
-
var DEAL_STAGE_OPTIONS = [
|
|
683
|
-
{ value: "interested", label: "Interested" },
|
|
684
|
-
{ value: "proposal", label: "Proposal" },
|
|
685
|
-
{ value: "closing", label: "Closing" },
|
|
686
|
-
{ value: "closed_won", label: "Closed Won" },
|
|
687
|
-
{ value: "closed_lost", label: "Closed Lost" },
|
|
688
|
-
{ value: "nurturing", label: "Nurturing" }
|
|
689
|
-
];
|
|
690
|
-
function formatDealStageLabel(stage) {
|
|
691
|
-
if (!stage) return "Unknown";
|
|
692
|
-
return stage.split("_").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
693
|
-
}
|
|
694
867
|
var sortAccessors = {
|
|
695
868
|
company: (deal) => deal.contact?.company?.name || deal.discovery_data?.company || deal.contact_email?.split("@")[1] || "",
|
|
696
869
|
contact: (deal) => [deal.contact?.first_name, deal.contact?.last_name].filter(Boolean).join(" ") || "",
|
|
@@ -857,11 +1030,31 @@ function DealsListPage() {
|
|
|
857
1030
|
)
|
|
858
1031
|
] });
|
|
859
1032
|
}
|
|
1033
|
+
function BookingLinkPanel({ dealId, contactId }) {
|
|
1034
|
+
const url = buildBookingUrl({ dealId, contactId });
|
|
1035
|
+
return /* @__PURE__ */ jsx(Card, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
|
|
1036
|
+
/* @__PURE__ */ jsx(Title, { order: 4, children: "Booking Link" }),
|
|
1037
|
+
/* @__PURE__ */ jsxs(Group, { gap: "xs", wrap: "nowrap", children: [
|
|
1038
|
+
/* @__PURE__ */ jsx(
|
|
1039
|
+
Text,
|
|
1040
|
+
{
|
|
1041
|
+
size: "sm",
|
|
1042
|
+
c: "var(--color-primary)",
|
|
1043
|
+
style: { flex: 1, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" },
|
|
1044
|
+
children: url
|
|
1045
|
+
}
|
|
1046
|
+
),
|
|
1047
|
+
/* @__PURE__ */ jsx(CopyButton, { value: url, children: ({ copied, copy }) => /* @__PURE__ */ jsx(Tooltip, { label: copied ? "Copied" : "Copy", withArrow: true, children: /* @__PURE__ */ jsx(ActionIcon, { variant: "light", color: copied ? "teal" : void 0, onClick: copy, children: copied ? /* @__PURE__ */ jsx(IconCheck, { size: 16 }) : /* @__PURE__ */ jsx(IconCopy, { size: 16 }) }) }) })
|
|
1048
|
+
] })
|
|
1049
|
+
] }) });
|
|
1050
|
+
}
|
|
860
1051
|
function DealDetailPage({ dealId, renderActions, onDealLoaded }) {
|
|
861
1052
|
const navigate = useNavigate();
|
|
862
1053
|
const deleteDeal = useDeleteDeal();
|
|
863
1054
|
const { data: deal, isLoading, error } = useDealDetail(dealId);
|
|
864
1055
|
const [deleteModalOpen, setDeleteModalOpen] = useState(false);
|
|
1056
|
+
const crmActions = useCrmActions();
|
|
1057
|
+
const actions = useMemo(() => deal ? deriveActions(deal, crmActions) : [], [deal, crmActions]);
|
|
865
1058
|
useEffect(() => {
|
|
866
1059
|
if (deal) onDealLoaded?.(deal);
|
|
867
1060
|
}, [deal, onDealLoaded]);
|
|
@@ -894,6 +1087,9 @@ function DealDetailPage({ dealId, renderActions, onDealLoaded }) {
|
|
|
894
1087
|
children: "View Proposal"
|
|
895
1088
|
}
|
|
896
1089
|
),
|
|
1090
|
+
actions.map(
|
|
1091
|
+
(action) => action.payloadSchema ? /* @__PURE__ */ jsx(ActionFormButton, { action, dealId: deal.id }, action.key) : /* @__PURE__ */ jsx(ActionButton, { action, dealId: deal.id }, action.key)
|
|
1092
|
+
),
|
|
897
1093
|
renderActions?.(deal),
|
|
898
1094
|
/* @__PURE__ */ jsx(ActionIcon, { variant: "subtle", color: "red", onClick: () => setDeleteModalOpen(true), children: /* @__PURE__ */ jsx(IconTrash, { size: 16 }) })
|
|
899
1095
|
] }) : /* @__PURE__ */ jsx(
|
|
@@ -988,6 +1184,7 @@ function DealDetailPage({ dealId, renderActions, onDealLoaded }) {
|
|
|
988
1184
|
] })
|
|
989
1185
|
] })
|
|
990
1186
|
] }) }),
|
|
1187
|
+
deal.contact_id && /* @__PURE__ */ jsx(BookingLinkPanel, { dealId: deal.id, contactId: deal.contact_id }),
|
|
991
1188
|
/* @__PURE__ */ jsx(Card, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
|
|
992
1189
|
/* @__PURE__ */ jsx(Title, { order: 4, children: "Company" }),
|
|
993
1190
|
/* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 1, sm: 2 }, spacing: "sm", children: [
|
|
@@ -1202,4 +1399,4 @@ function CompanyDetailPage({ companyId }) {
|
|
|
1202
1399
|
] }) });
|
|
1203
1400
|
}
|
|
1204
1401
|
|
|
1205
|
-
export { ActivityFeedWidget, CRM_ITEMS, CompanyDetailPage, CrmOverview, CrmSidebar, CrmSidebarMiddle, CrmSidebarTop, DEAL_STAGE_COLORS, DEAL_STAGE_OPTIONS, DealDetailPage, DealsListPage, MetricsStrip, MyTasksPanel, PIPELINE_FUNNEL_ORDER, PipelineFunnelWidget, QuickCreateActions, SAVED_VIEW_PRESETS, SavedViewsPanel,
|
|
1402
|
+
export { ActionButton, ActionFormButton, ActivityFeedWidget, CRM_ITEMS, CompanyDetailPage, CrmOverview, CrmSidebar, CrmSidebarMiddle, CrmSidebarTop, DEAL_STAGE_COLORS, DEAL_STAGE_OPTIONS, DealDetailPage, DealsListPage, MetricsStrip, MyTasksPanel, PIPELINE_FUNNEL_ORDER, PipelineFunnelWidget, QuickCreateActions, SAVED_VIEW_PRESETS, SavedViewsPanel, crmManifest, formatDealStageLabel, useCrmPipelineSummary, useCrmQuickMetrics, useRecentCrmActivity };
|