@elevasis/ui 2.28.1 → 2.30.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 (85) hide show
  1. package/dist/api/index.js +2 -2
  2. package/dist/app/index.d.ts +54 -0
  3. package/dist/app/index.js +10 -10
  4. package/dist/auth/index.js +4 -4
  5. package/dist/charts/index.d.ts +2 -1
  6. package/dist/charts/index.js +8 -8
  7. package/dist/{chunk-HNN3QALL.js → chunk-2DIYILF7.js} +9 -8
  8. package/dist/{chunk-XTVZFT7U.js → chunk-2Q2JQSQO.js} +1 -1
  9. package/dist/{chunk-UOHOKDKL.js → chunk-3GV5NHSS.js} +1052 -209
  10. package/dist/{chunk-PBNIW7KV.js → chunk-3MDNBHVB.js} +140 -9
  11. package/dist/{chunk-K4Q5QUHZ.js → chunk-4FZYEEPK.js} +6 -6
  12. package/dist/{chunk-MJMFIWEB.js → chunk-4SY6BTVZ.js} +2 -2
  13. package/dist/{chunk-HLFFKKT3.js → chunk-4VQ2PXMI.js} +14 -14
  14. package/dist/{chunk-WKJ47GIW.js → chunk-533DUEQY.js} +1 -1
  15. package/dist/{chunk-N63RKL3L.js → chunk-6EFVZV6X.js} +394 -311
  16. package/dist/{chunk-V3HUIZJX.js → chunk-6IXOKUBC.js} +1 -1
  17. package/dist/{chunk-GJXAAYZ3.js → chunk-6WXDE5LZ.js} +1 -1
  18. package/dist/{chunk-VKMNWHTL.js → chunk-6YT4IKJ7.js} +3 -3
  19. package/dist/{chunk-ZPXV5KI3.js → chunk-7E3FUTND.js} +1 -1
  20. package/dist/{chunk-YNYGUVGW.js → chunk-A7B7HLDF.js} +11 -10
  21. package/dist/{chunk-NU5FNTOB.js → chunk-AKOD52HS.js} +84 -15
  22. package/dist/{chunk-CT5IR4ZA.js → chunk-CLUP5H3C.js} +6 -9
  23. package/dist/{chunk-MYEOTM7D.js → chunk-CN2HC4D4.js} +5 -1
  24. package/dist/{chunk-ROSMICXG.js → chunk-CXY7FMUM.js} +35 -20
  25. package/dist/{chunk-HOIT677G.js → chunk-HUJCU55S.js} +1 -1
  26. package/dist/{chunk-SRNIHB7Y.js → chunk-HXZQWMKE.js} +1 -2
  27. package/dist/{chunk-M6OJ43NL.js → chunk-HYLERWRO.js} +99 -30
  28. package/dist/{chunk-XBMCDGHA.js → chunk-IKQ42WHU.js} +1 -1
  29. package/dist/{chunk-GESXCQWY.js → chunk-JA5ECJJB.js} +1 -1
  30. package/dist/{chunk-KU7ZDWQ7.js → chunk-JBWJ6WHZ.js} +1 -1
  31. package/dist/{chunk-MCRKFDKB.js → chunk-JKTPRYGV.js} +5 -5
  32. package/dist/{chunk-5WWZXCS5.js → chunk-KJ3QUBNU.js} +9 -2
  33. package/dist/{chunk-3HCTCMAS.js → chunk-LRZFLK2F.js} +3 -3
  34. package/dist/chunk-NITGGYH2.js +476 -0
  35. package/dist/{chunk-VMJVQAFZ.js → chunk-OAVTMITG.js} +1 -1
  36. package/dist/{chunk-TVJROM2V.js → chunk-P5WYW2GI.js} +58 -37
  37. package/dist/{chunk-5R27NFOI.js → chunk-SBCIB5TZ.js} +6 -27
  38. package/dist/{chunk-AYSO5A3R.js → chunk-SKXXT3E2.js} +4 -4
  39. package/dist/{chunk-MZCDRBSI.js → chunk-T2PAD63Y.js} +12 -12
  40. package/dist/{chunk-LH4GPYDX.js → chunk-T5Z7G2J2.js} +19 -3
  41. package/dist/{chunk-WFTNY755.js → chunk-VKIZUUPM.js} +1 -1
  42. package/dist/{chunk-S66IQSSR.js → chunk-WF227UBV.js} +1 -1
  43. package/dist/components/chat/index.d.ts +2 -1
  44. package/dist/components/chat/index.js +1 -1
  45. package/dist/components/index.js +42 -42
  46. package/dist/components/navigation/index.js +8 -8
  47. package/dist/features/auth/index.js +5 -5
  48. package/dist/features/crm/index.js +21 -21
  49. package/dist/features/dashboard/index.js +22 -22
  50. package/dist/features/delivery/index.js +21 -21
  51. package/dist/features/knowledge/index.js +45 -31
  52. package/dist/features/lead-gen/index.d.ts +116 -2
  53. package/dist/features/lead-gen/index.js +22 -22
  54. package/dist/features/monitoring/index.js +23 -23
  55. package/dist/features/monitoring/requests/index.js +20 -20
  56. package/dist/features/operations/index.js +27 -27
  57. package/dist/features/settings/index.js +22 -22
  58. package/dist/hooks/delivery/index.js +3 -3
  59. package/dist/hooks/index.d.ts +263 -4
  60. package/dist/hooks/index.js +20 -20
  61. package/dist/hooks/published.d.ts +263 -4
  62. package/dist/hooks/published.js +20 -20
  63. package/dist/index.d.ts +378 -10
  64. package/dist/index.js +21 -21
  65. package/dist/initialization/index.js +4 -4
  66. package/dist/knowledge/index.d.ts +55 -1
  67. package/dist/knowledge/index.js +102 -2
  68. package/dist/organization/index.js +4 -4
  69. package/dist/profile/index.js +2 -2
  70. package/dist/provider/ElevasisServiceContext.d.ts +11 -5
  71. package/dist/provider/ElevasisServiceContext.js +1 -1
  72. package/dist/provider/index.d.ts +119 -5
  73. package/dist/provider/index.js +17 -17
  74. package/dist/provider/published.d.ts +119 -5
  75. package/dist/provider/published.js +13 -13
  76. package/dist/test-utils/index.d.ts +3 -0
  77. package/dist/test-utils/index.js +30 -6
  78. package/dist/theme/index.js +3 -3
  79. package/dist/theme/presets/index.js +1 -1
  80. package/dist/utils/index.d.ts +1 -3
  81. package/dist/utils/index.js +1 -1
  82. package/dist/vite/index.js +2 -2
  83. package/dist/vite-plugin-knowledge/index.js +1 -1
  84. package/package.json +5 -5
  85. package/dist/chunk-JS7VBTC4.js +0 -250
@@ -1,4 +1,4 @@
1
- import { DEFAULT_ORGANIZATION_MODEL_PROSPECTING, PROSPECTING_STEPS } from './chunk-JS7VBTC4.js';
1
+ import { DEFAULT_ORGANIZATION_MODEL_PROSPECTING, PROSPECTING_STEPS } from './chunk-NITGGYH2.js';
2
2
  import { sanitizeInput } from './chunk-3MEXPLWT.js';
3
3
  import { PageContainer } from './chunk-BZZCNLT6.js';
4
4
  import { TableSelectionToolbar, SortableHeader } from './chunk-TUMSNGTX.js';
@@ -6,18 +6,19 @@ import { SubshellNavItem } from './chunk-X4WBGKJQ.js';
6
6
  import { SubshellSidebarSection } from './chunk-IIMU5YAJ.js';
7
7
  import { FilterBar } from './chunk-PDHTXPSF.js';
8
8
  import { CustomModal } from './chunk-KVJ3LFH2.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-N63RKL3L.js';
10
- import { showApiErrorNotification, showSuccessNotification } from './chunk-MZCDRBSI.js';
11
- import { PageTitleCaption, CenteredErrorState, StatCard, CardHeader, EmptyState, JsonViewer } from './chunk-GJXAAYZ3.js';
12
- import { useListActions, LEAD_GEN_STAGE_CATALOG, LEAD_GEN_PIPELINE_DEFINITIONS, findPipeline } from './chunk-NU5FNTOB.js';
9
+ import { acquisitionListKeys, useListsTelemetry, useLists, useCreateList, useTableSort, sortData, usePaginationState, useTableSelection, useWorkflowExecution, useList, useListProgress, useListExecutions, useDeleteList, useCompanyFacets, useCompanies, useDeleteCompanies, useContacts, useDeleteContacts, useCredentials, useVerifyCredential, useInFlightExecutions, useListRecords, useCompany, useContact, useExecutionSSE, useListMembers, useListMember, useTransitionListMember, useDeriveActions, useArtifacts } from './chunk-6EFVZV6X.js';
10
+ import { showApiErrorNotification, showSuccessNotification } from './chunk-T2PAD63Y.js';
11
+ import { PageTitleCaption, CenteredErrorState, StatCard, CardHeader, EmptyState, JsonViewer } from './chunk-6WXDE5LZ.js';
12
+ import { useListActions, LEAD_GEN_STAGE_CATALOG, LEAD_GEN_PIPELINE_DEFINITIONS, findPipeline } from './chunk-AKOD52HS.js';
13
13
  import { SubshellContentContainer } from './chunk-TKAYX2SP.js';
14
14
  import { useRouterContext } from './chunk-Q7DJKLEN.js';
15
- import { useElevasisServices } from './chunk-5WWZXCS5.js';
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, IconSettings, IconMail, IconUser, IconDatabase } from '@tabler/icons-react';
15
+ import { isStepStartedContext, isStepCompletedContext, isStepFailedContext } from './chunk-KRWALB24.js';
16
+ import { useElevasisServices } from './chunk-KJ3QUBNU.js';
17
+ 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, Progress } from '@mantine/core';
18
+ 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, IconSettings, IconCircleCheck, IconCircleDot, IconTerminal2, IconProgressCheck, IconMail, IconUser, IconDatabase } from '@tabler/icons-react';
18
19
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
19
20
  import { Link, useNavigate, useSearch } from '@tanstack/react-router';
20
- import { useState, useRef, useMemo, useEffect, Fragment as Fragment$1 } from 'react';
21
+ import { useState, useRef, useMemo, useEffect, Fragment as Fragment$1, useCallback } from 'react';
21
22
  import { useForm } from '@mantine/form';
22
23
  import { zod4Resolver } from 'mantine-form-zod-resolver';
23
24
  import { useQueryClient, useMutation } from '@tanstack/react-query';
@@ -84,13 +85,25 @@ function createBuildPlanSnapshotFromTemplateId(templateId) {
84
85
  primaryEntity: step.primaryEntity,
85
86
  outputs: [...step.outputs],
86
87
  stageKey: step.stageKey,
88
+ recordsStageKey: step.recordsStageKey ?? step.stageKey,
89
+ recordSourceStageKey: step.recordSourceStageKey ?? step.recordsStageKey ?? step.stageKey,
87
90
  dependencyMode: step.dependencyMode,
88
91
  capabilityKey: step.capabilityKey,
89
92
  defaultBatchSize: step.defaultBatchSize,
90
93
  maxBatchSize: step.maxBatchSize
91
94
  };
92
95
  if (step.description) snapshotStep.description = step.description;
96
+ if (step.recordEntity) snapshotStep.recordEntity = step.recordEntity;
93
97
  if (step.dependsOn?.length) snapshotStep.dependsOn = [...step.dependsOn];
98
+ if (step.credentialRequirements?.length) {
99
+ snapshotStep.credentialRequirements = step.credentialRequirements.map((requirement) => ({ ...requirement }));
100
+ }
101
+ if (step.recordColumns) {
102
+ snapshotStep.recordColumns = {
103
+ ...step.recordColumns.company ? { company: step.recordColumns.company.map((column) => ({ ...column })) } : {},
104
+ ...step.recordColumns.contact ? { contact: step.recordColumns.contact.map((column) => ({ ...column })) } : {}
105
+ };
106
+ }
94
107
  return snapshotStep;
95
108
  })
96
109
  };
@@ -586,7 +599,7 @@ function ContactDetailModal({
586
599
  ] }) : null });
587
600
  }
588
601
  function useDeleteLists() {
589
- const { apiRequest, organizationId } = useElevasisServices();
602
+ const { apiRequest, workOSOrganizationId } = useElevasisServices();
590
603
  const queryClient = useQueryClient();
591
604
  return useMutation({
592
605
  mutationFn: async (listIds) => {
@@ -601,8 +614,8 @@ function useDeleteLists() {
601
614
  );
602
615
  },
603
616
  onSuccess: () => {
604
- queryClient.invalidateQueries({ queryKey: acquisitionListKeys.list(organizationId) });
605
- queryClient.invalidateQueries({ queryKey: acquisitionListKeys.telemetry(organizationId) });
617
+ queryClient.invalidateQueries({ queryKey: acquisitionListKeys.list(workOSOrganizationId) });
618
+ queryClient.invalidateQueries({ queryKey: acquisitionListKeys.telemetry(workOSOrganizationId) });
606
619
  showSuccessNotification("Lists deleted");
607
620
  },
608
621
  onError: (error) => {
@@ -1369,6 +1382,253 @@ function ListMemberDrawer({ memberId, memberKind, listId, onClose }) {
1369
1382
  }
1370
1383
  );
1371
1384
  }
1385
+ function formatEventTime(timestamp) {
1386
+ return new Date(timestamp).toLocaleTimeString("en-US", {
1387
+ hour12: false,
1388
+ hour: "2-digit",
1389
+ minute: "2-digit",
1390
+ second: "2-digit"
1391
+ });
1392
+ }
1393
+ function formatId(executionId) {
1394
+ return executionId.length > 10 ? `${executionId.slice(0, 8)}...` : executionId;
1395
+ }
1396
+ function getLogLevelColor(level) {
1397
+ switch (level) {
1398
+ case "error":
1399
+ return "red";
1400
+ case "warn":
1401
+ return "yellow";
1402
+ case "debug":
1403
+ return "gray";
1404
+ default:
1405
+ return "blue";
1406
+ }
1407
+ }
1408
+ function getStepStatusColor(status) {
1409
+ switch (status) {
1410
+ case "failed":
1411
+ return "red";
1412
+ case "done":
1413
+ return "green";
1414
+ default:
1415
+ return "blue";
1416
+ }
1417
+ }
1418
+ function getStepStatusLabel(status) {
1419
+ switch (status) {
1420
+ case "failed":
1421
+ return "Failed";
1422
+ case "done":
1423
+ return "Done";
1424
+ default:
1425
+ return "Running";
1426
+ }
1427
+ }
1428
+ function getEventColor(event) {
1429
+ switch (event.type) {
1430
+ case "new-execution":
1431
+ return "blue";
1432
+ case "execution-complete":
1433
+ return event.data.success ? "green" : "red";
1434
+ case "log":
1435
+ return getLogLevelColor(event.data.log.level);
1436
+ default:
1437
+ return "gray";
1438
+ }
1439
+ }
1440
+ function getEventLabel(event) {
1441
+ switch (event.type) {
1442
+ case "new-execution":
1443
+ return "Execution started";
1444
+ case "execution-complete":
1445
+ return event.data.success ? "Execution completed" : "Execution failed";
1446
+ case "log":
1447
+ return event.data.log.message;
1448
+ default:
1449
+ return "Stream connected";
1450
+ }
1451
+ }
1452
+ function hasExecutionId(event) {
1453
+ return event.type !== "connected" && typeof event.executionId === "string" && event.executionId.length > 0;
1454
+ }
1455
+ function toTimelineEvents(events) {
1456
+ return events.filter(hasExecutionId).map((event) => ({
1457
+ executionId: event.executionId,
1458
+ timestamp: event.timestamp,
1459
+ label: getEventLabel(event),
1460
+ color: getEventColor(event),
1461
+ detail: event.type === "execution-complete" ? event.data.error : void 0
1462
+ })).sort((a, b) => b.timestamp - a.timestamp).slice(0, 5);
1463
+ }
1464
+ function collectStepStatuses(events, streamingLogs) {
1465
+ const statuses = /* @__PURE__ */ new Map();
1466
+ const applyLog = (executionId, log) => {
1467
+ const context = log.context;
1468
+ if (!context || context.type !== "workflow") return;
1469
+ if (!("stepId" in context)) return;
1470
+ let status = null;
1471
+ if (isStepStartedContext(context)) status = "running";
1472
+ if (isStepCompletedContext(context)) status = "done";
1473
+ if (isStepFailedContext(context)) status = "failed";
1474
+ if (!status) return;
1475
+ statuses.set(executionId, {
1476
+ executionId,
1477
+ stepId: context.stepId,
1478
+ status,
1479
+ timestamp: log.timestamp,
1480
+ message: log.message
1481
+ });
1482
+ };
1483
+ events.forEach((event) => {
1484
+ if (event.type === "log") applyLog(event.executionId, event.data.log);
1485
+ });
1486
+ streamingLogs.forEach((logs, executionId) => {
1487
+ logs.forEach((log) => applyLog(executionId, log));
1488
+ });
1489
+ return Array.from(statuses.values()).sort((a, b) => b.timestamp - a.timestamp);
1490
+ }
1491
+ function getExecutionId(execution) {
1492
+ return "executionId" in execution ? execution.executionId : execution.id;
1493
+ }
1494
+ function getInFlightExecutions(data) {
1495
+ if (Array.isArray(data)) return data;
1496
+ if (data && typeof data === "object" && "executions" in data) {
1497
+ const executions = data.executions;
1498
+ return Array.isArray(executions) ? executions : [];
1499
+ }
1500
+ return [];
1501
+ }
1502
+ function getStageProgress(progress, stageKey) {
1503
+ return progress?.byCompanyStage[stageKey] ?? progress?.byContactStage[stageKey];
1504
+ }
1505
+ function getStageLabel(stageKey) {
1506
+ return LEAD_GEN_STAGE_CATALOG[stageKey]?.label ?? stageKey;
1507
+ }
1508
+ function getDoneCount(stageProgress) {
1509
+ return stageProgress?.attempted ?? 0;
1510
+ }
1511
+ function LeadGenLiveStatusPanel({
1512
+ listId,
1513
+ resourceId,
1514
+ stageKey,
1515
+ stepLabel,
1516
+ enabled = true,
1517
+ onRunningChange
1518
+ }) {
1519
+ const sse = useExecutionSSE(resourceId, {
1520
+ enabled: enabled && Boolean(resourceId),
1521
+ listId
1522
+ });
1523
+ const inFlightQuery = useInFlightExecutions(resourceId, {
1524
+ enabled: enabled && Boolean(resourceId),
1525
+ limit: 5,
1526
+ refetchInterval: enabled && Boolean(resourceId) ? 2e3 : false
1527
+ });
1528
+ const inFlightExecutions = useMemo(() => getInFlightExecutions(inFlightQuery.data), [inFlightQuery.data]);
1529
+ const activeExecutionIds = useMemo(() => {
1530
+ const ids = new Set(sse.liveExecutions);
1531
+ inFlightExecutions.forEach((execution) => {
1532
+ const executionId = getExecutionId(execution);
1533
+ if (executionId) ids.add(executionId);
1534
+ });
1535
+ return Array.from(ids);
1536
+ }, [inFlightExecutions, sse.liveExecutions]);
1537
+ const running = enabled && activeExecutionIds.length > 0;
1538
+ const [showCompletedActivity, setShowCompletedActivity] = useState(false);
1539
+ const progressQuery = useListProgress(listId, {
1540
+ enabled: enabled && Boolean(listId),
1541
+ refetchInterval: running ? 2e3 : false
1542
+ });
1543
+ useEffect(() => {
1544
+ onRunningChange?.(running);
1545
+ }, [onRunningChange, running]);
1546
+ const stepStatuses = useMemo(
1547
+ () => collectStepStatuses(sse.events, sse.streamingLogs),
1548
+ [sse.events, sse.streamingLogs]
1549
+ );
1550
+ const latestStepStatus = stepStatuses[0];
1551
+ const lastEvents = useMemo(() => toTimelineEvents(sse.events), [sse.events]);
1552
+ const stageProgress = getStageProgress(progressQuery.data, stageKey);
1553
+ const doneCount = getDoneCount(stageProgress);
1554
+ const totalCount = stageProgress?.total ?? 0;
1555
+ const failed = Boolean(sse.error) || sse.events.some((event) => event.type === "execution-complete" && !event.data.success);
1556
+ const latestEvent = sse.latestEvent;
1557
+ useEffect(() => {
1558
+ if (running || failed || sse.error) {
1559
+ setShowCompletedActivity(true);
1560
+ return;
1561
+ }
1562
+ if (latestEvent?.type !== "execution-complete") return;
1563
+ setShowCompletedActivity(true);
1564
+ const timeoutId = window.setTimeout(() => setShowCompletedActivity(false), 1e4);
1565
+ return () => window.clearTimeout(timeoutId);
1566
+ }, [failed, latestEvent, running, sse.error]);
1567
+ const hasActivity = activeExecutionIds.length > 0 || sse.streamingLogs.size > 0 || showCompletedActivity || Boolean(sse.error);
1568
+ if (!enabled || !hasActivity && !sse.error && !failed) {
1569
+ return null;
1570
+ }
1571
+ return /* @__PURE__ */ jsx(Paper, { withBorder: true, p: "sm", children: /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
1572
+ /* @__PURE__ */ jsx(
1573
+ CardHeader,
1574
+ {
1575
+ icon: /* @__PURE__ */ jsx(IconTerminal2, { size: 16 }),
1576
+ title: stepLabel ? `${stepLabel} status` : "Live status",
1577
+ rightSection: /* @__PURE__ */ jsxs(Group, { gap: 6, children: [
1578
+ /* @__PURE__ */ jsx(
1579
+ Badge,
1580
+ {
1581
+ size: "xs",
1582
+ variant: "light",
1583
+ color: sse.connected ? "green" : running ? "yellow" : "gray",
1584
+ leftSection: sse.connected ? /* @__PURE__ */ jsx(IconCircleCheck, { size: 9 }) : /* @__PURE__ */ jsx(IconCircleDot, { size: 9 }),
1585
+ children: sse.connected ? "Connected" : running ? "Reconnecting" : "Idle"
1586
+ }
1587
+ ),
1588
+ running ? /* @__PURE__ */ jsxs(Badge, { size: "xs", variant: "filled", color: "blue", leftSection: /* @__PURE__ */ jsx(IconPlayerPlay, { size: 9 }), children: [
1589
+ activeExecutionIds.length,
1590
+ " running"
1591
+ ] }) : null
1592
+ ] })
1593
+ }
1594
+ ),
1595
+ sse.error ? /* @__PURE__ */ jsx(Alert, { icon: /* @__PURE__ */ jsx(IconAlertCircle, { size: 14 }), color: "red", variant: "light", p: "xs", children: /* @__PURE__ */ jsx(Text, { size: "xs", children: sse.error }) }) : null,
1596
+ activeExecutionIds.length > 0 ? /* @__PURE__ */ jsx(Group, { gap: 6, children: activeExecutionIds.map((executionId) => /* @__PURE__ */ jsx(Badge, { size: "xs", variant: "light", color: "blue", ff: "monospace", children: formatId(executionId) }, executionId)) }) : null,
1597
+ /* @__PURE__ */ jsxs(Box, { p: "xs", bg: "var(--surface-primary-subtle)", style: { borderRadius: "var(--mantine-radius-sm)" }, children: [
1598
+ /* @__PURE__ */ jsxs(Group, { justify: "space-between", gap: "xs", wrap: "nowrap", children: [
1599
+ /* @__PURE__ */ jsxs(Group, { gap: 6, wrap: "nowrap", style: { minWidth: 0 }, children: [
1600
+ /* @__PURE__ */ jsx(IconProgressCheck, { size: 14 }),
1601
+ /* @__PURE__ */ jsx(Text, { size: "xs", fw: 600, truncate: "end", children: getStageLabel(stageKey) })
1602
+ ] }),
1603
+ /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", ff: "monospace", children: [
1604
+ doneCount,
1605
+ "/",
1606
+ totalCount,
1607
+ " done"
1608
+ ] })
1609
+ ] }),
1610
+ /* @__PURE__ */ jsx(
1611
+ Progress,
1612
+ {
1613
+ mt: 6,
1614
+ size: "xs",
1615
+ value: totalCount > 0 ? doneCount / totalCount * 100 : 0,
1616
+ color: stageProgress && stageProgress.error > 0 ? "red" : "blue"
1617
+ }
1618
+ )
1619
+ ] }),
1620
+ latestStepStatus ? /* @__PURE__ */ jsxs(Group, { gap: 6, wrap: "nowrap", children: [
1621
+ /* @__PURE__ */ jsx(Badge, { size: "xs", color: getStepStatusColor(latestStepStatus.status), variant: "light", children: getStepStatusLabel(latestStepStatus.status) }),
1622
+ /* @__PURE__ */ jsx(Text, { size: "xs", fw: 500, truncate: "end", children: latestStepStatus.stepId }),
1623
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", truncate: "end", style: { flex: 1 }, children: latestStepStatus.message })
1624
+ ] }) : running ? /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "Waiting for workflow step events." }) : null,
1625
+ lastEvents.length > 0 ? /* @__PURE__ */ jsx(Stack, { gap: 4, children: lastEvents.map((event) => /* @__PURE__ */ jsxs(Group, { gap: 6, wrap: "nowrap", children: [
1626
+ /* @__PURE__ */ jsx(Badge, { size: "xs", variant: "dot", color: event.color, miw: 68, children: formatEventTime(event.timestamp) }),
1627
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", ff: "monospace", miw: 62, children: formatId(event.executionId) }),
1628
+ /* @__PURE__ */ jsx(Text, { size: "xs", truncate: "end", style: { flex: 1 }, children: event.detail ?? event.label })
1629
+ ] }, `${event.executionId}-${event.timestamp}-${event.label}`)) }) : null
1630
+ ] }) });
1631
+ }
1372
1632
  var panelStyle = {
1373
1633
  flex: 1,
1374
1634
  minHeight: 0,
@@ -1377,6 +1637,7 @@ var panelStyle = {
1377
1637
  };
1378
1638
  function StepDetailRightColumn({
1379
1639
  configuration,
1640
+ records,
1380
1641
  advanced,
1381
1642
  runs,
1382
1643
  action,
@@ -1397,7 +1658,7 @@ function StepDetailRightColumn({
1397
1658
  {
1398
1659
  value: activeTab,
1399
1660
  onChange: (value) => {
1400
- if (value === "configuration" || value === "advanced" || value === "runs") {
1661
+ if (value === "configuration" || value === "records" || value === "advanced" || value === "runs") {
1401
1662
  onTabChange(value);
1402
1663
  }
1403
1664
  },
@@ -1410,10 +1671,12 @@ function StepDetailRightColumn({
1410
1671
  children: [
1411
1672
  /* @__PURE__ */ jsxs(Tabs.List, { children: [
1412
1673
  /* @__PURE__ */ jsx(Tabs.Tab, { value: "configuration", children: "Configuration" }),
1674
+ /* @__PURE__ */ jsx(Tabs.Tab, { value: "records", children: "Records" }),
1413
1675
  /* @__PURE__ */ jsx(Tabs.Tab, { value: "advanced", children: "Advanced" }),
1414
1676
  /* @__PURE__ */ jsx(Tabs.Tab, { value: "runs", children: "Runs" })
1415
1677
  ] }),
1416
1678
  /* @__PURE__ */ jsx(Tabs.Panel, { value: "configuration", style: panelStyle, children: configuration }),
1679
+ /* @__PURE__ */ jsx(Tabs.Panel, { value: "records", style: panelStyle, children: records ?? /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "No records view configured for this step." }) }),
1417
1680
  /* @__PURE__ */ jsx(Tabs.Panel, { value: "advanced", style: panelStyle, children: advanced ?? /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "No advanced settings for this step." }) }),
1418
1681
  /* @__PURE__ */ jsx(Tabs.Panel, { value: "runs", style: panelStyle, children: runs })
1419
1682
  ]
@@ -1513,6 +1776,12 @@ var STAGE_COLOR_SETS = {
1513
1776
  border: "color-mix(in srgb, var(--color-border) 70%, var(--color-primary))",
1514
1777
  text: "var(--color-text)"
1515
1778
  },
1779
+ crawled: {
1780
+ accent: "color-mix(in srgb, var(--color-primary) 70%, var(--color-text-subtle))",
1781
+ background: "color-mix(in srgb, var(--color-primary) 9%, transparent)",
1782
+ border: "color-mix(in srgb, var(--color-border) 72%, var(--color-primary))",
1783
+ text: "var(--color-text)"
1784
+ },
1516
1785
  extracted: {
1517
1786
  accent: "color-mix(in srgb, var(--color-primary) 78%, var(--color-success))",
1518
1787
  background: "color-mix(in srgb, var(--color-primary) 10%, transparent)",
@@ -1628,7 +1897,7 @@ function buildLaneStages({
1628
1897
  })
1629
1898
  );
1630
1899
  }
1631
- function getStageProgress(progress, stageKey, entity) {
1900
+ function getStageProgress2(progress, stageKey, entity) {
1632
1901
  const stageProgress = entity === "company" ? progress.byCompanyStage[stageKey] : progress.byContactStage[stageKey];
1633
1902
  const total = stageProgress?.total ?? (entity === "company" ? progress.totalCompanies : progress.totalMembers);
1634
1903
  return {
@@ -1746,7 +2015,7 @@ function TimelineLane({
1746
2015
  position: "relative"
1747
2016
  },
1748
2017
  children: stages.map((stage, index) => {
1749
- const counts = getStageProgress(progress, stage.key, entity);
2018
+ const counts = getStageProgress2(progress, stage.key, entity);
1750
2019
  const accent = getLeadGenStageColorVar(stage.key);
1751
2020
  const completion = Math.round(percent(counts.success, counts.total));
1752
2021
  const attempted = Math.round(percent(counts.attempted, counts.total));
@@ -2416,7 +2685,7 @@ function sortStageKeys(keys) {
2416
2685
  return oa - ob || a.localeCompare(b);
2417
2686
  });
2418
2687
  }
2419
- function getStageProgress2(progress, step) {
2688
+ function getStageProgress3(progress, step) {
2420
2689
  return step.primaryEntity === "company" ? progress.byCompanyStage[step.stageKey] : progress.byContactStage[step.stageKey];
2421
2690
  }
2422
2691
  function getEntityTotal(progress, entity) {
@@ -2434,11 +2703,16 @@ function normalizeBuildPlanStep(step) {
2434
2703
  primaryEntity: step.primaryEntity,
2435
2704
  outputs: step.outputs,
2436
2705
  stageKey: step.stageKey,
2706
+ recordEntity: step.recordEntity,
2707
+ recordsStageKey: step.recordsStageKey,
2708
+ recordSourceStageKey: step.recordSourceStageKey,
2437
2709
  dependsOn: step.dependsOn,
2438
2710
  dependencyMode: step.dependencyMode,
2439
2711
  capabilityKey: step.capabilityKey,
2440
2712
  defaultBatchSize: step.defaultBatchSize,
2441
2713
  maxBatchSize: step.maxBatchSize,
2714
+ recordColumns: step.recordColumns,
2715
+ credentialRequirements: step.credentialRequirements,
2442
2716
  emptyBlockedText: fallback?.emptyBlockedText ?? "Complete prerequisite build steps before this action can run."
2443
2717
  };
2444
2718
  }
@@ -2563,7 +2837,7 @@ function getStepRecommendedAction(step, action, kind) {
2563
2837
  function deriveBuildStepStates(list, progress, actions) {
2564
2838
  const byStepId = /* @__PURE__ */ new Map();
2565
2839
  for (const step of resolveBuildPlanSteps(list)) {
2566
- const stageProgress = getStageProgress2(progress, step);
2840
+ const stageProgress = getStageProgress3(progress, step);
2567
2841
  const entityTotal = getEntityTotal(progress, step.primaryEntity);
2568
2842
  const prerequisites = getPrerequisiteSteps(step, byStepId);
2569
2843
  const hasDependencies = (step.dependsOn?.length ?? 0) > 0;
@@ -2652,6 +2926,101 @@ function displayMemberStageFor(row, kind) {
2652
2926
  function asRecord3(value) {
2653
2927
  return value && typeof value === "object" && !Array.isArray(value) ? value : {};
2654
2928
  }
2929
+ function valueAtPath(source, path) {
2930
+ return path.split(".").reduce((current, segment) => {
2931
+ if (!current || typeof current !== "object") return void 0;
2932
+ return current[segment];
2933
+ }, source);
2934
+ }
2935
+ function compactText(value) {
2936
+ if (value === null || value === void 0 || value === "") return "-";
2937
+ if (Array.isArray(value)) {
2938
+ if (!value.length) return "-";
2939
+ return value.map((item) => {
2940
+ if (item && typeof item === "object") {
2941
+ const labelled = item;
2942
+ return String(labelled.name ?? labelled.label ?? labelled.title ?? labelled.id ?? JSON.stringify(item));
2943
+ }
2944
+ return String(item);
2945
+ }).join(", ");
2946
+ }
2947
+ if (typeof value === "object") return JSON.stringify(value);
2948
+ return String(value);
2949
+ }
2950
+ function getRecordDisplayName(row) {
2951
+ if (row.entity === "company") return row.company?.name ?? "Company";
2952
+ const contact = row.contact;
2953
+ const full = [contact?.firstName, contact?.lastName].filter(Boolean).join(" ").trim();
2954
+ return full || contact?.email || "Contact";
2955
+ }
2956
+ function getRecordColumnSource(row) {
2957
+ if (row.entity === "company") {
2958
+ return {
2959
+ ...row,
2960
+ company: row.company ? {
2961
+ ...row.company,
2962
+ enrichmentData: row.enrichmentData,
2963
+ processingState: row.processingState
2964
+ } : null
2965
+ };
2966
+ }
2967
+ return {
2968
+ ...row,
2969
+ contact: row.contact ? {
2970
+ ...row.contact,
2971
+ name: getRecordDisplayName(row),
2972
+ enrichmentData: row.enrichmentData,
2973
+ processingState: row.processingState
2974
+ } : null
2975
+ };
2976
+ }
2977
+ function getStageEntry(row, stageKey) {
2978
+ const entry = valueAtPath(row.processingState, stageKey);
2979
+ return asRecord3(entry);
2980
+ }
2981
+ function getRecordStageStatus(row, stageKey) {
2982
+ const status = getStageEntry(row, stageKey).status;
2983
+ return typeof status === "string" ? status : "pending";
2984
+ }
2985
+ function getStageFailureReason(row, stageKey) {
2986
+ const data = asRecord3(getStageEntry(row, stageKey).data);
2987
+ const error = data.error ?? data.reason ?? data.failureReason ?? data.disqualifiedReason;
2988
+ return compactText(error);
2989
+ }
2990
+ function getRecordStatusColor(status) {
2991
+ switch (status) {
2992
+ case "success":
2993
+ return "green";
2994
+ case "error":
2995
+ case "failed":
2996
+ return "red";
2997
+ case "skipped":
2998
+ case "no_result":
2999
+ return "yellow";
3000
+ case "pending":
3001
+ return "gray";
3002
+ default:
3003
+ return "blue";
3004
+ }
3005
+ }
3006
+ function renderRecordValue(value, column) {
3007
+ if (value === null || value === void 0 || value === "") {
3008
+ return /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "-" });
3009
+ }
3010
+ switch (column.renderType) {
3011
+ case "badge":
3012
+ return /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: column.badgeColor ?? getRecordStatusColor(String(value)), children: compactText(value) });
3013
+ case "datetime":
3014
+ return compactText(typeof value === "string" ? formatDateTime4(value) : value);
3015
+ case "count":
3016
+ return Array.isArray(value) ? value.length : compactText(value);
3017
+ case "json":
3018
+ return /* @__PURE__ */ jsx(Text, { size: "xs", lineClamp: 2, children: compactText(value) });
3019
+ case "text":
3020
+ default:
3021
+ return /* @__PURE__ */ jsx(Text, { size: "sm", lineClamp: 2, children: compactText(value) });
3022
+ }
3023
+ }
2655
3024
  function getRunStatusColor(status) {
2656
3025
  switch (status) {
2657
3026
  case "completed":
@@ -3090,20 +3459,393 @@ function getBuildTabDescription(steps, currentStep) {
3090
3459
  if (currentStep.status === "failed") return `${currentStep.label} needs attention.`;
3091
3460
  return `${currentStep.label} is waiting on earlier work.`;
3092
3461
  }
3462
+ var RECORDS_PAGE_SIZE = 25;
3463
+ var EXPORT_WORKFLOW_ID = "lgn-06-export-list-workflow";
3464
+ function setValueAtInputPath(source, path, value) {
3465
+ const segments = path.split(".").filter(Boolean);
3466
+ if (!segments.length) return source;
3467
+ const next = { ...source };
3468
+ let cursor = next;
3469
+ segments.forEach((segment, index) => {
3470
+ if (index === segments.length - 1) {
3471
+ cursor[segment] = value;
3472
+ return;
3473
+ }
3474
+ const current = cursor[segment];
3475
+ const child = current && typeof current === "object" && !Array.isArray(current) ? { ...current } : {};
3476
+ cursor[segment] = child;
3477
+ cursor = child;
3478
+ });
3479
+ return next;
3480
+ }
3481
+ function credentialMatchesRequirement(credential, requirement) {
3482
+ const provider = requirement.provider.toLowerCase();
3483
+ const credentialProvider = credential.provider?.toLowerCase() ?? "";
3484
+ const credentialType = credential.type.toLowerCase();
3485
+ const credentialName = credential.name.toLowerCase();
3486
+ if (credentialProvider === provider || credentialType === provider || credentialName.startsWith(`${provider}-`) || credentialName.includes(`-${provider}`) || credentialName.includes(`${provider}-`)) {
3487
+ return true;
3488
+ }
3489
+ return credentialType === requirement.credentialType;
3490
+ }
3491
+ function getMatchingCredentials(credentials, requirement) {
3492
+ return credentials.filter((credential) => credentialMatchesRequirement(credential, requirement));
3493
+ }
3494
+ function getSelectedCredential(credentials, selections, requirement) {
3495
+ const selectedId = selections[requirement.key];
3496
+ return credentials.find((credential) => credential.id === selectedId) ?? null;
3497
+ }
3498
+ function getClickUpRequirement(step) {
3499
+ return step.credentialRequirements?.find((requirement) => requirement.provider === "clickup") ?? null;
3500
+ }
3501
+ function filterCredentialRequirementFields(layout, requirements) {
3502
+ if (!layout || requirements.length === 0) return layout;
3503
+ const credentialInputPaths = new Set(requirements.map((requirement) => requirement.inputPath).filter(Boolean));
3504
+ if (!credentialInputPaths.size) return layout;
3505
+ const filterSection = (section) => {
3506
+ const fields = section.fields.filter((field) => !credentialInputPaths.has(field.path));
3507
+ return fields.length > 0 ? { ...section, fields } : null;
3508
+ };
3509
+ return {
3510
+ ...layout,
3511
+ sections: layout.sections.map(filterSection).filter((section) => section !== null),
3512
+ advanced: layout.advanced ? filterSection(layout.advanced) ?? void 0 : void 0
3513
+ };
3514
+ }
3515
+ function hasMainConfigFields(layout) {
3516
+ return Boolean(layout?.sections.some((section) => section.fields.length > 0));
3517
+ }
3518
+ function hasAdvancedConfigFields(layout) {
3519
+ return Boolean(layout?.advanced?.fields.length);
3520
+ }
3521
+ function formatCredentialVerificationDetails(details) {
3522
+ if (!details) return null;
3523
+ const entries = Object.entries(details).filter(([, value]) => value !== null && value !== void 0 && value !== "").slice(0, 4);
3524
+ if (!entries.length) return null;
3525
+ return entries.map(([key, value]) => `${key}: ${compactText(value)}`).join(" | ");
3526
+ }
3527
+ function CredentialRequirementsPanel({
3528
+ requirements,
3529
+ credentials,
3530
+ credentialsLoading,
3531
+ selections,
3532
+ verificationByCredentialId,
3533
+ verifyingCredentialId,
3534
+ onSelect,
3535
+ onVerify
3536
+ }) {
3537
+ if (!requirements.length) return null;
3538
+ return /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
3539
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", fw: 700, tt: "uppercase", children: "Credentials" }),
3540
+ requirements.map((requirement) => {
3541
+ const matchingCredentials = getMatchingCredentials(credentials, requirement);
3542
+ const selectedCredential = getSelectedCredential(credentials, selections, requirement);
3543
+ const verification = selectedCredential ? verificationByCredentialId[selectedCredential.id] : null;
3544
+ return /* @__PURE__ */ jsxs(Stack, { gap: 6, children: [
3545
+ /* @__PURE__ */ jsxs(Group, { gap: "xs", align: "flex-end", wrap: "nowrap", children: [
3546
+ /* @__PURE__ */ jsx(
3547
+ Select,
3548
+ {
3549
+ label: requirement.label,
3550
+ description: `${requirement.provider} credential for ${requirement.inputPath}`,
3551
+ placeholder: credentialsLoading ? "Loading credentials..." : "Select credential",
3552
+ data: matchingCredentials.map((credential) => ({
3553
+ value: credential.id,
3554
+ label: `${credential.name} (${credential.provider ?? credential.type})`
3555
+ })),
3556
+ value: selectedCredential?.id ?? null,
3557
+ onChange: (credentialId) => {
3558
+ onSelect(requirement, credentials.find((credential) => credential.id === credentialId) ?? null);
3559
+ },
3560
+ required: requirement.required,
3561
+ disabled: credentialsLoading || matchingCredentials.length === 0,
3562
+ style: { flex: 1 }
3563
+ }
3564
+ ),
3565
+ /* @__PURE__ */ jsx(
3566
+ Button,
3567
+ {
3568
+ size: "xs",
3569
+ variant: "light",
3570
+ disabled: !selectedCredential,
3571
+ loading: selectedCredential ? verifyingCredentialId === selectedCredential.id : false,
3572
+ onClick: () => selectedCredential && onVerify(selectedCredential),
3573
+ children: "Test credential"
3574
+ }
3575
+ )
3576
+ ] }),
3577
+ matchingCredentials.length === 0 ? /* @__PURE__ */ jsx(
3578
+ Alert,
3579
+ {
3580
+ color: requirement.required ? "yellow" : "gray",
3581
+ variant: "light",
3582
+ icon: /* @__PURE__ */ jsx(IconAlertCircle, { size: 16 }),
3583
+ children: /* @__PURE__ */ jsxs(Text, { size: "sm", children: [
3584
+ "No matching ",
3585
+ requirement.provider,
3586
+ " credentials found. Add one in",
3587
+ " ",
3588
+ /* @__PURE__ */ jsx(Text, { component: "a", href: "/settings/credentials", size: "sm", fw: 600, children: "Settings -> Credentials" }),
3589
+ "."
3590
+ ] })
3591
+ }
3592
+ ) : null,
3593
+ verification ? /* @__PURE__ */ jsx(
3594
+ Alert,
3595
+ {
3596
+ color: verification.status === "success" ? "green" : "red",
3597
+ variant: "light",
3598
+ icon: /* @__PURE__ */ jsx(IconAlertCircle, { size: 16 }),
3599
+ children: /* @__PURE__ */ jsxs(Stack, { gap: 2, children: [
3600
+ /* @__PURE__ */ jsx(Text, { size: "sm", children: verification.message }),
3601
+ verification.checkedAt ? /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
3602
+ "Checked ",
3603
+ formatDateTime4(verification.checkedAt)
3604
+ ] }) : null,
3605
+ formatCredentialVerificationDetails(verification.details) ? /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: formatCredentialVerificationDetails(verification.details) }) : null
3606
+ ] })
3607
+ }
3608
+ ) : null
3609
+ ] }, requirement.key);
3610
+ })
3611
+ ] });
3612
+ }
3613
+ function BuildStepRunningWatcher({
3614
+ resourceId,
3615
+ onRunningChange
3616
+ }) {
3617
+ const inFlightQuery = useInFlightExecutions(resourceId, {
3618
+ enabled: Boolean(resourceId),
3619
+ limit: 5,
3620
+ refetchInterval: 2e3
3621
+ });
3622
+ const isRunning = (inFlightQuery.data?.executions.length ?? 0) > 0;
3623
+ useEffect(() => {
3624
+ onRunningChange(resourceId, isRunning);
3625
+ return () => onRunningChange(resourceId, false);
3626
+ }, [isRunning, onRunningChange, resourceId]);
3627
+ return null;
3628
+ }
3629
+ function StepRecordsPanel({
3630
+ list,
3631
+ step,
3632
+ credentialSelections,
3633
+ credentials,
3634
+ clickupListId
3635
+ }) {
3636
+ const queryClient = useQueryClient();
3637
+ const [page, setPage] = useState(1);
3638
+ const [approvalByCompanyId, setApprovalByCompanyId] = useState({});
3639
+ const [selectedRecord, setSelectedRecord] = useState(null);
3640
+ const recordsStageKey = step.recordSourceStageKey ?? step.recordsStageKey ?? step.stageKey;
3641
+ const recordsEntity = step.recordEntity ?? step.primaryEntity;
3642
+ const columns = step.recordColumns?.[recordsEntity] ?? [];
3643
+ const recordsQuery = useListRecords(list.id, {
3644
+ entity: recordsEntity,
3645
+ stage: recordsStageKey,
3646
+ limit: RECORDS_PAGE_SIZE,
3647
+ offset: (page - 1) * RECORDS_PAGE_SIZE
3648
+ });
3649
+ const exportWorkflowId = step.action?.resourceId ?? EXPORT_WORKFLOW_ID;
3650
+ const exportExecution = useWorkflowExecution({
3651
+ workflowId: exportWorkflowId,
3652
+ listId: list.id
3653
+ });
3654
+ const selectedCompanyId = selectedRecord?.entity === "company" ? selectedRecord.id : "";
3655
+ const selectedContactId = selectedRecord?.entity === "contact" ? selectedRecord.id : "";
3656
+ const selectedCompanyQuery = useCompany(selectedCompanyId);
3657
+ const selectedContactQuery = useContact(selectedContactId);
3658
+ useEffect(() => {
3659
+ setPage(1);
3660
+ setApprovalByCompanyId({});
3661
+ setSelectedRecord(null);
3662
+ }, [step.id]);
3663
+ const rows = recordsQuery.data?.data ?? [];
3664
+ const total = recordsQuery.data?.total ?? 0;
3665
+ const totalPages = Math.max(1, Math.ceil(total / RECORDS_PAGE_SIZE));
3666
+ const isReviewExportStep = step.primaryEntity === "company" && (step.capabilityKey === "lead-gen.export.list" || exportWorkflowId === EXPORT_WORKFLOW_ID);
3667
+ const approvedCompanyIds = Object.entries(approvalByCompanyId).filter(([, status]) => status === "approved").map(([companyId]) => companyId);
3668
+ const rejectedCompanyIds = Object.entries(approvalByCompanyId).filter(([, status]) => status === "rejected").map(([companyId]) => companyId);
3669
+ const clickupRequirement = getClickUpRequirement(step);
3670
+ const clickupCredential = clickupRequirement ? getSelectedCredential(credentials, credentialSelections, clickupRequirement) : null;
3671
+ const isClickUpExportPath = isReviewExportStep && Boolean(clickupRequirement);
3672
+ const normalizedClickupListId = clickupListId.trim();
3673
+ const canExport = approvedCompanyIds.length > 0 && (!isClickUpExportPath || Boolean(clickupCredential) && normalizedClickupListId.length > 0);
3674
+ const exportDisabledReason = approvedCompanyIds.length === 0 ? "Approve at least one company before export." : isClickUpExportPath && !clickupCredential ? "Select a ClickUp credential in Configuration." : isClickUpExportPath && !normalizedClickupListId ? "Enter a ClickUp List ID in Configuration." : null;
3675
+ const setApproval = (companyId, status) => {
3676
+ setApprovalByCompanyId((current) => ({ ...current, [companyId]: status }));
3677
+ };
3678
+ const openRecordDetail = (row) => {
3679
+ if (row.entity === "company") {
3680
+ setSelectedRecord({ entity: "company", id: row.companyId });
3681
+ return;
3682
+ }
3683
+ setSelectedRecord({ entity: "contact", id: row.contactId });
3684
+ };
3685
+ const exportApproved = async () => {
3686
+ try {
3687
+ const result = await exportExecution.execute({
3688
+ input: {
3689
+ listId: list.id,
3690
+ mode: "export",
3691
+ approved: true,
3692
+ ...isClickUpExportPath ? {
3693
+ destination: "clickup",
3694
+ clickupCredential: clickupCredential?.name,
3695
+ clickupListId: normalizedClickupListId
3696
+ } : {},
3697
+ approvedCompanyIds,
3698
+ rejectedCompanyIds
3699
+ }
3700
+ });
3701
+ showSuccessNotification(`Export workflow run started: ${result.executionId}`);
3702
+ await queryClient.invalidateQueries({ queryKey: acquisitionListKeys.all });
3703
+ } catch (error) {
3704
+ showApiErrorNotification(error);
3705
+ }
3706
+ };
3707
+ const selectedRecordIsLoading = selectedRecord?.entity === "company" ? selectedCompanyQuery.isLoading : selectedRecord?.entity === "contact" ? selectedContactQuery.isLoading : false;
3708
+ if (!columns.length) {
3709
+ return /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "No record columns are configured for this step." });
3710
+ }
3711
+ if (recordsQuery.isLoading) {
3712
+ return /* @__PURE__ */ jsx(Center, { p: "md", children: /* @__PURE__ */ jsx(Loader, { size: "sm" }) });
3713
+ }
3714
+ if (recordsQuery.error) {
3715
+ return /* @__PURE__ */ jsx(CenteredErrorState, { error: recordsQuery.error, title: "Failed to load records" });
3716
+ }
3717
+ if (!rows.length) {
3718
+ return /* @__PURE__ */ jsx(Stack, { gap: "xs", children: /* @__PURE__ */ jsxs(Text, { size: "sm", c: "dimmed", children: [
3719
+ "No records found for ",
3720
+ LEAD_GEN_STAGE_CATALOG[recordsStageKey]?.label ?? recordsStageKey,
3721
+ "."
3722
+ ] }) });
3723
+ }
3724
+ return /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
3725
+ /* @__PURE__ */ jsxs(Group, { justify: "space-between", gap: "xs", children: [
3726
+ /* @__PURE__ */ jsxs(Stack, { gap: 2, children: [
3727
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", fw: 700, tt: "uppercase", children: "Records" }),
3728
+ /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
3729
+ total,
3730
+ " ",
3731
+ recordsEntity,
3732
+ " records from ",
3733
+ LEAD_GEN_STAGE_CATALOG[recordsStageKey]?.label ?? recordsStageKey
3734
+ ] })
3735
+ ] }),
3736
+ recordsQuery.isFetching ? /* @__PURE__ */ jsx(Loader, { size: "xs" }) : null
3737
+ ] }),
3738
+ /* @__PURE__ */ jsx(Box, { style: { overflowX: "auto" }, children: /* @__PURE__ */ jsxs(Table, { highlightOnHover: true, children: [
3739
+ /* @__PURE__ */ jsx(Table.Thead, { children: /* @__PURE__ */ jsxs(Table.Tr, { children: [
3740
+ columns.map((column) => /* @__PURE__ */ jsx(Table.Th, { style: column.width ? { width: column.width } : void 0, children: column.label }, column.key)),
3741
+ /* @__PURE__ */ jsx(Table.Th, { children: "Status" }),
3742
+ /* @__PURE__ */ jsx(Table.Th, { children: "Failure reason" }),
3743
+ isReviewExportStep ? /* @__PURE__ */ jsx(Table.Th, { children: "Approval" }) : null
3744
+ ] }) }),
3745
+ /* @__PURE__ */ jsx(Table.Tbody, { children: rows.map((row) => {
3746
+ const columnSource = getRecordColumnSource(row);
3747
+ const status = getRecordStageStatus(row, recordsStageKey);
3748
+ const companyId = row.entity === "company" ? row.companyId : null;
3749
+ return /* @__PURE__ */ jsxs(Table.Tr, { onClick: () => openRecordDetail(row), style: { cursor: "pointer" }, children: [
3750
+ columns.map((column) => /* @__PURE__ */ jsx(Table.Td, { children: renderRecordValue(valueAtPath(columnSource, column.path), column) }, column.key)),
3751
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: getRecordStatusColor(status), children: status }) }),
3752
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", lineClamp: 2, children: getStageFailureReason(row, recordsStageKey) }) }),
3753
+ isReviewExportStep ? /* @__PURE__ */ jsx(Table.Td, { children: companyId ? /* @__PURE__ */ jsxs(Group, { gap: 4, wrap: "nowrap", "aria-label": `Approval for ${getRecordDisplayName(row)}`, children: [
3754
+ /* @__PURE__ */ jsx(
3755
+ Button,
3756
+ {
3757
+ size: "compact-xs",
3758
+ variant: approvalByCompanyId[companyId] === "approved" ? "filled" : "light",
3759
+ color: "green",
3760
+ onClick: (event) => {
3761
+ event.stopPropagation();
3762
+ setApproval(companyId, "approved");
3763
+ },
3764
+ children: "Approve"
3765
+ }
3766
+ ),
3767
+ /* @__PURE__ */ jsx(
3768
+ Button,
3769
+ {
3770
+ size: "compact-xs",
3771
+ variant: approvalByCompanyId[companyId] === "rejected" ? "filled" : "light",
3772
+ color: "red",
3773
+ onClick: (event) => {
3774
+ event.stopPropagation();
3775
+ setApproval(companyId, "rejected");
3776
+ },
3777
+ children: "Reject"
3778
+ }
3779
+ )
3780
+ ] }) : /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "-" }) }) : null
3781
+ ] }, row.id);
3782
+ }) })
3783
+ ] }) }),
3784
+ /* @__PURE__ */ jsxs(Group, { justify: "space-between", gap: "xs", children: [
3785
+ /* @__PURE__ */ jsx(Pagination, { size: "sm", total: totalPages, value: page, onChange: setPage, disabled: recordsQuery.isFetching }),
3786
+ isReviewExportStep ? /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
3787
+ /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
3788
+ approvedCompanyIds.length,
3789
+ " approved, ",
3790
+ rejectedCompanyIds.length,
3791
+ " rejected"
3792
+ ] }),
3793
+ exportDisabledReason ? /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: exportDisabledReason }) : null,
3794
+ /* @__PURE__ */ jsx(
3795
+ Button,
3796
+ {
3797
+ size: "xs",
3798
+ leftSection: /* @__PURE__ */ jsx(IconPlayerPlay, { size: 14 }),
3799
+ loading: exportExecution.isPending,
3800
+ disabled: !canExport,
3801
+ onClick: () => void exportApproved(),
3802
+ children: isClickUpExportPath ? "Export to ClickUp" : "Export approved"
3803
+ }
3804
+ )
3805
+ ] }) : null
3806
+ ] }),
3807
+ /* @__PURE__ */ jsx(
3808
+ CustomModal,
3809
+ {
3810
+ opened: Boolean(selectedRecord) && selectedRecordIsLoading,
3811
+ onClose: () => setSelectedRecord(null),
3812
+ size: "xl",
3813
+ children: /* @__PURE__ */ jsx(Center, { p: "xl", children: /* @__PURE__ */ jsx(Loader, { size: "sm" }) })
3814
+ }
3815
+ ),
3816
+ /* @__PURE__ */ jsx(
3817
+ CompanyDetailModal,
3818
+ {
3819
+ company: selectedRecord?.entity === "company" ? selectedCompanyQuery.data ?? null : null,
3820
+ onClose: () => setSelectedRecord(null)
3821
+ }
3822
+ ),
3823
+ /* @__PURE__ */ jsx(
3824
+ ContactDetailModal,
3825
+ {
3826
+ contact: selectedRecord?.entity === "contact" ? selectedContactQuery.data ?? null : null,
3827
+ onClose: () => setSelectedRecord(null)
3828
+ }
3829
+ )
3830
+ ] });
3831
+ }
3093
3832
  function BuildTab({
3094
3833
  list,
3095
3834
  steps,
3096
3835
  recommendedStep,
3097
3836
  onRunStep,
3098
3837
  onRetryStep,
3099
- onViewRunLog,
3100
- onViewRuns
3838
+ onViewRunLog
3101
3839
  }) {
3102
3840
  const [selectedStepId, setSelectedStepId] = useState(null);
3103
3841
  const [stepInputFormState, setStepInputFormState] = useState({});
3104
3842
  const [stepInputError, setStepInputError] = useState(null);
3105
3843
  const [stepInputInitialized, setStepInputInitialized] = useState(false);
3106
3844
  const [activeRightColumnTab, setActiveRightColumnTab] = useState("configuration");
3845
+ const [credentialSelections, setCredentialSelections] = useState({});
3846
+ const [credentialVerificationById, setCredentialVerificationById] = useState({});
3847
+ const [clickupListId, setClickupListId] = useState("");
3848
+ const [runningResources, setRunningResources] = useState({});
3107
3849
  const [stepInputResetKey, setStepInputResetKey] = useState(0);
3108
3850
  const currentStep = getDisplayStep(steps, recommendedStep);
3109
3851
  const selectedStep = steps.find((step) => step.id === selectedStepId) ?? currentStep;
@@ -3113,10 +3855,36 @@ function BuildTab({
3113
3855
  const selectedResourceId = selectedStep?.action?.resourceId ?? null;
3114
3856
  const stepSchema = selectedStep?.action?.schema;
3115
3857
  const stepLayout = selectedStep?.action?.layout;
3858
+ const credentialRequirements = selectedStep?.credentialRequirements ?? [];
3859
+ const visibleStepLayout = useMemo(
3860
+ () => filterCredentialRequirementFields(stepLayout, credentialRequirements),
3861
+ [stepLayout, credentialRequirements]
3862
+ );
3863
+ const showMainStepConfig = stepSchema && hasMainConfigFields(visibleStepLayout);
3864
+ const showAdvancedStepConfig = stepSchema && hasAdvancedConfigFields(visibleStepLayout);
3116
3865
  const recentRunsQuery = useListExecutions(list.id, { resourceId: selectedResourceId, limit: 5 });
3117
3866
  const recentRuns = useMemo(() => recentRunsQuery.data ?? [], [recentRunsQuery.data]);
3867
+ const credentialsQuery = useCredentials();
3868
+ const credentials = credentialsQuery.data ?? [];
3869
+ const verifyCredential = useVerifyCredential();
3118
3870
  const canRunSelectedAction = !!selectedStep?.action && (selectedActionKind === "retry_failed" || selectedActionKind === "run_next_batch");
3871
+ const selectedResourceIsRunning = selectedResourceId ? runningResources[selectedResourceId] ?? false : false;
3872
+ const selectedActionDisabled = !canRunSelectedAction || selectedResourceIsRunning;
3873
+ const selectedActionLabel = selectedResourceIsRunning ? "Running..." : selectedActionKind === "none" ? "No action" : getStepActionLabel(selectedActionKind);
3119
3874
  const selectedActionIcon = selectedActionKind === "retry_failed" ? /* @__PURE__ */ jsx(IconRefresh, { size: 14 }) : /* @__PURE__ */ jsx(IconPlayerPlay, { size: 14 });
3875
+ const handleResourceRunningChange = useCallback((resourceId, isRunning) => {
3876
+ setRunningResources((current) => {
3877
+ if (current[resourceId] === isRunning) return current;
3878
+ return { ...current, [resourceId]: isRunning };
3879
+ });
3880
+ }, []);
3881
+ const handleSelectedResourceRunningChange = useCallback(
3882
+ (isRunning) => {
3883
+ if (!selectedResourceId) return;
3884
+ handleResourceRunningChange(selectedResourceId, isRunning);
3885
+ },
3886
+ [handleResourceRunningChange, selectedResourceId]
3887
+ );
3120
3888
  useEffect(() => {
3121
3889
  setStepInputFormState(getInitialStepInput(list, selectedStep?.action));
3122
3890
  setStepInputError(null);
@@ -3139,8 +3907,45 @@ function BuildTab({
3139
3907
  setStepInputFormState(next);
3140
3908
  if (stepInputError) setStepInputError(null);
3141
3909
  };
3910
+ const handleCredentialSelect = (requirement, credential) => {
3911
+ setCredentialSelections((current) => ({
3912
+ ...current,
3913
+ [requirement.key]: credential?.id ?? null
3914
+ }));
3915
+ setStepInputFormState((current) => setValueAtInputPath(current, requirement.inputPath, credential?.name ?? ""));
3916
+ if (stepInputError) setStepInputError(null);
3917
+ };
3918
+ const handleVerifyCredential = (credential) => {
3919
+ verifyCredential.mutate(credential.id, {
3920
+ onSuccess: (result) => {
3921
+ setCredentialVerificationById((current) => ({
3922
+ ...current,
3923
+ [credential.id]: {
3924
+ status: result.ok ? "success" : "error",
3925
+ checkedAt: result.checkedAt,
3926
+ message: result.message ?? (result.ok ? "Credential verified." : "Credential verification failed."),
3927
+ details: result.details
3928
+ }
3929
+ }));
3930
+ },
3931
+ onError: (error) => {
3932
+ setCredentialVerificationById((current) => ({
3933
+ ...current,
3934
+ [credential.id]: {
3935
+ status: "error",
3936
+ message: error instanceof Error ? error.message : "Credential verification failed."
3937
+ }
3938
+ }));
3939
+ }
3940
+ });
3941
+ };
3942
+ const handleClickupListIdChange = (value) => {
3943
+ setClickupListId(value);
3944
+ setStepInputFormState((current) => setValueAtInputPath(current, "clickupListId", value));
3945
+ if (stepInputError) setStepInputError(null);
3946
+ };
3142
3947
  const handleSelectedAction = () => {
3143
- if (!selectedStep || !canRunSelectedAction) return;
3948
+ if (!selectedStep || selectedActionDisabled) return;
3144
3949
  if (stepSchema) {
3145
3950
  const parsed = stepSchema.safeParse(stepInputFormState);
3146
3951
  if (!parsed.success) {
@@ -3167,196 +3972,238 @@ function BuildTab({
3167
3972
  if (!resourceId) return;
3168
3973
  onViewRunLog(resourceId, run.executionId);
3169
3974
  };
3170
- return /* @__PURE__ */ jsx(
3171
- TabSection,
3172
- {
3173
- icon: /* @__PURE__ */ jsx(IconBolt, { size: 16 }),
3174
- title: "Build",
3175
- description: getBuildTabDescription(steps, currentStep),
3176
- children: /* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 1, md: 2 }, spacing: "md", children: [
3177
- /* @__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) => {
3178
- const isCurrent = currentStep?.id === step.id;
3179
- const isSelected = selectedStep?.id === step.id;
3180
- const isWaiting = step.status === "blocked";
3181
- const isPassiveWaiting = isWaiting && !isSelected && !isCurrent;
3182
- const stepTone = isSelected || isCurrent ? step.status : step.status === "complete" || isWaiting ? step.status : "neutral";
3183
- return /* @__PURE__ */ jsx(
3184
- UnstyledButton,
3185
- {
3186
- onClick: () => setSelectedStepId(step.id),
3187
- style: {
3188
- backgroundColor: isSelected ? "var(--surface-primary-strong)" : isPassiveWaiting ? "color-mix(in srgb, var(--color-surface-hover) 38%, transparent)" : "transparent",
3189
- border: isSelected ? "var(--active-border)" : "1px solid var(--color-border)",
3190
- borderStyle: isPassiveWaiting ? "dashed" : "solid",
3191
- borderRadius: 8,
3192
- display: "block",
3193
- opacity: isPassiveWaiting ? 0.72 : 1,
3194
- padding: 12,
3195
- textAlign: "left",
3196
- width: "100%"
3197
- },
3198
- children: /* @__PURE__ */ jsxs(Group, { gap: "sm", align: "flex-start", wrap: "nowrap", children: [
3199
- /* @__PURE__ */ jsx(
3200
- Box,
3201
- {
3202
- w: 28,
3203
- h: 28,
3204
- style: {
3205
- alignItems: "center",
3206
- ...getBuildToneStyle(stepTone),
3207
- borderRadius: 999,
3208
- borderStyle: "solid",
3209
- borderWidth: 1,
3210
- display: "flex",
3211
- flexShrink: 0,
3212
- fontWeight: 700,
3213
- justifyContent: "center"
3214
- },
3215
- children: index + 1
3216
- }
3217
- ),
3218
- /* @__PURE__ */ jsxs(Stack, { gap: 4, style: { minWidth: 0, flex: 1 }, children: [
3219
- /* @__PURE__ */ jsxs(Group, { gap: "xs", justify: "space-between", wrap: "nowrap", children: [
3220
- /* @__PURE__ */ jsx(Text, { fw: 700, c: isPassiveWaiting ? "dimmed" : void 0, truncate: true, children: step.label }),
3221
- isCurrent ? /* @__PURE__ */ jsx(StatusPill, { label: "Current", tone: step.status }) : null,
3222
- !isCurrent && isWaiting ? /* @__PURE__ */ jsx(StatusPill, { label: "Waiting", tone: "blocked" }) : null
3223
- ] }),
3224
- /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", lineClamp: 2, children: step.description }),
3225
- /* @__PURE__ */ jsx(StepCounterLine, { step })
3226
- ] })
3227
- ] })
3975
+ return /* @__PURE__ */ jsx(TabSection, { icon: /* @__PURE__ */ jsx(IconBolt, { size: 16 }), title: "Build", description: getBuildTabDescription(steps, currentStep), children: /* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 1, md: 2 }, spacing: "md", children: [
3976
+ /* @__PURE__ */ jsx(Card, { withBorder: true, style: { height: "100%", display: "flex", flexDirection: "column" }, children: /* @__PURE__ */ jsxs(Stack, { gap: "xs", style: { flex: 1, minHeight: 0 }, children: [
3977
+ steps.map(
3978
+ (step) => step.action?.resourceId ? /* @__PURE__ */ jsx(
3979
+ BuildStepRunningWatcher,
3980
+ {
3981
+ resourceId: step.action.resourceId,
3982
+ onRunningChange: handleResourceRunningChange
3983
+ },
3984
+ `running-${step.id}`
3985
+ ) : null
3986
+ ),
3987
+ steps.map((step, index) => {
3988
+ const isCurrent = currentStep?.id === step.id;
3989
+ const isSelected = selectedStep?.id === step.id;
3990
+ const stepResourceId = step.action?.resourceId ?? null;
3991
+ const isStepRunning = stepResourceId ? runningResources[stepResourceId] ?? false : false;
3992
+ const isWaiting = step.status === "blocked";
3993
+ const isPassiveWaiting = isWaiting && !isSelected && !isCurrent;
3994
+ const stepTone = isStepRunning ? "ready" : isSelected || isCurrent ? step.status : step.status === "complete" || isWaiting ? step.status : "neutral";
3995
+ return /* @__PURE__ */ jsx(
3996
+ UnstyledButton,
3997
+ {
3998
+ onClick: () => setSelectedStepId(step.id),
3999
+ style: {
4000
+ backgroundColor: isSelected ? "var(--surface-primary-strong)" : isStepRunning ? "color-mix(in srgb, var(--color-primary) 12%, transparent)" : isPassiveWaiting ? "color-mix(in srgb, var(--color-surface-hover) 38%, transparent)" : "transparent",
4001
+ border: isSelected ? "var(--active-border)" : isStepRunning ? "1px solid var(--border-primary-muted)" : "1px solid var(--color-border)",
4002
+ borderStyle: isPassiveWaiting && !isStepRunning ? "dashed" : "solid",
4003
+ borderRadius: 8,
4004
+ display: "block",
4005
+ opacity: isPassiveWaiting && !isStepRunning ? 0.72 : 1,
4006
+ padding: 12,
4007
+ textAlign: "left",
4008
+ width: "100%"
3228
4009
  },
3229
- step.id
3230
- );
3231
- }) }) }),
3232
- 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: [
3233
- /* @__PURE__ */ jsxs(Stack, { gap: 2, style: { minWidth: 0, flexShrink: 0 }, children: [
3234
- /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", fw: 700, tt: "uppercase", children: [
3235
- "Step ",
3236
- selectedStepIndex + 1,
3237
- " details"
3238
- ] }),
3239
- /* @__PURE__ */ jsx(Title, { order: 5, children: selectedStep.label })
4010
+ children: /* @__PURE__ */ jsxs(Group, { gap: "sm", align: "flex-start", wrap: "nowrap", children: [
4011
+ /* @__PURE__ */ jsx(
4012
+ Box,
4013
+ {
4014
+ w: 28,
4015
+ h: 28,
4016
+ style: {
4017
+ alignItems: "center",
4018
+ ...getBuildToneStyle(stepTone),
4019
+ borderRadius: 999,
4020
+ borderStyle: "solid",
4021
+ borderWidth: 1,
4022
+ display: "flex",
4023
+ flexShrink: 0,
4024
+ fontWeight: 700,
4025
+ justifyContent: "center"
4026
+ },
4027
+ children: index + 1
4028
+ }
4029
+ ),
4030
+ /* @__PURE__ */ jsxs(Stack, { gap: 4, style: { minWidth: 0, flex: 1 }, children: [
4031
+ /* @__PURE__ */ jsxs(Group, { gap: "xs", justify: "space-between", wrap: "nowrap", children: [
4032
+ /* @__PURE__ */ jsx(Text, { fw: 700, c: isPassiveWaiting && !isStepRunning ? "dimmed" : void 0, truncate: true, children: step.label }),
4033
+ isStepRunning ? /* @__PURE__ */ jsx(StatusPill, { label: "Running", tone: "ready" }) : null,
4034
+ !isStepRunning && isCurrent ? /* @__PURE__ */ jsx(StatusPill, { label: "Current", tone: step.status }) : null,
4035
+ !isStepRunning && !isCurrent && isWaiting ? /* @__PURE__ */ jsx(StatusPill, { label: "Waiting", tone: "blocked" }) : null
4036
+ ] }),
4037
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", lineClamp: 2, children: step.description }),
4038
+ /* @__PURE__ */ jsx(StepCounterLine, { step })
4039
+ ] })
4040
+ ] })
4041
+ },
4042
+ step.id
4043
+ );
4044
+ })
4045
+ ] }) }),
4046
+ 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: [
4047
+ /* @__PURE__ */ jsxs(Stack, { gap: 2, style: { minWidth: 0, flexShrink: 0 }, children: [
4048
+ /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", fw: 700, tt: "uppercase", children: [
4049
+ "Step ",
4050
+ selectedStepIndex + 1,
4051
+ " details"
4052
+ ] }),
4053
+ /* @__PURE__ */ jsx(Title, { order: 5, children: selectedStep.label })
4054
+ ] }),
4055
+ /* @__PURE__ */ jsx(BuildStepProgressBar, { step: selectedStep }),
4056
+ /* @__PURE__ */ jsx(
4057
+ StepDetailRightColumn,
4058
+ {
4059
+ activeTab: activeRightColumnTab,
4060
+ onTabChange: setActiveRightColumnTab,
4061
+ configuration: /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
4062
+ selectedResourceId && selectedStep.stageKey ? /* @__PURE__ */ jsx(
4063
+ LeadGenLiveStatusPanel,
4064
+ {
4065
+ listId: list.id,
4066
+ resourceId: selectedResourceId,
4067
+ stageKey: selectedStep.stageKey,
4068
+ stepLabel: selectedStep.label,
4069
+ enabled: true,
4070
+ onRunningChange: handleSelectedResourceRunningChange
4071
+ },
4072
+ selectedResourceId
4073
+ ) : null,
4074
+ selectedStep.status === "blocked" ? /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: selectedStep.emptyBlockedText }) : null,
4075
+ 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,
4076
+ !selectedStep.outputs.includes(selectedStep.primaryEntity) && selectedStep.recommendedAction?.defaultSize !== void 0 && selectedStep.recommendedAction.maxSize !== void 0 ? /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
4077
+ "Batch size: ",
4078
+ selectedStep.recommendedAction.defaultSize,
4079
+ " default,",
4080
+ " ",
4081
+ selectedStep.recommendedAction.maxSize,
4082
+ " max."
4083
+ ] }) : null,
4084
+ showMainStepConfig && visibleStepLayout ? /* @__PURE__ */ jsx(
4085
+ StepConfigForm,
4086
+ {
4087
+ slot: "main",
4088
+ schema: stepSchema,
4089
+ layout: visibleStepLayout,
4090
+ value: stepInputFormState,
4091
+ onChange: (v) => handleStepInputChange(asRecord3(v) ?? {}),
4092
+ disabled: !canRunSelectedAction
4093
+ },
4094
+ `${selectedResourceId ?? "none"}-main-${stepInputResetKey}`
4095
+ ) : !credentialRequirements.length ? /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "No configuration for this step." }) : null,
4096
+ /* @__PURE__ */ jsx(
4097
+ CredentialRequirementsPanel,
4098
+ {
4099
+ requirements: credentialRequirements,
4100
+ credentials,
4101
+ credentialsLoading: credentialsQuery.isLoading,
4102
+ selections: credentialSelections,
4103
+ verificationByCredentialId: credentialVerificationById,
4104
+ verifyingCredentialId: verifyCredential.isPending ? verifyCredential.variables ?? null : null,
4105
+ onSelect: handleCredentialSelect,
4106
+ onVerify: handleVerifyCredential
4107
+ }
4108
+ ),
4109
+ selectedStep.capabilityKey === "lead-gen.export.list" || getClickUpRequirement(selectedStep) ? /* @__PURE__ */ jsx(
4110
+ TextInput,
4111
+ {
4112
+ label: "ClickUp List ID",
4113
+ description: "Destination List ID for approved company tasks.",
4114
+ placeholder: "123456789",
4115
+ value: clickupListId,
4116
+ onChange: (event) => handleClickupListIdChange(event.currentTarget.value)
4117
+ }
4118
+ ) : null
3240
4119
  ] }),
3241
- /* @__PURE__ */ jsx(BuildStepProgressBar, { step: selectedStep }),
3242
- /* @__PURE__ */ jsx(
3243
- StepDetailRightColumn,
4120
+ advanced: showAdvancedStepConfig && visibleStepLayout ? /* @__PURE__ */ jsx(
4121
+ StepConfigForm,
3244
4122
  {
3245
- activeTab: activeRightColumnTab,
3246
- onTabChange: setActiveRightColumnTab,
3247
- configuration: /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
3248
- selectedStep.status === "blocked" ? /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: selectedStep.emptyBlockedText }) : null,
3249
- 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,
3250
- !selectedStep.outputs.includes(selectedStep.primaryEntity) && selectedStep.recommendedAction?.defaultSize !== void 0 && selectedStep.recommendedAction.maxSize !== void 0 ? /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
3251
- "Batch size: ",
3252
- selectedStep.recommendedAction.defaultSize,
3253
- " default,",
3254
- " ",
3255
- selectedStep.recommendedAction.maxSize,
3256
- " max."
3257
- ] }) : null,
3258
- stepSchema && stepLayout ? /* @__PURE__ */ jsx(
3259
- StepConfigForm,
3260
- {
3261
- slot: "main",
3262
- schema: stepSchema,
3263
- layout: stepLayout,
3264
- value: stepInputFormState,
3265
- onChange: (v) => handleStepInputChange(asRecord3(v) ?? {}),
3266
- disabled: !canRunSelectedAction
3267
- },
3268
- `${selectedResourceId ?? "none"}-main-${stepInputResetKey}`
3269
- ) : /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "No configuration for this step." })
3270
- ] }),
3271
- advanced: stepSchema && stepLayout?.advanced ? /* @__PURE__ */ jsx(
3272
- StepConfigForm,
4123
+ slot: "advanced",
4124
+ schema: stepSchema,
4125
+ layout: visibleStepLayout,
4126
+ value: stepInputFormState,
4127
+ onChange: (v) => handleStepInputChange(asRecord3(v) ?? {}),
4128
+ disabled: !canRunSelectedAction
4129
+ },
4130
+ `${selectedResourceId ?? "none"}-advanced-${stepInputResetKey}`
4131
+ ) : void 0,
4132
+ records: /* @__PURE__ */ jsx(
4133
+ StepRecordsPanel,
4134
+ {
4135
+ list,
4136
+ step: selectedStep,
4137
+ credentialSelections,
4138
+ credentials,
4139
+ clickupListId
4140
+ },
4141
+ selectedStep.id
4142
+ ),
4143
+ runs: selectedResourceId ? /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
4144
+ /* @__PURE__ */ jsxs(Group, { justify: "space-between", gap: "xs", children: [
4145
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", fw: 700, tt: "uppercase", children: "Recent runs" }),
4146
+ recentRunsQuery.isFetching ? /* @__PURE__ */ jsx(Loader, { size: "xs" }) : null
4147
+ ] }),
4148
+ !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) => {
4149
+ const canUseSettings = hasRunInput(run);
4150
+ return /* @__PURE__ */ jsx(
4151
+ Box,
3273
4152
  {
3274
- slot: "advanced",
3275
- schema: stepSchema,
3276
- layout: stepLayout,
3277
- value: stepInputFormState,
3278
- onChange: (v) => handleStepInputChange(asRecord3(v) ?? {}),
3279
- disabled: !canRunSelectedAction
4153
+ style: {
4154
+ border: "1px solid var(--color-border)",
4155
+ borderRadius: 8,
4156
+ padding: "8px 10px",
4157
+ width: "100%"
4158
+ },
4159
+ children: /* @__PURE__ */ jsxs(Group, { justify: "space-between", gap: "xs", wrap: "nowrap", children: [
4160
+ /* @__PURE__ */ jsx(
4161
+ UnstyledButton,
4162
+ {
4163
+ onClick: () => openRecentRunLog(run),
4164
+ style: { flex: 1, minWidth: 0, textAlign: "left" },
4165
+ children: /* @__PURE__ */ jsxs(Group, { justify: "space-between", gap: "xs", wrap: "nowrap", children: [
4166
+ /* @__PURE__ */ jsxs(Stack, { gap: 2, style: { minWidth: 0 }, children: [
4167
+ /* @__PURE__ */ jsx(Text, { size: "sm", fw: 600, truncate: true, children: formatDateTime4(run.createdAt) }),
4168
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", ff: "monospace", truncate: true, children: run.executionId })
4169
+ ] }),
4170
+ /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: getRunStatusColor(run.status), children: run.status })
4171
+ ] })
4172
+ }
4173
+ ),
4174
+ /* @__PURE__ */ jsx(Tooltip, { label: "Use this run's settings", children: /* @__PURE__ */ jsx(
4175
+ Button,
4176
+ {
4177
+ size: "xs",
4178
+ variant: "subtle",
4179
+ leftSection: /* @__PURE__ */ jsx(IconSettings, { size: 12 }),
4180
+ disabled: !canUseSettings,
4181
+ onClick: () => loadRecentRunInput(run),
4182
+ children: "Use settings"
4183
+ }
4184
+ ) })
4185
+ ] })
3280
4186
  },
3281
- `${selectedResourceId ?? "none"}-advanced-${stepInputResetKey}`
3282
- ) : void 0,
3283
- runs: selectedResourceId ? /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
3284
- /* @__PURE__ */ jsxs(Group, { justify: "space-between", gap: "xs", children: [
3285
- /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", fw: 700, tt: "uppercase", children: "Recent runs" }),
3286
- recentRunsQuery.isFetching ? /* @__PURE__ */ jsx(Loader, { size: "xs" }) : null
3287
- ] }),
3288
- !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) => {
3289
- const canUseSettings = hasRunInput(run);
3290
- return /* @__PURE__ */ jsx(
3291
- Box,
3292
- {
3293
- style: {
3294
- border: "1px solid var(--color-border)",
3295
- borderRadius: 8,
3296
- padding: "8px 10px",
3297
- width: "100%"
3298
- },
3299
- children: /* @__PURE__ */ jsxs(Group, { justify: "space-between", gap: "xs", wrap: "nowrap", children: [
3300
- /* @__PURE__ */ jsx(
3301
- UnstyledButton,
3302
- {
3303
- onClick: () => openRecentRunLog(run),
3304
- style: { flex: 1, minWidth: 0, textAlign: "left" },
3305
- children: /* @__PURE__ */ jsxs(Group, { justify: "space-between", gap: "xs", wrap: "nowrap", children: [
3306
- /* @__PURE__ */ jsxs(Stack, { gap: 2, style: { minWidth: 0 }, children: [
3307
- /* @__PURE__ */ jsx(Text, { size: "sm", fw: 600, truncate: true, children: formatDateTime4(run.createdAt) }),
3308
- /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", ff: "monospace", truncate: true, children: run.executionId })
3309
- ] }),
3310
- /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: getRunStatusColor(run.status), children: run.status })
3311
- ] })
3312
- }
3313
- ),
3314
- /* @__PURE__ */ jsx(Tooltip, { label: "Use this run's settings", children: /* @__PURE__ */ jsx(
3315
- Button,
3316
- {
3317
- size: "xs",
3318
- variant: "subtle",
3319
- leftSection: /* @__PURE__ */ jsx(IconSettings, { size: 12 }),
3320
- disabled: !canUseSettings,
3321
- onClick: () => loadRecentRunInput(run),
3322
- children: "Use settings"
3323
- }
3324
- ) })
3325
- ] })
3326
- },
3327
- run.executionId
3328
- );
3329
- }) })
3330
- ] }) : /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "No runs available for this step." }),
3331
- action: /* @__PURE__ */ jsxs(Fragment, { children: [
3332
- stepInputError ? /* @__PURE__ */ jsx(Alert, { color: "red", icon: /* @__PURE__ */ jsx(IconAlertCircle, { size: 16 }), variant: "light", children: stepInputError }) : null,
3333
- /* @__PURE__ */ jsxs(Group, { gap: "xs", justify: "flex-end", wrap: "nowrap", children: [
3334
- /* @__PURE__ */ jsx(
3335
- Button,
3336
- {
3337
- leftSection: selectedActionIcon,
3338
- disabled: !canRunSelectedAction,
3339
- onClick: handleSelectedAction,
3340
- children: selectedActionKind === "none" ? "No action" : getStepActionLabel(selectedActionKind)
3341
- }
3342
- ),
3343
- recentRuns.length > 0 ? /* @__PURE__ */ jsx(
3344
- Button,
3345
- {
3346
- variant: "light",
3347
- leftSection: /* @__PURE__ */ jsx(IconPlayerPlay, { size: 14 }),
3348
- onClick: () => onViewRuns(selectedResourceId),
3349
- children: "View runs"
3350
- }
3351
- ) : null
3352
- ] })
3353
- ] })
3354
- }
3355
- )
3356
- ] }) }) : null
3357
- ] })
3358
- }
3359
- );
4187
+ run.executionId
4188
+ );
4189
+ }) })
4190
+ ] }) : /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "No runs available for this step." }),
4191
+ action: /* @__PURE__ */ jsxs(Fragment, { children: [
4192
+ stepInputError ? /* @__PURE__ */ jsx(Alert, { color: "red", icon: /* @__PURE__ */ jsx(IconAlertCircle, { size: 16 }), variant: "light", children: stepInputError }) : null,
4193
+ /* @__PURE__ */ jsx(Group, { gap: "xs", justify: "flex-end", wrap: "nowrap", children: /* @__PURE__ */ jsx(
4194
+ Button,
4195
+ {
4196
+ leftSection: selectedActionIcon,
4197
+ disabled: selectedActionDisabled,
4198
+ onClick: handleSelectedAction,
4199
+ children: selectedActionLabel
4200
+ }
4201
+ ) })
4202
+ ] })
4203
+ }
4204
+ )
4205
+ ] }) }) : null
4206
+ ] }) });
3360
4207
  }
3361
4208
  function MembersTab({
3362
4209
  listId,
@@ -3638,10 +4485,6 @@ function LeadGenListDetailPage({ listId }) {
3638
4485
  params: { workflowId: resourceId },
3639
4486
  search: { exec: executionId }
3640
4487
  });
3641
- },
3642
- onViewRuns: (resourceId) => {
3643
- setRunsResourceFilter(resourceId);
3644
- setActiveTab("runs");
3645
4488
  }
3646
4489
  }
3647
4490
  ) }),