@elevasis/ui 2.24.0 → 2.25.1

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.
Files changed (103) hide show
  1. package/dist/app/index.css +11 -0
  2. package/dist/app/index.d.ts +457 -357
  3. package/dist/app/index.js +8 -7
  4. package/dist/auth/index.js +4 -4
  5. package/dist/charts/index.js +7 -7
  6. package/dist/{chunk-DDZOHLHB.js → chunk-26HQMR7S.js} +1 -1
  7. package/dist/{chunk-4NWNS7TX.js → chunk-3AHEHVJ6.js} +3 -3
  8. package/dist/{chunk-LJWV4TWV.js → chunk-4ZFBVND2.js} +2 -2
  9. package/dist/{chunk-FUEXGRFR.js → chunk-7LJUTTXU.js} +5 -5
  10. package/dist/{chunk-KCJ6VATY.js → chunk-AZQY7AJR.js} +13 -24
  11. package/dist/{chunk-BIWHHWCJ.js → chunk-DWK2QIAK.js} +2 -1
  12. package/dist/{chunk-ZDKQNQ4X.js → chunk-GESXCQWY.js} +1 -1
  13. package/dist/{chunk-Z6FAH4XV.js → chunk-HKBEURCV.js} +1 -1
  14. package/dist/{chunk-HC2KV6BU.js → chunk-HOIT677G.js} +1 -1
  15. package/dist/chunk-IS53MXE4.js +230 -0
  16. package/dist/{chunk-MTR6AN2C.js → chunk-IX7LWINC.js} +6 -5
  17. package/dist/{chunk-AXXTN44Z.js → chunk-IYIZYMIE.js} +2 -2
  18. package/dist/{chunk-VGNAV3TH.js → chunk-JDQSCEEF.js} +1237 -642
  19. package/dist/chunk-JMI7L7Y7.js +524 -0
  20. package/dist/{chunk-TSSKOQBX.js → chunk-KMAXFJPH.js} +2 -2
  21. package/dist/{chunk-HQ7M6PBW.js → chunk-KU7ZDWQ7.js} +1 -1
  22. package/dist/{chunk-YBZT7MJR.js → chunk-L4RT57WU.js} +7 -7
  23. package/dist/{chunk-2WZ635SS.js → chunk-MU4VPAMR.js} +2 -2
  24. package/dist/{chunk-3JCMO7SD.js → chunk-N55DVMAG.js} +1 -1
  25. package/dist/{chunk-EIOJNUPL.js → chunk-PNLJIPV5.js} +1 -1
  26. package/dist/{chunk-6Z3G4U2R.js → chunk-Q5BEODAT.js} +3 -2
  27. package/dist/{chunk-M2HWJY6O.js → chunk-QB2CC4VH.js} +2045 -243
  28. package/dist/{chunk-QULLZ5PE.js → chunk-RSG2O3HF.js} +893 -642
  29. package/dist/{chunk-M25JL54Z.js → chunk-RYTEQBAO.js} +1 -1
  30. package/dist/{chunk-XUYBOO32.js → chunk-U36X6NZM.js} +15 -7
  31. package/dist/{chunk-QSTH6T77.js → chunk-VKMNWHTL.js} +1 -1
  32. package/dist/{chunk-KLFIJDTD.js → chunk-VOVZLL23.js} +4 -4
  33. package/dist/{chunk-SLH2QLKV.js → chunk-WFTNY755.js} +1 -1
  34. package/dist/{chunk-DK2HVHCY.js → chunk-WKJ47GIW.js} +1 -1
  35. package/dist/chunk-X4WBGKJQ.js +138 -0
  36. package/dist/{chunk-QHSW4WHM.js → chunk-XTVZFT7U.js} +1 -1
  37. package/dist/components/index.css +11 -0
  38. package/dist/components/index.d.ts +478 -386
  39. package/dist/components/index.js +138 -267
  40. package/dist/components/navigation/index.css +11 -0
  41. package/dist/components/navigation/index.js +8 -8
  42. package/dist/features/auth/index.css +11 -0
  43. package/dist/features/auth/index.d.ts +369 -363
  44. package/dist/features/auth/index.js +7 -7
  45. package/dist/features/crm/index.css +11 -0
  46. package/dist/features/crm/index.d.ts +551 -360
  47. package/dist/features/crm/index.js +19 -19
  48. package/dist/features/dashboard/index.css +11 -0
  49. package/dist/features/dashboard/index.js +20 -20
  50. package/dist/features/delivery/index.css +11 -0
  51. package/dist/features/delivery/index.d.ts +363 -357
  52. package/dist/features/delivery/index.js +19 -19
  53. package/dist/features/lead-gen/index.css +11 -0
  54. package/dist/features/lead-gen/index.d.ts +174 -4
  55. package/dist/features/lead-gen/index.js +20 -20
  56. package/dist/features/monitoring/index.css +11 -0
  57. package/dist/features/monitoring/index.js +21 -21
  58. package/dist/features/monitoring/requests/index.css +11 -0
  59. package/dist/features/monitoring/requests/index.js +17 -17
  60. package/dist/features/operations/index.css +11 -0
  61. package/dist/features/operations/index.js +24 -24
  62. package/dist/features/seo/index.js +2 -2
  63. package/dist/features/settings/index.css +11 -0
  64. package/dist/features/settings/index.d.ts +369 -363
  65. package/dist/features/settings/index.js +19 -19
  66. package/dist/hooks/delivery/index.css +11 -0
  67. package/dist/hooks/delivery/index.d.ts +363 -357
  68. package/dist/hooks/delivery/index.js +2 -2
  69. package/dist/hooks/index.css +11 -0
  70. package/dist/hooks/index.d.ts +769 -401
  71. package/dist/hooks/index.js +17 -17
  72. package/dist/hooks/published.css +11 -0
  73. package/dist/hooks/published.d.ts +769 -401
  74. package/dist/hooks/published.js +17 -17
  75. package/dist/index.css +11 -0
  76. package/dist/index.d.ts +885 -383
  77. package/dist/index.js +17 -17
  78. package/dist/initialization/index.d.ts +369 -363
  79. package/dist/initialization/index.js +4 -4
  80. package/dist/layout/index.d.ts +6 -0
  81. package/dist/layout/index.js +5 -5
  82. package/dist/organization/index.css +11 -0
  83. package/dist/organization/index.js +4 -4
  84. package/dist/profile/index.d.ts +369 -363
  85. package/dist/profile/index.js +2 -2
  86. package/dist/provider/ElevasisServiceContext.d.ts +1 -0
  87. package/dist/provider/ElevasisServiceContext.js +1 -1
  88. package/dist/provider/index.css +11 -0
  89. package/dist/provider/index.d.ts +503 -362
  90. package/dist/provider/index.js +14 -14
  91. package/dist/provider/published.css +11 -0
  92. package/dist/provider/published.d.ts +494 -359
  93. package/dist/provider/published.js +12 -12
  94. package/dist/supabase/index.d.ts +369 -357
  95. package/dist/test-utils/index.js +1 -1
  96. package/dist/typeform/index.js +49 -20
  97. package/dist/types/index.d.ts +382 -363
  98. package/package.json +2 -2
  99. package/dist/chunk-CEWTOKE7.js +0 -109
  100. package/dist/chunk-OWHQ65EQ.js +0 -211
  101. package/dist/chunk-UDJE54WN.js +0 -209
  102. /package/dist/{chunk-IRW7JMQ4.js → chunk-5WWZXCS5.js} +0 -0
  103. /package/dist/{chunk-QJ2KCHKX.js → chunk-E565XMTQ.js} +0 -0
@@ -1,23 +1,24 @@
1
1
  import { PageContainer } from './chunk-BZZCNLT6.js';
2
2
  import { TableSelectionToolbar, SortableHeader } from './chunk-TUMSNGTX.js';
3
- import { SubshellNavItem } from './chunk-CEWTOKE7.js';
3
+ import { SubshellNavItem } from './chunk-X4WBGKJQ.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 { 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';
7
+ import { useDealTasksDue, useDealsLookup, useCreateDealTask, useDealsSummary, useDeleteDeal, usePaginationState, useDeals, useTableSort, sortData, useTableSelection, useDealDetail, useCompany, dealKeys, useExecuteAction } from './chunk-RSG2O3HF.js';
8
+ import { showApiErrorNotification, showSuccessNotification } from './chunk-HKBEURCV.js';
9
+ import { useCrmActions, DEFAULT_CRM_PRIORITY_RULE_CONFIG, deriveActions, CRM_PRIORITY_BUCKETS } from './chunk-JMI7L7Y7.js';
9
10
  import { SubshellContentContainer } from './chunk-TKAYX2SP.js';
10
- import { CenteredErrorState, CardHeader, PageTitleCaption, EmptyState, ActivityTimeline } from './chunk-XUYBOO32.js';
11
+ import { CenteredErrorState, CardHeader, StatCard, PageTitleCaption, EmptyState, ActivityTimeline } from './chunk-U36X6NZM.js';
11
12
  import { useRouterContext } from './chunk-Q7DJKLEN.js';
12
13
  import { PAGE_SIZE_DEFAULT, formatTimeAgo } from './chunk-SGXXJE52.js';
13
- import { useElevasisServices } from './chunk-IRW7JMQ4.js';
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';
14
+ import { useElevasisServices } from './chunk-5WWZXCS5.js';
15
+ import { Box, Stack, Group, Text, Badge, Center, Loader, UnstyledButton, Button, Modal, Title, Select, TextInput, Textarea, Paper, Alert, Table, SimpleGrid, Tabs, Checkbox, Tooltip, Pagination, Code, Card, Switch, NumberInput, ColorSwatch, Popover, ActionIcon, CopyButton, Divider } from '@mantine/core';
16
+ import { IconLayoutGrid, IconColumns, IconFileInvoice, IconAddressBook, IconTrophy, IconClockExclamation, IconUser, IconPlus, IconChecklist, IconHistory, IconAlertCircle, IconCurrencyDollar, IconChartBar, IconBriefcase, IconSearch, IconTargetArrow, IconAlertTriangle, IconMessages, IconArrowLeft, IconFileText, IconInbox, IconBolt, IconBuilding, IconSettings, IconRestore, IconInfoCircle, IconMailForward, IconCheckbox, IconCalendar, IconMail, IconPhone, IconArrowRight, IconNote, IconCheck, IconCopy } from '@tabler/icons-react';
16
17
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
17
18
  import { useState, useMemo, useEffect } from 'react';
18
- import { useForm } from '@mantine/form';
19
- import { useQuery } from '@tanstack/react-query';
19
+ import { useQuery, useQueryClient, useMutation } from '@tanstack/react-query';
20
20
  import { useNavigate } from '@tanstack/react-router';
21
+ import { useForm } from '@mantine/form';
21
22
 
22
23
  var CrmSidebarTop = () => {
23
24
  return /* @__PURE__ */ jsx(SubshellSidebarSection, { icon: IconAddressBook, label: "CRM" });
@@ -45,329 +46,138 @@ var SAVED_VIEW_PRESETS = [
45
46
  urlFilters: { stage: "closed_won" }
46
47
  }
47
48
  ];
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 };
49
+
50
+ // src/features/crm/pages/shared.ts
51
+ var DEAL_REFERRER_STORAGE_KEY = "crm:dealReferrer";
52
+ function setDealReferrer(referrer) {
53
+ if (typeof window === "undefined") return;
54
+ try {
55
+ window.sessionStorage.setItem(DEAL_REFERRER_STORAGE_KEY, referrer);
56
+ } catch {
83
57
  }
84
- return { schema, required: true, nullable: false, defaultValue: void 0 };
85
58
  }
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;
59
+ function getDealReferrer() {
60
+ if (typeof window === "undefined") return "deals";
61
+ try {
62
+ const value = window.sessionStorage.getItem(DEAL_REFERRER_STORAGE_KEY);
63
+ return value === "pipeline" ? "pipeline" : "deals";
64
+ } catch {
65
+ return "deals";
95
66
  }
96
- return shape;
97
67
  }
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;
68
+ var DEAL_STAGE_COLORS = {
69
+ interested: "blue",
70
+ proposal: "yellow",
71
+ closing: "orange",
72
+ closed_won: "green",
73
+ closed_lost: "red",
74
+ nurturing: "grape"
75
+ };
76
+ var DEAL_STAGE_OPTIONS = [
77
+ { value: "interested", label: "Interested" },
78
+ { value: "proposal", label: "Proposal" },
79
+ { value: "closing", label: "Closing" },
80
+ { value: "closed_won", label: "Closed Won" },
81
+ { value: "closed_lost", label: "Closed Lost" },
82
+ { value: "nurturing", label: "Nurturing" }
83
+ ];
84
+ var DEAL_PRIORITY_OPTIONS = [
85
+ { value: "needs_response", label: "Needs response" },
86
+ { value: "follow_up_due", label: "Follow-up due" },
87
+ { value: "waiting", label: "Waiting" },
88
+ { value: "stale", label: "Stale" },
89
+ { value: "closed_low", label: "Closed low" }
90
+ ];
91
+ function compareDealsByPriority(a, b, direction = "asc") {
92
+ const rankComparison = comparePriorityRank(a.priority, b.priority, direction);
93
+ if (rankComparison !== 0) return rankComparison;
94
+ const aActivity = getPriorityActivityTimestamp(a);
95
+ const bActivity = getPriorityActivityTimestamp(b);
96
+ return bActivity - aActivity;
105
97
  }
106
- function labelForField(name) {
107
- return name.replace(/[_-]+/g, " ").replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/^./, (s) => s.toUpperCase());
98
+ function compareDealsByPriorityThenUpdated(a, b, direction = "asc") {
99
+ const rankComparison = comparePriorityRank(a.priority, b.priority, direction);
100
+ if (rankComparison !== 0) return rankComparison;
101
+ return getUpdatedTimestamp(b) - getUpdatedTimestamp(a);
108
102
  }
109
- function isLongTextField(name) {
110
- return ["body", "message", "reply", "replyBody", "emailBody"].includes(name);
103
+ function formatDealStageLabel(stage) {
104
+ return formatDealWorkflowLabel(stage);
111
105
  }
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
- }
106
+ function formatDealStateLabel(state) {
107
+ return formatDealWorkflowLabel(state);
131
108
  }
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 };
109
+ function formatDealWorkflowLabel(value) {
110
+ const words = value?.trim().split("_").map((word) => word.trim().toLowerCase()).filter(Boolean);
111
+ if (!words?.length) return "Unknown";
112
+ return words.map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
189
113
  }
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));
114
+ function comparePriorityRank(a, b, direction) {
115
+ const comparison = a.rank - b.rank;
116
+ return direction === "asc" ? comparison : -comparison;
117
+ }
118
+ function getPriorityActivityTimestamp(deal) {
119
+ const value = deal.priority.latestActivityAt ?? deal.updated_at;
120
+ const timestamp = new Date(value).getTime();
121
+ return Number.isNaN(timestamp) ? 0 : timestamp;
122
+ }
123
+ function getUpdatedTimestamp(deal) {
124
+ const timestamp = new Date(deal.updated_at).getTime();
125
+ return Number.isNaN(timestamp) ? 0 : timestamp;
126
+ }
127
+ var BOOKING_PAGE_URL = "https://elevasis.io/inbound/book";
128
+ function buildBookingUrl(opts = {}) {
129
+ const params = new URLSearchParams();
130
+ if (opts.dealId) params.set("dealId", opts.dealId);
131
+ if (opts.contactId) params.set("contactId", opts.contactId);
132
+ return params.size > 0 ? `${BOOKING_PAGE_URL}?${params.toString()}` : BOOKING_PAGE_URL;
133
+ }
134
+ var KIND_ICONS = {
135
+ call: IconPhone,
136
+ email: IconMail,
137
+ meeting: IconCalendar,
138
+ other: IconCheckbox
139
+ };
140
+ function formatDueLabel(dueAt) {
141
+ if (!dueAt) return "";
142
+ const date = new Date(dueAt);
143
+ const now = /* @__PURE__ */ new Date();
144
+ const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
145
+ const dueDay = new Date(date.getFullYear(), date.getMonth(), date.getDate());
146
+ const diffDays = Math.round((dueDay.getTime() - today.getTime()) / 864e5);
147
+ if (diffDays < 0) return `${Math.abs(diffDays)}d overdue`;
148
+ if (diffDays === 0) {
149
+ return date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
200
150
  }
201
- return value;
151
+ return `in ${diffDays}d`;
202
152
  }
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;
153
+ function TaskRow({ task, onClick }) {
154
+ const KindIcon = KIND_ICONS[task.kind];
155
+ const dueLabel = formatDueLabel(task.dueAt);
156
+ const isOverdue = task.dueAt !== null && new Date(task.dueAt) < /* @__PURE__ */ new Date();
157
+ return /* @__PURE__ */ jsx(
158
+ UnstyledButton,
159
+ {
160
+ onClick,
161
+ style: {
162
+ display: "block",
163
+ width: "100%",
164
+ padding: "4px 6px",
165
+ borderRadius: "var(--mantine-radius-sm)",
166
+ transition: `background-color var(--duration-fast) var(--easing)`
167
+ },
168
+ onMouseEnter: (e) => {
169
+ e.currentTarget.style.backgroundColor = "var(--color-surface-hover)";
170
+ },
171
+ onMouseLeave: (e) => {
172
+ e.currentTarget.style.backgroundColor = "transparent";
173
+ },
174
+ children: /* @__PURE__ */ jsxs(Group, { gap: "xs", wrap: "nowrap", children: [
175
+ /* @__PURE__ */ jsx(KindIcon, { size: 14, style: { color: "var(--color-text-dimmed)", flexShrink: 0 } }),
176
+ /* @__PURE__ */ jsx(Text, { size: "xs", truncate: true, style: { flex: 1, minWidth: 0 }, children: task.title }),
177
+ dueLabel && /* @__PURE__ */ jsx(Badge, { size: "xs", variant: "light", color: isOverdue ? "red" : "gray", style: { flexShrink: 0 }, children: dueLabel })
178
+ ] })
208
179
  }
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
- }
324
- var KIND_ICONS = {
325
- call: IconPhone,
326
- email: IconMail,
327
- meeting: IconCalendar,
328
- other: IconCheckbox
329
- };
330
- function formatDueLabel(dueAt) {
331
- if (!dueAt) return "";
332
- const date = new Date(dueAt);
333
- const now = /* @__PURE__ */ new Date();
334
- const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
335
- const dueDay = new Date(date.getFullYear(), date.getMonth(), date.getDate());
336
- const diffDays = Math.round((dueDay.getTime() - today.getTime()) / 864e5);
337
- if (diffDays < 0) return `${Math.abs(diffDays)}d overdue`;
338
- if (diffDays === 0) {
339
- return date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
340
- }
341
- return `in ${diffDays}d`;
342
- }
343
- function TaskRow({ task, onClick }) {
344
- const KindIcon = KIND_ICONS[task.kind];
345
- const dueLabel = formatDueLabel(task.dueAt);
346
- const isOverdue = task.dueAt !== null && new Date(task.dueAt) < /* @__PURE__ */ new Date();
347
- return /* @__PURE__ */ jsx(
348
- UnstyledButton,
349
- {
350
- onClick,
351
- style: {
352
- display: "block",
353
- width: "100%",
354
- padding: "4px 6px",
355
- borderRadius: "var(--mantine-radius-sm)",
356
- transition: `background-color var(--duration-fast) var(--easing)`
357
- },
358
- onMouseEnter: (e) => {
359
- e.currentTarget.style.backgroundColor = "var(--color-surface-hover)";
360
- },
361
- onMouseLeave: (e) => {
362
- e.currentTarget.style.backgroundColor = "transparent";
363
- },
364
- children: /* @__PURE__ */ jsxs(Group, { gap: "xs", wrap: "nowrap", children: [
365
- /* @__PURE__ */ jsx(KindIcon, { size: 14, style: { color: "var(--color-text-dimmed)", flexShrink: 0 } }),
366
- /* @__PURE__ */ jsx(Text, { size: "xs", truncate: true, style: { flex: 1, minWidth: 0 }, children: task.title }),
367
- dueLabel && /* @__PURE__ */ jsx(Badge, { size: "xs", variant: "light", color: isOverdue ? "red" : "gray", style: { flexShrink: 0 }, children: dueLabel })
368
- ] })
369
- }
370
- );
180
+ );
371
181
  }
372
182
  function MyTasksPanel({
373
183
  onTaskClick,
@@ -571,6 +381,7 @@ var CrmSidebarMiddle = ({ items = CRM_ITEMS } = {}) => {
571
381
  icon: item.icon,
572
382
  label: item.label,
573
383
  isActive,
384
+ href: item.to,
574
385
  onClick: () => navigate(item.to)
575
386
  },
576
387
  item.to
@@ -659,35 +470,6 @@ function useRecentCrmActivity(opts) {
659
470
  error: query.error
660
471
  };
661
472
  }
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
- }
691
473
  var STAGE_LABELS = {
692
474
  interested: "Interested",
693
475
  proposal: "Proposal",
@@ -793,10 +575,10 @@ function ActivityFeedWidget({ onDealClick, limit }) {
793
575
  }
794
576
  return /* @__PURE__ */ jsx(Paper, { withBorder: true, p: "md", children: /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
795
577
  /* @__PURE__ */ jsx(CardHeader, { icon: /* @__PURE__ */ jsx(IconHistory, { size: 16 }), title: "Recent Activity" }),
796
- /* @__PURE__ */ jsxs(Table, { highlightOnHover: true, children: [
578
+ /* @__PURE__ */ jsxs(Table, { highlightOnHover: true, style: { tableLayout: "fixed", width: "100%" }, children: [
797
579
  /* @__PURE__ */ jsx(Table.Thead, { children: /* @__PURE__ */ jsxs(Table.Tr, { children: [
798
580
  /* @__PURE__ */ jsx(Table.Th, { w: 40 }),
799
- /* @__PURE__ */ jsx(Table.Th, { children: "Name" }),
581
+ /* @__PURE__ */ jsx(Table.Th, { w: 220, children: "Name" }),
800
582
  /* @__PURE__ */ jsx(Table.Th, { children: "Activity" }),
801
583
  /* @__PURE__ */ jsx(Table.Th, { w: 120, children: "When" })
802
584
  ] }) }),
@@ -804,8 +586,8 @@ function ActivityFeedWidget({ onDealClick, limit }) {
804
586
  const name = entry.contactName ?? entry.companyName ?? "Unknown";
805
587
  return /* @__PURE__ */ jsxs(Table.Tr, { style: { cursor: "pointer" }, onClick: () => onDealClick(entry.dealId), children: [
806
588
  /* @__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 }) }),
589
+ /* @__PURE__ */ jsx(Table.Td, { style: { maxWidth: 220 }, children: /* @__PURE__ */ jsx(Text, { size: "sm", fw: 500, truncate: true, title: name, style: { minWidth: 0 }, children: name }) }),
590
+ /* @__PURE__ */ jsx(Table.Td, { style: { minWidth: 0 }, children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", truncate: true, title: entry.description, style: { minWidth: 0 }, children: entry.description }) }),
809
591
  /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", style: { whiteSpace: "nowrap" }, children: formatRelativeTime(entry.occurredAt) }) })
810
592
  ] }, entry.id);
811
593
  }) })
@@ -820,20 +602,50 @@ var currencyFormatter = new Intl.NumberFormat("en-US", {
820
602
  function formatPercent(value) {
821
603
  return `${Math.round(value * 100)}%`;
822
604
  }
823
- function StatTile({ label, value }) {
824
- return /* @__PURE__ */ jsx(Card, { padding: "sm", children: /* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
825
- /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: label }),
826
- /* @__PURE__ */ jsx(Text, { fw: 700, size: "xl", children: value })
827
- ] }) });
828
- }
829
605
  function MetricsStrip() {
830
- const { data } = useCrmQuickMetrics();
831
- return /* @__PURE__ */ jsx(Paper, { withBorder: true, p: "md", children: /* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 2, sm: 4 }, children: [
832
- /* @__PURE__ */ jsx(StatTile, { label: "Total Pipeline Value", value: currencyFormatter.format(data.totalPipelineValue) }),
833
- /* @__PURE__ */ jsx(StatTile, { label: "Win Rate", value: formatPercent(data.winRate) }),
834
- /* @__PURE__ */ jsx(StatTile, { label: "Open Deals", value: String(data.openDeals) }),
835
- /* @__PURE__ */ jsx(StatTile, { label: "Won This Period", value: String(data.wonDeals) })
836
- ] }) });
606
+ const { data, isLoading } = useCrmQuickMetrics();
607
+ return /* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 2, sm: 4 }, children: [
608
+ /* @__PURE__ */ jsx(
609
+ StatCard,
610
+ {
611
+ variant: "hero",
612
+ icon: IconCurrencyDollar,
613
+ value: currencyFormatter.format(data.totalPipelineValue),
614
+ label: "Total Pipeline Value",
615
+ isLoading
616
+ }
617
+ ),
618
+ /* @__PURE__ */ jsx(
619
+ StatCard,
620
+ {
621
+ variant: "hero",
622
+ icon: IconChartBar,
623
+ value: formatPercent(data.winRate),
624
+ label: "Win Rate",
625
+ isLoading
626
+ }
627
+ ),
628
+ /* @__PURE__ */ jsx(
629
+ StatCard,
630
+ {
631
+ variant: "hero",
632
+ icon: IconBriefcase,
633
+ value: String(data.openDeals),
634
+ label: "Open Deals",
635
+ isLoading
636
+ }
637
+ ),
638
+ /* @__PURE__ */ jsx(
639
+ StatCard,
640
+ {
641
+ variant: "hero",
642
+ icon: IconTrophy,
643
+ value: String(data.wonDeals),
644
+ label: "Won This Period",
645
+ isLoading
646
+ }
647
+ )
648
+ ] });
837
649
  }
838
650
  function CrmOverview({
839
651
  onStageClick,
@@ -869,15 +681,30 @@ var sortAccessors = {
869
681
  contact: (deal) => [deal.contact?.first_name, deal.contact?.last_name].filter(Boolean).join(" ") || "",
870
682
  email: (deal) => deal.contact_email || "",
871
683
  stage: (deal) => deal.stage_key || "",
684
+ state: (deal) => deal.state_key || "",
685
+ ownership: (deal) => deal.ownership || "",
872
686
  updated: (deal) => deal.updated_at || ""
873
687
  };
688
+ var DEAL_OWNERSHIP_BADGE_COPY = {
689
+ us: {
690
+ color: "red",
691
+ label: "Our move",
692
+ tooltip: "We owe the next move"
693
+ },
694
+ them: {
695
+ color: "gray",
696
+ label: "Their move",
697
+ tooltip: "Lead owes the next move"
698
+ }
699
+ };
874
700
  function DealsListPage() {
875
701
  const navigate = useNavigate();
876
702
  const deleteDeal = useDeleteDeal();
877
703
  const [stageFilter, setStageFilter] = useState(null);
704
+ const [priorityFilter, setPriorityFilter] = useState("all");
878
705
  const [searchQuery, setSearchQuery] = useState("");
879
706
  const [showBatchDelete, setShowBatchDelete] = useState(false);
880
- const pagination = usePaginationState(PAGE_SIZE_DEFAULT, [stageFilter, searchQuery]);
707
+ const pagination = usePaginationState(PAGE_SIZE_DEFAULT, [stageFilter, priorityFilter, searchQuery]);
881
708
  const {
882
709
  data: deals,
883
710
  total,
@@ -889,8 +716,14 @@ function DealsListPage() {
889
716
  limit: PAGE_SIZE_DEFAULT,
890
717
  offset: pagination.offset
891
718
  });
892
- const { sort, toggleSort } = useTableSort("updated");
893
- const sortedDeals = useMemo(() => sortData(deals ?? [], sort, sortAccessors), [deals, sort]);
719
+ const { sort, toggleSort } = useTableSort("priority", "asc");
720
+ const sortedDeals = useMemo(() => {
721
+ const filteredDeals = priorityFilter === "all" ? deals ?? [] : (deals ?? []).filter((deal) => deal.priority.bucketKey === priorityFilter);
722
+ if (sort.column === "priority") {
723
+ return [...filteredDeals].sort((a, b) => compareDealsByPriorityThenUpdated(a, b, sort.direction));
724
+ }
725
+ return sortData(filteredDeals, sort, sortAccessors);
726
+ }, [deals, priorityFilter, sort]);
894
727
  const selection = useTableSelection(sortedDeals, sortedDeals);
895
728
  const handleDeleteSelected = async () => {
896
729
  await Promise.all([...selection.selectedIds].map((dealId) => deleteDeal.mutateAsync(dealId)));
@@ -939,7 +772,18 @@ function DealsListPage() {
939
772
  ]
940
773
  }
941
774
  ),
942
- isLoading ? /* @__PURE__ */ jsx(Center, { p: "xl", children: /* @__PURE__ */ jsx(Loader, {}) }) : error ? /* @__PURE__ */ jsx(CenteredErrorState, { error, title: "Failed to load deals" }) : !sortedDeals.length ? /* @__PURE__ */ jsx(EmptyState, { icon: IconTargetArrow, title: "No deals found" }) : /* @__PURE__ */ jsxs(Table, { children: [
775
+ /* @__PURE__ */ jsx(
776
+ Tabs,
777
+ {
778
+ value: priorityFilter,
779
+ onChange: (value) => setPriorityFilter(value ?? "all"),
780
+ children: /* @__PURE__ */ jsxs(Tabs.List, { children: [
781
+ /* @__PURE__ */ jsx(Tabs.Tab, { value: "all", children: "All priorities" }),
782
+ DEAL_PRIORITY_OPTIONS.map((option) => /* @__PURE__ */ jsx(Tabs.Tab, { value: option.value, children: option.label }, option.value))
783
+ ] })
784
+ }
785
+ ),
786
+ isLoading ? /* @__PURE__ */ jsx(Center, { p: "xl", children: /* @__PURE__ */ jsx(Loader, {}) }) : error ? /* @__PURE__ */ jsx(CenteredErrorState, { error, title: "Failed to load deals" }) : !sortedDeals.length ? /* @__PURE__ */ jsx(EmptyState, { icon: IconTargetArrow, title: "No deals found" }) : /* @__PURE__ */ jsxs(Table, { style: { tableLayout: "fixed" }, children: [
943
787
  /* @__PURE__ */ jsx(Table.Thead, { children: /* @__PURE__ */ jsxs(Table.Tr, { children: [
944
788
  /* @__PURE__ */ jsx(Table.Th, { w: 40, children: /* @__PURE__ */ jsx(
945
789
  Checkbox,
@@ -952,333 +796,776 @@ function DealsListPage() {
952
796
  /* @__PURE__ */ jsx(SortableHeader, { column: "company", sort, onToggle: toggleSort, children: "Company" }),
953
797
  /* @__PURE__ */ jsx(SortableHeader, { column: "contact", sort, onToggle: toggleSort, children: "Contact" }),
954
798
  /* @__PURE__ */ jsx(SortableHeader, { column: "email", sort, onToggle: toggleSort, children: "Email" }),
955
- /* @__PURE__ */ jsx(SortableHeader, { column: "stage", sort, onToggle: toggleSort, children: "Stage" }),
956
- /* @__PURE__ */ jsx(SortableHeader, { column: "updated", sort, onToggle: toggleSort, children: "Updated" })
799
+ /* @__PURE__ */ jsx(SortableHeader, { column: "stage", sort, onToggle: toggleSort, w: 120, children: "Stage" }),
800
+ /* @__PURE__ */ jsx(SortableHeader, { column: "state", sort, onToggle: toggleSort, w: 160, children: "State" }),
801
+ /* @__PURE__ */ jsx(SortableHeader, { column: "ownership", sort, onToggle: toggleSort, w: 100, children: "Move" }),
802
+ /* @__PURE__ */ jsx(SortableHeader, { column: "priority", sort, onToggle: toggleSort, w: 140, children: "Priority" }),
803
+ /* @__PURE__ */ jsx(SortableHeader, { column: "updated", sort, onToggle: toggleSort, w: 100, children: "Updated" })
957
804
  ] }) }),
958
805
  /* @__PURE__ */ jsx(Table.Tbody, { children: sortedDeals.map((deal) => {
959
806
  const discoveryData = deal.discovery_data;
960
807
  const companyName = deal.contact?.company?.name || discoveryData?.company || deal.contact_email?.split("@")[1] || "-";
961
808
  const contactName = [deal.contact?.first_name, deal.contact?.last_name].filter(Boolean).join(" ");
809
+ const ownershipBadge = deal.ownership ? DEAL_OWNERSHIP_BADGE_COPY[deal.ownership] : null;
962
810
  return /* @__PURE__ */ jsxs(
963
811
  Table.Tr,
964
812
  {
965
- style: { cursor: "pointer" },
966
- onClick: () => navigate({
967
- to: "/crm/deals/$dealId",
968
- params: { dealId: deal.id }
969
- }),
970
- children: [
971
- /* @__PURE__ */ jsx(Table.Td, { onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsx(
972
- Checkbox,
813
+ style: { cursor: "pointer" },
814
+ onClick: () => {
815
+ setDealReferrer("deals");
816
+ navigate({
817
+ to: "/crm/deals/$dealId",
818
+ params: { dealId: deal.id }
819
+ });
820
+ },
821
+ children: [
822
+ /* @__PURE__ */ jsx(Table.Td, { onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsx(
823
+ Checkbox,
824
+ {
825
+ checked: selection.isSelected(deal.id),
826
+ onChange: () => selection.toggle(deal.id)
827
+ }
828
+ ) }),
829
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { fw: 500, lineClamp: 1, children: companyName }) }),
830
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", lineClamp: 1, children: contactName || "-" }) }),
831
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", lineClamp: 1, children: deal.contact_email || "-" }) }),
832
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Badge, { variant: "light", color: DEAL_STAGE_COLORS[deal.stage_key || ""] || "gray", size: "sm", children: formatDealStageLabel(deal.stage_key) }) }),
833
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(
834
+ Badge,
835
+ {
836
+ variant: "light",
837
+ color: deal.state_key ? "gray" : "dark",
838
+ size: "sm",
839
+ style: { maxWidth: "100%", textTransform: "none" },
840
+ children: /* @__PURE__ */ jsx(Text, { span: true, size: "xs", truncate: "end", style: { maxWidth: "100%" }, children: formatDealStateLabel(deal.state_key) })
841
+ }
842
+ ) }),
843
+ /* @__PURE__ */ jsx(Table.Td, { children: ownershipBadge ? /* @__PURE__ */ jsx(Tooltip, { label: ownershipBadge.tooltip, withArrow: true, children: /* @__PURE__ */ jsx(
844
+ Badge,
845
+ {
846
+ variant: "light",
847
+ color: ownershipBadge.color,
848
+ size: "sm",
849
+ style: { maxWidth: "100%", textTransform: "none" },
850
+ children: /* @__PURE__ */ jsx(Text, { span: true, size: "xs", truncate: "end", style: { maxWidth: "100%" }, children: ownershipBadge.label })
851
+ }
852
+ ) }) : /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "-" }) }),
853
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Tooltip, { label: deal.priority.reason, withArrow: true, children: /* @__PURE__ */ jsx(
854
+ Badge,
855
+ {
856
+ variant: "light",
857
+ color: deal.priority.color,
858
+ size: "sm",
859
+ style: { maxWidth: "100%", textTransform: "none" },
860
+ children: /* @__PURE__ */ jsx(Text, { span: true, size: "xs", truncate: "end", style: { maxWidth: "100%" }, children: deal.priority.label })
861
+ }
862
+ ) }) }),
863
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", lineClamp: 1, children: formatTimeAgo(deal.updated_at) }) })
864
+ ]
865
+ },
866
+ deal.id
867
+ );
868
+ }) })
869
+ ] }),
870
+ total > PAGE_SIZE_DEFAULT && /* @__PURE__ */ jsx(Group, { justify: "flex-start", children: /* @__PURE__ */ jsx(
871
+ Pagination,
872
+ {
873
+ value: pagination.page,
874
+ onChange: pagination.setPage,
875
+ total: pagination.totalPages(total),
876
+ size: "sm"
877
+ }
878
+ ) })
879
+ ] }) })
880
+ ] }),
881
+ /* @__PURE__ */ jsx(
882
+ CustomModal,
883
+ {
884
+ opened: showBatchDelete,
885
+ onClose: () => !deleteDeal.isPending && setShowBatchDelete(false),
886
+ size: "sm",
887
+ loading: deleteDeal.isPending,
888
+ children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
889
+ /* @__PURE__ */ jsxs(Group, { gap: "sm", children: [
890
+ /* @__PURE__ */ jsx(IconAlertTriangle, { size: 24, color: "var(--color-error)" }),
891
+ /* @__PURE__ */ jsxs(Title, { order: 4, children: [
892
+ "Delete ",
893
+ selection.selectedCount,
894
+ " Deals"
895
+ ] })
896
+ ] }),
897
+ /* @__PURE__ */ jsxs(Text, { size: "sm", children: [
898
+ "Are you sure you want to delete",
899
+ " ",
900
+ /* @__PURE__ */ jsx(Text, { span: true, fw: 600, children: selection.selectedCount }),
901
+ " ",
902
+ "selected deals?"
903
+ ] }),
904
+ /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "This action cannot be undone." }),
905
+ /* @__PURE__ */ jsxs(Group, { justify: "flex-end", mt: "md", children: [
906
+ /* @__PURE__ */ jsx(Button, { variant: "light", onClick: () => setShowBatchDelete(false), disabled: deleteDeal.isPending, children: "Cancel" }),
907
+ /* @__PURE__ */ jsx(Button, { color: "red", loading: deleteDeal.isPending, onClick: () => void handleDeleteSelected(), children: "Delete" })
908
+ ] })
909
+ ] })
910
+ }
911
+ )
912
+ ] });
913
+ }
914
+ function ActionButton({ action, dealId }) {
915
+ const executeAction = useExecuteAction({ dealId });
916
+ return /* @__PURE__ */ jsx(
917
+ Button,
918
+ {
919
+ variant: "light",
920
+ size: "sm",
921
+ loading: executeAction.isPending,
922
+ onClick: () => executeAction.mutate({ key: action.key }),
923
+ children: action.label
924
+ }
925
+ );
926
+ }
927
+ function getType(schema) {
928
+ const zodSchema = schema;
929
+ return zodSchema._def?.type ?? zodSchema._def?.typeName ?? "";
930
+ }
931
+ function unwrapField(schema) {
932
+ const type = getType(schema);
933
+ if (type === "optional" || type === "ZodOptional") {
934
+ const inner = schema._def?.innerType;
935
+ if (!inner) return { schema, required: false, nullable: false, defaultValue: void 0 };
936
+ return { ...unwrapField(inner), required: false };
937
+ }
938
+ if (type === "nullable" || type === "ZodNullable") {
939
+ const inner = schema._def?.innerType;
940
+ if (!inner) return { schema, required: true, nullable: true, defaultValue: null };
941
+ return { ...unwrapField(inner), nullable: true };
942
+ }
943
+ if (type === "default" || type === "ZodDefault") {
944
+ const def = schema._def;
945
+ const inner = def?.innerType;
946
+ const defaultValue = typeof def?.defaultValue === "function" ? def.defaultValue() : def?.defaultValue;
947
+ if (!inner) return { schema, required: false, nullable: false, defaultValue };
948
+ return { ...unwrapField(inner), required: false, defaultValue };
949
+ }
950
+ return { schema, required: true, nullable: false, defaultValue: void 0 };
951
+ }
952
+ function getObjectShape(schema) {
953
+ const zodSchema = schema;
954
+ const type = getType(schema);
955
+ if (type !== "object" && type !== "ZodObject") {
956
+ return null;
957
+ }
958
+ const shape = zodSchema.shape;
959
+ if (!shape || typeof shape !== "object" || Array.isArray(shape)) {
960
+ return null;
961
+ }
962
+ return shape;
963
+ }
964
+ function getEnumValues(schema) {
965
+ const zodSchema = schema;
966
+ const values = zodSchema.options ?? zodSchema._def?.values ?? Object.values(zodSchema._def?.entries ?? {});
967
+ if (!Array.isArray(values) || values.some((value) => typeof value !== "string")) {
968
+ return null;
969
+ }
970
+ return values;
971
+ }
972
+ function labelForField(name) {
973
+ return name.replace(/[_-]+/g, " ").replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/^./, (s) => s.toUpperCase());
974
+ }
975
+ function isLongTextField(name) {
976
+ return ["body", "message", "reply", "replyBody", "emailBody"].includes(name);
977
+ }
978
+ function initialValueFor(field, defaultValue) {
979
+ if (defaultValue !== void 0) {
980
+ if (field.kind === "date" && defaultValue instanceof Date) {
981
+ return defaultValue.toISOString().slice(0, 10);
982
+ }
983
+ return defaultValue;
984
+ }
985
+ switch (field.kind) {
986
+ case "boolean":
987
+ return false;
988
+ case "number":
989
+ return field.required ? 0 : "";
990
+ case "enum":
991
+ return field.required ? field.enumValues?.[0] ?? "" : "";
992
+ case "date":
993
+ case "dateString":
994
+ case "string":
995
+ return "";
996
+ }
997
+ }
998
+ function describeSchema(schema) {
999
+ if (!schema) {
1000
+ return { supported: false, reason: "This action does not define a payload schema." };
1001
+ }
1002
+ const shape = getObjectShape(schema);
1003
+ if (!shape) {
1004
+ return { supported: false, reason: "This action requires a payload schema that is not a flat object." };
1005
+ }
1006
+ const fields = [];
1007
+ const initialValues = {};
1008
+ for (const [name, rawFieldSchema] of Object.entries(shape)) {
1009
+ const { schema: fieldSchema, required, nullable, defaultValue } = unwrapField(rawFieldSchema);
1010
+ const type = getType(fieldSchema);
1011
+ const zodField = fieldSchema;
1012
+ const stringFormat = zodField._def?.format;
1013
+ let descriptor = null;
1014
+ if (type === "string" || type === "ZodString") {
1015
+ descriptor = {
1016
+ name,
1017
+ label: labelForField(name),
1018
+ kind: stringFormat === "date" ? "dateString" : "string",
1019
+ schema: rawFieldSchema,
1020
+ required,
1021
+ nullable
1022
+ };
1023
+ } else if (type === "number" || type === "ZodNumber") {
1024
+ descriptor = { name, label: labelForField(name), kind: "number", schema: rawFieldSchema, required, nullable };
1025
+ } else if (type === "boolean" || type === "ZodBoolean") {
1026
+ descriptor = { name, label: labelForField(name), kind: "boolean", schema: rawFieldSchema, required, nullable };
1027
+ } else if (type === "enum" || type === "ZodEnum") {
1028
+ const enumValues = getEnumValues(fieldSchema);
1029
+ if (!enumValues) {
1030
+ return { supported: false, reason: `Field "${name}" uses an enum shape that is not supported by this form.` };
1031
+ }
1032
+ descriptor = {
1033
+ name,
1034
+ label: labelForField(name),
1035
+ kind: "enum",
1036
+ schema: rawFieldSchema,
1037
+ required,
1038
+ nullable,
1039
+ enumValues
1040
+ };
1041
+ } else if (type === "date" || type === "ZodDate") {
1042
+ descriptor = { name, label: labelForField(name), kind: "date", schema: rawFieldSchema, required, nullable };
1043
+ }
1044
+ if (!descriptor) {
1045
+ return {
1046
+ supported: false,
1047
+ reason: `Field "${name}" is not supported. CRM action forms currently support only flat string, number, boolean, enum, and date fields.`
1048
+ };
1049
+ }
1050
+ const field = { ...descriptor, defaultValue: initialValueFor(descriptor, defaultValue) };
1051
+ fields.push(field);
1052
+ initialValues[name] = field.defaultValue;
1053
+ }
1054
+ return { supported: true, fields, initialValues };
1055
+ }
1056
+ function normalizeValue(field, value) {
1057
+ const isEmpty = value === "" || value === void 0 || value === null;
1058
+ if (isEmpty) {
1059
+ if (!field.required) return void 0;
1060
+ if (field.nullable) return null;
1061
+ return value;
1062
+ }
1063
+ if (field.kind === "date") {
1064
+ const acceptsString = (field.schema._def?.coerce ?? false) === true;
1065
+ return acceptsString ? value : new Date(String(value));
1066
+ }
1067
+ return value;
1068
+ }
1069
+ function buildPayload(fields, values) {
1070
+ return fields.reduce((payload, field) => {
1071
+ const value = normalizeValue(field, values[field.name]);
1072
+ if (value !== void 0 || field.required) {
1073
+ payload[field.name] = value;
1074
+ }
1075
+ return payload;
1076
+ }, {});
1077
+ }
1078
+ function ActionFormButton({ action, isActive, onToggle }) {
1079
+ return /* @__PURE__ */ jsx(Button, { variant: isActive ? "filled" : "light", size: "sm", onClick: onToggle, children: action.label });
1080
+ }
1081
+ function ActionForm({ action, dealId, onClose }) {
1082
+ const executeAction = useExecuteAction({ dealId });
1083
+ const schemaDescription = describeSchema(action.payloadSchema);
1084
+ const form = useForm({
1085
+ initialValues: schemaDescription.supported ? schemaDescription.initialValues : {}
1086
+ });
1087
+ const handleSubmit = form.onSubmit(async (values) => {
1088
+ if (!schemaDescription.supported || !action.payloadSchema) return;
1089
+ form.clearErrors();
1090
+ const payload = buildPayload(schemaDescription.fields, values);
1091
+ const parsed = action.payloadSchema.safeParse(payload);
1092
+ if (!parsed.success) {
1093
+ const formErrors = {};
1094
+ for (const issue of parsed.error.issues) {
1095
+ const fieldName = issue.path[0];
1096
+ if (typeof fieldName === "string" && schemaDescription.fields.some((field) => field.name === fieldName)) {
1097
+ formErrors[fieldName] = issue.message;
1098
+ }
1099
+ }
1100
+ form.setErrors(formErrors);
1101
+ return;
1102
+ }
1103
+ await executeAction.mutateAsync({ key: action.key, payload: parsed.data });
1104
+ onClose();
1105
+ });
1106
+ return /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
1107
+ /* @__PURE__ */ jsx(Divider, {}),
1108
+ /* @__PURE__ */ jsx(Title, { order: 3, children: action.label }),
1109
+ schemaDescription.supported ? /* @__PURE__ */ jsx("form", { onSubmit: handleSubmit, children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
1110
+ schemaDescription.fields.map((field) => {
1111
+ if (field.kind === "number") {
1112
+ return /* @__PURE__ */ jsx(
1113
+ NumberInput,
1114
+ {
1115
+ label: field.label,
1116
+ required: field.required,
1117
+ disabled: executeAction.isPending,
1118
+ error: form.errors[field.name],
1119
+ ...form.getInputProps(field.name)
1120
+ },
1121
+ field.name
1122
+ );
1123
+ }
1124
+ if (field.kind === "boolean") {
1125
+ return /* @__PURE__ */ jsx(
1126
+ Switch,
1127
+ {
1128
+ label: field.label,
1129
+ disabled: executeAction.isPending,
1130
+ error: form.errors[field.name],
1131
+ ...form.getInputProps(field.name, { type: "checkbox" })
1132
+ },
1133
+ field.name
1134
+ );
1135
+ }
1136
+ if (field.kind === "enum") {
1137
+ return /* @__PURE__ */ jsx(
1138
+ Select,
1139
+ {
1140
+ label: field.label,
1141
+ required: field.required,
1142
+ disabled: executeAction.isPending,
1143
+ data: field.enumValues ?? [],
1144
+ error: form.errors[field.name],
1145
+ ...form.getInputProps(field.name)
1146
+ },
1147
+ field.name
1148
+ );
1149
+ }
1150
+ if (field.kind === "string" && isLongTextField(field.name)) {
1151
+ return /* @__PURE__ */ jsx(
1152
+ Textarea,
1153
+ {
1154
+ label: field.label,
1155
+ required: field.required,
1156
+ disabled: executeAction.isPending,
1157
+ error: form.errors[field.name],
1158
+ autosize: true,
1159
+ minRows: 6,
1160
+ ...form.getInputProps(field.name)
1161
+ },
1162
+ field.name
1163
+ );
1164
+ }
1165
+ return /* @__PURE__ */ jsx(
1166
+ TextInput,
1167
+ {
1168
+ label: field.label,
1169
+ type: field.kind === "date" || field.kind === "dateString" ? "date" : "text",
1170
+ required: field.required,
1171
+ disabled: executeAction.isPending,
1172
+ error: form.errors[field.name],
1173
+ ...form.getInputProps(field.name)
1174
+ },
1175
+ field.name
1176
+ );
1177
+ }),
1178
+ /* @__PURE__ */ jsxs(Group, { justify: "flex-end", gap: "sm", children: [
1179
+ /* @__PURE__ */ jsx(Button, { variant: "default", onClick: onClose, disabled: executeAction.isPending, children: "Cancel" }),
1180
+ /* @__PURE__ */ jsx(Button, { type: "submit", loading: executeAction.isPending, children: action.label })
1181
+ ] })
1182
+ ] }) }) : /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
1183
+ /* @__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 }) }),
1184
+ /* @__PURE__ */ jsx(Group, { justify: "flex-end", children: /* @__PURE__ */ jsx(Button, { variant: "default", onClick: onClose, disabled: executeAction.isPending, children: "Close" }) })
1185
+ ] })
1186
+ ] });
1187
+ }
1188
+ var THREAD_INDENT_STEP = 6;
1189
+ var THREAD_MAX_INDENT = 36;
1190
+ function formatSentAt(sentAt) {
1191
+ if (!sentAt) return "Unknown time";
1192
+ const date = new Date(sentAt);
1193
+ if (Number.isNaN(date.getTime())) return sentAt;
1194
+ return date.toLocaleString();
1195
+ }
1196
+ function getQuoteDepth(line) {
1197
+ const match = line.match(/^\s*((?:>\s*)+)(.*)$/);
1198
+ if (!match) return { depth: 0, text: line };
1199
+ return {
1200
+ depth: (match[1].match(/>/g) ?? []).length,
1201
+ text: match[2]
1202
+ };
1203
+ }
1204
+ function MessageBody({ body }) {
1205
+ const resolvedBody = body || "Full message body unavailable from Instantly.";
1206
+ return /* @__PURE__ */ jsx(Stack, { gap: 0, children: resolvedBody.split(/\r?\n/).map((line, index) => {
1207
+ const { depth, text } = getQuoteDepth(line);
1208
+ return /* @__PURE__ */ jsxs(
1209
+ Box,
1210
+ {
1211
+ style: {
1212
+ display: "grid",
1213
+ gridTemplateColumns: `${depth * 12}px minmax(0, 1fr)`,
1214
+ minHeight: "1.55em"
1215
+ },
1216
+ children: [
1217
+ /* @__PURE__ */ jsx(
1218
+ Box,
1219
+ {
1220
+ "aria-hidden": "true",
1221
+ style: {
1222
+ display: "grid",
1223
+ gridTemplateColumns: `repeat(${depth}, 12px)`
1224
+ },
1225
+ children: Array.from({ length: depth }).map((_, depthIndex) => /* @__PURE__ */ jsx(
1226
+ Box,
1227
+ {
1228
+ style: {
1229
+ borderLeft: "1px solid var(--color-border)",
1230
+ marginLeft: 4
1231
+ }
1232
+ },
1233
+ depthIndex
1234
+ ))
1235
+ }
1236
+ ),
1237
+ /* @__PURE__ */ jsx(Text, { size: "sm", lh: 1.55, style: { whiteSpace: "pre-wrap", overflowWrap: "anywhere" }, children: text || " " })
1238
+ ]
1239
+ },
1240
+ `${index}-${line}`
1241
+ );
1242
+ }) });
1243
+ }
1244
+ function ConversationThread({ messages }) {
1245
+ if (messages.length === 0) {
1246
+ return /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
1247
+ /* @__PURE__ */ jsx(CardHeader, { icon: /* @__PURE__ */ jsx(IconMessages, { size: 18 }), title: "Conversation", mb: "xs" }),
1248
+ /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "No synced email thread is available. Check Activity for local CRM events." })
1249
+ ] });
1250
+ }
1251
+ return /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
1252
+ /* @__PURE__ */ jsx(CardHeader, { icon: /* @__PURE__ */ jsx(IconMessages, { size: 18 }), title: "Conversation", mb: 0 }),
1253
+ [...messages].reverse().map((message, index) => {
1254
+ const isOutbound = message.direction === "outbound";
1255
+ const participant = isOutbound ? message.toEmail : message.fromEmail;
1256
+ const olderMessageIndent = Math.min(index * THREAD_INDENT_STEP, THREAD_MAX_INDENT);
1257
+ const olderMessageLevels = olderMessageIndent / THREAD_INDENT_STEP;
1258
+ const directionLabel = isOutbound ? "Sent to" : "Received from";
1259
+ const messageIcon = isOutbound ? /* @__PURE__ */ jsx(IconMailForward, { size: 16 }) : /* @__PURE__ */ jsx(IconInbox, { size: 16 });
1260
+ return /* @__PURE__ */ jsxs(
1261
+ Box,
1262
+ {
1263
+ style: {
1264
+ display: "grid",
1265
+ gridTemplateColumns: `${olderMessageIndent}px minmax(0, 1fr)`
1266
+ },
1267
+ children: [
1268
+ /* @__PURE__ */ jsx(
1269
+ Box,
1270
+ {
1271
+ "aria-hidden": "true",
1272
+ style: {
1273
+ display: "grid",
1274
+ gridTemplateColumns: `repeat(${olderMessageLevels}, ${THREAD_INDENT_STEP}px)`
1275
+ },
1276
+ children: Array.from({ length: olderMessageLevels }).map((_, levelIndex) => /* @__PURE__ */ jsx(
1277
+ Box,
1278
+ {
1279
+ style: {
1280
+ borderLeft: "1px solid var(--color-border)"
1281
+ }
1282
+ },
1283
+ levelIndex
1284
+ ))
1285
+ }
1286
+ ),
1287
+ /* @__PURE__ */ jsx(
1288
+ Box,
1289
+ {
1290
+ pl: "md",
1291
+ py: "xs",
1292
+ style: {
1293
+ borderLeft: `2px solid ${isOutbound ? "var(--mantine-color-blue-5)" : "var(--mantine-color-teal-5)"}`
1294
+ },
1295
+ children: /* @__PURE__ */ jsxs(Stack, { gap: 6, children: [
1296
+ /* @__PURE__ */ jsx(
1297
+ CardHeader,
973
1298
  {
974
- checked: selection.isSelected(deal.id),
975
- onChange: () => selection.toggle(deal.id)
1299
+ icon: messageIcon,
1300
+ iconColor: isOutbound ? "blue" : "teal",
1301
+ title: `${directionLabel} ${participant || "unknown recipient"}`,
1302
+ subtitle: message.subject ?? void 0,
1303
+ titleOrder: 5,
1304
+ mb: 0,
1305
+ rightSection: /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", style: { flexShrink: 0, textAlign: "right" }, children: formatSentAt(message.sentAt) })
976
1306
  }
977
- ) }),
978
- /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { fw: 500, children: companyName }) }),
979
- /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", children: contactName || "-" }) }),
980
- /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: deal.contact_email || "-" }) }),
981
- /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Badge, { variant: "light", color: DEAL_STAGE_COLORS[deal.stage_key || ""] || "gray", size: "sm", children: formatDealStageLabel(deal.stage_key) }) }),
982
- /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: formatTimeAgo(deal.updated_at) }) })
983
- ]
984
- },
985
- deal.id
986
- );
987
- }) })
988
- ] }),
989
- total > PAGE_SIZE_DEFAULT && /* @__PURE__ */ jsx(Group, { justify: "center", children: /* @__PURE__ */ jsx(
990
- Pagination,
991
- {
992
- value: pagination.page,
993
- onChange: pagination.setPage,
994
- total: pagination.totalPages(total),
995
- size: "sm"
996
- }
997
- ) })
998
- ] }) })
999
- ] }),
1000
- /* @__PURE__ */ jsx(
1001
- CustomModal,
1002
- {
1003
- opened: showBatchDelete,
1004
- onClose: () => !deleteDeal.isPending && setShowBatchDelete(false),
1005
- size: "sm",
1006
- loading: deleteDeal.isPending,
1007
- children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
1008
- /* @__PURE__ */ jsxs(Group, { gap: "sm", children: [
1009
- /* @__PURE__ */ jsx(IconAlertTriangle, { size: 24, color: "var(--color-error)" }),
1010
- /* @__PURE__ */ jsxs(Title, { order: 4, children: [
1011
- "Delete ",
1012
- selection.selectedCount,
1013
- " Deals"
1014
- ] })
1015
- ] }),
1016
- /* @__PURE__ */ jsxs(Text, { size: "sm", children: [
1017
- "Are you sure you want to delete",
1018
- " ",
1019
- /* @__PURE__ */ jsx(Text, { span: true, fw: 600, children: selection.selectedCount }),
1020
- " ",
1021
- "selected deals?"
1022
- ] }),
1023
- /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "This action cannot be undone." }),
1024
- /* @__PURE__ */ jsxs(Group, { justify: "flex-end", mt: "md", children: [
1025
- /* @__PURE__ */ jsx(Button, { variant: "light", onClick: () => setShowBatchDelete(false), disabled: deleteDeal.isPending, children: "Cancel" }),
1026
- /* @__PURE__ */ jsx(Button, { color: "red", loading: deleteDeal.isPending, onClick: () => void handleDeleteSelected(), children: "Delete" })
1027
- ] })
1028
- ] })
1029
- }
1030
- )
1307
+ ),
1308
+ /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(MessageBody, { body: message.body }) })
1309
+ ] })
1310
+ }
1311
+ )
1312
+ ]
1313
+ },
1314
+ message.id
1315
+ );
1316
+ })
1031
1317
  ] });
1032
1318
  }
1033
- function BookingLinkPanel({ dealId, contactId }) {
1319
+ var CANONICAL_ACTION_KEYS = /* @__PURE__ */ new Set(["send_reply"]);
1320
+ var DEAL_OWNERSHIP_BADGE_COPY2 = {
1321
+ us: {
1322
+ color: "red",
1323
+ label: "Our move"
1324
+ },
1325
+ them: {
1326
+ color: "gray",
1327
+ label: "Their move"
1328
+ }
1329
+ };
1330
+ function getContactName(deal) {
1331
+ return [deal.contact?.first_name, deal.contact?.last_name].filter(Boolean).join(" ");
1332
+ }
1333
+ function getCompanyName(deal) {
1334
+ return deal.contact?.company?.name || deal.discovery_data?.company || deal.contact_email?.split("@")[1] || "Unknown";
1335
+ }
1336
+ function toAction(actionDef) {
1337
+ const { key, label, payloadSchema } = actionDef;
1338
+ return { key, label, payloadSchema };
1339
+ }
1340
+ function getVisibleCanonicalDealActions(deal, crmActions) {
1341
+ if (deal.nextAction && CANONICAL_ACTION_KEYS.has(deal.nextAction)) {
1342
+ if (deal.nextAction === "send_reply" && deal.ownership === "them") return [];
1343
+ const nextAction = crmActions.find((action) => action.key === deal.nextAction);
1344
+ return nextAction ? [toAction(nextAction)] : [];
1345
+ }
1346
+ return deriveActions(deal, crmActions).filter((action) => {
1347
+ if (!CANONICAL_ACTION_KEYS.has(action.key)) return false;
1348
+ if (action.key === "send_reply" && deal.ownership === "them") return false;
1349
+ return true;
1350
+ });
1351
+ }
1352
+ function CopyBookingLinkButton({ dealId, contactId }) {
1034
1353
  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
- ] }) });
1354
+ return /* @__PURE__ */ jsx(CopyButton, { value: url, timeout: 1500, children: ({ copied, copy }) => /* @__PURE__ */ jsx(
1355
+ Button,
1356
+ {
1357
+ variant: "light",
1358
+ size: "xs",
1359
+ color: copied ? "teal" : void 0,
1360
+ leftSection: copied ? /* @__PURE__ */ jsx(IconCheck, { size: 14 }) : /* @__PURE__ */ jsx(IconCopy, { size: 14 }),
1361
+ onClick: copy,
1362
+ children: copied ? "Copied" : "Copy Booking Link"
1363
+ }
1364
+ ) });
1365
+ }
1366
+ function DetailRow({ label, value }) {
1367
+ return /* @__PURE__ */ jsxs(Group, { gap: "xs", align: "flex-start", wrap: "nowrap", children: [
1368
+ /* @__PURE__ */ jsx(Text, { size: "sm", fw: 500, style: { width: 120, flexShrink: 0 }, children: label }),
1369
+ /* @__PURE__ */ jsx(Text, { size: "sm", style: { minWidth: 0, wordBreak: "break-word", color: "var(--color-text-subtle)" }, children: value || "N/A" })
1370
+ ] });
1050
1371
  }
1051
1372
  function DealDetailPage({ dealId, renderActions, onDealLoaded }) {
1052
1373
  const navigate = useNavigate();
1053
- const deleteDeal = useDeleteDeal();
1374
+ const [referrer] = useState(() => getDealReferrer());
1375
+ const backTarget = referrer === "pipeline" ? { to: "/crm/pipeline", label: "Pipeline" } : { to: "/crm/deals", label: "Deals" };
1054
1376
  const { data: deal, isLoading, error } = useDealDetail(dealId);
1055
- const [deleteModalOpen, setDeleteModalOpen] = useState(false);
1056
1377
  const crmActions = useCrmActions();
1057
- const actions = useMemo(() => deal ? deriveActions(deal, crmActions) : [], [deal, crmActions]);
1378
+ const actions = useMemo(
1379
+ () => deal ? getVisibleCanonicalDealActions(deal, crmActions) : [],
1380
+ [deal, crmActions]
1381
+ );
1382
+ const [activeActionKey, setActiveActionKey] = useState(null);
1383
+ const activeAction = activeActionKey ? actions.find((action) => action.key === activeActionKey) : null;
1384
+ useEffect(() => {
1385
+ if (activeActionKey && !actions.some((action) => action.key === activeActionKey)) {
1386
+ setActiveActionKey(null);
1387
+ }
1388
+ }, [actions, activeActionKey]);
1058
1389
  useEffect(() => {
1059
1390
  if (deal) onDealLoaded?.(deal);
1060
1391
  }, [deal, onDealLoaded]);
1061
- const title = deal ? `${[deal.contact?.first_name, deal.contact?.last_name].filter(Boolean).join(" ") || "Unknown"} Deal` : "Deal Detail";
1062
- const contactName = useMemo(
1063
- () => deal ? [deal.contact?.first_name, deal.contact?.last_name].filter(Boolean).join(" ") : "",
1064
- [deal]
1065
- );
1066
- const companyName = useMemo(() => {
1067
- if (!deal) return null;
1068
- return deal.contact?.company?.name || deal.discovery_data?.company || deal.contact_email?.split("@")[1] || "Unknown";
1069
- }, [deal]);
1070
- const headerActions = deal ? /* @__PURE__ */ jsxs(Group, { children: [
1392
+ const titleName = deal ? getContactName(deal) || getCompanyName(deal) : null;
1393
+ const title = titleName ? `${titleName} Deal` : "Deal Detail";
1394
+ const companyName = deal ? getCompanyName(deal) : "Unknown";
1395
+ const ownershipBadge = deal?.ownership ? DEAL_OWNERSHIP_BADGE_COPY2[deal.ownership] : null;
1396
+ const activityLog = (deal?.activity_log || []).filter(Boolean);
1397
+ const hasDiscoveryData = deal?.discovery_data != null && (typeof deal.discovery_data !== "object" || Array.isArray(deal.discovery_data) || Object.keys(deal.discovery_data).length > 0);
1398
+ const headerActions = /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
1071
1399
  /* @__PURE__ */ jsx(
1072
1400
  Button,
1073
1401
  {
1074
1402
  variant: "light",
1075
1403
  size: "sm",
1076
1404
  leftSection: /* @__PURE__ */ jsx(IconArrowLeft, { size: 16 }),
1077
- onClick: () => navigate({ to: "/crm/deals" }),
1078
- children: "Deals"
1405
+ onClick: () => navigate({ to: backTarget.to }),
1406
+ children: backTarget.label
1079
1407
  }
1080
1408
  ),
1081
- deal.proposal_pdf_url && /* @__PURE__ */ jsx(
1409
+ deal?.proposal_pdf_url && /* @__PURE__ */ jsx(
1082
1410
  Button,
1083
1411
  {
1084
1412
  variant: "light",
1413
+ size: "sm",
1085
1414
  leftSection: /* @__PURE__ */ jsx(IconFileText, { size: 16 }),
1086
1415
  onClick: () => window.open(deal.proposal_pdf_url, "_blank"),
1087
1416
  children: "View Proposal"
1088
1417
  }
1089
1418
  ),
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
- ),
1093
- renderActions?.(deal),
1094
- /* @__PURE__ */ jsx(ActionIcon, { variant: "subtle", color: "red", onClick: () => setDeleteModalOpen(true), children: /* @__PURE__ */ jsx(IconTrash, { size: 16 }) })
1095
- ] }) : /* @__PURE__ */ jsx(
1096
- Button,
1097
- {
1098
- variant: "light",
1099
- size: "sm",
1100
- leftSection: /* @__PURE__ */ jsx(IconArrowLeft, { size: 16 }),
1101
- onClick: () => navigate({ to: "/crm/deals" }),
1102
- children: "Deals"
1103
- }
1104
- );
1419
+ deal && renderActions?.(deal)
1420
+ ] });
1105
1421
  if (isLoading) {
1106
- return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsxs(PageContainer, { children: [
1107
- /* @__PURE__ */ jsx(Stack, { children: /* @__PURE__ */ jsx(PageTitleCaption, { title, caption: "Loading deal details...", rightSection: headerActions }) }),
1422
+ return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsx(PageContainer, { children: /* @__PURE__ */ jsxs(Stack, { children: [
1423
+ /* @__PURE__ */ jsx(PageTitleCaption, { title, caption: "Loading deal details...", rightSection: headerActions }),
1108
1424
  /* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsx(Center, { p: "xl", children: /* @__PURE__ */ jsx(Loader, {}) }) })
1109
- ] }) });
1425
+ ] }) }) });
1110
1426
  }
1111
1427
  if (error) {
1112
- return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsxs(PageContainer, { children: [
1113
- /* @__PURE__ */ jsx(Stack, { children: /* @__PURE__ */ jsx(PageTitleCaption, { title, caption: "Unable to load deal details", rightSection: headerActions }) }),
1428
+ return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsx(PageContainer, { children: /* @__PURE__ */ jsxs(Stack, { children: [
1429
+ /* @__PURE__ */ jsx(PageTitleCaption, { title, caption: "Unable to load deal details", rightSection: headerActions }),
1114
1430
  /* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsx(CenteredErrorState, { error, title: "Failed to load deal" }) })
1115
- ] }) });
1431
+ ] }) }) });
1116
1432
  }
1117
1433
  if (!deal) {
1118
- return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsxs(PageContainer, { children: [
1119
- /* @__PURE__ */ jsx(Stack, { children: /* @__PURE__ */ jsx(PageTitleCaption, { title, caption: "Deal not found", rightSection: headerActions }) }),
1120
- /* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsx(EmptyState, { icon: IconTrash, title: "Deal not found", description: "The selected deal no longer exists." }) })
1121
- ] }) });
1434
+ return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsx(PageContainer, { children: /* @__PURE__ */ jsxs(Stack, { children: [
1435
+ /* @__PURE__ */ jsx(PageTitleCaption, { title, caption: "Deal not found", rightSection: headerActions }),
1436
+ /* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsx(EmptyState, { icon: IconInbox, title: "Deal not found", description: "The selected deal no longer exists." }) })
1437
+ ] }) }) });
1122
1438
  }
1123
- const activityLog = deal.activity_log || [];
1124
- return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsxs(PageContainer, { children: [
1125
- /* @__PURE__ */ jsx(Stack, { children: /* @__PURE__ */ jsx(
1439
+ return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsx(PageContainer, { children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
1440
+ /* @__PURE__ */ jsx(
1126
1441
  PageTitleCaption,
1127
1442
  {
1128
1443
  title,
1129
- caption: `${companyName || "Unknown"} - ${formatDealStageLabel(deal.stage_key)}`,
1444
+ caption: `${companyName} - ${formatDealStageLabel(deal.stage_key)}`,
1130
1445
  rightSection: headerActions
1131
1446
  }
1132
- ) }),
1133
- /* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsxs(Tabs, { defaultValue: "details", children: [
1134
- /* @__PURE__ */ jsxs(Tabs.List, { children: [
1135
- /* @__PURE__ */ jsx(Tabs.Tab, { value: "details", children: "Details" }),
1136
- /* @__PURE__ */ jsx(Tabs.Tab, { value: "discovery", children: "Discovery Data" }),
1137
- /* @__PURE__ */ jsx(Tabs.Tab, { value: "activity", children: "Activity" })
1447
+ ),
1448
+ /* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { gap: "lg", children: [
1449
+ /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
1450
+ /* @__PURE__ */ jsx(
1451
+ CardHeader,
1452
+ {
1453
+ icon: /* @__PURE__ */ jsx(IconBriefcase, { size: 18 }),
1454
+ title: "Deal",
1455
+ mb: 0,
1456
+ rightSection: /* @__PURE__ */ jsx(CopyBookingLinkButton, { dealId: deal.id, contactId: deal.contact?.id })
1457
+ }
1458
+ ),
1459
+ /* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 1, sm: 2 }, spacing: "sm", children: [
1460
+ /* @__PURE__ */ jsx(
1461
+ DetailRow,
1462
+ {
1463
+ label: "Stage",
1464
+ value: /* @__PURE__ */ jsx(Badge, { color: DEAL_STAGE_COLORS[deal.stage_key || ""] || "gray", children: formatDealStageLabel(deal.stage_key) })
1465
+ }
1466
+ ),
1467
+ /* @__PURE__ */ jsx(
1468
+ DetailRow,
1469
+ {
1470
+ label: "Priority",
1471
+ value: /* @__PURE__ */ jsx(Tooltip, { label: deal.priority.reason, withArrow: true, children: /* @__PURE__ */ jsx(
1472
+ Badge,
1473
+ {
1474
+ variant: "light",
1475
+ color: deal.priority.color,
1476
+ size: "sm",
1477
+ style: { maxWidth: "100%", textTransform: "none" },
1478
+ children: /* @__PURE__ */ jsx(Text, { span: true, size: "xs", truncate: "end", style: { maxWidth: "100%" }, children: deal.priority.label })
1479
+ }
1480
+ ) })
1481
+ }
1482
+ ),
1483
+ /* @__PURE__ */ jsx(
1484
+ DetailRow,
1485
+ {
1486
+ label: "Move",
1487
+ value: ownershipBadge ? /* @__PURE__ */ jsx(
1488
+ Badge,
1489
+ {
1490
+ variant: "light",
1491
+ color: ownershipBadge.color,
1492
+ size: "sm",
1493
+ style: { maxWidth: "100%", textTransform: "none" },
1494
+ children: /* @__PURE__ */ jsx(Text, { span: true, size: "xs", truncate: "end", style: { maxWidth: "100%" }, children: ownershipBadge.label })
1495
+ }
1496
+ ) : "N/A"
1497
+ }
1498
+ ),
1499
+ /* @__PURE__ */ jsx(
1500
+ DetailRow,
1501
+ {
1502
+ label: "Sent",
1503
+ value: deal.proposal_sent_at ? new Date(deal.proposal_sent_at).toLocaleString() : "N/A"
1504
+ }
1505
+ ),
1506
+ /* @__PURE__ */ jsx(DetailRow, { label: "Envelope ID", value: deal.signature_envelope_id || "N/A" }),
1507
+ /* @__PURE__ */ jsx(DetailRow, { label: "Contact Email", value: deal.contact?.email || deal.contact_email || "N/A" })
1508
+ ] })
1138
1509
  ] }),
1139
- /* @__PURE__ */ jsx(Tabs.Panel, { value: "details", pt: "md", children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
1140
- /* @__PURE__ */ jsx(Card, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
1141
- /* @__PURE__ */ jsx(Title, { order: 4, children: "Deal" }),
1142
- /* @__PURE__ */ jsxs(Group, { children: [
1143
- /* @__PURE__ */ jsx(Text, { fw: 500, children: "Stage:" }),
1144
- /* @__PURE__ */ jsx(Badge, { color: DEAL_STAGE_COLORS[deal.stage_key || ""] || "gray", children: formatDealStageLabel(deal.stage_key) })
1145
- ] }),
1146
- /* @__PURE__ */ jsxs(Group, { children: [
1147
- /* @__PURE__ */ jsx(Text, { fw: 500, children: "Sent:" }),
1148
- /* @__PURE__ */ jsx(Text, { children: deal.proposal_sent_at ? new Date(deal.proposal_sent_at).toLocaleString() : "N/A" })
1149
- ] }),
1150
- /* @__PURE__ */ jsxs(Group, { children: [
1151
- /* @__PURE__ */ jsx(Text, { fw: 500, children: "Envelope ID:" }),
1152
- /* @__PURE__ */ jsx(Text, { children: deal.signature_envelope_id || "N/A" })
1153
- ] })
1154
- ] }) }),
1155
- /* @__PURE__ */ jsx(Card, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
1156
- /* @__PURE__ */ jsx(Title, { order: 4, children: "Contact" }),
1157
- /* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 1, sm: 2 }, spacing: "sm", children: [
1158
- /* @__PURE__ */ jsxs(Group, { children: [
1159
- /* @__PURE__ */ jsx(Text, { fw: 500, children: "Name:" }),
1160
- /* @__PURE__ */ jsx(Text, { children: contactName || "N/A" })
1161
- ] }),
1162
- /* @__PURE__ */ jsxs(Group, { children: [
1163
- /* @__PURE__ */ jsx(Text, { fw: 500, children: "Email:" }),
1164
- /* @__PURE__ */ jsx(Text, { children: deal.contact?.email || deal.contact_email || "N/A" })
1165
- ] }),
1166
- /* @__PURE__ */ jsxs(Group, { children: [
1167
- /* @__PURE__ */ jsx(Text, { fw: 500, children: "Title:" }),
1168
- /* @__PURE__ */ jsx(Text, { children: deal.contact?.title || "N/A" })
1169
- ] }),
1170
- /* @__PURE__ */ jsxs(Group, { children: [
1171
- /* @__PURE__ */ jsx(Text, { fw: 500, children: "Pipeline Status:" }),
1172
- /* @__PURE__ */ jsx(Text, { size: "sm", children: deal.contact?.pipeline_status ? Object.entries(deal.contact.pipeline_status).map(([key, val]) => {
1173
- const status = val?.status;
1174
- return status ? `${key}: ${status}` : null;
1175
- }).filter(Boolean).join(", ") || "N/A" : "N/A" })
1176
- ] }),
1177
- deal.contact?.headline && /* @__PURE__ */ jsxs(Group, { children: [
1178
- /* @__PURE__ */ jsx(Text, { fw: 500, children: "Headline:" }),
1179
- /* @__PURE__ */ jsx(Text, { children: deal.contact.headline })
1180
- ] }),
1181
- deal.contact?.linkedin_url && /* @__PURE__ */ jsxs(Group, { children: [
1182
- /* @__PURE__ */ jsx(Text, { fw: 500, children: "LinkedIn:" }),
1183
- /* @__PURE__ */ jsx(Text, { component: "a", href: deal.contact.linkedin_url, target: "_blank", c: "blue", children: "View Profile" })
1184
- ] })
1185
- ] })
1186
- ] }) }),
1187
- deal.contact_id && /* @__PURE__ */ jsx(BookingLinkPanel, { dealId: deal.id, contactId: deal.contact_id }),
1188
- /* @__PURE__ */ jsx(Card, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
1189
- /* @__PURE__ */ jsx(Title, { order: 4, children: "Company" }),
1190
- /* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 1, sm: 2 }, spacing: "sm", children: [
1191
- /* @__PURE__ */ jsxs(Group, { children: [
1192
- /* @__PURE__ */ jsx(Text, { fw: 500, children: "Name:" }),
1193
- /* @__PURE__ */ jsx(Text, { children: deal.contact?.company?.name || "N/A" })
1194
- ] }),
1195
- /* @__PURE__ */ jsxs(Group, { children: [
1196
- /* @__PURE__ */ jsx(Text, { fw: 500, children: "Domain:" }),
1197
- /* @__PURE__ */ jsx(Text, { children: deal.contact?.company?.domain || "N/A" })
1198
- ] }),
1199
- /* @__PURE__ */ jsxs(Group, { children: [
1200
- /* @__PURE__ */ jsx(Text, { fw: 500, children: "Segment:" }),
1201
- /* @__PURE__ */ jsx(Text, { children: deal.contact?.company?.segment || "N/A" })
1202
- ] }),
1203
- /* @__PURE__ */ jsxs(Group, { children: [
1204
- /* @__PURE__ */ jsx(Text, { fw: 500, children: "Category:" }),
1205
- /* @__PURE__ */ jsx(Text, { children: deal.contact?.company?.category || "N/A" })
1206
- ] }),
1207
- /* @__PURE__ */ jsxs(Group, { children: [
1208
- /* @__PURE__ */ jsx(Text, { fw: 500, children: "Employees:" }),
1209
- /* @__PURE__ */ jsx(Text, { children: deal.contact?.company?.num_employees || "N/A" })
1210
- ] }),
1211
- deal.contact?.company?.website && /* @__PURE__ */ jsxs(Group, { children: [
1212
- /* @__PURE__ */ jsx(Text, { fw: 500, children: "Website:" }),
1213
- /* @__PURE__ */ jsx(Text, { component: "a", href: deal.contact.company.website, target: "_blank", c: "blue", children: deal.contact.company.website })
1214
- ] })
1215
- ] })
1216
- ] }) }),
1217
- ["closing", "closed_won"].includes(deal.stage_key || "") && /* @__PURE__ */ jsx(Card, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
1218
- /* @__PURE__ */ jsx(Title, { order: 4, children: "Payment" }),
1219
- /* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 1, sm: 2 }, spacing: "sm", children: [
1220
- /* @__PURE__ */ jsxs(Group, { children: [
1221
- /* @__PURE__ */ jsx(Text, { fw: 500, children: "Initial Fee:" }),
1222
- /* @__PURE__ */ jsxs(Text, { children: [
1223
- "$",
1224
- deal.initial_fee?.toLocaleString() || "Not set"
1225
- ] })
1226
- ] }),
1227
- /* @__PURE__ */ jsxs(Group, { children: [
1228
- /* @__PURE__ */ jsx(Text, { fw: 500, children: "Monthly Fee:" }),
1229
- /* @__PURE__ */ jsxs(Text, { children: [
1230
- "$",
1231
- deal.monthly_fee?.toLocaleString() || "Not set",
1232
- "/mo"
1233
- ] })
1234
- ] })
1235
- ] }),
1236
- deal.stripe_payment_link && /* @__PURE__ */ jsxs(Group, { children: [
1237
- /* @__PURE__ */ jsx(Text, { fw: 500, children: "Payment Link:" }),
1238
- /* @__PURE__ */ jsx(Text, { component: "a", href: deal.stripe_payment_link, target: "_blank", c: "blue", children: deal.stripe_payment_link })
1239
- ] })
1510
+ /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
1511
+ /* @__PURE__ */ jsx(CardHeader, { icon: /* @__PURE__ */ jsx(IconUser, { size: 18 }), title: "Contact", mb: 0 }),
1512
+ /* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 1, sm: 2 }, spacing: "sm", children: [
1513
+ /* @__PURE__ */ jsx(DetailRow, { label: "Name", value: getContactName(deal) || "N/A" }),
1514
+ /* @__PURE__ */ jsx(DetailRow, { label: "Title", value: deal.contact?.title || "N/A" }),
1515
+ /* @__PURE__ */ jsx(DetailRow, { label: "Company", value: deal.contact?.company?.name || "N/A" }),
1516
+ /* @__PURE__ */ jsx(DetailRow, { label: "Domain", value: deal.contact?.company?.domain || "N/A" }),
1517
+ /* @__PURE__ */ jsx(DetailRow, { label: "Segment", value: deal.contact?.company?.segment || "N/A" }),
1518
+ /* @__PURE__ */ jsx(DetailRow, { label: "Category", value: deal.contact?.company?.category || "N/A" })
1519
+ ] })
1520
+ ] }),
1521
+ /* @__PURE__ */ jsxs(Tabs, { defaultValue: "conversation", keepMounted: false, children: [
1522
+ /* @__PURE__ */ jsxs(Tabs.List, { children: [
1523
+ /* @__PURE__ */ jsx(Tabs.Tab, { value: "conversation", leftSection: /* @__PURE__ */ jsx(IconMessages, { size: 16 }), children: "Conversation" }),
1524
+ /* @__PURE__ */ jsx(Tabs.Tab, { value: "activity", leftSection: /* @__PURE__ */ jsx(IconHistory, { size: 16 }), children: "Activity" }),
1525
+ /* @__PURE__ */ jsx(Tabs.Tab, { value: "discovery", leftSection: /* @__PURE__ */ jsx(IconSearch, { size: 16 }), children: "Discovery" })
1526
+ ] }),
1527
+ /* @__PURE__ */ jsx(Tabs.Panel, { value: "conversation", pt: "md", children: /* @__PURE__ */ jsx(ConversationThread, { messages: deal.conversation.messages }) }),
1528
+ /* @__PURE__ */ jsx(Tabs.Panel, { value: "activity", pt: "md", children: /* @__PURE__ */ jsx(ActivityTimeline, { activities: activityLog }) }),
1529
+ /* @__PURE__ */ jsx(Tabs.Panel, { value: "discovery", pt: "md", children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
1530
+ /* @__PURE__ */ jsx(
1531
+ CardHeader,
1532
+ {
1533
+ icon: /* @__PURE__ */ jsx(IconSearch, { size: 18 }),
1534
+ title: "Discovery",
1535
+ subtitle: "Structured intake answers and qualification context captured before proposal work.",
1536
+ mb: 0
1537
+ }
1538
+ ),
1539
+ hasDiscoveryData ? /* @__PURE__ */ jsx(Code, { block: true, children: JSON.stringify(deal.discovery_data, null, 2) }) : /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "No discovery responses have been captured for this deal." })
1240
1540
  ] }) })
1241
- ] }) }),
1242
- /* @__PURE__ */ jsx(Tabs.Panel, { value: "discovery", pt: "md", children: /* @__PURE__ */ jsx(Card, { withBorder: true, children: /* @__PURE__ */ jsx(Code, { block: true, children: JSON.stringify(deal.discovery_data, null, 2) }) }) }),
1243
- /* @__PURE__ */ jsx(Tabs.Panel, { value: "activity", pt: "md", children: /* @__PURE__ */ jsx(ActivityTimeline, { activities: activityLog }) })
1541
+ ] })
1244
1542
  ] }) }),
1245
- /* @__PURE__ */ jsx(CustomModal, { opened: deleteModalOpen, onClose: () => setDeleteModalOpen(false), size: "sm", children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
1246
- /* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "flex-start", children: [
1247
- /* @__PURE__ */ jsx(Title, { order: 4, children: "Delete deal" }),
1248
- /* @__PURE__ */ jsx(ActionIcon, { variant: "subtle", onClick: () => setDeleteModalOpen(false), children: /* @__PURE__ */ jsx(IconX, { size: 18 }) })
1249
- ] }),
1250
- /* @__PURE__ */ jsxs(Text, { size: "sm", children: [
1251
- "Are you sure you want to delete the deal for ",
1252
- /* @__PURE__ */ jsx("strong", { children: contactName || deal.contact_email }),
1253
- "? This will cancel any active schedules and pending tasks for this contact."
1254
- ] }),
1255
- (deal.signature_envelope_id || deal.stripe_payment_link) && /* @__PURE__ */ jsxs(Alert, { color: "yellow", variant: "light", children: [
1256
- "This deal has ",
1257
- deal.signature_envelope_id ? "a signed contract" : "",
1258
- deal.signature_envelope_id && deal.stripe_payment_link ? " and " : "",
1259
- deal.stripe_payment_link ? "an active payment link" : "",
1260
- " that will not be automatically cleaned up."
1261
- ] }),
1262
- /* @__PURE__ */ jsx(Divider, {}),
1263
- /* @__PURE__ */ jsxs(Group, { justify: "flex-end", children: [
1264
- /* @__PURE__ */ jsx(Button, { variant: "default", onClick: () => setDeleteModalOpen(false), disabled: deleteDeal.isPending, children: "Cancel" }),
1265
- /* @__PURE__ */ jsx(
1266
- Button,
1543
+ /* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
1544
+ /* @__PURE__ */ jsx(CardHeader, { icon: /* @__PURE__ */ jsx(IconBolt, { size: 18 }), title: "Actions", titleOrder: 2, mb: 0 }),
1545
+ actions.length > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
1546
+ /* @__PURE__ */ jsx(Group, { gap: "sm", children: actions.map(
1547
+ (action) => action.payloadSchema ? /* @__PURE__ */ jsx(
1548
+ ActionFormButton,
1549
+ {
1550
+ action,
1551
+ isActive: activeActionKey === action.key,
1552
+ onToggle: () => setActiveActionKey((current) => current === action.key ? null : action.key)
1553
+ },
1554
+ action.key
1555
+ ) : /* @__PURE__ */ jsx(ActionButton, { action, dealId: deal.id }, action.key)
1556
+ ) }),
1557
+ activeAction?.payloadSchema && /* @__PURE__ */ jsx(
1558
+ ActionForm,
1267
1559
  {
1268
- color: "red",
1269
- loading: deleteDeal.isPending,
1270
- onClick: () => deleteDeal.mutate(deal.id, {
1271
- onSuccess: () => {
1272
- setDeleteModalOpen(false);
1273
- void navigate({ to: "/crm/deals" });
1274
- }
1275
- }),
1276
- children: "Delete deal"
1277
- }
1560
+ action: activeAction,
1561
+ dealId: deal.id,
1562
+ onClose: () => setActiveActionKey(null)
1563
+ },
1564
+ activeAction.key
1278
1565
  )
1279
- ] })
1566
+ ] }) : /* @__PURE__ */ jsx(Alert, { color: "gray", variant: "light", children: "No reply actions are currently available for this deal." })
1280
1567
  ] }) })
1281
- ] }) });
1568
+ ] }) }) });
1282
1569
  }
1283
1570
  function CompanyDetailPage({ companyId }) {
1284
1571
  const navigate = useNavigate();
@@ -1398,5 +1685,313 @@ function CompanyDetailPage({ companyId }) {
1398
1685
  ] }) })
1399
1686
  ] }) });
1400
1687
  }
1688
+ var crmPrioritySettingsKeys = {
1689
+ all: ["crm-priority-settings"],
1690
+ detail: (organizationId) => [...crmPrioritySettingsKeys.all, organizationId]
1691
+ };
1692
+ function useCrmPrioritySettings() {
1693
+ const { apiRequest, isReady, organizationId } = useElevasisServices();
1694
+ return useQuery({
1695
+ queryKey: crmPrioritySettingsKeys.detail(organizationId),
1696
+ queryFn: () => apiRequest("/crm/settings/priority"),
1697
+ enabled: isReady
1698
+ });
1699
+ }
1700
+ function useUpdateCrmPrioritySettings() {
1701
+ const { apiRequest, organizationId } = useElevasisServices();
1702
+ const queryClient = useQueryClient();
1703
+ return useMutation({
1704
+ mutationFn: (override) => apiRequest("/crm/settings/priority", {
1705
+ method: "PATCH",
1706
+ body: JSON.stringify(override)
1707
+ }),
1708
+ onSuccess: () => {
1709
+ queryClient.invalidateQueries({ queryKey: crmPrioritySettingsKeys.detail(organizationId) });
1710
+ queryClient.invalidateQueries({ queryKey: dealKeys.lists() });
1711
+ queryClient.invalidateQueries({ queryKey: dealKeys.details() });
1712
+ showSuccessNotification("CRM priority settings saved.");
1713
+ },
1714
+ onError: (error) => {
1715
+ showApiErrorNotification(error);
1716
+ }
1717
+ });
1718
+ }
1719
+ function useResetCrmPrioritySettings() {
1720
+ const { apiRequest, organizationId } = useElevasisServices();
1721
+ const queryClient = useQueryClient();
1722
+ return useMutation({
1723
+ mutationFn: () => apiRequest("/crm/settings/priority", {
1724
+ method: "DELETE"
1725
+ }),
1726
+ onSuccess: () => {
1727
+ queryClient.invalidateQueries({ queryKey: crmPrioritySettingsKeys.detail(organizationId) });
1728
+ queryClient.invalidateQueries({ queryKey: dealKeys.lists() });
1729
+ queryClient.invalidateQueries({ queryKey: dealKeys.details() });
1730
+ showSuccessNotification("CRM priority settings reset to Org-OS defaults.");
1731
+ },
1732
+ onError: (error) => {
1733
+ showApiErrorNotification(error);
1734
+ }
1735
+ });
1736
+ }
1737
+ var BUCKET_KEYS = [
1738
+ "needs_response",
1739
+ "follow_up_due",
1740
+ "waiting",
1741
+ "stale",
1742
+ "closed_low"
1743
+ ];
1744
+ var COLOR_OPTIONS = ["red", "orange", "yellow", "blue", "gray", "dark", "green", "teal", "violet", "grape"].map(
1745
+ (color) => ({ value: color, label: color })
1746
+ );
1747
+ var ORDER_OPTIONS = [
1748
+ { value: "10", label: "First - most urgent" },
1749
+ { value: "20", label: "Second - follow-up queue" },
1750
+ { value: "30", label: "Middle - waiting" },
1751
+ { value: "40", label: "Later - stale review" },
1752
+ { value: "50", label: "Last - closed or low priority" }
1753
+ ];
1754
+ var BUCKET_RULE_COPY = {
1755
+ needs_response: {
1756
+ applies: "Current state or latest activity indicates the lead replied or cancelled and needs a human response.",
1757
+ example: "A lead replies to the discovery email; the deal moves to Needs Response immediately."
1758
+ },
1759
+ follow_up_due: {
1760
+ applies: "The configured follow-up window for the current state has elapsed.",
1761
+ example: "A discovery link was sent 5 days ago and the follow-up window is 3 days."
1762
+ },
1763
+ waiting: {
1764
+ applies: "No response is needed yet, or the next action time is still in the future.",
1765
+ example: "A discovery link was sent yesterday and the follow-up window has not elapsed."
1766
+ },
1767
+ stale: {
1768
+ applies: "No meaningful activity has happened for the stale threshold and no stronger bucket applies.",
1769
+ example: "A deal has had no activity for 14 days and is not waiting on a scheduled follow-up."
1770
+ },
1771
+ closed_low: {
1772
+ applies: "The deal is in a closed stage, so it should stay out of the active follow-up queue.",
1773
+ example: "A deal is marked Closed Won or Closed Lost."
1774
+ }
1775
+ };
1776
+ function getOrderLabel(rank) {
1777
+ if (rank <= 10) return "Shown first";
1778
+ if (rank <= 20) return "After urgent replies";
1779
+ if (rank <= 30) return "Middle";
1780
+ if (rank <= 40) return "Later review";
1781
+ return "Shown last";
1782
+ }
1783
+ function toResolvedConfig(config = DEFAULT_CRM_PRIORITY_RULE_CONFIG) {
1784
+ return {
1785
+ ...config,
1786
+ enabled: "enabled" in config ? config.enabled : true,
1787
+ buckets: [...config.buckets].sort((a, b) => a.rank - b.rank),
1788
+ closedStageKeys: [...config.closedStageKeys],
1789
+ followUpAfterDaysByStateKey: { ...config.followUpAfterDaysByStateKey }
1790
+ };
1791
+ }
1792
+ function draftFromConfig(config) {
1793
+ const defaultBuckets = new Map(CRM_PRIORITY_BUCKETS.map((bucket) => [bucket.bucketKey, bucket]));
1794
+ const resolvedBuckets = new Map(config.buckets.map((bucket) => [bucket.bucketKey, bucket]));
1795
+ return {
1796
+ enabled: config.enabled,
1797
+ staleAfterDays: config.staleAfterDays,
1798
+ buckets: Object.fromEntries(
1799
+ BUCKET_KEYS.map((bucketKey) => {
1800
+ const bucket = resolvedBuckets.get(bucketKey) ?? defaultBuckets.get(bucketKey);
1801
+ return [bucketKey, { label: bucket.label, color: bucket.color, rank: bucket.rank }];
1802
+ })
1803
+ )
1804
+ };
1805
+ }
1806
+ function toOverride(draft) {
1807
+ return {
1808
+ enabled: draft.enabled,
1809
+ staleAfterDays: draft.staleAfterDays,
1810
+ buckets: Object.fromEntries(
1811
+ BUCKET_KEYS.map((bucketKey) => [
1812
+ bucketKey,
1813
+ {
1814
+ label: draft.buckets[bucketKey].label.trim(),
1815
+ color: draft.buckets[bucketKey].color.trim(),
1816
+ rank: draft.buckets[bucketKey].rank
1817
+ }
1818
+ ])
1819
+ )
1820
+ };
1821
+ }
1822
+ function CrmSettingsPage() {
1823
+ const settingsQuery = useCrmPrioritySettings();
1824
+ const updateSettings = useUpdateCrmPrioritySettings();
1825
+ const resetSettings = useResetCrmPrioritySettings();
1826
+ const resolvedConfig = useMemo(() => toResolvedConfig(settingsQuery.data?.resolved), [settingsQuery.data?.resolved]);
1827
+ const defaultConfig = useMemo(() => toResolvedConfig(settingsQuery.data?.defaults), [settingsQuery.data?.defaults]);
1828
+ const [draft, setDraft] = useState(() => draftFromConfig(resolvedConfig));
1829
+ useEffect(() => {
1830
+ if (settingsQuery.data) {
1831
+ setDraft(draftFromConfig(toResolvedConfig(settingsQuery.data.resolved)));
1832
+ }
1833
+ }, [settingsQuery.data]);
1834
+ const hasOverride = Boolean(settingsQuery.data?.override);
1835
+ const isSaving = updateSettings.isPending || resetSettings.isPending;
1836
+ const updateBucket = (bucketKey, field, value) => {
1837
+ setDraft((current) => ({
1838
+ ...current,
1839
+ buckets: {
1840
+ ...current.buckets,
1841
+ [bucketKey]: {
1842
+ ...current.buckets[bucketKey],
1843
+ [field]: value
1844
+ }
1845
+ }
1846
+ }));
1847
+ };
1848
+ const handleReset = async () => {
1849
+ const response = await resetSettings.mutateAsync();
1850
+ setDraft(draftFromConfig(toResolvedConfig(response.resolved)));
1851
+ };
1852
+ return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsx(PageContainer, { children: /* @__PURE__ */ jsxs(Stack, { children: [
1853
+ /* @__PURE__ */ jsx(
1854
+ PageTitleCaption,
1855
+ {
1856
+ title: "CRM Settings",
1857
+ caption: "Scoped CRM controls for priority presentation and thresholds",
1858
+ rightSection: !settingsQuery.isLoading && !settingsQuery.error ? /* @__PURE__ */ jsx(
1859
+ Switch,
1860
+ {
1861
+ label: "Priority Enabled",
1862
+ checked: draft.enabled,
1863
+ onChange: (event) => setDraft((current) => ({ ...current, enabled: event.currentTarget.checked }))
1864
+ }
1865
+ ) : null
1866
+ }
1867
+ ),
1868
+ settingsQuery.isLoading ? /* @__PURE__ */ jsx(Center, { p: "xl", children: /* @__PURE__ */ jsx(Loader, {}) }) : settingsQuery.error ? /* @__PURE__ */ jsx(CenteredErrorState, { error: settingsQuery.error, title: "Failed to load CRM settings" }) : /* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { children: [
1869
+ /* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "flex-start", children: [
1870
+ /* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
1871
+ /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
1872
+ /* @__PURE__ */ jsx(IconSettings, { size: 18 }),
1873
+ /* @__PURE__ */ jsx(Title, { order: 3, children: "Priority Rules" }),
1874
+ /* @__PURE__ */ jsx(Badge, { variant: "light", color: hasOverride ? "blue" : "gray", children: hasOverride ? "Custom" : "Org-OS defaults" })
1875
+ ] }),
1876
+ /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "These settings are saved for CRM priority evaluation. Deals continue to receive priority from the backend response." })
1877
+ ] }),
1878
+ /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
1879
+ /* @__PURE__ */ jsx(
1880
+ Button,
1881
+ {
1882
+ variant: "light",
1883
+ leftSection: /* @__PURE__ */ jsx(IconRestore, { size: 16 }),
1884
+ loading: resetSettings.isPending,
1885
+ disabled: isSaving,
1886
+ onClick: () => void handleReset(),
1887
+ children: "Reset defaults"
1888
+ }
1889
+ ),
1890
+ /* @__PURE__ */ jsx(
1891
+ Button,
1892
+ {
1893
+ loading: updateSettings.isPending,
1894
+ disabled: isSaving,
1895
+ onClick: () => void updateSettings.mutateAsync(toOverride(draft)),
1896
+ children: "Save changes"
1897
+ }
1898
+ )
1899
+ ] })
1900
+ ] }),
1901
+ /* @__PURE__ */ jsx(
1902
+ NumberInput,
1903
+ {
1904
+ label: "Stale threshold",
1905
+ description: "Deals with no meaningful activity after this many days can move to Stale.",
1906
+ min: 1,
1907
+ max: 365,
1908
+ value: draft.staleAfterDays,
1909
+ onChange: (value) => setDraft((current) => ({ ...current, staleAfterDays: Number(value) || current.staleAfterDays })),
1910
+ suffix: " days",
1911
+ maw: 320
1912
+ }
1913
+ ),
1914
+ /* @__PURE__ */ jsxs(Table, { style: { tableLayout: "fixed" }, children: [
1915
+ /* @__PURE__ */ jsx(Table.Thead, { children: /* @__PURE__ */ jsxs(Table.Tr, { children: [
1916
+ /* @__PURE__ */ jsx(Table.Th, { w: 150, children: "Bucket" }),
1917
+ /* @__PURE__ */ jsx(Table.Th, { w: 180, children: "Label" }),
1918
+ /* @__PURE__ */ jsx(Table.Th, { w: 170, children: "Color" }),
1919
+ /* @__PURE__ */ jsx(Table.Th, { w: 210, children: "Display order" }),
1920
+ /* @__PURE__ */ jsx(Table.Th, { w: 80, children: "Logic" })
1921
+ ] }) }),
1922
+ /* @__PURE__ */ jsx(Table.Tbody, { children: BUCKET_KEYS.map((bucketKey) => {
1923
+ const bucket = draft.buckets[bucketKey];
1924
+ const defaultBucket = defaultConfig.buckets.find((candidate) => candidate.bucketKey === bucketKey);
1925
+ return /* @__PURE__ */ jsxs(Table.Tr, { children: [
1926
+ /* @__PURE__ */ jsxs(Table.Td, { children: [
1927
+ /* @__PURE__ */ jsx(Text, { size: "sm", fw: 600, children: bucketKey.replaceAll("_", " ") }),
1928
+ /* @__PURE__ */ jsxs(
1929
+ Badge,
1930
+ {
1931
+ mt: 6,
1932
+ variant: "light",
1933
+ color: defaultBucket?.color ?? "gray",
1934
+ style: { textTransform: "none" },
1935
+ children: [
1936
+ "Default: ",
1937
+ defaultBucket ? getOrderLabel(defaultBucket.rank) : "Not set"
1938
+ ]
1939
+ }
1940
+ )
1941
+ ] }),
1942
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(
1943
+ TextInput,
1944
+ {
1945
+ size: "sm",
1946
+ value: bucket.label,
1947
+ onChange: (event) => updateBucket(bucketKey, "label", event.currentTarget.value)
1948
+ }
1949
+ ) }),
1950
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsxs(Group, { gap: "xs", wrap: "nowrap", children: [
1951
+ /* @__PURE__ */ jsx(ColorSwatch, { color: `var(--mantine-color-${bucket.color}-6)`, size: 18 }),
1952
+ /* @__PURE__ */ jsx(
1953
+ Select,
1954
+ {
1955
+ size: "sm",
1956
+ data: COLOR_OPTIONS,
1957
+ value: bucket.color,
1958
+ searchable: true,
1959
+ allowDeselect: false,
1960
+ onChange: (value) => updateBucket(bucketKey, "color", value ?? bucket.color)
1961
+ }
1962
+ )
1963
+ ] }) }),
1964
+ /* @__PURE__ */ jsxs(Table.Td, { children: [
1965
+ /* @__PURE__ */ jsx(
1966
+ Select,
1967
+ {
1968
+ size: "sm",
1969
+ data: ORDER_OPTIONS,
1970
+ value: String(bucket.rank),
1971
+ allowDeselect: false,
1972
+ onChange: (value) => updateBucket(bucketKey, "rank", Number(value) || bucket.rank)
1973
+ }
1974
+ ),
1975
+ /* @__PURE__ */ jsx(Text, { mt: 4, size: "xs", c: "dimmed", children: "Lower order appears earlier in Pipeline and Deals." })
1976
+ ] }),
1977
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsxs(Popover, { width: 360, position: "left", withArrow: true, shadow: "md", children: [
1978
+ /* @__PURE__ */ jsx(Popover.Target, { children: /* @__PURE__ */ jsx(ActionIcon, { variant: "subtle", color: "gray", "aria-label": `Show ${bucket.label} logic`, children: /* @__PURE__ */ jsx(IconInfoCircle, { size: 18 }) }) }),
1979
+ /* @__PURE__ */ jsx(Popover.Dropdown, { children: /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
1980
+ /* @__PURE__ */ jsxs("div", { children: [
1981
+ /* @__PURE__ */ jsx(Text, { size: "xs", fw: 700, tt: "uppercase", c: "dimmed", children: "When it applies" }),
1982
+ /* @__PURE__ */ jsx(Text, { size: "sm", children: BUCKET_RULE_COPY[bucketKey].applies })
1983
+ ] }),
1984
+ /* @__PURE__ */ jsxs("div", { children: [
1985
+ /* @__PURE__ */ jsx(Text, { size: "xs", fw: 700, tt: "uppercase", c: "dimmed", children: "Example" }),
1986
+ /* @__PURE__ */ jsx(Text, { size: "sm", children: BUCKET_RULE_COPY[bucketKey].example })
1987
+ ] })
1988
+ ] }) })
1989
+ ] }) })
1990
+ ] }, bucketKey);
1991
+ }) })
1992
+ ] })
1993
+ ] }) })
1994
+ ] }) }) });
1995
+ }
1401
1996
 
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 };
1997
+ export { ActivityFeedWidget, CRM_ITEMS, CompanyDetailPage, ConversationThread, CrmOverview, CrmSettingsPage, CrmSidebar, CrmSidebarMiddle, CrmSidebarTop, DEAL_STAGE_COLORS, DEAL_STAGE_OPTIONS, DealDetailPage, DealsListPage, MetricsStrip, MyTasksPanel, PIPELINE_FUNNEL_ORDER, PipelineFunnelWidget, QuickCreateActions, SAVED_VIEW_PRESETS, SavedViewsPanel, compareDealsByPriority, crmManifest, crmPrioritySettingsKeys, formatDealStageLabel, setDealReferrer, useCrmPipelineSummary, useCrmPrioritySettings, useCrmQuickMetrics, useRecentCrmActivity, useResetCrmPrioritySettings, useUpdateCrmPrioritySettings };