@elevasis/ui 2.25.6 → 2.26.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 (105) hide show
  1. package/dist/api/index.js +2 -2
  2. package/dist/app/index.css +15 -5
  3. package/dist/app/index.d.ts +61 -14
  4. package/dist/app/index.js +6 -6
  5. package/dist/charts/index.js +6 -5
  6. package/dist/chunk-3MEXPLWT.js +265 -0
  7. package/dist/{chunk-BDKM56TP.js → chunk-4KTLOK7K.js} +1 -1
  8. package/dist/{chunk-KMAXFJPH.js → chunk-CW3UNAF2.js} +5 -409
  9. package/dist/{chunk-HKBEURCV.js → chunk-G26INIF3.js} +1 -1
  10. package/dist/{chunk-7F3IQMLI.js → chunk-G66QFZXD.js} +11 -214
  11. package/dist/{chunk-QIW6OCEI.js → chunk-HLFFKKT3.js} +27 -373
  12. package/dist/chunk-JDNEWB5F.js +10 -0
  13. package/dist/{chunk-L7D6KNHV.js → chunk-JKBGDFX2.js} +890 -749
  14. package/dist/{chunk-YRKQNPK2.js → chunk-JPGX3533.js} +4 -3
  15. package/dist/chunk-KCGGA36K.js +73 -0
  16. package/dist/chunk-KEFWANZY.js +155 -0
  17. package/dist/chunk-LH4GPYDX.js +448 -0
  18. package/dist/{chunk-JXSBOG2R.js → chunk-LWKZ3BCC.js} +5 -4
  19. package/dist/chunk-OGXKOMUT.js +412 -0
  20. package/dist/chunk-OHXU5WWK.js +3731 -0
  21. package/dist/chunk-ONFKASZI.js +2004 -0
  22. package/dist/{chunk-U36X6NZM.js → chunk-RIFTUOPE.js} +2 -14
  23. package/dist/{chunk-T6INEVX6.js → chunk-SGS4CQ2B.js} +1 -1
  24. package/dist/{chunk-C7IBFI5B.js → chunk-UPMX5GJI.js} +5 -5
  25. package/dist/{chunk-ARJPZ66V.js → chunk-UY5I2KOZ.js} +208 -3124
  26. package/dist/chunk-W2ZTLH7Y.js +662 -0
  27. package/dist/{chunk-KNISO652.js → chunk-WUVR4QY6.js} +9 -9
  28. package/dist/{chunk-Q5BEODAT.js → chunk-X2SUMO3P.js} +2 -1
  29. package/dist/{chunk-SNHGSCKH.js → chunk-XBMCDGHA.js} +1 -1
  30. package/dist/{chunk-N55DVMAG.js → chunk-XQQEKWTL.js} +2 -6
  31. package/dist/{chunk-SBQ4MYQV.js → chunk-XZSEPJZQ.js} +5 -6
  32. package/dist/{chunk-CPAJXBTL.js → chunk-YHBPR67D.js} +490 -676
  33. package/dist/{chunk-QARSVM7Q.js → chunk-YO2YORW4.js} +4 -4
  34. package/dist/{chunk-TAIX4NO3.js → chunk-ZFLM2YVW.js} +2 -2
  35. package/dist/components/index.css +15 -5
  36. package/dist/components/index.d.ts +202 -383
  37. package/dist/components/index.js +43 -429
  38. package/dist/components/navigation/index.css +25 -15
  39. package/dist/execution/index.d.ts +0 -73
  40. package/dist/features/auth/index.css +25 -15
  41. package/dist/features/crm/index.css +25 -15
  42. package/dist/features/crm/index.d.ts +49 -49
  43. package/dist/features/crm/index.js +14 -15
  44. package/dist/features/dashboard/index.css +25 -15
  45. package/dist/features/dashboard/index.js +18 -16
  46. package/dist/features/delivery/index.css +15 -5
  47. package/dist/features/delivery/index.js +14 -15
  48. package/dist/features/knowledge/index.css +611 -0
  49. package/dist/features/knowledge/index.js +375 -72
  50. package/dist/features/lead-gen/index.css +25 -15
  51. package/dist/features/lead-gen/index.d.ts +60 -21
  52. package/dist/features/lead-gen/index.js +16 -16
  53. package/dist/features/monitoring/index.css +15 -5
  54. package/dist/features/monitoring/index.js +17 -17
  55. package/dist/features/monitoring/requests/index.css +25 -15
  56. package/dist/features/monitoring/requests/index.js +13 -14
  57. package/dist/features/operations/index.css +25 -15
  58. package/dist/features/operations/index.d.ts +16 -98
  59. package/dist/features/operations/index.js +26 -22
  60. package/dist/features/settings/index.css +25 -15
  61. package/dist/features/settings/index.d.ts +1 -0
  62. package/dist/features/settings/index.js +15 -16
  63. package/dist/hooks/delivery/index.css +25 -15
  64. package/dist/hooks/delivery/index.js +2 -2
  65. package/dist/hooks/index.css +15 -5
  66. package/dist/hooks/index.d.ts +172 -380
  67. package/dist/hooks/index.js +13 -14
  68. package/dist/hooks/published.css +15 -5
  69. package/dist/hooks/published.d.ts +172 -380
  70. package/dist/hooks/published.js +13 -14
  71. package/dist/index.css +15 -5
  72. package/dist/index.d.ts +988 -403
  73. package/dist/index.js +15 -15
  74. package/dist/initialization/index.d.ts +1 -0
  75. package/dist/knowledge/index.d.ts +981 -41
  76. package/dist/knowledge/index.js +5449 -294
  77. package/dist/layout/index.d.ts +2 -0
  78. package/dist/layout/index.js +3 -2
  79. package/dist/organization/index.css +25 -15
  80. package/dist/organization/index.d.ts +1 -0
  81. package/dist/provider/index.css +25 -15
  82. package/dist/provider/index.d.ts +818 -26
  83. package/dist/provider/index.js +11 -11
  84. package/dist/provider/published.css +25 -15
  85. package/dist/provider/published.d.ts +817 -25
  86. package/dist/provider/published.js +8 -9
  87. package/dist/test-utils/index.js +2 -2
  88. package/dist/test-utils/setup.js +1 -1
  89. package/dist/theme/index.js +3 -2
  90. package/dist/theme/presets/index.d.ts +97 -0
  91. package/dist/theme/presets/index.js +3 -0
  92. package/dist/types/index.d.ts +71 -126
  93. package/dist/utils/index.js +1 -1
  94. package/dist/vite/index.d.ts +7 -0
  95. package/dist/vite/index.js +10 -0
  96. package/dist/vite-plugin-knowledge/index.d.ts +1 -33
  97. package/dist/vite-plugin-knowledge/index.js +1 -66
  98. package/package.json +16 -3
  99. package/src/knowledge/README.md +8 -8
  100. package/src/theme/presets/README.md +19 -0
  101. package/dist/chunk-5RLYII6P.js +0 -314
  102. package/dist/chunk-6U7AIIHF.js +0 -880
  103. package/dist/chunk-HAEJ4M54.js +0 -94
  104. package/dist/chunk-LPM7O6XM.js +0 -293
  105. /package/dist/{chunk-SGXXJE52.js → chunk-QD4X4H5A.js} +0 -0
@@ -1,25 +1,26 @@
1
- import { DEFAULT_ORGANIZATION_MODEL_PROSPECTING } from './chunk-BDKM56TP.js';
1
+ import { DEFAULT_ORGANIZATION_MODEL_PROSPECTING } from './chunk-4KTLOK7K.js';
2
+ import { sanitizeInput } from './chunk-3MEXPLWT.js';
2
3
  import { PageContainer } from './chunk-BZZCNLT6.js';
3
4
  import { TableSelectionToolbar, SortableHeader } from './chunk-TUMSNGTX.js';
4
5
  import { SubshellNavItem } from './chunk-X4WBGKJQ.js';
5
6
  import { SubshellSidebarSection } from './chunk-IIMU5YAJ.js';
6
7
  import { FilterBar } from './chunk-PDHTXPSF.js';
7
8
  import { CustomModal } from './chunk-KVJ3LFH2.js';
8
- import { acquisitionListKeys, useListsTelemetry, useLists, useCreateList, useTableSort, sortData, usePaginationState, useTableSelection, useWorkflowExecution, useList, useListProgress, useListExecutions, useDeleteList, useUpdateList, useUpdateListConfig, useCompanyFacets, useCompanies, useDeleteCompanies, useContacts, useDeleteContacts, useListMembers, useListMember, useTransitionListMember, useDeriveActions, useArtifacts } from './chunk-CPAJXBTL.js';
9
- import { showApiErrorNotification, showSuccessNotification } from './chunk-HKBEURCV.js';
9
+ import { acquisitionListKeys, useListsTelemetry, useLists, useCreateList, useTableSort, sortData, usePaginationState, useTableSelection, useWorkflowExecution, useList, useListProgress, useListExecutions, useDeleteList, useCompanyFacets, useCompanies, useDeleteCompanies, useContacts, useDeleteContacts, useListMembers, useListMember, useTransitionListMember, useDeriveActions, useArtifacts } from './chunk-YHBPR67D.js';
10
+ import { showApiErrorNotification, showSuccessNotification } from './chunk-G26INIF3.js';
11
+ import { PageTitleCaption, CenteredErrorState, StatCard, CardHeader, EmptyState, JsonViewer } from './chunk-RIFTUOPE.js';
12
+ import { useListActions, LEAD_GEN_STAGE_CATALOG, LEAD_GEN_PIPELINE_DEFINITIONS, findPipeline } from './chunk-W2ZTLH7Y.js';
10
13
  import { SubshellContentContainer } from './chunk-TKAYX2SP.js';
11
- import { PageTitleCaption, CenteredErrorState, StatCard, CardHeader, EmptyState, JsonViewer } from './chunk-U36X6NZM.js';
12
- import { useListActions } from './chunk-LPM7O6XM.js';
13
- import { LEAD_GEN_STAGE_CATALOG, LEAD_GEN_PIPELINE_DEFINITIONS, findPipeline } from './chunk-5RLYII6P.js';
14
14
  import { useRouterContext } from './chunk-Q7DJKLEN.js';
15
15
  import { useElevasisServices } from './chunk-5WWZXCS5.js';
16
- import { Stack, Group, Title, Text, Paper, Anchor, ActionIcon, Divider, Box, SimpleGrid, Badge, Card, Center, Loader, Alert, Table, Button, TextInput, Select, Checkbox, Pagination, Textarea, NumberInput, Switch, Tooltip, Tabs, ThemeIcon, Collapse, SegmentedControl, UnstyledButton, Drawer, ScrollArea } from '@mantine/core';
17
- import { IconLayoutGrid, IconList, IconBuilding, IconAddressBook, IconTarget, IconExternalLink, IconX, IconAlertCircle, IconPlayerPlay, IconArrowRight, IconSparkles, IconListDetails, IconPlus, IconSearch, IconAlertTriangle, IconLayoutDashboard, IconBolt, IconCopy, IconUsers, IconSwitchHorizontal, IconTrash, IconBuildingFactory2, IconArrowLeft, IconChevronDown, IconChevronRight, IconRefresh, IconMail, IconUser, IconDatabase } from '@tabler/icons-react';
16
+ import { Stack, Group, Title, Text, Alert, Button, Collapse, Paper, Anchor, ActionIcon, Divider, Box, SimpleGrid, Badge, Card, Center, Loader, Table, TextInput, Select, Checkbox, Pagination, Textarea, Tooltip, Tabs, ThemeIcon, Pill, SegmentedControl, UnstyledButton, Drawer, JsonInput, Switch, NumberInput, MultiSelect, TagsInput, ScrollArea } from '@mantine/core';
17
+ import { IconLayoutGrid, IconList, IconBuilding, IconAddressBook, IconTarget, IconExternalLink, IconX, IconAlertCircle, IconPlayerPlay, IconArrowRight, IconSparkles, IconListDetails, IconPlus, IconSearch, IconAlertTriangle, IconLayoutDashboard, IconBolt, IconCopy, IconUsers, IconTrash, IconBuildingFactory2, IconArrowLeft, IconChevronDown, IconChevronRight, IconRefresh, IconMail, IconUser, IconDatabase } from '@tabler/icons-react';
18
18
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
19
19
  import { Link, useNavigate, useSearch } from '@tanstack/react-router';
20
- import { useQueryClient, useMutation } from '@tanstack/react-query';
21
- import { useState, useMemo, useEffect, Fragment as Fragment$1 } from 'react';
20
+ import { useState, useRef, useMemo, useEffect, Fragment as Fragment$1 } from 'react';
22
21
  import { useForm } from '@mantine/form';
22
+ import { zod4Resolver } from 'mantine-form-zod-resolver';
23
+ import { useQueryClient, useMutation } from '@tanstack/react-query';
23
24
 
24
25
  var LeadGenSidebarTop = () => {
25
26
  return /* @__PURE__ */ jsx(SubshellSidebarSection, { icon: IconTarget, label: "Lead Gen" });
@@ -110,6 +111,283 @@ function TabSection({ icon, title, description, rightSection, children }) {
110
111
  children
111
112
  ] });
112
113
  }
114
+ function renderField(field, form, disabled, jsonState, setJsonState) {
115
+ const inputProps = form.getInputProps(field.path);
116
+ const label = field.label;
117
+ const description = field.description;
118
+ const placeholder = field.placeholder;
119
+ switch (field.component) {
120
+ case "segmented": {
121
+ const options = field.options ?? [];
122
+ return /* @__PURE__ */ jsx(
123
+ SegmentedControl,
124
+ {
125
+ fullWidth: true,
126
+ disabled,
127
+ data: options,
128
+ value: inputProps.value ?? options[0]?.value ?? "",
129
+ onChange: (val) => {
130
+ form.setFieldValue(field.path, val);
131
+ }
132
+ },
133
+ field.path
134
+ );
135
+ }
136
+ case "tags":
137
+ return /* @__PURE__ */ jsx(
138
+ TagsInput,
139
+ {
140
+ label,
141
+ description,
142
+ placeholder,
143
+ disabled,
144
+ value: inputProps.value ?? [],
145
+ onChange: (val) => form.setFieldValue(field.path, val),
146
+ error: inputProps.error,
147
+ clearable: true
148
+ },
149
+ field.path
150
+ );
151
+ case "multiselect":
152
+ return /* @__PURE__ */ jsx(
153
+ MultiSelect,
154
+ {
155
+ label,
156
+ description,
157
+ placeholder,
158
+ disabled,
159
+ data: field.options ?? [],
160
+ value: inputProps.value ?? [],
161
+ onChange: (val) => form.setFieldValue(field.path, val),
162
+ error: inputProps.error,
163
+ clearable: true
164
+ },
165
+ field.path
166
+ );
167
+ case "select":
168
+ return /* @__PURE__ */ jsx(
169
+ Select,
170
+ {
171
+ label,
172
+ description,
173
+ placeholder,
174
+ disabled,
175
+ data: field.options ?? [],
176
+ ...inputProps
177
+ },
178
+ field.path
179
+ );
180
+ case "textinput":
181
+ return /* @__PURE__ */ jsx(
182
+ TextInput,
183
+ {
184
+ label,
185
+ description,
186
+ placeholder,
187
+ disabled,
188
+ ...inputProps
189
+ },
190
+ field.path
191
+ );
192
+ case "textarea":
193
+ return /* @__PURE__ */ jsx(
194
+ Textarea,
195
+ {
196
+ label,
197
+ description,
198
+ placeholder,
199
+ disabled,
200
+ autosize: true,
201
+ minRows: 3,
202
+ ...inputProps
203
+ },
204
+ field.path
205
+ );
206
+ case "numberinput":
207
+ return /* @__PURE__ */ jsx(
208
+ NumberInput,
209
+ {
210
+ label,
211
+ description,
212
+ placeholder,
213
+ disabled,
214
+ min: field.min,
215
+ max: field.max,
216
+ step: field.step,
217
+ allowDecimal: false,
218
+ value: inputProps.value ?? 0,
219
+ onChange: (val) => form.setFieldValue(field.path, typeof val === "number" ? val : 0),
220
+ error: inputProps.error
221
+ },
222
+ field.path
223
+ );
224
+ case "switch":
225
+ return /* @__PURE__ */ jsx(
226
+ Switch,
227
+ {
228
+ label,
229
+ description,
230
+ disabled,
231
+ checked: inputProps.value ?? false,
232
+ onChange: (e) => form.setFieldValue(field.path, e.currentTarget.checked),
233
+ error: inputProps.error
234
+ },
235
+ field.path
236
+ );
237
+ case "json": {
238
+ const path = field.path;
239
+ const state = jsonState[path];
240
+ const rawValue = state?.raw ?? (() => {
241
+ const current = inputProps.value;
242
+ if (current === void 0 || current === null) return "";
243
+ if (typeof current === "string") return current;
244
+ try {
245
+ return JSON.stringify(current, null, 2);
246
+ } catch {
247
+ return "";
248
+ }
249
+ })();
250
+ return /* @__PURE__ */ jsx(
251
+ JsonInput,
252
+ {
253
+ label,
254
+ description,
255
+ placeholder,
256
+ disabled,
257
+ value: rawValue,
258
+ onChange: (next) => {
259
+ if (!next.trim()) {
260
+ setJsonState(path, { raw: next, invalid: false });
261
+ form.setFieldValue(path, void 0);
262
+ return;
263
+ }
264
+ try {
265
+ const parsed = JSON.parse(next);
266
+ setJsonState(path, { raw: next, invalid: false });
267
+ form.setFieldValue(path, parsed);
268
+ } catch {
269
+ setJsonState(path, { raw: next, invalid: true });
270
+ form.setFieldError(path, "Invalid JSON");
271
+ }
272
+ },
273
+ error: state?.invalid ? "Invalid JSON" : inputProps.error,
274
+ autosize: true,
275
+ minRows: 3,
276
+ formatOnBlur: true
277
+ },
278
+ path
279
+ );
280
+ }
281
+ default:
282
+ return null;
283
+ }
284
+ }
285
+ function renderSection(section, values, form, disabled, jsonState, setJsonState) {
286
+ if (section.when && !section.when(values)) return null;
287
+ const visibleFields = section.fields.filter((f) => !f.when || f.when(values));
288
+ return /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
289
+ section.title && /* @__PURE__ */ jsxs(Box, { children: [
290
+ /* @__PURE__ */ jsx(Title, { order: 5, children: section.title }),
291
+ section.description && /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: section.description })
292
+ ] }),
293
+ visibleFields.map((field) => renderField(field, form, disabled, jsonState, setJsonState))
294
+ ] }, section.id);
295
+ }
296
+ function StepConfigForm({
297
+ schema,
298
+ layout,
299
+ value,
300
+ onChange,
301
+ onAction,
302
+ actions,
303
+ disabled = false,
304
+ slot = "all"
305
+ }) {
306
+ const initialValues = sanitizeInput(schema, value);
307
+ const [validationError, setValidationError] = useState(null);
308
+ const [advancedOpen, setAdvancedOpen] = useState(false);
309
+ const [jsonState, setJsonStateMap] = useState({});
310
+ const validationErrorRef = useRef(null);
311
+ const setJsonState = (path, next) => {
312
+ setJsonStateMap((prev) => ({ ...prev, [path]: next }));
313
+ };
314
+ const form = useForm({
315
+ initialValues,
316
+ validate: zod4Resolver(schema),
317
+ // onValuesChange fires synchronously after each field update — no setTimeout needed.
318
+ onValuesChange: (values2) => {
319
+ if (validationErrorRef.current) {
320
+ validationErrorRef.current = null;
321
+ setValidationError(null);
322
+ }
323
+ onChange(values2);
324
+ }
325
+ });
326
+ const handleActionClick = (action) => {
327
+ const parsed = schema.safeParse(form.values);
328
+ if (!parsed.success) {
329
+ const firstIssue = parsed.error.issues[0];
330
+ const fieldErrors = {};
331
+ for (const issue of parsed.error.issues) {
332
+ const path = issue.path.join(".");
333
+ if (path && !fieldErrors[path]) {
334
+ fieldErrors[path] = issue.message;
335
+ }
336
+ }
337
+ form.setErrors(fieldErrors);
338
+ const msg = firstIssue?.message ?? "Validation failed";
339
+ validationErrorRef.current = msg;
340
+ setValidationError(msg);
341
+ return;
342
+ }
343
+ validationErrorRef.current = null;
344
+ setValidationError(null);
345
+ void onAction?.(action, parsed.data);
346
+ };
347
+ const values = form.values;
348
+ if (slot === "advanced") {
349
+ 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." }) });
350
+ }
351
+ return /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
352
+ slot === "all" && validationError && /* @__PURE__ */ jsx(
353
+ Alert,
354
+ {
355
+ color: "red",
356
+ onClose: () => {
357
+ validationErrorRef.current = null;
358
+ setValidationError(null);
359
+ },
360
+ withCloseButton: true,
361
+ children: validationError
362
+ }
363
+ ),
364
+ layout.sections.map((section) => renderSection(section, values, form, disabled, jsonState, setJsonState)),
365
+ slot === "all" && layout.advanced && /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
366
+ /* @__PURE__ */ jsx(
367
+ Button,
368
+ {
369
+ variant: "subtle",
370
+ size: "xs",
371
+ onClick: () => setAdvancedOpen((o) => !o),
372
+ style: { alignSelf: "flex-start" },
373
+ children: advancedOpen ? "Hide advanced" : "Show advanced"
374
+ }
375
+ ),
376
+ /* @__PURE__ */ jsx(Collapse, { in: advancedOpen, children: renderSection(layout.advanced, values, form, disabled, jsonState, setJsonState) })
377
+ ] }),
378
+ slot === "all" && actions && actions.length > 0 && /* @__PURE__ */ jsx(Group, { gap: "sm", children: actions.map((action) => /* @__PURE__ */ jsx(
379
+ Button,
380
+ {
381
+ variant: action.variant ?? "filled",
382
+ loading: action.loading,
383
+ disabled: disabled || action.disabled,
384
+ onClick: () => handleActionClick(action),
385
+ children: action.label
386
+ },
387
+ action.id
388
+ )) })
389
+ ] });
390
+ }
113
391
  var LEAD_GEN_ROUTE_LINKS = [
114
392
  { label: "Overview", to: "/lead-gen" },
115
393
  { label: "Lists", to: "/lead-gen/lists" },
@@ -1101,6 +1379,61 @@ function ListMemberDrawer({ memberId, memberKind, listId, onClose }) {
1101
1379
  }
1102
1380
  );
1103
1381
  }
1382
+ var panelStyle = {
1383
+ flex: 1,
1384
+ minHeight: 0,
1385
+ overflowY: "auto",
1386
+ paddingTop: "var(--mantine-spacing-sm)"
1387
+ };
1388
+ function StepDetailRightColumn({
1389
+ configuration,
1390
+ advanced,
1391
+ runs,
1392
+ action,
1393
+ activeTab,
1394
+ onTabChange
1395
+ }) {
1396
+ return /* @__PURE__ */ jsxs(
1397
+ Stack,
1398
+ {
1399
+ gap: "sm",
1400
+ style: {
1401
+ flex: 1,
1402
+ minHeight: 0
1403
+ },
1404
+ children: [
1405
+ /* @__PURE__ */ jsxs(
1406
+ Tabs,
1407
+ {
1408
+ value: activeTab,
1409
+ onChange: (value) => {
1410
+ if (value === "configuration" || value === "advanced" || value === "runs") {
1411
+ onTabChange(value);
1412
+ }
1413
+ },
1414
+ style: {
1415
+ flex: 1,
1416
+ minHeight: 0,
1417
+ display: "flex",
1418
+ flexDirection: "column"
1419
+ },
1420
+ children: [
1421
+ /* @__PURE__ */ jsxs(Tabs.List, { children: [
1422
+ /* @__PURE__ */ jsx(Tabs.Tab, { value: "configuration", children: "Configuration" }),
1423
+ /* @__PURE__ */ jsx(Tabs.Tab, { value: "advanced", children: "Advanced" }),
1424
+ /* @__PURE__ */ jsx(Tabs.Tab, { value: "runs", children: "Runs" })
1425
+ ] }),
1426
+ /* @__PURE__ */ jsx(Tabs.Panel, { value: "configuration", style: panelStyle, children: configuration }),
1427
+ /* @__PURE__ */ jsx(Tabs.Panel, { value: "advanced", style: panelStyle, children: advanced ?? /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "No advanced settings for this step." }) }),
1428
+ /* @__PURE__ */ jsx(Tabs.Panel, { value: "runs", style: panelStyle, children: runs })
1429
+ ]
1430
+ }
1431
+ ),
1432
+ /* @__PURE__ */ jsx(Stack, { gap: "xs", style: { flexShrink: 0 }, children: action })
1433
+ ]
1434
+ }
1435
+ );
1436
+ }
1104
1437
  function ListBuilderIndexPage() {
1105
1438
  const navigate = useNavigate();
1106
1439
  const [query, setQuery] = useState("");
@@ -1508,6 +1841,9 @@ function PipelineFunnel({ progress, pipelineConfig, actions }) {
1508
1841
  )
1509
1842
  ] }) });
1510
1843
  }
1844
+ function asRecord(value) {
1845
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
1846
+ }
1511
1847
  function selectedCount(selection) {
1512
1848
  return selection.selectedCompanyIds.length + selection.selectedContactIds.length;
1513
1849
  }
@@ -1532,7 +1868,9 @@ function RunWorkflowModal({
1532
1868
  onResourceChange,
1533
1869
  onSubmitted
1534
1870
  }) {
1535
- const [selectedResourceId, setSelectedResourceId] = useState(initialResourceId ?? actions[0]?.resourceId ?? null);
1871
+ const [selectedResourceId, setSelectedResourceId] = useState(
1872
+ initialResourceId ?? actions[0]?.resourceId ?? null
1873
+ );
1536
1874
  const selectedAction = useMemo(
1537
1875
  () => actions.find((action) => action.resourceId === selectedResourceId) ?? actions[0],
1538
1876
  [actions, selectedResourceId]
@@ -1568,57 +1906,88 @@ function RunWorkflowModal({
1568
1906
  showApiErrorNotification(error);
1569
1907
  }
1570
1908
  };
1571
- const Form = selectedAction?.inputForm;
1572
1909
  const count = selectedCount(selection);
1573
- return /* @__PURE__ */ jsx(CustomModal, { opened, onClose: () => !execution.isPending && onClose(), size: "lg", loading: execution.isPending, children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
1574
- /* @__PURE__ */ jsxs(Group, { gap: "sm", children: [
1575
- /* @__PURE__ */ jsx(IconBolt, { size: 22 }),
1576
- /* @__PURE__ */ jsxs("div", { children: [
1577
- /* @__PURE__ */ jsx(Text, { fw: 600, children: title }),
1578
- /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: description })
1579
- ] })
1580
- ] }),
1581
- !actions.length ? /* @__PURE__ */ jsx(Alert, { icon: /* @__PURE__ */ jsx(IconAlertCircle, { size: 16 }), color: "gray", variant: "light", children: "No list builder workflows are registered." }) : /* @__PURE__ */ jsxs(Fragment, { children: [
1582
- /* @__PURE__ */ jsx(
1583
- Select,
1584
- {
1585
- label: "Workflow",
1586
- data: actionOptions,
1587
- value: selectedAction?.resourceId ?? null,
1588
- onChange: handleResourceChange,
1589
- disabled: execution.isPending || lockResourceSelection,
1590
- searchable: true
1591
- }
1592
- ),
1593
- selectedAction ? /* @__PURE__ */ jsxs(Stack, { gap: 6, children: [
1594
- /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
1595
- /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", children: selectedAction.category }),
1596
- selectedAction.stagesAffected.map((stage) => /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "outline", color: "gray", children: stage }, stage))
1910
+ return /* @__PURE__ */ jsx(
1911
+ CustomModal,
1912
+ {
1913
+ opened,
1914
+ onClose: () => !execution.isPending && onClose(),
1915
+ size: "lg",
1916
+ loading: execution.isPending,
1917
+ children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
1918
+ /* @__PURE__ */ jsxs(Group, { gap: "sm", children: [
1919
+ /* @__PURE__ */ jsx(IconBolt, { size: 22 }),
1920
+ /* @__PURE__ */ jsxs("div", { children: [
1921
+ /* @__PURE__ */ jsx(Text, { fw: 600, children: title }),
1922
+ /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: description })
1923
+ ] })
1597
1924
  ] }),
1598
- /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: selectedAction.description })
1599
- ] }) : null,
1600
- count > 0 ? /* @__PURE__ */ jsxs(Alert, { color: "blue", variant: "light", children: [
1601
- "This run includes ",
1602
- selection.selectedCompanyIds.length,
1603
- " selected companies and",
1604
- " ",
1605
- selection.selectedContactIds.length,
1606
- " selected contacts."
1607
- ] }) : null,
1608
- Form && selectedAction ? /* @__PURE__ */ jsx(Form, { list, onSubmit: submitInput }) : selectedAction ? /* @__PURE__ */ jsx(Group, { justify: "flex-end", children: /* @__PURE__ */ jsxs(
1609
- Button,
1610
- {
1611
- leftSection: /* @__PURE__ */ jsx(IconPlayerPlay, { size: 16 }),
1612
- loading: execution.isPending,
1613
- onClick: () => void submitInput(selectedAction.defaultInput?.(list) ?? { listId: list.id }),
1614
- children: [
1615
- "Run ",
1616
- selectedAction.label
1617
- ]
1618
- }
1619
- ) }) : null
1620
- ] })
1621
- ] }) });
1925
+ !actions.length ? /* @__PURE__ */ jsx(Alert, { icon: /* @__PURE__ */ jsx(IconAlertCircle, { size: 16 }), color: "gray", variant: "light", children: "No list builder workflows are registered." }) : /* @__PURE__ */ jsxs(Fragment, { children: [
1926
+ /* @__PURE__ */ jsx(
1927
+ Select,
1928
+ {
1929
+ label: "Workflow",
1930
+ data: actionOptions,
1931
+ value: selectedAction?.resourceId ?? null,
1932
+ onChange: handleResourceChange,
1933
+ disabled: execution.isPending || lockResourceSelection,
1934
+ searchable: true
1935
+ }
1936
+ ),
1937
+ selectedAction ? /* @__PURE__ */ jsxs(Stack, { gap: 6, children: [
1938
+ /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
1939
+ /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", children: selectedAction.category }),
1940
+ selectedAction.stagesAffected.map((stage) => /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "outline", color: "gray", children: stage }, stage))
1941
+ ] }),
1942
+ /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: selectedAction.description })
1943
+ ] }) : null,
1944
+ count > 0 ? /* @__PURE__ */ jsxs(Alert, { color: "blue", variant: "light", children: [
1945
+ "This run includes ",
1946
+ selection.selectedCompanyIds.length,
1947
+ " selected companies and",
1948
+ " ",
1949
+ selection.selectedContactIds.length,
1950
+ " selected contacts."
1951
+ ] }) : null,
1952
+ selectedAction ? /* @__PURE__ */ jsx(
1953
+ RunWorkflowConfigForm,
1954
+ {
1955
+ action: selectedAction,
1956
+ list,
1957
+ onSubmit: submitInput,
1958
+ isSubmitting: execution.isPending
1959
+ },
1960
+ selectedAction.resourceId
1961
+ ) : null
1962
+ ] })
1963
+ ] })
1964
+ }
1965
+ );
1966
+ }
1967
+ function RunWorkflowConfigForm({ action, list, onSubmit, isSubmitting }) {
1968
+ const schema = action.schema;
1969
+ const layout = action.layout;
1970
+ const [config, setConfig] = useState(() => asRecord(action.defaultInput?.(list)));
1971
+ const handleChange = (next) => {
1972
+ setConfig(next);
1973
+ };
1974
+ const handleSubmit = async () => {
1975
+ await onSubmit(config);
1976
+ };
1977
+ return /* @__PURE__ */ jsx(Stack, { gap: "md", children: /* @__PURE__ */ jsx(
1978
+ StepConfigForm,
1979
+ {
1980
+ schema,
1981
+ layout,
1982
+ value: config,
1983
+ onChange: (v) => handleChange(asRecord(v)),
1984
+ actions: [{ id: "run", label: `Run ${action.label}`, loading: isSubmitting }],
1985
+ onAction: async () => {
1986
+ await handleSubmit();
1987
+ },
1988
+ disabled: isSubmitting
1989
+ }
1990
+ ) });
1622
1991
  }
1623
1992
  function formatDateTime2(value) {
1624
1993
  if (!value) return "Not yet";
@@ -1668,15 +2037,20 @@ function getSummaryKeys(value) {
1668
2037
  if (!hasObjectContent(value)) return [];
1669
2038
  return Object.keys(value).slice(0, 6);
1670
2039
  }
1671
- function WorkflowRunsPanel({ listId, title = "Workflow Runs" }) {
2040
+ function WorkflowRunsPanel({
2041
+ listId,
2042
+ title = "Workflow Runs",
2043
+ resourceFilter,
2044
+ onClearResourceFilter
2045
+ }) {
1672
2046
  const navigate = useNavigate();
1673
- const executionsQuery = useListExecutions(listId);
2047
+ const executionsQuery = useListExecutions(listId, resourceFilter ? { resourceId: resourceFilter } : {});
1674
2048
  const [expandedExecutionId, setExpandedExecutionId] = useState(null);
1675
2049
  const executions = useMemo(() => executionsQuery.data ?? [], [executionsQuery.data]);
1676
2050
  const openWorkflow = (resourceId) => {
1677
2051
  void navigate({ to: "/operations/resources/workflow/$workflowId", params: { workflowId: resourceId } });
1678
2052
  };
1679
- return /* @__PURE__ */ jsx(
2053
+ return /* @__PURE__ */ jsxs(
1680
2054
  TabSection,
1681
2055
  {
1682
2056
  icon: /* @__PURE__ */ jsx(IconPlayerPlay, { size: 16 }),
@@ -1693,89 +2067,95 @@ function WorkflowRunsPanel({ listId, title = "Workflow Runs" }) {
1693
2067
  children: /* @__PURE__ */ jsx(IconRefresh, { size: 14 })
1694
2068
  }
1695
2069
  ) }),
1696
- children: executionsQuery.isLoading ? /* @__PURE__ */ jsx(Center, { p: "lg", children: /* @__PURE__ */ jsx(Loader, { size: "sm" }) }) : !executions.length ? /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "No workflow runs recorded for this list." }) : /* @__PURE__ */ jsx(Table.ScrollContainer, { minWidth: 720, children: /* @__PURE__ */ jsxs(Table, { highlightOnHover: true, children: [
1697
- /* @__PURE__ */ jsx(Table.Thead, { children: /* @__PURE__ */ jsxs(Table.Tr, { children: [
1698
- /* @__PURE__ */ jsx(Table.Th, {}),
1699
- /* @__PURE__ */ jsx(Table.Th, { children: "Workflow" }),
1700
- /* @__PURE__ */ jsx(Table.Th, { children: "Status" }),
1701
- /* @__PURE__ */ jsx(Table.Th, { children: "Started" }),
1702
- /* @__PURE__ */ jsx(Table.Th, { children: "Completed" }),
1703
- /* @__PURE__ */ jsx(Table.Th, { children: "Duration" })
1704
- ] }) }),
1705
- /* @__PURE__ */ jsx(Table.Tbody, { children: executions.map((execution) => {
1706
- const details = getRunDetails(execution);
1707
- const hasDetails = details.input !== void 0 || details.config !== void 0;
1708
- const expanded = expandedExecutionId === execution.executionId;
1709
- return /* @__PURE__ */ jsxs(Fragment$1, { children: [
1710
- /* @__PURE__ */ jsxs(Table.Tr, { children: [
1711
- /* @__PURE__ */ jsx(Table.Td, { w: 32, children: /* @__PURE__ */ jsx(
1712
- ThemeIcon,
1713
- {
1714
- variant: "light",
1715
- color: "blue",
1716
- size: "md",
1717
- role: hasDetails ? "button" : void 0,
1718
- tabIndex: hasDetails ? 0 : void 0,
1719
- "aria-label": hasDetails ? expanded ? "Collapse run details" : "Expand run details" : void 0,
1720
- onClick: hasDetails ? () => setExpandedExecutionId(expanded ? null : execution.executionId) : void 0,
1721
- onKeyDown: hasDetails ? (event) => {
1722
- if (event.key === "Enter" || event.key === " ") {
1723
- event.preventDefault();
1724
- setExpandedExecutionId(expanded ? null : execution.executionId);
1725
- }
1726
- } : void 0,
1727
- style: {
1728
- cursor: hasDetails ? "pointer" : "default",
1729
- opacity: hasDetails ? 1 : 0.7
1730
- },
1731
- children: expanded ? /* @__PURE__ */ jsx(IconChevronDown, { size: 14 }) : /* @__PURE__ */ jsx(IconChevronRight, { size: 14 })
1732
- }
1733
- ) }),
1734
- /* @__PURE__ */ jsxs(Table.Td, { children: [
1735
- execution.resourceId ? /* @__PURE__ */ jsx(
1736
- Text,
2070
+ children: [
2071
+ resourceFilter ? /* @__PURE__ */ jsxs(Group, { gap: "xs", mb: "xs", children: [
2072
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "Filtered by workflow:" }),
2073
+ /* @__PURE__ */ jsx(Pill, { withRemoveButton: true, onRemove: onClearResourceFilter, children: resourceFilter })
2074
+ ] }) : null,
2075
+ 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: [
2076
+ /* @__PURE__ */ jsx(Table.Thead, { children: /* @__PURE__ */ jsxs(Table.Tr, { children: [
2077
+ /* @__PURE__ */ jsx(Table.Th, {}),
2078
+ /* @__PURE__ */ jsx(Table.Th, { children: "Workflow" }),
2079
+ /* @__PURE__ */ jsx(Table.Th, { children: "Status" }),
2080
+ /* @__PURE__ */ jsx(Table.Th, { children: "Started" }),
2081
+ /* @__PURE__ */ jsx(Table.Th, { children: "Completed" }),
2082
+ /* @__PURE__ */ jsx(Table.Th, { children: "Duration" })
2083
+ ] }) }),
2084
+ /* @__PURE__ */ jsx(Table.Tbody, { children: executions.map((execution) => {
2085
+ const details = getRunDetails(execution);
2086
+ const hasDetails = details.input !== void 0 || details.config !== void 0;
2087
+ const expanded = expandedExecutionId === execution.executionId;
2088
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [
2089
+ /* @__PURE__ */ jsxs(Table.Tr, { children: [
2090
+ /* @__PURE__ */ jsx(Table.Td, { w: 32, children: /* @__PURE__ */ jsx(
2091
+ ThemeIcon,
1737
2092
  {
1738
- fw: 500,
1739
- role: "button",
1740
- tabIndex: 0,
1741
- c: "blue",
1742
- onClick: () => openWorkflow(execution.resourceId),
1743
- onKeyDown: (event) => {
2093
+ variant: "light",
2094
+ color: "blue",
2095
+ size: "md",
2096
+ role: hasDetails ? "button" : void 0,
2097
+ tabIndex: hasDetails ? 0 : void 0,
2098
+ "aria-label": hasDetails ? expanded ? "Collapse run details" : "Expand run details" : void 0,
2099
+ onClick: hasDetails ? () => setExpandedExecutionId(expanded ? null : execution.executionId) : void 0,
2100
+ onKeyDown: hasDetails ? (event) => {
1744
2101
  if (event.key === "Enter" || event.key === " ") {
1745
2102
  event.preventDefault();
1746
- openWorkflow(execution.resourceId);
2103
+ setExpandedExecutionId(expanded ? null : execution.executionId);
1747
2104
  }
2105
+ } : void 0,
2106
+ style: {
2107
+ cursor: hasDetails ? "pointer" : "default",
2108
+ opacity: hasDetails ? 1 : 0.7
1748
2109
  },
1749
- style: { cursor: "pointer" },
1750
- children: execution.resourceId
2110
+ children: expanded ? /* @__PURE__ */ jsx(IconChevronDown, { size: 14 }) : /* @__PURE__ */ jsx(IconChevronRight, { size: 14 })
1751
2111
  }
1752
- ) : /* @__PURE__ */ jsx(Text, { fw: 500, children: "Unknown workflow" }),
1753
- /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", ff: "monospace", children: execution.executionId })
1754
- ] }),
1755
- /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: getStatusColor2(execution.status), children: execution.status }) }),
1756
- /* @__PURE__ */ jsx(Table.Td, { children: formatDateTime2(execution.createdAt) }),
1757
- /* @__PURE__ */ jsx(Table.Td, { children: formatDateTime2(execution.completedAt) }),
1758
- /* @__PURE__ */ jsx(Table.Td, { children: formatDuration(execution.durationMs) })
1759
- ] }),
1760
- /* @__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: [
1761
- details.input !== void 0 ? /* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
1762
- /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
1763
- /* @__PURE__ */ jsx(Text, { size: "sm", fw: 600, children: "Input" }),
1764
- getSummaryKeys(details.input).map((key) => /* @__PURE__ */ jsx(Badge, { size: "xs", variant: "light", color: "gray", children: key }, key))
1765
- ] }),
1766
- /* @__PURE__ */ jsx(JsonViewer, { data: details.input, maxHeight: 220, fontSize: "0.75rem" })
1767
- ] }) : null,
1768
- details.config !== void 0 ? /* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
1769
- /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
1770
- /* @__PURE__ */ jsx(Text, { size: "sm", fw: 600, children: "Config" }),
1771
- getSummaryKeys(details.config).map((key) => /* @__PURE__ */ jsx(Badge, { size: "xs", variant: "light", color: "gray", children: key }, key))
2112
+ ) }),
2113
+ /* @__PURE__ */ jsxs(Table.Td, { children: [
2114
+ execution.resourceId ? /* @__PURE__ */ jsx(
2115
+ Text,
2116
+ {
2117
+ fw: 500,
2118
+ role: "button",
2119
+ tabIndex: 0,
2120
+ c: "blue",
2121
+ onClick: () => openWorkflow(execution.resourceId),
2122
+ onKeyDown: (event) => {
2123
+ if (event.key === "Enter" || event.key === " ") {
2124
+ event.preventDefault();
2125
+ openWorkflow(execution.resourceId);
2126
+ }
2127
+ },
2128
+ style: { cursor: "pointer" },
2129
+ children: execution.resourceId
2130
+ }
2131
+ ) : /* @__PURE__ */ jsx(Text, { fw: 500, children: "Unknown workflow" }),
2132
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", ff: "monospace", children: execution.executionId })
1772
2133
  ] }),
1773
- /* @__PURE__ */ jsx(JsonViewer, { data: details.config, maxHeight: 220, fontSize: "0.75rem" })
1774
- ] }) : null
1775
- ] }) }) }) })
1776
- ] }, execution.executionId);
1777
- }) })
1778
- ] }) })
2134
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: getStatusColor2(execution.status), children: execution.status }) }),
2135
+ /* @__PURE__ */ jsx(Table.Td, { children: formatDateTime2(execution.createdAt) }),
2136
+ /* @__PURE__ */ jsx(Table.Td, { children: formatDateTime2(execution.completedAt) }),
2137
+ /* @__PURE__ */ jsx(Table.Td, { children: formatDuration(execution.durationMs) })
2138
+ ] }),
2139
+ /* @__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: [
2140
+ details.input !== void 0 ? /* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
2141
+ /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
2142
+ /* @__PURE__ */ jsx(Text, { size: "sm", fw: 600, children: "Input" }),
2143
+ getSummaryKeys(details.input).map((key) => /* @__PURE__ */ jsx(Badge, { size: "xs", variant: "light", color: "gray", children: key }, key))
2144
+ ] }),
2145
+ /* @__PURE__ */ jsx(JsonViewer, { data: details.input, maxHeight: 220, fontSize: "0.75rem" })
2146
+ ] }) : null,
2147
+ details.config !== void 0 ? /* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
2148
+ /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
2149
+ /* @__PURE__ */ jsx(Text, { size: "sm", fw: 600, children: "Config" }),
2150
+ getSummaryKeys(details.config).map((key) => /* @__PURE__ */ jsx(Badge, { size: "xs", variant: "light", color: "gray", children: key }, key))
2151
+ ] }),
2152
+ /* @__PURE__ */ jsx(JsonViewer, { data: details.config, maxHeight: 220, fontSize: "0.75rem" })
2153
+ ] }) : null
2154
+ ] }) }) }) })
2155
+ ] }, execution.executionId);
2156
+ }) })
2157
+ ] }) })
2158
+ ]
1779
2159
  }
1780
2160
  );
1781
2161
  }
@@ -1918,277 +2298,6 @@ function ListBuilderPage({ listId }) {
1918
2298
  )
1919
2299
  ] });
1920
2300
  }
1921
- function CompanyCleanupForm({ list, onSubmit }) {
1922
- const [isSubmitting, setIsSubmitting] = useState(false);
1923
- const form = useForm({
1924
- initialValues: {
1925
- targetDescription: "",
1926
- tag: "",
1927
- dryRun: true,
1928
- batchSize: 20
1929
- },
1930
- validate: {
1931
- targetDescription: (value) => value.trim().length > 0 ? null : "Target description is required.",
1932
- batchSize: (value) => Number.isInteger(value) && value >= 1 ? null : "Use a whole number of at least 1."
1933
- }
1934
- });
1935
- const handleSubmit = form.onSubmit(async (values) => {
1936
- setIsSubmitting(true);
1937
- try {
1938
- const tag = values.tag.trim();
1939
- await onSubmit({
1940
- listId: list.id,
1941
- targetDescription: values.targetDescription.trim(),
1942
- ...tag ? { tag } : {},
1943
- dryRun: values.dryRun,
1944
- batchSize: values.batchSize
1945
- });
1946
- } finally {
1947
- setIsSubmitting(false);
1948
- }
1949
- });
1950
- return /* @__PURE__ */ jsx("form", { onSubmit: handleSubmit, children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
1951
- /* @__PURE__ */ jsx(
1952
- Textarea,
1953
- {
1954
- label: "Target description",
1955
- placeholder: "independent veterinary clinics in Orange County",
1956
- minRows: 3,
1957
- autosize: true,
1958
- disabled: isSubmitting,
1959
- required: true,
1960
- ...form.getInputProps("targetDescription")
1961
- }
1962
- ),
1963
- /* @__PURE__ */ jsx(TextInput, { label: "Tag kept companies", placeholder: "vet-clinic", disabled: isSubmitting, ...form.getInputProps("tag") }),
1964
- /* @__PURE__ */ jsx(
1965
- NumberInput,
1966
- {
1967
- label: "Batch size",
1968
- min: 1,
1969
- allowDecimal: false,
1970
- disabled: isSubmitting,
1971
- required: true,
1972
- ...form.getInputProps("batchSize")
1973
- }
1974
- ),
1975
- /* @__PURE__ */ jsx(Switch, { label: "Dry run", disabled: isSubmitting, ...form.getInputProps("dryRun", { type: "checkbox" }) }),
1976
- /* @__PURE__ */ jsx(Group, { justify: "flex-end", children: /* @__PURE__ */ jsx(
1977
- Button,
1978
- {
1979
- type: "submit",
1980
- loading: isSubmitting,
1981
- disabled: !form.values.targetDescription.trim(),
1982
- leftSection: /* @__PURE__ */ jsx(IconPlayerPlay, { size: 16 }),
1983
- children: "Run Company Cleanup"
1984
- }
1985
- ) })
1986
- ] }) });
1987
- }
1988
- function optionalNumber(value) {
1989
- return value === "" ? void 0 : value;
1990
- }
1991
- function EmailDiscoveryForm({ list, onSubmit }) {
1992
- const [isSubmitting, setIsSubmitting] = useState(false);
1993
- const form = useForm({
1994
- initialValues: {
1995
- maxCompanies: "",
1996
- dryRun: false,
1997
- chain: false,
1998
- concurrency: 1,
1999
- credentialName: "",
2000
- tombaTimeoutMs: 15e3
2001
- },
2002
- validate: {
2003
- maxCompanies: (value) => value === "" || Number.isInteger(value) && value >= 1 && value <= 500 ? null : "Use a whole number from 1 to 500.",
2004
- concurrency: (value) => Number.isInteger(value) && value >= 1 && value <= 10 ? null : "Use a whole number from 1 to 10.",
2005
- tombaTimeoutMs: (value) => Number.isInteger(value) && value >= 1e3 ? null : "Use a timeout of at least 1000 ms."
2006
- }
2007
- });
2008
- const handleSubmit = form.onSubmit(async (values) => {
2009
- setIsSubmitting(true);
2010
- try {
2011
- const credentialName = values.credentialName.trim();
2012
- await onSubmit({
2013
- listId: list.id,
2014
- ...optionalNumber(values.maxCompanies) !== void 0 ? { maxCompanies: optionalNumber(values.maxCompanies) } : {},
2015
- dryRun: values.dryRun,
2016
- chain: values.chain,
2017
- concurrency: values.concurrency,
2018
- ...credentialName ? { credentialName } : {},
2019
- tombaTimeoutMs: values.tombaTimeoutMs
2020
- });
2021
- } finally {
2022
- setIsSubmitting(false);
2023
- }
2024
- });
2025
- return /* @__PURE__ */ jsx("form", { onSubmit: handleSubmit, children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
2026
- /* @__PURE__ */ jsx(
2027
- NumberInput,
2028
- {
2029
- label: "Max companies",
2030
- description: "Leave empty to process every eligible company.",
2031
- min: 1,
2032
- max: 500,
2033
- allowDecimal: false,
2034
- disabled: isSubmitting,
2035
- ...form.getInputProps("maxCompanies")
2036
- }
2037
- ),
2038
- /* @__PURE__ */ jsx(
2039
- NumberInput,
2040
- {
2041
- label: "Concurrency",
2042
- min: 1,
2043
- max: 10,
2044
- allowDecimal: false,
2045
- disabled: isSubmitting,
2046
- required: true,
2047
- ...form.getInputProps("concurrency")
2048
- }
2049
- ),
2050
- /* @__PURE__ */ jsx(
2051
- TextInput,
2052
- {
2053
- label: "Tomba credential",
2054
- placeholder: "Default credential",
2055
- disabled: isSubmitting,
2056
- ...form.getInputProps("credentialName")
2057
- }
2058
- ),
2059
- /* @__PURE__ */ jsx(
2060
- NumberInput,
2061
- {
2062
- label: "Tomba timeout",
2063
- suffix: " ms",
2064
- min: 1e3,
2065
- step: 1e3,
2066
- allowDecimal: false,
2067
- disabled: isSubmitting,
2068
- required: true,
2069
- ...form.getInputProps("tombaTimeoutMs")
2070
- }
2071
- ),
2072
- /* @__PURE__ */ jsx(Switch, { label: "Dry run", disabled: isSubmitting, ...form.getInputProps("dryRun", { type: "checkbox" }) }),
2073
- /* @__PURE__ */ jsx(
2074
- Switch,
2075
- {
2076
- label: "Run email verification after discovery",
2077
- disabled: isSubmitting,
2078
- ...form.getInputProps("chain", { type: "checkbox" })
2079
- }
2080
- ),
2081
- /* @__PURE__ */ jsx(Group, { justify: "flex-end", children: /* @__PURE__ */ jsx(Button, { type: "submit", loading: isSubmitting, leftSection: /* @__PURE__ */ jsx(IconPlayerPlay, { size: 16 }), children: "Run Email Discovery" }) })
2082
- ] }) });
2083
- }
2084
- function EmailVerificationForm({ list, onSubmit }) {
2085
- const [isSubmitting, setIsSubmitting] = useState(false);
2086
- const form = useForm({
2087
- initialValues: {
2088
- limit: 100,
2089
- skipAlreadyVerified: true
2090
- },
2091
- validate: {
2092
- limit: (value) => Number.isInteger(value) && value >= 1 && value <= 500 ? null : "Use a whole number from 1 to 500."
2093
- }
2094
- });
2095
- const handleSubmit = form.onSubmit(async (values) => {
2096
- setIsSubmitting(true);
2097
- try {
2098
- await onSubmit({
2099
- listId: list.id,
2100
- limit: values.limit,
2101
- skipAlreadyVerified: values.skipAlreadyVerified
2102
- });
2103
- } finally {
2104
- setIsSubmitting(false);
2105
- }
2106
- });
2107
- return /* @__PURE__ */ jsx("form", { onSubmit: handleSubmit, children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
2108
- /* @__PURE__ */ jsx(
2109
- NumberInput,
2110
- {
2111
- label: "Contact limit",
2112
- min: 1,
2113
- max: 500,
2114
- allowDecimal: false,
2115
- disabled: isSubmitting,
2116
- required: true,
2117
- ...form.getInputProps("limit")
2118
- }
2119
- ),
2120
- /* @__PURE__ */ jsx(
2121
- Switch,
2122
- {
2123
- label: "Skip already verified contacts",
2124
- disabled: isSubmitting,
2125
- ...form.getInputProps("skipAlreadyVerified", { type: "checkbox" })
2126
- }
2127
- ),
2128
- /* @__PURE__ */ jsx(Group, { justify: "flex-end", children: /* @__PURE__ */ jsx(Button, { type: "submit", loading: isSubmitting, leftSection: /* @__PURE__ */ jsx(IconPlayerPlay, { size: 16 }), children: "Run Email Verification" }) })
2129
- ] }) });
2130
- }
2131
- function WebsiteExtractForm({ list, onSubmit }) {
2132
- const [isSubmitting, setIsSubmitting] = useState(false);
2133
- const form = useForm({
2134
- initialValues: {
2135
- limit: 200,
2136
- concurrency: 10,
2137
- chain: false
2138
- },
2139
- validate: {
2140
- limit: (value) => Number.isInteger(value) && value >= 1 && value <= 200 ? null : "Use a whole number from 1 to 200.",
2141
- concurrency: (value) => Number.isInteger(value) && value >= 1 ? null : "Use a whole number of at least 1."
2142
- }
2143
- });
2144
- const handleSubmit = form.onSubmit(async (values) => {
2145
- setIsSubmitting(true);
2146
- try {
2147
- await onSubmit({
2148
- listId: list.id,
2149
- limit: values.limit,
2150
- concurrency: values.concurrency,
2151
- chain: values.chain
2152
- });
2153
- } finally {
2154
- setIsSubmitting(false);
2155
- }
2156
- });
2157
- return /* @__PURE__ */ jsx("form", { onSubmit: handleSubmit, children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
2158
- /* @__PURE__ */ jsx(
2159
- NumberInput,
2160
- {
2161
- label: "Company limit",
2162
- min: 1,
2163
- max: 200,
2164
- allowDecimal: false,
2165
- disabled: isSubmitting,
2166
- required: true,
2167
- ...form.getInputProps("limit")
2168
- }
2169
- ),
2170
- /* @__PURE__ */ jsx(
2171
- NumberInput,
2172
- {
2173
- label: "Concurrency",
2174
- min: 1,
2175
- allowDecimal: false,
2176
- disabled: isSubmitting,
2177
- required: true,
2178
- ...form.getInputProps("concurrency")
2179
- }
2180
- ),
2181
- /* @__PURE__ */ jsx(
2182
- Switch,
2183
- {
2184
- label: "Run company qualification after extraction",
2185
- disabled: isSubmitting,
2186
- ...form.getInputProps("chain", { type: "checkbox" })
2187
- }
2188
- ),
2189
- /* @__PURE__ */ jsx(Group, { justify: "flex-end", children: /* @__PURE__ */ jsx(Button, { type: "submit", loading: isSubmitting, leftSection: /* @__PURE__ */ jsx(IconPlayerPlay, { size: 16 }), children: "Run Website Extract" }) })
2190
- ] }) });
2191
- }
2192
2301
  function formatDateTime4(value) {
2193
2302
  if (!value) return "Not yet";
2194
2303
  return new Date(value).toLocaleString("en-US", {
@@ -2237,7 +2346,7 @@ function getMemberStateColor2(stateKey) {
2237
2346
  return "gray";
2238
2347
  }
2239
2348
  }
2240
- function asRecord(value) {
2349
+ function asRecord2(value) {
2241
2350
  return value && typeof value === "object" && !Array.isArray(value) ? value : {};
2242
2351
  }
2243
2352
  function getRunStatusColor(status) {
@@ -2259,25 +2368,22 @@ function getRunStatusColor(status) {
2259
2368
  }
2260
2369
  }
2261
2370
  function getRunInput(run) {
2262
- const payload = asRecord(run.payload);
2371
+ const payload = asRecord2(run.payload);
2263
2372
  return run.input ?? run.inputs ?? payload.input ?? payload.inputs;
2264
2373
  }
2265
- function getPersistedStepConfig(list, capabilityKey) {
2266
- return capabilityKey ? asRecord(list.metadata.stepConfig?.[capabilityKey]) : {};
2374
+ function sanitizeStepInput(value, _capabilityKey) {
2375
+ return value;
2267
2376
  }
2268
2377
  function getDefaultStepInput(list, action) {
2269
- return asRecord(action?.defaultInput?.(list));
2378
+ return asRecord2(action?.defaultInput?.(list));
2270
2379
  }
2271
- function getInitialStepConfig(list, action, capabilityKey) {
2272
- const persistedConfig = capabilityKey ? list.metadata.stepConfig?.[capabilityKey] : void 0;
2273
- if (persistedConfig !== void 0) return asRecord(persistedConfig);
2380
+ function getInitialStepInput(list, action) {
2274
2381
  return getDefaultStepInput(list, action);
2275
2382
  }
2276
- function mergeStepRunInput(list, action, capabilityKey, formStateAtSubmit) {
2383
+ function mergeStepRunInput(list, action, inputAtSubmit) {
2277
2384
  return {
2278
2385
  ...getDefaultStepInput(list, action),
2279
- ...getPersistedStepConfig(list, capabilityKey),
2280
- ...formStateAtSubmit
2386
+ ...inputAtSubmit
2281
2387
  };
2282
2388
  }
2283
2389
  var ORPHAN_STAGE_ORDER = 9999;
@@ -2285,10 +2391,6 @@ var EMPTY_SELECTION2 = {
2285
2391
  selectedCompanyIds: [],
2286
2392
  selectedContactIds: []
2287
2393
  };
2288
- var BUILD_TEMPLATE_SELECT_OPTIONS2 = PROSPECTING_BUILD_TEMPLATE_OPTIONS.map((template) => ({
2289
- value: template.id,
2290
- label: template.label
2291
- }));
2292
2394
  var MVP_BUILD_STEPS = [
2293
2395
  {
2294
2396
  id: "source-companies",
@@ -2444,10 +2546,12 @@ function getPrerequisiteSteps(step, byStepId) {
2444
2546
  return dependency ? [dependency] : [];
2445
2547
  });
2446
2548
  }
2447
- function getStepActionKind(ready, failed, action) {
2549
+ function getStepActionKind(step, ready, failed, action) {
2448
2550
  if (!action) return "none";
2449
2551
  if (failed > 0) return "retry_failed";
2450
2552
  if (ready > 0) return "run_next_batch";
2553
+ const isSeedStep = (step.dependsOn?.length ?? 0) === 0 && step.outputs.includes(step.primaryEntity);
2554
+ if (isSeedStep) return "run_next_batch";
2451
2555
  return "none";
2452
2556
  }
2453
2557
  function getStepActionLabel(kind) {
@@ -2480,7 +2584,7 @@ function deriveBuildStepStates(list, progress, actions) {
2480
2584
  const entityTotal = getEntityTotal(progress, step.primaryEntity);
2481
2585
  const prerequisites = getPrerequisiteSteps(step, byStepId);
2482
2586
  const hasDependencies = (step.dependsOn?.length ?? 0) > 0;
2483
- const eligibleTotal = hasDependencies ? prerequisites.reduce((sum, prerequisite) => sum + prerequisite.complete, 0) : entityTotal > 0 ? entityTotal : step.outputs.includes(step.primaryEntity) ? step.defaultBatchSize : 0;
2587
+ const eligibleTotal = hasDependencies ? prerequisites.reduce((sum, prerequisite) => sum + prerequisite.complete, 0) : entityTotal > 0 ? entityTotal : 0;
2484
2588
  const total = Math.max(stageProgress?.total ?? 0, entityTotal, eligibleTotal);
2485
2589
  const complete = stageProgress ? stageProgress.success + stageProgress.noResult + stageProgress.skipped + stageProgress.other : 0;
2486
2590
  const failed = stageProgress?.error ?? 0;
@@ -2488,7 +2592,7 @@ function deriveBuildStepStates(list, progress, actions) {
2488
2592
  const blocked = hasDependencies ? Math.max(entityTotal - eligibleTotal, 0) : 0;
2489
2593
  const status = failed > 0 ? "failed" : ready > 0 ? "ready" : blocked > 0 || total === 0 ? "blocked" : "complete";
2490
2594
  const action = findActionForStep(actions, step);
2491
- const nextActionKind = getStepActionKind(ready, failed, action);
2595
+ const nextActionKind = getStepActionKind(step, ready, failed, action);
2492
2596
  const state = {
2493
2597
  ...step,
2494
2598
  status,
@@ -2516,74 +2620,173 @@ function resolveBuildState(list, progress, actions) {
2516
2620
  recommendedAction: recommendedStep?.recommendedAction ?? null
2517
2621
  };
2518
2622
  }
2519
- function PipelineSection({
2520
- title,
2623
+ function getStageStatus(entry) {
2624
+ if (!entry || (entry.total ?? 0) === 0) return "not-started";
2625
+ if ((entry.error ?? 0) > 0) return "errors";
2626
+ if ((entry.attempted ?? 0) >= (entry.total ?? 0) && (entry.success ?? 0) > 0) return "complete";
2627
+ return "in-progress";
2628
+ }
2629
+ function getStageColors(status) {
2630
+ switch (status) {
2631
+ case "complete":
2632
+ return {
2633
+ bg: "var(--color-success)",
2634
+ fg: "white",
2635
+ border: "var(--color-success)",
2636
+ ring: "color-mix(in srgb, var(--color-success) 22%, transparent)"
2637
+ };
2638
+ case "errors":
2639
+ return {
2640
+ bg: "var(--color-error)",
2641
+ fg: "white",
2642
+ border: "var(--color-error)",
2643
+ ring: "color-mix(in srgb, var(--color-error) 22%, transparent)"
2644
+ };
2645
+ case "in-progress":
2646
+ return {
2647
+ bg: "var(--color-primary)",
2648
+ fg: "white",
2649
+ border: "var(--color-primary)",
2650
+ ring: "color-mix(in srgb, var(--color-primary) 22%, transparent)"
2651
+ };
2652
+ case "not-started":
2653
+ default:
2654
+ return {
2655
+ bg: "color-mix(in srgb, var(--color-primary) 28%, var(--mantine-color-body))",
2656
+ fg: "color-mix(in srgb, var(--color-primary) 55%, var(--color-text-dimmed))",
2657
+ border: "color-mix(in srgb, var(--color-primary) 45%, var(--color-border))",
2658
+ ring: "color-mix(in srgb, var(--color-primary) 10%, transparent)"
2659
+ };
2660
+ }
2661
+ }
2662
+ var STAGE_NODE_WIDTH = 110;
2663
+ var STAGE_CIRCLE_SIZE = 36;
2664
+ function StageNode({
2665
+ index,
2666
+ stageKey,
2667
+ fallbackLabel,
2668
+ entry
2669
+ }) {
2670
+ const label = LEAD_GEN_STAGE_CATALOG[stageKey]?.label ?? fallbackLabel ?? stageKey;
2671
+ const status = getStageStatus(entry);
2672
+ const colors = getStageColors(status);
2673
+ const total = entry?.total ?? 0;
2674
+ const attempted = entry?.attempted ?? 0;
2675
+ const success = entry?.success ?? 0;
2676
+ const tooltipLines = entry ? [
2677
+ `${attempted} / ${total} attempted`,
2678
+ `${success} success`,
2679
+ (entry.noResult ?? 0) > 0 ? `${entry.noResult} no result` : null,
2680
+ (entry.skipped ?? 0) > 0 ? `${entry.skipped} skipped` : null,
2681
+ (entry.error ?? 0) > 0 ? `${entry.error} errors` : null,
2682
+ (entry.notAttempted ?? 0) > 0 ? `${entry.notAttempted} not attempted` : null
2683
+ ].filter(Boolean) : ["Not started"];
2684
+ return /* @__PURE__ */ jsxs(
2685
+ Stack,
2686
+ {
2687
+ gap: 6,
2688
+ align: "center",
2689
+ style: {
2690
+ width: STAGE_NODE_WIDTH,
2691
+ flex: "0 0 auto",
2692
+ padding: "0 4px",
2693
+ position: "relative",
2694
+ zIndex: 1
2695
+ },
2696
+ children: [
2697
+ /* @__PURE__ */ jsx(Tooltip, { label: tooltipLines.join(" \xB7 "), position: "top", withArrow: true, multiline: true, maw: 260, children: /* @__PURE__ */ jsx(
2698
+ Box,
2699
+ {
2700
+ style: {
2701
+ width: STAGE_CIRCLE_SIZE,
2702
+ height: STAGE_CIRCLE_SIZE,
2703
+ borderRadius: "50%",
2704
+ backgroundColor: colors.bg,
2705
+ border: `1.5px solid ${colors.border}`,
2706
+ display: "flex",
2707
+ alignItems: "center",
2708
+ justifyContent: "center",
2709
+ boxShadow: `0 0 0 4px ${colors.ring}`,
2710
+ transition: "all var(--duration-normal) var(--easing)"
2711
+ },
2712
+ children: /* @__PURE__ */ jsx(Text, { size: "sm", fw: status === "not-started" ? 500 : 700, style: { color: colors.fg, lineHeight: 1 }, children: status === "complete" ? "\u2713" : index + 1 })
2713
+ }
2714
+ ) }),
2715
+ /* @__PURE__ */ jsxs(Stack, { gap: 2, align: "center", style: { minWidth: 0, maxWidth: "100%" }, children: [
2716
+ /* @__PURE__ */ jsx(
2717
+ Text,
2718
+ {
2719
+ size: "xs",
2720
+ fw: status === "not-started" ? 500 : 600,
2721
+ ta: "center",
2722
+ lineClamp: 1,
2723
+ style: {
2724
+ maxWidth: "100%",
2725
+ color: status === "not-started" ? "var(--color-text-dimmed)" : "var(--color-text)",
2726
+ letterSpacing: 0.2
2727
+ },
2728
+ children: label
2729
+ }
2730
+ ),
2731
+ /* @__PURE__ */ jsx(Text, { size: "xs", style: { color: "var(--color-text-subtle)", fontVariantNumeric: "tabular-nums" }, children: entry ? `${attempted}/${total}` : "Pending" })
2732
+ ] })
2733
+ ]
2734
+ }
2735
+ );
2736
+ }
2737
+ function PipelineStepper({
2738
+ plannedSteps,
2521
2739
  byStage,
2522
2740
  emptyText
2523
2741
  }) {
2524
- const orderedKeys = sortStageKeys(Object.keys(byStage));
2525
- return /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
2526
- /* @__PURE__ */ jsx(Text, { size: "sm", fw: 600, children: title }),
2527
- orderedKeys.length === 0 ? /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: emptyText }) : /* @__PURE__ */ jsx(Stack, { gap: "xs", children: orderedKeys.map((key) => {
2528
- const entry = byStage[key];
2529
- const total = entry?.total ?? 0;
2530
- const attempted = entry?.attempted ?? 0;
2531
- const percent2 = total > 0 ? Math.round(attempted / total * 100) : 0;
2532
- const label = LEAD_GEN_STAGE_CATALOG[key]?.label ?? key;
2533
- const segments = [
2534
- { key: "success", value: entry?.success ?? 0, color: "var(--mantine-color-green-6)" },
2535
- { key: "noResult", value: entry?.noResult ?? 0, color: "var(--mantine-color-gray-6)" },
2536
- { key: "skipped", value: entry?.skipped ?? 0, color: "var(--mantine-color-yellow-6)" },
2537
- { key: "error", value: entry?.error ?? 0, color: "var(--mantine-color-red-6)" },
2538
- { key: "other", value: entry?.other ?? 0, color: "var(--mantine-color-blue-6)" }
2539
- ].filter((segment) => segment.value > 0);
2540
- const statusSummary = [
2541
- `${entry?.success ?? 0} success`,
2542
- `${entry?.noResult ?? 0} no result`,
2543
- `${entry?.skipped ?? 0} skipped`,
2544
- `${entry?.error ?? 0} errors`,
2545
- (entry?.other ?? 0) > 0 ? `${entry?.other ?? 0} other` : null,
2546
- (entry?.notAttempted ?? 0) > 0 ? `${entry?.notAttempted ?? 0} not attempted` : null
2547
- ].filter(Boolean);
2548
- return /* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
2549
- /* @__PURE__ */ jsxs(Group, { justify: "space-between", gap: "xs", children: [
2550
- /* @__PURE__ */ jsx(Text, { size: "sm", children: label }),
2551
- /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
2552
- attempted,
2553
- " / ",
2554
- total,
2555
- " attempted (",
2556
- percent2,
2557
- "%)"
2558
- ] })
2559
- ] }),
2742
+ const stageKeys = plannedSteps.length > 0 ? plannedSteps.map((s) => s.stageKey) : sortStageKeys(Object.keys(byStage));
2743
+ const labelByKey = new Map(plannedSteps.map((s) => [s.stageKey, s.label]));
2744
+ if (stageKeys.length === 0) {
2745
+ return /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: emptyText });
2746
+ }
2747
+ return /* @__PURE__ */ jsx(Box, { style: { overflowX: "auto", padding: "4px 0" }, children: /* @__PURE__ */ jsxs(
2748
+ Box,
2749
+ {
2750
+ style: {
2751
+ position: "relative",
2752
+ display: "flex",
2753
+ justifyContent: "space-between",
2754
+ alignItems: "flex-start",
2755
+ minWidth: stageKeys.length * STAGE_NODE_WIDTH
2756
+ },
2757
+ children: [
2560
2758
  /* @__PURE__ */ jsx(
2561
2759
  Box,
2562
2760
  {
2563
- h: 8,
2564
- bg: "var(--mantine-color-dark-4)",
2565
- style: { borderRadius: 999, display: "flex", overflow: "hidden" },
2566
- children: segments.map((segment) => /* @__PURE__ */ jsx(
2567
- Box,
2568
- {
2569
- h: "100%",
2570
- w: `${total > 0 ? segment.value / total * 100 : 0}%`,
2571
- bg: segment.color
2572
- },
2573
- segment.key
2574
- ))
2761
+ style: {
2762
+ position: "absolute",
2763
+ top: STAGE_CIRCLE_SIZE / 2 - 1,
2764
+ left: STAGE_NODE_WIDTH / 2,
2765
+ right: STAGE_NODE_WIDTH / 2,
2766
+ height: 2,
2767
+ backgroundColor: "var(--color-border)"
2768
+ }
2575
2769
  }
2576
2770
  ),
2577
- /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: statusSummary.join(" \xB7 ") })
2578
- ] }, key);
2579
- }) })
2580
- ] });
2771
+ stageKeys.map((key, index) => /* @__PURE__ */ jsx(StageNode, { index, stageKey: key, fallbackLabel: labelByKey.get(key), entry: byStage[key] }, key))
2772
+ ]
2773
+ }
2774
+ ) });
2581
2775
  }
2582
2776
  function PipelineStagesCard({ list, progress }) {
2583
- const hasAnyActivity = Object.keys(progress.byCompanyStage).length > 0 || Object.keys(progress.byContactStage).length > 0;
2777
+ const plannedSteps = list.metadata.buildPlanSnapshot?.steps ?? [];
2778
+ const templateLabel = list.metadata.buildPlanSnapshot?.templateLabel;
2779
+ const combinedByStage = { ...progress.byCompanyStage, ...progress.byContactStage };
2780
+ const hasAnyContent = plannedSteps.length > 0 || Object.keys(combinedByStage).length > 0;
2584
2781
  return /* @__PURE__ */ jsx(Card, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
2585
2782
  /* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "center", children: [
2586
- /* @__PURE__ */ jsx(Title, { order: 5, children: "Pipeline Stages" }),
2783
+ /* @__PURE__ */ jsxs(Stack, { gap: 2, children: [
2784
+ /* @__PURE__ */ jsx(Title, { order: 5, children: "Pipeline Stages" }),
2785
+ templateLabel && /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
2786
+ "Template: ",
2787
+ templateLabel
2788
+ ] })
2789
+ ] }),
2587
2790
  /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
2588
2791
  /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "filled", color: getStatusColor4(list.status), children: list.status }),
2589
2792
  list.launchedAt && /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
@@ -2596,21 +2799,20 @@ function PipelineStagesCard({ list, progress }) {
2596
2799
  ] })
2597
2800
  ] })
2598
2801
  ] }),
2599
- !hasAnyActivity ? /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "No pipeline activity yet \u2014 stages appear here as workflows write processing_state flags." }) : /* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 1, md: 2 }, spacing: "md", children: [
2600
- /* @__PURE__ */ jsx(
2601
- PipelineSection,
2602
- {
2603
- title: `Company Pipeline (${progress.totalCompanies})`,
2604
- byStage: progress.byCompanyStage,
2605
- emptyText: "No company-stage flags recorded yet."
2606
- }
2607
- ),
2802
+ !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: [
2803
+ /* @__PURE__ */ jsxs(Text, { size: "sm", fw: 600, children: [
2804
+ "Pipeline (",
2805
+ progress.totalCompanies,
2806
+ " companies \xB7 ",
2807
+ progress.totalMembers,
2808
+ " contacts)"
2809
+ ] }),
2608
2810
  /* @__PURE__ */ jsx(
2609
- PipelineSection,
2811
+ PipelineStepper,
2610
2812
  {
2611
- title: `Contact Pipeline (${progress.totalMembers})`,
2612
- byStage: progress.byContactStage,
2613
- emptyText: "No contact-stage flags recorded yet."
2813
+ plannedSteps,
2814
+ byStage: combinedByStage,
2815
+ emptyText: "No pipeline activity recorded yet."
2614
2816
  }
2615
2817
  )
2616
2818
  ] })
@@ -2624,12 +2826,12 @@ function ListConfigCard({ list }) {
2624
2826
  if (!hasIcp && !hasScraping) {
2625
2827
  return /* @__PURE__ */ jsx(Card, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
2626
2828
  /* @__PURE__ */ jsx(Title, { order: 5, children: "List Config" }),
2627
- /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "No ICP rubric or scraping criteria configured for this list." })
2829
+ /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "No ICP rubric or scraping criteria recorded for this list." })
2628
2830
  ] }) });
2629
2831
  }
2630
2832
  return /* @__PURE__ */ jsx(Card, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
2631
2833
  /* @__PURE__ */ jsx(Title, { order: 5, children: "List Config" }),
2632
- /* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 1, md: 2 }, spacing: "md", children: [
2834
+ (hasIcp || hasScraping) && /* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 1, md: 2 }, spacing: "md", children: [
2633
2835
  hasIcp && /* @__PURE__ */ jsxs(Stack, { gap: 6, children: [
2634
2836
  /* @__PURE__ */ jsx(Text, { size: "sm", fw: 600, children: "ICP Rubric" }),
2635
2837
  icp.qualificationRubricKey && /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
@@ -2700,26 +2902,13 @@ function OverviewTab({ list, progress }) {
2700
2902
  /* @__PURE__ */ jsx(PipelineStagesCard, { list, progress }),
2701
2903
  /* @__PURE__ */ jsx(ListConfigCard, { list }),
2702
2904
  hasMetadata && /* @__PURE__ */ jsx(Card, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
2703
- /* @__PURE__ */ jsx(Title, { order: 5, children: "Metadata / Qualification Signals" }),
2905
+ /* @__PURE__ */ jsx(Title, { order: 5, children: "Metadata" }),
2704
2906
  /* @__PURE__ */ jsx(JsonViewer, { data: list.metadata, maxHeight: 320 })
2705
2907
  ] }) })
2706
2908
  ]
2707
2909
  }
2708
2910
  );
2709
2911
  }
2710
- function getBuildStatusLabel(status) {
2711
- switch (status) {
2712
- case "failed":
2713
- return "Needs retry";
2714
- case "blocked":
2715
- return "Waiting";
2716
- case "complete":
2717
- return "Complete";
2718
- case "ready":
2719
- default:
2720
- return "Ready";
2721
- }
2722
- }
2723
2912
  function getBuildToneStyle(tone) {
2724
2913
  switch (tone) {
2725
2914
  case "complete":
@@ -2737,7 +2926,7 @@ function getBuildToneStyle(tone) {
2737
2926
  case "ready":
2738
2927
  return {
2739
2928
  backgroundColor: "color-mix(in srgb, var(--color-primary) 14%, transparent)",
2740
- borderColor: "color-mix(in srgb, var(--color-primary) 34%, var(--color-border))",
2929
+ borderColor: "var(--border-primary-muted)",
2741
2930
  color: "var(--color-primary)"
2742
2931
  };
2743
2932
  case "blocked":
@@ -2772,23 +2961,21 @@ function StatusPill({ label, tone }) {
2772
2961
  }
2773
2962
  );
2774
2963
  }
2775
- function CountBadge({ label, value, tone }) {
2776
- const toneStyle = getBuildToneStyle(tone);
2777
- return /* @__PURE__ */ jsxs(
2778
- Box,
2779
- {
2780
- p: "xs",
2781
- style: {
2782
- backgroundColor: "var(--color-surface)",
2783
- border: "1px solid var(--color-border)",
2784
- borderRadius: 8
2785
- },
2786
- children: [
2787
- /* @__PURE__ */ jsx(Text, { size: "lg", fw: 700, style: { color: toneStyle.color }, children: value }),
2788
- /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", tt: "uppercase", fw: 700, children: label })
2789
- ]
2790
- }
2791
- );
2964
+ function StepCounterLine({ step }) {
2965
+ const items = [
2966
+ step.ready > 0 ? { label: "ready", value: step.ready, color: "var(--color-primary)" } : null,
2967
+ step.complete > 0 ? { label: "done", value: step.complete, color: "var(--color-success)" } : null,
2968
+ step.failed > 0 ? { label: "failed", value: step.failed, color: "var(--color-error)" } : null,
2969
+ step.blocked > 0 ? { label: "waiting", value: step.blocked, color: "var(--color-text-dimmed)" } : null
2970
+ ].filter((item) => item !== null);
2971
+ if (!items.length) {
2972
+ return /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "No records yet" });
2973
+ }
2974
+ return /* @__PURE__ */ jsx(Group, { gap: "md", wrap: "nowrap", children: items.map((item) => /* @__PURE__ */ jsxs(Text, { size: "xs", fw: 700, style: { color: item.color }, children: [
2975
+ item.value,
2976
+ " ",
2977
+ item.label
2978
+ ] }, item.label)) });
2792
2979
  }
2793
2980
  function BuildStepProgressBar({ step }) {
2794
2981
  const segments = [
@@ -2813,15 +3000,6 @@ function BuildStepProgressBar({ step }) {
2813
3000
  }
2814
3001
  );
2815
3002
  }
2816
- function getStepCompactSummary(step) {
2817
- const parts = [
2818
- step.complete > 0 ? `${step.complete} complete` : null,
2819
- step.ready > 0 ? `${step.ready} ready` : null,
2820
- step.failed > 0 ? `${step.failed} failed` : null,
2821
- step.blocked > 0 ? `${step.blocked} waiting` : null
2822
- ].filter(Boolean);
2823
- return parts.length ? parts.join(" | ") : "No records yet";
2824
- }
2825
3003
  function getDisplayStep(steps, recommendedStep) {
2826
3004
  return recommendedStep ?? steps.find((step) => step.status !== "complete") ?? steps[steps.length - 1] ?? null;
2827
3005
  }
@@ -2834,41 +3012,67 @@ function BuildTab({
2834
3012
  onViewRuns
2835
3013
  }) {
2836
3014
  const [selectedStepId, setSelectedStepId] = useState(null);
2837
- const [stepConfigFormState, setStepConfigFormState] = useState({});
2838
- const updateListConfigMutation = useUpdateListConfig(list.id);
3015
+ const [stepInputFormState, setStepInputFormState] = useState({});
3016
+ const [stepInputError, setStepInputError] = useState(null);
3017
+ const [stepInputInitialized, setStepInputInitialized] = useState(false);
3018
+ const [activeRightColumnTab, setActiveRightColumnTab] = useState("configuration");
3019
+ const [stepInputResetKey, setStepInputResetKey] = useState(0);
2839
3020
  const currentStep = getDisplayStep(steps, recommendedStep);
2840
3021
  const selectedStep = steps.find((step) => step.id === selectedStepId) ?? currentStep;
2841
3022
  const selectedStepIndex = selectedStep ? steps.findIndex((step) => step.id === selectedStep.id) : -1;
2842
3023
  const selectedActionKind = selectedStep?.nextActionKind ?? "none";
2843
3024
  const selectedCapabilityKey = selectedStep?.action?.capabilityKey ?? selectedStep?.capabilityKey;
2844
3025
  const selectedResourceId = selectedStep?.action?.resourceId ?? null;
2845
- const ConfigForm = selectedStep?.action?.ConfigForm;
3026
+ const stepSchema = selectedStep?.action?.schema;
3027
+ const stepLayout = selectedStep?.action?.layout;
2846
3028
  const recentRunsQuery = useListExecutions(list.id, { resourceId: selectedResourceId, limit: 5 });
2847
3029
  const recentRuns = useMemo(() => recentRunsQuery.data ?? [], [recentRunsQuery.data]);
2848
3030
  const canRunSelectedAction = !!selectedStep?.action && (selectedActionKind === "retry_failed" || selectedActionKind === "run_next_batch");
2849
3031
  const selectedActionIcon = selectedActionKind === "retry_failed" ? /* @__PURE__ */ jsx(IconRefresh, { size: 14 }) : /* @__PURE__ */ jsx(IconPlayerPlay, { size: 14 });
2850
3032
  useEffect(() => {
2851
- setStepConfigFormState(getInitialStepConfig(list, selectedStep?.action, selectedCapabilityKey));
2852
- }, [list, selectedCapabilityKey, selectedStep?.action]);
2853
- const handleSaveStepConfig = () => {
2854
- if (!selectedCapabilityKey) return;
2855
- updateListConfigMutation.mutate({
2856
- stepConfig: {
2857
- [selectedCapabilityKey]: stepConfigFormState
3033
+ setStepInputFormState(getInitialStepInput(list, selectedStep?.action));
3034
+ setStepInputError(null);
3035
+ setStepInputInitialized(false);
3036
+ setStepInputResetKey((k) => k + 1);
3037
+ }, [list, selectedStep?.action]);
3038
+ useEffect(() => {
3039
+ if (stepInputInitialized) return;
3040
+ if (recentRunsQuery.isLoading) return;
3041
+ if (recentRuns.length > 0) {
3042
+ const lastInput = getRunInput(recentRuns[0]);
3043
+ if (lastInput) {
3044
+ setStepInputFormState(sanitizeStepInput(asRecord2(lastInput)));
3045
+ setStepInputResetKey((k) => k + 1);
2858
3046
  }
2859
- });
3047
+ }
3048
+ setStepInputInitialized(true);
3049
+ }, [stepInputInitialized, recentRunsQuery.isLoading, recentRuns, selectedCapabilityKey]);
3050
+ const handleStepInputChange = (next) => {
3051
+ setStepInputFormState(next);
3052
+ if (stepInputError) setStepInputError(null);
2860
3053
  };
2861
3054
  const handleSelectedAction = () => {
2862
3055
  if (!selectedStep || !canRunSelectedAction) return;
2863
- const submittedConfig = ConfigForm ? stepConfigFormState : {};
3056
+ if (stepSchema) {
3057
+ const parsed = stepSchema.safeParse(stepInputFormState);
3058
+ if (!parsed.success) {
3059
+ const firstIssue = parsed.error.issues[0];
3060
+ setStepInputError(firstIssue?.message ?? "Validation failed");
3061
+ return;
3062
+ }
3063
+ }
3064
+ setStepInputError(null);
3065
+ const submittedInput = stepSchema ? stepInputFormState : {};
2864
3066
  if (selectedActionKind === "retry_failed") {
2865
- onRetryStep(selectedStep, submittedConfig);
3067
+ onRetryStep(selectedStep, submittedInput);
2866
3068
  return;
2867
3069
  }
2868
- onRunStep(selectedStep, submittedConfig);
3070
+ onRunStep(selectedStep, submittedInput);
2869
3071
  };
2870
3072
  const loadRecentRunInput = (run) => {
2871
- setStepConfigFormState(asRecord(getRunInput(run)));
3073
+ setStepInputFormState(sanitizeStepInput(asRecord2(getRunInput(run))));
3074
+ setStepInputResetKey((k) => k + 1);
3075
+ setActiveRightColumnTab("configuration");
2872
3076
  };
2873
3077
  return /* @__PURE__ */ jsx(
2874
3078
  TabSection,
@@ -2877,7 +3081,7 @@ function BuildTab({
2877
3081
  title: "Build",
2878
3082
  description: currentStep ? `${currentStep.label} is the current step in this list build.` : "No build steps are available for this list.",
2879
3083
  children: /* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 1, md: 2 }, spacing: "md", children: [
2880
- /* @__PURE__ */ jsx(Stack, { gap: "xs", children: steps.map((step, index) => {
3084
+ /* @__PURE__ */ jsx(Card, { withBorder: true, style: { height: "100%", display: "flex", flexDirection: "column" }, children: /* @__PURE__ */ jsx(Stack, { gap: "xs", style: { flex: 1, minHeight: 0 }, children: steps.map((step, index) => {
2881
3085
  const isCurrent = currentStep?.id === step.id;
2882
3086
  const isSelected = selectedStep?.id === step.id;
2883
3087
  const isWaiting = step.status === "blocked";
@@ -2888,7 +3092,7 @@ function BuildTab({
2888
3092
  {
2889
3093
  onClick: () => setSelectedStepId(step.id),
2890
3094
  style: {
2891
- backgroundColor: isSelected ? "var(--active-background)" : isPassiveWaiting ? "color-mix(in srgb, var(--color-surface-hover) 38%, transparent)" : "transparent",
3095
+ backgroundColor: isSelected ? "var(--surface-primary-strong)" : isPassiveWaiting ? "color-mix(in srgb, var(--color-surface-hover) 38%, transparent)" : "transparent",
2892
3096
  border: isSelected ? "var(--active-border)" : "1px solid var(--color-border)",
2893
3097
  borderStyle: isPassiveWaiting ? "dashed" : "solid",
2894
3098
  borderRadius: 8,
@@ -2918,101 +3122,123 @@ function BuildTab({
2918
3122
  children: index + 1
2919
3123
  }
2920
3124
  ),
2921
- /* @__PURE__ */ jsxs(Stack, { gap: 3, style: { minWidth: 0, flex: 1 }, children: [
3125
+ /* @__PURE__ */ jsxs(Stack, { gap: 4, style: { minWidth: 0, flex: 1 }, children: [
2922
3126
  /* @__PURE__ */ jsxs(Group, { gap: "xs", justify: "space-between", wrap: "nowrap", children: [
2923
3127
  /* @__PURE__ */ jsx(Text, { fw: 700, c: isPassiveWaiting ? "dimmed" : void 0, truncate: true, children: step.label }),
2924
3128
  isCurrent ? /* @__PURE__ */ jsx(StatusPill, { label: "Current", tone: step.status }) : null,
2925
3129
  !isCurrent && isWaiting ? /* @__PURE__ */ jsx(StatusPill, { label: "Waiting", tone: "blocked" }) : null
2926
3130
  ] }),
2927
- /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: getStepCompactSummary(step) })
3131
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", lineClamp: 2, children: step.description }),
3132
+ /* @__PURE__ */ jsx(StepCounterLine, { step })
2928
3133
  ] })
2929
3134
  ] })
2930
3135
  },
2931
3136
  step.id
2932
3137
  );
2933
- }) }),
2934
- selectedStep ? /* @__PURE__ */ jsx(Card, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
2935
- /* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "flex-start", gap: "sm", children: [
2936
- /* @__PURE__ */ jsxs(Stack, { gap: 2, style: { minWidth: 0 }, children: [
2937
- /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", fw: 700, tt: "uppercase", children: [
2938
- "Step ",
2939
- selectedStepIndex + 1,
2940
- " details"
2941
- ] }),
2942
- /* @__PURE__ */ jsx(Title, { order: 5, children: selectedStep.label })
3138
+ }) }) }),
3139
+ 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: [
3140
+ /* @__PURE__ */ jsxs(Stack, { gap: 2, style: { minWidth: 0, flexShrink: 0 }, children: [
3141
+ /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", fw: 700, tt: "uppercase", children: [
3142
+ "Step ",
3143
+ selectedStepIndex + 1,
3144
+ " details"
2943
3145
  ] }),
2944
- /* @__PURE__ */ jsx(StatusPill, { label: getBuildStatusLabel(selectedStep.status), tone: selectedStep.status })
2945
- ] }),
2946
- /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: selectedStep.description }),
2947
- selectedStep.status === "blocked" ? /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: selectedStep.emptyBlockedText }) : null,
2948
- /* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 2, sm: 4 }, spacing: "xs", children: [
2949
- /* @__PURE__ */ jsx(CountBadge, { label: "Ready", value: selectedStep.ready, tone: "ready" }),
2950
- /* @__PURE__ */ jsx(CountBadge, { label: "Complete", value: selectedStep.complete, tone: "complete" }),
2951
- /* @__PURE__ */ jsx(CountBadge, { label: "Failed", value: selectedStep.failed, tone: "failed" }),
2952
- /* @__PURE__ */ jsx(CountBadge, { label: "Waiting", value: selectedStep.blocked, tone: "blocked" })
3146
+ /* @__PURE__ */ jsx(Title, { order: 5, children: selectedStep.label })
2953
3147
  ] }),
2954
3148
  /* @__PURE__ */ jsx(BuildStepProgressBar, { step: selectedStep }),
2955
- selectedStep.recommendedAction?.defaultSize !== void 0 && selectedStep.recommendedAction.maxSize !== void 0 ? /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
2956
- "Batch size: ",
2957
- selectedStep.recommendedAction.defaultSize,
2958
- " default,",
2959
- " ",
2960
- selectedStep.recommendedAction.maxSize,
2961
- " max."
2962
- ] }) : null,
2963
- ConfigForm ? /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
2964
- /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", fw: 700, tt: "uppercase", children: "Configure" }),
2965
- /* @__PURE__ */ jsx(ConfigForm, { value: stepConfigFormState, onChange: setStepConfigFormState }),
2966
- /* @__PURE__ */ jsx(Group, { justify: "flex-end", children: /* @__PURE__ */ jsx(
2967
- Button,
2968
- {
2969
- size: "xs",
2970
- variant: "light",
2971
- loading: updateListConfigMutation.isPending,
2972
- onClick: handleSaveStepConfig,
2973
- children: "Save"
2974
- }
2975
- ) })
2976
- ] }) : null,
2977
- /* @__PURE__ */ jsxs(Group, { gap: "xs", justify: "flex-end", wrap: "nowrap", children: [
2978
- /* @__PURE__ */ jsx(
2979
- Button,
2980
- {
2981
- leftSection: selectedActionIcon,
2982
- disabled: !canRunSelectedAction,
2983
- onClick: handleSelectedAction,
2984
- children: selectedActionKind === "none" ? "No action" : getStepActionLabel(selectedActionKind)
2985
- }
2986
- ),
2987
- /* @__PURE__ */ jsx(Button, { variant: "light", leftSection: /* @__PURE__ */ jsx(IconPlayerPlay, { size: 14 }), onClick: onViewRuns, children: "View runs" })
2988
- ] }),
2989
- selectedResourceId ? /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
2990
- /* @__PURE__ */ jsxs(Group, { justify: "space-between", gap: "xs", children: [
2991
- /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", fw: 700, tt: "uppercase", children: "Recent runs" }),
2992
- recentRunsQuery.isFetching ? /* @__PURE__ */ jsx(Loader, { size: "xs" }) : null
2993
- ] }),
2994
- !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) => /* @__PURE__ */ jsx(
2995
- UnstyledButton,
2996
- {
2997
- onClick: () => loadRecentRunInput(run),
2998
- style: {
2999
- border: "1px solid var(--color-border)",
3000
- borderRadius: 8,
3001
- padding: "8px 10px",
3002
- textAlign: "left",
3003
- width: "100%"
3149
+ /* @__PURE__ */ jsx(
3150
+ StepDetailRightColumn,
3151
+ {
3152
+ activeTab: activeRightColumnTab,
3153
+ onTabChange: setActiveRightColumnTab,
3154
+ configuration: /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
3155
+ selectedStep.status === "blocked" ? /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: selectedStep.emptyBlockedText }) : null,
3156
+ selectedStep.outputs.includes(selectedStep.primaryEntity) && selectedStep.ready === 0 && selectedStep.complete === 0 && selectedStep.failed === 0 && selectedStep.blocked === 0 ? /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "Run this step to populate members." }) : null,
3157
+ !selectedStep.outputs.includes(selectedStep.primaryEntity) && selectedStep.recommendedAction?.defaultSize !== void 0 && selectedStep.recommendedAction.maxSize !== void 0 ? /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
3158
+ "Batch size: ",
3159
+ selectedStep.recommendedAction.defaultSize,
3160
+ " default,",
3161
+ " ",
3162
+ selectedStep.recommendedAction.maxSize,
3163
+ " max."
3164
+ ] }) : null,
3165
+ stepSchema && stepLayout ? /* @__PURE__ */ jsx(
3166
+ StepConfigForm,
3167
+ {
3168
+ slot: "main",
3169
+ schema: stepSchema,
3170
+ layout: stepLayout,
3171
+ value: stepInputFormState,
3172
+ onChange: (v) => handleStepInputChange(asRecord2(v) ?? {}),
3173
+ disabled: !canRunSelectedAction
3174
+ },
3175
+ `${selectedResourceId ?? "none"}-main-${stepInputResetKey}`
3176
+ ) : /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "No configuration for this step." })
3177
+ ] }),
3178
+ advanced: stepSchema && stepLayout?.advanced ? /* @__PURE__ */ jsx(
3179
+ StepConfigForm,
3180
+ {
3181
+ slot: "advanced",
3182
+ schema: stepSchema,
3183
+ layout: stepLayout,
3184
+ value: stepInputFormState,
3185
+ onChange: (v) => handleStepInputChange(asRecord2(v) ?? {}),
3186
+ disabled: !canRunSelectedAction
3004
3187
  },
3005
- children: /* @__PURE__ */ jsxs(Group, { justify: "space-between", gap: "xs", wrap: "nowrap", children: [
3006
- /* @__PURE__ */ jsxs(Stack, { gap: 2, style: { minWidth: 0 }, children: [
3007
- /* @__PURE__ */ jsx(Text, { size: "sm", fw: 600, truncate: true, children: formatDateTime4(run.createdAt) }),
3008
- /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", ff: "monospace", truncate: true, children: run.executionId })
3009
- ] }),
3010
- /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: getRunStatusColor(run.status), children: run.status })
3188
+ `${selectedResourceId ?? "none"}-advanced-${stepInputResetKey}`
3189
+ ) : void 0,
3190
+ runs: selectedResourceId ? /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
3191
+ /* @__PURE__ */ jsxs(Group, { justify: "space-between", gap: "xs", children: [
3192
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", fw: 700, tt: "uppercase", children: "Recent runs" }),
3193
+ recentRunsQuery.isFetching ? /* @__PURE__ */ jsx(Loader, { size: "xs" }) : null
3194
+ ] }),
3195
+ !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) => /* @__PURE__ */ jsx(
3196
+ UnstyledButton,
3197
+ {
3198
+ onClick: () => loadRecentRunInput(run),
3199
+ style: {
3200
+ border: "1px solid var(--color-border)",
3201
+ borderRadius: 8,
3202
+ padding: "8px 10px",
3203
+ textAlign: "left",
3204
+ width: "100%"
3205
+ },
3206
+ children: /* @__PURE__ */ jsxs(Group, { justify: "space-between", gap: "xs", wrap: "nowrap", children: [
3207
+ /* @__PURE__ */ jsxs(Stack, { gap: 2, style: { minWidth: 0 }, children: [
3208
+ /* @__PURE__ */ jsx(Text, { size: "sm", fw: 600, truncate: true, children: formatDateTime4(run.createdAt) }),
3209
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", ff: "monospace", truncate: true, children: run.executionId })
3210
+ ] }),
3211
+ /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: getRunStatusColor(run.status), children: run.status })
3212
+ ] })
3213
+ },
3214
+ run.executionId
3215
+ )) })
3216
+ ] }) : /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "No runs available for this step." }),
3217
+ action: /* @__PURE__ */ jsxs(Fragment, { children: [
3218
+ stepInputError ? /* @__PURE__ */ jsx(Alert, { color: "red", icon: /* @__PURE__ */ jsx(IconAlertCircle, { size: 16 }), variant: "light", children: stepInputError }) : null,
3219
+ /* @__PURE__ */ jsxs(Group, { gap: "xs", justify: "flex-end", wrap: "nowrap", children: [
3220
+ /* @__PURE__ */ jsx(
3221
+ Button,
3222
+ {
3223
+ leftSection: selectedActionIcon,
3224
+ disabled: !canRunSelectedAction,
3225
+ onClick: handleSelectedAction,
3226
+ children: selectedActionKind === "none" ? "No action" : getStepActionLabel(selectedActionKind)
3227
+ }
3228
+ ),
3229
+ recentRuns.length > 0 ? /* @__PURE__ */ jsx(
3230
+ Button,
3231
+ {
3232
+ variant: "light",
3233
+ leftSection: /* @__PURE__ */ jsx(IconPlayerPlay, { size: 14 }),
3234
+ onClick: () => onViewRuns(selectedResourceId),
3235
+ children: "View runs"
3236
+ }
3237
+ ) : null
3011
3238
  ] })
3012
- },
3013
- run.executionId
3014
- )) })
3015
- ] }) : null
3239
+ ] })
3240
+ }
3241
+ )
3016
3242
  ] }) }) : null
3017
3243
  ] })
3018
3244
  }
@@ -3128,25 +3354,22 @@ function LeadGenListDetailPage({ listId }) {
3128
3354
  const listQuery = useList(listId);
3129
3355
  const progressQuery = useListProgress(listId);
3130
3356
  const deleteListMutation = useDeleteList();
3131
- const updateListMutation = useUpdateList(listId);
3132
- const updateListConfigMutation = useUpdateListConfig(listId);
3133
3357
  const [deleteModalOpen, setDeleteModalOpen] = useState(false);
3134
- const [pipelineModalOpen, setPipelineModalOpen] = useState(false);
3135
- const [selectedBuildTemplateId, setSelectedBuildTemplateId] = useState(DEFAULT_PROSPECTING_BUILD_TEMPLATE_ID);
3136
3358
  const [runModalOpen, setRunModalOpen] = useState(false);
3137
3359
  const [activeTab, setActiveTab] = useState("overview");
3360
+ const [runsResourceFilter, setRunsResourceFilter] = useState(null);
3138
3361
  const [initialRunResourceId, setInitialRunResourceId] = useState(null);
3139
- const [pendingStepRunConfig, setPendingStepRunConfig] = useState(null);
3362
+ const [pendingStepRunInput, setPendingStepRunInput] = useState(null);
3140
3363
  const runModalActions = useMemo(() => {
3141
- if (!pendingStepRunConfig) return actions;
3364
+ if (!pendingStepRunInput) return actions;
3142
3365
  return actions.map((action) => {
3143
- if (action.resourceId !== pendingStepRunConfig.resourceId) return action;
3366
+ if (action.resourceId !== pendingStepRunInput.resourceId) return action;
3144
3367
  return {
3145
3368
  ...action,
3146
- defaultInput: (nextList) => mergeStepRunInput(nextList, action, pendingStepRunConfig.capabilityKey, pendingStepRunConfig.formStateAtSubmit)
3369
+ defaultInput: (nextList) => mergeStepRunInput(nextList, action, pendingStepRunInput.inputAtSubmit)
3147
3370
  };
3148
3371
  });
3149
- }, [actions, pendingStepRunConfig]);
3372
+ }, [actions, pendingStepRunInput]);
3150
3373
  const isLoading = listQuery.isLoading || progressQuery.isLoading;
3151
3374
  const error = listQuery.error ?? progressQuery.error;
3152
3375
  const backButton = /* @__PURE__ */ jsx(
@@ -3200,12 +3423,7 @@ function LeadGenListDetailPage({ listId }) {
3200
3423
  }
3201
3424
  const list = listQuery.data;
3202
3425
  const progress = progressQuery.data;
3203
- const currentBuildTemplateId = list.metadata.buildPlanSnapshot?.templateId ?? DEFAULT_PROSPECTING_BUILD_TEMPLATE_ID;
3204
3426
  const currentBuildTemplateLabel = list.metadata.buildPlanSnapshot?.templateLabel ?? "Default lead generation";
3205
- const selectedBuildTemplate = PROSPECTING_BUILD_TEMPLATE_OPTIONS.find(
3206
- (template) => template.id === selectedBuildTemplateId
3207
- );
3208
- const isSameBuildTemplate = selectedBuildTemplateId === currentBuildTemplateId;
3209
3427
  const buildResolution = resolveBuildState(list, progress, actions);
3210
3428
  const buildSteps = buildResolution.steps;
3211
3429
  const recommendedBuildStep = buildResolution.recommendedStep;
@@ -3217,17 +3435,16 @@ function LeadGenListDetailPage({ listId }) {
3217
3435
  const closeRunModal = () => {
3218
3436
  setRunModalOpen(false);
3219
3437
  setInitialRunResourceId(null);
3220
- setPendingStepRunConfig(null);
3438
+ setPendingStepRunInput(null);
3221
3439
  };
3222
- const openStepRun = (step, formStateAtSubmit) => {
3223
- if (step.action?.ConfigForm) {
3224
- setPendingStepRunConfig({
3440
+ const openStepRun = (step, inputAtSubmit) => {
3441
+ if (step.action?.schema) {
3442
+ setPendingStepRunInput({
3225
3443
  resourceId: step.action.resourceId,
3226
- capabilityKey: step.action.capabilityKey ?? step.capabilityKey,
3227
- formStateAtSubmit
3444
+ inputAtSubmit
3228
3445
  });
3229
3446
  } else {
3230
- setPendingStepRunConfig(null);
3447
+ setPendingStepRunInput(null);
3231
3448
  }
3232
3449
  openRunModal(step.action?.resourceId ?? null);
3233
3450
  };
@@ -3243,39 +3460,6 @@ function LeadGenListDetailPage({ listId }) {
3243
3460
  }
3244
3461
  });
3245
3462
  };
3246
- const openPipelineModal = () => {
3247
- setSelectedBuildTemplateId(currentBuildTemplateId);
3248
- setPipelineModalOpen(true);
3249
- };
3250
- const closePipelineModal = () => {
3251
- if (updateListMutation.isPending) return;
3252
- setPipelineModalOpen(false);
3253
- setSelectedBuildTemplateId(currentBuildTemplateId);
3254
- };
3255
- const handleChangePipeline = () => {
3256
- if (isSameBuildTemplate) return;
3257
- updateListMutation.mutate(
3258
- {
3259
- buildTemplateId: selectedBuildTemplateId,
3260
- confirmBuildTemplateChange: true
3261
- },
3262
- {
3263
- onSuccess: () => {
3264
- setPipelineModalOpen(false);
3265
- }
3266
- }
3267
- );
3268
- };
3269
- const persistSubmittedStepConfig = (resourceId) => {
3270
- if (!pendingStepRunConfig || pendingStepRunConfig.resourceId !== resourceId || !pendingStepRunConfig.capabilityKey) {
3271
- return;
3272
- }
3273
- updateListConfigMutation.mutate({
3274
- stepConfig: {
3275
- [pendingStepRunConfig.capabilityKey]: pendingStepRunConfig.formStateAtSubmit
3276
- }
3277
- });
3278
- };
3279
3463
  return /* @__PURE__ */ jsxs(SubshellContentContainer, { children: [
3280
3464
  /* @__PURE__ */ jsx(PageContainer, { children: /* @__PURE__ */ jsxs(Stack, { children: [
3281
3465
  /* @__PURE__ */ jsx(
@@ -3295,16 +3479,6 @@ function LeadGenListDetailPage({ listId }) {
3295
3479
  ] })
3296
3480
  ] }),
3297
3481
  /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
3298
- /* @__PURE__ */ jsx(
3299
- Button,
3300
- {
3301
- size: "xs",
3302
- variant: "light",
3303
- leftSection: /* @__PURE__ */ jsx(IconSwitchHorizontal, { size: 14 }),
3304
- onClick: openPipelineModal,
3305
- children: "Change pipeline"
3306
- }
3307
- ),
3308
3482
  /* @__PURE__ */ jsxs(Group, { gap: 4, wrap: "nowrap", onClick: handleCopyListCommand, style: { cursor: "pointer" }, children: [
3309
3483
  /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", ff: "monospace", children: list.id }),
3310
3484
  /* @__PURE__ */ jsx(ActionIcon, { variant: "subtle", size: "sm", "aria-label": "Copy list command", children: /* @__PURE__ */ jsx(IconCopy, { size: 14 }) })
@@ -3341,56 +3515,24 @@ function LeadGenListDetailPage({ listId }) {
3341
3515
  recommendedStep: recommendedBuildStep,
3342
3516
  onRunStep: openStepRun,
3343
3517
  onRetryStep: openStepRun,
3344
- onViewRuns: () => setActiveTab("runs")
3518
+ onViewRuns: (resourceId) => {
3519
+ setRunsResourceFilter(resourceId);
3520
+ setActiveTab("runs");
3521
+ }
3345
3522
  }
3346
3523
  ) }),
3347
- /* @__PURE__ */ jsx(Tabs.Panel, { value: "runs", pt: "sm", children: /* @__PURE__ */ jsx(WorkflowRunsPanel, { listId, title: "Runs" }) })
3524
+ /* @__PURE__ */ jsx(Tabs.Panel, { value: "runs", pt: "sm", children: /* @__PURE__ */ jsx(
3525
+ WorkflowRunsPanel,
3526
+ {
3527
+ listId,
3528
+ title: "Runs",
3529
+ resourceFilter: runsResourceFilter,
3530
+ onClearResourceFilter: () => setRunsResourceFilter(null)
3531
+ }
3532
+ ) })
3348
3533
  ] }) })
3349
3534
  ] }) }),
3350
3535
  /* @__PURE__ */ jsx(ListMemberDrawer, { memberId, memberKind, listId, onClose: handleDrawerClose }),
3351
- /* @__PURE__ */ jsx(
3352
- CustomModal,
3353
- {
3354
- opened: pipelineModalOpen,
3355
- onClose: closePipelineModal,
3356
- size: "md",
3357
- loading: updateListMutation.isPending,
3358
- children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
3359
- /* @__PURE__ */ jsxs(Group, { gap: "sm", children: [
3360
- /* @__PURE__ */ jsx(IconSwitchHorizontal, { size: 22 }),
3361
- /* @__PURE__ */ jsx(Title, { order: 4, children: "Change build pipeline" })
3362
- ] }),
3363
- /* @__PURE__ */ jsx(Alert, { icon: /* @__PURE__ */ jsx(IconAlertCircle, { size: 16 }), color: "yellow", variant: "light", children: /* @__PURE__ */ jsx(Text, { size: "sm", children: "Changing the pipeline replaces this list's build plan snapshot. Existing records stay in place, but future build steps on the Build tab will follow the newly selected pipeline." }) }),
3364
- /* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
3365
- /* @__PURE__ */ jsx(
3366
- Select,
3367
- {
3368
- label: "Build Pipeline",
3369
- data: BUILD_TEMPLATE_SELECT_OPTIONS2,
3370
- value: selectedBuildTemplateId,
3371
- onChange: (value) => setSelectedBuildTemplateId(value ?? currentBuildTemplateId),
3372
- disabled: updateListMutation.isPending,
3373
- required: true
3374
- }
3375
- ),
3376
- selectedBuildTemplate?.description ? /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: selectedBuildTemplate.description }) : null
3377
- ] }),
3378
- /* @__PURE__ */ jsxs(Group, { justify: "flex-end", mt: "md", children: [
3379
- /* @__PURE__ */ jsx(Button, { variant: "light", onClick: closePipelineModal, disabled: updateListMutation.isPending, children: "Cancel" }),
3380
- /* @__PURE__ */ jsx(
3381
- Button,
3382
- {
3383
- color: "yellow",
3384
- loading: updateListMutation.isPending,
3385
- disabled: isSameBuildTemplate,
3386
- onClick: handleChangePipeline,
3387
- children: "Confirm pipeline change"
3388
- }
3389
- )
3390
- ] })
3391
- ] })
3392
- }
3393
- ),
3394
3536
  /* @__PURE__ */ jsx(
3395
3537
  CustomModal,
3396
3538
  {
@@ -3429,8 +3571,7 @@ function LeadGenListDetailPage({ listId }) {
3429
3571
  title: "Run build step",
3430
3572
  description: "Run the selected bounded build workflow for this list.",
3431
3573
  lockResourceSelection: true,
3432
- onSubmitted: (resourceId, executionId) => {
3433
- persistSubmittedStepConfig(resourceId);
3574
+ onSubmitted: (_, executionId) => {
3434
3575
  showSuccessNotification(`Workflow run started: ${executionId}`);
3435
3576
  }
3436
3577
  }
@@ -3795,4 +3936,4 @@ function LeadGenContactsPage() {
3795
3936
  ] }) });
3796
3937
  }
3797
3938
 
3798
- export { CompanyCleanupForm, CompanyDetailModal, ContactDetailModal, EmailDiscoveryForm, EmailVerificationForm, LEAD_GEN_ITEMS, LEAD_GEN_ROUTE_LINKS, LeadGenCompaniesPage, LeadGenContactsPage, LeadGenListDetailPage, LeadGenListsPage, LeadGenOverviewPage, LeadGenRouteShell, LeadGenSidebar, LeadGenSidebarMiddle, LeadGenSidebarTop, ListBuilderIndexPage, ListBuilderPage, RunWorkflowModal, TabSection, WebsiteExtractForm, formatDate, getEnrichmentColor, getEnrichmentStatus, getStateKeyColor, getStatusColor, leadGenManifest, useDeleteLists };
3939
+ export { CompanyDetailModal, ContactDetailModal, LEAD_GEN_ITEMS, LEAD_GEN_ROUTE_LINKS, LeadGenCompaniesPage, LeadGenContactsPage, LeadGenListDetailPage, LeadGenListsPage, LeadGenOverviewPage, LeadGenRouteShell, LeadGenSidebar, LeadGenSidebarMiddle, LeadGenSidebarTop, ListBuilderIndexPage, ListBuilderPage, RunWorkflowModal, StepConfigForm, TabSection, formatDate, getEnrichmentColor, getEnrichmentStatus, getStateKeyColor, getStatusColor, leadGenManifest, useDeleteLists };