@elevasis/ui 2.25.5 → 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 (101) hide show
  1. package/dist/api/index.js +2 -2
  2. package/dist/app/index.css +18 -29
  3. package/dist/app/index.d.ts +118 -64
  4. package/dist/app/index.js +6 -5
  5. package/dist/charts/index.js +6 -5
  6. package/dist/chunk-3MEXPLWT.js +265 -0
  7. package/dist/{chunk-IS53MXE4.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-NHHCUECV.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-VMAWXEVG.js → chunk-JKBGDFX2.js} +1136 -828
  14. package/dist/{chunk-MU4VPAMR.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-QNCVK3ZF.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-KINQW4JT.js → chunk-UPMX5GJI.js} +5 -5
  25. package/dist/{chunk-N2AP4I5N.js → chunk-UY5I2KOZ.js} +223 -3857
  26. package/dist/{chunk-JMI7L7Y7.js → chunk-W2ZTLH7Y.js} +142 -4
  27. package/dist/{chunk-3KY2GNPE.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-F7JDHS2I.js → chunk-XZSEPJZQ.js} +5 -5
  32. package/dist/{chunk-5BJXMZN4.js → chunk-YHBPR67D.js} +438 -620
  33. package/dist/{chunk-FVKLHLF4.js → chunk-YO2YORW4.js} +4 -4
  34. package/dist/{chunk-TAIX4NO3.js → chunk-ZFLM2YVW.js} +2 -2
  35. package/dist/components/index.css +18 -29
  36. package/dist/components/index.d.ts +211 -383
  37. package/dist/components/index.js +43 -427
  38. package/dist/components/navigation/index.css +28 -39
  39. package/dist/execution/index.d.ts +0 -73
  40. package/dist/features/auth/index.css +28 -39
  41. package/dist/features/crm/index.css +28 -39
  42. package/dist/features/crm/index.d.ts +49 -49
  43. package/dist/features/crm/index.js +14 -14
  44. package/dist/features/dashboard/index.css +28 -39
  45. package/dist/features/dashboard/index.js +18 -15
  46. package/dist/features/delivery/index.css +18 -29
  47. package/dist/features/delivery/index.js +14 -14
  48. package/dist/features/knowledge/index.css +611 -0
  49. package/dist/features/knowledge/index.js +700 -1
  50. package/dist/features/lead-gen/index.css +28 -39
  51. package/dist/features/lead-gen/index.d.ts +212 -166
  52. package/dist/features/lead-gen/index.js +16 -15
  53. package/dist/features/monitoring/index.css +18 -29
  54. package/dist/features/monitoring/index.js +17 -16
  55. package/dist/features/monitoring/requests/index.css +28 -39
  56. package/dist/features/monitoring/requests/index.js +13 -13
  57. package/dist/features/operations/index.css +28 -39
  58. package/dist/features/operations/index.d.ts +16 -98
  59. package/dist/features/operations/index.js +26 -20
  60. package/dist/features/settings/index.css +28 -39
  61. package/dist/features/settings/index.d.ts +1 -0
  62. package/dist/features/settings/index.js +15 -15
  63. package/dist/hooks/delivery/index.css +28 -39
  64. package/dist/hooks/delivery/index.js +2 -2
  65. package/dist/hooks/index.css +18 -29
  66. package/dist/hooks/index.d.ts +180 -377
  67. package/dist/hooks/index.js +13 -13
  68. package/dist/hooks/published.css +18 -29
  69. package/dist/hooks/published.d.ts +180 -377
  70. package/dist/hooks/published.js +13 -13
  71. package/dist/index.css +18 -29
  72. package/dist/index.d.ts +1549 -946
  73. package/dist/index.js +15 -14
  74. package/dist/initialization/index.d.ts +1 -0
  75. package/dist/knowledge/index.d.ts +991 -41
  76. package/dist/knowledge/index.js +5469 -413
  77. package/dist/layout/index.d.ts +2 -0
  78. package/dist/layout/index.js +3 -2
  79. package/dist/organization/index.css +28 -39
  80. package/dist/organization/index.d.ts +1 -0
  81. package/dist/provider/index.css +28 -39
  82. package/dist/provider/index.d.ts +1147 -348
  83. package/dist/provider/index.js +11 -10
  84. package/dist/provider/published.css +28 -39
  85. package/dist/provider/published.d.ts +1146 -347
  86. package/dist/provider/published.js +8 -8
  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 +46 -33
  99. package/src/knowledge/README.md +8 -8
  100. package/src/theme/presets/README.md +19 -0
  101. /package/dist/{chunk-SGXXJE52.js → chunk-QD4X4H5A.js} +0 -0
@@ -1,24 +1,26 @@
1
- import { DEFAULT_ORGANIZATION_MODEL_PROSPECTING } from './chunk-IS53MXE4.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, useCompanyFacets, useCompanies, useDeleteCompanies, useContacts, useDeleteContacts, useListMembers, useListMember, useTransitionListMember, useDeriveActions, useArtifacts } from './chunk-5BJXMZN4.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, LEAD_GEN_STAGE_CATALOG, LEAD_GEN_PIPELINE_DEFINITIONS, findPipeline } from './chunk-JMI7L7Y7.js';
13
14
  import { useRouterContext } from './chunk-Q7DJKLEN.js';
14
15
  import { useElevasisServices } from './chunk-5WWZXCS5.js';
15
- import { Stack, Paper, Text, Anchor, Group, Title, 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';
16
- import { IconLayoutGrid, IconList, IconBuilding, IconAddressBook, IconTarget, IconExternalLink, IconX, IconAlertCircle, IconPlayerPlay, IconArrowRight, IconSparkles, IconListDetails, IconPlus, IconSearch, IconAlertTriangle, IconLayoutDashboard, IconBolt, IconCopy, IconUsers, IconSwitchHorizontal, IconTrash, IconBuildingFactory2, IconArrowLeft, IconRefresh, IconChevronDown, IconChevronRight, 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';
17
18
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
18
19
  import { Link, useNavigate, useSearch } from '@tanstack/react-router';
19
- import { useQueryClient, useMutation } from '@tanstack/react-query';
20
- import { useState, useMemo, useEffect, Fragment as Fragment$1 } from 'react';
20
+ import { useState, useRef, useMemo, useEffect, Fragment as Fragment$1 } from 'react';
21
21
  import { useForm } from '@mantine/form';
22
+ import { zod4Resolver } from 'mantine-form-zod-resolver';
23
+ import { useQueryClient, useMutation } from '@tanstack/react-query';
22
24
 
23
25
  var LeadGenSidebarTop = () => {
24
26
  return /* @__PURE__ */ jsx(SubshellSidebarSection, { icon: IconTarget, label: "Lead Gen" });
@@ -93,6 +95,299 @@ function createBuildPlanSnapshotFromTemplateId(templateId) {
93
95
  })
94
96
  };
95
97
  }
98
+ function TabSection({ icon, title, description, rightSection, children }) {
99
+ const wrappedIcon = icon ? /* @__PURE__ */ jsx(ThemeIcon, { size: "md", variant: "light", children: icon }) : null;
100
+ return /* @__PURE__ */ jsxs(Stack, { gap: "sm", p: "md", children: [
101
+ /* @__PURE__ */ jsxs(Group, { justify: "space-between", gap: "md", align: "flex-start", wrap: "nowrap", children: [
102
+ /* @__PURE__ */ jsxs(Stack, { gap: 2, style: { minWidth: 0 }, children: [
103
+ /* @__PURE__ */ jsxs(Group, { gap: "xs", wrap: "nowrap", children: [
104
+ wrappedIcon,
105
+ /* @__PURE__ */ jsx(Title, { order: 3, style: { overflowWrap: "anywhere" }, children: title })
106
+ ] }),
107
+ description ? /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", style: { overflowWrap: "anywhere" }, children: description }) : null
108
+ ] }),
109
+ rightSection
110
+ ] }),
111
+ children
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
+ }
96
391
  var LEAD_GEN_ROUTE_LINKS = [
97
392
  { label: "Overview", to: "/lead-gen" },
98
393
  { label: "Lists", to: "/lead-gen/lists" },
@@ -1084,6 +1379,61 @@ function ListMemberDrawer({ memberId, memberKind, listId, onClose }) {
1084
1379
  }
1085
1380
  );
1086
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
+ }
1087
1437
  function ListBuilderIndexPage() {
1088
1438
  const navigate = useNavigate();
1089
1439
  const [query, setQuery] = useState("");
@@ -1491,6 +1841,9 @@ function PipelineFunnel({ progress, pipelineConfig, actions }) {
1491
1841
  )
1492
1842
  ] }) });
1493
1843
  }
1844
+ function asRecord(value) {
1845
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
1846
+ }
1494
1847
  function selectedCount(selection) {
1495
1848
  return selection.selectedCompanyIds.length + selection.selectedContactIds.length;
1496
1849
  }
@@ -1515,7 +1868,9 @@ function RunWorkflowModal({
1515
1868
  onResourceChange,
1516
1869
  onSubmitted
1517
1870
  }) {
1518
- const [selectedResourceId, setSelectedResourceId] = useState(initialResourceId ?? actions[0]?.resourceId ?? null);
1871
+ const [selectedResourceId, setSelectedResourceId] = useState(
1872
+ initialResourceId ?? actions[0]?.resourceId ?? null
1873
+ );
1519
1874
  const selectedAction = useMemo(
1520
1875
  () => actions.find((action) => action.resourceId === selectedResourceId) ?? actions[0],
1521
1876
  [actions, selectedResourceId]
@@ -1551,57 +1906,88 @@ function RunWorkflowModal({
1551
1906
  showApiErrorNotification(error);
1552
1907
  }
1553
1908
  };
1554
- const Form = selectedAction?.inputForm;
1555
1909
  const count = selectedCount(selection);
1556
- return /* @__PURE__ */ jsx(CustomModal, { opened, onClose: () => !execution.isPending && onClose(), size: "lg", loading: execution.isPending, children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
1557
- /* @__PURE__ */ jsxs(Group, { gap: "sm", children: [
1558
- /* @__PURE__ */ jsx(IconBolt, { size: 22 }),
1559
- /* @__PURE__ */ jsxs("div", { children: [
1560
- /* @__PURE__ */ jsx(Text, { fw: 600, children: title }),
1561
- /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: description })
1562
- ] })
1563
- ] }),
1564
- !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: [
1565
- /* @__PURE__ */ jsx(
1566
- Select,
1567
- {
1568
- label: "Workflow",
1569
- data: actionOptions,
1570
- value: selectedAction?.resourceId ?? null,
1571
- onChange: handleResourceChange,
1572
- disabled: execution.isPending || lockResourceSelection,
1573
- searchable: true
1574
- }
1575
- ),
1576
- selectedAction ? /* @__PURE__ */ jsxs(Stack, { gap: 6, children: [
1577
- /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
1578
- /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", children: selectedAction.category }),
1579
- 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
+ ] })
1580
1924
  ] }),
1581
- /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: selectedAction.description })
1582
- ] }) : null,
1583
- count > 0 ? /* @__PURE__ */ jsxs(Alert, { color: "blue", variant: "light", children: [
1584
- "This run includes ",
1585
- selection.selectedCompanyIds.length,
1586
- " selected companies and",
1587
- " ",
1588
- selection.selectedContactIds.length,
1589
- " selected contacts."
1590
- ] }) : null,
1591
- Form && selectedAction ? /* @__PURE__ */ jsx(Form, { list, onSubmit: submitInput }) : selectedAction ? /* @__PURE__ */ jsx(Group, { justify: "flex-end", children: /* @__PURE__ */ jsxs(
1592
- Button,
1593
- {
1594
- leftSection: /* @__PURE__ */ jsx(IconPlayerPlay, { size: 16 }),
1595
- loading: execution.isPending,
1596
- onClick: () => void submitInput(selectedAction.defaultInput?.(list) ?? { listId: list.id }),
1597
- children: [
1598
- "Run ",
1599
- selectedAction.label
1600
- ]
1601
- }
1602
- ) }) : null
1603
- ] })
1604
- ] }) });
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
+ ) });
1605
1991
  }
1606
1992
  function formatDateTime2(value) {
1607
1993
  if (!value) return "Not yet";
@@ -1651,117 +2037,127 @@ function getSummaryKeys(value) {
1651
2037
  if (!hasObjectContent(value)) return [];
1652
2038
  return Object.keys(value).slice(0, 6);
1653
2039
  }
1654
- function WorkflowRunsPanel({ listId, title = "Workflow Runs" }) {
2040
+ function WorkflowRunsPanel({
2041
+ listId,
2042
+ title = "Workflow Runs",
2043
+ resourceFilter,
2044
+ onClearResourceFilter
2045
+ }) {
1655
2046
  const navigate = useNavigate();
1656
- const executionsQuery = useListExecutions(listId);
2047
+ const executionsQuery = useListExecutions(listId, resourceFilter ? { resourceId: resourceFilter } : {});
1657
2048
  const [expandedExecutionId, setExpandedExecutionId] = useState(null);
1658
2049
  const executions = useMemo(() => executionsQuery.data ?? [], [executionsQuery.data]);
1659
2050
  const openWorkflow = (resourceId) => {
1660
2051
  void navigate({ to: "/operations/resources/workflow/$workflowId", params: { workflowId: resourceId } });
1661
2052
  };
1662
- return /* @__PURE__ */ jsxs(Stack, { gap: "sm", p: "md", children: [
1663
- /* @__PURE__ */ jsx(
1664
- CardHeader,
1665
- {
1666
- icon: /* @__PURE__ */ jsx(IconPlayerPlay, { size: 16 }),
1667
- title,
1668
- rightSection: /* @__PURE__ */ jsx(Tooltip, { label: "Refresh runs", children: /* @__PURE__ */ jsx(
1669
- ActionIcon,
1670
- {
1671
- variant: "subtle",
1672
- size: "sm",
1673
- "aria-label": "Refresh workflow runs",
1674
- loading: executionsQuery.isFetching,
1675
- onClick: () => void executionsQuery.refetch(),
1676
- children: /* @__PURE__ */ jsx(IconRefresh, { size: 14 })
1677
- }
1678
- ) })
1679
- }
1680
- ),
1681
- 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: [
1682
- /* @__PURE__ */ jsx(Table.Thead, { children: /* @__PURE__ */ jsxs(Table.Tr, { children: [
1683
- /* @__PURE__ */ jsx(Table.Th, {}),
1684
- /* @__PURE__ */ jsx(Table.Th, { children: "Workflow" }),
1685
- /* @__PURE__ */ jsx(Table.Th, { children: "Status" }),
1686
- /* @__PURE__ */ jsx(Table.Th, { children: "Started" }),
1687
- /* @__PURE__ */ jsx(Table.Th, { children: "Completed" }),
1688
- /* @__PURE__ */ jsx(Table.Th, { children: "Duration" })
1689
- ] }) }),
1690
- /* @__PURE__ */ jsx(Table.Tbody, { children: executions.map((execution) => {
1691
- const details = getRunDetails(execution);
1692
- const hasDetails = details.input !== void 0 || details.config !== void 0;
1693
- const expanded = expandedExecutionId === execution.executionId;
1694
- return /* @__PURE__ */ jsxs(Fragment$1, { children: [
1695
- /* @__PURE__ */ jsxs(Table.Tr, { children: [
1696
- /* @__PURE__ */ jsx(Table.Td, { w: 32, children: /* @__PURE__ */ jsx(
1697
- ThemeIcon,
1698
- {
1699
- variant: "light",
1700
- color: "blue",
1701
- size: "md",
1702
- role: hasDetails ? "button" : void 0,
1703
- tabIndex: hasDetails ? 0 : void 0,
1704
- "aria-label": hasDetails ? expanded ? "Collapse run details" : "Expand run details" : void 0,
1705
- onClick: hasDetails ? () => setExpandedExecutionId(expanded ? null : execution.executionId) : void 0,
1706
- onKeyDown: hasDetails ? (event) => {
1707
- if (event.key === "Enter" || event.key === " ") {
1708
- event.preventDefault();
1709
- setExpandedExecutionId(expanded ? null : execution.executionId);
2053
+ return /* @__PURE__ */ jsxs(
2054
+ TabSection,
2055
+ {
2056
+ icon: /* @__PURE__ */ jsx(IconPlayerPlay, { size: 16 }),
2057
+ title,
2058
+ description: "Execution history recorded for this lead-gen list.",
2059
+ rightSection: /* @__PURE__ */ jsx(Tooltip, { label: "Refresh runs", children: /* @__PURE__ */ jsx(
2060
+ ActionIcon,
2061
+ {
2062
+ variant: "subtle",
2063
+ size: "sm",
2064
+ "aria-label": "Refresh workflow runs",
2065
+ loading: executionsQuery.isFetching,
2066
+ onClick: () => void executionsQuery.refetch(),
2067
+ children: /* @__PURE__ */ jsx(IconRefresh, { size: 14 })
2068
+ }
2069
+ ) }),
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,
2092
+ {
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) => {
2101
+ if (event.key === "Enter" || event.key === " ") {
2102
+ event.preventDefault();
2103
+ setExpandedExecutionId(expanded ? null : execution.executionId);
2104
+ }
2105
+ } : void 0,
2106
+ style: {
2107
+ cursor: hasDetails ? "pointer" : "default",
2108
+ opacity: hasDetails ? 1 : 0.7
2109
+ },
2110
+ children: expanded ? /* @__PURE__ */ jsx(IconChevronDown, { size: 14 }) : /* @__PURE__ */ jsx(IconChevronRight, { size: 14 })
1710
2111
  }
1711
- } : void 0,
1712
- style: {
1713
- cursor: hasDetails ? "pointer" : "default",
1714
- opacity: hasDetails ? 1 : 0.7
1715
- },
1716
- children: expanded ? /* @__PURE__ */ jsx(IconChevronDown, { size: 14 }) : /* @__PURE__ */ jsx(IconChevronRight, { size: 14 })
1717
- }
1718
- ) }),
1719
- /* @__PURE__ */ jsxs(Table.Td, { children: [
1720
- execution.resourceId ? /* @__PURE__ */ jsx(
1721
- Text,
1722
- {
1723
- fw: 500,
1724
- role: "button",
1725
- tabIndex: 0,
1726
- c: "blue",
1727
- onClick: () => openWorkflow(execution.resourceId),
1728
- onKeyDown: (event) => {
1729
- if (event.key === "Enter" || event.key === " ") {
1730
- event.preventDefault();
1731
- openWorkflow(execution.resourceId);
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
1732
2130
  }
1733
- },
1734
- style: { cursor: "pointer" },
1735
- children: execution.resourceId
1736
- }
1737
- ) : /* @__PURE__ */ jsx(Text, { fw: 500, children: "Unknown workflow" }),
1738
- /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", ff: "monospace", children: execution.executionId })
1739
- ] }),
1740
- /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: getStatusColor2(execution.status), children: execution.status }) }),
1741
- /* @__PURE__ */ jsx(Table.Td, { children: formatDateTime2(execution.createdAt) }),
1742
- /* @__PURE__ */ jsx(Table.Td, { children: formatDateTime2(execution.completedAt) }),
1743
- /* @__PURE__ */ jsx(Table.Td, { children: formatDuration(execution.durationMs) })
1744
- ] }),
1745
- /* @__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: [
1746
- details.input !== void 0 ? /* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
1747
- /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
1748
- /* @__PURE__ */ jsx(Text, { size: "sm", fw: 600, children: "Input" }),
1749
- getSummaryKeys(details.input).map((key) => /* @__PURE__ */ jsx(Badge, { size: "xs", variant: "light", color: "gray", children: key }, key))
1750
- ] }),
1751
- /* @__PURE__ */ jsx(JsonViewer, { data: details.input, maxHeight: 220, fontSize: "0.75rem" })
1752
- ] }) : null,
1753
- details.config !== void 0 ? /* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
1754
- /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
1755
- /* @__PURE__ */ jsx(Text, { size: "sm", fw: 600, children: "Config" }),
1756
- getSummaryKeys(details.config).map((key) => /* @__PURE__ */ jsx(Badge, { size: "xs", variant: "light", color: "gray", children: key }, key))
2131
+ ) : /* @__PURE__ */ jsx(Text, { fw: 500, children: "Unknown workflow" }),
2132
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", ff: "monospace", children: execution.executionId })
2133
+ ] }),
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) })
1757
2138
  ] }),
1758
- /* @__PURE__ */ jsx(JsonViewer, { data: details.config, maxHeight: 220, fontSize: "0.75rem" })
1759
- ] }) : null
1760
- ] }) }) }) })
1761
- ] }, execution.executionId);
1762
- }) })
1763
- ] }) })
1764
- ] });
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
+ ]
2159
+ }
2160
+ );
1765
2161
  }
1766
2162
  var EMPTY_SELECTION = {
1767
2163
  selectedCompanyIds: [],
@@ -1902,290 +2298,19 @@ function ListBuilderPage({ listId }) {
1902
2298
  )
1903
2299
  ] });
1904
2300
  }
1905
- function CompanyCleanupForm({ list, onSubmit }) {
1906
- const [isSubmitting, setIsSubmitting] = useState(false);
1907
- const form = useForm({
1908
- initialValues: {
1909
- targetDescription: "",
1910
- tag: "",
1911
- dryRun: true,
1912
- batchSize: 20
1913
- },
1914
- validate: {
1915
- targetDescription: (value) => value.trim().length > 0 ? null : "Target description is required.",
1916
- batchSize: (value) => Number.isInteger(value) && value >= 1 ? null : "Use a whole number of at least 1."
1917
- }
1918
- });
1919
- const handleSubmit = form.onSubmit(async (values) => {
1920
- setIsSubmitting(true);
1921
- try {
1922
- const tag = values.tag.trim();
1923
- await onSubmit({
1924
- listId: list.id,
1925
- targetDescription: values.targetDescription.trim(),
1926
- ...tag ? { tag } : {},
1927
- dryRun: values.dryRun,
1928
- batchSize: values.batchSize
1929
- });
1930
- } finally {
1931
- setIsSubmitting(false);
1932
- }
2301
+ function formatDateTime4(value) {
2302
+ if (!value) return "Not yet";
2303
+ return new Date(value).toLocaleString("en-US", {
2304
+ month: "short",
2305
+ day: "numeric",
2306
+ year: "numeric",
2307
+ hour: "numeric",
2308
+ minute: "2-digit"
1933
2309
  });
1934
- return /* @__PURE__ */ jsx("form", { onSubmit: handleSubmit, children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
1935
- /* @__PURE__ */ jsx(
1936
- Textarea,
1937
- {
1938
- label: "Target description",
1939
- placeholder: "independent veterinary clinics in Orange County",
1940
- minRows: 3,
1941
- autosize: true,
1942
- disabled: isSubmitting,
1943
- required: true,
1944
- ...form.getInputProps("targetDescription")
1945
- }
1946
- ),
1947
- /* @__PURE__ */ jsx(TextInput, { label: "Tag kept companies", placeholder: "vet-clinic", disabled: isSubmitting, ...form.getInputProps("tag") }),
1948
- /* @__PURE__ */ jsx(
1949
- NumberInput,
1950
- {
1951
- label: "Batch size",
1952
- min: 1,
1953
- allowDecimal: false,
1954
- disabled: isSubmitting,
1955
- required: true,
1956
- ...form.getInputProps("batchSize")
1957
- }
1958
- ),
1959
- /* @__PURE__ */ jsx(Switch, { label: "Dry run", disabled: isSubmitting, ...form.getInputProps("dryRun", { type: "checkbox" }) }),
1960
- /* @__PURE__ */ jsx(Group, { justify: "flex-end", children: /* @__PURE__ */ jsx(
1961
- Button,
1962
- {
1963
- type: "submit",
1964
- loading: isSubmitting,
1965
- disabled: !form.values.targetDescription.trim(),
1966
- leftSection: /* @__PURE__ */ jsx(IconPlayerPlay, { size: 16 }),
1967
- children: "Run Company Cleanup"
1968
- }
1969
- ) })
1970
- ] }) });
1971
2310
  }
1972
- function optionalNumber(value) {
1973
- return value === "" ? void 0 : value;
1974
- }
1975
- function EmailDiscoveryForm({ list, onSubmit }) {
1976
- const [isSubmitting, setIsSubmitting] = useState(false);
1977
- const form = useForm({
1978
- initialValues: {
1979
- maxCompanies: "",
1980
- dryRun: false,
1981
- chain: false,
1982
- concurrency: 1,
1983
- credentialName: "",
1984
- tombaTimeoutMs: 15e3
1985
- },
1986
- validate: {
1987
- maxCompanies: (value) => value === "" || Number.isInteger(value) && value >= 1 && value <= 500 ? null : "Use a whole number from 1 to 500.",
1988
- concurrency: (value) => Number.isInteger(value) && value >= 1 && value <= 10 ? null : "Use a whole number from 1 to 10.",
1989
- tombaTimeoutMs: (value) => Number.isInteger(value) && value >= 1e3 ? null : "Use a timeout of at least 1000 ms."
1990
- }
1991
- });
1992
- const handleSubmit = form.onSubmit(async (values) => {
1993
- setIsSubmitting(true);
1994
- try {
1995
- const credentialName = values.credentialName.trim();
1996
- await onSubmit({
1997
- listId: list.id,
1998
- ...optionalNumber(values.maxCompanies) !== void 0 ? { maxCompanies: optionalNumber(values.maxCompanies) } : {},
1999
- dryRun: values.dryRun,
2000
- chain: values.chain,
2001
- concurrency: values.concurrency,
2002
- ...credentialName ? { credentialName } : {},
2003
- tombaTimeoutMs: values.tombaTimeoutMs
2004
- });
2005
- } finally {
2006
- setIsSubmitting(false);
2007
- }
2008
- });
2009
- return /* @__PURE__ */ jsx("form", { onSubmit: handleSubmit, children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
2010
- /* @__PURE__ */ jsx(
2011
- NumberInput,
2012
- {
2013
- label: "Max companies",
2014
- description: "Leave empty to process every eligible company.",
2015
- min: 1,
2016
- max: 500,
2017
- allowDecimal: false,
2018
- disabled: isSubmitting,
2019
- ...form.getInputProps("maxCompanies")
2020
- }
2021
- ),
2022
- /* @__PURE__ */ jsx(
2023
- NumberInput,
2024
- {
2025
- label: "Concurrency",
2026
- min: 1,
2027
- max: 10,
2028
- allowDecimal: false,
2029
- disabled: isSubmitting,
2030
- required: true,
2031
- ...form.getInputProps("concurrency")
2032
- }
2033
- ),
2034
- /* @__PURE__ */ jsx(
2035
- TextInput,
2036
- {
2037
- label: "Tomba credential",
2038
- placeholder: "Default credential",
2039
- disabled: isSubmitting,
2040
- ...form.getInputProps("credentialName")
2041
- }
2042
- ),
2043
- /* @__PURE__ */ jsx(
2044
- NumberInput,
2045
- {
2046
- label: "Tomba timeout",
2047
- suffix: " ms",
2048
- min: 1e3,
2049
- step: 1e3,
2050
- allowDecimal: false,
2051
- disabled: isSubmitting,
2052
- required: true,
2053
- ...form.getInputProps("tombaTimeoutMs")
2054
- }
2055
- ),
2056
- /* @__PURE__ */ jsx(Switch, { label: "Dry run", disabled: isSubmitting, ...form.getInputProps("dryRun", { type: "checkbox" }) }),
2057
- /* @__PURE__ */ jsx(
2058
- Switch,
2059
- {
2060
- label: "Run email verification after discovery",
2061
- disabled: isSubmitting,
2062
- ...form.getInputProps("chain", { type: "checkbox" })
2063
- }
2064
- ),
2065
- /* @__PURE__ */ jsx(Group, { justify: "flex-end", children: /* @__PURE__ */ jsx(Button, { type: "submit", loading: isSubmitting, leftSection: /* @__PURE__ */ jsx(IconPlayerPlay, { size: 16 }), children: "Run Email Discovery" }) })
2066
- ] }) });
2067
- }
2068
- function EmailVerificationForm({ list, onSubmit }) {
2069
- const [isSubmitting, setIsSubmitting] = useState(false);
2070
- const form = useForm({
2071
- initialValues: {
2072
- limit: 100,
2073
- skipAlreadyVerified: true
2074
- },
2075
- validate: {
2076
- limit: (value) => Number.isInteger(value) && value >= 1 && value <= 500 ? null : "Use a whole number from 1 to 500."
2077
- }
2078
- });
2079
- const handleSubmit = form.onSubmit(async (values) => {
2080
- setIsSubmitting(true);
2081
- try {
2082
- await onSubmit({
2083
- listId: list.id,
2084
- limit: values.limit,
2085
- skipAlreadyVerified: values.skipAlreadyVerified
2086
- });
2087
- } finally {
2088
- setIsSubmitting(false);
2089
- }
2090
- });
2091
- return /* @__PURE__ */ jsx("form", { onSubmit: handleSubmit, children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
2092
- /* @__PURE__ */ jsx(
2093
- NumberInput,
2094
- {
2095
- label: "Contact limit",
2096
- min: 1,
2097
- max: 500,
2098
- allowDecimal: false,
2099
- disabled: isSubmitting,
2100
- required: true,
2101
- ...form.getInputProps("limit")
2102
- }
2103
- ),
2104
- /* @__PURE__ */ jsx(
2105
- Switch,
2106
- {
2107
- label: "Skip already verified contacts",
2108
- disabled: isSubmitting,
2109
- ...form.getInputProps("skipAlreadyVerified", { type: "checkbox" })
2110
- }
2111
- ),
2112
- /* @__PURE__ */ jsx(Group, { justify: "flex-end", children: /* @__PURE__ */ jsx(Button, { type: "submit", loading: isSubmitting, leftSection: /* @__PURE__ */ jsx(IconPlayerPlay, { size: 16 }), children: "Run Email Verification" }) })
2113
- ] }) });
2114
- }
2115
- function WebsiteExtractForm({ list, onSubmit }) {
2116
- const [isSubmitting, setIsSubmitting] = useState(false);
2117
- const form = useForm({
2118
- initialValues: {
2119
- limit: 200,
2120
- concurrency: 10,
2121
- chain: false
2122
- },
2123
- validate: {
2124
- limit: (value) => Number.isInteger(value) && value >= 1 && value <= 200 ? null : "Use a whole number from 1 to 200.",
2125
- concurrency: (value) => Number.isInteger(value) && value >= 1 ? null : "Use a whole number of at least 1."
2126
- }
2127
- });
2128
- const handleSubmit = form.onSubmit(async (values) => {
2129
- setIsSubmitting(true);
2130
- try {
2131
- await onSubmit({
2132
- listId: list.id,
2133
- limit: values.limit,
2134
- concurrency: values.concurrency,
2135
- chain: values.chain
2136
- });
2137
- } finally {
2138
- setIsSubmitting(false);
2139
- }
2140
- });
2141
- return /* @__PURE__ */ jsx("form", { onSubmit: handleSubmit, children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
2142
- /* @__PURE__ */ jsx(
2143
- NumberInput,
2144
- {
2145
- label: "Company limit",
2146
- min: 1,
2147
- max: 200,
2148
- allowDecimal: false,
2149
- disabled: isSubmitting,
2150
- required: true,
2151
- ...form.getInputProps("limit")
2152
- }
2153
- ),
2154
- /* @__PURE__ */ jsx(
2155
- NumberInput,
2156
- {
2157
- label: "Concurrency",
2158
- min: 1,
2159
- allowDecimal: false,
2160
- disabled: isSubmitting,
2161
- required: true,
2162
- ...form.getInputProps("concurrency")
2163
- }
2164
- ),
2165
- /* @__PURE__ */ jsx(
2166
- Switch,
2167
- {
2168
- label: "Run company qualification after extraction",
2169
- disabled: isSubmitting,
2170
- ...form.getInputProps("chain", { type: "checkbox" })
2171
- }
2172
- ),
2173
- /* @__PURE__ */ jsx(Group, { justify: "flex-end", children: /* @__PURE__ */ jsx(Button, { type: "submit", loading: isSubmitting, leftSection: /* @__PURE__ */ jsx(IconPlayerPlay, { size: 16 }), children: "Run Website Extract" }) })
2174
- ] }) });
2175
- }
2176
- function formatDateTime4(value) {
2177
- if (!value) return "Not yet";
2178
- return new Date(value).toLocaleString("en-US", {
2179
- month: "short",
2180
- day: "numeric",
2181
- year: "numeric",
2182
- hour: "numeric",
2183
- minute: "2-digit"
2184
- });
2185
- }
2186
- function contactDisplayName(firstName, lastName) {
2187
- const full = [firstName, lastName].filter(Boolean).join(" ").trim();
2188
- return full || "\u2014";
2311
+ function contactDisplayName(firstName, lastName) {
2312
+ const full = [firstName, lastName].filter(Boolean).join(" ").trim();
2313
+ return full || "\u2014";
2189
2314
  }
2190
2315
  function getStatusColor4(status) {
2191
2316
  switch (status) {
@@ -2221,15 +2346,51 @@ function getMemberStateColor2(stateKey) {
2221
2346
  return "gray";
2222
2347
  }
2223
2348
  }
2349
+ function asRecord2(value) {
2350
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
2351
+ }
2352
+ function getRunStatusColor(status) {
2353
+ switch (status) {
2354
+ case "completed":
2355
+ case "success":
2356
+ case "succeeded":
2357
+ return "green";
2358
+ case "running":
2359
+ case "pending":
2360
+ case "queued":
2361
+ return "blue";
2362
+ case "failed":
2363
+ case "error":
2364
+ case "cancelled":
2365
+ return "red";
2366
+ default:
2367
+ return "gray";
2368
+ }
2369
+ }
2370
+ function getRunInput(run) {
2371
+ const payload = asRecord2(run.payload);
2372
+ return run.input ?? run.inputs ?? payload.input ?? payload.inputs;
2373
+ }
2374
+ function sanitizeStepInput(value, _capabilityKey) {
2375
+ return value;
2376
+ }
2377
+ function getDefaultStepInput(list, action) {
2378
+ return asRecord2(action?.defaultInput?.(list));
2379
+ }
2380
+ function getInitialStepInput(list, action) {
2381
+ return getDefaultStepInput(list, action);
2382
+ }
2383
+ function mergeStepRunInput(list, action, inputAtSubmit) {
2384
+ return {
2385
+ ...getDefaultStepInput(list, action),
2386
+ ...inputAtSubmit
2387
+ };
2388
+ }
2224
2389
  var ORPHAN_STAGE_ORDER = 9999;
2225
2390
  var EMPTY_SELECTION2 = {
2226
2391
  selectedCompanyIds: [],
2227
2392
  selectedContactIds: []
2228
2393
  };
2229
- var BUILD_TEMPLATE_SELECT_OPTIONS2 = PROSPECTING_BUILD_TEMPLATE_OPTIONS.map((template) => ({
2230
- value: template.id,
2231
- label: template.label
2232
- }));
2233
2394
  var MVP_BUILD_STEPS = [
2234
2395
  {
2235
2396
  id: "source-companies",
@@ -2385,10 +2546,12 @@ function getPrerequisiteSteps(step, byStepId) {
2385
2546
  return dependency ? [dependency] : [];
2386
2547
  });
2387
2548
  }
2388
- function getStepActionKind(ready, failed, action) {
2549
+ function getStepActionKind(step, ready, failed, action) {
2389
2550
  if (!action) return "none";
2390
2551
  if (failed > 0) return "retry_failed";
2391
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";
2392
2555
  return "none";
2393
2556
  }
2394
2557
  function getStepActionLabel(kind) {
@@ -2421,7 +2584,7 @@ function deriveBuildStepStates(list, progress, actions) {
2421
2584
  const entityTotal = getEntityTotal(progress, step.primaryEntity);
2422
2585
  const prerequisites = getPrerequisiteSteps(step, byStepId);
2423
2586
  const hasDependencies = (step.dependsOn?.length ?? 0) > 0;
2424
- 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;
2425
2588
  const total = Math.max(stageProgress?.total ?? 0, entityTotal, eligibleTotal);
2426
2589
  const complete = stageProgress ? stageProgress.success + stageProgress.noResult + stageProgress.skipped + stageProgress.other : 0;
2427
2590
  const failed = stageProgress?.error ?? 0;
@@ -2429,7 +2592,7 @@ function deriveBuildStepStates(list, progress, actions) {
2429
2592
  const blocked = hasDependencies ? Math.max(entityTotal - eligibleTotal, 0) : 0;
2430
2593
  const status = failed > 0 ? "failed" : ready > 0 ? "ready" : blocked > 0 || total === 0 ? "blocked" : "complete";
2431
2594
  const action = findActionForStep(actions, step);
2432
- const nextActionKind = getStepActionKind(ready, failed, action);
2595
+ const nextActionKind = getStepActionKind(step, ready, failed, action);
2433
2596
  const state = {
2434
2597
  ...step,
2435
2598
  status,
@@ -2457,74 +2620,173 @@ function resolveBuildState(list, progress, actions) {
2457
2620
  recommendedAction: recommendedStep?.recommendedAction ?? null
2458
2621
  };
2459
2622
  }
2460
- function PipelineSection({
2461
- 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,
2462
2739
  byStage,
2463
2740
  emptyText
2464
2741
  }) {
2465
- const orderedKeys = sortStageKeys(Object.keys(byStage));
2466
- return /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
2467
- /* @__PURE__ */ jsx(Text, { size: "sm", fw: 600, children: title }),
2468
- orderedKeys.length === 0 ? /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: emptyText }) : /* @__PURE__ */ jsx(Stack, { gap: "xs", children: orderedKeys.map((key) => {
2469
- const entry = byStage[key];
2470
- const total = entry?.total ?? 0;
2471
- const attempted = entry?.attempted ?? 0;
2472
- const percent2 = total > 0 ? Math.round(attempted / total * 100) : 0;
2473
- const label = LEAD_GEN_STAGE_CATALOG[key]?.label ?? key;
2474
- const segments = [
2475
- { key: "success", value: entry?.success ?? 0, color: "var(--mantine-color-green-6)" },
2476
- { key: "noResult", value: entry?.noResult ?? 0, color: "var(--mantine-color-gray-6)" },
2477
- { key: "skipped", value: entry?.skipped ?? 0, color: "var(--mantine-color-yellow-6)" },
2478
- { key: "error", value: entry?.error ?? 0, color: "var(--mantine-color-red-6)" },
2479
- { key: "other", value: entry?.other ?? 0, color: "var(--mantine-color-blue-6)" }
2480
- ].filter((segment) => segment.value > 0);
2481
- const statusSummary = [
2482
- `${entry?.success ?? 0} success`,
2483
- `${entry?.noResult ?? 0} no result`,
2484
- `${entry?.skipped ?? 0} skipped`,
2485
- `${entry?.error ?? 0} errors`,
2486
- (entry?.other ?? 0) > 0 ? `${entry?.other ?? 0} other` : null,
2487
- (entry?.notAttempted ?? 0) > 0 ? `${entry?.notAttempted ?? 0} not attempted` : null
2488
- ].filter(Boolean);
2489
- return /* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
2490
- /* @__PURE__ */ jsxs(Group, { justify: "space-between", gap: "xs", children: [
2491
- /* @__PURE__ */ jsx(Text, { size: "sm", children: label }),
2492
- /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
2493
- attempted,
2494
- " / ",
2495
- total,
2496
- " attempted (",
2497
- percent2,
2498
- "%)"
2499
- ] })
2500
- ] }),
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: [
2501
2758
  /* @__PURE__ */ jsx(
2502
2759
  Box,
2503
2760
  {
2504
- h: 8,
2505
- bg: "var(--mantine-color-dark-4)",
2506
- style: { borderRadius: 999, display: "flex", overflow: "hidden" },
2507
- children: segments.map((segment) => /* @__PURE__ */ jsx(
2508
- Box,
2509
- {
2510
- h: "100%",
2511
- w: `${total > 0 ? segment.value / total * 100 : 0}%`,
2512
- bg: segment.color
2513
- },
2514
- segment.key
2515
- ))
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
+ }
2516
2769
  }
2517
2770
  ),
2518
- /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: statusSummary.join(" \xB7 ") })
2519
- ] }, key);
2520
- }) })
2521
- ] });
2771
+ stageKeys.map((key, index) => /* @__PURE__ */ jsx(StageNode, { index, stageKey: key, fallbackLabel: labelByKey.get(key), entry: byStage[key] }, key))
2772
+ ]
2773
+ }
2774
+ ) });
2522
2775
  }
2523
2776
  function PipelineStagesCard({ list, progress }) {
2524
- 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;
2525
2781
  return /* @__PURE__ */ jsx(Card, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
2526
2782
  /* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "center", children: [
2527
- /* @__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
+ ] }),
2528
2790
  /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
2529
2791
  /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "filled", color: getStatusColor4(list.status), children: list.status }),
2530
2792
  list.launchedAt && /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
@@ -2537,21 +2799,20 @@ function PipelineStagesCard({ list, progress }) {
2537
2799
  ] })
2538
2800
  ] })
2539
2801
  ] }),
2540
- !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: [
2541
- /* @__PURE__ */ jsx(
2542
- PipelineSection,
2543
- {
2544
- title: `Company Pipeline (${progress.totalCompanies})`,
2545
- byStage: progress.byCompanyStage,
2546
- emptyText: "No company-stage flags recorded yet."
2547
- }
2548
- ),
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
+ ] }),
2549
2810
  /* @__PURE__ */ jsx(
2550
- PipelineSection,
2811
+ PipelineStepper,
2551
2812
  {
2552
- title: `Contact Pipeline (${progress.totalMembers})`,
2553
- byStage: progress.byContactStage,
2554
- emptyText: "No contact-stage flags recorded yet."
2813
+ plannedSteps,
2814
+ byStage: combinedByStage,
2815
+ emptyText: "No pipeline activity recorded yet."
2555
2816
  }
2556
2817
  )
2557
2818
  ] })
@@ -2565,12 +2826,12 @@ function ListConfigCard({ list }) {
2565
2826
  if (!hasIcp && !hasScraping) {
2566
2827
  return /* @__PURE__ */ jsx(Card, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
2567
2828
  /* @__PURE__ */ jsx(Title, { order: 5, children: "List Config" }),
2568
- /* @__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." })
2569
2830
  ] }) });
2570
2831
  }
2571
2832
  return /* @__PURE__ */ jsx(Card, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
2572
2833
  /* @__PURE__ */ jsx(Title, { order: 5, children: "List Config" }),
2573
- /* @__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: [
2574
2835
  hasIcp && /* @__PURE__ */ jsxs(Stack, { gap: 6, children: [
2575
2836
  /* @__PURE__ */ jsx(Text, { size: "sm", fw: 600, children: "ICP Rubric" }),
2576
2837
  icp.qualificationRubricKey && /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
@@ -2627,32 +2888,26 @@ function ListConfigCard({ list }) {
2627
2888
  }
2628
2889
  function OverviewTab({ list, progress }) {
2629
2890
  const hasMetadata = list.metadata && Object.keys(list.metadata).length > 0;
2630
- return /* @__PURE__ */ jsxs(Stack, { gap: "md", p: "md", children: [
2631
- /* @__PURE__ */ jsx(CardHeader, { icon: /* @__PURE__ */ jsx(IconBuilding, { size: 16 }), title: "Overview" }),
2632
- /* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 1, sm: 2 }, children: [
2633
- /* @__PURE__ */ jsx(StatCard, { label: "Companies", value: progress.totalCompanies, icon: IconBuilding }),
2634
- /* @__PURE__ */ jsx(StatCard, { label: "Members", value: progress.totalMembers, icon: IconUsers })
2635
- ] }),
2636
- /* @__PURE__ */ jsx(PipelineStagesCard, { list, progress }),
2637
- /* @__PURE__ */ jsx(ListConfigCard, { list }),
2638
- hasMetadata && /* @__PURE__ */ jsx(Card, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
2639
- /* @__PURE__ */ jsx(Title, { order: 5, children: "Metadata / Qualification Signals" }),
2640
- /* @__PURE__ */ jsx(JsonViewer, { data: list.metadata, maxHeight: 320 })
2641
- ] }) })
2642
- ] });
2643
- }
2644
- function getBuildStatusLabel(status) {
2645
- switch (status) {
2646
- case "failed":
2647
- return "Needs retry";
2648
- case "blocked":
2649
- return "Waiting";
2650
- case "complete":
2651
- return "Complete";
2652
- case "ready":
2653
- default:
2654
- return "Ready";
2655
- }
2891
+ return /* @__PURE__ */ jsxs(
2892
+ TabSection,
2893
+ {
2894
+ icon: /* @__PURE__ */ jsx(IconBuilding, { size: 16 }),
2895
+ title: "Overview",
2896
+ description: "Configuration, progress, and source metadata for this list.",
2897
+ children: [
2898
+ /* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 1, sm: 2 }, children: [
2899
+ /* @__PURE__ */ jsx(StatCard, { label: "Companies", value: progress.totalCompanies, icon: IconBuilding }),
2900
+ /* @__PURE__ */ jsx(StatCard, { label: "Members", value: progress.totalMembers, icon: IconUsers })
2901
+ ] }),
2902
+ /* @__PURE__ */ jsx(PipelineStagesCard, { list, progress }),
2903
+ /* @__PURE__ */ jsx(ListConfigCard, { list }),
2904
+ hasMetadata && /* @__PURE__ */ jsx(Card, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
2905
+ /* @__PURE__ */ jsx(Title, { order: 5, children: "Metadata" }),
2906
+ /* @__PURE__ */ jsx(JsonViewer, { data: list.metadata, maxHeight: 320 })
2907
+ ] }) })
2908
+ ]
2909
+ }
2910
+ );
2656
2911
  }
2657
2912
  function getBuildToneStyle(tone) {
2658
2913
  switch (tone) {
@@ -2671,7 +2926,7 @@ function getBuildToneStyle(tone) {
2671
2926
  case "ready":
2672
2927
  return {
2673
2928
  backgroundColor: "color-mix(in srgb, var(--color-primary) 14%, transparent)",
2674
- borderColor: "color-mix(in srgb, var(--color-primary) 34%, var(--color-border))",
2929
+ borderColor: "var(--border-primary-muted)",
2675
2930
  color: "var(--color-primary)"
2676
2931
  };
2677
2932
  case "blocked":
@@ -2706,23 +2961,21 @@ function StatusPill({ label, tone }) {
2706
2961
  }
2707
2962
  );
2708
2963
  }
2709
- function CountBadge({ label, value, tone }) {
2710
- const toneStyle = getBuildToneStyle(tone);
2711
- return /* @__PURE__ */ jsxs(
2712
- Box,
2713
- {
2714
- p: "xs",
2715
- style: {
2716
- backgroundColor: "var(--color-surface)",
2717
- border: "1px solid var(--color-border)",
2718
- borderRadius: 8
2719
- },
2720
- children: [
2721
- /* @__PURE__ */ jsx(Text, { size: "lg", fw: 700, style: { color: toneStyle.color }, children: value }),
2722
- /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", tt: "uppercase", fw: 700, children: label })
2723
- ]
2724
- }
2725
- );
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)) });
2726
2979
  }
2727
2980
  function BuildStepProgressBar({ step }) {
2728
2981
  const segments = [
@@ -2747,19 +3000,11 @@ function BuildStepProgressBar({ step }) {
2747
3000
  }
2748
3001
  );
2749
3002
  }
2750
- function getStepCompactSummary(step) {
2751
- const parts = [
2752
- step.complete > 0 ? `${step.complete} complete` : null,
2753
- step.ready > 0 ? `${step.ready} ready` : null,
2754
- step.failed > 0 ? `${step.failed} failed` : null,
2755
- step.blocked > 0 ? `${step.blocked} waiting` : null
2756
- ].filter(Boolean);
2757
- return parts.length ? parts.join(" | ") : "No records yet";
2758
- }
2759
3003
  function getDisplayStep(steps, recommendedStep) {
2760
3004
  return recommendedStep ?? steps.find((step) => step.status !== "complete") ?? steps[steps.length - 1] ?? null;
2761
3005
  }
2762
3006
  function BuildTab({
3007
+ list,
2763
3008
  steps,
2764
3009
  recommendedStep,
2765
3010
  onRunStep,
@@ -2767,83 +3012,132 @@ function BuildTab({
2767
3012
  onViewRuns
2768
3013
  }) {
2769
3014
  const [selectedStepId, setSelectedStepId] = useState(null);
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);
2770
3020
  const currentStep = getDisplayStep(steps, recommendedStep);
2771
3021
  const selectedStep = steps.find((step) => step.id === selectedStepId) ?? currentStep;
2772
3022
  const selectedStepIndex = selectedStep ? steps.findIndex((step) => step.id === selectedStep.id) : -1;
2773
3023
  const selectedActionKind = selectedStep?.nextActionKind ?? "none";
3024
+ const selectedCapabilityKey = selectedStep?.action?.capabilityKey ?? selectedStep?.capabilityKey;
3025
+ const selectedResourceId = selectedStep?.action?.resourceId ?? null;
3026
+ const stepSchema = selectedStep?.action?.schema;
3027
+ const stepLayout = selectedStep?.action?.layout;
3028
+ const recentRunsQuery = useListExecutions(list.id, { resourceId: selectedResourceId, limit: 5 });
3029
+ const recentRuns = useMemo(() => recentRunsQuery.data ?? [], [recentRunsQuery.data]);
2774
3030
  const canRunSelectedAction = !!selectedStep?.action && (selectedActionKind === "retry_failed" || selectedActionKind === "run_next_batch");
2775
3031
  const selectedActionIcon = selectedActionKind === "retry_failed" ? /* @__PURE__ */ jsx(IconRefresh, { size: 14 }) : /* @__PURE__ */ jsx(IconPlayerPlay, { size: 14 });
3032
+ useEffect(() => {
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);
3046
+ }
3047
+ }
3048
+ setStepInputInitialized(true);
3049
+ }, [stepInputInitialized, recentRunsQuery.isLoading, recentRuns, selectedCapabilityKey]);
3050
+ const handleStepInputChange = (next) => {
3051
+ setStepInputFormState(next);
3052
+ if (stepInputError) setStepInputError(null);
3053
+ };
2776
3054
  const handleSelectedAction = () => {
2777
3055
  if (!selectedStep || !canRunSelectedAction) return;
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 : {};
2778
3066
  if (selectedActionKind === "retry_failed") {
2779
- onRetryStep(selectedStep);
3067
+ onRetryStep(selectedStep, submittedInput);
2780
3068
  return;
2781
3069
  }
2782
- onRunStep(selectedStep);
3070
+ onRunStep(selectedStep, submittedInput);
2783
3071
  };
2784
- return /* @__PURE__ */ jsxs(Stack, { gap: "md", p: "md", children: [
2785
- /* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
2786
- /* @__PURE__ */ jsx(CardHeader, { icon: /* @__PURE__ */ jsx(IconBolt, { size: 16 }), title: "Build" }),
2787
- /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: currentStep ? `${currentStep.label} is the current step in this list build.` : "No build steps are available for this list." })
2788
- ] }),
2789
- /* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 1, md: 2 }, spacing: "md", children: [
2790
- /* @__PURE__ */ jsx(Stack, { gap: "xs", children: steps.map((step, index) => {
2791
- const isCurrent = currentStep?.id === step.id;
2792
- const isSelected = selectedStep?.id === step.id;
2793
- const isWaiting = step.status === "blocked";
2794
- const isPassiveWaiting = isWaiting && !isSelected && !isCurrent;
2795
- const stepTone = isSelected || isCurrent ? step.status : step.status === "complete" || isWaiting ? step.status : "neutral";
2796
- return /* @__PURE__ */ jsx(
2797
- UnstyledButton,
2798
- {
2799
- onClick: () => setSelectedStepId(step.id),
2800
- style: {
2801
- backgroundColor: isSelected ? "var(--active-background)" : isPassiveWaiting ? "color-mix(in srgb, var(--color-surface-hover) 38%, transparent)" : "transparent",
2802
- border: isSelected ? "var(--active-border)" : "1px solid var(--color-border)",
2803
- borderStyle: isPassiveWaiting ? "dashed" : "solid",
2804
- borderRadius: 8,
2805
- display: "block",
2806
- opacity: isPassiveWaiting ? 0.72 : 1,
2807
- padding: 12,
2808
- textAlign: "left",
2809
- width: "100%"
2810
- },
2811
- children: /* @__PURE__ */ jsxs(Group, { gap: "sm", align: "flex-start", wrap: "nowrap", children: [
2812
- /* @__PURE__ */ jsx(
2813
- Box,
2814
- {
2815
- w: 28,
2816
- h: 28,
2817
- style: {
2818
- alignItems: "center",
2819
- ...getBuildToneStyle(stepTone),
2820
- borderRadius: 999,
2821
- borderStyle: "solid",
2822
- borderWidth: 1,
2823
- display: "flex",
2824
- flexShrink: 0,
2825
- fontWeight: 700,
2826
- justifyContent: "center"
2827
- },
2828
- children: index + 1
2829
- }
2830
- ),
2831
- /* @__PURE__ */ jsxs(Stack, { gap: 3, style: { minWidth: 0, flex: 1 }, children: [
2832
- /* @__PURE__ */ jsxs(Group, { gap: "xs", justify: "space-between", wrap: "nowrap", children: [
2833
- /* @__PURE__ */ jsx(Text, { fw: 700, c: isPassiveWaiting ? "dimmed" : void 0, truncate: true, children: step.label }),
2834
- isCurrent ? /* @__PURE__ */ jsx(StatusPill, { label: "Current", tone: step.status }) : null,
2835
- !isCurrent && isWaiting ? /* @__PURE__ */ jsx(StatusPill, { label: "Waiting", tone: "blocked" }) : null
2836
- ] }),
2837
- /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: getStepCompactSummary(step) })
3072
+ const loadRecentRunInput = (run) => {
3073
+ setStepInputFormState(sanitizeStepInput(asRecord2(getRunInput(run))));
3074
+ setStepInputResetKey((k) => k + 1);
3075
+ setActiveRightColumnTab("configuration");
3076
+ };
3077
+ return /* @__PURE__ */ jsx(
3078
+ TabSection,
3079
+ {
3080
+ icon: /* @__PURE__ */ jsx(IconBolt, { size: 16 }),
3081
+ title: "Build",
3082
+ description: currentStep ? `${currentStep.label} is the current step in this list build.` : "No build steps are available for this list.",
3083
+ children: /* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 1, md: 2 }, spacing: "md", children: [
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) => {
3085
+ const isCurrent = currentStep?.id === step.id;
3086
+ const isSelected = selectedStep?.id === step.id;
3087
+ const isWaiting = step.status === "blocked";
3088
+ const isPassiveWaiting = isWaiting && !isSelected && !isCurrent;
3089
+ const stepTone = isSelected || isCurrent ? step.status : step.status === "complete" || isWaiting ? step.status : "neutral";
3090
+ return /* @__PURE__ */ jsx(
3091
+ UnstyledButton,
3092
+ {
3093
+ onClick: () => setSelectedStepId(step.id),
3094
+ style: {
3095
+ backgroundColor: isSelected ? "var(--surface-primary-strong)" : isPassiveWaiting ? "color-mix(in srgb, var(--color-surface-hover) 38%, transparent)" : "transparent",
3096
+ border: isSelected ? "var(--active-border)" : "1px solid var(--color-border)",
3097
+ borderStyle: isPassiveWaiting ? "dashed" : "solid",
3098
+ borderRadius: 8,
3099
+ display: "block",
3100
+ opacity: isPassiveWaiting ? 0.72 : 1,
3101
+ padding: 12,
3102
+ textAlign: "left",
3103
+ width: "100%"
3104
+ },
3105
+ children: /* @__PURE__ */ jsxs(Group, { gap: "sm", align: "flex-start", wrap: "nowrap", children: [
3106
+ /* @__PURE__ */ jsx(
3107
+ Box,
3108
+ {
3109
+ w: 28,
3110
+ h: 28,
3111
+ style: {
3112
+ alignItems: "center",
3113
+ ...getBuildToneStyle(stepTone),
3114
+ borderRadius: 999,
3115
+ borderStyle: "solid",
3116
+ borderWidth: 1,
3117
+ display: "flex",
3118
+ flexShrink: 0,
3119
+ fontWeight: 700,
3120
+ justifyContent: "center"
3121
+ },
3122
+ children: index + 1
3123
+ }
3124
+ ),
3125
+ /* @__PURE__ */ jsxs(Stack, { gap: 4, style: { minWidth: 0, flex: 1 }, children: [
3126
+ /* @__PURE__ */ jsxs(Group, { gap: "xs", justify: "space-between", wrap: "nowrap", children: [
3127
+ /* @__PURE__ */ jsx(Text, { fw: 700, c: isPassiveWaiting ? "dimmed" : void 0, truncate: true, children: step.label }),
3128
+ isCurrent ? /* @__PURE__ */ jsx(StatusPill, { label: "Current", tone: step.status }) : null,
3129
+ !isCurrent && isWaiting ? /* @__PURE__ */ jsx(StatusPill, { label: "Waiting", tone: "blocked" }) : null
3130
+ ] }),
3131
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", lineClamp: 2, children: step.description }),
3132
+ /* @__PURE__ */ jsx(StepCounterLine, { step })
3133
+ ] })
2838
3134
  ] })
2839
- ] })
2840
- },
2841
- step.id
2842
- );
2843
- }) }),
2844
- selectedStep ? /* @__PURE__ */ jsx(Card, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
2845
- /* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "flex-start", gap: "sm", children: [
2846
- /* @__PURE__ */ jsxs(Stack, { gap: 2, style: { minWidth: 0 }, children: [
3135
+ },
3136
+ step.id
3137
+ );
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: [
2847
3141
  /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", fw: 700, tt: "uppercase", children: [
2848
3142
  "Step ",
2849
3143
  selectedStepIndex + 1,
@@ -2851,40 +3145,104 @@ function BuildTab({
2851
3145
  ] }),
2852
3146
  /* @__PURE__ */ jsx(Title, { order: 5, children: selectedStep.label })
2853
3147
  ] }),
2854
- /* @__PURE__ */ jsx(StatusPill, { label: getBuildStatusLabel(selectedStep.status), tone: selectedStep.status })
2855
- ] }),
2856
- /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: selectedStep.description }),
2857
- selectedStep.status === "blocked" ? /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: selectedStep.emptyBlockedText }) : null,
2858
- /* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 2, sm: 4 }, spacing: "xs", children: [
2859
- /* @__PURE__ */ jsx(CountBadge, { label: "Ready", value: selectedStep.ready, tone: "ready" }),
2860
- /* @__PURE__ */ jsx(CountBadge, { label: "Complete", value: selectedStep.complete, tone: "complete" }),
2861
- /* @__PURE__ */ jsx(CountBadge, { label: "Failed", value: selectedStep.failed, tone: "failed" }),
2862
- /* @__PURE__ */ jsx(CountBadge, { label: "Waiting", value: selectedStep.blocked, tone: "blocked" })
2863
- ] }),
2864
- /* @__PURE__ */ jsx(BuildStepProgressBar, { step: selectedStep }),
2865
- selectedStep.recommendedAction?.defaultSize !== void 0 && selectedStep.recommendedAction.maxSize !== void 0 ? /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
2866
- "Batch size: ",
2867
- selectedStep.recommendedAction.defaultSize,
2868
- " default,",
2869
- " ",
2870
- selectedStep.recommendedAction.maxSize,
2871
- " max."
2872
- ] }) : null,
2873
- /* @__PURE__ */ jsxs(Group, { gap: "xs", justify: "flex-end", wrap: "nowrap", children: [
3148
+ /* @__PURE__ */ jsx(BuildStepProgressBar, { step: selectedStep }),
2874
3149
  /* @__PURE__ */ jsx(
2875
- Button,
3150
+ StepDetailRightColumn,
2876
3151
  {
2877
- leftSection: selectedActionIcon,
2878
- disabled: !canRunSelectedAction,
2879
- onClick: handleSelectedAction,
2880
- children: selectedActionKind === "none" ? "No action" : getStepActionLabel(selectedActionKind)
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
3187
+ },
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
3238
+ ] })
3239
+ ] })
2881
3240
  }
2882
- ),
2883
- /* @__PURE__ */ jsx(Button, { variant: "light", leftSection: /* @__PURE__ */ jsx(IconPlayerPlay, { size: 14 }), onClick: onViewRuns, children: "View runs" })
2884
- ] })
2885
- ] }) }) : null
2886
- ] })
2887
- ] });
3241
+ )
3242
+ ] }) }) : null
3243
+ ] })
3244
+ }
3245
+ );
2888
3246
  }
2889
3247
  function MembersTab({
2890
3248
  listId,
@@ -2896,75 +3254,76 @@ function MembersTab({
2896
3254
  const companiesQuery = useCompanies({ listId, limit: 100, offset: 0 });
2897
3255
  const contacts = contactsQuery.data?.data ?? [];
2898
3256
  const companies = companiesQuery.data?.data ?? [];
2899
- return /* @__PURE__ */ jsxs(Stack, { gap: "sm", p: "md", children: [
2900
- /* @__PURE__ */ jsx(
2901
- CardHeader,
2902
- {
2903
- icon: /* @__PURE__ */ jsx(IconAddressBook, { size: 16 }),
2904
- title: "Members",
2905
- rightSection: /* @__PURE__ */ jsx(
2906
- SegmentedControl,
2907
- {
2908
- value: memberTab,
2909
- onChange: (value) => setMemberTab(value),
2910
- size: "xs",
2911
- data: [
2912
- { label: `Members (${progress.totalMembers})`, value: "contacts" },
2913
- { label: `Companies (${progress.totalCompanies})`, value: "companies" }
2914
- ]
2915
- }
2916
- )
2917
- }
2918
- ),
2919
- memberTab === "contacts" && /* @__PURE__ */ jsx(Fragment, { children: contactsQuery.isLoading ? /* @__PURE__ */ jsx(Center, { p: "md", children: /* @__PURE__ */ jsx(Loader, { size: "sm" }) }) : !contacts.length ? /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "No contacts attached to this list yet." }) : /* @__PURE__ */ jsxs(Table, { highlightOnHover: true, children: [
2920
- /* @__PURE__ */ jsx(Table.Thead, { children: /* @__PURE__ */ jsxs(Table.Tr, { children: [
2921
- /* @__PURE__ */ jsx(Table.Th, { children: "Name" }),
2922
- /* @__PURE__ */ jsx(Table.Th, { children: "Email" }),
2923
- /* @__PURE__ */ jsx(Table.Th, { children: "Title" }),
2924
- /* @__PURE__ */ jsx(Table.Th, { children: "Company" }),
2925
- /* @__PURE__ */ jsx(Table.Th, { children: "Status" }),
2926
- /* @__PURE__ */ jsx(Table.Th, { children: "State" }),
2927
- /* @__PURE__ */ jsx(Table.Th, { children: "Created" })
2928
- ] }) }),
2929
- /* @__PURE__ */ jsx(Table.Tbody, { children: contacts.map((contact) => {
2930
- const handleRowClick = () => onMemberClick?.(contact.id, "contact");
2931
- const memberState = contact.stateKey ?? null;
2932
- return /* @__PURE__ */ jsxs(Table.Tr, { onClick: handleRowClick, style: { cursor: "pointer" }, children: [
2933
- /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { fw: 500, children: contactDisplayName(contact.firstName, contact.lastName) }) }),
2934
- /* @__PURE__ */ jsx(Table.Td, { children: contact.email }),
2935
- /* @__PURE__ */ jsx(Table.Td, { children: contact.title ?? "\u2014" }),
2936
- /* @__PURE__ */ jsx(Table.Td, { children: contact.company?.name ?? "\u2014" }),
2937
- /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: getStatusColor4(contact.status), children: contact.status }) }),
2938
- /* @__PURE__ */ jsx(Table.Td, { children: memberState ? /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "dot", color: getMemberStateColor2(memberState), children: memberState }) : /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "\u2014" }) }),
2939
- /* @__PURE__ */ jsx(Table.Td, { children: formatDateTime4(contact.createdAt) })
2940
- ] }, contact.id);
2941
- }) })
2942
- ] }) }),
2943
- memberTab === "companies" && /* @__PURE__ */ jsx(Fragment, { children: companiesQuery.isLoading ? /* @__PURE__ */ jsx(Center, { p: "md", children: /* @__PURE__ */ jsx(Loader, { size: "sm" }) }) : !companies.length ? /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "No companies attached to this list yet." }) : /* @__PURE__ */ jsxs(Table, { highlightOnHover: true, children: [
2944
- /* @__PURE__ */ jsx(Table.Thead, { children: /* @__PURE__ */ jsxs(Table.Tr, { children: [
2945
- /* @__PURE__ */ jsx(Table.Th, { children: "Name" }),
2946
- /* @__PURE__ */ jsx(Table.Th, { children: "Domain" }),
2947
- /* @__PURE__ */ jsx(Table.Th, { children: "Segment" }),
2948
- /* @__PURE__ */ jsx(Table.Th, { children: "Contacts" }),
2949
- /* @__PURE__ */ jsx(Table.Th, { children: "Status" }),
2950
- /* @__PURE__ */ jsx(Table.Th, { children: "State" }),
2951
- /* @__PURE__ */ jsx(Table.Th, { children: "Created" })
2952
- ] }) }),
2953
- /* @__PURE__ */ jsx(Table.Tbody, { children: companies.map((company) => {
2954
- const handleRowClick = () => onMemberClick?.(company.id, "company");
2955
- const memberState = company.stateKey ?? null;
2956
- return /* @__PURE__ */ jsxs(Table.Tr, { onClick: handleRowClick, style: { cursor: "pointer" }, children: [
2957
- /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { fw: 500, children: company.name }) }),
2958
- /* @__PURE__ */ jsx(Table.Td, { children: company.domain ?? "\u2014" }),
2959
- /* @__PURE__ */ jsx(Table.Td, { children: company.segment ?? "\u2014" }),
2960
- /* @__PURE__ */ jsx(Table.Td, { children: company.contactCount }),
2961
- /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: getStatusColor4(company.status), children: company.status }) }),
2962
- /* @__PURE__ */ jsx(Table.Td, { children: memberState ? /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "dot", color: getMemberStateColor2(memberState), children: memberState }) : /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "\u2014" }) }),
2963
- /* @__PURE__ */ jsx(Table.Td, { children: formatDateTime4(company.createdAt) })
2964
- ] }, company.id);
2965
- }) })
2966
- ] }) })
2967
- ] });
3257
+ return /* @__PURE__ */ jsxs(
3258
+ TabSection,
3259
+ {
3260
+ icon: /* @__PURE__ */ jsx(IconAddressBook, { size: 16 }),
3261
+ title: "Members",
3262
+ description: "Contacts and companies attached to this list.",
3263
+ rightSection: /* @__PURE__ */ jsx(
3264
+ SegmentedControl,
3265
+ {
3266
+ value: memberTab,
3267
+ onChange: (value) => setMemberTab(value),
3268
+ size: "xs",
3269
+ data: [
3270
+ { label: `Members (${progress.totalMembers})`, value: "contacts" },
3271
+ { label: `Companies (${progress.totalCompanies})`, value: "companies" }
3272
+ ]
3273
+ }
3274
+ ),
3275
+ children: [
3276
+ memberTab === "contacts" && /* @__PURE__ */ jsx(Fragment, { children: contactsQuery.isLoading ? /* @__PURE__ */ jsx(Center, { p: "md", children: /* @__PURE__ */ jsx(Loader, { size: "sm" }) }) : !contacts.length ? /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "No contacts attached to this list yet." }) : /* @__PURE__ */ jsxs(Table, { highlightOnHover: true, children: [
3277
+ /* @__PURE__ */ jsx(Table.Thead, { children: /* @__PURE__ */ jsxs(Table.Tr, { children: [
3278
+ /* @__PURE__ */ jsx(Table.Th, { children: "Name" }),
3279
+ /* @__PURE__ */ jsx(Table.Th, { children: "Email" }),
3280
+ /* @__PURE__ */ jsx(Table.Th, { children: "Title" }),
3281
+ /* @__PURE__ */ jsx(Table.Th, { children: "Company" }),
3282
+ /* @__PURE__ */ jsx(Table.Th, { children: "Status" }),
3283
+ /* @__PURE__ */ jsx(Table.Th, { children: "State" }),
3284
+ /* @__PURE__ */ jsx(Table.Th, { children: "Created" })
3285
+ ] }) }),
3286
+ /* @__PURE__ */ jsx(Table.Tbody, { children: contacts.map((contact) => {
3287
+ const handleRowClick = () => onMemberClick?.(contact.id, "contact");
3288
+ const memberState = contact.stateKey ?? null;
3289
+ return /* @__PURE__ */ jsxs(Table.Tr, { onClick: handleRowClick, style: { cursor: "pointer" }, children: [
3290
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { fw: 500, children: contactDisplayName(contact.firstName, contact.lastName) }) }),
3291
+ /* @__PURE__ */ jsx(Table.Td, { children: contact.email }),
3292
+ /* @__PURE__ */ jsx(Table.Td, { children: contact.title ?? "\u2014" }),
3293
+ /* @__PURE__ */ jsx(Table.Td, { children: contact.company?.name ?? "\u2014" }),
3294
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: getStatusColor4(contact.status), children: contact.status }) }),
3295
+ /* @__PURE__ */ jsx(Table.Td, { children: memberState ? /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "dot", color: getMemberStateColor2(memberState), children: memberState }) : /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "\u2014" }) }),
3296
+ /* @__PURE__ */ jsx(Table.Td, { children: formatDateTime4(contact.createdAt) })
3297
+ ] }, contact.id);
3298
+ }) })
3299
+ ] }) }),
3300
+ memberTab === "companies" && /* @__PURE__ */ jsx(Fragment, { children: companiesQuery.isLoading ? /* @__PURE__ */ jsx(Center, { p: "md", children: /* @__PURE__ */ jsx(Loader, { size: "sm" }) }) : !companies.length ? /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "No companies attached to this list yet." }) : /* @__PURE__ */ jsxs(Table, { highlightOnHover: true, children: [
3301
+ /* @__PURE__ */ jsx(Table.Thead, { children: /* @__PURE__ */ jsxs(Table.Tr, { children: [
3302
+ /* @__PURE__ */ jsx(Table.Th, { children: "Name" }),
3303
+ /* @__PURE__ */ jsx(Table.Th, { children: "Domain" }),
3304
+ /* @__PURE__ */ jsx(Table.Th, { children: "Segment" }),
3305
+ /* @__PURE__ */ jsx(Table.Th, { children: "Contacts" }),
3306
+ /* @__PURE__ */ jsx(Table.Th, { children: "Status" }),
3307
+ /* @__PURE__ */ jsx(Table.Th, { children: "State" }),
3308
+ /* @__PURE__ */ jsx(Table.Th, { children: "Created" })
3309
+ ] }) }),
3310
+ /* @__PURE__ */ jsx(Table.Tbody, { children: companies.map((company) => {
3311
+ const handleRowClick = () => onMemberClick?.(company.id, "company");
3312
+ const memberState = company.stateKey ?? null;
3313
+ return /* @__PURE__ */ jsxs(Table.Tr, { onClick: handleRowClick, style: { cursor: "pointer" }, children: [
3314
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { fw: 500, children: company.name }) }),
3315
+ /* @__PURE__ */ jsx(Table.Td, { children: company.domain ?? "\u2014" }),
3316
+ /* @__PURE__ */ jsx(Table.Td, { children: company.segment ?? "\u2014" }),
3317
+ /* @__PURE__ */ jsx(Table.Td, { children: company.contactCount }),
3318
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: getStatusColor4(company.status), children: company.status }) }),
3319
+ /* @__PURE__ */ jsx(Table.Td, { children: memberState ? /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "dot", color: getMemberStateColor2(memberState), children: memberState }) : /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "\u2014" }) }),
3320
+ /* @__PURE__ */ jsx(Table.Td, { children: formatDateTime4(company.createdAt) })
3321
+ ] }, company.id);
3322
+ }) })
3323
+ ] }) })
3324
+ ]
3325
+ }
3326
+ );
2968
3327
  }
2969
3328
  function ListDetailHeader({
2970
3329
  title,
@@ -2995,13 +3354,22 @@ function LeadGenListDetailPage({ listId }) {
2995
3354
  const listQuery = useList(listId);
2996
3355
  const progressQuery = useListProgress(listId);
2997
3356
  const deleteListMutation = useDeleteList();
2998
- const updateListMutation = useUpdateList(listId);
2999
3357
  const [deleteModalOpen, setDeleteModalOpen] = useState(false);
3000
- const [pipelineModalOpen, setPipelineModalOpen] = useState(false);
3001
- const [selectedBuildTemplateId, setSelectedBuildTemplateId] = useState(DEFAULT_PROSPECTING_BUILD_TEMPLATE_ID);
3002
3358
  const [runModalOpen, setRunModalOpen] = useState(false);
3003
3359
  const [activeTab, setActiveTab] = useState("overview");
3360
+ const [runsResourceFilter, setRunsResourceFilter] = useState(null);
3004
3361
  const [initialRunResourceId, setInitialRunResourceId] = useState(null);
3362
+ const [pendingStepRunInput, setPendingStepRunInput] = useState(null);
3363
+ const runModalActions = useMemo(() => {
3364
+ if (!pendingStepRunInput) return actions;
3365
+ return actions.map((action) => {
3366
+ if (action.resourceId !== pendingStepRunInput.resourceId) return action;
3367
+ return {
3368
+ ...action,
3369
+ defaultInput: (nextList) => mergeStepRunInput(nextList, action, pendingStepRunInput.inputAtSubmit)
3370
+ };
3371
+ });
3372
+ }, [actions, pendingStepRunInput]);
3005
3373
  const isLoading = listQuery.isLoading || progressQuery.isLoading;
3006
3374
  const error = listQuery.error ?? progressQuery.error;
3007
3375
  const backButton = /* @__PURE__ */ jsx(
@@ -3055,12 +3423,7 @@ function LeadGenListDetailPage({ listId }) {
3055
3423
  }
3056
3424
  const list = listQuery.data;
3057
3425
  const progress = progressQuery.data;
3058
- const currentBuildTemplateId = list.metadata.buildPlanSnapshot?.templateId ?? DEFAULT_PROSPECTING_BUILD_TEMPLATE_ID;
3059
3426
  const currentBuildTemplateLabel = list.metadata.buildPlanSnapshot?.templateLabel ?? "Default lead generation";
3060
- const selectedBuildTemplate = PROSPECTING_BUILD_TEMPLATE_OPTIONS.find(
3061
- (template) => template.id === selectedBuildTemplateId
3062
- );
3063
- const isSameBuildTemplate = selectedBuildTemplateId === currentBuildTemplateId;
3064
3427
  const buildResolution = resolveBuildState(list, progress, actions);
3065
3428
  const buildSteps = buildResolution.steps;
3066
3429
  const recommendedBuildStep = buildResolution.recommendedStep;
@@ -3072,8 +3435,17 @@ function LeadGenListDetailPage({ listId }) {
3072
3435
  const closeRunModal = () => {
3073
3436
  setRunModalOpen(false);
3074
3437
  setInitialRunResourceId(null);
3438
+ setPendingStepRunInput(null);
3075
3439
  };
3076
- const openStepRun = (step) => {
3440
+ const openStepRun = (step, inputAtSubmit) => {
3441
+ if (step.action?.schema) {
3442
+ setPendingStepRunInput({
3443
+ resourceId: step.action.resourceId,
3444
+ inputAtSubmit
3445
+ });
3446
+ } else {
3447
+ setPendingStepRunInput(null);
3448
+ }
3077
3449
  openRunModal(step.action?.resourceId ?? null);
3078
3450
  };
3079
3451
  const handleCopyListCommand = () => {
@@ -3088,29 +3460,6 @@ function LeadGenListDetailPage({ listId }) {
3088
3460
  }
3089
3461
  });
3090
3462
  };
3091
- const openPipelineModal = () => {
3092
- setSelectedBuildTemplateId(currentBuildTemplateId);
3093
- setPipelineModalOpen(true);
3094
- };
3095
- const closePipelineModal = () => {
3096
- if (updateListMutation.isPending) return;
3097
- setPipelineModalOpen(false);
3098
- setSelectedBuildTemplateId(currentBuildTemplateId);
3099
- };
3100
- const handleChangePipeline = () => {
3101
- if (isSameBuildTemplate) return;
3102
- updateListMutation.mutate(
3103
- {
3104
- buildTemplateId: selectedBuildTemplateId,
3105
- confirmBuildTemplateChange: true
3106
- },
3107
- {
3108
- onSuccess: () => {
3109
- setPipelineModalOpen(false);
3110
- }
3111
- }
3112
- );
3113
- };
3114
3463
  return /* @__PURE__ */ jsxs(SubshellContentContainer, { children: [
3115
3464
  /* @__PURE__ */ jsx(PageContainer, { children: /* @__PURE__ */ jsxs(Stack, { children: [
3116
3465
  /* @__PURE__ */ jsx(
@@ -3130,16 +3479,6 @@ function LeadGenListDetailPage({ listId }) {
3130
3479
  ] })
3131
3480
  ] }),
3132
3481
  /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
3133
- /* @__PURE__ */ jsx(
3134
- Button,
3135
- {
3136
- size: "xs",
3137
- variant: "light",
3138
- leftSection: /* @__PURE__ */ jsx(IconSwitchHorizontal, { size: 14 }),
3139
- onClick: openPipelineModal,
3140
- children: "Change pipeline"
3141
- }
3142
- ),
3143
3482
  /* @__PURE__ */ jsxs(Group, { gap: 4, wrap: "nowrap", onClick: handleCopyListCommand, style: { cursor: "pointer" }, children: [
3144
3483
  /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", ff: "monospace", children: list.id }),
3145
3484
  /* @__PURE__ */ jsx(ActionIcon, { variant: "subtle", size: "sm", "aria-label": "Copy list command", children: /* @__PURE__ */ jsx(IconCopy, { size: 14 }) })
@@ -3171,60 +3510,29 @@ function LeadGenListDetailPage({ listId }) {
3171
3510
  /* @__PURE__ */ jsx(Tabs.Panel, { value: "build", pt: "sm", children: /* @__PURE__ */ jsx(
3172
3511
  BuildTab,
3173
3512
  {
3513
+ list,
3174
3514
  steps: buildSteps,
3175
3515
  recommendedStep: recommendedBuildStep,
3176
3516
  onRunStep: openStepRun,
3177
3517
  onRetryStep: openStepRun,
3178
- onViewRuns: () => setActiveTab("runs")
3518
+ onViewRuns: (resourceId) => {
3519
+ setRunsResourceFilter(resourceId);
3520
+ setActiveTab("runs");
3521
+ }
3179
3522
  }
3180
3523
  ) }),
3181
- /* @__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
+ ) })
3182
3533
  ] }) })
3183
3534
  ] }) }),
3184
3535
  /* @__PURE__ */ jsx(ListMemberDrawer, { memberId, memberKind, listId, onClose: handleDrawerClose }),
3185
- /* @__PURE__ */ jsx(
3186
- CustomModal,
3187
- {
3188
- opened: pipelineModalOpen,
3189
- onClose: closePipelineModal,
3190
- size: "md",
3191
- loading: updateListMutation.isPending,
3192
- children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
3193
- /* @__PURE__ */ jsxs(Group, { gap: "sm", children: [
3194
- /* @__PURE__ */ jsx(IconSwitchHorizontal, { size: 22 }),
3195
- /* @__PURE__ */ jsx(Title, { order: 4, children: "Change build pipeline" })
3196
- ] }),
3197
- /* @__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." }) }),
3198
- /* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
3199
- /* @__PURE__ */ jsx(
3200
- Select,
3201
- {
3202
- label: "Build Pipeline",
3203
- data: BUILD_TEMPLATE_SELECT_OPTIONS2,
3204
- value: selectedBuildTemplateId,
3205
- onChange: (value) => setSelectedBuildTemplateId(value ?? currentBuildTemplateId),
3206
- disabled: updateListMutation.isPending,
3207
- required: true
3208
- }
3209
- ),
3210
- selectedBuildTemplate?.description ? /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: selectedBuildTemplate.description }) : null
3211
- ] }),
3212
- /* @__PURE__ */ jsxs(Group, { justify: "flex-end", mt: "md", children: [
3213
- /* @__PURE__ */ jsx(Button, { variant: "light", onClick: closePipelineModal, disabled: updateListMutation.isPending, children: "Cancel" }),
3214
- /* @__PURE__ */ jsx(
3215
- Button,
3216
- {
3217
- color: "yellow",
3218
- loading: updateListMutation.isPending,
3219
- disabled: isSameBuildTemplate,
3220
- onClick: handleChangePipeline,
3221
- children: "Confirm pipeline change"
3222
- }
3223
- )
3224
- ] })
3225
- ] })
3226
- }
3227
- ),
3228
3536
  /* @__PURE__ */ jsx(
3229
3537
  CustomModal,
3230
3538
  {
@@ -3257,13 +3565,13 @@ function LeadGenListDetailPage({ listId }) {
3257
3565
  opened: runModalOpen,
3258
3566
  onClose: closeRunModal,
3259
3567
  list,
3260
- actions,
3568
+ actions: runModalActions,
3261
3569
  selection: EMPTY_SELECTION2,
3262
3570
  initialResourceId: initialRunResourceId,
3263
3571
  title: "Run build step",
3264
3572
  description: "Run the selected bounded build workflow for this list.",
3265
3573
  lockResourceSelection: true,
3266
- onSubmitted: (_resourceId, executionId) => {
3574
+ onSubmitted: (_, executionId) => {
3267
3575
  showSuccessNotification(`Workflow run started: ${executionId}`);
3268
3576
  }
3269
3577
  }
@@ -3628,4 +3936,4 @@ function LeadGenContactsPage() {
3628
3936
  ] }) });
3629
3937
  }
3630
3938
 
3631
- 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, 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 };