@elevasis/ui 2.67.0 → 2.69.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. package/dist/api/index.d.ts +1 -1
  2. package/dist/api/index.js +3 -3
  3. package/dist/app/index.d.ts +575 -370
  4. package/dist/app/index.js +17 -23
  5. package/dist/auth/index.css +149 -149
  6. package/dist/auth/index.d.ts +5 -0
  7. package/dist/auth/index.js +11 -13
  8. package/dist/charts/index.css +208 -208
  9. package/dist/charts/index.js +11 -13
  10. package/dist/{chunk-M535KHLW.js → chunk-26BTE3Z4.js} +1 -1
  11. package/dist/chunk-3KMDHCAR.js +52 -0
  12. package/dist/chunk-3MEXPLWT.js +265 -0
  13. package/dist/chunk-4JHLS234.js +39 -0
  14. package/dist/chunk-6PZFOHJK.js +1764 -0
  15. package/dist/chunk-7WJEPTE7.js +194 -0
  16. package/dist/chunk-AH52XQB7.js +1 -0
  17. package/dist/{chunk-UFTM5SZZ.js → chunk-AKANHRQ5.js} +9 -3
  18. package/dist/chunk-BZZCNLT6.js +12 -0
  19. package/dist/chunk-C75XWBYZ.js +383 -0
  20. package/dist/chunk-DLR4SDZW.js +51 -0
  21. package/dist/{chunk-4OEVLV66.js → chunk-FLECOYUS.js} +113 -148
  22. package/dist/chunk-IRV6KJPV.js +425 -0
  23. package/dist/chunk-IT7GTJCB.js +10 -0
  24. package/dist/chunk-LGKLC5MG.js +44 -0
  25. package/dist/chunk-MFLM7BVC.js +85 -0
  26. package/dist/chunk-MRVGQLWN.js +1595 -0
  27. package/dist/{chunk-4DV4ECMG.js → chunk-N7U24M2J.js} +4 -0
  28. package/dist/{chunk-I7BZVVVU.js → chunk-NM4A63KJ.js} +104 -415
  29. package/dist/chunk-NU3NBXHP.js +1194 -0
  30. package/dist/chunk-PDHTXPSF.js +12 -0
  31. package/dist/chunk-RW64UJWT.js +1934 -0
  32. package/dist/{chunk-JFBZ6XDW.js → chunk-SNZDFCOQ.js} +3 -49
  33. package/dist/chunk-TUMSNGTX.js +35 -0
  34. package/dist/chunk-UD657ICS.js +589 -0
  35. package/dist/{chunk-XNW3O6QW.js → chunk-VDX6DL7Z.js} +1 -1
  36. package/dist/chunk-WXHAHKKI.js +16411 -0
  37. package/dist/chunk-XAZR3T6Y.js +2162 -0
  38. package/dist/chunk-XQ3ILKYN.js +37 -0
  39. package/dist/chunk-YCRINA2D.js +67 -0
  40. package/dist/chunk-YGCMMOY4.js +1484 -0
  41. package/dist/chunk-YNRYIXUT.js +10945 -0
  42. package/dist/chunk-ZD6LM53O.js +4528 -0
  43. package/dist/chunk-ZPTEGGKU.js +631 -0
  44. package/dist/components/chat/index.js +2 -1
  45. package/dist/components/index.css +208 -208
  46. package/dist/components/index.d.ts +1634 -264
  47. package/dist/components/index.js +2774 -14
  48. package/dist/components/navigation/index.css +149 -149
  49. package/dist/components/navigation/index.js +12 -13
  50. package/dist/features/auth/index.css +149 -149
  51. package/dist/features/auth/index.d.ts +457 -167
  52. package/dist/features/auth/index.js +13 -14
  53. package/dist/features/clients/index.css +208 -208
  54. package/dist/features/clients/index.d.ts +39 -1
  55. package/dist/features/clients/index.js +16 -13
  56. package/dist/features/content/index.d.ts +1788 -0
  57. package/dist/features/content/index.js +2180 -0
  58. package/dist/features/crm/index.d.ts +1802 -231
  59. package/dist/features/crm/index.js +16 -13
  60. package/dist/features/dashboard/index.d.ts +1 -8
  61. package/dist/features/dashboard/index.js +17 -13
  62. package/dist/features/delivery/index.d.ts +457 -167
  63. package/dist/features/delivery/index.js +16 -13
  64. package/dist/features/knowledge/index.css +149 -149
  65. package/dist/features/knowledge/index.js +11 -13
  66. package/dist/features/lead-gen/index.d.ts +53 -104
  67. package/dist/features/lead-gen/index.js +17 -13
  68. package/dist/features/monitoring/index.js +16 -13
  69. package/dist/features/monitoring/requests/index.js +15 -20
  70. package/dist/features/operations/index.d.ts +1 -86
  71. package/dist/features/operations/index.js +22 -12
  72. package/dist/features/public-agent-chat/index.js +2 -1
  73. package/dist/features/seo/index.js +2 -1
  74. package/dist/features/settings/index.d.ts +457 -167
  75. package/dist/features/settings/index.js +15 -13
  76. package/dist/hooks/access/index.css +149 -149
  77. package/dist/hooks/access/index.d.ts +5 -0
  78. package/dist/hooks/access/index.js +11 -13
  79. package/dist/hooks/delivery/index.d.ts +457 -167
  80. package/dist/hooks/delivery/index.js +2 -23
  81. package/dist/hooks/index.d.ts +2203 -244
  82. package/dist/hooks/index.js +11 -13
  83. package/dist/hooks/operations/command-view/utils/transformCommandViewData.d.ts +1 -8
  84. package/dist/hooks/published.d.ts +2203 -244
  85. package/dist/hooks/published.js +11 -13
  86. package/dist/initialization/index.d.ts +465 -169
  87. package/dist/knowledge/index.css +149 -149
  88. package/dist/knowledge/index.d.ts +5 -90
  89. package/dist/knowledge/index.js +3215 -3203
  90. package/dist/{knowledge-search-index-6EZNBNSR.js → knowledge-search-index-LP2WVSQX.js} +689 -695
  91. package/dist/layout/index.css +149 -149
  92. package/dist/layout/index.js +15 -13
  93. package/dist/organization/index.d.ts +10 -4
  94. package/dist/organization/index.js +1 -21
  95. package/dist/profile/index.d.ts +457 -167
  96. package/dist/provider/index.d.ts +576 -372
  97. package/dist/provider/index.js +11 -13
  98. package/dist/provider/published.css +149 -149
  99. package/dist/provider/published.d.ts +576 -372
  100. package/dist/provider/published.js +11 -13
  101. package/dist/supabase/index.d.ts +906 -347
  102. package/dist/test-utils/index.d.ts +15 -2
  103. package/dist/test-utils/index.js +41 -12
  104. package/dist/test-utils/setup-integration.js +1 -1
  105. package/dist/test-utils/setup.js +1 -1
  106. package/dist/theme/index.js +3 -2
  107. package/dist/theme/presets/index.js +1 -1
  108. package/dist/typeform/index.js +2049 -1
  109. package/dist/types/index.d.ts +477 -177
  110. package/dist/utils/index.d.ts +1 -8
  111. package/dist/utils/index.js +1 -1
  112. package/dist/vite/index.js +2 -2
  113. package/dist/vite-plugin-knowledge/index.js +1 -1
  114. package/package.json +7 -3
  115. package/src/features/README.md +28 -28
  116. package/dist/chunk-5TERYXOO.js +0 -51390
  117. package/dist/chunk-AUDNF2Q7.js +0 -2050
  118. package/dist/index.d.ts +0 -17888
  119. package/dist/index.js +0 -30
  120. package/dist/{chunk-GMXGDO3I.js → chunk-MISHPEJX.js} +1 -1
  121. package/dist/{chunk-RBGVNPA6.js → chunk-XQBURFKM.js} +0 -0
  122. package/dist/{index.css → features/content/index.css} +149 -149
@@ -0,0 +1,4528 @@
1
+ import { LEAD_GEN_API_INTERFACE } from './chunk-7WJEPTE7.js';
2
+ import { sanitizeInput } from './chunk-3MEXPLWT.js';
3
+ import { PageContainer } from './chunk-BZZCNLT6.js';
4
+ import { TableSelectionToolbar, SortableHeader } from './chunk-TUMSNGTX.js';
5
+ import { FilterBar } from './chunk-PDHTXPSF.js';
6
+ import { LeadGenSidebarMiddle, SubshellContentContainer, PageTitleCaption, acquisitionListKeys, useOptionalElevasisSystems, getAllBuildTemplates, getLeadGenStageCatalog, useListsTelemetry, useLists, useAccess, AccessKeys, CenteredErrorState, StatCard, useTableSort, sortData, usePaginationState, useTableSelection, EmptyState, CustomModal, useListActions, useList, useListProgress, useListExecutions, useDeleteList, useExecuteAsync, useCompanyFacets, useCompanies, useDeleteCompanies, useContacts, useDeleteContacts, useCreateList, JsonViewer, useCredentials, useVerifyCredential, useWorkflowExecution, useInFlightExecutions, useExecutionSSE, useListRecords, useListMembers, useListMember, useTransitionListMember, useDeriveActions, useArtifacts } from './chunk-WXHAHKKI.js';
7
+ import { showApiErrorNotification, showSuccessNotification } from './chunk-C75XWBYZ.js';
8
+ import { SubshellSidebarSection } from './chunk-MISHPEJX.js';
9
+ import { CardHeader } from './chunk-62GVNH5U.js';
10
+ import { computeInterfaceReadiness, getSystem } from './chunk-NM4A63KJ.js';
11
+ import { useElevasisServices } from './chunk-BI5VDO2K.js';
12
+ import { Stack, Group, Title, Text, Alert, Button, Collapse, Paper, Anchor, Center, Loader, SimpleGrid, Table, Badge, TextInput, Select, Checkbox, Pagination, ActionIcon, Tooltip, Tabs, ThemeIcon, Box, Textarea, Card, SegmentedControl, UnstyledButton, Pill, Drawer, JsonInput, Switch, NumberInput, MultiSelect, TagsInput, ScrollArea } from '@mantine/core';
13
+ import { IconTarget, IconAlertCircle, IconPlayerPlay, IconArrowRight, IconListDetails, IconPlus, IconAlertTriangle, IconSearch, IconList, IconExternalLink, IconCopy, IconTrash, IconBuilding, IconUsers, IconBolt, IconLayoutDashboard, IconBuildingFactory2, IconArrowLeft, IconSparkles, IconAddressBook, IconSettings, IconChevronDown, IconChevronRight, IconRefresh, IconMail, IconUser, IconDatabase } from '@tabler/icons-react';
14
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
15
+ import { useState, useRef, useMemo, useCallback, useEffect, Fragment as Fragment$1 } from 'react';
16
+ import { useForm } from '@mantine/form';
17
+ import { zod4Resolver } from 'mantine-form-zod-resolver';
18
+ import { Link, useNavigate, useSearch } from '@tanstack/react-router';
19
+ import { useQueryClient, useMutation } from '@tanstack/react-query';
20
+
21
+ var LeadGenSidebarTop = () => {
22
+ return /* @__PURE__ */ jsx(SubshellSidebarSection, { icon: IconTarget, label: "Lead Gen" });
23
+ };
24
+ var LeadGenSidebar = ({ children } = {}) => {
25
+ return /* @__PURE__ */ jsxs(Stack, { gap: 0, style: { height: "100%", display: "flex", flexDirection: "column" }, children: [
26
+ /* @__PURE__ */ jsx(LeadGenSidebarTop, {}),
27
+ children ?? /* @__PURE__ */ jsx(LeadGenSidebarMiddle, {})
28
+ ] });
29
+ };
30
+
31
+ // ../core/src/business/acquisition/build-templates.ts
32
+ function getProspectingBuildTemplateOptions(templates) {
33
+ return templates.map(({ id, label, description }) => ({
34
+ id,
35
+ label,
36
+ description
37
+ }));
38
+ }
39
+ function createBuildPlanSnapshotFromTemplate(template) {
40
+ const stepIdByStageKey = new Map(template.steps.map((step) => [step.stageKey, step.id]));
41
+ const stepIds = new Set(template.steps.map((step) => step.id));
42
+ return {
43
+ templateId: template.id,
44
+ templateLabel: template.label,
45
+ steps: template.steps.map((step) => {
46
+ const snapshotStep = {
47
+ id: step.id,
48
+ label: step.label,
49
+ primaryEntity: step.primaryEntity,
50
+ outputs: [...step.outputs],
51
+ stageKey: step.stageKey,
52
+ recordsStageKey: step.recordsStageKey ?? step.stageKey,
53
+ recordSourceStageKey: step.recordSourceStageKey ?? step.recordsStageKey ?? step.stageKey,
54
+ dependencyMode: step.dependencyMode,
55
+ actionKey: step.actionKey,
56
+ defaultBatchSize: step.defaultBatchSize,
57
+ maxBatchSize: step.maxBatchSize
58
+ };
59
+ if (step.description) snapshotStep.description = step.description;
60
+ if (step.recordEntity) snapshotStep.recordEntity = step.recordEntity;
61
+ if (step.dependsOn?.length) {
62
+ snapshotStep.dependsOn = step.dependsOn.map(
63
+ (dependency) => stepIds.has(dependency) ? dependency : stepIdByStageKey.get(dependency) ?? dependency
64
+ );
65
+ }
66
+ if (step.credentialRequirements?.length) {
67
+ snapshotStep.credentialRequirements = step.credentialRequirements.map((requirement) => ({
68
+ ...requirement
69
+ }));
70
+ }
71
+ if (step.recordColumns) {
72
+ snapshotStep.recordColumns = {
73
+ ...step.recordColumns.company ? {
74
+ company: step.recordColumns.company.map((column) => ({ ...column }))
75
+ } : {},
76
+ ...step.recordColumns.contact ? {
77
+ contact: step.recordColumns.contact.map((column) => ({ ...column }))
78
+ } : {}
79
+ };
80
+ }
81
+ return snapshotStep;
82
+ })
83
+ };
84
+ }
85
+ function createBuildPlanSnapshotFromTemplateId(templateId, templates = []) {
86
+ const template = templates.find((item) => item.id === templateId);
87
+ if (!template) return null;
88
+ return createBuildPlanSnapshotFromTemplate(template);
89
+ }
90
+ function TabSection({ icon, title, description, rightSection, children }) {
91
+ const wrappedIcon = icon ? /* @__PURE__ */ jsx(ThemeIcon, { size: "md", variant: "light", children: icon }) : null;
92
+ return /* @__PURE__ */ jsxs(Stack, { gap: "sm", p: "md", children: [
93
+ /* @__PURE__ */ jsxs(Group, { justify: "space-between", gap: "md", align: "flex-start", wrap: "nowrap", children: [
94
+ /* @__PURE__ */ jsxs(Stack, { gap: 2, style: { minWidth: 0 }, children: [
95
+ /* @__PURE__ */ jsxs(Group, { gap: "xs", wrap: "nowrap", children: [
96
+ wrappedIcon,
97
+ /* @__PURE__ */ jsx(Title, { order: 3, style: { overflowWrap: "anywhere" }, children: title })
98
+ ] }),
99
+ description ? /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", style: { overflowWrap: "anywhere" }, children: description }) : null
100
+ ] }),
101
+ rightSection
102
+ ] }),
103
+ children
104
+ ] });
105
+ }
106
+ function renderField(field, form, disabled, jsonState, setJsonState) {
107
+ const inputProps = form.getInputProps(field.path);
108
+ const label = field.label;
109
+ const description = field.description;
110
+ const placeholder = field.placeholder;
111
+ switch (field.component) {
112
+ case "checkboxgroup":
113
+ return /* @__PURE__ */ jsx(
114
+ Checkbox.Group,
115
+ {
116
+ label,
117
+ description,
118
+ value: inputProps.value ?? [],
119
+ onChange: (val) => form.setFieldValue(field.path, val),
120
+ error: inputProps.error,
121
+ children: /* @__PURE__ */ jsx(Stack, { gap: 6, mt: "xs", children: (field.options ?? []).map((option) => /* @__PURE__ */ jsx(
122
+ Checkbox,
123
+ {
124
+ value: option.value,
125
+ label: option.label,
126
+ disabled
127
+ },
128
+ option.value
129
+ )) })
130
+ },
131
+ field.path
132
+ );
133
+ case "segmented": {
134
+ const options = field.options ?? [];
135
+ return /* @__PURE__ */ jsx(
136
+ SegmentedControl,
137
+ {
138
+ fullWidth: true,
139
+ disabled,
140
+ data: options,
141
+ value: inputProps.value ?? options[0]?.value ?? "",
142
+ onChange: (val) => {
143
+ form.setFieldValue(field.path, val);
144
+ }
145
+ },
146
+ field.path
147
+ );
148
+ }
149
+ case "tags":
150
+ return /* @__PURE__ */ jsx(
151
+ TagsInput,
152
+ {
153
+ label,
154
+ description,
155
+ placeholder,
156
+ disabled,
157
+ value: inputProps.value ?? [],
158
+ onChange: (val) => form.setFieldValue(field.path, val),
159
+ error: inputProps.error,
160
+ clearable: true
161
+ },
162
+ field.path
163
+ );
164
+ case "multiselect":
165
+ return /* @__PURE__ */ jsx(
166
+ MultiSelect,
167
+ {
168
+ label,
169
+ description,
170
+ placeholder,
171
+ disabled,
172
+ data: field.options ?? [],
173
+ value: inputProps.value ?? [],
174
+ onChange: (val) => form.setFieldValue(field.path, val),
175
+ error: inputProps.error,
176
+ clearable: true
177
+ },
178
+ field.path
179
+ );
180
+ case "select":
181
+ return /* @__PURE__ */ jsx(
182
+ Select,
183
+ {
184
+ label,
185
+ description,
186
+ placeholder,
187
+ disabled,
188
+ data: field.options ?? [],
189
+ ...inputProps
190
+ },
191
+ field.path
192
+ );
193
+ case "textinput":
194
+ return /* @__PURE__ */ jsx(
195
+ TextInput,
196
+ {
197
+ label,
198
+ description,
199
+ placeholder,
200
+ disabled,
201
+ ...inputProps
202
+ },
203
+ field.path
204
+ );
205
+ case "textarea":
206
+ return /* @__PURE__ */ jsx(
207
+ Textarea,
208
+ {
209
+ label,
210
+ description,
211
+ placeholder,
212
+ disabled,
213
+ autosize: true,
214
+ minRows: 3,
215
+ ...inputProps
216
+ },
217
+ field.path
218
+ );
219
+ case "numberinput":
220
+ return /* @__PURE__ */ jsx(
221
+ NumberInput,
222
+ {
223
+ label,
224
+ description,
225
+ placeholder,
226
+ disabled,
227
+ min: field.min,
228
+ max: field.max,
229
+ step: field.step,
230
+ allowDecimal: false,
231
+ value: inputProps.value ?? 0,
232
+ onChange: (val) => form.setFieldValue(field.path, typeof val === "number" ? val : 0),
233
+ error: inputProps.error
234
+ },
235
+ field.path
236
+ );
237
+ case "switch":
238
+ return /* @__PURE__ */ jsx(
239
+ Switch,
240
+ {
241
+ label,
242
+ description,
243
+ disabled,
244
+ checked: inputProps.value ?? false,
245
+ onChange: (e) => form.setFieldValue(field.path, e.currentTarget.checked),
246
+ error: inputProps.error
247
+ },
248
+ field.path
249
+ );
250
+ case "json": {
251
+ const path = field.path;
252
+ const state = jsonState[path];
253
+ const rawValue = state?.raw ?? (() => {
254
+ const current = inputProps.value;
255
+ if (current === void 0 || current === null) return "";
256
+ if (typeof current === "string") return current;
257
+ try {
258
+ return JSON.stringify(current, null, 2);
259
+ } catch {
260
+ return "";
261
+ }
262
+ })();
263
+ return /* @__PURE__ */ jsx(
264
+ JsonInput,
265
+ {
266
+ label,
267
+ description,
268
+ placeholder,
269
+ disabled,
270
+ value: rawValue,
271
+ onChange: (next) => {
272
+ if (!next.trim()) {
273
+ setJsonState(path, { raw: next, invalid: false });
274
+ form.setFieldValue(path, void 0);
275
+ return;
276
+ }
277
+ try {
278
+ const parsed = JSON.parse(next);
279
+ setJsonState(path, { raw: next, invalid: false });
280
+ form.setFieldValue(path, parsed);
281
+ } catch {
282
+ setJsonState(path, { raw: next, invalid: true });
283
+ form.setFieldError(path, "Invalid JSON");
284
+ }
285
+ },
286
+ error: state?.invalid ? "Invalid JSON" : inputProps.error,
287
+ autosize: true,
288
+ minRows: 3,
289
+ formatOnBlur: true
290
+ },
291
+ path
292
+ );
293
+ }
294
+ default:
295
+ return null;
296
+ }
297
+ }
298
+ function renderSection(section, values, form, disabled, jsonState, setJsonState) {
299
+ if (section.when && !section.when(values)) return null;
300
+ const visibleFields = section.fields.filter((f) => !f.when || f.when(values));
301
+ return /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
302
+ section.title && /* @__PURE__ */ jsxs(Box, { children: [
303
+ /* @__PURE__ */ jsx(Title, { order: 5, children: section.title }),
304
+ section.description && /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: section.description })
305
+ ] }),
306
+ visibleFields.map((field) => renderField(field, form, disabled, jsonState, setJsonState))
307
+ ] }, section.id);
308
+ }
309
+ function StepConfigForm({
310
+ schema,
311
+ layout,
312
+ value,
313
+ onChange,
314
+ onAction,
315
+ actions,
316
+ disabled = false,
317
+ slot = "all"
318
+ }) {
319
+ const initialValues = sanitizeInput(schema, value);
320
+ const [validationError, setValidationError] = useState(null);
321
+ const [advancedOpen, setAdvancedOpen] = useState(false);
322
+ const [jsonState, setJsonStateMap] = useState({});
323
+ const validationErrorRef = useRef(null);
324
+ const setJsonState = (path, next) => {
325
+ setJsonStateMap((prev) => ({ ...prev, [path]: next }));
326
+ };
327
+ const form = useForm({
328
+ initialValues,
329
+ validate: zod4Resolver(schema),
330
+ // onValuesChange fires synchronously after each field update — no setTimeout needed.
331
+ onValuesChange: (values2) => {
332
+ if (validationErrorRef.current) {
333
+ validationErrorRef.current = null;
334
+ setValidationError(null);
335
+ }
336
+ onChange(values2);
337
+ }
338
+ });
339
+ const handleActionClick = (action) => {
340
+ const parsed = schema.safeParse(form.values);
341
+ if (!parsed.success) {
342
+ const firstIssue = parsed.error.issues[0];
343
+ const fieldErrors = {};
344
+ for (const issue of parsed.error.issues) {
345
+ const path = issue.path.join(".");
346
+ if (path && !fieldErrors[path]) {
347
+ fieldErrors[path] = issue.message;
348
+ }
349
+ }
350
+ form.setErrors(fieldErrors);
351
+ const msg = firstIssue?.message ?? "Validation failed";
352
+ validationErrorRef.current = msg;
353
+ setValidationError(msg);
354
+ return;
355
+ }
356
+ validationErrorRef.current = null;
357
+ setValidationError(null);
358
+ void onAction?.(action, parsed.data);
359
+ };
360
+ const values = form.values;
361
+ if (slot === "advanced") {
362
+ return /* @__PURE__ */ jsx(Stack, { gap: "md", children: layout.advanced ? renderSection(layout.advanced, values, form, disabled, jsonState, setJsonState) : /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "No advanced settings for this step." }) });
363
+ }
364
+ return /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
365
+ slot === "all" && validationError && /* @__PURE__ */ jsx(
366
+ Alert,
367
+ {
368
+ color: "red",
369
+ onClose: () => {
370
+ validationErrorRef.current = null;
371
+ setValidationError(null);
372
+ },
373
+ withCloseButton: true,
374
+ children: validationError
375
+ }
376
+ ),
377
+ layout.sections.map((section) => renderSection(section, values, form, disabled, jsonState, setJsonState)),
378
+ slot === "all" && layout.advanced && /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
379
+ /* @__PURE__ */ jsx(
380
+ Button,
381
+ {
382
+ variant: "subtle",
383
+ size: "xs",
384
+ onClick: () => setAdvancedOpen((o) => !o),
385
+ style: { alignSelf: "flex-start" },
386
+ children: advancedOpen ? "Hide advanced" : "Show advanced"
387
+ }
388
+ ),
389
+ /* @__PURE__ */ jsx(Collapse, { in: advancedOpen, children: renderSection(layout.advanced, values, form, disabled, jsonState, setJsonState) })
390
+ ] }),
391
+ slot === "all" && actions && actions.length > 0 && /* @__PURE__ */ jsx(Group, { gap: "sm", children: actions.map((action) => /* @__PURE__ */ jsx(
392
+ Button,
393
+ {
394
+ variant: action.variant ?? "filled",
395
+ loading: action.loading,
396
+ disabled: disabled || action.disabled,
397
+ onClick: () => handleActionClick(action),
398
+ children: action.label
399
+ },
400
+ action.id
401
+ )) })
402
+ ] });
403
+ }
404
+ var LEAD_GEN_ROUTE_LINKS = [
405
+ { label: "Overview", to: "/lead-gen" },
406
+ { label: "Lists", to: "/lead-gen/lists" },
407
+ { label: "Companies", to: "/lead-gen/companies" },
408
+ { label: "Contacts", to: "/lead-gen/contacts" }
409
+ ];
410
+ function LeadGenRouteShell({
411
+ title,
412
+ caption,
413
+ body,
414
+ links = LEAD_GEN_ROUTE_LINKS
415
+ }) {
416
+ return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsx(PageContainer, { children: /* @__PURE__ */ jsxs(Stack, { children: [
417
+ /* @__PURE__ */ jsx(PageTitleCaption, { title, caption }),
418
+ /* @__PURE__ */ jsx(Paper, { withBorder: true, p: "lg", children: /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
419
+ body,
420
+ /* @__PURE__ */ jsx(Text, { children: links.map((link, index) => /* @__PURE__ */ jsxs("span", { children: [
421
+ index > 0 ? " | " : null,
422
+ /* @__PURE__ */ jsx(Anchor, { component: Link, to: link.to, children: link.label })
423
+ ] }, link.to)) })
424
+ ] }) })
425
+ ] }) }) });
426
+ }
427
+ function formatDate(dateValue) {
428
+ const date = typeof dateValue === "string" ? new Date(dateValue) : dateValue;
429
+ return date.toLocaleDateString("en-US", {
430
+ month: "short",
431
+ day: "numeric",
432
+ year: "numeric"
433
+ });
434
+ }
435
+ function getStatusColor(status) {
436
+ return status === "active" ? "green" : status === "invalid" ? "red" : "gray";
437
+ }
438
+ function getStateKeyColor(stateKey) {
439
+ switch (stateKey) {
440
+ case "draft":
441
+ return "gray";
442
+ case "enriching":
443
+ return "blue";
444
+ case "launched":
445
+ return "green";
446
+ case "closing":
447
+ return "yellow";
448
+ case "archived":
449
+ return "dimmed";
450
+ default:
451
+ return "gray";
452
+ }
453
+ }
454
+ function getEnrichmentColor(status) {
455
+ switch (status) {
456
+ case "complete":
457
+ return "green";
458
+ case "pending":
459
+ return "yellow";
460
+ case "failed":
461
+ return "red";
462
+ default:
463
+ return "gray";
464
+ }
465
+ }
466
+ function getEnrichmentStatus(enrichmentData) {
467
+ if (!enrichmentData || typeof enrichmentData !== "object") return "pending";
468
+ const website = enrichmentData.website;
469
+ const linkedin = enrichmentData.linkedin;
470
+ if (website === "complete" && linkedin === "complete") return "complete";
471
+ if (website === "failed" || linkedin === "failed") return "failed";
472
+ return "pending";
473
+ }
474
+ function useDeleteLists() {
475
+ const { apiRequest, workOSOrganizationId } = useElevasisServices();
476
+ const queryClient = useQueryClient();
477
+ return useMutation({
478
+ mutationFn: async (listIds) => {
479
+ const uniqueIds = [...new Set(listIds)].filter(Boolean);
480
+ if (uniqueIds.length === 0) return;
481
+ await Promise.all(
482
+ uniqueIds.map(
483
+ (listId) => apiRequest(`/acquisition/lists/${listId}`, {
484
+ method: "DELETE"
485
+ })
486
+ )
487
+ );
488
+ },
489
+ onSuccess: () => {
490
+ queryClient.invalidateQueries({ queryKey: acquisitionListKeys.list(workOSOrganizationId) });
491
+ queryClient.invalidateQueries({ queryKey: acquisitionListKeys.telemetry(workOSOrganizationId) });
492
+ showSuccessNotification("Lists deleted");
493
+ },
494
+ onError: (error) => {
495
+ showApiErrorNotification(error);
496
+ }
497
+ });
498
+ }
499
+ function isLeadGenExportAction(action) {
500
+ if (!action) return false;
501
+ const ontologyActionSegment = action.split(":action/")[1];
502
+ if (ontologyActionSegment) {
503
+ return ontologyActionSegment === "export" || ontologyActionSegment.startsWith("export.") || ontologyActionSegment.startsWith("export-");
504
+ }
505
+ return action === "lead-gen.export.list" || action.includes(".export.");
506
+ }
507
+ function getLeadGenExportWorkflowId(model) {
508
+ return Object.values(model.resources ?? {}).find(
509
+ (resource) => resource.kind === "workflow" && resource.systemPath === "sales.lead-gen" && (isLeadGenExportAction(resource.ontology?.primaryAction) || resource.ontology?.actions?.some(isLeadGenExportAction) === true)
510
+ )?.id;
511
+ }
512
+ function toBuildStepDefinition(step) {
513
+ return {
514
+ ...step,
515
+ description: step.description ?? `Run ${step.label.toLowerCase()} for this list.`,
516
+ outputs: [...step.outputs],
517
+ emptyBlockedText: step.emptyBlockedText ?? "Complete prerequisite build steps before this action can run."
518
+ };
519
+ }
520
+ function getDefaultBuildTemplateId(model, templates) {
521
+ const configuredId = getSystem(model, "sales.lead-gen")?.config?.defaultBuildTemplateId;
522
+ if (typeof configuredId === "string" && templates.some((template) => template.id === configuredId)) return configuredId;
523
+ return templates[0]?.id;
524
+ }
525
+ function getDefaultBuildSteps(templates, defaultBuildTemplateId) {
526
+ const template = templates.find((item) => item.id === defaultBuildTemplateId) ?? templates[0];
527
+ return template?.steps.map((step) => toBuildStepDefinition(step)) ?? [];
528
+ }
529
+ function summarizeReadinessIssues(issues) {
530
+ return issues[0]?.message;
531
+ }
532
+ function getLeadGenApiInterfaceReadiness(model) {
533
+ if (!model) {
534
+ return {
535
+ systemPath: LEAD_GEN_API_INTERFACE.systemPath,
536
+ interfaceKey: LEAD_GEN_API_INTERFACE.interfaceKey,
537
+ readinessProfile: LEAD_GEN_API_INTERFACE.readinessProfile,
538
+ declared: false,
539
+ ready: false,
540
+ reason: "No Organization Model is available for lead-gen System Interface readiness.",
541
+ issues: [
542
+ {
543
+ family: "SYSTEM_INTERFACE_MISSING",
544
+ code: "missing-organization-model",
545
+ message: "No Organization Model is available for lead-gen System Interface readiness."
546
+ }
547
+ ]
548
+ };
549
+ }
550
+ const readiness = computeInterfaceReadiness(model, LEAD_GEN_API_INTERFACE);
551
+ return {
552
+ systemPath: LEAD_GEN_API_INTERFACE.systemPath,
553
+ interfaceKey: LEAD_GEN_API_INTERFACE.interfaceKey,
554
+ readinessProfile: readiness.readinessProfile,
555
+ declared: getSystem(model, LEAD_GEN_API_INTERFACE.systemPath)?.apiInterface !== void 0,
556
+ ready: readiness.ready,
557
+ reason: summarizeReadinessIssues(readiness.issues),
558
+ issues: readiness.issues
559
+ };
560
+ }
561
+ function useLeadGenConfig() {
562
+ const systems = useOptionalElevasisSystems();
563
+ const organizationModel = systems?.organizationModel;
564
+ return useMemo(() => {
565
+ const leadGenApiInterface = getLeadGenApiInterfaceReadiness(organizationModel);
566
+ const interfaceState = {
567
+ leadGenApiInterface,
568
+ canUseLeadGenApiActions: leadGenApiInterface.ready
569
+ };
570
+ if (!organizationModel) return interfaceState;
571
+ const buildTemplates = getAllBuildTemplates(organizationModel);
572
+ const defaultBuildTemplateId = getDefaultBuildTemplateId(organizationModel, buildTemplates);
573
+ return {
574
+ stageCatalog: getLeadGenStageCatalog(organizationModel),
575
+ exportWorkflowId: getLeadGenExportWorkflowId(organizationModel),
576
+ defaultBuildTemplateId,
577
+ defaultBuildSteps: getDefaultBuildSteps(buildTemplates, defaultBuildTemplateId),
578
+ buildTemplates,
579
+ ...interfaceState
580
+ };
581
+ }, [organizationModel]);
582
+ }
583
+ var DATA_MODE_OPTIONS = [
584
+ { value: "mock", label: "Mock data" },
585
+ { value: "live", label: "Live data" }
586
+ ];
587
+ function CreateListModal({ opened, onClose, canCreate, onCreated }) {
588
+ const leadGenConfig = useLeadGenConfig();
589
+ const createListMutation = useCreateList();
590
+ const buildTemplateOptions = useMemo(
591
+ () => getProspectingBuildTemplateOptions(leadGenConfig.buildTemplates ?? []).map((template) => ({
592
+ value: template.id,
593
+ label: template.label
594
+ })),
595
+ [leadGenConfig.buildTemplates]
596
+ );
597
+ const defaultBuildTemplateId = leadGenConfig.defaultBuildTemplateId ?? buildTemplateOptions[0]?.value ?? "";
598
+ const [newListName, setNewListName] = useState("");
599
+ const [newListDescription, setNewListDescription] = useState("");
600
+ const [newBuildTemplateId, setNewBuildTemplateId] = useState("");
601
+ const [newDataMode, setNewDataMode] = useState("mock");
602
+ const selectedBuildTemplateId = newBuildTemplateId || defaultBuildTemplateId;
603
+ const selectedBuildTemplate = leadGenConfig.buildTemplates?.find((template) => template.id === selectedBuildTemplateId);
604
+ function resetForm() {
605
+ setNewListName("");
606
+ setNewListDescription("");
607
+ setNewBuildTemplateId("");
608
+ setNewDataMode("mock");
609
+ }
610
+ function handleClose() {
611
+ if (createListMutation.isPending) return;
612
+ resetForm();
613
+ onClose();
614
+ }
615
+ function handleCreateList() {
616
+ if (!canCreate) return;
617
+ const trimmedName = newListName.trim();
618
+ if (!trimmedName) return;
619
+ const body = {
620
+ name: trimmedName,
621
+ description: newListDescription.trim() || null,
622
+ ...selectedBuildTemplateId ? { buildTemplateId: selectedBuildTemplateId } : {},
623
+ pipelineConfig: {
624
+ dataMode: newDataMode
625
+ }
626
+ };
627
+ createListMutation.mutate(body, {
628
+ onSuccess: (list) => {
629
+ resetForm();
630
+ onClose();
631
+ onCreated?.(list);
632
+ }
633
+ });
634
+ }
635
+ return /* @__PURE__ */ jsx(CustomModal, { opened, onClose: handleClose, size: "md", loading: createListMutation.isPending, children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
636
+ /* @__PURE__ */ jsxs(Group, { gap: "sm", children: [
637
+ /* @__PURE__ */ jsx(IconSparkles, { size: 24 }),
638
+ /* @__PURE__ */ jsx(Title, { order: 4, children: "Create List" })
639
+ ] }),
640
+ /* @__PURE__ */ jsx(
641
+ TextInput,
642
+ {
643
+ label: "List Name",
644
+ placeholder: "e.g. Orange County Vets Q2",
645
+ value: newListName,
646
+ onChange: (event) => setNewListName(event.currentTarget.value),
647
+ disabled: createListMutation.isPending,
648
+ required: true
649
+ }
650
+ ),
651
+ /* @__PURE__ */ jsx(
652
+ Textarea,
653
+ {
654
+ label: "Description",
655
+ placeholder: "Optional context for this list",
656
+ value: newListDescription,
657
+ onChange: (event) => setNewListDescription(event.currentTarget.value),
658
+ disabled: createListMutation.isPending,
659
+ minRows: 3
660
+ }
661
+ ),
662
+ /* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
663
+ /* @__PURE__ */ jsx(
664
+ Select,
665
+ {
666
+ label: "Build Pipeline",
667
+ description: "Choose the sequence this list should follow.",
668
+ data: buildTemplateOptions,
669
+ value: selectedBuildTemplateId || null,
670
+ onChange: (value) => setNewBuildTemplateId(value ?? ""),
671
+ disabled: createListMutation.isPending || buildTemplateOptions.length === 0,
672
+ required: true
673
+ }
674
+ ),
675
+ selectedBuildTemplate?.description ? /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: selectedBuildTemplate.description }) : null
676
+ ] }),
677
+ /* @__PURE__ */ jsx(
678
+ Select,
679
+ {
680
+ label: "Data Mode",
681
+ description: "Choose whether sourcing and enrichment use demo fixtures or live providers.",
682
+ data: DATA_MODE_OPTIONS,
683
+ value: newDataMode,
684
+ onChange: (value) => setNewDataMode(value ?? "mock"),
685
+ disabled: createListMutation.isPending,
686
+ required: true
687
+ }
688
+ ),
689
+ /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "The list will open in draft mode after creation so you can review configuration and run build steps from the list workspace." }),
690
+ /* @__PURE__ */ jsxs(Group, { justify: "flex-end", children: [
691
+ /* @__PURE__ */ jsx(Button, { variant: "light", onClick: handleClose, disabled: createListMutation.isPending, children: "Cancel" }),
692
+ /* @__PURE__ */ jsx(
693
+ Button,
694
+ {
695
+ onClick: handleCreateList,
696
+ loading: createListMutation.isPending,
697
+ disabled: !newListName.trim() || !canCreate,
698
+ children: "Create List"
699
+ }
700
+ )
701
+ ] })
702
+ ] }) });
703
+ }
704
+
705
+ // src/features/lead-gen/pages/overview-telemetry.ts
706
+ function computeBacklog(current, completed) {
707
+ return Math.max(current - completed, 0);
708
+ }
709
+ function getOrderedStageEntries(stageCatalog) {
710
+ return Object.values(stageCatalog).sort((a, b) => a.order - b.order || a.key.localeCompare(b.key));
711
+ }
712
+ function resolveReadinessStages(orderedStageEntries) {
713
+ const idx = orderedStageEntries.findIndex((e) => e.readinessTarget === true);
714
+ return {
715
+ readinessTargetEntry: idx >= 0 ? orderedStageEntries[idx] : void 0,
716
+ predecessorEntry: idx > 0 ? orderedStageEntries[idx - 1] : void 0
717
+ };
718
+ }
719
+ function computeStageTotals(lists) {
720
+ return lists.reduce((acc, list) => {
721
+ for (const [key, count] of Object.entries(list.stageCounts)) {
722
+ acc[key] = (acc[key] ?? 0) + count;
723
+ }
724
+ return acc;
725
+ }, {});
726
+ }
727
+ function computeReadyBacklog(stageTotals, orderedStageEntries, readiness) {
728
+ if (readiness.readinessTargetEntry) {
729
+ return computeBacklog(
730
+ stageTotals[readiness.predecessorEntry?.key ?? ""] ?? 0,
731
+ stageTotals[readiness.readinessTargetEntry.key] ?? 0
732
+ );
733
+ }
734
+ return computeBacklog(
735
+ stageTotals[orderedStageEntries[orderedStageEntries.length - 2]?.key ?? ""] ?? 0,
736
+ stageTotals[orderedStageEntries[orderedStageEntries.length - 1]?.key ?? ""] ?? 0
737
+ );
738
+ }
739
+ function getOverviewStatus(list, readinessTargetEntry, predecessorEntry) {
740
+ if ((list.activeWorkflows?.length ?? 0) > 0) {
741
+ return { label: "Active Work", color: "green" };
742
+ }
743
+ if (readinessTargetEntry && predecessorEntry) {
744
+ const predecessorCount = list.stageCounts[predecessorEntry.key] ?? 0;
745
+ const targetCount = list.stageCounts[readinessTargetEntry.key] ?? 0;
746
+ if (predecessorCount > 0 && targetCount >= predecessorCount) {
747
+ return { label: "Complete", color: "blue" };
748
+ }
749
+ }
750
+ return { label: "Idle", color: "gray" };
751
+ }
752
+ function getNextFocus(list, orderedStageEntries) {
753
+ if ((list.activeWorkflows?.length ?? 0) > 0) {
754
+ return { label: "Workflow running", count: list.activeWorkflows?.length ?? 0 };
755
+ }
756
+ if (list.totalCompanies === 0 && list.totalContacts === 0) {
757
+ return { label: "Populate list", count: null };
758
+ }
759
+ for (let i = 1; i < orderedStageEntries.length; i++) {
760
+ const predecessor = orderedStageEntries[i - 1];
761
+ const current = orderedStageEntries[i];
762
+ if (!predecessor || !current) continue;
763
+ const gap = computeBacklog(list.stageCounts[predecessor.key] ?? 0, list.stageCounts[current.key] ?? 0);
764
+ if (gap > 0) {
765
+ return { label: current.label, count: gap };
766
+ }
767
+ }
768
+ return { label: "Complete", count: null };
769
+ }
770
+ function computeListTotalBacklog(list, orderedStageEntries) {
771
+ let total = 0;
772
+ for (let i = 1; i < orderedStageEntries.length; i++) {
773
+ const predecessor = orderedStageEntries[i - 1];
774
+ const current = orderedStageEntries[i];
775
+ if (!predecessor || !current) continue;
776
+ total += computeBacklog(list.stageCounts[predecessor.key] ?? 0, list.stageCounts[current.key] ?? 0);
777
+ }
778
+ return total;
779
+ }
780
+ function formatCountLabel(value, noun) {
781
+ return `${value} ${noun}${value === 1 ? "" : "s"}`;
782
+ }
783
+ function LeadGenOverviewPage({ headerActions, tabs, footer } = {}) {
784
+ const navigate = useNavigate();
785
+ const telemetryQuery = useListsTelemetry();
786
+ const listsQuery = useLists();
787
+ const leadGenConfig = useLeadGenConfig();
788
+ const canManageLeadGen = useAccess(AccessKeys.leadGenManage);
789
+ const [showCreateList, setShowCreateList] = useState(false);
790
+ const data = telemetryQuery.data;
791
+ const listMetaById = new Map((listsQuery.data ?? []).map((list) => [list.id, list]));
792
+ const stageCatalog = leadGenConfig.stageCatalog ?? {};
793
+ const orderedStageEntries = getOrderedStageEntries(stageCatalog);
794
+ const { readinessTargetEntry, predecessorEntry } = resolveReadinessStages(orderedStageEntries);
795
+ const stageTotals = computeStageTotals(data ?? []);
796
+ const readyBacklog = computeReadyBacklog(stageTotals, orderedStageEntries, { readinessTargetEntry, predecessorEntry });
797
+ const deliverabilityTotals = data?.reduce(
798
+ (acc, list) => ({
799
+ valid: acc.valid + list.deliverability.valid,
800
+ risky: acc.risky + list.deliverability.risky,
801
+ invalid: acc.invalid + list.deliverability.invalid,
802
+ unknown: acc.unknown + list.deliverability.unknown,
803
+ bounced: acc.bounced + list.deliverability.bounced
804
+ }),
805
+ { valid: 0, risky: 0, invalid: 0, unknown: 0, bounced: 0 }
806
+ ) ?? { risky: 0, invalid: 0, bounced: 0 };
807
+ const deliverabilityRiskCount = deliverabilityTotals.risky + deliverabilityTotals.invalid + deliverabilityTotals.bounced;
808
+ const totalLists = data?.length ?? 0;
809
+ const canUseApiActions = leadGenConfig.canUseLeadGenApiActions;
810
+ const canCreateLists = canManageLeadGen.allowed && canUseApiActions;
811
+ const apiDisabledReason = leadGenConfig.leadGenApiInterface.reason;
812
+ const readySummaryLabel = readinessTargetEntry ? `ready: ${readinessTargetEntry.label.toLowerCase()}` : "next-stage backlog";
813
+ const readyCardLabel = readinessTargetEntry ? `Ready: ${readinessTargetEntry.label}` : "Next-stage backlog";
814
+ const summaryLine = [
815
+ `${totalLists} total ${totalLists === 1 ? "list" : "lists"}`,
816
+ `${formatCountLabel(readyBacklog, "contact")} ${readySummaryLabel}`,
817
+ deliverabilityRiskCount === 0 ? "no risks" : `${formatCountLabel(deliverabilityRiskCount, "risk")}`
818
+ ].join(" | ");
819
+ const overviewRows = data?.map((list) => {
820
+ const listMeta = listMetaById.get(list.listId);
821
+ const nextFocus = getNextFocus(list, orderedStageEntries);
822
+ return {
823
+ ...list,
824
+ name: listMeta?.name ?? `List ${list.listId.slice(0, 8)}`,
825
+ nextFocus,
826
+ status: getOverviewStatus(list, readinessTargetEntry, predecessorEntry)
827
+ };
828
+ }).sort((a, b) => {
829
+ const aActive = a.status.label === "Active Work" ? 1 : 0;
830
+ const bActive = b.status.label === "Active Work" ? 1 : 0;
831
+ if (aActive !== bActive) return bActive - aActive;
832
+ const aBacklog = computeListTotalBacklog(a, orderedStageEntries);
833
+ const bBacklog = computeListTotalBacklog(b, orderedStageEntries);
834
+ if (aBacklog !== bBacklog) return bBacklog - aBacklog;
835
+ return b.totalContacts - a.totalContacts;
836
+ }) ?? [];
837
+ return /* @__PURE__ */ jsxs(SubshellContentContainer, { children: [
838
+ /* @__PURE__ */ jsx(PageContainer, { children: /* @__PURE__ */ jsxs(Stack, { children: [
839
+ /* @__PURE__ */ jsx(PageTitleCaption, { title: "Lead Gen Overview", caption: summaryLine }),
840
+ headerActions ? /* @__PURE__ */ jsx(Group, { gap: "xs", children: headerActions }) : null,
841
+ tabs ?? null,
842
+ telemetryQuery.isLoading ? /* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsx(Center, { p: "xl", children: /* @__PURE__ */ jsx(Loader, {}) }) }) : telemetryQuery.isError ? /* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsx(CenteredErrorState, { error: telemetryQuery.error, title: "Failed to load list telemetry" }) }) : !data?.length ? /* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsx(Center, { h: 300, children: /* @__PURE__ */ jsx(Alert, { icon: /* @__PURE__ */ jsx(IconAlertCircle, { size: 16 }), color: "gray", variant: "light", children: "No lists yet." }) }) }) : /* @__PURE__ */ jsxs(Fragment, { children: [
843
+ /* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 1, sm: 3 }, children: [
844
+ /* @__PURE__ */ jsx(StatCard, { variant: "hero", icon: IconPlayerPlay, value: totalLists, label: "Total Lists" }),
845
+ /* @__PURE__ */ jsx(StatCard, { variant: "hero", icon: IconArrowRight, value: readyBacklog, label: readyCardLabel }),
846
+ /* @__PURE__ */ jsx(StatCard, { variant: "hero", icon: IconAlertCircle, value: deliverabilityRiskCount, label: "At Risk" })
847
+ ] }),
848
+ /* @__PURE__ */ jsx(Paper, { withBorder: true, p: "md", children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
849
+ /* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "flex-start", gap: "md", children: [
850
+ /* @__PURE__ */ jsx(
851
+ CardHeader,
852
+ {
853
+ icon: /* @__PURE__ */ jsx(IconListDetails, { size: 18 }),
854
+ title: "List Snapshot",
855
+ subtitle: "Ranked to surface active work first, then the largest unresolved backlogs."
856
+ }
857
+ ),
858
+ canManageLeadGen.allowed ? /* @__PURE__ */ jsx(
859
+ Button,
860
+ {
861
+ leftSection: /* @__PURE__ */ jsx(IconPlus, { size: 16 }),
862
+ onClick: () => setShowCreateList(true),
863
+ size: "xs",
864
+ disabled: !canUseApiActions,
865
+ title: !canUseApiActions ? apiDisabledReason : void 0,
866
+ children: "Create List"
867
+ }
868
+ ) : null
869
+ ] }),
870
+ /* @__PURE__ */ jsxs(Table, { children: [
871
+ /* @__PURE__ */ jsx(Table.Thead, { children: /* @__PURE__ */ jsxs(Table.Tr, { children: [
872
+ /* @__PURE__ */ jsx(Table.Th, { children: "List" }),
873
+ /* @__PURE__ */ jsx(Table.Th, { children: "Companies" }),
874
+ /* @__PURE__ */ jsx(Table.Th, { children: "Contacts" }),
875
+ /* @__PURE__ */ jsx(Table.Th, { children: "Next Focus" }),
876
+ /* @__PURE__ */ jsx(Table.Th, { children: "Status" })
877
+ ] }) }),
878
+ /* @__PURE__ */ jsx(Table.Tbody, { children: overviewRows.map((list) => /* @__PURE__ */ jsxs(
879
+ Table.Tr,
880
+ {
881
+ style: { cursor: "pointer" },
882
+ onClick: () => navigate({ to: "/lead-gen/lists/$listId", params: { listId: list.listId } }),
883
+ children: [
884
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsxs(Stack, { gap: 2, children: [
885
+ /* @__PURE__ */ jsx(Text, { size: "sm", fw: 600, children: list.name }),
886
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: list.listId })
887
+ ] }) }),
888
+ /* @__PURE__ */ jsx(Table.Td, { children: list.totalCompanies }),
889
+ /* @__PURE__ */ jsx(Table.Td, { children: list.totalContacts }),
890
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsxs(Stack, { gap: 2, children: [
891
+ /* @__PURE__ */ jsx(Text, { size: "sm", fw: 500, children: list.nextFocus.label }),
892
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: list.nextFocus.count == null ? "No outstanding count" : `${list.nextFocus.count} item${list.nextFocus.count === 1 ? "" : "s"} queued` })
893
+ ] }) }),
894
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: list.status.color, children: list.status.label }) })
895
+ ]
896
+ },
897
+ list.listId
898
+ )) })
899
+ ] })
900
+ ] }) })
901
+ ] }),
902
+ /* @__PURE__ */ jsx(
903
+ CreateListModal,
904
+ {
905
+ opened: showCreateList,
906
+ onClose: () => setShowCreateList(false),
907
+ canCreate: canCreateLists,
908
+ onCreated: (list) => navigate({ to: "/lead-gen/lists/$listId", params: { listId: list.id } })
909
+ }
910
+ )
911
+ ] }) }),
912
+ footer ?? null
913
+ ] });
914
+ }
915
+ var PAGE_SIZE_DEFAULT = 20;
916
+ var STATUS_FILTER_OPTIONS = [
917
+ { value: "", label: "All statuses" },
918
+ { value: "draft", label: "Draft" },
919
+ { value: "enriching", label: "Enriching" },
920
+ { value: "launched", label: "Launched" },
921
+ { value: "closing", label: "Closing" },
922
+ { value: "archived", label: "Archived" }
923
+ ];
924
+ function LeadGenListsPage() {
925
+ const navigate = useNavigate();
926
+ const leadGenConfig = useLeadGenConfig();
927
+ const [searchQuery, setSearchQuery] = useState("");
928
+ const [statusFilter, setStatusFilter] = useState("");
929
+ const [showCreateList, setShowCreateList] = useState(false);
930
+ const [showBatchDelete, setShowBatchDelete] = useState(false);
931
+ const { data: lists, isLoading: listsLoading } = useLists();
932
+ const { data: telemetry, isLoading: telemetryLoading } = useListsTelemetry();
933
+ const canManageLeadGen = useAccess(AccessKeys.leadGenManage);
934
+ const deleteListsMutation = useDeleteLists();
935
+ const { sort, toggleSort } = useTableSort("created");
936
+ const contactCountByListId = useMemo(
937
+ () => new Map((telemetry ?? []).map((item) => [item.listId, item.totalContacts])),
938
+ [telemetry]
939
+ );
940
+ const sortAccessors = useMemo(
941
+ () => ({
942
+ name: (list) => list.name,
943
+ description: (list) => list.description || "",
944
+ contacts: (list) => contactCountByListId.get(list.id) ?? 0,
945
+ batches: (list) => list.batchIds.length,
946
+ created: (list) => list.createdAt,
947
+ status: (list) => list.status
948
+ }),
949
+ [contactCountByListId]
950
+ );
951
+ const filteredLists = useMemo(() => {
952
+ if (!lists) return [];
953
+ let result = lists;
954
+ if (searchQuery.trim()) {
955
+ const query = searchQuery.toLowerCase();
956
+ result = result.filter((list) => list.name.toLowerCase().includes(query));
957
+ }
958
+ if (statusFilter) {
959
+ result = result.filter((list) => list.status === statusFilter);
960
+ }
961
+ return result;
962
+ }, [lists, searchQuery, statusFilter]);
963
+ const sortedLists = useMemo(() => sortData(filteredLists, sort, sortAccessors), [filteredLists, sort, sortAccessors]);
964
+ const pagination = usePaginationState(PAGE_SIZE_DEFAULT, [searchQuery, statusFilter], sortedLists.length);
965
+ const paginatedLists = useMemo(
966
+ () => sortedLists.slice(pagination.offset, pagination.offset + PAGE_SIZE_DEFAULT),
967
+ [sortedLists, pagination.offset]
968
+ );
969
+ const selection = useTableSelection(paginatedLists, sortedLists);
970
+ const canUseApiActions = leadGenConfig.canUseLeadGenApiActions;
971
+ const apiDisabledReason = leadGenConfig.leadGenApiInterface.reason;
972
+ const canCreateOrDeleteLists = canManageLeadGen.allowed && canUseApiActions;
973
+ return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsxs(PageContainer, { children: [
974
+ /* @__PURE__ */ jsx(Stack, { children: /* @__PURE__ */ jsx(
975
+ PageTitleCaption,
976
+ {
977
+ title: "Lists",
978
+ caption: "Lead lists and contact organization",
979
+ rightSection: canManageLeadGen.allowed ? /* @__PURE__ */ jsx(
980
+ Button,
981
+ {
982
+ leftSection: /* @__PURE__ */ jsx(IconPlus, { size: 16 }),
983
+ onClick: () => setShowCreateList(true),
984
+ variant: "light",
985
+ size: "xs",
986
+ disabled: !canUseApiActions,
987
+ title: !canUseApiActions ? apiDisabledReason : void 0,
988
+ children: "New List"
989
+ }
990
+ ) : null
991
+ }
992
+ ) }),
993
+ !canUseApiActions ? /* @__PURE__ */ jsx(Alert, { icon: /* @__PURE__ */ jsx(IconAlertTriangle, { size: 16 }), color: "yellow", variant: "light", children: /* @__PURE__ */ jsx(Text, { size: "sm", children: "Lead-gen API actions are disabled until System Interface readiness passes. Existing list data can still be reviewed when available." }) }) : null,
994
+ /* @__PURE__ */ jsx(Paper, { children: /* @__PURE__ */ jsxs(Stack, { children: [
995
+ /* @__PURE__ */ jsxs(
996
+ FilterBar,
997
+ {
998
+ actions: /* @__PURE__ */ jsx(
999
+ TableSelectionToolbar,
1000
+ {
1001
+ selectedCount: selection.selectedCount,
1002
+ onDelete: canCreateOrDeleteLists ? () => setShowBatchDelete(true) : void 0,
1003
+ isDeleting: deleteListsMutation.isPending
1004
+ }
1005
+ ),
1006
+ children: [
1007
+ /* @__PURE__ */ jsx(
1008
+ TextInput,
1009
+ {
1010
+ placeholder: "Search by name...",
1011
+ leftSection: /* @__PURE__ */ jsx(IconSearch, { size: 16 }),
1012
+ value: searchQuery,
1013
+ onChange: (e) => setSearchQuery(e.currentTarget.value)
1014
+ }
1015
+ ),
1016
+ /* @__PURE__ */ jsx(
1017
+ Select,
1018
+ {
1019
+ placeholder: "All statuses",
1020
+ data: STATUS_FILTER_OPTIONS,
1021
+ value: statusFilter,
1022
+ onChange: (value) => setStatusFilter(value ?? ""),
1023
+ clearable: true,
1024
+ size: "sm",
1025
+ w: 160
1026
+ }
1027
+ )
1028
+ ]
1029
+ }
1030
+ ),
1031
+ listsLoading || telemetryLoading ? /* @__PURE__ */ jsx(Center, { p: "xl", children: /* @__PURE__ */ jsx(Loader, {}) }) : !filteredLists.length ? /* @__PURE__ */ jsx(
1032
+ EmptyState,
1033
+ {
1034
+ icon: IconList,
1035
+ title: searchQuery.trim() || statusFilter ? "No lists match your filters" : "No lists yet",
1036
+ description: searchQuery.trim() || statusFilter ? void 0 : !canUseApiActions ? "Lead-gen System Interface readiness is missing, so list creation is disabled." : "Create one to get started.",
1037
+ action: searchQuery.trim() || statusFilter || !canCreateOrDeleteLists ? void 0 : {
1038
+ label: "Create List",
1039
+ onClick: () => setShowCreateList(true),
1040
+ icon: /* @__PURE__ */ jsx(IconPlus, { size: 16 })
1041
+ }
1042
+ }
1043
+ ) : /* @__PURE__ */ jsxs(Table, { children: [
1044
+ /* @__PURE__ */ jsx(Table.Thead, { children: /* @__PURE__ */ jsxs(Table.Tr, { children: [
1045
+ /* @__PURE__ */ jsx(Table.Th, { w: 40, children: /* @__PURE__ */ jsx(
1046
+ Checkbox,
1047
+ {
1048
+ checked: selection.isPageAllSelected,
1049
+ indeterminate: selection.isPagePartiallySelected,
1050
+ onChange: selection.togglePage
1051
+ }
1052
+ ) }),
1053
+ /* @__PURE__ */ jsx(SortableHeader, { column: "name", sort, onToggle: toggleSort, children: "Name" }),
1054
+ /* @__PURE__ */ jsx(SortableHeader, { column: "description", sort, onToggle: toggleSort, children: "Description" }),
1055
+ /* @__PURE__ */ jsx(SortableHeader, { column: "status", sort, onToggle: toggleSort, children: "Status" }),
1056
+ /* @__PURE__ */ jsx(Table.Th, { children: "Pipeline" }),
1057
+ /* @__PURE__ */ jsx(SortableHeader, { column: "contacts", sort, onToggle: toggleSort, children: "Contacts" }),
1058
+ /* @__PURE__ */ jsx(SortableHeader, { column: "batches", sort, onToggle: toggleSort, children: "Batches" }),
1059
+ /* @__PURE__ */ jsx(SortableHeader, { column: "created", sort, onToggle: toggleSort, children: "Created" }),
1060
+ /* @__PURE__ */ jsx(Table.Th, { children: "List" })
1061
+ ] }) }),
1062
+ /* @__PURE__ */ jsx(Table.Tbody, { children: paginatedLists.map((list) => {
1063
+ const pipelineLabel = list.metadata.buildPlanSnapshot?.templateLabel;
1064
+ return /* @__PURE__ */ jsxs(
1065
+ Table.Tr,
1066
+ {
1067
+ style: { cursor: "pointer" },
1068
+ onClick: () => navigate({ to: "/lead-gen/lists/$listId", params: { listId: list.id } }),
1069
+ children: [
1070
+ /* @__PURE__ */ jsx(Table.Td, { onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsx(
1071
+ Checkbox,
1072
+ {
1073
+ checked: selection.isSelected(list.id),
1074
+ onChange: () => selection.toggle(list.id)
1075
+ }
1076
+ ) }),
1077
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsxs(Group, { gap: 6, wrap: "nowrap", children: [
1078
+ /* @__PURE__ */ jsx(Text, { fw: 500, children: list.name }),
1079
+ list.instantlyCampaignId ? /* @__PURE__ */ jsx(
1080
+ Anchor,
1081
+ {
1082
+ href: `https://app.instantly.ai/app/campaign/${list.instantlyCampaignId}/analytics`,
1083
+ target: "_blank",
1084
+ rel: "noreferrer",
1085
+ onClick: (e) => e.stopPropagation(),
1086
+ c: "dimmed",
1087
+ lh: 1,
1088
+ children: /* @__PURE__ */ jsx(IconExternalLink, { size: 16 })
1089
+ }
1090
+ ) : null
1091
+ ] }) }),
1092
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: list.description || "-" }) }),
1093
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Badge, { size: "sm", color: getStateKeyColor(list.status), variant: "light", children: list.status }) }),
1094
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: pipelineLabel || "\u2014" }) }),
1095
+ /* @__PURE__ */ jsx(Table.Td, { children: contactCountByListId.get(list.id) ?? 0 }),
1096
+ /* @__PURE__ */ jsx(Table.Td, { children: list.batchIds.length }),
1097
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: formatDate(list.createdAt) }) }),
1098
+ /* @__PURE__ */ jsx(Table.Td, { onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsx(
1099
+ Button,
1100
+ {
1101
+ size: "xs",
1102
+ variant: "subtle",
1103
+ leftSection: /* @__PURE__ */ jsx(IconList, { size: 14 }),
1104
+ onClick: () => navigate({ to: "/lead-gen/lists/$listId", params: { listId: list.id } }),
1105
+ children: "Open"
1106
+ }
1107
+ ) })
1108
+ ]
1109
+ },
1110
+ list.id
1111
+ );
1112
+ }) })
1113
+ ] }),
1114
+ sortedLists.length > PAGE_SIZE_DEFAULT && /* @__PURE__ */ jsx(Group, { justify: "center", children: /* @__PURE__ */ jsx(
1115
+ Pagination,
1116
+ {
1117
+ value: pagination.page,
1118
+ onChange: pagination.setPage,
1119
+ total: pagination.totalPages(sortedLists.length),
1120
+ size: "sm"
1121
+ }
1122
+ ) })
1123
+ ] }) }),
1124
+ /* @__PURE__ */ jsx(
1125
+ CreateListModal,
1126
+ {
1127
+ opened: showCreateList,
1128
+ onClose: () => setShowCreateList(false),
1129
+ canCreate: canCreateOrDeleteLists,
1130
+ onCreated: (list) => navigate({ to: "/lead-gen/lists/$listId", params: { listId: list.id } })
1131
+ }
1132
+ ),
1133
+ /* @__PURE__ */ jsx(
1134
+ CustomModal,
1135
+ {
1136
+ opened: showBatchDelete,
1137
+ onClose: () => !deleteListsMutation.isPending && setShowBatchDelete(false),
1138
+ size: "sm",
1139
+ loading: deleteListsMutation.isPending,
1140
+ children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
1141
+ /* @__PURE__ */ jsxs(Group, { gap: "sm", children: [
1142
+ /* @__PURE__ */ jsx(IconAlertTriangle, { size: 24, color: "var(--color-error)" }),
1143
+ /* @__PURE__ */ jsxs(Title, { order: 4, children: [
1144
+ "Delete ",
1145
+ selection.selectedCount,
1146
+ " Lists"
1147
+ ] })
1148
+ ] }),
1149
+ /* @__PURE__ */ jsxs(Text, { size: "sm", children: [
1150
+ "Are you sure you want to delete",
1151
+ " ",
1152
+ /* @__PURE__ */ jsx(Text, { span: true, fw: 600, children: selection.selectedCount }),
1153
+ " ",
1154
+ "selected lists?"
1155
+ ] }),
1156
+ /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "This action cannot be undone." }),
1157
+ /* @__PURE__ */ jsxs(Group, { justify: "flex-end", mt: "md", children: [
1158
+ /* @__PURE__ */ jsx(
1159
+ Button,
1160
+ {
1161
+ variant: "light",
1162
+ onClick: () => setShowBatchDelete(false),
1163
+ disabled: deleteListsMutation.isPending,
1164
+ children: "Cancel"
1165
+ }
1166
+ ),
1167
+ /* @__PURE__ */ jsx(
1168
+ Button,
1169
+ {
1170
+ color: "red",
1171
+ loading: deleteListsMutation.isPending,
1172
+ disabled: !canCreateOrDeleteLists,
1173
+ onClick: () => {
1174
+ if (!canCreateOrDeleteLists) return;
1175
+ deleteListsMutation.mutate([...selection.selectedIds], {
1176
+ onSuccess: () => {
1177
+ setShowBatchDelete(false);
1178
+ selection.clear();
1179
+ }
1180
+ });
1181
+ },
1182
+ children: "Delete"
1183
+ }
1184
+ )
1185
+ ] })
1186
+ ] })
1187
+ }
1188
+ )
1189
+ ] }) });
1190
+ }
1191
+
1192
+ // src/lib/lead-gen/legacy-pipeline.ts
1193
+ function resolveLegacyListMemberPipelineLabel(member, stageCatalog = {}) {
1194
+ const stage = stageCatalog[member.stageKey];
1195
+ if (!stage) return null;
1196
+ return {
1197
+ stageLabel: stage.label,
1198
+ stateLabel: stageCatalog[member.stateKey]?.label ?? member.stateKey
1199
+ };
1200
+ }
1201
+ function formatDateTime(value) {
1202
+ if (!value) return "Not yet";
1203
+ return new Date(value).toLocaleString("en-US", {
1204
+ month: "short",
1205
+ day: "numeric",
1206
+ year: "numeric",
1207
+ hour: "numeric",
1208
+ minute: "2-digit"
1209
+ });
1210
+ }
1211
+ function getMemberStateColor(stateKey, stageCatalog) {
1212
+ const stage = stageCatalog[stateKey];
1213
+ if (stage?.entity === "contact") return "green";
1214
+ if (stage?.entity === "company") return "blue";
1215
+ return "gray";
1216
+ }
1217
+ function ArtifactsPanel({ listMemberId }) {
1218
+ const { data, isLoading, error } = useArtifacts({ ownerKind: "list_member", ownerId: listMemberId });
1219
+ const artifacts = data?.artifacts ?? [];
1220
+ if (isLoading) {
1221
+ return /* @__PURE__ */ jsx(Center, { p: "xl", children: /* @__PURE__ */ jsx(Loader, { size: "sm" }) });
1222
+ }
1223
+ if (error) {
1224
+ return /* @__PURE__ */ jsx(CenteredErrorState, { error, title: "Failed to load artifacts" });
1225
+ }
1226
+ if (!artifacts.length) {
1227
+ return /* @__PURE__ */ jsx(Alert, { icon: /* @__PURE__ */ jsx(IconDatabase, { size: 16 }), color: "gray", variant: "light", children: "No artifacts recorded for this member yet." });
1228
+ }
1229
+ return /* @__PURE__ */ jsx(Stack, { gap: "sm", children: artifacts.map((artifact) => /* @__PURE__ */ jsx(Card, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
1230
+ /* @__PURE__ */ jsxs(Group, { justify: "space-between", children: [
1231
+ /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
1232
+ /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: "blue", children: artifact.kind }),
1233
+ /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
1234
+ "v",
1235
+ artifact.version
1236
+ ] })
1237
+ ] }),
1238
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: formatDateTime(artifact.createdAt) })
1239
+ ] }),
1240
+ /* @__PURE__ */ jsx(JsonViewer, { data: artifact.content, maxHeight: 240 })
1241
+ ] }) }, artifact.id)) });
1242
+ }
1243
+ function ListMemberDrawerContent({ member, listId }) {
1244
+ const leadGenConfig = useLeadGenConfig();
1245
+ const { stageCatalog = {} } = leadGenConfig;
1246
+ const transitionMember = useTransitionListMember();
1247
+ const statefulItem = useMemo(
1248
+ () => ({
1249
+ id: member.id,
1250
+ pipeline_key: member.pipelineKey,
1251
+ stage_key: member.stageKey,
1252
+ state_key: member.stateKey,
1253
+ activity_log: member.activityLog
1254
+ }),
1255
+ [member]
1256
+ );
1257
+ const derivedActions = useDeriveActions(statefulItem);
1258
+ const resolved = resolveLegacyListMemberPipelineLabel(member, stageCatalog);
1259
+ const contactName = [member.contact?.firstName, member.contact?.lastName].filter(Boolean).join(" ").trim() || member.contact?.email || "\u2014";
1260
+ return /* @__PURE__ */ jsxs(Stack, { gap: "md", p: "md", children: [
1261
+ /* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
1262
+ /* @__PURE__ */ jsx(Group, { gap: "xs", align: "center", children: resolved ? /* @__PURE__ */ jsxs(Fragment, { children: [
1263
+ /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: "blue", children: resolved.stageLabel }),
1264
+ /* @__PURE__ */ jsx(IconChevronRight, { size: 14, style: { color: "var(--color-text-dimmed)" } }),
1265
+ /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "filled", color: getMemberStateColor(member.stateKey, stageCatalog), children: resolved.stateLabel })
1266
+ ] }) : /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "filled", color: getMemberStateColor(member.stateKey, stageCatalog), children: member.stateKey }) }),
1267
+ /* @__PURE__ */ jsx(Title, { order: 4, children: contactName }),
1268
+ member.contact?.email && /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
1269
+ /* @__PURE__ */ jsx(IconMail, { size: 14, style: { color: "var(--color-text-dimmed)" } }),
1270
+ /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: member.contact.email })
1271
+ ] }),
1272
+ member.contact?.title && /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
1273
+ /* @__PURE__ */ jsx(IconUser, { size: 14, style: { color: "var(--color-text-dimmed)" } }),
1274
+ /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: member.contact.title })
1275
+ ] })
1276
+ ] }),
1277
+ derivedActions.length > 0 && /* @__PURE__ */ jsx(Group, { gap: "xs", children: derivedActions.map((action) => /* @__PURE__ */ jsx(
1278
+ Button,
1279
+ {
1280
+ variant: "light",
1281
+ size: "xs",
1282
+ loading: transitionMember.isPending,
1283
+ disabled: !leadGenConfig.canUseLeadGenApiActions,
1284
+ onClick: () => {
1285
+ if (!leadGenConfig.canUseLeadGenApiActions) return;
1286
+ transitionMember.mutate({
1287
+ memberId: member.id,
1288
+ listId,
1289
+ pipelineKey: member.pipelineKey,
1290
+ // Lead-gen action dispatch targets are resolved server-side via the action key.
1291
+ // Stage/state keys are no longer embedded in the client-side Action shape.
1292
+ stageKey: action.key,
1293
+ stateKey: null
1294
+ });
1295
+ },
1296
+ children: action.label
1297
+ },
1298
+ action.key
1299
+ )) }),
1300
+ derivedActions.length > 0 && !leadGenConfig.canUseLeadGenApiActions ? /* @__PURE__ */ jsx(Alert, { icon: /* @__PURE__ */ jsx(IconAlertCircle, { size: 16 }), color: "yellow", variant: "light", children: "API-backed member actions require System Interface readiness." }) : null,
1301
+ /* @__PURE__ */ jsx(ArtifactsPanel, { listMemberId: member.id })
1302
+ ] });
1303
+ }
1304
+ function CompanyMemberDrawerContent({ companyId: _companyId }) {
1305
+ return /* @__PURE__ */ jsxs(Stack, { gap: "md", p: "md", children: [
1306
+ /* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
1307
+ /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: "gray", children: "company member" }),
1308
+ /* @__PURE__ */ jsx(Title, { order: 4, children: "Company" })
1309
+ ] }),
1310
+ /* @__PURE__ */ jsx(Alert, { icon: /* @__PURE__ */ jsx(IconAlertCircle, { size: 16 }), color: "blue", variant: "light", children: "Company-level pipeline transitions are not yet available in this view." })
1311
+ ] });
1312
+ }
1313
+ function ContactMemberDrawerShell({ contactId, listId }) {
1314
+ const { data: membersData, isLoading, error } = useListMembers({ listId });
1315
+ const listMember = useMemo(() => {
1316
+ if (!membersData?.members) return null;
1317
+ return membersData.members.find((m) => m.contactId === contactId) ?? null;
1318
+ }, [membersData, contactId]);
1319
+ const directQuery = useListMember(listMember === null && !isLoading ? contactId : "");
1320
+ const resolvedMember = listMember ?? directQuery.data ?? null;
1321
+ if (isLoading) {
1322
+ return /* @__PURE__ */ jsx(Center, { p: "xl", children: /* @__PURE__ */ jsx(Loader, { size: "sm" }) });
1323
+ }
1324
+ if (error) {
1325
+ return /* @__PURE__ */ jsx(CenteredErrorState, { error, title: "Failed to load member detail" });
1326
+ }
1327
+ if (!resolvedMember) {
1328
+ return /* @__PURE__ */ jsx(Stack, { gap: "md", p: "md", children: /* @__PURE__ */ jsx(Alert, { icon: /* @__PURE__ */ jsx(IconAlertCircle, { size: 16 }), color: "gray", variant: "light", children: "Member detail not found. The member may have been removed from this list." }) });
1329
+ }
1330
+ return /* @__PURE__ */ jsx(ListMemberDrawerContent, { member: resolvedMember, listId });
1331
+ }
1332
+ function ListMemberDrawer({ memberId, memberKind, listId, onClose }) {
1333
+ const opened = !!memberId && !!memberKind;
1334
+ return /* @__PURE__ */ jsxs(
1335
+ Drawer,
1336
+ {
1337
+ opened,
1338
+ onClose,
1339
+ position: "right",
1340
+ size: "lg",
1341
+ title: "Member Detail",
1342
+ styles: {
1343
+ body: { padding: 0 }
1344
+ },
1345
+ children: [
1346
+ opened && memberId && memberKind === "contact" && /* @__PURE__ */ jsx(ContactMemberDrawerShell, { contactId: memberId, listId }),
1347
+ opened && memberId && memberKind === "company" && /* @__PURE__ */ jsx(CompanyMemberDrawerContent, { companyId: memberId })
1348
+ ]
1349
+ }
1350
+ );
1351
+ }
1352
+ function LeadGenReadinessAlert({
1353
+ canUseApiActions,
1354
+ reason
1355
+ }) {
1356
+ if (canUseApiActions) return null;
1357
+ return /* @__PURE__ */ jsxs(Alert, { icon: /* @__PURE__ */ jsx(IconAlertCircle, { size: 16 }), color: "yellow", variant: "light", children: [
1358
+ "Lead-gen API actions are disabled until System Interface readiness passes. Read-only list detail can still render when data is available.",
1359
+ reason ? ` ${reason}` : ""
1360
+ ] });
1361
+ }
1362
+ function formatDateTime2(value) {
1363
+ if (!value) return "Not yet";
1364
+ return new Date(value).toLocaleString("en-US", {
1365
+ month: "short",
1366
+ day: "numeric",
1367
+ year: "numeric",
1368
+ hour: "numeric",
1369
+ minute: "2-digit"
1370
+ });
1371
+ }
1372
+ function formatDuration(durationMs) {
1373
+ if (durationMs == null) return "n/a";
1374
+ if (durationMs < 1e3) return `${durationMs} ms`;
1375
+ const seconds = durationMs / 1e3;
1376
+ if (seconds < 60) return `${seconds.toFixed(1)} s`;
1377
+ return `${Math.floor(seconds / 60)}m ${Math.round(seconds % 60)}s`;
1378
+ }
1379
+ function getStatusColor2(status) {
1380
+ switch (status) {
1381
+ case "completed":
1382
+ case "success":
1383
+ case "succeeded":
1384
+ return "green";
1385
+ case "running":
1386
+ case "pending":
1387
+ case "queued":
1388
+ return "blue";
1389
+ case "failed":
1390
+ case "error":
1391
+ case "cancelled":
1392
+ return "red";
1393
+ default:
1394
+ return "gray";
1395
+ }
1396
+ }
1397
+ function hasObjectContent(value) {
1398
+ return typeof value === "object" && value !== null && Object.keys(value).length > 0;
1399
+ }
1400
+ function getRunDetails(run) {
1401
+ const payload = hasObjectContent(run.payload) ? run.payload : void 0;
1402
+ const input = run.input ?? run.inputs ?? payload?.input ?? payload?.inputs;
1403
+ const config = run.configSnapshot ?? run.config_snapshot ?? run.config ?? payload?.config_snapshot ?? payload?.configSnapshot ?? payload?.config;
1404
+ return { input, config };
1405
+ }
1406
+ function getSummaryKeys(value) {
1407
+ if (!hasObjectContent(value)) return [];
1408
+ return Object.keys(value).slice(0, 6);
1409
+ }
1410
+ function WorkflowRunsPanel({
1411
+ listId,
1412
+ title = "Workflow Runs",
1413
+ resourceFilter,
1414
+ onClearResourceFilter
1415
+ }) {
1416
+ const navigate = useNavigate();
1417
+ const executionsQuery = useListExecutions(listId, resourceFilter ? { resourceId: resourceFilter } : {});
1418
+ const [expandedExecutionId, setExpandedExecutionId] = useState(null);
1419
+ const executions = useMemo(() => executionsQuery.data ?? [], [executionsQuery.data]);
1420
+ const openWorkflow = (resourceId) => {
1421
+ void navigate({ to: "/operations/resources/workflow/$workflowId", params: { workflowId: resourceId } });
1422
+ };
1423
+ return /* @__PURE__ */ jsxs(
1424
+ TabSection,
1425
+ {
1426
+ icon: /* @__PURE__ */ jsx(IconPlayerPlay, { size: 16 }),
1427
+ title,
1428
+ description: "Execution history recorded for this lead-gen list.",
1429
+ rightSection: /* @__PURE__ */ jsx(Tooltip, { label: "Refresh runs", children: /* @__PURE__ */ jsx(
1430
+ ActionIcon,
1431
+ {
1432
+ variant: "subtle",
1433
+ size: "sm",
1434
+ "aria-label": "Refresh workflow runs",
1435
+ loading: executionsQuery.isFetching,
1436
+ onClick: () => void executionsQuery.refetch(),
1437
+ children: /* @__PURE__ */ jsx(IconRefresh, { size: 14 })
1438
+ }
1439
+ ) }),
1440
+ children: [
1441
+ resourceFilter ? /* @__PURE__ */ jsxs(Group, { gap: "xs", mb: "xs", children: [
1442
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "Filtered by workflow:" }),
1443
+ /* @__PURE__ */ jsx(Pill, { withRemoveButton: true, onRemove: onClearResourceFilter, children: resourceFilter })
1444
+ ] }) : null,
1445
+ executionsQuery.isLoading ? /* @__PURE__ */ jsx(Center, { p: "lg", children: /* @__PURE__ */ jsx(Loader, { size: "sm" }) }) : !executions.length ? /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: resourceFilter ? `No workflow runs recorded for ${resourceFilter}.` : "No workflow runs recorded for this list." }) : /* @__PURE__ */ jsx(Table.ScrollContainer, { minWidth: 720, children: /* @__PURE__ */ jsxs(Table, { highlightOnHover: true, children: [
1446
+ /* @__PURE__ */ jsx(Table.Thead, { children: /* @__PURE__ */ jsxs(Table.Tr, { children: [
1447
+ /* @__PURE__ */ jsx(Table.Th, {}),
1448
+ /* @__PURE__ */ jsx(Table.Th, { children: "Workflow" }),
1449
+ /* @__PURE__ */ jsx(Table.Th, { children: "Status" }),
1450
+ /* @__PURE__ */ jsx(Table.Th, { children: "Started" }),
1451
+ /* @__PURE__ */ jsx(Table.Th, { children: "Completed" }),
1452
+ /* @__PURE__ */ jsx(Table.Th, { children: "Duration" })
1453
+ ] }) }),
1454
+ /* @__PURE__ */ jsx(Table.Tbody, { children: executions.map((execution) => {
1455
+ const details = getRunDetails(execution);
1456
+ const hasDetails = details.input !== void 0 || details.config !== void 0;
1457
+ const expanded = expandedExecutionId === execution.executionId;
1458
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [
1459
+ /* @__PURE__ */ jsxs(Table.Tr, { children: [
1460
+ /* @__PURE__ */ jsx(Table.Td, { w: 32, children: /* @__PURE__ */ jsx(
1461
+ ThemeIcon,
1462
+ {
1463
+ variant: "light",
1464
+ color: "blue",
1465
+ size: "md",
1466
+ role: hasDetails ? "button" : void 0,
1467
+ tabIndex: hasDetails ? 0 : void 0,
1468
+ "aria-label": hasDetails ? expanded ? "Collapse run details" : "Expand run details" : void 0,
1469
+ onClick: hasDetails ? () => setExpandedExecutionId(expanded ? null : execution.executionId) : void 0,
1470
+ onKeyDown: hasDetails ? (event) => {
1471
+ if (event.key === "Enter" || event.key === " ") {
1472
+ event.preventDefault();
1473
+ setExpandedExecutionId(expanded ? null : execution.executionId);
1474
+ }
1475
+ } : void 0,
1476
+ style: {
1477
+ cursor: hasDetails ? "pointer" : "default",
1478
+ opacity: hasDetails ? 1 : 0.7
1479
+ },
1480
+ children: expanded ? /* @__PURE__ */ jsx(IconChevronDown, { size: 14 }) : /* @__PURE__ */ jsx(IconChevronRight, { size: 14 })
1481
+ }
1482
+ ) }),
1483
+ /* @__PURE__ */ jsxs(Table.Td, { children: [
1484
+ execution.resourceId ? /* @__PURE__ */ jsx(
1485
+ Text,
1486
+ {
1487
+ fw: 500,
1488
+ role: "button",
1489
+ tabIndex: 0,
1490
+ c: "blue",
1491
+ onClick: () => openWorkflow(execution.resourceId),
1492
+ onKeyDown: (event) => {
1493
+ if (event.key === "Enter" || event.key === " ") {
1494
+ event.preventDefault();
1495
+ openWorkflow(execution.resourceId);
1496
+ }
1497
+ },
1498
+ style: { cursor: "pointer" },
1499
+ children: execution.resourceId
1500
+ }
1501
+ ) : /* @__PURE__ */ jsx(Text, { fw: 500, children: "Unknown workflow" }),
1502
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", ff: "monospace", children: execution.executionId })
1503
+ ] }),
1504
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: getStatusColor2(execution.status), children: execution.status }) }),
1505
+ /* @__PURE__ */ jsx(Table.Td, { children: formatDateTime2(execution.createdAt) }),
1506
+ /* @__PURE__ */ jsx(Table.Td, { children: formatDateTime2(execution.completedAt) }),
1507
+ /* @__PURE__ */ jsx(Table.Td, { children: formatDuration(execution.durationMs) })
1508
+ ] }),
1509
+ /* @__PURE__ */ jsx(Table.Tr, { children: /* @__PURE__ */ jsx(Table.Td, { colSpan: 6, p: 0, children: /* @__PURE__ */ jsx(Collapse, { in: expanded, children: /* @__PURE__ */ jsxs(Stack, { gap: "sm", p: "sm", children: [
1510
+ details.input !== void 0 ? /* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
1511
+ /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
1512
+ /* @__PURE__ */ jsx(Text, { size: "sm", fw: 600, children: "Input" }),
1513
+ getSummaryKeys(details.input).map((key) => /* @__PURE__ */ jsx(Badge, { size: "xs", variant: "light", color: "gray", children: key }, key))
1514
+ ] }),
1515
+ /* @__PURE__ */ jsx(JsonViewer, { data: details.input, maxHeight: 220, fontSize: "0.75rem" })
1516
+ ] }) : null,
1517
+ details.config !== void 0 ? /* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
1518
+ /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
1519
+ /* @__PURE__ */ jsx(Text, { size: "sm", fw: 600, children: "Config" }),
1520
+ getSummaryKeys(details.config).map((key) => /* @__PURE__ */ jsx(Badge, { size: "xs", variant: "light", color: "gray", children: key }, key))
1521
+ ] }),
1522
+ /* @__PURE__ */ jsx(JsonViewer, { data: details.config, maxHeight: 220, fontSize: "0.75rem" })
1523
+ ] }) : null
1524
+ ] }) }) }) })
1525
+ ] }, execution.executionId);
1526
+ }) })
1527
+ ] }) })
1528
+ ]
1529
+ }
1530
+ );
1531
+ }
1532
+
1533
+ // src/features/lead-gen/build-state.ts
1534
+ var ORPHAN_STAGE_ORDER = 9999;
1535
+ function asRecord(value) {
1536
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
1537
+ }
1538
+ function sortStageKeys(keys, stageCatalog = {}) {
1539
+ return keys.slice().sort((a, b) => {
1540
+ const oa = stageCatalog[a]?.order ?? ORPHAN_STAGE_ORDER;
1541
+ const ob = stageCatalog[b]?.order ?? ORPHAN_STAGE_ORDER;
1542
+ return oa - ob || a.localeCompare(b);
1543
+ });
1544
+ }
1545
+ function getStageProgress(progress, step) {
1546
+ return step.primaryEntity === "company" ? progress.byCompanyStage[step.stageKey] : progress.byContactStage[step.stageKey];
1547
+ }
1548
+ function getEntityTotal(progress, entity) {
1549
+ return entity === "company" ? progress.totalCompanies : progress.totalMembers;
1550
+ }
1551
+ function findActionForStep(actions, step) {
1552
+ return actions.find((action) => action.actionKey === step.actionKey) ?? actions.find((action) => action.stagesAffected?.includes(step.stageKey));
1553
+ }
1554
+ function normalizeBuildPlanStep(step, fallbackSteps = []) {
1555
+ const fallback = fallbackSteps.find((item) => item.id === step.id || item.stageKey === step.stageKey);
1556
+ return {
1557
+ id: step.id,
1558
+ label: step.label,
1559
+ description: step.description ?? fallback?.description ?? `Run ${step.label.toLowerCase()} for this list.`,
1560
+ primaryEntity: step.primaryEntity,
1561
+ outputs: step.outputs,
1562
+ stageKey: step.stageKey,
1563
+ recordEntity: step.recordEntity,
1564
+ recordsStageKey: step.recordsStageKey,
1565
+ recordSourceStageKey: step.recordSourceStageKey,
1566
+ dependsOn: step.dependsOn,
1567
+ dependencyMode: step.dependencyMode,
1568
+ actionKey: step.actionKey,
1569
+ defaultBatchSize: step.defaultBatchSize,
1570
+ maxBatchSize: step.maxBatchSize,
1571
+ recordColumns: step.recordColumns,
1572
+ credentialRequirements: step.credentialRequirements,
1573
+ emptyBlockedText: fallback?.emptyBlockedText ?? "Complete prerequisite build steps before this action can run."
1574
+ };
1575
+ }
1576
+ function isCurrentBuildPlanStep(step) {
1577
+ if (!step || typeof step !== "object") return false;
1578
+ const candidate = step;
1579
+ return (candidate.primaryEntity === "company" || candidate.primaryEntity === "contact") && Array.isArray(candidate.outputs) && candidate.outputs.length > 0 && candidate.dependencyMode === "per-record-eligibility";
1580
+ }
1581
+ function resolveBuildPlanSteps(list, defaultBuildSteps = [], buildTemplates = []) {
1582
+ const snapshot = list.metadata.buildPlanSnapshot;
1583
+ const snapshotSteps = snapshot?.steps;
1584
+ if (snapshotSteps?.length && snapshotSteps.every(isCurrentBuildPlanStep)) {
1585
+ return snapshotSteps.map((step) => normalizeBuildPlanStep(step, defaultBuildSteps));
1586
+ }
1587
+ if (snapshot?.templateId) {
1588
+ const templateSnapshot = createBuildPlanSnapshotFromTemplateId(snapshot.templateId, buildTemplates);
1589
+ if (templateSnapshot?.steps.length) {
1590
+ return templateSnapshot.steps.map((step) => normalizeBuildPlanStep(step, defaultBuildSteps));
1591
+ }
1592
+ }
1593
+ return defaultBuildSteps;
1594
+ }
1595
+ function getPrerequisiteSteps(step, byStepId) {
1596
+ return (step.dependsOn ?? []).flatMap((dependencyId) => {
1597
+ const dependency = byStepId.get(dependencyId);
1598
+ return dependency ? [dependency] : [];
1599
+ });
1600
+ }
1601
+ function getStepActionKind(step, ready, failed, action) {
1602
+ if (!action) return "none";
1603
+ if (failed > 0) return "retry_failed";
1604
+ if (ready > 0) return "run_next_batch";
1605
+ const isSeedStep = (step.dependsOn?.length ?? 0) === 0 && step.outputs.includes(step.primaryEntity);
1606
+ if (isSeedStep) return "run_next_batch";
1607
+ return "none";
1608
+ }
1609
+ function getStepActionLabel(kind) {
1610
+ switch (kind) {
1611
+ case "retry_failed":
1612
+ return "Retry failed";
1613
+ case "run_next_batch":
1614
+ return "Run next batch";
1615
+ case "advanced":
1616
+ return "Advanced";
1617
+ case "none":
1618
+ default:
1619
+ return "No action";
1620
+ }
1621
+ }
1622
+ function createCompleteStageCounts(total) {
1623
+ return {
1624
+ total,
1625
+ attempted: total,
1626
+ success: total,
1627
+ noResult: 0,
1628
+ skipped: 0,
1629
+ error: 0,
1630
+ other: 0,
1631
+ notAttempted: 0
1632
+ };
1633
+ }
1634
+ function isStageComplete(entry) {
1635
+ if (!entry || entry.total <= 0) return false;
1636
+ return entry.error === 0 && entry.attempted >= entry.total && entry.success + entry.noResult + entry.skipped + entry.other >= entry.total;
1637
+ }
1638
+ function getExecutionInput(run) {
1639
+ return asRecord(run.input);
1640
+ }
1641
+ function isCompletedRun(run) {
1642
+ return run.status === "completed" || run.status === "warning" || run.status === "success" || run.status === "succeeded";
1643
+ }
1644
+ function getApprovedCompanyIds(run) {
1645
+ const input = getExecutionInput(run);
1646
+ const raw = input.approvedCompanyIds;
1647
+ return Array.isArray(raw) ? raw.filter((value) => typeof value === "string") : [];
1648
+ }
1649
+ function hasApprovedExportRun(executions, exportWorkflowId) {
1650
+ if (!exportWorkflowId) return false;
1651
+ return executions.some((run) => {
1652
+ if (!isCompletedRun(run) || run.resourceId !== exportWorkflowId) return false;
1653
+ const input = getExecutionInput(run);
1654
+ return input.approved === true || getApprovedCompanyIds(run).length > 0;
1655
+ });
1656
+ }
1657
+ function hasCompletedSourcingRun(step, action, executions) {
1658
+ return executions.some((run) => isCompletedRun(run) && run.resourceId === (action?.resourceId ?? step.actionKey));
1659
+ }
1660
+ function deriveBusinessProgress(list, progress, actions = [], executions = [], config = {}) {
1661
+ const byCompanyStage = { ...progress.byCompanyStage };
1662
+ const byContactStage = { ...progress.byContactStage };
1663
+ const steps = resolveBuildPlanSteps(list, config.defaultBuildSteps, config.buildTemplates);
1664
+ for (const step of steps) {
1665
+ const stageProgress = step.primaryEntity === "company" ? byCompanyStage[step.stageKey] : byContactStage[step.stageKey];
1666
+ if (stageProgress || (step.dependsOn?.length ?? 0) > 0 || !step.outputs.includes("company")) continue;
1667
+ if (progress.totalCompanies <= 0) continue;
1668
+ const action = findActionForStep(actions, step);
1669
+ if (hasCompletedSourcingRun(step, action, executions) || step.stageKey === "populated") {
1670
+ byCompanyStage[step.stageKey] = createCompleteStageCounts(progress.totalCompanies);
1671
+ }
1672
+ }
1673
+ if (!isStageComplete(byCompanyStage.uploaded) && hasApprovedExportRun(executions, config.exportWorkflowId)) {
1674
+ const exportedCount = Math.max(
1675
+ ...executions.filter((run) => isCompletedRun(run) && run.resourceId === config.exportWorkflowId).map((run) => getApprovedCompanyIds(run).length),
1676
+ 0
1677
+ );
1678
+ const total = exportedCount > 0 ? exportedCount : progress.totalCompanies;
1679
+ if (total > 0) byCompanyStage.uploaded = createCompleteStageCounts(total);
1680
+ }
1681
+ return {
1682
+ ...progress,
1683
+ byCompanyStage,
1684
+ byContactStage
1685
+ };
1686
+ }
1687
+ function getStepRecommendedAction(step, action, kind) {
1688
+ if (!action || kind === "none") return void 0;
1689
+ return {
1690
+ kind,
1691
+ label: getStepActionLabel(kind),
1692
+ actionKey: action.actionKey ?? step.actionKey,
1693
+ defaultSize: step.defaultBatchSize,
1694
+ maxSize: step.maxBatchSize
1695
+ };
1696
+ }
1697
+ function deriveBuildStepStates(list, progress, actions, defaultBuildSteps = [], buildTemplates = []) {
1698
+ const byStepId = /* @__PURE__ */ new Map();
1699
+ for (const step of resolveBuildPlanSteps(list, defaultBuildSteps, buildTemplates)) {
1700
+ const stageProgress = getStageProgress(progress, step);
1701
+ const entityTotal = getEntityTotal(progress, step.primaryEntity);
1702
+ const prerequisites = getPrerequisiteSteps(step, byStepId);
1703
+ const hasDependencies = (step.dependsOn?.length ?? 0) > 0;
1704
+ const prerequisiteEligibleTotal = prerequisites.reduce((sum, prerequisite) => sum + prerequisite.complete, 0);
1705
+ const eligibleTotal = hasDependencies ? entityTotal > 0 ? Math.min(entityTotal, prerequisiteEligibleTotal) : prerequisiteEligibleTotal : entityTotal > 0 ? entityTotal : 0;
1706
+ const total = Math.max(stageProgress?.total ?? 0, entityTotal, eligibleTotal);
1707
+ const complete = stageProgress ? stageProgress.success + stageProgress.noResult + stageProgress.skipped + stageProgress.other : 0;
1708
+ const failed = stageProgress?.error ?? 0;
1709
+ const ready = Math.max(0, eligibleTotal - complete - failed);
1710
+ const blocked = hasDependencies ? Math.max(total - eligibleTotal - complete - failed, 0) : 0;
1711
+ const action = findActionForStep(actions, step);
1712
+ const nextActionKind = getStepActionKind(step, ready, failed, action);
1713
+ const isActionableEmptySeedStep = !hasDependencies && total === 0 && nextActionKind === "run_next_batch";
1714
+ const status = failed > 0 ? "failed" : ready > 0 || isActionableEmptySeedStep ? "ready" : blocked > 0 || total === 0 ? "blocked" : "complete";
1715
+ const state = {
1716
+ ...step,
1717
+ status,
1718
+ ready,
1719
+ complete,
1720
+ failed,
1721
+ blocked,
1722
+ nextActionKind,
1723
+ recommendedAction: getStepRecommendedAction(step, action, nextActionKind),
1724
+ action
1725
+ };
1726
+ byStepId.set(step.id, state);
1727
+ }
1728
+ return Array.from(byStepId.values());
1729
+ }
1730
+ function getRecommendedBuildStep(steps) {
1731
+ return steps.find((step) => step.nextActionKind === "retry_failed") ?? steps.find((step) => step.nextActionKind === "run_next_batch") ?? null;
1732
+ }
1733
+ function resolveBuildState(list, progress, actions, config = {}) {
1734
+ const steps = deriveBuildStepStates(list, progress, actions, config.defaultBuildSteps, config.buildTemplates);
1735
+ const recommendedStep = getRecommendedBuildStep(steps);
1736
+ return {
1737
+ steps,
1738
+ recommendedStep,
1739
+ recommendedAction: recommendedStep?.recommendedAction ?? null
1740
+ };
1741
+ }
1742
+
1743
+ // src/lib/lead-gen/processing-state.ts
1744
+ function isRecord(value) {
1745
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
1746
+ }
1747
+ function parseProcessingStageStatus(value) {
1748
+ if (value === true || value === "success") return "success";
1749
+ if (value === "no_result" || value === "noResult") return "noResult";
1750
+ if (value === "skipped") return "skipped";
1751
+ if (value === "error") return "error";
1752
+ if (isRecord(value)) return parseProcessingStageStatus(value.status);
1753
+ if (typeof value === "string") return "other";
1754
+ return null;
1755
+ }
1756
+ function parseProcessingState(value, stageCatalog = {}) {
1757
+ const parsed = {};
1758
+ const stageStatuses = {};
1759
+ const knownStageStatuses = {};
1760
+ const unknownFields = {};
1761
+ if (!isRecord(value)) {
1762
+ Object.defineProperties(parsed, {
1763
+ stageStatuses: { value: stageStatuses },
1764
+ knownStageStatuses: { value: knownStageStatuses },
1765
+ unknownFields: { value: unknownFields }
1766
+ });
1767
+ return parsed;
1768
+ }
1769
+ for (const [key, rawStatus] of Object.entries(value)) {
1770
+ const parsedStatus = parseProcessingStageStatus(rawStatus);
1771
+ const isKnownStage = Boolean(stageCatalog[key]);
1772
+ if (!parsedStatus) {
1773
+ unknownFields[key] = rawStatus;
1774
+ continue;
1775
+ }
1776
+ stageStatuses[key] = parsedStatus;
1777
+ parsed[key] = parsedStatus;
1778
+ if (isKnownStage) {
1779
+ knownStageStatuses[key] = parsedStatus;
1780
+ } else {
1781
+ unknownFields[key] = rawStatus;
1782
+ }
1783
+ }
1784
+ Object.defineProperties(parsed, {
1785
+ stageStatuses: { value: stageStatuses },
1786
+ knownStageStatuses: { value: knownStageStatuses },
1787
+ unknownFields: { value: unknownFields }
1788
+ });
1789
+ return parsed;
1790
+ }
1791
+ function readLeadGenProcessingState(row) {
1792
+ if (!isRecord(row)) return null;
1793
+ return row.processingState ?? row.processing_state ?? row.pipelineStatus ?? null;
1794
+ }
1795
+ function readLeadGenStateKey(row) {
1796
+ if (!isRecord(row)) return null;
1797
+ const stateKey = row.stateKey ?? row.state_key;
1798
+ return typeof stateKey === "string" && stateKey.length > 0 ? stateKey : null;
1799
+ }
1800
+ function getDisplayLeadGenStageStateFor(row, entity, stageCatalog = {}) {
1801
+ const parsed = parseProcessingState(readLeadGenProcessingState(row), stageCatalog);
1802
+ const latest = Object.values(stageCatalog).filter((stage) => stage.entity === entity && parsed.knownStageStatuses[stage.key]).sort((a, b) => b.order - a.order)[0];
1803
+ if (latest) {
1804
+ return {
1805
+ stageKey: latest.key,
1806
+ stageStatus: parsed.knownStageStatuses[latest.key]
1807
+ };
1808
+ }
1809
+ const unknownStageKey = Object.keys(parsed.stageStatuses).find((stageKey) => !stageCatalog[stageKey]);
1810
+ if (unknownStageKey) {
1811
+ return {
1812
+ stageKey: unknownStageKey,
1813
+ stageStatus: parsed.stageStatuses[unknownStageKey]
1814
+ };
1815
+ }
1816
+ const stateKey = readLeadGenStateKey(row);
1817
+ return stateKey && stageCatalog[stateKey] ? { stageKey: stateKey, stageStatus: null } : { stageKey: null, stageStatus: null };
1818
+ }
1819
+
1820
+ // src/features/lead-gen/pages/list-detail/helpers.ts
1821
+ function formatDateTime3(value) {
1822
+ if (!value) return "Not yet";
1823
+ return new Date(value).toLocaleString("en-US", {
1824
+ month: "short",
1825
+ day: "numeric",
1826
+ year: "numeric",
1827
+ hour: "numeric",
1828
+ minute: "2-digit"
1829
+ });
1830
+ }
1831
+ function contactDisplayName(firstName, lastName) {
1832
+ const full = [firstName, lastName].filter(Boolean).join(" ").trim();
1833
+ return full || "\u2014";
1834
+ }
1835
+ function getStatusColor3(status) {
1836
+ switch (status) {
1837
+ case "draft":
1838
+ return "gray";
1839
+ case "enriching":
1840
+ return "blue";
1841
+ case "launched":
1842
+ return "green";
1843
+ case "closing":
1844
+ return "yellow";
1845
+ case "archived":
1846
+ return "red";
1847
+ default:
1848
+ return "gray";
1849
+ }
1850
+ }
1851
+ function getMemberStateColor2(stateKey, stageCatalog) {
1852
+ const stage = stageCatalog[stateKey];
1853
+ if (stage?.entity === "contact") return "green";
1854
+ if (stage?.entity === "company") return "blue";
1855
+ return "gray";
1856
+ }
1857
+ function displayMemberStageFor(row, kind, stageCatalog) {
1858
+ const stage = getDisplayLeadGenStageStateFor(row, kind, stageCatalog);
1859
+ if (!stage.stageKey) return null;
1860
+ const catalogStage = stageCatalog[stage.stageKey];
1861
+ return {
1862
+ key: stage.stageKey,
1863
+ label: catalogStage?.label ?? stage.stageKey
1864
+ };
1865
+ }
1866
+ function asRecord2(value) {
1867
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
1868
+ }
1869
+ function valueAtPath(source, path) {
1870
+ return path.split(".").reduce((current, segment) => {
1871
+ if (!current || typeof current !== "object") return void 0;
1872
+ return current[segment];
1873
+ }, source);
1874
+ }
1875
+ function compactText(value) {
1876
+ if (value === null || value === void 0 || value === "") return "-";
1877
+ if (Array.isArray(value)) {
1878
+ if (!value.length) return "-";
1879
+ return value.map((item) => {
1880
+ if (item && typeof item === "object") {
1881
+ const labelled = item;
1882
+ return String(labelled.name ?? labelled.label ?? labelled.title ?? labelled.id ?? JSON.stringify(item));
1883
+ }
1884
+ return String(item);
1885
+ }).join(", ");
1886
+ }
1887
+ if (typeof value === "object") return JSON.stringify(value);
1888
+ return String(value);
1889
+ }
1890
+ function getRecordDisplayName(row) {
1891
+ if (row.entity === "company") return row.company?.name ?? "Company";
1892
+ const contact = row.contact;
1893
+ const full = [contact?.firstName, contact?.lastName].filter(Boolean).join(" ").trim();
1894
+ return full || contact?.email || "Contact";
1895
+ }
1896
+ function getRecordColumnSource(row) {
1897
+ if (row.entity === "company") {
1898
+ return {
1899
+ ...row,
1900
+ company: row.company ? {
1901
+ ...row.company,
1902
+ enrichmentData: row.enrichmentData,
1903
+ processingState: row.processingState
1904
+ } : null
1905
+ };
1906
+ }
1907
+ return {
1908
+ ...row,
1909
+ contact: row.contact ? {
1910
+ ...row.contact,
1911
+ name: getRecordDisplayName(row),
1912
+ enrichmentData: row.enrichmentData,
1913
+ processingState: row.processingState
1914
+ } : null
1915
+ };
1916
+ }
1917
+ function getStageEntry(row, stageKey) {
1918
+ const entry = valueAtPath(row.processingState, stageKey);
1919
+ return asRecord2(entry);
1920
+ }
1921
+ function getRecordStageStatus(row, stageKey) {
1922
+ const status = getStageEntry(row, stageKey).status;
1923
+ return typeof status === "string" ? status : "pending";
1924
+ }
1925
+ function getStageFailureReason(row, stageKey) {
1926
+ const data = asRecord2(getStageEntry(row, stageKey).data);
1927
+ const error = data.error ?? data.reason ?? data.failureReason ?? data.disqualifiedReason;
1928
+ return compactText(error);
1929
+ }
1930
+ function getRecordStatusColor(status) {
1931
+ switch (status) {
1932
+ case "success":
1933
+ return "green";
1934
+ case "error":
1935
+ case "failed":
1936
+ return "red";
1937
+ case "skipped":
1938
+ case "no_result":
1939
+ return "yellow";
1940
+ case "pending":
1941
+ return "gray";
1942
+ default:
1943
+ return "blue";
1944
+ }
1945
+ }
1946
+ function getRunStatusColor(status) {
1947
+ switch (status) {
1948
+ case "completed":
1949
+ case "success":
1950
+ case "succeeded":
1951
+ return "green";
1952
+ case "running":
1953
+ case "pending":
1954
+ case "queued":
1955
+ return "blue";
1956
+ case "failed":
1957
+ case "error":
1958
+ case "cancelled":
1959
+ return "red";
1960
+ default:
1961
+ return "gray";
1962
+ }
1963
+ }
1964
+ function isRunInFlight(status) {
1965
+ return status === "running" || status === "pending" || status === "queued";
1966
+ }
1967
+ function isRunTerminal(status) {
1968
+ return !isRunInFlight(status);
1969
+ }
1970
+ function getRunInput(run) {
1971
+ const payload = asRecord2(run.payload);
1972
+ return run.input ?? run.inputs ?? payload.input ?? payload.inputs;
1973
+ }
1974
+ function hasRunInput(run) {
1975
+ const input = getRunInput(run);
1976
+ return input !== void 0 && input !== null;
1977
+ }
1978
+ function sanitizeStepInput(value, actionKey) {
1979
+ if (isLeadGenExportAction(actionKey)) return value;
1980
+ const next = { ...value };
1981
+ delete next.mode;
1982
+ return next;
1983
+ }
1984
+ function getDefaultStepInput(list, action) {
1985
+ return asRecord2(action?.defaultInput?.(list));
1986
+ }
1987
+ function getInitialStepInput(list, action) {
1988
+ return getDefaultStepInput(list, action);
1989
+ }
1990
+ function getStageStatus(entry) {
1991
+ if (!entry || (entry.total ?? 0) === 0) return "not-started";
1992
+ if ((entry.error ?? 0) > 0) return "errors";
1993
+ if ((entry.attempted ?? 0) >= (entry.total ?? 0) && (entry.success ?? 0) > 0) return "complete";
1994
+ return "in-progress";
1995
+ }
1996
+ function getStageColors(status) {
1997
+ switch (status) {
1998
+ case "complete":
1999
+ return {
2000
+ bg: "var(--color-success)",
2001
+ fg: "white",
2002
+ border: "var(--color-success)",
2003
+ ring: "color-mix(in srgb, var(--color-success) 22%, transparent)"
2004
+ };
2005
+ case "errors":
2006
+ return {
2007
+ bg: "var(--color-error)",
2008
+ fg: "white",
2009
+ border: "var(--color-error)",
2010
+ ring: "color-mix(in srgb, var(--color-error) 22%, transparent)"
2011
+ };
2012
+ case "in-progress":
2013
+ return {
2014
+ bg: "var(--color-primary)",
2015
+ fg: "white",
2016
+ border: "var(--color-primary)",
2017
+ ring: "color-mix(in srgb, var(--color-primary) 22%, transparent)"
2018
+ };
2019
+ case "not-started":
2020
+ default:
2021
+ return {
2022
+ bg: "color-mix(in srgb, var(--color-primary) 28%, var(--mantine-color-body))",
2023
+ fg: "color-mix(in srgb, var(--color-primary) 55%, var(--color-text-dimmed))",
2024
+ border: "color-mix(in srgb, var(--color-primary) 45%, var(--color-border))",
2025
+ ring: "color-mix(in srgb, var(--color-primary) 10%, transparent)"
2026
+ };
2027
+ }
2028
+ }
2029
+ var STAGE_NODE_WIDTH = 110;
2030
+ var STAGE_CIRCLE_SIZE = 36;
2031
+ function StageNode({
2032
+ index,
2033
+ stageKey,
2034
+ fallbackLabel,
2035
+ entry,
2036
+ stageCatalog
2037
+ }) {
2038
+ const label = stageCatalog[stageKey]?.label ?? fallbackLabel ?? stageKey;
2039
+ const status = getStageStatus(entry);
2040
+ const colors = getStageColors(status);
2041
+ const total = entry?.total ?? 0;
2042
+ const attempted = entry?.attempted ?? 0;
2043
+ const success = entry?.success ?? 0;
2044
+ const done = entry ? entry.success + entry.noResult + entry.skipped + entry.other : 0;
2045
+ const summary = entry ? status === "complete" ? `${done}/${total} done` : status === "errors" ? `${entry.error} failed` : `${attempted}/${total} started` : "Not started";
2046
+ const tooltipLines = entry ? [
2047
+ `${attempted} / ${total} attempted`,
2048
+ `${success} success`,
2049
+ (entry.noResult ?? 0) > 0 ? `${entry.noResult} no result` : null,
2050
+ (entry.skipped ?? 0) > 0 ? `${entry.skipped} skipped` : null,
2051
+ (entry.error ?? 0) > 0 ? `${entry.error} errors` : null,
2052
+ (entry.notAttempted ?? 0) > 0 ? `${entry.notAttempted} not attempted` : null
2053
+ ].filter(Boolean) : ["Not started"];
2054
+ return /* @__PURE__ */ jsxs(
2055
+ Stack,
2056
+ {
2057
+ gap: 6,
2058
+ align: "center",
2059
+ style: {
2060
+ width: STAGE_NODE_WIDTH,
2061
+ flex: "0 0 auto",
2062
+ padding: "0 4px",
2063
+ position: "relative",
2064
+ zIndex: 1
2065
+ },
2066
+ children: [
2067
+ /* @__PURE__ */ jsx(Tooltip, { label: tooltipLines.join(" \xB7 "), position: "top", withArrow: true, multiline: true, maw: 260, children: /* @__PURE__ */ jsx(
2068
+ Box,
2069
+ {
2070
+ style: {
2071
+ width: STAGE_CIRCLE_SIZE,
2072
+ height: STAGE_CIRCLE_SIZE,
2073
+ borderRadius: "50%",
2074
+ backgroundColor: colors.bg,
2075
+ border: `1.5px solid ${colors.border}`,
2076
+ display: "flex",
2077
+ alignItems: "center",
2078
+ justifyContent: "center",
2079
+ boxShadow: `0 0 0 4px ${colors.ring}`,
2080
+ transition: "all var(--duration-normal) var(--easing)"
2081
+ },
2082
+ children: /* @__PURE__ */ jsx(Box, { style: { color: colors.fg, lineHeight: 1, display: "flex", alignItems: "center" }, children: status === "complete" ? "OK" : index + 1 })
2083
+ }
2084
+ ) }),
2085
+ /* @__PURE__ */ jsxs(Stack, { gap: 2, align: "center", style: { minWidth: 0, maxWidth: "100%" }, children: [
2086
+ /* @__PURE__ */ jsx(
2087
+ Text,
2088
+ {
2089
+ size: "xs",
2090
+ fw: status === "not-started" ? 500 : 600,
2091
+ ta: "center",
2092
+ lineClamp: 1,
2093
+ style: {
2094
+ maxWidth: "100%",
2095
+ color: status === "not-started" ? "var(--color-text-dimmed)" : "var(--color-text)",
2096
+ letterSpacing: 0.2
2097
+ },
2098
+ children: label
2099
+ }
2100
+ ),
2101
+ /* @__PURE__ */ jsx(Text, { size: "xs", style: { color: "var(--color-text-subtle)", fontVariantNumeric: "tabular-nums" }, children: summary })
2102
+ ] })
2103
+ ]
2104
+ }
2105
+ );
2106
+ }
2107
+ function PipelineStepper({
2108
+ plannedSteps,
2109
+ byStage,
2110
+ emptyText,
2111
+ stageCatalog
2112
+ }) {
2113
+ const stageKeys = plannedSteps.length > 0 ? plannedSteps.map((s) => s.stageKey) : sortStageKeys(Object.keys(byStage), stageCatalog);
2114
+ const labelByKey = new Map(plannedSteps.map((s) => [s.stageKey, s.label]));
2115
+ if (stageKeys.length === 0) {
2116
+ return /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: emptyText });
2117
+ }
2118
+ return /* @__PURE__ */ jsx(Box, { style: { overflowX: "auto", padding: "4px 0" }, children: /* @__PURE__ */ jsxs(
2119
+ Box,
2120
+ {
2121
+ style: {
2122
+ position: "relative",
2123
+ display: "flex",
2124
+ justifyContent: "space-between",
2125
+ alignItems: "flex-start",
2126
+ minWidth: stageKeys.length * STAGE_NODE_WIDTH
2127
+ },
2128
+ children: [
2129
+ /* @__PURE__ */ jsx(
2130
+ Box,
2131
+ {
2132
+ style: {
2133
+ position: "absolute",
2134
+ top: STAGE_CIRCLE_SIZE / 2 - 1,
2135
+ left: STAGE_NODE_WIDTH / 2,
2136
+ right: STAGE_NODE_WIDTH / 2,
2137
+ height: 2,
2138
+ backgroundColor: "var(--color-border)"
2139
+ }
2140
+ }
2141
+ ),
2142
+ stageKeys.map((key, index) => /* @__PURE__ */ jsx(
2143
+ StageNode,
2144
+ {
2145
+ index,
2146
+ stageKey: key,
2147
+ fallbackLabel: labelByKey.get(key),
2148
+ entry: byStage[key],
2149
+ stageCatalog
2150
+ },
2151
+ key
2152
+ ))
2153
+ ]
2154
+ }
2155
+ ) });
2156
+ }
2157
+ function PipelineStagesCard({
2158
+ list,
2159
+ progress,
2160
+ leadGenConfig
2161
+ }) {
2162
+ const stageCatalog = leadGenConfig.stageCatalog ?? {};
2163
+ const plannedSteps = resolveBuildPlanSteps(list, leadGenConfig.defaultBuildSteps, leadGenConfig.buildTemplates);
2164
+ const templateLabel = list.metadata.buildPlanSnapshot?.templateLabel;
2165
+ const combinedByStage = { ...progress.byCompanyStage, ...progress.byContactStage };
2166
+ const hasAnyContent = plannedSteps.length > 0 || Object.keys(combinedByStage).length > 0;
2167
+ return /* @__PURE__ */ jsx(Card, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
2168
+ /* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "center", children: [
2169
+ /* @__PURE__ */ jsxs(Stack, { gap: 2, children: [
2170
+ /* @__PURE__ */ jsx(Title, { order: 5, children: "List Build Progress" }),
2171
+ templateLabel && /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
2172
+ "Template: ",
2173
+ templateLabel
2174
+ ] })
2175
+ ] }),
2176
+ /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
2177
+ /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "filled", color: getStatusColor3(list.status), children: list.status }),
2178
+ list.launchedAt && /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
2179
+ "Launched ",
2180
+ formatDateTime3(list.launchedAt)
2181
+ ] }),
2182
+ list.completedAt && /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
2183
+ "Completed ",
2184
+ formatDateTime3(list.completedAt)
2185
+ ] })
2186
+ ] })
2187
+ ] }),
2188
+ !hasAnyContent ? /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "No pipeline stages configured yet \u2014 plan a build template to populate this list." }) : /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
2189
+ /* @__PURE__ */ jsxs(Text, { size: "sm", fw: 600, children: [
2190
+ "Pipeline (",
2191
+ progress.totalCompanies,
2192
+ " companies \xB7 ",
2193
+ progress.totalMembers,
2194
+ " contacts)"
2195
+ ] }),
2196
+ /* @__PURE__ */ jsx(
2197
+ PipelineStepper,
2198
+ {
2199
+ plannedSteps,
2200
+ byStage: combinedByStage,
2201
+ emptyText: "No pipeline activity recorded yet.",
2202
+ stageCatalog
2203
+ }
2204
+ )
2205
+ ] })
2206
+ ] }) });
2207
+ }
2208
+ function OverviewTab({
2209
+ list,
2210
+ progress,
2211
+ leadGenConfig
2212
+ }) {
2213
+ const hasMetadata = list.metadata && Object.keys(list.metadata).length > 0;
2214
+ return /* @__PURE__ */ jsxs(
2215
+ TabSection,
2216
+ {
2217
+ icon: /* @__PURE__ */ jsx(IconBuilding, { size: 16 }),
2218
+ title: "Overview",
2219
+ description: "Build status and source details for this list.",
2220
+ children: [
2221
+ /* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 1, sm: 2 }, children: [
2222
+ /* @__PURE__ */ jsx(StatCard, { label: "Companies", value: progress.totalCompanies, icon: IconBuilding }),
2223
+ /* @__PURE__ */ jsx(StatCard, { label: "Members", value: progress.totalMembers, icon: IconUsers })
2224
+ ] }),
2225
+ /* @__PURE__ */ jsx(PipelineStagesCard, { list, progress, leadGenConfig }),
2226
+ hasMetadata && /* @__PURE__ */ jsx(Card, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
2227
+ /* @__PURE__ */ jsx(Title, { order: 5, children: "Metadata" }),
2228
+ /* @__PURE__ */ jsx(JsonViewer, { data: list.metadata, maxHeight: 320 })
2229
+ ] }) })
2230
+ ]
2231
+ }
2232
+ );
2233
+ }
2234
+ function MembersTab({
2235
+ listId,
2236
+ progress,
2237
+ stageCatalog,
2238
+ onMemberClick
2239
+ }) {
2240
+ const [memberTab, setMemberTab] = useState("contacts");
2241
+ const contactsQuery = useContacts({ listId, limit: 100, offset: 0 });
2242
+ const companiesQuery = useCompanies({ listId, limit: 100, offset: 0 });
2243
+ const contacts = contactsQuery.data?.data ?? [];
2244
+ const companies = companiesQuery.data?.data ?? [];
2245
+ const activeQuery = memberTab === "contacts" ? contactsQuery : companiesQuery;
2246
+ const activeError = activeQuery.error ?? activeQuery.failureReason;
2247
+ const activeIsInitialLoading = activeQuery.isLoading && !activeError;
2248
+ return /* @__PURE__ */ jsxs(
2249
+ TabSection,
2250
+ {
2251
+ icon: /* @__PURE__ */ jsx(IconAddressBook, { size: 16 }),
2252
+ title: "Members",
2253
+ description: "Contacts and companies attached to this list.",
2254
+ rightSection: /* @__PURE__ */ jsx(
2255
+ SegmentedControl,
2256
+ {
2257
+ value: memberTab,
2258
+ onChange: (value) => setMemberTab(value),
2259
+ size: "xs",
2260
+ data: [
2261
+ { label: `Members (${progress.totalMembers})`, value: "contacts" },
2262
+ { label: `Companies (${progress.totalCompanies})`, value: "companies" }
2263
+ ]
2264
+ }
2265
+ ),
2266
+ children: [
2267
+ memberTab === "contacts" && /* @__PURE__ */ jsx(Fragment, { children: activeError ? /* @__PURE__ */ jsx(CenteredErrorState, { error: activeError, title: "Failed to load members", h: 160 }) : activeIsInitialLoading ? /* @__PURE__ */ jsx(Center, { p: "md", children: /* @__PURE__ */ jsx(Loader, { size: "sm" }) }) : !contacts.length ? /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "No contacts attached to this list yet." }) : /* @__PURE__ */ jsxs(Table, { highlightOnHover: true, children: [
2268
+ /* @__PURE__ */ jsx(Table.Thead, { children: /* @__PURE__ */ jsxs(Table.Tr, { children: [
2269
+ /* @__PURE__ */ jsx(Table.Th, { children: "Name" }),
2270
+ /* @__PURE__ */ jsx(Table.Th, { children: "Email" }),
2271
+ /* @__PURE__ */ jsx(Table.Th, { children: "Title" }),
2272
+ /* @__PURE__ */ jsx(Table.Th, { children: "Company" }),
2273
+ /* @__PURE__ */ jsx(Table.Th, { children: "Status" }),
2274
+ /* @__PURE__ */ jsx(Table.Th, { children: "State" }),
2275
+ /* @__PURE__ */ jsx(Table.Th, { children: "Created" })
2276
+ ] }) }),
2277
+ /* @__PURE__ */ jsx(Table.Tbody, { children: contacts.map((contact) => {
2278
+ const handleRowClick = () => onMemberClick?.(contact.id, "contact");
2279
+ const memberStage = displayMemberStageFor(contact, "contact", stageCatalog);
2280
+ return /* @__PURE__ */ jsxs(Table.Tr, { onClick: handleRowClick, style: { cursor: "pointer" }, children: [
2281
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { fw: 500, children: contactDisplayName(contact.firstName, contact.lastName) }) }),
2282
+ /* @__PURE__ */ jsx(Table.Td, { children: contact.email }),
2283
+ /* @__PURE__ */ jsx(Table.Td, { children: contact.title ?? "\u2014" }),
2284
+ /* @__PURE__ */ jsx(Table.Td, { children: contact.company?.name ?? "\u2014" }),
2285
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: getStatusColor3(contact.status), children: contact.status }) }),
2286
+ /* @__PURE__ */ jsx(Table.Td, { children: memberStage ? /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "dot", color: getMemberStateColor2(memberStage.key, stageCatalog), children: memberStage.label }) : /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "\u2014" }) }),
2287
+ /* @__PURE__ */ jsx(Table.Td, { children: formatDateTime3(contact.createdAt) })
2288
+ ] }, contact.id);
2289
+ }) })
2290
+ ] }) }),
2291
+ memberTab === "companies" && /* @__PURE__ */ jsx(Fragment, { children: activeError ? /* @__PURE__ */ jsx(CenteredErrorState, { error: activeError, title: "Failed to load companies", h: 160 }) : activeIsInitialLoading ? /* @__PURE__ */ jsx(Center, { p: "md", children: /* @__PURE__ */ jsx(Loader, { size: "sm" }) }) : !companies.length ? /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "No companies attached to this list yet." }) : /* @__PURE__ */ jsxs(Table, { highlightOnHover: true, children: [
2292
+ /* @__PURE__ */ jsx(Table.Thead, { children: /* @__PURE__ */ jsxs(Table.Tr, { children: [
2293
+ /* @__PURE__ */ jsx(Table.Th, { children: "Name" }),
2294
+ /* @__PURE__ */ jsx(Table.Th, { children: "Domain" }),
2295
+ /* @__PURE__ */ jsx(Table.Th, { children: "Segment" }),
2296
+ /* @__PURE__ */ jsx(Table.Th, { children: "Contacts" }),
2297
+ /* @__PURE__ */ jsx(Table.Th, { children: "Status" }),
2298
+ /* @__PURE__ */ jsx(Table.Th, { children: "State" }),
2299
+ /* @__PURE__ */ jsx(Table.Th, { children: "Created" })
2300
+ ] }) }),
2301
+ /* @__PURE__ */ jsx(Table.Tbody, { children: companies.map((company) => {
2302
+ const handleRowClick = () => onMemberClick?.(company.id, "company");
2303
+ const memberStage = displayMemberStageFor(company, "company", stageCatalog);
2304
+ return /* @__PURE__ */ jsxs(Table.Tr, { onClick: handleRowClick, style: { cursor: "pointer" }, children: [
2305
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { fw: 500, children: company.name }) }),
2306
+ /* @__PURE__ */ jsx(Table.Td, { children: company.domain ?? "\u2014" }),
2307
+ /* @__PURE__ */ jsx(Table.Td, { children: company.segment ?? "\u2014" }),
2308
+ /* @__PURE__ */ jsx(Table.Td, { children: company.contactCount }),
2309
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: getStatusColor3(company.status), children: company.status }) }),
2310
+ /* @__PURE__ */ jsx(Table.Td, { children: memberStage ? /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "dot", color: getMemberStateColor2(memberStage.key, stageCatalog), children: memberStage.label }) : /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "\u2014" }) }),
2311
+ /* @__PURE__ */ jsx(Table.Td, { children: formatDateTime3(company.createdAt) })
2312
+ ] }, company.id);
2313
+ }) })
2314
+ ] }) })
2315
+ ]
2316
+ }
2317
+ );
2318
+ }
2319
+ var panelStyle = {
2320
+ flex: 1,
2321
+ minHeight: 0,
2322
+ minWidth: 0,
2323
+ overflowX: "hidden",
2324
+ overflowY: "auto",
2325
+ paddingTop: "var(--mantine-spacing-sm)"
2326
+ };
2327
+ function StepDetailRightColumn({
2328
+ configuration,
2329
+ records,
2330
+ advanced,
2331
+ runs,
2332
+ action,
2333
+ activeTab,
2334
+ onTabChange
2335
+ }) {
2336
+ return /* @__PURE__ */ jsxs(
2337
+ Stack,
2338
+ {
2339
+ gap: "sm",
2340
+ style: {
2341
+ flex: 1,
2342
+ minHeight: 0,
2343
+ minWidth: 0
2344
+ },
2345
+ children: [
2346
+ /* @__PURE__ */ jsxs(
2347
+ Tabs,
2348
+ {
2349
+ value: activeTab,
2350
+ onChange: (value) => {
2351
+ if (value === "configuration" || value === "records" || value === "advanced" || value === "runs") {
2352
+ onTabChange(value);
2353
+ }
2354
+ },
2355
+ style: {
2356
+ flex: 1,
2357
+ minHeight: 0,
2358
+ minWidth: 0,
2359
+ display: "flex",
2360
+ flexDirection: "column"
2361
+ },
2362
+ children: [
2363
+ /* @__PURE__ */ jsxs(Tabs.List, { children: [
2364
+ /* @__PURE__ */ jsx(Tabs.Tab, { value: "configuration", children: "Configuration" }),
2365
+ /* @__PURE__ */ jsx(Tabs.Tab, { value: "records", children: "Records" }),
2366
+ /* @__PURE__ */ jsx(Tabs.Tab, { value: "advanced", children: "Advanced" }),
2367
+ /* @__PURE__ */ jsx(Tabs.Tab, { value: "runs", children: "Runs" })
2368
+ ] }),
2369
+ /* @__PURE__ */ jsx(Tabs.Panel, { value: "configuration", style: panelStyle, children: configuration }),
2370
+ /* @__PURE__ */ jsx(Tabs.Panel, { value: "records", style: panelStyle, children: records ?? /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "No records view configured for this step." }) }),
2371
+ /* @__PURE__ */ jsx(Tabs.Panel, { value: "advanced", style: panelStyle, children: advanced ?? /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "No advanced settings for this step." }) }),
2372
+ /* @__PURE__ */ jsx(Tabs.Panel, { value: "runs", style: panelStyle, children: runs })
2373
+ ]
2374
+ }
2375
+ ),
2376
+ /* @__PURE__ */ jsx(Stack, { gap: "xs", style: { flexShrink: 0, minWidth: 0, overflowX: "hidden" }, children: action })
2377
+ ]
2378
+ }
2379
+ );
2380
+ }
2381
+ function renderRecordValue(value, column) {
2382
+ if (value === null || value === void 0 || value === "") {
2383
+ return /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "-" });
2384
+ }
2385
+ switch (column.renderType) {
2386
+ case "badge":
2387
+ return /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: column.badgeColor ?? getRecordStatusColor(String(value)), children: compactText(value) });
2388
+ case "datetime":
2389
+ return compactText(typeof value === "string" ? formatDateTime3(value) : value);
2390
+ case "count":
2391
+ return Array.isArray(value) ? value.length : compactText(value);
2392
+ case "json":
2393
+ return /* @__PURE__ */ jsx(Text, { size: "xs", lineClamp: 2, children: compactText(value) });
2394
+ case "text":
2395
+ default:
2396
+ return /* @__PURE__ */ jsx(Text, { size: "sm", lineClamp: 2, children: compactText(value) });
2397
+ }
2398
+ }
2399
+ function getBuildToneStyle(tone) {
2400
+ switch (tone) {
2401
+ case "complete":
2402
+ return {
2403
+ backgroundColor: "color-mix(in srgb, var(--color-success) 16%, transparent)",
2404
+ borderColor: "color-mix(in srgb, var(--color-success) 34%, var(--color-border))",
2405
+ color: "var(--color-success)"
2406
+ };
2407
+ case "failed":
2408
+ return {
2409
+ backgroundColor: "color-mix(in srgb, var(--color-error) 14%, transparent)",
2410
+ borderColor: "color-mix(in srgb, var(--color-error) 38%, var(--color-border))",
2411
+ color: "var(--color-error)"
2412
+ };
2413
+ case "ready":
2414
+ return {
2415
+ backgroundColor: "color-mix(in srgb, var(--color-primary) 14%, transparent)",
2416
+ borderColor: "var(--border-primary-muted)",
2417
+ color: "var(--color-primary)"
2418
+ };
2419
+ case "blocked":
2420
+ case "neutral":
2421
+ default:
2422
+ return {
2423
+ backgroundColor: "color-mix(in srgb, var(--color-surface-hover) 70%, transparent)",
2424
+ borderColor: "var(--color-border)",
2425
+ color: "var(--color-text-dimmed)"
2426
+ };
2427
+ }
2428
+ }
2429
+ function StatusPill({ label, tone }) {
2430
+ return /* @__PURE__ */ jsx(
2431
+ Box,
2432
+ {
2433
+ component: "span",
2434
+ px: 8,
2435
+ py: 3,
2436
+ style: {
2437
+ ...getBuildToneStyle(tone),
2438
+ borderRadius: 999,
2439
+ borderStyle: "solid",
2440
+ borderWidth: 1,
2441
+ display: "inline-flex",
2442
+ fontSize: 11,
2443
+ fontWeight: 700,
2444
+ lineHeight: 1,
2445
+ whiteSpace: "nowrap"
2446
+ },
2447
+ children: label
2448
+ }
2449
+ );
2450
+ }
2451
+ function StepCounterLine({ step, isRunning = false }) {
2452
+ if (isRunning) {
2453
+ const processed = step.complete > 0 ? `${step.complete} processed` : "processing";
2454
+ return /* @__PURE__ */ jsxs(Text, { size: "xs", fw: 700, style: { color: "var(--color-primary)" }, children: [
2455
+ "Running - ",
2456
+ processed
2457
+ ] });
2458
+ }
2459
+ const items = [
2460
+ step.ready > 0 ? { label: "ready", value: step.ready, color: "var(--color-primary)" } : null,
2461
+ step.complete > 0 ? { label: "done", value: step.complete, color: "var(--color-success)" } : null,
2462
+ step.failed > 0 ? { label: "failed", value: step.failed, color: "var(--color-error)" } : null,
2463
+ step.blocked > 0 ? { label: "waiting", value: step.blocked, color: "var(--color-text-dimmed)" } : null
2464
+ ].filter((item) => item !== null);
2465
+ if (!items.length) {
2466
+ return /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "No records yet" });
2467
+ }
2468
+ return /* @__PURE__ */ jsx(Group, { gap: "md", wrap: "nowrap", children: items.map((item) => /* @__PURE__ */ jsxs(Text, { size: "xs", fw: 700, style: { color: item.color }, children: [
2469
+ item.value,
2470
+ " ",
2471
+ item.label
2472
+ ] }, item.label)) });
2473
+ }
2474
+ function BuildStepProgressBar({ step, isRunning = false }) {
2475
+ const segments = [
2476
+ { key: "complete", value: step.complete, color: isRunning ? "var(--color-primary)" : "var(--color-success)" },
2477
+ { key: "ready", value: step.ready, color: "var(--color-primary)" },
2478
+ { key: "failed", value: step.failed, color: "var(--color-error)" },
2479
+ { key: "blocked", value: step.blocked, color: "var(--color-border)" }
2480
+ ].filter((segment) => segment.value > 0);
2481
+ const total = segments.reduce((sum, segment) => sum + segment.value, 0);
2482
+ return /* @__PURE__ */ jsx(
2483
+ Box,
2484
+ {
2485
+ h: 8,
2486
+ "aria-label": `${step.label} progress: ${step.complete} ${isRunning ? "processed" : "complete"}, ${step.ready} ready, ${step.failed} failed, ${step.blocked} blocked`,
2487
+ style: {
2488
+ backgroundColor: "color-mix(in srgb, var(--color-border) 65%, transparent)",
2489
+ borderRadius: 999,
2490
+ display: "flex",
2491
+ overflow: "hidden"
2492
+ },
2493
+ children: segments.map((segment) => /* @__PURE__ */ jsx(Box, { h: "100%", w: `${total > 0 ? segment.value / total * 100 : 0}%`, bg: segment.color }, segment.key))
2494
+ }
2495
+ );
2496
+ }
2497
+ function getDisplayStep(steps, recommendedStep) {
2498
+ return recommendedStep ?? steps.find((step) => step.status !== "complete") ?? steps[steps.length - 1] ?? null;
2499
+ }
2500
+ function getBuildTabDescription(steps, currentStep, runningStep = null) {
2501
+ if (!steps.length) return "No build steps are available for this list.";
2502
+ if (runningStep) return `${runningStep.label} is running.`;
2503
+ if (steps.every((step) => step.status === "complete")) return "List build complete.";
2504
+ if (!currentStep) return "No build steps need attention right now.";
2505
+ if (currentStep.status === "ready") return `${currentStep.label} is ready to run.`;
2506
+ if (currentStep.status === "failed") return `${currentStep.label} needs attention.`;
2507
+ return `${currentStep.label} is waiting on earlier work.`;
2508
+ }
2509
+ var RECORDS_PAGE_SIZE = 25;
2510
+ function setValueAtInputPath(source, path, value) {
2511
+ const segments = path.split(".").filter(Boolean);
2512
+ if (!segments.length) return source;
2513
+ const next = { ...source };
2514
+ let cursor = next;
2515
+ segments.forEach((segment, index) => {
2516
+ if (index === segments.length - 1) {
2517
+ cursor[segment] = value;
2518
+ return;
2519
+ }
2520
+ const current = cursor[segment];
2521
+ const child = current && typeof current === "object" && !Array.isArray(current) ? { ...current } : {};
2522
+ cursor[segment] = child;
2523
+ cursor = child;
2524
+ });
2525
+ return next;
2526
+ }
2527
+ function getSelectedCredential(credentials, selections, requirement) {
2528
+ const selectedId = selections[requirement.key];
2529
+ return credentials.find((credential) => credential.id === selectedId) ?? null;
2530
+ }
2531
+ function filterCredentialRequirementFields(layout, requirements) {
2532
+ if (!layout || requirements.length === 0) return layout;
2533
+ const credentialInputPaths = new Set(requirements.map((requirement) => requirement.inputPath).filter(Boolean));
2534
+ if (!credentialInputPaths.size) return layout;
2535
+ const filterSection = (section) => {
2536
+ const fields = section.fields.filter((field) => !credentialInputPaths.has(field.path));
2537
+ return fields.length > 0 ? { ...section, fields } : null;
2538
+ };
2539
+ return {
2540
+ ...layout,
2541
+ sections: layout.sections.map(filterSection).filter((section) => section !== null),
2542
+ advanced: layout.advanced ? filterSection(layout.advanced) ?? void 0 : void 0
2543
+ };
2544
+ }
2545
+ function hasMainConfigFields(layout) {
2546
+ return Boolean(layout?.sections.some((section) => section.fields.length > 0));
2547
+ }
2548
+ function hasAdvancedConfigFields(layout) {
2549
+ return Boolean(layout?.advanced?.fields.length);
2550
+ }
2551
+ function formatCredentialVerificationDetails(details) {
2552
+ if (!details) return null;
2553
+ const entries = Object.entries(details).filter(([, value]) => value !== null && value !== void 0 && value !== "").slice(0, 4);
2554
+ if (!entries.length) return null;
2555
+ return entries.map(([key, value]) => `${key}: ${compactText(value)}`).join(" | ");
2556
+ }
2557
+ function CredentialRequirementsPanel({
2558
+ requirements,
2559
+ credentials,
2560
+ credentialsLoading,
2561
+ credentialsError,
2562
+ selections,
2563
+ verificationByCredentialId,
2564
+ verifyingCredentialId,
2565
+ disabled,
2566
+ onSelect,
2567
+ onVerify
2568
+ }) {
2569
+ if (!requirements.length) return null;
2570
+ return /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
2571
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", fw: 700, tt: "uppercase", children: "Credentials" }),
2572
+ requirements.map((requirement) => {
2573
+ const selectedCredential = getSelectedCredential(credentials, selections, requirement);
2574
+ const verification = selectedCredential ? verificationByCredentialId[selectedCredential.id] : null;
2575
+ const credentialsUnavailable = Boolean(credentialsError) && credentials.length === 0;
2576
+ return /* @__PURE__ */ jsxs(Stack, { gap: 6, children: [
2577
+ /* @__PURE__ */ jsxs(Group, { gap: "xs", align: "flex-end", wrap: "nowrap", children: [
2578
+ /* @__PURE__ */ jsx(
2579
+ Select,
2580
+ {
2581
+ label: requirement.label,
2582
+ description: `${requirement.provider} credential for ${requirement.inputPath}`,
2583
+ placeholder: credentialsLoading ? "Loading credentials..." : credentialsUnavailable ? "Credentials unavailable" : "Select credential",
2584
+ data: credentials.map((credential) => ({
2585
+ value: credential.id,
2586
+ label: `${credential.name} (${credential.provider ?? credential.type})`
2587
+ })),
2588
+ value: selectedCredential?.id ?? null,
2589
+ onChange: (credentialId) => {
2590
+ onSelect(requirement, credentials.find((credential) => credential.id === credentialId) ?? null);
2591
+ },
2592
+ required: requirement.required,
2593
+ disabled: disabled || credentialsLoading || credentialsUnavailable || credentials.length === 0,
2594
+ style: { flex: 1 }
2595
+ }
2596
+ ),
2597
+ /* @__PURE__ */ jsx(
2598
+ Button,
2599
+ {
2600
+ size: "xs",
2601
+ variant: "light",
2602
+ disabled: disabled || !selectedCredential,
2603
+ loading: selectedCredential ? verifyingCredentialId === selectedCredential.id : false,
2604
+ onClick: () => selectedCredential && onVerify(selectedCredential),
2605
+ children: "Test credential"
2606
+ }
2607
+ )
2608
+ ] }),
2609
+ credentialsUnavailable ? /* @__PURE__ */ jsx(Alert, { color: "red", variant: "light", icon: /* @__PURE__ */ jsx(IconAlertCircle, { size: 16 }), children: /* @__PURE__ */ jsxs(Text, { size: "sm", children: [
2610
+ "Credentials could not be loaded. ",
2611
+ credentialsError?.message ?? "Refresh the page and try again."
2612
+ ] }) }) : !credentialsLoading && credentials.length === 0 ? /* @__PURE__ */ jsx(
2613
+ Alert,
2614
+ {
2615
+ color: requirement.required ? "yellow" : "gray",
2616
+ variant: "light",
2617
+ icon: /* @__PURE__ */ jsx(IconAlertCircle, { size: 16 }),
2618
+ children: /* @__PURE__ */ jsxs(Text, { size: "sm", children: [
2619
+ "No credentials found. Add one in",
2620
+ " ",
2621
+ /* @__PURE__ */ jsx(Text, { component: "a", href: "/settings/credentials", size: "sm", fw: 600, children: "Settings -> Credentials" }),
2622
+ "."
2623
+ ] })
2624
+ }
2625
+ ) : null,
2626
+ verification ? /* @__PURE__ */ jsx(
2627
+ Alert,
2628
+ {
2629
+ color: verification.status === "success" ? "green" : "red",
2630
+ variant: "light",
2631
+ icon: /* @__PURE__ */ jsx(IconAlertCircle, { size: 16 }),
2632
+ children: /* @__PURE__ */ jsxs(Stack, { gap: 2, children: [
2633
+ /* @__PURE__ */ jsx(Text, { size: "sm", children: verification.message }),
2634
+ verification.checkedAt ? /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
2635
+ "Checked ",
2636
+ formatDateTime3(verification.checkedAt)
2637
+ ] }) : null,
2638
+ formatCredentialVerificationDetails(verification.details) ? /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: formatCredentialVerificationDetails(verification.details) }) : null
2639
+ ] })
2640
+ }
2641
+ ) : null
2642
+ ] }, requirement.key);
2643
+ })
2644
+ ] });
2645
+ }
2646
+ function BuildStepRunningWatcher({
2647
+ listId,
2648
+ resourceId,
2649
+ onRunningChange
2650
+ }) {
2651
+ const queryClient = useQueryClient();
2652
+ const { workOSOrganizationId } = useElevasisServices();
2653
+ const [wasRunning, setWasRunning] = useState(false);
2654
+ const inFlightQuery = useInFlightExecutions(resourceId, {
2655
+ enabled: Boolean(resourceId),
2656
+ limit: 5,
2657
+ listId,
2658
+ refetchInterval: 2e3
2659
+ });
2660
+ const isRunning = (inFlightQuery.data?.executions.length ?? 0) > 0;
2661
+ useExecutionSSE(resourceId, {
2662
+ enabled: Boolean(resourceId) && isRunning,
2663
+ listId
2664
+ });
2665
+ useEffect(() => {
2666
+ if (!inFlightQuery.isFetched) return;
2667
+ onRunningChange(resourceId, isRunning);
2668
+ if (wasRunning && !isRunning) {
2669
+ queryClient.invalidateQueries({ queryKey: acquisitionListKeys.executionsBase(workOSOrganizationId, listId) });
2670
+ queryClient.invalidateQueries({ queryKey: acquisitionListKeys.progress(workOSOrganizationId, listId) });
2671
+ }
2672
+ setWasRunning(isRunning);
2673
+ return () => onRunningChange(resourceId, false);
2674
+ }, [
2675
+ inFlightQuery.isFetched,
2676
+ isRunning,
2677
+ listId,
2678
+ onRunningChange,
2679
+ queryClient,
2680
+ resourceId,
2681
+ wasRunning,
2682
+ workOSOrganizationId
2683
+ ]);
2684
+ return null;
2685
+ }
2686
+ function StepRecordsPanel({
2687
+ list,
2688
+ step,
2689
+ exportWorkflowId,
2690
+ stageCatalog,
2691
+ approvalByCompanyId,
2692
+ canManage,
2693
+ onApprovalChange
2694
+ }) {
2695
+ const [page, setPage] = useState(1);
2696
+ const recordsStageKey = step.recordSourceStageKey ?? step.recordsStageKey ?? step.stageKey;
2697
+ const recordsEntity = step.recordEntity ?? step.primaryEntity;
2698
+ const columns = step.recordColumns?.[recordsEntity] ?? [];
2699
+ const recordsQuery = useListRecords(list.id, {
2700
+ entity: recordsEntity,
2701
+ stage: recordsStageKey,
2702
+ limit: RECORDS_PAGE_SIZE,
2703
+ offset: (page - 1) * RECORDS_PAGE_SIZE
2704
+ });
2705
+ useEffect(() => {
2706
+ setPage(1);
2707
+ }, [step.id]);
2708
+ const rows = recordsQuery.data?.data ?? [];
2709
+ const total = recordsQuery.data?.total ?? 0;
2710
+ const totalPages = Math.max(1, Math.ceil(total / RECORDS_PAGE_SIZE));
2711
+ const isReviewExportStep = step.primaryEntity === "company" && (isLeadGenExportAction(step.actionKey) || step.action?.resourceId === exportWorkflowId);
2712
+ const approvedCompanyIds = Object.entries(approvalByCompanyId).filter(([, status]) => status === "approved").map(([companyId]) => companyId);
2713
+ const rejectedCompanyIds = Object.entries(approvalByCompanyId).filter(([, status]) => status === "rejected").map(([companyId]) => companyId);
2714
+ const setApproval = (companyId, status) => {
2715
+ onApprovalChange(companyId, status);
2716
+ };
2717
+ const getRecordDetailHref = (row) => row.entity === "company" ? `/crm/companies/${row.companyId}` : `/crm/contacts/${row.contactId}`;
2718
+ if (!columns.length) {
2719
+ return /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "No record columns are configured for this step." });
2720
+ }
2721
+ if (recordsQuery.isLoading) {
2722
+ return /* @__PURE__ */ jsx(Center, { p: "md", children: /* @__PURE__ */ jsx(Loader, { size: "sm" }) });
2723
+ }
2724
+ if (recordsQuery.error) {
2725
+ return /* @__PURE__ */ jsx(CenteredErrorState, { error: recordsQuery.error, title: "Failed to load records" });
2726
+ }
2727
+ if (!rows.length) {
2728
+ return /* @__PURE__ */ jsx(Stack, { gap: "xs", children: /* @__PURE__ */ jsxs(Text, { size: "sm", c: "dimmed", children: [
2729
+ "No records found for ",
2730
+ stageCatalog[recordsStageKey]?.label ?? recordsStageKey,
2731
+ "."
2732
+ ] }) });
2733
+ }
2734
+ return /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
2735
+ /* @__PURE__ */ jsxs(Group, { justify: "space-between", gap: "xs", children: [
2736
+ /* @__PURE__ */ jsxs(Stack, { gap: 2, children: [
2737
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", fw: 700, tt: "uppercase", children: "Records" }),
2738
+ /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
2739
+ total,
2740
+ " ",
2741
+ recordsEntity,
2742
+ " records from ",
2743
+ stageCatalog[recordsStageKey]?.label ?? recordsStageKey
2744
+ ] })
2745
+ ] }),
2746
+ recordsQuery.isFetching ? /* @__PURE__ */ jsx(Loader, { size: "xs" }) : null
2747
+ ] }),
2748
+ /* @__PURE__ */ jsx(Box, { style: { overflowX: "auto" }, children: /* @__PURE__ */ jsxs(Table, { highlightOnHover: true, children: [
2749
+ /* @__PURE__ */ jsx(Table.Thead, { children: /* @__PURE__ */ jsxs(Table.Tr, { children: [
2750
+ columns.map((column) => /* @__PURE__ */ jsx(Table.Th, { style: column.width ? { width: column.width } : void 0, children: column.label }, column.key)),
2751
+ /* @__PURE__ */ jsx(Table.Th, { children: "Status" }),
2752
+ /* @__PURE__ */ jsx(Table.Th, { children: "Failure reason" }),
2753
+ isReviewExportStep ? /* @__PURE__ */ jsx(Table.Th, { children: "Approval" }) : null,
2754
+ /* @__PURE__ */ jsx(Table.Th, { w: 44, "aria-label": "Open record" })
2755
+ ] }) }),
2756
+ /* @__PURE__ */ jsx(Table.Tbody, { children: rows.map((row) => {
2757
+ const columnSource = getRecordColumnSource(row);
2758
+ const status = getRecordStageStatus(row, recordsStageKey);
2759
+ const companyId = row.entity === "company" ? row.companyId : null;
2760
+ const recordHref = getRecordDetailHref(row);
2761
+ return /* @__PURE__ */ jsxs(Table.Tr, { children: [
2762
+ columns.map((column) => /* @__PURE__ */ jsx(Table.Td, { children: renderRecordValue(valueAtPath(columnSource, column.path), column) }, column.key)),
2763
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: getRecordStatusColor(status), children: status }) }),
2764
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", lineClamp: 2, children: getStageFailureReason(row, recordsStageKey) }) }),
2765
+ isReviewExportStep ? /* @__PURE__ */ jsx(Table.Td, { children: companyId ? /* @__PURE__ */ jsxs(Group, { gap: 4, wrap: "nowrap", "aria-label": `Approval for ${getRecordDisplayName(row)}`, children: [
2766
+ /* @__PURE__ */ jsx(
2767
+ Button,
2768
+ {
2769
+ size: "compact-xs",
2770
+ variant: approvalByCompanyId[companyId] === "approved" ? "filled" : "light",
2771
+ color: "green",
2772
+ disabled: !canManage,
2773
+ onClick: (event) => {
2774
+ event.stopPropagation();
2775
+ if (!canManage) return;
2776
+ setApproval(companyId, "approved");
2777
+ },
2778
+ children: "Approve"
2779
+ }
2780
+ ),
2781
+ /* @__PURE__ */ jsx(
2782
+ Button,
2783
+ {
2784
+ size: "compact-xs",
2785
+ variant: approvalByCompanyId[companyId] === "rejected" ? "filled" : "light",
2786
+ color: "red",
2787
+ disabled: !canManage,
2788
+ onClick: (event) => {
2789
+ event.stopPropagation();
2790
+ if (!canManage) return;
2791
+ setApproval(companyId, "rejected");
2792
+ },
2793
+ children: "Reject"
2794
+ }
2795
+ )
2796
+ ] }) : /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "-" }) }) : null,
2797
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Tooltip, { label: "Open record", children: /* @__PURE__ */ jsx(
2798
+ ActionIcon,
2799
+ {
2800
+ component: "a",
2801
+ href: recordHref,
2802
+ target: "_blank",
2803
+ rel: "noopener noreferrer",
2804
+ size: "sm",
2805
+ variant: "subtle",
2806
+ "aria-label": `Open ${getRecordDisplayName(row)}`,
2807
+ children: /* @__PURE__ */ jsx(IconExternalLink, { size: 14 })
2808
+ }
2809
+ ) }) })
2810
+ ] }, row.id);
2811
+ }) })
2812
+ ] }) }),
2813
+ /* @__PURE__ */ jsxs(Group, { justify: "space-between", gap: "xs", children: [
2814
+ /* @__PURE__ */ jsx(Pagination, { size: "sm", total: totalPages, value: page, onChange: setPage, disabled: recordsQuery.isFetching }),
2815
+ isReviewExportStep ? /* @__PURE__ */ jsx(Group, { gap: "xs", children: /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
2816
+ approvedCompanyIds.length,
2817
+ " approved, ",
2818
+ rejectedCompanyIds.length,
2819
+ " rejected"
2820
+ ] }) }) : null
2821
+ ] })
2822
+ ] });
2823
+ }
2824
+ function BuildTab({
2825
+ list,
2826
+ steps,
2827
+ executions,
2828
+ recommendedStep,
2829
+ optimisticRuns,
2830
+ exportWorkflowId,
2831
+ stageCatalog,
2832
+ canManage,
2833
+ onRunStep,
2834
+ onRetryStep,
2835
+ onViewRunLog
2836
+ }) {
2837
+ const queryClient = useQueryClient();
2838
+ const { workOSOrganizationId } = useElevasisServices();
2839
+ const [selectedStepId, setSelectedStepId] = useState(null);
2840
+ const [stepInputFormState, setStepInputFormState] = useState({});
2841
+ const [stepInputError, setStepInputError] = useState(null);
2842
+ const [stepInputInitialized, setStepInputInitialized] = useState(false);
2843
+ const [activeRightColumnTab, setActiveRightColumnTab] = useState("configuration");
2844
+ const [credentialSelections, setCredentialSelections] = useState({});
2845
+ const [credentialVerificationById, setCredentialVerificationById] = useState({});
2846
+ const [reviewExportApprovals, setReviewExportApprovals] = useState({});
2847
+ const [runningResources, setRunningResources] = useState({});
2848
+ const lastObservedTerminalRunRef = useRef(null);
2849
+ const [stepInputResetKey, setStepInputResetKey] = useState(0);
2850
+ const currentStep = getDisplayStep(steps, recommendedStep);
2851
+ const selectedStep = steps.find((step) => step.id === selectedStepId) ?? currentStep;
2852
+ const selectedStepIndex = selectedStep ? steps.findIndex((step) => step.id === selectedStep.id) : -1;
2853
+ const selectedActionKind = selectedStep?.nextActionKind ?? "none";
2854
+ const selectedCapabilityKey = selectedStep?.action?.actionKey ?? selectedStep?.actionKey;
2855
+ const selectedResourceId = selectedStep?.action?.resourceId ?? null;
2856
+ const stepSchema = selectedStep?.action?.schema;
2857
+ const stepLayout = selectedStep?.action?.layout;
2858
+ const credentialRequirements = selectedStep?.credentialRequirements ?? [];
2859
+ const selectedStepIsReviewExport = selectedStep?.primaryEntity === "company" && (isLeadGenExportAction(selectedStep.actionKey) || selectedStep.action?.resourceId === exportWorkflowId);
2860
+ const selectedExportWorkflowId = selectedStep?.action?.resourceId ?? exportWorkflowId ?? "";
2861
+ const visibleStepLayout = useMemo(
2862
+ () => filterCredentialRequirementFields(stepLayout, credentialRequirements),
2863
+ [stepLayout, credentialRequirements]
2864
+ );
2865
+ const showMainStepConfig = stepSchema && hasMainConfigFields(visibleStepLayout);
2866
+ const showAdvancedStepConfig = stepSchema && hasAdvancedConfigFields(visibleStepLayout);
2867
+ const recentRunsQuery = useListExecutions(list.id, { resourceId: selectedResourceId, limit: 5 });
2868
+ const recentRuns = useMemo(() => {
2869
+ const serverRuns = recentRunsQuery.data ?? [];
2870
+ const serverExecutionIds = new Set(serverRuns.map((run) => run.executionId));
2871
+ const pendingRuns = optimisticRuns.filter(
2872
+ (run) => run.resourceId === selectedResourceId && !serverExecutionIds.has(run.executionId)
2873
+ );
2874
+ return [...pendingRuns, ...serverRuns].sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()).slice(0, 5);
2875
+ }, [optimisticRuns, recentRunsQuery.data, selectedResourceId]);
2876
+ const runningResourceIds = useMemo(() => {
2877
+ const ids = /* @__PURE__ */ new Set();
2878
+ const serverExecutionIds = new Set(executions.map((run) => run.executionId));
2879
+ executions.forEach((run) => {
2880
+ if (run.resourceId && isRunInFlight(run.status)) ids.add(run.resourceId);
2881
+ });
2882
+ optimisticRuns.forEach((run) => {
2883
+ if (serverExecutionIds.has(run.executionId)) return;
2884
+ if (run.resourceId && isRunInFlight(run.status)) ids.add(run.resourceId);
2885
+ });
2886
+ return ids;
2887
+ }, [executions, optimisticRuns]);
2888
+ const runningStep = steps.find((step) => {
2889
+ const resourceId = step.action?.resourceId;
2890
+ return resourceId ? (runningResources[resourceId] ?? false) || runningResourceIds.has(resourceId) : false;
2891
+ }) ?? null;
2892
+ const latestRecentRun = recentRuns[0] ?? null;
2893
+ const credentialsQuery = useCredentials();
2894
+ const credentials = credentialsQuery.data ?? [];
2895
+ const verifyCredential = useVerifyCredential();
2896
+ const exportExecution = useWorkflowExecution({
2897
+ workflowId: selectedExportWorkflowId,
2898
+ listId: list.id
2899
+ });
2900
+ const approvedCompanyIds = Object.entries(reviewExportApprovals).filter(([, status]) => status === "approved").map(([companyId]) => companyId);
2901
+ const rejectedCompanyIds = Object.entries(reviewExportApprovals).filter(([, status]) => status === "rejected").map(([companyId]) => companyId);
2902
+ const canExportSelectedRecords = selectedStepIsReviewExport && activeRightColumnTab === "records" && approvedCompanyIds.length > 0;
2903
+ const selectedExportDisabledReason = !selectedStepIsReviewExport || activeRightColumnTab !== "records" ? null : approvedCompanyIds.length === 0 ? "Approve records" : null;
2904
+ const canRunSelectedAction = !!selectedStep?.action && (selectedActionKind === "retry_failed" || selectedActionKind === "run_next_batch");
2905
+ const selectedResourceIsRunning = selectedResourceId ? (runningResources[selectedResourceId] ?? false) || runningResourceIds.has(selectedResourceId) : false;
2906
+ const selectedActionDisabled = !canManage ? true : selectedStepIsReviewExport && activeRightColumnTab !== "records" ? false : selectedStepIsReviewExport ? !canExportSelectedRecords || exportExecution.isPending : !canRunSelectedAction || selectedResourceIsRunning;
2907
+ const selectedActionLabel = selectedResourceIsRunning ? "Running..." : selectedStepIsReviewExport && activeRightColumnTab !== "records" ? "Approve records" : selectedStepIsReviewExport ? selectedExportDisabledReason ?? "Export approved" : selectedActionKind === "none" ? "No action" : getStepActionLabel(selectedActionKind);
2908
+ const selectedActionIcon = selectedStepIsReviewExport && activeRightColumnTab !== "records" ? /* @__PURE__ */ jsx(IconAddressBook, { size: 14 }) : selectedActionKind === "retry_failed" ? /* @__PURE__ */ jsx(IconRefresh, { size: 14 }) : /* @__PURE__ */ jsx(IconPlayerPlay, { size: 14 });
2909
+ const handleResourceRunningChange = useCallback((resourceId, isRunning) => {
2910
+ setRunningResources((current) => {
2911
+ if (current[resourceId] === isRunning) return current;
2912
+ return { ...current, [resourceId]: isRunning };
2913
+ });
2914
+ }, []);
2915
+ useEffect(() => {
2916
+ if (!selectedResourceId || !latestRecentRun || !isRunTerminal(latestRecentRun.status)) return;
2917
+ if (lastObservedTerminalRunRef.current === latestRecentRun.executionId) return;
2918
+ lastObservedTerminalRunRef.current = latestRecentRun.executionId;
2919
+ handleResourceRunningChange(selectedResourceId, false);
2920
+ queryClient.invalidateQueries({ queryKey: acquisitionListKeys.detail(workOSOrganizationId, list.id) });
2921
+ queryClient.invalidateQueries({ queryKey: acquisitionListKeys.progress(workOSOrganizationId, list.id) });
2922
+ queryClient.invalidateQueries({
2923
+ queryKey: [...acquisitionListKeys.all, "records", workOSOrganizationId, list.id]
2924
+ });
2925
+ }, [handleResourceRunningChange, latestRecentRun, list.id, queryClient, selectedResourceId, workOSOrganizationId]);
2926
+ useEffect(() => {
2927
+ setStepInputFormState(getInitialStepInput(list, selectedStep?.action));
2928
+ setStepInputError(null);
2929
+ setStepInputInitialized(false);
2930
+ setStepInputResetKey((k) => k + 1);
2931
+ setReviewExportApprovals({});
2932
+ }, [list, selectedStep?.action]);
2933
+ useEffect(() => {
2934
+ if (stepInputInitialized) return;
2935
+ if (recentRunsQuery.isLoading) return;
2936
+ if (recentRuns.length > 0) {
2937
+ const lastInput = getRunInput(recentRuns[0]);
2938
+ if (lastInput) {
2939
+ setStepInputFormState(sanitizeStepInput(asRecord2(lastInput), selectedCapabilityKey));
2940
+ setStepInputResetKey((k) => k + 1);
2941
+ }
2942
+ }
2943
+ setStepInputInitialized(true);
2944
+ }, [stepInputInitialized, recentRunsQuery.isLoading, recentRuns, selectedCapabilityKey]);
2945
+ const handleStepInputChange = (next) => {
2946
+ if (!canManage) return;
2947
+ setStepInputFormState(next);
2948
+ if (stepInputError) setStepInputError(null);
2949
+ };
2950
+ const handleCredentialSelect = (requirement, credential) => {
2951
+ if (!canManage) return;
2952
+ setCredentialSelections((current) => ({
2953
+ ...current,
2954
+ [requirement.key]: credential?.id ?? null
2955
+ }));
2956
+ setStepInputFormState((current) => setValueAtInputPath(current, requirement.inputPath, credential?.name ?? ""));
2957
+ if (stepInputError) setStepInputError(null);
2958
+ };
2959
+ const handleVerifyCredential = (credential) => {
2960
+ if (!canManage) return;
2961
+ verifyCredential.mutate(credential.id, {
2962
+ onSuccess: (result) => {
2963
+ setCredentialVerificationById((current) => ({
2964
+ ...current,
2965
+ [credential.id]: {
2966
+ status: result.ok ? "success" : "error",
2967
+ checkedAt: result.checkedAt,
2968
+ message: result.message ?? (result.ok ? "Credential verified." : "Credential verification failed."),
2969
+ details: result.details
2970
+ }
2971
+ }));
2972
+ },
2973
+ onError: (error) => {
2974
+ setCredentialVerificationById((current) => ({
2975
+ ...current,
2976
+ [credential.id]: {
2977
+ status: "error",
2978
+ message: error instanceof Error ? error.message : "Credential verification failed."
2979
+ }
2980
+ }));
2981
+ }
2982
+ });
2983
+ };
2984
+ const handleReviewExportApprovalChange = (companyId, status) => {
2985
+ if (!canManage) return;
2986
+ setReviewExportApprovals((current) => ({ ...current, [companyId]: status }));
2987
+ if (stepInputError) setStepInputError(null);
2988
+ };
2989
+ const exportApprovedRecords = async () => {
2990
+ if (!canManage) return;
2991
+ try {
2992
+ const result = await exportExecution.execute({
2993
+ input: {
2994
+ listId: list.id,
2995
+ mode: "export",
2996
+ approved: true,
2997
+ approvedCompanyIds,
2998
+ rejectedCompanyIds
2999
+ }
3000
+ });
3001
+ showSuccessNotification(`Export workflow run started: ${result.executionId}`);
3002
+ await queryClient.invalidateQueries({ queryKey: acquisitionListKeys.all });
3003
+ } catch (error) {
3004
+ showApiErrorNotification(error);
3005
+ }
3006
+ };
3007
+ const handleSelectedAction = async () => {
3008
+ if (!canManage) return;
3009
+ if (!selectedStep || selectedActionDisabled) return;
3010
+ if (selectedStepIsReviewExport && activeRightColumnTab !== "records") {
3011
+ setActiveRightColumnTab("records");
3012
+ return;
3013
+ }
3014
+ if (selectedStepIsReviewExport) {
3015
+ await exportApprovedRecords();
3016
+ return;
3017
+ }
3018
+ if (stepSchema) {
3019
+ const parsed = stepSchema.safeParse(stepInputFormState);
3020
+ if (!parsed.success) {
3021
+ const firstIssue = parsed.error.issues[0];
3022
+ setStepInputError(firstIssue?.message ?? "Validation failed");
3023
+ return;
3024
+ }
3025
+ }
3026
+ setStepInputError(null);
3027
+ const submittedInput = stepSchema ? stepInputFormState : {};
3028
+ setActiveRightColumnTab("runs");
3029
+ const resourceId = selectedStep.action?.resourceId ?? null;
3030
+ try {
3031
+ let result = null;
3032
+ if (selectedActionKind === "retry_failed") {
3033
+ result = await onRetryStep(selectedStep, submittedInput);
3034
+ } else {
3035
+ result = await onRunStep(selectedStep, submittedInput);
3036
+ }
3037
+ if (resourceId && result?.executionId) handleResourceRunningChange(resourceId, true);
3038
+ } catch {
3039
+ if (resourceId) handleResourceRunningChange(resourceId, false);
3040
+ }
3041
+ };
3042
+ const loadRecentRunInput = (run) => {
3043
+ if (!canManage) return;
3044
+ setStepInputFormState(sanitizeStepInput(asRecord2(getRunInput(run)), selectedCapabilityKey));
3045
+ setStepInputResetKey((k) => k + 1);
3046
+ setActiveRightColumnTab("configuration");
3047
+ };
3048
+ const openRecentRunLog = (run) => {
3049
+ const resourceId = run.resourceId ?? selectedResourceId;
3050
+ if (!resourceId) return;
3051
+ onViewRunLog(resourceId, run.executionId);
3052
+ };
3053
+ return /* @__PURE__ */ jsx(
3054
+ TabSection,
3055
+ {
3056
+ icon: /* @__PURE__ */ jsx(IconBolt, { size: 16 }),
3057
+ title: "Build",
3058
+ description: getBuildTabDescription(steps, currentStep, runningStep),
3059
+ children: /* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 1, md: 2 }, spacing: "md", children: [
3060
+ /* @__PURE__ */ jsx(Card, { withBorder: true, style: { height: "100%", display: "flex", flexDirection: "column" }, children: /* @__PURE__ */ jsxs(Stack, { gap: "xs", style: { flex: 1, minHeight: 0 }, children: [
3061
+ steps.map(
3062
+ (step) => step.action?.resourceId ? /* @__PURE__ */ jsx(
3063
+ BuildStepRunningWatcher,
3064
+ {
3065
+ listId: list.id,
3066
+ resourceId: step.action.resourceId,
3067
+ onRunningChange: handleResourceRunningChange
3068
+ },
3069
+ `running-${step.id}`
3070
+ ) : null
3071
+ ),
3072
+ steps.map((step, index) => {
3073
+ const isCurrent = currentStep?.id === step.id;
3074
+ const isSelected = selectedStep?.id === step.id;
3075
+ const stepResourceId = step.action?.resourceId ?? null;
3076
+ const isStepRunning = stepResourceId ? (runningResources[stepResourceId] ?? false) || runningResourceIds.has(stepResourceId) : false;
3077
+ const isWaiting = step.status === "blocked";
3078
+ const isPassiveWaiting = isWaiting && !isSelected && !isCurrent;
3079
+ const stepTone = isStepRunning ? "ready" : isSelected || isCurrent ? step.status : step.status === "complete" || isWaiting ? step.status : "neutral";
3080
+ return /* @__PURE__ */ jsx(
3081
+ UnstyledButton,
3082
+ {
3083
+ onClick: () => setSelectedStepId(step.id),
3084
+ style: {
3085
+ backgroundColor: isSelected ? "var(--surface-primary-strong)" : isStepRunning ? "color-mix(in srgb, var(--color-primary) 12%, transparent)" : isPassiveWaiting ? "color-mix(in srgb, var(--color-surface-hover) 38%, transparent)" : "transparent",
3086
+ border: isSelected ? "var(--active-border)" : isStepRunning ? "1px solid var(--border-primary-muted)" : "1px solid var(--color-border)",
3087
+ borderStyle: isPassiveWaiting && !isStepRunning ? "dashed" : "solid",
3088
+ borderRadius: 8,
3089
+ display: "block",
3090
+ opacity: isPassiveWaiting && !isStepRunning ? 0.72 : 1,
3091
+ padding: 12,
3092
+ textAlign: "left",
3093
+ width: "100%"
3094
+ },
3095
+ children: /* @__PURE__ */ jsxs(Group, { gap: "sm", align: "flex-start", wrap: "nowrap", children: [
3096
+ /* @__PURE__ */ jsx(
3097
+ Box,
3098
+ {
3099
+ w: 28,
3100
+ h: 28,
3101
+ style: {
3102
+ alignItems: "center",
3103
+ ...getBuildToneStyle(stepTone),
3104
+ borderRadius: 999,
3105
+ borderStyle: "solid",
3106
+ borderWidth: 1,
3107
+ display: "flex",
3108
+ flexShrink: 0,
3109
+ fontWeight: 700,
3110
+ justifyContent: "center"
3111
+ },
3112
+ children: index + 1
3113
+ }
3114
+ ),
3115
+ /* @__PURE__ */ jsxs(Stack, { gap: 4, style: { minWidth: 0, flex: 1 }, children: [
3116
+ /* @__PURE__ */ jsxs(Group, { gap: "xs", justify: "space-between", wrap: "nowrap", children: [
3117
+ /* @__PURE__ */ jsx(Text, { fw: 700, c: isPassiveWaiting && !isStepRunning ? "dimmed" : void 0, truncate: true, children: step.label }),
3118
+ !isStepRunning && isWaiting ? /* @__PURE__ */ jsx(StatusPill, { label: "Waiting", tone: "blocked" }) : null
3119
+ ] }),
3120
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", lineClamp: 2, children: step.description }),
3121
+ /* @__PURE__ */ jsx(StepCounterLine, { step, isRunning: isStepRunning })
3122
+ ] })
3123
+ ] })
3124
+ },
3125
+ step.id
3126
+ );
3127
+ })
3128
+ ] }) }),
3129
+ selectedStep ? /* @__PURE__ */ jsx(Card, { withBorder: true, style: { height: "100%", display: "flex", flexDirection: "column" }, children: /* @__PURE__ */ jsxs(Stack, { gap: "md", style: { flex: 1, minHeight: 0 }, children: [
3130
+ /* @__PURE__ */ jsxs(Stack, { gap: 2, style: { minWidth: 0, flexShrink: 0 }, children: [
3131
+ /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", fw: 700, tt: "uppercase", children: [
3132
+ "Step ",
3133
+ selectedStepIndex + 1,
3134
+ " details"
3135
+ ] }),
3136
+ /* @__PURE__ */ jsx(Title, { order: 5, children: selectedStep.label })
3137
+ ] }),
3138
+ /* @__PURE__ */ jsx(BuildStepProgressBar, { step: selectedStep, isRunning: selectedResourceIsRunning }),
3139
+ /* @__PURE__ */ jsx(
3140
+ StepDetailRightColumn,
3141
+ {
3142
+ activeTab: activeRightColumnTab,
3143
+ onTabChange: setActiveRightColumnTab,
3144
+ configuration: /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
3145
+ showMainStepConfig && visibleStepLayout ? /* @__PURE__ */ jsx(
3146
+ StepConfigForm,
3147
+ {
3148
+ slot: "main",
3149
+ schema: stepSchema,
3150
+ layout: visibleStepLayout,
3151
+ value: stepInputFormState,
3152
+ onChange: (v) => handleStepInputChange(asRecord2(v) ?? {}),
3153
+ disabled: !canManage || !canRunSelectedAction
3154
+ },
3155
+ `${selectedResourceId ?? "none"}-main-${stepInputResetKey}`
3156
+ ) : !credentialRequirements.length ? /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "No configuration for this step." }) : null,
3157
+ /* @__PURE__ */ jsx(
3158
+ CredentialRequirementsPanel,
3159
+ {
3160
+ requirements: credentialRequirements,
3161
+ credentials,
3162
+ credentialsLoading: credentialsQuery.isLoading,
3163
+ credentialsError: credentialsQuery.error instanceof Error ? credentialsQuery.error : null,
3164
+ selections: credentialSelections,
3165
+ verificationByCredentialId: credentialVerificationById,
3166
+ verifyingCredentialId: verifyCredential.isPending ? verifyCredential.variables ?? null : null,
3167
+ disabled: !canManage,
3168
+ onSelect: handleCredentialSelect,
3169
+ onVerify: handleVerifyCredential
3170
+ }
3171
+ )
3172
+ ] }),
3173
+ advanced: showAdvancedStepConfig && visibleStepLayout ? /* @__PURE__ */ jsx(
3174
+ StepConfigForm,
3175
+ {
3176
+ slot: "advanced",
3177
+ schema: stepSchema,
3178
+ layout: visibleStepLayout,
3179
+ value: stepInputFormState,
3180
+ onChange: (v) => handleStepInputChange(asRecord2(v) ?? {}),
3181
+ disabled: !canManage || !canRunSelectedAction
3182
+ },
3183
+ `${selectedResourceId ?? "none"}-advanced-${stepInputResetKey}`
3184
+ ) : void 0,
3185
+ records: /* @__PURE__ */ jsx(
3186
+ StepRecordsPanel,
3187
+ {
3188
+ list,
3189
+ step: selectedStep,
3190
+ exportWorkflowId,
3191
+ stageCatalog,
3192
+ approvalByCompanyId: reviewExportApprovals,
3193
+ canManage,
3194
+ onApprovalChange: handleReviewExportApprovalChange
3195
+ },
3196
+ selectedStep.id
3197
+ ),
3198
+ runs: selectedResourceId ? /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
3199
+ /* @__PURE__ */ jsxs(Group, { justify: "space-between", gap: "xs", children: [
3200
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", fw: 700, tt: "uppercase", children: "Recent runs" }),
3201
+ recentRunsQuery.isFetching ? /* @__PURE__ */ jsx(Loader, { size: "xs" }) : null
3202
+ ] }),
3203
+ !recentRuns.length && !recentRunsQuery.isLoading ? /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "No runs yet for this step." }) : /* @__PURE__ */ jsx(Stack, { gap: 4, children: recentRuns.map((run) => {
3204
+ const canUseSettings = hasRunInput(run);
3205
+ return /* @__PURE__ */ jsx(
3206
+ Box,
3207
+ {
3208
+ style: {
3209
+ border: "1px solid var(--color-border)",
3210
+ borderRadius: 8,
3211
+ padding: "8px 10px",
3212
+ width: "100%"
3213
+ },
3214
+ children: /* @__PURE__ */ jsxs(Group, { justify: "space-between", gap: "xs", wrap: "nowrap", children: [
3215
+ /* @__PURE__ */ jsx(
3216
+ UnstyledButton,
3217
+ {
3218
+ onClick: () => openRecentRunLog(run),
3219
+ style: { flex: 1, minWidth: 0, textAlign: "left" },
3220
+ children: /* @__PURE__ */ jsxs(Group, { justify: "space-between", gap: "xs", wrap: "nowrap", children: [
3221
+ /* @__PURE__ */ jsxs(Stack, { gap: 2, style: { minWidth: 0 }, children: [
3222
+ /* @__PURE__ */ jsx(Text, { size: "sm", fw: 600, truncate: true, children: formatDateTime3(run.createdAt) }),
3223
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", ff: "monospace", truncate: true, children: run.executionId })
3224
+ ] }),
3225
+ /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: getRunStatusColor(run.status), children: run.status })
3226
+ ] })
3227
+ }
3228
+ ),
3229
+ /* @__PURE__ */ jsx(Tooltip, { label: "Use this run's settings", children: /* @__PURE__ */ jsx(
3230
+ Button,
3231
+ {
3232
+ size: "xs",
3233
+ variant: "subtle",
3234
+ leftSection: /* @__PURE__ */ jsx(IconSettings, { size: 12 }),
3235
+ disabled: !canManage || !canUseSettings,
3236
+ onClick: () => loadRecentRunInput(run),
3237
+ children: "Use settings"
3238
+ }
3239
+ ) })
3240
+ ] })
3241
+ },
3242
+ run.executionId
3243
+ );
3244
+ }) })
3245
+ ] }) : /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "No runs available for this step." }),
3246
+ action: /* @__PURE__ */ jsxs(Fragment, { children: [
3247
+ stepInputError ? /* @__PURE__ */ jsx(Alert, { color: "red", icon: /* @__PURE__ */ jsx(IconAlertCircle, { size: 16 }), variant: "light", children: stepInputError }) : null,
3248
+ /* @__PURE__ */ jsx(Group, { gap: "xs", justify: "flex-end", wrap: "nowrap", style: { minWidth: 0, overflowX: "hidden" }, children: /* @__PURE__ */ jsx(Tooltip, { label: selectedExportDisabledReason, disabled: !selectedExportDisabledReason, children: /* @__PURE__ */ jsx(
3249
+ Button,
3250
+ {
3251
+ leftSection: selectedActionIcon,
3252
+ disabled: selectedActionDisabled,
3253
+ onClick: handleSelectedAction,
3254
+ style: { maxWidth: "100%" },
3255
+ children: selectedActionLabel
3256
+ }
3257
+ ) }) })
3258
+ ] })
3259
+ }
3260
+ )
3261
+ ] }) }) : null
3262
+ ] })
3263
+ }
3264
+ );
3265
+ }
3266
+ var LIST_DETAIL_TABS = ["overview", "members", "build", "runs"];
3267
+ function isListDetailTab(value) {
3268
+ return typeof value === "string" && LIST_DETAIL_TABS.includes(value);
3269
+ }
3270
+ function normalizeListDetailTab(value) {
3271
+ return isListDetailTab(value) ? value : "overview";
3272
+ }
3273
+ function ListDetailHeader({
3274
+ title,
3275
+ caption,
3276
+ rightSection
3277
+ }) {
3278
+ return /* @__PURE__ */ jsx(PageTitleCaption, { title, caption, rightSection });
3279
+ }
3280
+ function LeadGenListDetailPage({
3281
+ listId,
3282
+ headerActions,
3283
+ tabs,
3284
+ footer
3285
+ }) {
3286
+ const navigate = useNavigate();
3287
+ const actions = useListActions();
3288
+ const leadGenConfig = useLeadGenConfig();
3289
+ const stageCatalog = leadGenConfig.stageCatalog ?? {};
3290
+ const rawSearch = useSearch({ strict: false });
3291
+ const activeTab = normalizeListDetailTab(rawSearch.tab);
3292
+ const memberId = rawSearch.member ?? null;
3293
+ const memberKind = rawSearch.memberKind ?? null;
3294
+ const updateSearch = useCallback(
3295
+ (patch, options = {}) => {
3296
+ void navigate({
3297
+ to: ".",
3298
+ search: ((current) => {
3299
+ const next = { ...current, ...patch };
3300
+ for (const [key, value] of Object.entries(patch)) {
3301
+ if (value === void 0) {
3302
+ delete next[key];
3303
+ }
3304
+ }
3305
+ return next;
3306
+ }),
3307
+ replace: options.replace
3308
+ });
3309
+ },
3310
+ [navigate]
3311
+ );
3312
+ useEffect(() => {
3313
+ if (rawSearch.tab && !isListDetailTab(rawSearch.tab)) {
3314
+ updateSearch({ tab: "overview" }, { replace: true });
3315
+ }
3316
+ }, [rawSearch.tab, updateSearch]);
3317
+ const handleTabChange = (value) => {
3318
+ if (!isListDetailTab(value) || value === activeTab) return;
3319
+ updateSearch({ tab: value });
3320
+ };
3321
+ const handleMemberClick = (id, kind) => {
3322
+ updateSearch({ member: id, memberKind: kind });
3323
+ };
3324
+ const handleDrawerClose = () => {
3325
+ updateSearch({ member: void 0, memberKind: void 0 });
3326
+ };
3327
+ const listQuery = useList(listId);
3328
+ const progressQuery = useListProgress(listId);
3329
+ const executionsQuery = useListExecutions(listId);
3330
+ const canManageLeadGen = useAccess(AccessKeys.leadGenManage);
3331
+ const deleteListMutation = useDeleteList();
3332
+ const executeAsync = useExecuteAsync();
3333
+ const queryClient = useQueryClient();
3334
+ const { workOSOrganizationId } = useElevasisServices();
3335
+ const [deleteModalOpen, setDeleteModalOpen] = useState(false);
3336
+ const [runsResourceFilter, setRunsResourceFilter] = useState(null);
3337
+ const [optimisticRuns, setOptimisticRuns] = useState([]);
3338
+ const isLoading = listQuery.isLoading || progressQuery.isLoading || executionsQuery.isLoading;
3339
+ const error = listQuery.error ?? progressQuery.error ?? executionsQuery.error;
3340
+ const backButton = /* @__PURE__ */ jsx(
3341
+ Button,
3342
+ {
3343
+ variant: "light",
3344
+ size: "xs",
3345
+ leftSection: /* @__PURE__ */ jsx(IconArrowLeft, { size: 16 }),
3346
+ onClick: () => navigate({ to: "/lead-gen/lists" }),
3347
+ children: "Lists"
3348
+ }
3349
+ );
3350
+ if (isLoading) {
3351
+ return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsx(PageContainer, { children: /* @__PURE__ */ jsxs(Stack, { children: [
3352
+ /* @__PURE__ */ jsx(
3353
+ ListDetailHeader,
3354
+ {
3355
+ title: "List Detail",
3356
+ caption: "Configuration, progress, and execution history for a single lead-gen list",
3357
+ rightSection: backButton
3358
+ }
3359
+ ),
3360
+ /* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsx(Center, { p: "xl", children: /* @__PURE__ */ jsx(Loader, {}) }) })
3361
+ ] }) }) });
3362
+ }
3363
+ if (error) {
3364
+ return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsx(PageContainer, { children: /* @__PURE__ */ jsxs(Stack, { children: [
3365
+ /* @__PURE__ */ jsx(
3366
+ ListDetailHeader,
3367
+ {
3368
+ title: "List Detail",
3369
+ caption: "Configuration, progress, and execution history for a single lead-gen list",
3370
+ rightSection: backButton
3371
+ }
3372
+ ),
3373
+ /* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsx(CenteredErrorState, { error, title: "Failed to load list detail" }) })
3374
+ ] }) }) });
3375
+ }
3376
+ if (!listQuery.data || !progressQuery.data) {
3377
+ return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsx(PageContainer, { children: /* @__PURE__ */ jsxs(Stack, { children: [
3378
+ /* @__PURE__ */ jsx(
3379
+ ListDetailHeader,
3380
+ {
3381
+ title: "List Not Found",
3382
+ caption: "The requested lead-gen list is unavailable.",
3383
+ rightSection: backButton
3384
+ }
3385
+ ),
3386
+ /* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsx(Center, { h: 240, children: /* @__PURE__ */ jsx(Alert, { icon: /* @__PURE__ */ jsx(IconAlertCircle, { size: 16 }), color: "gray", variant: "light", children: "The requested list could not be found." }) }) })
3387
+ ] }) }) });
3388
+ }
3389
+ const list = listQuery.data;
3390
+ const progress = progressQuery.data;
3391
+ const executions = executionsQuery.data ?? [];
3392
+ const businessProgress = deriveBusinessProgress(list, progress, actions, executions, leadGenConfig);
3393
+ const currentBuildTemplateLabel = list.metadata.buildPlanSnapshot?.templateLabel ?? "Default lead generation";
3394
+ const buildResolution = resolveBuildState(list, businessProgress, actions, leadGenConfig);
3395
+ const buildSteps = buildResolution.steps;
3396
+ const recommendedBuildStep = buildResolution.recommendedStep;
3397
+ const canUseApiActions = leadGenConfig.canUseLeadGenApiActions;
3398
+ const apiDisabledReason = leadGenConfig.leadGenApiInterface.reason;
3399
+ const canManageLeadGenApi = canManageLeadGen.allowed && canUseApiActions;
3400
+ const runStep = async (step, inputAtSubmit) => {
3401
+ if (!canManageLeadGenApi) return null;
3402
+ const resourceId = step.action?.resourceId;
3403
+ if (!resourceId) return null;
3404
+ try {
3405
+ const result = await executeAsync.mutateAsync({
3406
+ resourceId,
3407
+ resourceType: "workflow",
3408
+ input: inputAtSubmit,
3409
+ listId: list.id
3410
+ });
3411
+ showSuccessNotification(`Workflow run started: ${result.executionId}`);
3412
+ setOptimisticRuns((current) => [
3413
+ {
3414
+ executionId: result.executionId,
3415
+ resourceId,
3416
+ status: result.status || "running",
3417
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
3418
+ completedAt: null,
3419
+ durationMs: null,
3420
+ input: inputAtSubmit
3421
+ },
3422
+ ...current.filter((run) => run.executionId !== result.executionId)
3423
+ ]);
3424
+ queryClient.invalidateQueries({
3425
+ queryKey: acquisitionListKeys.executionsBase(workOSOrganizationId, list.id)
3426
+ });
3427
+ return result;
3428
+ } catch (error2) {
3429
+ showApiErrorNotification(error2);
3430
+ throw error2;
3431
+ }
3432
+ };
3433
+ const handleCopyListCommand = () => {
3434
+ void navigator.clipboard.writeText(`/acquisition --lead-gen list ${list.id}`);
3435
+ showSuccessNotification("Copied list command to clipboard");
3436
+ };
3437
+ const handleDeleteList = () => {
3438
+ if (!canManageLeadGenApi) return;
3439
+ deleteListMutation.mutate(list.id, {
3440
+ onSuccess: () => {
3441
+ setDeleteModalOpen(false);
3442
+ void navigate({ to: "/lead-gen/lists" });
3443
+ }
3444
+ });
3445
+ };
3446
+ return /* @__PURE__ */ jsxs(SubshellContentContainer, { children: [
3447
+ /* @__PURE__ */ jsx(PageContainer, { children: /* @__PURE__ */ jsxs(Stack, { children: [
3448
+ /* @__PURE__ */ jsx(
3449
+ ListDetailHeader,
3450
+ {
3451
+ title: list.name,
3452
+ caption: list.description ?? "Configuration, progress, and execution history for this list"
3453
+ }
3454
+ ),
3455
+ /* @__PURE__ */ jsxs(Group, { justify: "space-between", gap: "xs", wrap: "nowrap", children: [
3456
+ /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
3457
+ /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "filled", color: getStatusColor3(list.status), children: list.status }),
3458
+ /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: "violet", children: currentBuildTemplateLabel }),
3459
+ /* @__PURE__ */ jsxs(Text, { size: "sm", c: "dimmed", children: [
3460
+ "Created ",
3461
+ formatDateTime3(list.createdAt)
3462
+ ] })
3463
+ ] }),
3464
+ /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
3465
+ /* @__PURE__ */ jsxs(Group, { gap: 4, wrap: "nowrap", onClick: handleCopyListCommand, style: { cursor: "pointer" }, children: [
3466
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", ff: "monospace", children: list.id }),
3467
+ /* @__PURE__ */ jsx(ActionIcon, { variant: "subtle", size: "sm", "aria-label": "Copy list command", children: /* @__PURE__ */ jsx(IconCopy, { size: 14 }) })
3468
+ ] }),
3469
+ /* @__PURE__ */ jsx(Tooltip, { label: "Delete list", children: /* @__PURE__ */ jsx(
3470
+ ActionIcon,
3471
+ {
3472
+ variant: "subtle",
3473
+ color: "red",
3474
+ size: "sm",
3475
+ "aria-label": "Delete list",
3476
+ disabled: !canManageLeadGenApi,
3477
+ loading: deleteListMutation.isPending,
3478
+ onClick: () => setDeleteModalOpen(true),
3479
+ children: /* @__PURE__ */ jsx(IconTrash, { size: 14 })
3480
+ }
3481
+ ) }),
3482
+ backButton
3483
+ ] })
3484
+ ] }),
3485
+ /* @__PURE__ */ jsx(LeadGenReadinessAlert, { canUseApiActions, reason: apiDisabledReason }),
3486
+ headerActions ? /* @__PURE__ */ jsx(Group, { gap: "xs", children: headerActions }) : null,
3487
+ /* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsxs(Tabs, { value: activeTab, onChange: handleTabChange, keepMounted: false, children: [
3488
+ /* @__PURE__ */ jsxs(Tabs.List, { children: [
3489
+ /* @__PURE__ */ jsx(Tabs.Tab, { value: "overview", leftSection: /* @__PURE__ */ jsx(IconBuilding, { size: 14 }), children: "Overview" }),
3490
+ /* @__PURE__ */ jsx(Tabs.Tab, { value: "members", leftSection: /* @__PURE__ */ jsx(IconUsers, { size: 14 }), children: "Members" }),
3491
+ /* @__PURE__ */ jsx(Tabs.Tab, { value: "build", leftSection: /* @__PURE__ */ jsx(IconBolt, { size: 14 }), children: "Build" }),
3492
+ /* @__PURE__ */ jsx(Tabs.Tab, { value: "runs", leftSection: /* @__PURE__ */ jsx(IconPlayerPlay, { size: 14 }), children: "Runs" })
3493
+ ] }),
3494
+ tabs ?? null,
3495
+ /* @__PURE__ */ jsx(Tabs.Panel, { value: "overview", pt: "sm", children: /* @__PURE__ */ jsx(
3496
+ OverviewTab,
3497
+ {
3498
+ list,
3499
+ progress: businessProgress,
3500
+ canManage: canManageLeadGenApi,
3501
+ leadGenConfig
3502
+ }
3503
+ ) }),
3504
+ /* @__PURE__ */ jsx(Tabs.Panel, { value: "members", pt: "sm", children: /* @__PURE__ */ jsx(
3505
+ MembersTab,
3506
+ {
3507
+ listId,
3508
+ progress: businessProgress,
3509
+ stageCatalog,
3510
+ onMemberClick: handleMemberClick
3511
+ }
3512
+ ) }),
3513
+ /* @__PURE__ */ jsx(Tabs.Panel, { value: "build", pt: "sm", children: /* @__PURE__ */ jsx(
3514
+ BuildTab,
3515
+ {
3516
+ list,
3517
+ steps: buildSteps,
3518
+ executions,
3519
+ recommendedStep: recommendedBuildStep,
3520
+ optimisticRuns,
3521
+ exportWorkflowId: leadGenConfig.exportWorkflowId,
3522
+ stageCatalog,
3523
+ canManage: canManageLeadGenApi,
3524
+ onRunStep: runStep,
3525
+ onRetryStep: runStep,
3526
+ onViewRunLog: (resourceId, executionId) => {
3527
+ void navigate({
3528
+ to: "/operations/resources/workflow/$workflowId",
3529
+ params: { workflowId: resourceId },
3530
+ search: { exec: executionId }
3531
+ });
3532
+ }
3533
+ }
3534
+ ) }),
3535
+ /* @__PURE__ */ jsx(Tabs.Panel, { value: "runs", pt: "sm", children: /* @__PURE__ */ jsx(
3536
+ WorkflowRunsPanel,
3537
+ {
3538
+ listId,
3539
+ title: "Runs",
3540
+ resourceFilter: runsResourceFilter,
3541
+ onClearResourceFilter: () => setRunsResourceFilter(null)
3542
+ }
3543
+ ) })
3544
+ ] }) })
3545
+ ] }) }),
3546
+ /* @__PURE__ */ jsx(ListMemberDrawer, { memberId, memberKind, listId, onClose: handleDrawerClose }),
3547
+ /* @__PURE__ */ jsx(
3548
+ CustomModal,
3549
+ {
3550
+ opened: deleteModalOpen,
3551
+ onClose: () => !deleteListMutation.isPending && setDeleteModalOpen(false),
3552
+ size: "sm",
3553
+ loading: deleteListMutation.isPending,
3554
+ children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
3555
+ /* @__PURE__ */ jsxs(Group, { gap: "sm", children: [
3556
+ /* @__PURE__ */ jsx(IconTrash, { size: 22, color: "var(--color-error)" }),
3557
+ /* @__PURE__ */ jsx(Title, { order: 4, children: "Delete list" })
3558
+ ] }),
3559
+ /* @__PURE__ */ jsxs(Text, { size: "sm", children: [
3560
+ "Are you sure you want to delete",
3561
+ " ",
3562
+ /* @__PURE__ */ jsx(Text, { span: true, fw: 600, children: list.name }),
3563
+ "?"
3564
+ ] }),
3565
+ /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "This removes the list and its list membership links. Companies and contacts remain in the shared lead-gen database." }),
3566
+ /* @__PURE__ */ jsxs(Group, { justify: "flex-end", mt: "md", children: [
3567
+ /* @__PURE__ */ jsx(Button, { variant: "light", onClick: () => setDeleteModalOpen(false), disabled: deleteListMutation.isPending, children: "Cancel" }),
3568
+ /* @__PURE__ */ jsx(
3569
+ Button,
3570
+ {
3571
+ color: "red",
3572
+ loading: deleteListMutation.isPending,
3573
+ disabled: !canManageLeadGenApi,
3574
+ onClick: handleDeleteList,
3575
+ children: "Delete"
3576
+ }
3577
+ )
3578
+ ] })
3579
+ ] })
3580
+ }
3581
+ ),
3582
+ footer ?? null
3583
+ ] });
3584
+ }
3585
+ function ListBuilderIndexPage() {
3586
+ const navigate = useNavigate();
3587
+ const [query, setQuery] = useState("");
3588
+ const listsQuery = useLists();
3589
+ const lists = listsQuery.data ?? [];
3590
+ const filteredLists = useMemo(() => {
3591
+ const normalized = query.trim().toLowerCase();
3592
+ if (!normalized) return lists;
3593
+ return lists.filter((list) => list.name.toLowerCase().includes(normalized));
3594
+ }, [lists, query]);
3595
+ const openList = (listId) => {
3596
+ navigate({ to: "/lead-gen/lists/$listId", params: { listId }, search: { tab: "build" } });
3597
+ };
3598
+ return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsx(PageContainer, { children: /* @__PURE__ */ jsxs(Stack, { children: [
3599
+ /* @__PURE__ */ jsx(PageTitleCaption, { title: "List Builder", caption: "Choose a list to run lead-gen workflows and monitor progress." }),
3600
+ /* @__PURE__ */ jsx(Paper, { withBorder: true, p: "md", children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
3601
+ /* @__PURE__ */ jsx(
3602
+ TextInput,
3603
+ {
3604
+ placeholder: "Search lists...",
3605
+ leftSection: /* @__PURE__ */ jsx(IconSearch, { size: 16 }),
3606
+ value: query,
3607
+ onChange: (event) => setQuery(event.currentTarget.value)
3608
+ }
3609
+ ),
3610
+ listsQuery.isLoading ? /* @__PURE__ */ jsx(Center, { p: "xl", children: /* @__PURE__ */ jsx(Loader, {}) }) : !filteredLists.length ? /* @__PURE__ */ jsx(
3611
+ EmptyState,
3612
+ {
3613
+ icon: IconLayoutDashboard,
3614
+ title: query.trim() ? "No lists match your search" : "No lists available",
3615
+ description: query.trim() ? void 0 : "Create a list first, then open it in the builder."
3616
+ }
3617
+ ) : /* @__PURE__ */ jsxs(Table, { highlightOnHover: true, children: [
3618
+ /* @__PURE__ */ jsx(Table.Thead, { children: /* @__PURE__ */ jsxs(Table.Tr, { children: [
3619
+ /* @__PURE__ */ jsx(Table.Th, { children: "Name" }),
3620
+ /* @__PURE__ */ jsx(Table.Th, { children: "Status" }),
3621
+ /* @__PURE__ */ jsx(Table.Th, { children: "Batches" }),
3622
+ /* @__PURE__ */ jsx(Table.Th, { children: "Created" })
3623
+ ] }) }),
3624
+ /* @__PURE__ */ jsx(Table.Tbody, { children: filteredLists.map((list) => /* @__PURE__ */ jsxs(
3625
+ Table.Tr,
3626
+ {
3627
+ role: "button",
3628
+ tabIndex: 0,
3629
+ onClick: () => openList(list.id),
3630
+ onKeyDown: (event) => {
3631
+ if (event.key === "Enter" || event.key === " ") {
3632
+ event.preventDefault();
3633
+ openList(list.id);
3634
+ }
3635
+ },
3636
+ style: { cursor: "pointer" },
3637
+ children: [
3638
+ /* @__PURE__ */ jsxs(Table.Td, { children: [
3639
+ /* @__PURE__ */ jsx(Text, { fw: 500, children: list.name }),
3640
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: list.description ?? list.id })
3641
+ ] }),
3642
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: getStateKeyColor(list.status), children: list.status }) }),
3643
+ /* @__PURE__ */ jsx(Table.Td, { children: list.batchIds.length }),
3644
+ /* @__PURE__ */ jsx(Table.Td, { children: formatDate(list.createdAt) })
3645
+ ]
3646
+ },
3647
+ list.id
3648
+ )) })
3649
+ ] })
3650
+ ] }) })
3651
+ ] }) }) });
3652
+ }
3653
+
3654
+ // src/lib/lead-gen/stage-colors.ts
3655
+ var FALLBACK_STAGE_COLORS = {
3656
+ accent: "var(--color-text-dimmed)",
3657
+ background: "color-mix(in srgb, var(--color-text-dimmed) 10%, transparent)",
3658
+ border: "color-mix(in srgb, var(--color-border) 85%, var(--color-text-dimmed))",
3659
+ text: "var(--color-text)"
3660
+ };
3661
+ var STAGE_COLOR_SETS = {
3662
+ scraped: {
3663
+ accent: "var(--color-text-subtle)",
3664
+ background: "color-mix(in srgb, var(--color-text-subtle) 12%, transparent)",
3665
+ border: "color-mix(in srgb, var(--color-border) 80%, var(--color-text-subtle))",
3666
+ text: "var(--color-text)"
3667
+ },
3668
+ populated: {
3669
+ accent: "var(--color-primary)",
3670
+ background: "color-mix(in srgb, var(--color-primary) 12%, transparent)",
3671
+ border: "color-mix(in srgb, var(--color-border) 70%, var(--color-primary))",
3672
+ text: "var(--color-text)"
3673
+ },
3674
+ crawled: {
3675
+ accent: "color-mix(in srgb, var(--color-primary) 70%, var(--color-text-subtle))",
3676
+ background: "color-mix(in srgb, var(--color-primary) 9%, transparent)",
3677
+ border: "color-mix(in srgb, var(--color-border) 72%, var(--color-primary))",
3678
+ text: "var(--color-text)"
3679
+ },
3680
+ extracted: {
3681
+ accent: "color-mix(in srgb, var(--color-primary) 78%, var(--color-success))",
3682
+ background: "color-mix(in srgb, var(--color-primary) 10%, transparent)",
3683
+ border: "color-mix(in srgb, var(--color-border) 65%, var(--color-primary))",
3684
+ text: "var(--color-text)"
3685
+ },
3686
+ enriched: {
3687
+ accent: "color-mix(in srgb, var(--color-success) 70%, var(--color-primary))",
3688
+ background: "color-mix(in srgb, var(--color-success) 12%, transparent)",
3689
+ border: "color-mix(in srgb, var(--color-border) 68%, var(--color-success))",
3690
+ text: "var(--color-text)"
3691
+ },
3692
+ "decision-makers-enriched": {
3693
+ accent: "color-mix(in srgb, var(--color-primary) 66%, var(--color-warning))",
3694
+ background: "color-mix(in srgb, var(--color-primary) 12%, transparent)",
3695
+ border: "color-mix(in srgb, var(--color-border) 66%, var(--color-primary))",
3696
+ text: "var(--color-text)"
3697
+ },
3698
+ discovered: {
3699
+ accent: "color-mix(in srgb, var(--color-primary) 70%, var(--color-warning))",
3700
+ background: "color-mix(in srgb, var(--color-warning) 10%, transparent)",
3701
+ border: "color-mix(in srgb, var(--color-border) 68%, var(--color-warning))",
3702
+ text: "var(--color-text)"
3703
+ },
3704
+ verified: {
3705
+ accent: "var(--color-success)",
3706
+ background: "color-mix(in srgb, var(--color-success) 12%, transparent)",
3707
+ border: "color-mix(in srgb, var(--color-border) 64%, var(--color-success))",
3708
+ text: "var(--color-text)"
3709
+ },
3710
+ qualified: {
3711
+ accent: "color-mix(in srgb, var(--color-success) 82%, var(--color-text))",
3712
+ background: "color-mix(in srgb, var(--color-success) 14%, transparent)",
3713
+ border: "color-mix(in srgb, var(--color-border) 62%, var(--color-success))",
3714
+ text: "var(--color-text)"
3715
+ },
3716
+ personalized: {
3717
+ accent: "color-mix(in srgb, var(--color-primary) 58%, var(--color-success))",
3718
+ background: "color-mix(in srgb, var(--color-primary) 11%, transparent)",
3719
+ border: "color-mix(in srgb, var(--color-border) 66%, var(--color-primary))",
3720
+ text: "var(--color-text)"
3721
+ },
3722
+ uploaded: {
3723
+ accent: "color-mix(in srgb, var(--color-warning) 72%, var(--color-primary))",
3724
+ background: "color-mix(in srgb, var(--color-warning) 12%, transparent)",
3725
+ border: "color-mix(in srgb, var(--color-border) 64%, var(--color-warning))",
3726
+ text: "var(--color-text)"
3727
+ },
3728
+ interested: {
3729
+ accent: "color-mix(in srgb, var(--color-success) 72%, var(--color-warning))",
3730
+ background: "color-mix(in srgb, var(--color-success) 15%, transparent)",
3731
+ border: "color-mix(in srgb, var(--color-border) 58%, var(--color-success))",
3732
+ text: "var(--color-text)"
3733
+ },
3734
+ disqualified: {
3735
+ accent: "var(--color-error)",
3736
+ background: "color-mix(in srgb, var(--color-error) 10%, transparent)",
3737
+ border: "color-mix(in srgb, var(--color-border) 66%, var(--color-error))",
3738
+ text: "var(--color-text)"
3739
+ },
3740
+ invalid: {
3741
+ accent: "color-mix(in srgb, var(--color-error) 72%, var(--color-text-dimmed))",
3742
+ background: "color-mix(in srgb, var(--color-error) 8%, transparent)",
3743
+ border: "color-mix(in srgb, var(--color-border) 78%, var(--color-error))",
3744
+ text: "var(--color-text)"
3745
+ }
3746
+ };
3747
+ function getLeadGenStageColor(stageKey) {
3748
+ return STAGE_COLOR_SETS[stageKey] ?? FALLBACK_STAGE_COLORS;
3749
+ }
3750
+ function getLeadGenStageColorVar(stageKey, tone = "accent") {
3751
+ return getLeadGenStageColor(stageKey)[tone];
3752
+ }
3753
+ var FALLBACK_STAGE_ORDER = 1e4;
3754
+ function unique(values) {
3755
+ return Array.from(new Set(values.filter(Boolean)));
3756
+ }
3757
+ function configuredStages(pipelineConfig, stageCatalog) {
3758
+ return (pipelineConfig?.stages ?? []).filter((stage) => stage.enabled !== false).slice().sort((a, b) => {
3759
+ const aOrder = a.order ?? stageCatalog[a.key]?.order ?? FALLBACK_STAGE_ORDER;
3760
+ const bOrder = b.order ?? stageCatalog[b.key]?.order ?? FALLBACK_STAGE_ORDER;
3761
+ return aOrder - bOrder || a.key.localeCompare(b.key);
3762
+ });
3763
+ }
3764
+ function workflowStageKeys(actions) {
3765
+ return unique((actions ?? []).flatMap((action) => [...action.stagesAffected]));
3766
+ }
3767
+ function sourceFor(stageKey, observedKeys, configKeys, workflowKeys) {
3768
+ if (observedKeys.has(stageKey)) return "activity";
3769
+ if (configKeys.has(stageKey)) return "config";
3770
+ if (workflowKeys.has(stageKey)) return "workflow";
3771
+ return "activity";
3772
+ }
3773
+ function sortStageNodes(nodes) {
3774
+ return nodes.slice().sort((a, b) => a.order - b.order || a.key.localeCompare(b.key));
3775
+ }
3776
+ function buildLaneStages({
3777
+ entity,
3778
+ progress,
3779
+ pipelineConfig,
3780
+ actions,
3781
+ stageCatalog
3782
+ }) {
3783
+ const stageMap = entity === "company" ? progress.byCompanyStage : progress.byContactStage;
3784
+ const observedKeys = new Set(Object.keys(stageMap));
3785
+ const configStages = configuredStages(pipelineConfig, stageCatalog);
3786
+ const configStageByKey = new Map(configStages.map((stage) => [stage.key, stage]));
3787
+ const configKeys = new Set(configStages.map((stage) => stage.key));
3788
+ const workflowKeys = new Set(workflowStageKeys(actions));
3789
+ const stageKeys = unique([...observedKeys, ...configKeys, ...workflowKeys]);
3790
+ return sortStageNodes(
3791
+ stageKeys.flatMap((key) => {
3792
+ const catalogEntry = stageCatalog[key];
3793
+ const inferredEntity = catalogEntry?.entity ?? (observedKeys.has(key) ? entity : null);
3794
+ if (inferredEntity !== entity) return [];
3795
+ return [
3796
+ {
3797
+ key,
3798
+ label: configStageByKey.get(key)?.label ?? catalogEntry?.label ?? key,
3799
+ order: configStageByKey.get(key)?.order ?? catalogEntry?.order ?? FALLBACK_STAGE_ORDER,
3800
+ entity,
3801
+ source: sourceFor(key, observedKeys, configKeys, workflowKeys)
3802
+ }
3803
+ ];
3804
+ })
3805
+ );
3806
+ }
3807
+ function getStageProgress2(progress, stageKey, entity) {
3808
+ const stageProgress = entity === "company" ? progress.byCompanyStage[stageKey] : progress.byContactStage[stageKey];
3809
+ const total = stageProgress?.total ?? (entity === "company" ? progress.totalCompanies : progress.totalMembers);
3810
+ return {
3811
+ total,
3812
+ attempted: stageProgress?.attempted ?? 0,
3813
+ success: stageProgress?.success ?? 0,
3814
+ noResult: stageProgress?.noResult ?? 0,
3815
+ skipped: stageProgress?.skipped ?? 0,
3816
+ error: stageProgress?.error ?? 0,
3817
+ other: stageProgress?.other ?? 0,
3818
+ notAttempted: stageProgress?.notAttempted ?? total
3819
+ };
3820
+ }
3821
+ function percent(value, total) {
3822
+ if (total <= 0) return 0;
3823
+ return Math.max(0, Math.min(100, value / total * 100));
3824
+ }
3825
+ function SourceBadge({ source }) {
3826
+ const label = source === "activity" ? "recorded" : source;
3827
+ const color = source === "activity" ? "green" : source === "config" ? "blue" : "gray";
3828
+ return /* @__PURE__ */ jsx(Badge, { size: "xs", variant: "light", color, children: label });
3829
+ }
3830
+ function ProgressRail({ counts, stageKey }) {
3831
+ const accent = getLeadGenStageColorVar(stageKey);
3832
+ const segments = [
3833
+ { key: "success", value: counts.success, color: accent },
3834
+ { key: "noResult", value: counts.noResult, color: "var(--color-text-subtle)" },
3835
+ { key: "skipped", value: counts.skipped, color: "var(--color-warning)" },
3836
+ { key: "error", value: counts.error, color: "var(--color-error)" },
3837
+ { key: "other", value: counts.other, color: "var(--color-primary)" }
3838
+ ].filter((segment) => segment.value > 0);
3839
+ return /* @__PURE__ */ jsx(
3840
+ Box,
3841
+ {
3842
+ h: 6,
3843
+ bg: "var(--color-surface-hover)",
3844
+ style: {
3845
+ borderRadius: 999,
3846
+ display: "flex",
3847
+ overflow: "hidden",
3848
+ opacity: counts.total > 0 ? 1 : 0.55
3849
+ },
3850
+ children: segments.length > 0 ? segments.map((segment) => /* @__PURE__ */ jsx(Box, { h: "100%", w: `${percent(segment.value, counts.total)}%`, bg: segment.color }, segment.key)) : /* @__PURE__ */ jsx(Box, { h: "100%", w: counts.total > 0 ? `${percent(counts.attempted, counts.total)}%` : "0%", bg: accent })
3851
+ }
3852
+ );
3853
+ }
3854
+ function TimelineLane({
3855
+ title,
3856
+ entity,
3857
+ total,
3858
+ stages,
3859
+ progress,
3860
+ emptyText
3861
+ }) {
3862
+ const Icon = entity === "company" ? IconBuilding : IconUsers;
3863
+ return /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
3864
+ /* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "center", gap: "xs", children: [
3865
+ /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
3866
+ /* @__PURE__ */ jsx(
3867
+ Box,
3868
+ {
3869
+ w: 28,
3870
+ h: 28,
3871
+ style: {
3872
+ alignItems: "center",
3873
+ background: "color-mix(in srgb, var(--color-primary) 14%, transparent)",
3874
+ border: "1px solid color-mix(in srgb, var(--color-border) 75%, var(--color-primary))",
3875
+ borderRadius: 999,
3876
+ color: "var(--color-primary)",
3877
+ display: "flex",
3878
+ justifyContent: "center"
3879
+ },
3880
+ children: /* @__PURE__ */ jsx(Icon, { size: 15 })
3881
+ }
3882
+ ),
3883
+ /* @__PURE__ */ jsxs(Stack, { gap: 0, children: [
3884
+ /* @__PURE__ */ jsx(Text, { size: "sm", fw: 700, children: title }),
3885
+ /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
3886
+ total,
3887
+ " ",
3888
+ entity === "company" ? "companies" : "contacts"
3889
+ ] })
3890
+ ] })
3891
+ ] }),
3892
+ /* @__PURE__ */ jsxs(Badge, { size: "sm", variant: "outline", color: entity === "company" ? "blue" : "teal", children: [
3893
+ stages.length,
3894
+ " stages"
3895
+ ] })
3896
+ ] }),
3897
+ stages.length === 0 ? /* @__PURE__ */ jsx(
3898
+ Box,
3899
+ {
3900
+ p: "sm",
3901
+ style: {
3902
+ border: "1px dashed var(--color-border)",
3903
+ borderRadius: "var(--mantine-radius-md)"
3904
+ },
3905
+ children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: emptyText })
3906
+ }
3907
+ ) : /* @__PURE__ */ jsx(ScrollArea, { type: "hover", offsetScrollbars: true, children: /* @__PURE__ */ jsx(
3908
+ Box,
3909
+ {
3910
+ style: {
3911
+ minWidth: Math.max(560, stages.length * 178),
3912
+ padding: "8px 2px 2px"
3913
+ },
3914
+ children: /* @__PURE__ */ jsx(
3915
+ Box,
3916
+ {
3917
+ style: {
3918
+ alignItems: "start",
3919
+ display: "grid",
3920
+ gap: 0,
3921
+ gridTemplateColumns: `repeat(${stages.length}, minmax(150px, 1fr))`,
3922
+ position: "relative"
3923
+ },
3924
+ children: stages.map((stage, index) => {
3925
+ const counts = getStageProgress2(progress, stage.key, entity);
3926
+ const accent = getLeadGenStageColorVar(stage.key);
3927
+ const completion = Math.round(percent(counts.success, counts.total));
3928
+ const attempted = Math.round(percent(counts.attempted, counts.total));
3929
+ return /* @__PURE__ */ jsxs(Stack, { gap: 8, px: "xs", style: { position: "relative", zIndex: 1 }, children: [
3930
+ /* @__PURE__ */ jsxs(Group, { gap: 8, wrap: "nowrap", children: [
3931
+ /* @__PURE__ */ jsx(
3932
+ Box,
3933
+ {
3934
+ w: 44,
3935
+ h: 44,
3936
+ style: {
3937
+ alignItems: "center",
3938
+ background: getLeadGenStageColorVar(stage.key, "background"),
3939
+ border: `1px solid ${getLeadGenStageColorVar(stage.key, "border")}`,
3940
+ borderRadius: 999,
3941
+ boxShadow: `0 0 0 4px ${getLeadGenStageColorVar(stage.key, "background")}`,
3942
+ color: accent,
3943
+ display: "flex",
3944
+ flexShrink: 0,
3945
+ fontSize: 13,
3946
+ fontWeight: 800,
3947
+ justifyContent: "center"
3948
+ },
3949
+ children: index + 1
3950
+ }
3951
+ ),
3952
+ /* @__PURE__ */ jsxs(Stack, { gap: 1, style: { minWidth: 0 }, children: [
3953
+ /* @__PURE__ */ jsxs(Group, { gap: 6, wrap: "nowrap", children: [
3954
+ /* @__PURE__ */ jsx(Text, { size: "sm", fw: 700, lineClamp: 1, children: stage.label }),
3955
+ /* @__PURE__ */ jsx(SourceBadge, { source: stage.source })
3956
+ ] }),
3957
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", lineClamp: 1, children: counts.total > 0 ? `${counts.attempted} / ${counts.total} attempted` : `Ready for ${entity === "company" ? "company" : "contact"} rows` })
3958
+ ] })
3959
+ ] }),
3960
+ /* @__PURE__ */ jsx(ProgressRail, { counts, stageKey: stage.key }),
3961
+ /* @__PURE__ */ jsxs(Group, { gap: 10, wrap: "nowrap", children: [
3962
+ /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
3963
+ counts.success,
3964
+ " success"
3965
+ ] }),
3966
+ /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
3967
+ attempted,
3968
+ "% attempted"
3969
+ ] }),
3970
+ counts.error > 0 ? /* @__PURE__ */ jsxs(Text, { size: "xs", c: "red", children: [
3971
+ counts.error,
3972
+ " errors"
3973
+ ] }) : null
3974
+ ] }),
3975
+ /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
3976
+ completion,
3977
+ "% complete"
3978
+ ] })
3979
+ ] }, stage.key);
3980
+ })
3981
+ }
3982
+ )
3983
+ }
3984
+ ) })
3985
+ ] });
3986
+ }
3987
+ function PipelineFunnel({ progress, pipelineConfig, actions }) {
3988
+ const { stageCatalog = {} } = useLeadGenConfig();
3989
+ const companyStages = buildLaneStages({ entity: "company", progress, pipelineConfig, actions, stageCatalog });
3990
+ const contactStages = buildLaneStages({ entity: "contact", progress, pipelineConfig, actions, stageCatalog });
3991
+ return /* @__PURE__ */ jsx(Paper, { withBorder: true, p: "md", children: /* @__PURE__ */ jsxs(Stack, { gap: "lg", children: [
3992
+ /* @__PURE__ */ jsx(CardHeader, { icon: /* @__PURE__ */ jsx(IconListDetails, { size: 16 }), title: "Pipeline" }),
3993
+ /* @__PURE__ */ jsx(
3994
+ TimelineLane,
3995
+ {
3996
+ title: "Company pipeline",
3997
+ entity: "company",
3998
+ total: progress.totalCompanies,
3999
+ stages: companyStages,
4000
+ progress,
4001
+ emptyText: "No company stages are configured, runnable, or recorded yet."
4002
+ }
4003
+ ),
4004
+ /* @__PURE__ */ jsx(
4005
+ TimelineLane,
4006
+ {
4007
+ title: "Contact pipeline",
4008
+ entity: "contact",
4009
+ total: progress.totalMembers,
4010
+ stages: contactStages,
4011
+ progress,
4012
+ emptyText: "No contact stages are configured, runnable, or recorded yet."
4013
+ }
4014
+ )
4015
+ ] }) });
4016
+ }
4017
+ function formatDateTime4(value) {
4018
+ if (!value) return "Not yet";
4019
+ return new Date(value).toLocaleString("en-US", {
4020
+ month: "short",
4021
+ day: "numeric",
4022
+ year: "numeric",
4023
+ hour: "numeric",
4024
+ minute: "2-digit"
4025
+ });
4026
+ }
4027
+ function getStatusColor4(status) {
4028
+ switch (status) {
4029
+ case "completed":
4030
+ case "success":
4031
+ case "launched":
4032
+ return "green";
4033
+ case "running":
4034
+ case "pending":
4035
+ case "enriching":
4036
+ return "blue";
4037
+ case "failed":
4038
+ case "error":
4039
+ case "archived":
4040
+ return "red";
4041
+ case "closing":
4042
+ return "yellow";
4043
+ default:
4044
+ return "gray";
4045
+ }
4046
+ }
4047
+ function ListBuilderPage({ listId }) {
4048
+ const navigate = useNavigate();
4049
+ const actions = useListActions();
4050
+ const listQuery = useList(listId);
4051
+ const progressQuery = useListProgress(listId);
4052
+ const executionsQuery = useListExecutions(listId);
4053
+ const isLoading = listQuery.isLoading || progressQuery.isLoading || executionsQuery.isLoading;
4054
+ const error = listQuery.error ?? progressQuery.error ?? executionsQuery.error;
4055
+ const executions = useMemo(() => executionsQuery.data ?? [], [executionsQuery.data]);
4056
+ const openListDetail = () => {
4057
+ void navigate({ to: "/lead-gen/lists/$listId", params: { listId }, search: { tab: "build" } });
4058
+ };
4059
+ const copyListCommand = (id) => {
4060
+ void navigator.clipboard.writeText(`/acquisition --lead-gen list ${id}`);
4061
+ showSuccessNotification("Copied list command to clipboard");
4062
+ };
4063
+ const backButton = /* @__PURE__ */ jsx(
4064
+ Button,
4065
+ {
4066
+ variant: "light",
4067
+ size: "xs",
4068
+ leftSection: /* @__PURE__ */ jsx(IconArrowLeft, { size: 16 }),
4069
+ onClick: () => navigate({ to: "/lead-gen/lists" }),
4070
+ children: "Lists"
4071
+ }
4072
+ );
4073
+ if (isLoading) {
4074
+ return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsx(PageContainer, { children: /* @__PURE__ */ jsxs(Stack, { children: [
4075
+ /* @__PURE__ */ jsx(PageTitleCaption, { title: "List Builder", caption: "Lead-gen workspace", rightSection: backButton }),
4076
+ /* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsx(Center, { p: "xl", children: /* @__PURE__ */ jsx(Loader, {}) }) })
4077
+ ] }) }) });
4078
+ }
4079
+ if (error) {
4080
+ return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsx(PageContainer, { children: /* @__PURE__ */ jsxs(Stack, { children: [
4081
+ /* @__PURE__ */ jsx(PageTitleCaption, { title: "List Builder", caption: "Lead-gen workspace", rightSection: backButton }),
4082
+ /* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsx(CenteredErrorState, { error, title: "Failed to load list builder" }) })
4083
+ ] }) }) });
4084
+ }
4085
+ if (!listQuery.data || !progressQuery.data) {
4086
+ return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsx(PageContainer, { children: /* @__PURE__ */ jsxs(Stack, { children: [
4087
+ /* @__PURE__ */ jsx(PageTitleCaption, { title: "List Not Found", caption: "The requested lead-gen list is unavailable." }),
4088
+ /* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsx(Center, { h: 240, children: /* @__PURE__ */ jsx(Alert, { icon: /* @__PURE__ */ jsx(IconAlertCircle, { size: 16 }), color: "gray", variant: "light", children: "The requested list could not be found." }) }) })
4089
+ ] }) }) });
4090
+ }
4091
+ const list = listQuery.data;
4092
+ const progress = progressQuery.data;
4093
+ return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsx(PageContainer, { children: /* @__PURE__ */ jsxs(Stack, { children: [
4094
+ /* @__PURE__ */ jsx(
4095
+ PageTitleCaption,
4096
+ {
4097
+ title: list.name,
4098
+ caption: list.description ?? "Lead-gen list builder",
4099
+ rightSection: /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
4100
+ /* @__PURE__ */ jsx(Button, { size: "xs", leftSection: /* @__PURE__ */ jsx(IconBolt, { size: 16 }), onClick: openListDetail, children: "Open Build" }),
4101
+ backButton
4102
+ ] })
4103
+ }
4104
+ ),
4105
+ /* @__PURE__ */ jsxs(Group, { justify: "space-between", gap: "xs", children: [
4106
+ /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
4107
+ /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "filled", color: getStatusColor4(list.status), children: list.status }),
4108
+ /* @__PURE__ */ jsxs(Text, { size: "sm", c: "dimmed", children: [
4109
+ "Created ",
4110
+ formatDateTime4(list.createdAt)
4111
+ ] })
4112
+ ] }),
4113
+ /* @__PURE__ */ jsx(Group, { gap: "xs", children: /* @__PURE__ */ jsxs(Group, { gap: 4, wrap: "nowrap", onClick: () => copyListCommand(list.id), style: { cursor: "pointer" }, children: [
4114
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", ff: "monospace", children: list.id }),
4115
+ /* @__PURE__ */ jsx(ActionIcon, { variant: "subtle", size: "sm", "aria-label": "Copy list command", children: /* @__PURE__ */ jsx(IconCopy, { size: 14 }) })
4116
+ ] }) })
4117
+ ] }),
4118
+ /* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 1, sm: 2, lg: 4 }, children: [
4119
+ /* @__PURE__ */ jsx(StatCard, { label: "Companies", value: progress.totalCompanies, icon: IconBuilding }),
4120
+ /* @__PURE__ */ jsx(StatCard, { label: "Contacts", value: progress.totalMembers, icon: IconUsers }),
4121
+ /* @__PURE__ */ jsx(StatCard, { label: "Workflows", value: actions.length, icon: IconBolt }),
4122
+ /* @__PURE__ */ jsx(StatCard, { label: "Runs", value: executions.length, icon: IconPlayerPlay })
4123
+ ] }),
4124
+ /* @__PURE__ */ jsx(PipelineFunnel, { progress, pipelineConfig: list.pipelineConfig, actions }),
4125
+ /* @__PURE__ */ jsx(WorkflowRunsPanel, { listId })
4126
+ ] }) }) });
4127
+ }
4128
+ var PAGE_SIZE_DEFAULT2 = 20;
4129
+ function getValueAtPath(source, path) {
4130
+ return path.split(".").reduce((current, segment) => {
4131
+ if (current == null || typeof current !== "object") return void 0;
4132
+ return current[segment];
4133
+ }, source);
4134
+ }
4135
+ function renderExtraColumnValue(value, column) {
4136
+ if (value === null || value === void 0 || value === "") return "-";
4137
+ if (column.renderType === "count" && Array.isArray(value)) return String(value.length);
4138
+ if (Array.isArray(value)) return value.map(String).join(", ");
4139
+ return String(value);
4140
+ }
4141
+ function LeadGenCompaniesPage({ extraColumns, replaceDefaultColumns } = {}) {
4142
+ const [companySearch, setCompanySearch] = useState("");
4143
+ const [segmentFilter, setSegmentFilter] = useState(null);
4144
+ const [categoryFilter, setCategoryFilter] = useState(null);
4145
+ const [statusFilter, setStatusFilter] = useState(null);
4146
+ const [showBatchDelete, setShowBatchDelete] = useState(false);
4147
+ const navigate = useNavigate();
4148
+ const pagination = usePaginationState(PAGE_SIZE_DEFAULT2, [companySearch, segmentFilter, categoryFilter, statusFilter]);
4149
+ const { data: companyFacets } = useCompanyFacets();
4150
+ const {
4151
+ data: pagedCompanies,
4152
+ isLoading: pagedCompaniesLoading,
4153
+ error: pagedCompaniesError
4154
+ } = useCompanies({
4155
+ search: companySearch || void 0,
4156
+ segment: segmentFilter || void 0,
4157
+ category: categoryFilter || void 0,
4158
+ status: statusFilter || void 0,
4159
+ limit: PAGE_SIZE_DEFAULT2,
4160
+ offset: pagination.offset
4161
+ });
4162
+ const deleteCompaniesMutation = useDeleteCompanies();
4163
+ const companyRows = pagedCompanies?.data ?? [];
4164
+ const selection = useTableSelection(companyRows);
4165
+ useEffect(() => {
4166
+ if ((pagedCompanies?.total ?? 0) === 0) return;
4167
+ if (companyRows.length > 0 || pagination.page === 1) return;
4168
+ pagination.setPage(pagination.page - 1);
4169
+ }, [companyRows.length, pagedCompanies?.total, pagination.page, pagination.setPage]);
4170
+ function handleBatchDelete() {
4171
+ deleteCompaniesMutation.mutate([...selection.selectedIds], {
4172
+ onSuccess: () => {
4173
+ selection.clear();
4174
+ setShowBatchDelete(false);
4175
+ }
4176
+ });
4177
+ }
4178
+ return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsxs(PageContainer, { children: [
4179
+ /* @__PURE__ */ jsx(Stack, { children: /* @__PURE__ */ jsx(PageTitleCaption, { title: "Companies", caption: "Company records and enrichment tracking" }) }),
4180
+ /* @__PURE__ */ jsx(Paper, { children: /* @__PURE__ */ jsxs(Stack, { children: [
4181
+ /* @__PURE__ */ jsxs(
4182
+ FilterBar,
4183
+ {
4184
+ actions: /* @__PURE__ */ jsx(
4185
+ TableSelectionToolbar,
4186
+ {
4187
+ selectedCount: selection.selectedCount,
4188
+ onDelete: () => setShowBatchDelete(true),
4189
+ isDeleting: deleteCompaniesMutation.isPending
4190
+ }
4191
+ ),
4192
+ children: [
4193
+ /* @__PURE__ */ jsx(
4194
+ Select,
4195
+ {
4196
+ placeholder: "All Segments",
4197
+ data: companyFacets?.segments ?? [],
4198
+ value: segmentFilter,
4199
+ onChange: (value) => setSegmentFilter(value),
4200
+ style: { minWidth: 180 },
4201
+ size: "sm",
4202
+ clearable: true
4203
+ }
4204
+ ),
4205
+ /* @__PURE__ */ jsx(
4206
+ Select,
4207
+ {
4208
+ placeholder: "All Categories",
4209
+ data: companyFacets?.categories ?? [],
4210
+ value: categoryFilter,
4211
+ onChange: (value) => setCategoryFilter(value),
4212
+ style: { minWidth: 160 },
4213
+ size: "sm",
4214
+ clearable: true
4215
+ }
4216
+ ),
4217
+ /* @__PURE__ */ jsx(
4218
+ Select,
4219
+ {
4220
+ placeholder: "All Statuses",
4221
+ data: (companyFacets?.statuses ?? []).map((status) => ({
4222
+ value: status,
4223
+ label: status[0].toUpperCase() + status.slice(1)
4224
+ })),
4225
+ value: statusFilter,
4226
+ onChange: (value) => setStatusFilter(value),
4227
+ style: { minWidth: 160 },
4228
+ size: "sm",
4229
+ clearable: true
4230
+ }
4231
+ ),
4232
+ /* @__PURE__ */ jsx(
4233
+ TextInput,
4234
+ {
4235
+ placeholder: "Search by name or domain...",
4236
+ leftSection: /* @__PURE__ */ jsx(IconSearch, { size: 16 }),
4237
+ style: { minWidth: 250 },
4238
+ size: "sm",
4239
+ value: companySearch,
4240
+ onChange: (e) => setCompanySearch(e.target.value)
4241
+ }
4242
+ )
4243
+ ]
4244
+ }
4245
+ ),
4246
+ pagedCompaniesLoading ? /* @__PURE__ */ jsx(Center, { p: "xl", children: /* @__PURE__ */ jsx(Loader, {}) }) : pagedCompaniesError ? /* @__PURE__ */ jsx(CenteredErrorState, { error: pagedCompaniesError, title: "Failed to load companies" }) : !companyRows.length ? /* @__PURE__ */ jsx(EmptyState, { icon: IconBuildingFactory2, title: "No companies yet", description: "Add one to get started." }) : /* @__PURE__ */ jsxs(Table, { children: [
4247
+ /* @__PURE__ */ jsx(Table.Thead, { children: /* @__PURE__ */ jsxs(Table.Tr, { children: [
4248
+ /* @__PURE__ */ jsx(Table.Th, { w: 40, children: /* @__PURE__ */ jsx(
4249
+ Checkbox,
4250
+ {
4251
+ checked: selection.isPageAllSelected,
4252
+ indeterminate: selection.isPagePartiallySelected,
4253
+ onChange: selection.togglePage
4254
+ }
4255
+ ) }),
4256
+ !replaceDefaultColumns ? /* @__PURE__ */ jsxs(Fragment, { children: [
4257
+ /* @__PURE__ */ jsx(Table.Th, { children: "Company" }),
4258
+ /* @__PURE__ */ jsx(Table.Th, { children: "Domain" }),
4259
+ /* @__PURE__ */ jsx(Table.Th, { children: "Segment" }),
4260
+ /* @__PURE__ */ jsx(Table.Th, { children: "Category" }),
4261
+ /* @__PURE__ */ jsx(Table.Th, { children: "Employees" }),
4262
+ /* @__PURE__ */ jsx(Table.Th, { children: "Enrichment" }),
4263
+ /* @__PURE__ */ jsx(Table.Th, { children: "Contacts" })
4264
+ ] }) : null,
4265
+ (extraColumns ?? []).map((column) => /* @__PURE__ */ jsx(Table.Th, { style: column.width ? { width: column.width } : void 0, children: column.label }, column.key))
4266
+ ] }) }),
4267
+ /* @__PURE__ */ jsx(Table.Tbody, { children: companyRows.map((company) => {
4268
+ const enrichStatus = getEnrichmentStatus(company.enrichmentData);
4269
+ return /* @__PURE__ */ jsxs(
4270
+ Table.Tr,
4271
+ {
4272
+ style: { cursor: "pointer" },
4273
+ onClick: () => navigate({ to: "/crm/companies/$companyId", params: { companyId: company.id } }),
4274
+ children: [
4275
+ /* @__PURE__ */ jsx(
4276
+ Table.Td,
4277
+ {
4278
+ onClick: (e) => {
4279
+ e.stopPropagation();
4280
+ selection.toggle(company.id);
4281
+ },
4282
+ children: /* @__PURE__ */ jsx(
4283
+ Checkbox,
4284
+ {
4285
+ checked: selection.isSelected(company.id),
4286
+ onChange: () => selection.toggle(company.id),
4287
+ onClick: (e) => e.stopPropagation()
4288
+ }
4289
+ )
4290
+ }
4291
+ ),
4292
+ !replaceDefaultColumns ? /* @__PURE__ */ jsxs(Fragment, { children: [
4293
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { fw: 500, children: company.name }) }),
4294
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: company.domain || "-" }) }),
4295
+ /* @__PURE__ */ jsx(Table.Td, { children: company.segment || "-" }),
4296
+ /* @__PURE__ */ jsx(Table.Td, { children: company.category || "-" }),
4297
+ /* @__PURE__ */ jsx(Table.Td, { children: company.numEmployees || "-" }),
4298
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Badge, { variant: "light", color: getEnrichmentColor(enrichStatus), size: "sm", children: enrichStatus }) }),
4299
+ /* @__PURE__ */ jsx(Table.Td, { children: company.contactCount })
4300
+ ] }) : null,
4301
+ (extraColumns ?? []).map((column) => /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", children: renderExtraColumnValue(getValueAtPath(company, column.path), column) }) }, column.key))
4302
+ ]
4303
+ },
4304
+ company.id
4305
+ );
4306
+ }) })
4307
+ ] }),
4308
+ (pagedCompanies?.total ?? 0) > PAGE_SIZE_DEFAULT2 ? /* @__PURE__ */ jsx(Group, { justify: "center", children: /* @__PURE__ */ jsx(
4309
+ Pagination,
4310
+ {
4311
+ value: pagination.page,
4312
+ onChange: pagination.setPage,
4313
+ total: pagination.totalPages(pagedCompanies?.total ?? 0),
4314
+ size: "sm"
4315
+ }
4316
+ ) }) : null
4317
+ ] }) }),
4318
+ /* @__PURE__ */ jsx(CustomModal, { opened: showBatchDelete, onClose: () => setShowBatchDelete(false), size: "sm", children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
4319
+ /* @__PURE__ */ jsxs(Group, { gap: "sm", children: [
4320
+ /* @__PURE__ */ jsx(IconAlertTriangle, { size: 20, color: "var(--mantine-color-red-6)" }),
4321
+ /* @__PURE__ */ jsxs(Title, { order: 5, children: [
4322
+ "Delete ",
4323
+ selection.selectedCount,
4324
+ " companies?"
4325
+ ] })
4326
+ ] }),
4327
+ /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "This will permanently delete the selected companies and cannot be undone." }),
4328
+ /* @__PURE__ */ jsxs(Group, { justify: "flex-end", gap: "sm", children: [
4329
+ /* @__PURE__ */ jsx(Button, { variant: "default", size: "sm", onClick: () => setShowBatchDelete(false), children: "Cancel" }),
4330
+ /* @__PURE__ */ jsx(Button, { color: "red", size: "sm", loading: deleteCompaniesMutation.isPending, onClick: handleBatchDelete, children: "Delete" })
4331
+ ] })
4332
+ ] }) })
4333
+ ] }) });
4334
+ }
4335
+ var PAGE_SIZE_DEFAULT3 = 20;
4336
+ function getValueAtPath2(source, path) {
4337
+ return path.split(".").reduce((current, segment) => {
4338
+ if (current == null || typeof current !== "object") return void 0;
4339
+ return current[segment];
4340
+ }, source);
4341
+ }
4342
+ function renderExtraColumnValue2(value, column) {
4343
+ if (value === null || value === void 0 || value === "") return "-";
4344
+ if (column.renderType === "count" && Array.isArray(value)) return String(value.length);
4345
+ if (Array.isArray(value)) return value.map(String).join(", ");
4346
+ return String(value);
4347
+ }
4348
+ function LeadGenContactsPage({ extraColumns, replaceDefaultColumns } = {}) {
4349
+ const [contactSearch, setContactSearch] = useState("");
4350
+ const [statusFilter, setStatusFilter] = useState(null);
4351
+ const [listFilter, setListFilter] = useState(null);
4352
+ const [showBatchDelete, setShowBatchDelete] = useState(false);
4353
+ const navigate = useNavigate();
4354
+ const pagination = usePaginationState(PAGE_SIZE_DEFAULT3, [contactSearch, statusFilter, listFilter]);
4355
+ const { data: lists } = useLists();
4356
+ const {
4357
+ data: contacts,
4358
+ isLoading: contactsLoading,
4359
+ error: contactsError
4360
+ } = useContacts({
4361
+ search: contactSearch || void 0,
4362
+ contactStatus: statusFilter || void 0,
4363
+ listId: listFilter || void 0,
4364
+ limit: PAGE_SIZE_DEFAULT3,
4365
+ offset: pagination.offset
4366
+ });
4367
+ const deleteContactsMutation = useDeleteContacts();
4368
+ const contactRows = contacts?.data ?? [];
4369
+ const selection = useTableSelection(contactRows);
4370
+ useEffect(() => {
4371
+ if ((contacts?.total ?? 0) === 0) return;
4372
+ if (contactRows.length > 0 || pagination.page === 1) return;
4373
+ pagination.setPage(pagination.page - 1);
4374
+ }, [contactRows.length, contacts?.total, pagination.page, pagination.setPage]);
4375
+ function handleBatchDelete() {
4376
+ deleteContactsMutation.mutate([...selection.selectedIds], {
4377
+ onSuccess: () => {
4378
+ selection.clear();
4379
+ setShowBatchDelete(false);
4380
+ }
4381
+ });
4382
+ }
4383
+ return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsxs(PageContainer, { children: [
4384
+ /* @__PURE__ */ jsx(Stack, { children: /* @__PURE__ */ jsx(PageTitleCaption, { title: "Contacts", caption: "Contact records and outreach tracking" }) }),
4385
+ /* @__PURE__ */ jsx(Paper, { children: /* @__PURE__ */ jsxs(Stack, { children: [
4386
+ /* @__PURE__ */ jsxs(
4387
+ FilterBar,
4388
+ {
4389
+ actions: /* @__PURE__ */ jsx(
4390
+ TableSelectionToolbar,
4391
+ {
4392
+ selectedCount: selection.selectedCount,
4393
+ onDelete: () => setShowBatchDelete(true),
4394
+ isDeleting: deleteContactsMutation.isPending
4395
+ }
4396
+ ),
4397
+ children: [
4398
+ /* @__PURE__ */ jsx(
4399
+ Select,
4400
+ {
4401
+ placeholder: "All Statuses",
4402
+ data: [
4403
+ { value: "active", label: "Active" },
4404
+ { value: "invalid", label: "Invalid" }
4405
+ ],
4406
+ value: statusFilter,
4407
+ onChange: (value) => setStatusFilter(value),
4408
+ style: { minWidth: 160 },
4409
+ size: "sm",
4410
+ clearable: true
4411
+ }
4412
+ ),
4413
+ /* @__PURE__ */ jsx(
4414
+ Select,
4415
+ {
4416
+ placeholder: "All Lists",
4417
+ data: (lists ?? []).map((list) => ({ value: list.id, label: list.name })),
4418
+ value: listFilter,
4419
+ onChange: (value) => setListFilter(value),
4420
+ style: { minWidth: 200 },
4421
+ size: "sm",
4422
+ clearable: true
4423
+ }
4424
+ ),
4425
+ /* @__PURE__ */ jsx(
4426
+ TextInput,
4427
+ {
4428
+ placeholder: "Search by name or email...",
4429
+ leftSection: /* @__PURE__ */ jsx(IconSearch, { size: 16 }),
4430
+ style: { minWidth: 250 },
4431
+ size: "sm",
4432
+ value: contactSearch,
4433
+ onChange: (e) => setContactSearch(e.target.value)
4434
+ }
4435
+ )
4436
+ ]
4437
+ }
4438
+ ),
4439
+ contactsLoading ? /* @__PURE__ */ jsx(Center, { p: "xl", children: /* @__PURE__ */ jsx(Loader, {}) }) : contactsError ? /* @__PURE__ */ jsx(CenteredErrorState, { error: contactsError, title: "Failed to load contacts" }) : !contactRows.length ? /* @__PURE__ */ jsx(EmptyState, { icon: IconUsers, title: "No contacts yet", description: "Add one to get started." }) : /* @__PURE__ */ jsxs(Table, { children: [
4440
+ /* @__PURE__ */ jsx(Table.Thead, { children: /* @__PURE__ */ jsxs(Table.Tr, { children: [
4441
+ /* @__PURE__ */ jsx(Table.Th, { w: 40, children: /* @__PURE__ */ jsx(
4442
+ Checkbox,
4443
+ {
4444
+ checked: selection.isPageAllSelected,
4445
+ indeterminate: selection.isPagePartiallySelected,
4446
+ onChange: selection.togglePage
4447
+ }
4448
+ ) }),
4449
+ !replaceDefaultColumns ? /* @__PURE__ */ jsxs(Fragment, { children: [
4450
+ /* @__PURE__ */ jsx(Table.Th, { children: "Name" }),
4451
+ /* @__PURE__ */ jsx(Table.Th, { children: "Email" }),
4452
+ /* @__PURE__ */ jsx(Table.Th, { children: "Company" }),
4453
+ /* @__PURE__ */ jsx(Table.Th, { children: "Title" }),
4454
+ /* @__PURE__ */ jsx(Table.Th, { children: "Status" }),
4455
+ /* @__PURE__ */ jsx(Table.Th, { children: "Personalized" })
4456
+ ] }) : null,
4457
+ (extraColumns ?? []).map((column) => /* @__PURE__ */ jsx(Table.Th, { style: column.width ? { width: column.width } : void 0, children: column.label }, column.key))
4458
+ ] }) }),
4459
+ /* @__PURE__ */ jsx(Table.Tbody, { children: contactRows.map((contact) => {
4460
+ const fullName = [contact.firstName, contact.lastName].filter(Boolean).join(" ") || contact.email;
4461
+ const hasPersonalization = !!contact.openingLine;
4462
+ return /* @__PURE__ */ jsxs(
4463
+ Table.Tr,
4464
+ {
4465
+ style: { cursor: "pointer" },
4466
+ onClick: () => navigate({ to: "/crm/contacts/$contactId", params: { contactId: contact.id } }),
4467
+ children: [
4468
+ /* @__PURE__ */ jsx(
4469
+ Table.Td,
4470
+ {
4471
+ onClick: (e) => {
4472
+ e.stopPropagation();
4473
+ selection.toggle(contact.id);
4474
+ },
4475
+ children: /* @__PURE__ */ jsx(
4476
+ Checkbox,
4477
+ {
4478
+ checked: selection.isSelected(contact.id),
4479
+ onChange: () => selection.toggle(contact.id),
4480
+ onClick: (e) => e.stopPropagation()
4481
+ }
4482
+ )
4483
+ }
4484
+ ),
4485
+ !replaceDefaultColumns ? /* @__PURE__ */ jsxs(Fragment, { children: [
4486
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { fw: 500, children: fullName }) }),
4487
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: contact.email }) }),
4488
+ /* @__PURE__ */ jsx(Table.Td, { children: contact.company?.name || "-" }),
4489
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: contact.title || "-" }) }),
4490
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Badge, { variant: "light", color: getStatusColor(contact.status), size: "sm", children: contact.status || "unknown" }) }),
4491
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Badge, { variant: "light", color: hasPersonalization ? "green" : "gray", size: "sm", children: hasPersonalization ? "Yes" : "No" }) })
4492
+ ] }) : null,
4493
+ (extraColumns ?? []).map((column) => /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", children: renderExtraColumnValue2(getValueAtPath2(contact, column.path), column) }) }, column.key))
4494
+ ]
4495
+ },
4496
+ contact.id
4497
+ );
4498
+ }) })
4499
+ ] }),
4500
+ (contacts?.total ?? 0) > PAGE_SIZE_DEFAULT3 ? /* @__PURE__ */ jsx(Group, { justify: "center", children: /* @__PURE__ */ jsx(
4501
+ Pagination,
4502
+ {
4503
+ value: pagination.page,
4504
+ onChange: pagination.setPage,
4505
+ total: pagination.totalPages(contacts?.total ?? 0),
4506
+ size: "sm"
4507
+ }
4508
+ ) }) : null
4509
+ ] }) }),
4510
+ /* @__PURE__ */ jsx(CustomModal, { opened: showBatchDelete, onClose: () => setShowBatchDelete(false), size: "sm", children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
4511
+ /* @__PURE__ */ jsxs(Group, { gap: "sm", children: [
4512
+ /* @__PURE__ */ jsx(IconAlertTriangle, { size: 20, color: "var(--mantine-color-red-6)" }),
4513
+ /* @__PURE__ */ jsxs(Title, { order: 5, children: [
4514
+ "Delete ",
4515
+ selection.selectedCount,
4516
+ " contacts?"
4517
+ ] })
4518
+ ] }),
4519
+ /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "This will permanently delete the selected contacts and cannot be undone." }),
4520
+ /* @__PURE__ */ jsxs(Group, { justify: "flex-end", gap: "sm", children: [
4521
+ /* @__PURE__ */ jsx(Button, { variant: "default", size: "sm", onClick: () => setShowBatchDelete(false), children: "Cancel" }),
4522
+ /* @__PURE__ */ jsx(Button, { color: "red", size: "sm", loading: deleteContactsMutation.isPending, onClick: handleBatchDelete, children: "Delete" })
4523
+ ] })
4524
+ ] }) })
4525
+ ] }) });
4526
+ }
4527
+
4528
+ export { LEAD_GEN_ROUTE_LINKS, LeadGenCompaniesPage, LeadGenContactsPage, LeadGenListDetailPage, LeadGenListsPage, LeadGenOverviewPage, LeadGenReadinessAlert, LeadGenRouteShell, LeadGenSidebar, LeadGenSidebarTop, ListBuilderIndexPage, ListBuilderPage, ORPHAN_STAGE_ORDER, StepConfigForm, TabSection, deriveBusinessProgress, formatDate, getEnrichmentColor, getEnrichmentStatus, getLeadGenApiInterfaceReadiness, getLeadGenExportWorkflowId, getStateKeyColor, getStatusColor, getStepActionLabel, isLeadGenExportAction, resolveBuildPlanSteps, resolveBuildState, sortStageKeys, useDeleteLists, useLeadGenConfig };