@elevasis/ui 2.25.0 → 2.25.2

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/app/index.css +14 -2
  2. package/dist/app/index.d.ts +457 -357
  3. package/dist/app/index.js +8 -7
  4. package/dist/auth/index.js +4 -4
  5. package/dist/charts/index.js +7 -7
  6. package/dist/{chunk-DDZOHLHB.js → chunk-26HQMR7S.js} +1 -1
  7. package/dist/{chunk-ZMXZ476Y.js → chunk-3AHEHVJ6.js} +3 -3
  8. package/dist/{chunk-LJWV4TWV.js → chunk-4ZFBVND2.js} +2 -2
  9. package/dist/{chunk-7D2HSSIW.js → chunk-7LJUTTXU.js} +5 -5
  10. package/dist/{chunk-ABV5LDDC.js → chunk-AZQY7AJR.js} +13 -24
  11. package/dist/{chunk-BIWHHWCJ.js → chunk-DWK2QIAK.js} +2 -1
  12. package/dist/{chunk-ZDKQNQ4X.js → chunk-GESXCQWY.js} +1 -1
  13. package/dist/{chunk-Z6FAH4XV.js → chunk-HKBEURCV.js} +1 -1
  14. package/dist/{chunk-HC2KV6BU.js → chunk-HOIT677G.js} +1 -1
  15. package/dist/chunk-IS53MXE4.js +230 -0
  16. package/dist/{chunk-WSC5LU3U.js → chunk-IX7LWINC.js} +6 -5
  17. package/dist/{chunk-AXXTN44Z.js → chunk-IYIZYMIE.js} +2 -2
  18. package/dist/{chunk-WWVSPOJY.js → chunk-JDQSCEEF.js} +1237 -642
  19. package/dist/{chunk-AZXSFDG2.js → chunk-JMI7L7Y7.js} +113 -63
  20. package/dist/{chunk-TSSKOQBX.js → chunk-KMAXFJPH.js} +2 -2
  21. package/dist/{chunk-HQ7M6PBW.js → chunk-KU7ZDWQ7.js} +1 -1
  22. package/dist/{chunk-ZGZZIR6K.js → chunk-L4RT57WU.js} +7 -7
  23. package/dist/{chunk-LK4MPIMK.js → chunk-MU4VPAMR.js} +2 -2
  24. package/dist/{chunk-3JCMO7SD.js → chunk-N55DVMAG.js} +1 -1
  25. package/dist/{chunk-EIOJNUPL.js → chunk-PNLJIPV5.js} +1 -1
  26. package/dist/{chunk-6Z3G4U2R.js → chunk-Q5BEODAT.js} +3 -2
  27. package/dist/{chunk-HVC2BTFO.js → chunk-QB2CC4VH.js} +1979 -257
  28. package/dist/{chunk-QJLRDTYS.js → chunk-RSG2O3HF.js} +932 -707
  29. package/dist/{chunk-M25JL54Z.js → chunk-RYTEQBAO.js} +1 -1
  30. package/dist/{chunk-XUYBOO32.js → chunk-U36X6NZM.js} +15 -7
  31. package/dist/{chunk-QSTH6T77.js → chunk-VKMNWHTL.js} +1 -1
  32. package/dist/{chunk-V3UOW2HG.js → chunk-VOVZLL23.js} +4 -4
  33. package/dist/{chunk-SLH2QLKV.js → chunk-WFTNY755.js} +1 -1
  34. package/dist/{chunk-DK2HVHCY.js → chunk-WKJ47GIW.js} +1 -1
  35. package/dist/chunk-X4WBGKJQ.js +138 -0
  36. package/dist/{chunk-QHSW4WHM.js → chunk-XTVZFT7U.js} +1 -1
  37. package/dist/components/index.css +14 -2
  38. package/dist/components/index.d.ts +478 -386
  39. package/dist/components/index.js +138 -336
  40. package/dist/components/navigation/index.css +14 -2
  41. package/dist/components/navigation/index.js +8 -8
  42. package/dist/features/auth/index.css +14 -2
  43. package/dist/features/auth/index.d.ts +369 -363
  44. package/dist/features/auth/index.js +7 -7
  45. package/dist/features/crm/index.css +14 -2
  46. package/dist/features/crm/index.d.ts +551 -360
  47. package/dist/features/crm/index.js +19 -19
  48. package/dist/features/dashboard/index.css +14 -2
  49. package/dist/features/dashboard/index.js +20 -20
  50. package/dist/features/delivery/index.css +14 -2
  51. package/dist/features/delivery/index.d.ts +363 -357
  52. package/dist/features/delivery/index.js +19 -19
  53. package/dist/features/lead-gen/index.css +14 -2
  54. package/dist/features/lead-gen/index.d.ts +160 -2
  55. package/dist/features/lead-gen/index.js +20 -19
  56. package/dist/features/monitoring/index.css +14 -2
  57. package/dist/features/monitoring/index.js +21 -21
  58. package/dist/features/monitoring/requests/index.css +14 -2
  59. package/dist/features/monitoring/requests/index.js +17 -17
  60. package/dist/features/operations/index.css +14 -2
  61. package/dist/features/operations/index.js +24 -23
  62. package/dist/features/seo/index.js +2 -2
  63. package/dist/features/settings/index.css +14 -2
  64. package/dist/features/settings/index.d.ts +369 -363
  65. package/dist/features/settings/index.js +19 -19
  66. package/dist/hooks/delivery/index.css +14 -2
  67. package/dist/hooks/delivery/index.d.ts +363 -357
  68. package/dist/hooks/delivery/index.js +2 -2
  69. package/dist/hooks/index.css +14 -2
  70. package/dist/hooks/index.d.ts +746 -399
  71. package/dist/hooks/index.js +17 -17
  72. package/dist/hooks/published.css +14 -2
  73. package/dist/hooks/published.d.ts +746 -399
  74. package/dist/hooks/published.js +17 -17
  75. package/dist/index.css +14 -2
  76. package/dist/index.d.ts +862 -381
  77. package/dist/index.js +17 -17
  78. package/dist/initialization/index.d.ts +369 -363
  79. package/dist/initialization/index.js +4 -4
  80. package/dist/layout/index.d.ts +6 -0
  81. package/dist/layout/index.js +5 -5
  82. package/dist/organization/index.css +14 -2
  83. package/dist/organization/index.js +4 -4
  84. package/dist/profile/index.d.ts +369 -363
  85. package/dist/profile/index.js +2 -2
  86. package/dist/provider/ElevasisServiceContext.d.ts +1 -0
  87. package/dist/provider/ElevasisServiceContext.js +1 -1
  88. package/dist/provider/index.css +14 -2
  89. package/dist/provider/index.d.ts +503 -362
  90. package/dist/provider/index.js +14 -14
  91. package/dist/provider/published.css +14 -2
  92. package/dist/provider/published.d.ts +494 -359
  93. package/dist/provider/published.js +12 -12
  94. package/dist/supabase/index.d.ts +369 -357
  95. package/dist/test-utils/index.js +1 -1
  96. package/dist/typeform/index.js +49 -20
  97. package/dist/types/index.d.ts +382 -363
  98. package/package.json +4 -4
  99. package/dist/chunk-CEWTOKE7.js +0 -109
  100. /package/dist/{chunk-IRW7JMQ4.js → chunk-5WWZXCS5.js} +0 -0
  101. /package/dist/{chunk-QJ2KCHKX.js → chunk-E565XMTQ.js} +0 -0
@@ -1,22 +1,24 @@
1
+ import { DEFAULT_ORGANIZATION_MODEL_PROSPECTING } from './chunk-IS53MXE4.js';
1
2
  import { PageContainer } from './chunk-BZZCNLT6.js';
2
3
  import { TableSelectionToolbar, SortableHeader } from './chunk-TUMSNGTX.js';
3
- import { SubshellNavItem } from './chunk-CEWTOKE7.js';
4
+ import { SubshellNavItem } from './chunk-X4WBGKJQ.js';
4
5
  import { SubshellSidebarSection } from './chunk-IIMU5YAJ.js';
5
6
  import { FilterBar } from './chunk-PDHTXPSF.js';
6
7
  import { CustomModal } from './chunk-KVJ3LFH2.js';
7
- import { acquisitionListKeys, useListsTelemetry, useLists, useCreateList, useTableSort, sortData, usePaginationState, useTableSelection, useList, useListProgress, useListExecutions, useDeleteList, useCompanyFacets, useCompanies, useDeleteCompanies, useContacts, useDeleteContacts, useArtifacts, useListMembers, useListMember, useTransitionListMember, useDeriveActions } from './chunk-QJLRDTYS.js';
8
- import { showApiErrorNotification, showSuccessNotification } from './chunk-Z6FAH4XV.js';
9
- import { LEAD_GEN_STAGE_CATALOG, LEAD_GEN_PIPELINE_DEFINITIONS, findPipeline } from './chunk-AZXSFDG2.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-RSG2O3HF.js';
9
+ import { showApiErrorNotification, showSuccessNotification } from './chunk-HKBEURCV.js';
10
+ import { useListActions, LEAD_GEN_STAGE_CATALOG, LEAD_GEN_PIPELINE_DEFINITIONS, findPipeline } from './chunk-JMI7L7Y7.js';
10
11
  import { SubshellContentContainer } from './chunk-TKAYX2SP.js';
11
- import { PageTitleCaption, CenteredErrorState, StatCard, CardHeader, EmptyState, JsonViewer } from './chunk-XUYBOO32.js';
12
+ import { PageTitleCaption, CenteredErrorState, StatCard, CardHeader, EmptyState, JsonViewer } from './chunk-U36X6NZM.js';
12
13
  import { useRouterContext } from './chunk-Q7DJKLEN.js';
13
- import { useElevasisServices } from './chunk-IRW7JMQ4.js';
14
- import { Stack, Paper, Text, Anchor, Group, Title, ActionIcon, Divider, Box, SimpleGrid, Badge, Card, Button, Center, Loader, Alert, Table, TextInput, Select, Checkbox, Pagination, Textarea, Tooltip, Tabs, SegmentedControl, Drawer } from '@mantine/core';
15
- import { IconLayoutGrid, IconList, IconBuilding, IconAddressBook, IconTarget, IconExternalLink, IconX, IconAlertCircle, IconPlayerPlay, IconArrowRight, IconSparkles, IconListDetails, IconPlus, IconSearch, IconAlertTriangle, IconCopy, IconTrash, IconUsers, IconDatabase, IconBuildingFactory2, IconArrowLeft, IconChevronRight, IconMail, IconUser } from '@tabler/icons-react';
14
+ 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
17
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
17
18
  import { Link, useNavigate, useSearch } from '@tanstack/react-router';
18
19
  import { useQueryClient, useMutation } from '@tanstack/react-query';
19
- import { useState, useMemo, useEffect } from 'react';
20
+ import { useState, useMemo, useEffect, Fragment as Fragment$1 } from 'react';
21
+ import { useForm } from '@mantine/form';
20
22
 
21
23
  var LeadGenSidebarTop = () => {
22
24
  return /* @__PURE__ */ jsx(SubshellSidebarSection, { icon: IconTarget, label: "Lead Gen" });
@@ -37,6 +39,7 @@ var LeadGenSidebarMiddle = ({ items = LEAD_GEN_ITEMS } = {}) => {
37
39
  icon: item.icon,
38
40
  label: item.label,
39
41
  isActive,
42
+ href: item.to,
40
43
  onClick: () => navigate(item.to)
41
44
  },
42
45
  item.to
@@ -56,6 +59,40 @@ var leadGenManifest = {
56
59
  icon: IconTarget,
57
60
  sidebar: LeadGenSidebar
58
61
  };
62
+
63
+ // ../core/src/business/acquisition/build-templates.ts
64
+ var PROSPECTING_BUILD_TEMPLATE_OPTIONS = DEFAULT_ORGANIZATION_MODEL_PROSPECTING.buildTemplates.map(
65
+ ({ id, label, description }) => ({
66
+ id,
67
+ label,
68
+ description
69
+ })
70
+ );
71
+ var DEFAULT_PROSPECTING_BUILD_TEMPLATE_ID = DEFAULT_ORGANIZATION_MODEL_PROSPECTING.defaultBuildTemplateId;
72
+ function createBuildPlanSnapshotFromTemplateId(templateId) {
73
+ const template = DEFAULT_ORGANIZATION_MODEL_PROSPECTING.buildTemplates.find((item) => item.id === templateId);
74
+ if (!template) return null;
75
+ return {
76
+ templateId: template.id,
77
+ templateLabel: template.label,
78
+ steps: template.steps.map((step) => {
79
+ const snapshotStep = {
80
+ id: step.id,
81
+ label: step.label,
82
+ primaryEntity: step.primaryEntity,
83
+ outputs: [...step.outputs],
84
+ stageKey: step.stageKey,
85
+ dependencyMode: step.dependencyMode,
86
+ capabilityKey: step.capabilityKey,
87
+ defaultBatchSize: step.defaultBatchSize,
88
+ maxBatchSize: step.maxBatchSize
89
+ };
90
+ if (step.description) snapshotStep.description = step.description;
91
+ if (step.dependsOn?.length) snapshotStep.dependsOn = [...step.dependsOn];
92
+ return snapshotStep;
93
+ })
94
+ };
95
+ }
59
96
  var LEAD_GEN_ROUTE_LINKS = [
60
97
  { label: "Overview", to: "/lead-gen" },
61
98
  { label: "Lists", to: "/lead-gen/lists" },
@@ -462,14 +499,7 @@ function LeadGenOverviewPage() {
462
499
  return b.totalContacts - a.totalContacts;
463
500
  }) ?? [];
464
501
  return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsx(PageContainer, { children: /* @__PURE__ */ jsxs(Stack, { children: [
465
- /* @__PURE__ */ jsx(
466
- PageTitleCaption,
467
- {
468
- title: "Lead Gen Overview",
469
- caption: summaryLine,
470
- rightSection: /* @__PURE__ */ jsx(Button, { component: Link, to: "/lead-gen/lists", size: "xs", variant: "light", children: "Lists" })
471
- }
472
- ),
502
+ /* @__PURE__ */ jsx(PageTitleCaption, { title: "Lead Gen Overview", caption: summaryLine }),
473
503
  telemetryQuery.isLoading ? /* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsx(Center, { p: "xl", children: /* @__PURE__ */ jsx(Loader, {}) }) }) : telemetryQuery.isError ? /* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsx(CenteredErrorState, { error: telemetryQuery.error, title: "Failed to load list telemetry" }) }) : !data?.length ? /* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsx(Center, { h: 300, children: /* @__PURE__ */ jsx(Alert, { icon: /* @__PURE__ */ jsx(IconAlertCircle, { size: 16 }), color: "gray", variant: "light", children: "No lists yet." }) }) }) : /* @__PURE__ */ jsxs(Fragment, { children: [
474
504
  /* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 1, sm: 3 }, children: [
475
505
  /* @__PURE__ */ jsx(StatCard, { variant: "hero", icon: IconPlayerPlay, value: totalLists, label: "Total Lists" }),
@@ -477,15 +507,7 @@ function LeadGenOverviewPage() {
477
507
  /* @__PURE__ */ jsx(StatCard, { variant: "hero", icon: IconAlertCircle, value: deliverabilityRiskCount, label: "At Risk" })
478
508
  ] }),
479
509
  /* @__PURE__ */ jsx(Paper, { withBorder: true, p: "md", children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
480
- /* @__PURE__ */ jsx(
481
- CardHeader,
482
- {
483
- icon: /* @__PURE__ */ jsx(IconSparkles, { size: 18 }),
484
- title: "Next Action",
485
- subtitle: primaryAction.title,
486
- rightSection: /* @__PURE__ */ jsx(Button, { component: Link, to: primaryAction.buttonTo, size: "xs", variant: "light", children: primaryAction.buttonLabel })
487
- }
488
- ),
510
+ /* @__PURE__ */ jsx(CardHeader, { icon: /* @__PURE__ */ jsx(IconSparkles, { size: 18 }), title: "Next Action", subtitle: primaryAction.title }),
489
511
  /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", maw: 620, children: primaryAction.detail }),
490
512
  /* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 1, md: 2 }, children: [
491
513
  /* @__PURE__ */ jsxs(Card, { withBorder: true, p: "sm", children: [
@@ -510,8 +532,7 @@ function LeadGenOverviewPage() {
510
532
  {
511
533
  icon: /* @__PURE__ */ jsx(IconListDetails, { size: 18 }),
512
534
  title: "List Snapshot",
513
- subtitle: "Ranked to surface active work first, then the largest unresolved backlogs.",
514
- rightSection: /* @__PURE__ */ jsx(Button, { component: Link, to: "/lead-gen/lists", size: "xs", variant: "subtle", children: "Open lists" })
535
+ subtitle: "Ranked to surface active work first, then the largest unresolved backlogs."
515
536
  }
516
537
  ),
517
538
  /* @__PURE__ */ jsxs(Table, { children: [
@@ -549,6 +570,10 @@ function LeadGenOverviewPage() {
549
570
  ] }) }) });
550
571
  }
551
572
  var PAGE_SIZE_DEFAULT = 20;
573
+ var BUILD_TEMPLATE_SELECT_OPTIONS = PROSPECTING_BUILD_TEMPLATE_OPTIONS.map((template) => ({
574
+ value: template.id,
575
+ label: template.label
576
+ }));
552
577
  var STATUS_FILTER_OPTIONS = [
553
578
  { value: "", label: "All statuses" },
554
579
  { value: "draft", label: "Draft" },
@@ -564,7 +589,7 @@ function LeadGenListsPage() {
564
589
  const [showCreateList, setShowCreateList] = useState(false);
565
590
  const [newListName, setNewListName] = useState("");
566
591
  const [newListDescription, setNewListDescription] = useState("");
567
- const [newRubricKey, setNewRubricKey] = useState("");
592
+ const [newBuildTemplateId, setNewBuildTemplateId] = useState(DEFAULT_PROSPECTING_BUILD_TEMPLATE_ID);
568
593
  const [showBatchDelete, setShowBatchDelete] = useState(false);
569
594
  const { data: lists, isLoading: listsLoading } = useLists();
570
595
  const { data: telemetry, isLoading: telemetryLoading } = useListsTelemetry();
@@ -605,21 +630,20 @@ function LeadGenListsPage() {
605
630
  [sortedLists, pagination.offset]
606
631
  );
607
632
  const selection = useTableSelection(paginatedLists, sortedLists);
633
+ const selectedBuildTemplate = PROSPECTING_BUILD_TEMPLATE_OPTIONS.find((template) => template.id === newBuildTemplateId);
608
634
  function resetCreateListModal() {
609
635
  setNewListName("");
610
636
  setNewListDescription("");
611
- setNewRubricKey("");
637
+ setNewBuildTemplateId(DEFAULT_PROSPECTING_BUILD_TEMPLATE_ID);
612
638
  setShowCreateList(false);
613
639
  }
614
640
  function handleCreateList() {
615
641
  const trimmedName = newListName.trim();
616
642
  if (!trimmedName) return;
617
- const trimmedRubric = newRubricKey.trim();
618
- const icp = trimmedRubric ? { qualificationRubricKey: trimmedRubric } : void 0;
619
643
  const body = {
620
644
  name: trimmedName,
621
645
  description: newListDescription.trim() || null,
622
- ...icp ? { icp } : {}
646
+ buildTemplateId: newBuildTemplateId
623
647
  };
624
648
  createListMutation.mutate(body, {
625
649
  onSuccess: (list) => {
@@ -708,13 +732,14 @@ function LeadGenListsPage() {
708
732
  /* @__PURE__ */ jsx(SortableHeader, { column: "name", sort, onToggle: toggleSort, children: "Name" }),
709
733
  /* @__PURE__ */ jsx(SortableHeader, { column: "description", sort, onToggle: toggleSort, children: "Description" }),
710
734
  /* @__PURE__ */ jsx(SortableHeader, { column: "status", sort, onToggle: toggleSort, children: "Status" }),
711
- /* @__PURE__ */ jsx(Table.Th, { children: "ICP Rubric" }),
735
+ /* @__PURE__ */ jsx(Table.Th, { children: "Pipeline" }),
712
736
  /* @__PURE__ */ jsx(SortableHeader, { column: "contacts", sort, onToggle: toggleSort, children: "Contacts" }),
713
737
  /* @__PURE__ */ jsx(SortableHeader, { column: "batches", sort, onToggle: toggleSort, children: "Batches" }),
714
- /* @__PURE__ */ jsx(SortableHeader, { column: "created", sort, onToggle: toggleSort, children: "Created" })
738
+ /* @__PURE__ */ jsx(SortableHeader, { column: "created", sort, onToggle: toggleSort, children: "Created" }),
739
+ /* @__PURE__ */ jsx(Table.Th, { children: "List" })
715
740
  ] }) }),
716
741
  /* @__PURE__ */ jsx(Table.Tbody, { children: paginatedLists.map((list) => {
717
- const rubricKey = list.icp?.qualificationRubricKey;
742
+ const pipelineLabel = list.metadata.buildPlanSnapshot?.templateLabel;
718
743
  return /* @__PURE__ */ jsxs(
719
744
  Table.Tr,
720
745
  {
@@ -745,10 +770,20 @@ function LeadGenListsPage() {
745
770
  ] }) }),
746
771
  /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: list.description || "-" }) }),
747
772
  /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Badge, { size: "sm", color: getStateKeyColor(list.status), variant: "light", children: list.status }) }),
748
- /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: rubricKey || "\u2014" }) }),
773
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: pipelineLabel || "\u2014" }) }),
749
774
  /* @__PURE__ */ jsx(Table.Td, { children: contactCountByListId.get(list.id) ?? 0 }),
750
775
  /* @__PURE__ */ jsx(Table.Td, { children: list.batchIds.length }),
751
- /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: formatDate(list.createdAt) }) })
776
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: formatDate(list.createdAt) }) }),
777
+ /* @__PURE__ */ jsx(Table.Td, { onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsx(
778
+ Button,
779
+ {
780
+ size: "xs",
781
+ variant: "subtle",
782
+ leftSection: /* @__PURE__ */ jsx(IconList, { size: 14 }),
783
+ onClick: () => navigate({ to: "/lead-gen/lists/$listId", params: { listId: list.id } }),
784
+ children: "Open"
785
+ }
786
+ ) })
752
787
  ]
753
788
  },
754
789
  list.id
@@ -799,18 +834,22 @@ function LeadGenListsPage() {
799
834
  minRows: 3
800
835
  }
801
836
  ),
802
- /* @__PURE__ */ jsx(
803
- TextInput,
804
- {
805
- label: "ICP Rubric Key",
806
- description: "Optional free-form text key identifying the qualification rubric for this list.",
807
- placeholder: "e.g. smb-veterinary-us",
808
- value: newRubricKey,
809
- onChange: (event) => setNewRubricKey(event.currentTarget.value),
810
- disabled: createListMutation.isPending
811
- }
812
- ),
813
- /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "The list will open in draft mode after creation so you can review configuration, edit pipeline steps, and run workflows from the detail page." }),
837
+ /* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
838
+ /* @__PURE__ */ jsx(
839
+ Select,
840
+ {
841
+ label: "Build Pipeline",
842
+ description: "Choose the sequence this list should follow.",
843
+ data: BUILD_TEMPLATE_SELECT_OPTIONS,
844
+ value: newBuildTemplateId,
845
+ onChange: (value) => setNewBuildTemplateId(value ?? DEFAULT_PROSPECTING_BUILD_TEMPLATE_ID),
846
+ disabled: createListMutation.isPending,
847
+ required: true
848
+ }
849
+ ),
850
+ selectedBuildTemplate?.description ? /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: selectedBuildTemplate.description }) : null
851
+ ] }),
852
+ /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "The list will open in draft mode after creation so you can review configuration and run build steps from the list workspace." }),
814
853
  /* @__PURE__ */ jsxs(Group, { justify: "flex-end", children: [
815
854
  /* @__PURE__ */ jsx(Button, { variant: "light", onClick: resetCreateListModal, disabled: createListMutation.isPending, children: "Cancel" }),
816
855
  /* @__PURE__ */ jsx(Button, { onClick: handleCreateList, loading: createListMutation.isPending, disabled: !newListName.trim(), children: "Create List" })
@@ -1045,168 +1084,1485 @@ function ListMemberDrawer({ memberId, memberKind, listId, onClose }) {
1045
1084
  }
1046
1085
  );
1047
1086
  }
1048
- function formatDateTime2(value) {
1049
- if (!value) return "Not yet";
1050
- return new Date(value).toLocaleString("en-US", {
1051
- month: "short",
1052
- day: "numeric",
1053
- year: "numeric",
1054
- hour: "numeric",
1055
- minute: "2-digit"
1056
- });
1057
- }
1058
- function contactDisplayName(firstName, lastName) {
1059
- const full = [firstName, lastName].filter(Boolean).join(" ").trim();
1060
- return full || "\u2014";
1087
+ function ListBuilderIndexPage() {
1088
+ const navigate = useNavigate();
1089
+ const [query, setQuery] = useState("");
1090
+ const listsQuery = useLists();
1091
+ const lists = listsQuery.data ?? [];
1092
+ const filteredLists = useMemo(() => {
1093
+ const normalized = query.trim().toLowerCase();
1094
+ if (!normalized) return lists;
1095
+ return lists.filter((list) => list.name.toLowerCase().includes(normalized));
1096
+ }, [lists, query]);
1097
+ const openList = (listId) => {
1098
+ navigate({ to: "/lead-gen/list-builder/$listId", params: { listId } });
1099
+ };
1100
+ return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsx(PageContainer, { children: /* @__PURE__ */ jsxs(Stack, { children: [
1101
+ /* @__PURE__ */ jsx(PageTitleCaption, { title: "List Builder", caption: "Choose a list to run lead-gen workflows and monitor progress." }),
1102
+ /* @__PURE__ */ jsx(Paper, { withBorder: true, p: "md", children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
1103
+ /* @__PURE__ */ jsx(
1104
+ TextInput,
1105
+ {
1106
+ placeholder: "Search lists...",
1107
+ leftSection: /* @__PURE__ */ jsx(IconSearch, { size: 16 }),
1108
+ value: query,
1109
+ onChange: (event) => setQuery(event.currentTarget.value)
1110
+ }
1111
+ ),
1112
+ listsQuery.isLoading ? /* @__PURE__ */ jsx(Center, { p: "xl", children: /* @__PURE__ */ jsx(Loader, {}) }) : !filteredLists.length ? /* @__PURE__ */ jsx(
1113
+ EmptyState,
1114
+ {
1115
+ icon: IconLayoutDashboard,
1116
+ title: query.trim() ? "No lists match your search" : "No lists available",
1117
+ description: query.trim() ? void 0 : "Create a list first, then open it in the builder."
1118
+ }
1119
+ ) : /* @__PURE__ */ jsxs(Table, { highlightOnHover: true, children: [
1120
+ /* @__PURE__ */ jsx(Table.Thead, { children: /* @__PURE__ */ jsxs(Table.Tr, { children: [
1121
+ /* @__PURE__ */ jsx(Table.Th, { children: "Name" }),
1122
+ /* @__PURE__ */ jsx(Table.Th, { children: "Status" }),
1123
+ /* @__PURE__ */ jsx(Table.Th, { children: "Batches" }),
1124
+ /* @__PURE__ */ jsx(Table.Th, { children: "Created" })
1125
+ ] }) }),
1126
+ /* @__PURE__ */ jsx(Table.Tbody, { children: filteredLists.map((list) => /* @__PURE__ */ jsxs(
1127
+ Table.Tr,
1128
+ {
1129
+ role: "button",
1130
+ tabIndex: 0,
1131
+ onClick: () => openList(list.id),
1132
+ onKeyDown: (event) => {
1133
+ if (event.key === "Enter" || event.key === " ") {
1134
+ event.preventDefault();
1135
+ openList(list.id);
1136
+ }
1137
+ },
1138
+ style: { cursor: "pointer" },
1139
+ children: [
1140
+ /* @__PURE__ */ jsxs(Table.Td, { children: [
1141
+ /* @__PURE__ */ jsx(Text, { fw: 500, children: list.name }),
1142
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: list.description ?? list.id })
1143
+ ] }),
1144
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: getStateKeyColor(list.status), children: list.status }) }),
1145
+ /* @__PURE__ */ jsx(Table.Td, { children: list.batchIds.length }),
1146
+ /* @__PURE__ */ jsx(Table.Td, { children: formatDate(list.createdAt) })
1147
+ ]
1148
+ },
1149
+ list.id
1150
+ )) })
1151
+ ] })
1152
+ ] }) })
1153
+ ] }) }) });
1061
1154
  }
1062
- function getStatusColor2(status) {
1063
- switch (status) {
1064
- case "draft":
1065
- return "gray";
1066
- case "enriching":
1067
- return "blue";
1068
- case "launched":
1069
- return "green";
1070
- case "closing":
1071
- return "yellow";
1072
- case "archived":
1073
- return "red";
1074
- default:
1075
- return "gray";
1155
+
1156
+ // src/lib/lead-gen/stage-colors.ts
1157
+ var FALLBACK_STAGE_COLORS = {
1158
+ accent: "var(--color-text-dimmed)",
1159
+ background: "color-mix(in srgb, var(--color-text-dimmed) 10%, transparent)",
1160
+ border: "color-mix(in srgb, var(--color-border) 85%, var(--color-text-dimmed))",
1161
+ text: "var(--color-text)"
1162
+ };
1163
+ var STAGE_COLOR_SETS = {
1164
+ scraped: {
1165
+ accent: "var(--color-text-subtle)",
1166
+ background: "color-mix(in srgb, var(--color-text-subtle) 12%, transparent)",
1167
+ border: "color-mix(in srgb, var(--color-border) 80%, var(--color-text-subtle))",
1168
+ text: "var(--color-text)"
1169
+ },
1170
+ populated: {
1171
+ accent: "var(--color-primary)",
1172
+ background: "color-mix(in srgb, var(--color-primary) 12%, transparent)",
1173
+ border: "color-mix(in srgb, var(--color-border) 70%, var(--color-primary))",
1174
+ text: "var(--color-text)"
1175
+ },
1176
+ extracted: {
1177
+ accent: "color-mix(in srgb, var(--color-primary) 78%, var(--color-success))",
1178
+ background: "color-mix(in srgb, var(--color-primary) 10%, transparent)",
1179
+ border: "color-mix(in srgb, var(--color-border) 65%, var(--color-primary))",
1180
+ text: "var(--color-text)"
1181
+ },
1182
+ enriched: {
1183
+ accent: "color-mix(in srgb, var(--color-success) 70%, var(--color-primary))",
1184
+ background: "color-mix(in srgb, var(--color-success) 12%, transparent)",
1185
+ border: "color-mix(in srgb, var(--color-border) 68%, var(--color-success))",
1186
+ text: "var(--color-text)"
1187
+ },
1188
+ discovered: {
1189
+ accent: "color-mix(in srgb, var(--color-primary) 70%, var(--color-warning))",
1190
+ background: "color-mix(in srgb, var(--color-warning) 10%, transparent)",
1191
+ border: "color-mix(in srgb, var(--color-border) 68%, var(--color-warning))",
1192
+ text: "var(--color-text)"
1193
+ },
1194
+ verified: {
1195
+ accent: "var(--color-success)",
1196
+ background: "color-mix(in srgb, var(--color-success) 12%, transparent)",
1197
+ border: "color-mix(in srgb, var(--color-border) 64%, var(--color-success))",
1198
+ text: "var(--color-text)"
1199
+ },
1200
+ qualified: {
1201
+ accent: "color-mix(in srgb, var(--color-success) 82%, var(--color-text))",
1202
+ background: "color-mix(in srgb, var(--color-success) 14%, transparent)",
1203
+ border: "color-mix(in srgb, var(--color-border) 62%, var(--color-success))",
1204
+ text: "var(--color-text)"
1205
+ },
1206
+ personalized: {
1207
+ accent: "color-mix(in srgb, var(--color-primary) 58%, var(--color-success))",
1208
+ background: "color-mix(in srgb, var(--color-primary) 11%, transparent)",
1209
+ border: "color-mix(in srgb, var(--color-border) 66%, var(--color-primary))",
1210
+ text: "var(--color-text)"
1211
+ },
1212
+ uploaded: {
1213
+ accent: "color-mix(in srgb, var(--color-warning) 72%, var(--color-primary))",
1214
+ background: "color-mix(in srgb, var(--color-warning) 12%, transparent)",
1215
+ border: "color-mix(in srgb, var(--color-border) 64%, var(--color-warning))",
1216
+ text: "var(--color-text)"
1217
+ },
1218
+ interested: {
1219
+ accent: "color-mix(in srgb, var(--color-success) 72%, var(--color-warning))",
1220
+ background: "color-mix(in srgb, var(--color-success) 15%, transparent)",
1221
+ border: "color-mix(in srgb, var(--color-border) 58%, var(--color-success))",
1222
+ text: "var(--color-text)"
1076
1223
  }
1224
+ };
1225
+ function getLeadGenStageColor(stageKey) {
1226
+ if (!LEAD_GEN_STAGE_CATALOG[stageKey]) return FALLBACK_STAGE_COLORS;
1227
+ return STAGE_COLOR_SETS[stageKey] ?? FALLBACK_STAGE_COLORS;
1077
1228
  }
1078
- function getMemberStateColor2(stateKey) {
1079
- switch (stateKey) {
1080
- case "personalized":
1081
- case "verified":
1082
- case "qualified":
1083
- case "interested":
1084
- return "green";
1085
- case "uploaded":
1086
- case "discovered":
1087
- case "extracted":
1088
- case "populated":
1089
- return "blue";
1090
- case "pending":
1091
- return "gray";
1092
- default:
1093
- return "gray";
1094
- }
1229
+ function getLeadGenStageColorVar(stageKey, tone = "accent") {
1230
+ return getLeadGenStageColor(stageKey)[tone];
1095
1231
  }
1096
- var ORPHAN_STAGE_ORDER = 9999;
1097
- function sortStageKeys(keys) {
1098
- return keys.slice().sort((a, b) => {
1099
- const oa = LEAD_GEN_STAGE_CATALOG[a]?.order ?? ORPHAN_STAGE_ORDER;
1100
- const ob = LEAD_GEN_STAGE_CATALOG[b]?.order ?? ORPHAN_STAGE_ORDER;
1101
- return oa - ob || a.localeCompare(b);
1232
+ var FALLBACK_STAGE_ORDER = 1e4;
1233
+ function unique(values) {
1234
+ return Array.from(new Set(values.filter(Boolean)));
1235
+ }
1236
+ function configuredStages(pipelineConfig) {
1237
+ return (pipelineConfig?.stages ?? []).filter((stage) => stage.enabled !== false).slice().sort((a, b) => {
1238
+ const aOrder = a.order ?? LEAD_GEN_STAGE_CATALOG[a.key]?.order ?? FALLBACK_STAGE_ORDER;
1239
+ const bOrder = b.order ?? LEAD_GEN_STAGE_CATALOG[b.key]?.order ?? FALLBACK_STAGE_ORDER;
1240
+ return aOrder - bOrder || a.key.localeCompare(b.key);
1102
1241
  });
1103
1242
  }
1104
- function PipelineSection({
1243
+ function workflowStageKeys(actions) {
1244
+ return unique((actions ?? []).flatMap((action) => [...action.stagesAffected]));
1245
+ }
1246
+ function sourceFor(stageKey, observedKeys, configKeys, workflowKeys) {
1247
+ if (observedKeys.has(stageKey)) return "activity";
1248
+ if (configKeys.has(stageKey)) return "config";
1249
+ if (workflowKeys.has(stageKey)) return "workflow";
1250
+ return "activity";
1251
+ }
1252
+ function sortStageNodes(nodes) {
1253
+ return nodes.slice().sort((a, b) => a.order - b.order || a.key.localeCompare(b.key));
1254
+ }
1255
+ function buildLaneStages({
1256
+ entity,
1257
+ progress,
1258
+ pipelineConfig,
1259
+ actions
1260
+ }) {
1261
+ const stageMap = entity === "company" ? progress.byCompanyStage : progress.byContactStage;
1262
+ const observedKeys = new Set(Object.keys(stageMap));
1263
+ const configStages = configuredStages(pipelineConfig);
1264
+ const configStageByKey = new Map(configStages.map((stage) => [stage.key, stage]));
1265
+ const configKeys = new Set(configStages.map((stage) => stage.key));
1266
+ const workflowKeys = new Set(workflowStageKeys(actions));
1267
+ const stageKeys = unique([...observedKeys, ...configKeys, ...workflowKeys]);
1268
+ return sortStageNodes(
1269
+ stageKeys.flatMap((key) => {
1270
+ const catalogEntry = LEAD_GEN_STAGE_CATALOG[key];
1271
+ const inferredEntity = catalogEntry?.entity ?? (observedKeys.has(key) ? entity : null);
1272
+ if (inferredEntity !== entity) return [];
1273
+ return [
1274
+ {
1275
+ key,
1276
+ label: configStageByKey.get(key)?.label ?? catalogEntry?.label ?? key,
1277
+ order: configStageByKey.get(key)?.order ?? catalogEntry?.order ?? FALLBACK_STAGE_ORDER,
1278
+ entity,
1279
+ source: sourceFor(key, observedKeys, configKeys, workflowKeys)
1280
+ }
1281
+ ];
1282
+ })
1283
+ );
1284
+ }
1285
+ function getStageProgress(progress, stageKey, entity) {
1286
+ const stageProgress = entity === "company" ? progress.byCompanyStage[stageKey] : progress.byContactStage[stageKey];
1287
+ const total = stageProgress?.total ?? (entity === "company" ? progress.totalCompanies : progress.totalMembers);
1288
+ return {
1289
+ total,
1290
+ attempted: stageProgress?.attempted ?? 0,
1291
+ success: stageProgress?.success ?? 0,
1292
+ noResult: stageProgress?.noResult ?? 0,
1293
+ skipped: stageProgress?.skipped ?? 0,
1294
+ error: stageProgress?.error ?? 0,
1295
+ other: stageProgress?.other ?? 0,
1296
+ notAttempted: stageProgress?.notAttempted ?? total
1297
+ };
1298
+ }
1299
+ function percent(value, total) {
1300
+ if (total <= 0) return 0;
1301
+ return Math.max(0, Math.min(100, value / total * 100));
1302
+ }
1303
+ function SourceBadge({ source }) {
1304
+ const label = source === "activity" ? "recorded" : source;
1305
+ const color = source === "activity" ? "green" : source === "config" ? "blue" : "gray";
1306
+ return /* @__PURE__ */ jsx(Badge, { size: "xs", variant: "light", color, children: label });
1307
+ }
1308
+ function ProgressRail({ counts, stageKey }) {
1309
+ const accent = getLeadGenStageColorVar(stageKey);
1310
+ const segments = [
1311
+ { key: "success", value: counts.success, color: accent },
1312
+ { key: "noResult", value: counts.noResult, color: "var(--color-text-subtle)" },
1313
+ { key: "skipped", value: counts.skipped, color: "var(--color-warning)" },
1314
+ { key: "error", value: counts.error, color: "var(--color-error)" },
1315
+ { key: "other", value: counts.other, color: "var(--color-primary)" }
1316
+ ].filter((segment) => segment.value > 0);
1317
+ return /* @__PURE__ */ jsx(
1318
+ Box,
1319
+ {
1320
+ h: 6,
1321
+ bg: "var(--color-surface-hover)",
1322
+ style: {
1323
+ borderRadius: 999,
1324
+ display: "flex",
1325
+ overflow: "hidden",
1326
+ opacity: counts.total > 0 ? 1 : 0.55
1327
+ },
1328
+ children: segments.length > 0 ? segments.map((segment) => /* @__PURE__ */ jsx(Box, { h: "100%", w: `${percent(segment.value, counts.total)}%`, bg: segment.color }, segment.key)) : /* @__PURE__ */ jsx(Box, { h: "100%", w: counts.total > 0 ? `${percent(counts.attempted, counts.total)}%` : "0%", bg: accent })
1329
+ }
1330
+ );
1331
+ }
1332
+ function TimelineLane({
1105
1333
  title,
1106
- byStage,
1334
+ entity,
1335
+ total,
1336
+ stages,
1337
+ progress,
1107
1338
  emptyText
1108
1339
  }) {
1109
- const orderedKeys = sortStageKeys(Object.keys(byStage));
1110
- return /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
1111
- /* @__PURE__ */ jsx(Text, { size: "sm", fw: 600, children: title }),
1112
- orderedKeys.length === 0 ? /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: emptyText }) : /* @__PURE__ */ jsx(Stack, { gap: "xs", children: orderedKeys.map((key) => {
1113
- const entry = byStage[key];
1114
- const total = entry?.total ?? 0;
1115
- const attempted = entry?.attempted ?? 0;
1116
- const percent = total > 0 ? Math.round(attempted / total * 100) : 0;
1117
- const label = LEAD_GEN_STAGE_CATALOG[key]?.label ?? key;
1118
- const segments = [
1119
- { key: "success", value: entry?.success ?? 0, color: "var(--mantine-color-green-6)" },
1120
- { key: "noResult", value: entry?.noResult ?? 0, color: "var(--mantine-color-gray-6)" },
1121
- { key: "skipped", value: entry?.skipped ?? 0, color: "var(--mantine-color-yellow-6)" },
1122
- { key: "error", value: entry?.error ?? 0, color: "var(--mantine-color-red-6)" },
1123
- { key: "other", value: entry?.other ?? 0, color: "var(--mantine-color-blue-6)" }
1124
- ].filter((segment) => segment.value > 0);
1125
- const statusSummary = [
1126
- `${entry?.success ?? 0} success`,
1127
- `${entry?.noResult ?? 0} no result`,
1128
- `${entry?.skipped ?? 0} skipped`,
1129
- `${entry?.error ?? 0} errors`,
1130
- (entry?.other ?? 0) > 0 ? `${entry?.other ?? 0} other` : null,
1131
- (entry?.notAttempted ?? 0) > 0 ? `${entry?.notAttempted ?? 0} not attempted` : null
1132
- ].filter(Boolean);
1133
- return /* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
1134
- /* @__PURE__ */ jsxs(Group, { justify: "space-between", gap: "xs", children: [
1135
- /* @__PURE__ */ jsx(Text, { size: "sm", children: label }),
1340
+ const Icon = entity === "company" ? IconBuilding : IconUsers;
1341
+ return /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
1342
+ /* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "center", gap: "xs", children: [
1343
+ /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
1344
+ /* @__PURE__ */ jsx(
1345
+ Box,
1346
+ {
1347
+ w: 28,
1348
+ h: 28,
1349
+ style: {
1350
+ alignItems: "center",
1351
+ background: "color-mix(in srgb, var(--color-primary) 14%, transparent)",
1352
+ border: "1px solid color-mix(in srgb, var(--color-border) 75%, var(--color-primary))",
1353
+ borderRadius: 999,
1354
+ color: "var(--color-primary)",
1355
+ display: "flex",
1356
+ justifyContent: "center"
1357
+ },
1358
+ children: /* @__PURE__ */ jsx(Icon, { size: 15 })
1359
+ }
1360
+ ),
1361
+ /* @__PURE__ */ jsxs(Stack, { gap: 0, children: [
1362
+ /* @__PURE__ */ jsx(Text, { size: "sm", fw: 700, children: title }),
1136
1363
  /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
1137
- attempted,
1138
- " / ",
1139
1364
  total,
1140
- " attempted (",
1141
- percent,
1142
- "%)"
1365
+ " ",
1366
+ entity === "company" ? "companies" : "contacts"
1143
1367
  ] })
1144
- ] }),
1145
- /* @__PURE__ */ jsx(
1368
+ ] })
1369
+ ] }),
1370
+ /* @__PURE__ */ jsxs(Badge, { size: "sm", variant: "outline", color: entity === "company" ? "blue" : "teal", children: [
1371
+ stages.length,
1372
+ " stages"
1373
+ ] })
1374
+ ] }),
1375
+ stages.length === 0 ? /* @__PURE__ */ jsx(
1376
+ Box,
1377
+ {
1378
+ p: "sm",
1379
+ style: {
1380
+ border: "1px dashed var(--color-border)",
1381
+ borderRadius: "var(--mantine-radius-md)"
1382
+ },
1383
+ children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: emptyText })
1384
+ }
1385
+ ) : /* @__PURE__ */ jsx(ScrollArea, { type: "hover", offsetScrollbars: true, children: /* @__PURE__ */ jsx(
1386
+ Box,
1387
+ {
1388
+ style: {
1389
+ minWidth: Math.max(560, stages.length * 178),
1390
+ padding: "8px 2px 2px"
1391
+ },
1392
+ children: /* @__PURE__ */ jsx(
1146
1393
  Box,
1147
1394
  {
1148
- h: 8,
1149
- bg: "var(--mantine-color-dark-4)",
1150
- style: { borderRadius: 999, display: "flex", overflow: "hidden" },
1151
- children: segments.map((segment) => /* @__PURE__ */ jsx(
1152
- Box,
1153
- {
1154
- h: "100%",
1155
- w: `${total > 0 ? segment.value / total * 100 : 0}%`,
1156
- bg: segment.color
1157
- },
1158
- segment.key
1159
- ))
1395
+ style: {
1396
+ alignItems: "start",
1397
+ display: "grid",
1398
+ gap: 0,
1399
+ gridTemplateColumns: `repeat(${stages.length}, minmax(150px, 1fr))`,
1400
+ position: "relative"
1401
+ },
1402
+ children: stages.map((stage, index) => {
1403
+ const counts = getStageProgress(progress, stage.key, entity);
1404
+ const accent = getLeadGenStageColorVar(stage.key);
1405
+ const completion = Math.round(percent(counts.success, counts.total));
1406
+ const attempted = Math.round(percent(counts.attempted, counts.total));
1407
+ return /* @__PURE__ */ jsxs(Stack, { gap: 8, px: "xs", style: { position: "relative", zIndex: 1 }, children: [
1408
+ /* @__PURE__ */ jsxs(Group, { gap: 8, wrap: "nowrap", children: [
1409
+ /* @__PURE__ */ jsx(
1410
+ Box,
1411
+ {
1412
+ w: 44,
1413
+ h: 44,
1414
+ style: {
1415
+ alignItems: "center",
1416
+ background: getLeadGenStageColorVar(stage.key, "background"),
1417
+ border: `1px solid ${getLeadGenStageColorVar(stage.key, "border")}`,
1418
+ borderRadius: 999,
1419
+ boxShadow: `0 0 0 4px ${getLeadGenStageColorVar(stage.key, "background")}`,
1420
+ color: accent,
1421
+ display: "flex",
1422
+ flexShrink: 0,
1423
+ fontSize: 13,
1424
+ fontWeight: 800,
1425
+ justifyContent: "center"
1426
+ },
1427
+ children: index + 1
1428
+ }
1429
+ ),
1430
+ /* @__PURE__ */ jsxs(Stack, { gap: 1, style: { minWidth: 0 }, children: [
1431
+ /* @__PURE__ */ jsxs(Group, { gap: 6, wrap: "nowrap", children: [
1432
+ /* @__PURE__ */ jsx(Text, { size: "sm", fw: 700, lineClamp: 1, children: stage.label }),
1433
+ /* @__PURE__ */ jsx(SourceBadge, { source: stage.source })
1434
+ ] }),
1435
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", lineClamp: 1, children: counts.total > 0 ? `${counts.attempted} / ${counts.total} attempted` : `Ready for ${entity === "company" ? "company" : "contact"} rows` })
1436
+ ] })
1437
+ ] }),
1438
+ /* @__PURE__ */ jsx(ProgressRail, { counts, stageKey: stage.key }),
1439
+ /* @__PURE__ */ jsxs(Group, { gap: 10, wrap: "nowrap", children: [
1440
+ /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
1441
+ counts.success,
1442
+ " success"
1443
+ ] }),
1444
+ /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
1445
+ attempted,
1446
+ "% attempted"
1447
+ ] }),
1448
+ counts.error > 0 ? /* @__PURE__ */ jsxs(Text, { size: "xs", c: "red", children: [
1449
+ counts.error,
1450
+ " errors"
1451
+ ] }) : null
1452
+ ] }),
1453
+ /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
1454
+ completion,
1455
+ "% complete"
1456
+ ] })
1457
+ ] }, stage.key);
1458
+ })
1160
1459
  }
1161
- ),
1162
- /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: statusSummary.join(" \xB7 ") })
1163
- ] }, key);
1164
- }) })
1460
+ )
1461
+ }
1462
+ ) })
1165
1463
  ] });
1166
1464
  }
1167
- function PipelineStagesCard({ list, progress }) {
1168
- const hasAnyActivity = Object.keys(progress.byCompanyStage).length > 0 || Object.keys(progress.byContactStage).length > 0;
1169
- return /* @__PURE__ */ jsx(Card, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
1170
- /* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "center", children: [
1171
- /* @__PURE__ */ jsx(Title, { order: 5, children: "Pipeline Stages" }),
1172
- /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
1173
- /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "filled", color: getStatusColor2(list.status), children: list.status }),
1174
- list.launchedAt && /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
1175
- "Launched ",
1176
- formatDateTime2(list.launchedAt)
1177
- ] }),
1178
- list.completedAt && /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
1179
- "Completed ",
1180
- formatDateTime2(list.completedAt)
1181
- ] })
1465
+ function PipelineFunnel({ progress, pipelineConfig, actions }) {
1466
+ const companyStages = buildLaneStages({ entity: "company", progress, pipelineConfig, actions });
1467
+ const contactStages = buildLaneStages({ entity: "contact", progress, pipelineConfig, actions });
1468
+ return /* @__PURE__ */ jsx(Paper, { withBorder: true, p: "md", children: /* @__PURE__ */ jsxs(Stack, { gap: "lg", children: [
1469
+ /* @__PURE__ */ jsx(CardHeader, { icon: /* @__PURE__ */ jsx(IconListDetails, { size: 16 }), title: "Pipeline" }),
1470
+ /* @__PURE__ */ jsx(
1471
+ TimelineLane,
1472
+ {
1473
+ title: "Company pipeline",
1474
+ entity: "company",
1475
+ total: progress.totalCompanies,
1476
+ stages: companyStages,
1477
+ progress,
1478
+ emptyText: "No company stages are configured, runnable, or recorded yet."
1479
+ }
1480
+ ),
1481
+ /* @__PURE__ */ jsx(
1482
+ TimelineLane,
1483
+ {
1484
+ title: "Contact pipeline",
1485
+ entity: "contact",
1486
+ total: progress.totalMembers,
1487
+ stages: contactStages,
1488
+ progress,
1489
+ emptyText: "No contact stages are configured, runnable, or recorded yet."
1490
+ }
1491
+ )
1492
+ ] }) });
1493
+ }
1494
+ function selectedCount(selection) {
1495
+ return selection.selectedCompanyIds.length + selection.selectedContactIds.length;
1496
+ }
1497
+ function buildInput(input, selection) {
1498
+ const base = input && typeof input === "object" && !Array.isArray(input) ? input : {};
1499
+ return {
1500
+ ...base,
1501
+ selectedCompanyIds: selection.selectedCompanyIds,
1502
+ selectedContactIds: selection.selectedContactIds
1503
+ };
1504
+ }
1505
+ function RunWorkflowModal({
1506
+ opened,
1507
+ onClose,
1508
+ list,
1509
+ actions,
1510
+ selection,
1511
+ initialResourceId,
1512
+ title = "Run Workflow",
1513
+ description = "Start a workflow immediately for this list.",
1514
+ lockResourceSelection = false,
1515
+ onResourceChange,
1516
+ onSubmitted
1517
+ }) {
1518
+ const [selectedResourceId, setSelectedResourceId] = useState(initialResourceId ?? actions[0]?.resourceId ?? null);
1519
+ const selectedAction = useMemo(
1520
+ () => actions.find((action) => action.resourceId === selectedResourceId) ?? actions[0],
1521
+ [actions, selectedResourceId]
1522
+ );
1523
+ const execution = useWorkflowExecution({
1524
+ workflowId: selectedAction?.resourceId ?? "",
1525
+ listId: list.id
1526
+ });
1527
+ useEffect(() => {
1528
+ if (!opened) return;
1529
+ const nextResourceId = initialResourceId ?? actions[0]?.resourceId ?? null;
1530
+ setSelectedResourceId(nextResourceId);
1531
+ onResourceChange?.(nextResourceId);
1532
+ }, [actions, initialResourceId, onResourceChange, opened]);
1533
+ const actionOptions = actions.map((action) => ({
1534
+ value: action.resourceId,
1535
+ label: action.label
1536
+ }));
1537
+ const handleResourceChange = (value) => {
1538
+ setSelectedResourceId(value);
1539
+ onResourceChange?.(value);
1540
+ execution.reset();
1541
+ };
1542
+ const submitInput = async (input) => {
1543
+ if (!selectedAction) return;
1544
+ try {
1545
+ const result = await execution.execute({
1546
+ input: buildInput(input, selection)
1547
+ });
1548
+ onSubmitted?.(selectedAction.resourceId, result.executionId);
1549
+ onClose();
1550
+ } catch (error) {
1551
+ showApiErrorNotification(error);
1552
+ }
1553
+ };
1554
+ const Form = selectedAction?.inputForm;
1555
+ 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 })
1182
1562
  ] })
1183
1563
  ] }),
1184
- !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: [
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: [
1185
1565
  /* @__PURE__ */ jsx(
1186
- PipelineSection,
1566
+ Select,
1187
1567
  {
1188
- title: `Company Pipeline (${progress.totalCompanies})`,
1189
- byStage: progress.byCompanyStage,
1190
- emptyText: "No company-stage flags recorded yet."
1568
+ label: "Workflow",
1569
+ data: actionOptions,
1570
+ value: selectedAction?.resourceId ?? null,
1571
+ onChange: handleResourceChange,
1572
+ disabled: execution.isPending || lockResourceSelection,
1573
+ searchable: true
1191
1574
  }
1192
1575
  ),
1193
- /* @__PURE__ */ jsx(
1194
- PipelineSection,
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))
1580
+ ] }),
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,
1195
1593
  {
1196
- title: `Contact Pipeline (${progress.totalMembers})`,
1197
- byStage: progress.byContactStage,
1198
- emptyText: "No contact-stage flags recorded yet."
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
+ ]
1199
1601
  }
1200
- )
1602
+ ) }) : null
1201
1603
  ] })
1202
1604
  ] }) });
1203
1605
  }
1204
- function ListConfigCard({ list }) {
1205
- const icp = list.icp ?? {};
1206
- const scraping = list.scrapingConfig ?? {};
1207
- const hasIcp = Object.values(icp).some((v) => v !== void 0 && v !== null && v !== "");
1208
- const hasScraping = Object.values(scraping).some((v) => v !== void 0 && v !== null && v !== "");
1209
- if (!hasIcp && !hasScraping) {
1606
+ function formatDateTime2(value) {
1607
+ if (!value) return "Not yet";
1608
+ return new Date(value).toLocaleString("en-US", {
1609
+ month: "short",
1610
+ day: "numeric",
1611
+ year: "numeric",
1612
+ hour: "numeric",
1613
+ minute: "2-digit"
1614
+ });
1615
+ }
1616
+ function formatDuration(durationMs) {
1617
+ if (durationMs == null) return "n/a";
1618
+ if (durationMs < 1e3) return `${durationMs} ms`;
1619
+ const seconds = durationMs / 1e3;
1620
+ if (seconds < 60) return `${seconds.toFixed(1)} s`;
1621
+ return `${Math.floor(seconds / 60)}m ${Math.round(seconds % 60)}s`;
1622
+ }
1623
+ function getStatusColor2(status) {
1624
+ switch (status) {
1625
+ case "completed":
1626
+ case "success":
1627
+ case "succeeded":
1628
+ return "green";
1629
+ case "running":
1630
+ case "pending":
1631
+ case "queued":
1632
+ return "blue";
1633
+ case "failed":
1634
+ case "error":
1635
+ case "cancelled":
1636
+ return "red";
1637
+ default:
1638
+ return "gray";
1639
+ }
1640
+ }
1641
+ function hasObjectContent(value) {
1642
+ return typeof value === "object" && value !== null && Object.keys(value).length > 0;
1643
+ }
1644
+ function getRunDetails(run) {
1645
+ const payload = hasObjectContent(run.payload) ? run.payload : void 0;
1646
+ const input = run.input ?? run.inputs ?? payload?.input ?? payload?.inputs;
1647
+ const config = run.configSnapshot ?? run.config_snapshot ?? run.config ?? payload?.config_snapshot ?? payload?.configSnapshot ?? payload?.config;
1648
+ return { input, config };
1649
+ }
1650
+ function getSummaryKeys(value) {
1651
+ if (!hasObjectContent(value)) return [];
1652
+ return Object.keys(value).slice(0, 6);
1653
+ }
1654
+ function WorkflowRunsPanel({ listId, title = "Workflow Runs" }) {
1655
+ const navigate = useNavigate();
1656
+ const executionsQuery = useListExecutions(listId);
1657
+ const [expandedExecutionId, setExpandedExecutionId] = useState(null);
1658
+ const executions = useMemo(() => executionsQuery.data ?? [], [executionsQuery.data]);
1659
+ const openWorkflow = (resourceId) => {
1660
+ void navigate({ to: "/operations/resources/workflow/$workflowId", params: { workflowId: resourceId } });
1661
+ };
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);
1710
+ }
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);
1732
+ }
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))
1757
+ ] }),
1758
+ /* @__PURE__ */ jsx(JsonViewer, { data: details.config, maxHeight: 220, fontSize: "0.75rem" })
1759
+ ] }) : null
1760
+ ] }) }) }) })
1761
+ ] }, execution.executionId);
1762
+ }) })
1763
+ ] }) })
1764
+ ] });
1765
+ }
1766
+ var EMPTY_SELECTION = {
1767
+ selectedCompanyIds: [],
1768
+ selectedContactIds: []
1769
+ };
1770
+ function formatDateTime3(value) {
1771
+ if (!value) return "Not yet";
1772
+ return new Date(value).toLocaleString("en-US", {
1773
+ month: "short",
1774
+ day: "numeric",
1775
+ year: "numeric",
1776
+ hour: "numeric",
1777
+ minute: "2-digit"
1778
+ });
1779
+ }
1780
+ function getStatusColor3(status) {
1781
+ switch (status) {
1782
+ case "completed":
1783
+ case "success":
1784
+ case "launched":
1785
+ return "green";
1786
+ case "running":
1787
+ case "pending":
1788
+ case "enriching":
1789
+ return "blue";
1790
+ case "failed":
1791
+ case "error":
1792
+ case "archived":
1793
+ return "red";
1794
+ case "closing":
1795
+ return "yellow";
1796
+ default:
1797
+ return "gray";
1798
+ }
1799
+ }
1800
+ function ListBuilderPage({ listId }) {
1801
+ const navigate = useNavigate();
1802
+ const actions = useListActions();
1803
+ const [runModalOpen, setRunModalOpen] = useState(false);
1804
+ const [initialRunResourceId, setInitialRunResourceId] = useState(null);
1805
+ const listQuery = useList(listId);
1806
+ const progressQuery = useListProgress(listId);
1807
+ const executionsQuery = useListExecutions(listId);
1808
+ const isLoading = listQuery.isLoading || progressQuery.isLoading || executionsQuery.isLoading;
1809
+ const error = listQuery.error ?? progressQuery.error ?? executionsQuery.error;
1810
+ const executions = useMemo(() => executionsQuery.data ?? [], [executionsQuery.data]);
1811
+ const openRunModal = (resourceId) => {
1812
+ const nextResourceId = actions[0]?.resourceId ?? null;
1813
+ setInitialRunResourceId(nextResourceId);
1814
+ setRunModalOpen(true);
1815
+ };
1816
+ const closeRunModal = () => {
1817
+ setRunModalOpen(false);
1818
+ setInitialRunResourceId(null);
1819
+ };
1820
+ const copyListCommand = (id) => {
1821
+ void navigator.clipboard.writeText(`/acquisition --lead-gen list ${id}`);
1822
+ showSuccessNotification("Copied list command to clipboard");
1823
+ };
1824
+ const backButton = /* @__PURE__ */ jsx(
1825
+ Button,
1826
+ {
1827
+ variant: "light",
1828
+ size: "xs",
1829
+ leftSection: /* @__PURE__ */ jsx(IconArrowLeft, { size: 16 }),
1830
+ onClick: () => navigate({ to: "/lead-gen/lists" }),
1831
+ children: "Lists"
1832
+ }
1833
+ );
1834
+ if (isLoading) {
1835
+ return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsx(PageContainer, { children: /* @__PURE__ */ jsxs(Stack, { children: [
1836
+ /* @__PURE__ */ jsx(PageTitleCaption, { title: "List Builder", caption: "Lead-gen workspace", rightSection: backButton }),
1837
+ /* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsx(Center, { p: "xl", children: /* @__PURE__ */ jsx(Loader, {}) }) })
1838
+ ] }) }) });
1839
+ }
1840
+ if (error) {
1841
+ return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsx(PageContainer, { children: /* @__PURE__ */ jsxs(Stack, { children: [
1842
+ /* @__PURE__ */ jsx(PageTitleCaption, { title: "List Builder", caption: "Lead-gen workspace", rightSection: backButton }),
1843
+ /* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsx(CenteredErrorState, { error, title: "Failed to load list builder" }) })
1844
+ ] }) }) });
1845
+ }
1846
+ if (!listQuery.data || !progressQuery.data) {
1847
+ return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsx(PageContainer, { children: /* @__PURE__ */ jsxs(Stack, { children: [
1848
+ /* @__PURE__ */ jsx(PageTitleCaption, { title: "List Not Found", caption: "The requested lead-gen list is unavailable." }),
1849
+ /* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsx(Center, { h: 240, children: /* @__PURE__ */ jsx(Alert, { icon: /* @__PURE__ */ jsx(IconAlertCircle, { size: 16 }), color: "gray", variant: "light", children: "The requested list could not be found." }) }) })
1850
+ ] }) }) });
1851
+ }
1852
+ const list = listQuery.data;
1853
+ const progress = progressQuery.data;
1854
+ return /* @__PURE__ */ jsxs(SubshellContentContainer, { children: [
1855
+ /* @__PURE__ */ jsx(PageContainer, { children: /* @__PURE__ */ jsxs(Stack, { children: [
1856
+ /* @__PURE__ */ jsx(
1857
+ PageTitleCaption,
1858
+ {
1859
+ title: list.name,
1860
+ caption: list.description ?? "Lead-gen list builder",
1861
+ rightSection: /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
1862
+ /* @__PURE__ */ jsx(Button, { size: "xs", leftSection: /* @__PURE__ */ jsx(IconBolt, { size: 16 }), onClick: () => openRunModal(), children: "Run Workflow" }),
1863
+ backButton
1864
+ ] })
1865
+ }
1866
+ ),
1867
+ /* @__PURE__ */ jsxs(Group, { justify: "space-between", gap: "xs", children: [
1868
+ /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
1869
+ /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "filled", color: getStatusColor3(list.status), children: list.status }),
1870
+ /* @__PURE__ */ jsxs(Text, { size: "sm", c: "dimmed", children: [
1871
+ "Created ",
1872
+ formatDateTime3(list.createdAt)
1873
+ ] })
1874
+ ] }),
1875
+ /* @__PURE__ */ jsx(Group, { gap: "xs", children: /* @__PURE__ */ jsxs(Group, { gap: 4, wrap: "nowrap", onClick: () => copyListCommand(list.id), style: { cursor: "pointer" }, children: [
1876
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", ff: "monospace", children: list.id }),
1877
+ /* @__PURE__ */ jsx(ActionIcon, { variant: "subtle", size: "sm", "aria-label": "Copy list command", children: /* @__PURE__ */ jsx(IconCopy, { size: 14 }) })
1878
+ ] }) })
1879
+ ] }),
1880
+ /* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 1, sm: 2, lg: 4 }, children: [
1881
+ /* @__PURE__ */ jsx(StatCard, { label: "Companies", value: progress.totalCompanies, icon: IconBuilding }),
1882
+ /* @__PURE__ */ jsx(StatCard, { label: "Contacts", value: progress.totalMembers, icon: IconUsers }),
1883
+ /* @__PURE__ */ jsx(StatCard, { label: "Workflows", value: actions.length, icon: IconBolt }),
1884
+ /* @__PURE__ */ jsx(StatCard, { label: "Runs", value: executions.length, icon: IconPlayerPlay })
1885
+ ] }),
1886
+ /* @__PURE__ */ jsx(PipelineFunnel, { progress, pipelineConfig: list.pipelineConfig, actions }),
1887
+ /* @__PURE__ */ jsx(WorkflowRunsPanel, { listId })
1888
+ ] }) }),
1889
+ /* @__PURE__ */ jsx(
1890
+ RunWorkflowModal,
1891
+ {
1892
+ opened: runModalOpen,
1893
+ onClose: closeRunModal,
1894
+ list,
1895
+ actions,
1896
+ selection: EMPTY_SELECTION,
1897
+ initialResourceId: initialRunResourceId,
1898
+ onSubmitted: (_resourceId, executionId) => {
1899
+ showSuccessNotification(`Workflow run started: ${executionId}`);
1900
+ }
1901
+ }
1902
+ )
1903
+ ] });
1904
+ }
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
+ }
1933
+ });
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
+ }
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";
2189
+ }
2190
+ function getStatusColor4(status) {
2191
+ switch (status) {
2192
+ case "draft":
2193
+ return "gray";
2194
+ case "enriching":
2195
+ return "blue";
2196
+ case "launched":
2197
+ return "green";
2198
+ case "closing":
2199
+ return "yellow";
2200
+ case "archived":
2201
+ return "red";
2202
+ default:
2203
+ return "gray";
2204
+ }
2205
+ }
2206
+ function getMemberStateColor2(stateKey) {
2207
+ switch (stateKey) {
2208
+ case "personalized":
2209
+ case "verified":
2210
+ case "qualified":
2211
+ case "interested":
2212
+ return "green";
2213
+ case "uploaded":
2214
+ case "discovered":
2215
+ case "extracted":
2216
+ case "populated":
2217
+ return "blue";
2218
+ case "pending":
2219
+ return "gray";
2220
+ default:
2221
+ return "gray";
2222
+ }
2223
+ }
2224
+ var ORPHAN_STAGE_ORDER = 9999;
2225
+ var EMPTY_SELECTION2 = {
2226
+ selectedCompanyIds: [],
2227
+ selectedContactIds: []
2228
+ };
2229
+ var BUILD_TEMPLATE_SELECT_OPTIONS2 = PROSPECTING_BUILD_TEMPLATE_OPTIONS.map((template) => ({
2230
+ value: template.id,
2231
+ label: template.label
2232
+ }));
2233
+ var MVP_BUILD_STEPS = [
2234
+ {
2235
+ id: "source-companies",
2236
+ label: "Source companies",
2237
+ description: "Add or import the company cohort for this list.",
2238
+ primaryEntity: "company",
2239
+ outputs: ["company"],
2240
+ stageKey: "populated",
2241
+ dependencyMode: "per-record-eligibility",
2242
+ capabilityKey: "lead-gen.company.source",
2243
+ defaultBatchSize: 100,
2244
+ maxBatchSize: 250,
2245
+ emptyBlockedText: "No company source has been configured or populated yet."
2246
+ },
2247
+ {
2248
+ id: "analyze-websites",
2249
+ label: "Analyze websites",
2250
+ description: "Extract website intelligence for sourced companies.",
2251
+ primaryEntity: "company",
2252
+ outputs: ["company"],
2253
+ stageKey: "extracted",
2254
+ dependsOn: ["source-companies"],
2255
+ dependencyMode: "per-record-eligibility",
2256
+ capabilityKey: "lead-gen.company.website-extract",
2257
+ defaultBatchSize: 50,
2258
+ maxBatchSize: 100,
2259
+ emptyBlockedText: "Source companies before website analysis can run."
2260
+ },
2261
+ {
2262
+ id: "qualify-companies",
2263
+ label: "Qualify companies",
2264
+ description: "Apply the ICP rubric to companies with enough context.",
2265
+ primaryEntity: "company",
2266
+ outputs: ["company"],
2267
+ stageKey: "qualified",
2268
+ dependsOn: ["analyze-websites"],
2269
+ dependencyMode: "per-record-eligibility",
2270
+ capabilityKey: "lead-gen.company.qualify",
2271
+ defaultBatchSize: 100,
2272
+ maxBatchSize: 250,
2273
+ emptyBlockedText: "Analyze websites before qualification can run."
2274
+ },
2275
+ {
2276
+ id: "find-contacts",
2277
+ label: "Find contacts",
2278
+ description: "Discover contacts for qualified companies.",
2279
+ primaryEntity: "contact",
2280
+ outputs: ["contact"],
2281
+ stageKey: "discovered",
2282
+ dependsOn: ["qualify-companies"],
2283
+ dependencyMode: "per-record-eligibility",
2284
+ capabilityKey: "lead-gen.contact.discover",
2285
+ defaultBatchSize: 50,
2286
+ maxBatchSize: 100,
2287
+ emptyBlockedText: "Qualify companies before contact discovery can run."
2288
+ },
2289
+ {
2290
+ id: "verify-emails",
2291
+ label: "Verify emails",
2292
+ description: "Verify deliverability for discovered contact emails.",
2293
+ primaryEntity: "contact",
2294
+ outputs: ["contact"],
2295
+ stageKey: "verified",
2296
+ dependsOn: ["find-contacts"],
2297
+ dependencyMode: "per-record-eligibility",
2298
+ capabilityKey: "lead-gen.contact.verify-email",
2299
+ defaultBatchSize: 100,
2300
+ maxBatchSize: 500,
2301
+ emptyBlockedText: "Find contacts before email verification can run."
2302
+ },
2303
+ {
2304
+ id: "personalize",
2305
+ label: "Personalize",
2306
+ description: "Generate outreach personalization for verified contacts.",
2307
+ primaryEntity: "contact",
2308
+ outputs: ["contact"],
2309
+ stageKey: "personalized",
2310
+ dependsOn: ["verify-emails"],
2311
+ dependencyMode: "per-record-eligibility",
2312
+ capabilityKey: "lead-gen.contact.personalize",
2313
+ defaultBatchSize: 25,
2314
+ maxBatchSize: 100,
2315
+ emptyBlockedText: "Verify emails before personalization can run."
2316
+ },
2317
+ {
2318
+ id: "review",
2319
+ label: "Review",
2320
+ description: "Review personalized records before outreach handoff.",
2321
+ primaryEntity: "contact",
2322
+ outputs: ["export"],
2323
+ stageKey: "uploaded",
2324
+ dependsOn: ["personalize"],
2325
+ dependencyMode: "per-record-eligibility",
2326
+ capabilityKey: "lead-gen.review.outreach-ready",
2327
+ defaultBatchSize: 25,
2328
+ maxBatchSize: 100,
2329
+ emptyBlockedText: "Personalize contacts before records are ready for review."
2330
+ }
2331
+ ];
2332
+ function sortStageKeys(keys) {
2333
+ return keys.slice().sort((a, b) => {
2334
+ const oa = LEAD_GEN_STAGE_CATALOG[a]?.order ?? ORPHAN_STAGE_ORDER;
2335
+ const ob = LEAD_GEN_STAGE_CATALOG[b]?.order ?? ORPHAN_STAGE_ORDER;
2336
+ return oa - ob || a.localeCompare(b);
2337
+ });
2338
+ }
2339
+ function getStageProgress2(progress, step) {
2340
+ return step.primaryEntity === "company" ? progress.byCompanyStage[step.stageKey] : progress.byContactStage[step.stageKey];
2341
+ }
2342
+ function getEntityTotal(progress, entity) {
2343
+ return entity === "company" ? progress.totalCompanies : progress.totalMembers;
2344
+ }
2345
+ function findActionForStep(actions, step) {
2346
+ return actions.find((action) => action.capabilityKey === step.capabilityKey) ?? actions.find((action) => action.stagesAffected?.includes(step.stageKey));
2347
+ }
2348
+ function normalizeBuildPlanStep(step) {
2349
+ const fallback = MVP_BUILD_STEPS.find((item) => item.id === step.id || item.stageKey === step.stageKey);
2350
+ return {
2351
+ id: step.id,
2352
+ label: step.label,
2353
+ description: step.description ?? fallback?.description ?? `Run ${step.label.toLowerCase()} for this list.`,
2354
+ primaryEntity: step.primaryEntity,
2355
+ outputs: step.outputs,
2356
+ stageKey: step.stageKey,
2357
+ dependsOn: step.dependsOn,
2358
+ dependencyMode: step.dependencyMode,
2359
+ capabilityKey: step.capabilityKey,
2360
+ defaultBatchSize: step.defaultBatchSize,
2361
+ maxBatchSize: step.maxBatchSize,
2362
+ emptyBlockedText: fallback?.emptyBlockedText ?? "Complete prerequisite build steps before this action can run."
2363
+ };
2364
+ }
2365
+ function isCurrentBuildPlanStep(step) {
2366
+ if (!step || typeof step !== "object") return false;
2367
+ const candidate = step;
2368
+ return (candidate.primaryEntity === "company" || candidate.primaryEntity === "contact") && Array.isArray(candidate.outputs) && candidate.outputs.length > 0 && candidate.dependencyMode === "per-record-eligibility";
2369
+ }
2370
+ function resolveBuildPlanSteps(list) {
2371
+ const snapshot = list.metadata.buildPlanSnapshot;
2372
+ const snapshotSteps = snapshot?.steps;
2373
+ if (snapshotSteps?.length && snapshotSteps.every(isCurrentBuildPlanStep)) {
2374
+ return snapshotSteps.map(normalizeBuildPlanStep);
2375
+ }
2376
+ if (snapshot?.templateId) {
2377
+ const templateSnapshot = createBuildPlanSnapshotFromTemplateId(snapshot.templateId);
2378
+ if (templateSnapshot?.steps.length) return templateSnapshot.steps.map(normalizeBuildPlanStep);
2379
+ }
2380
+ return MVP_BUILD_STEPS;
2381
+ }
2382
+ function getPrerequisiteSteps(step, byStepId) {
2383
+ return (step.dependsOn ?? []).flatMap((dependencyId) => {
2384
+ const dependency = byStepId.get(dependencyId);
2385
+ return dependency ? [dependency] : [];
2386
+ });
2387
+ }
2388
+ function getStepActionKind(ready, failed, action) {
2389
+ if (!action) return "none";
2390
+ if (failed > 0) return "retry_failed";
2391
+ if (ready > 0) return "run_next_batch";
2392
+ return "none";
2393
+ }
2394
+ function getStepActionLabel(kind) {
2395
+ switch (kind) {
2396
+ case "retry_failed":
2397
+ return "Retry failed";
2398
+ case "run_next_batch":
2399
+ return "Run next batch";
2400
+ case "advanced":
2401
+ return "Advanced";
2402
+ case "none":
2403
+ default:
2404
+ return "No action";
2405
+ }
2406
+ }
2407
+ function getStepRecommendedAction(step, action, kind) {
2408
+ if (!action || kind === "none") return void 0;
2409
+ return {
2410
+ kind,
2411
+ label: getStepActionLabel(kind),
2412
+ capabilityKey: action.capabilityKey ?? step.capabilityKey,
2413
+ defaultSize: step.defaultBatchSize,
2414
+ maxSize: step.maxBatchSize
2415
+ };
2416
+ }
2417
+ function deriveBuildStepStates(list, progress, actions) {
2418
+ const byStepId = /* @__PURE__ */ new Map();
2419
+ for (const step of resolveBuildPlanSteps(list)) {
2420
+ const stageProgress = getStageProgress2(progress, step);
2421
+ const entityTotal = getEntityTotal(progress, step.primaryEntity);
2422
+ const prerequisites = getPrerequisiteSteps(step, byStepId);
2423
+ 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;
2425
+ const total = Math.max(stageProgress?.total ?? 0, entityTotal, eligibleTotal);
2426
+ const complete = stageProgress ? stageProgress.success + stageProgress.noResult + stageProgress.skipped + stageProgress.other : 0;
2427
+ const failed = stageProgress?.error ?? 0;
2428
+ const ready = Math.max(0, eligibleTotal - complete - failed);
2429
+ const blocked = hasDependencies ? Math.max(entityTotal - eligibleTotal, 0) : 0;
2430
+ const status = failed > 0 ? "failed" : ready > 0 ? "ready" : blocked > 0 || total === 0 ? "blocked" : "complete";
2431
+ const action = findActionForStep(actions, step);
2432
+ const nextActionKind = getStepActionKind(ready, failed, action);
2433
+ const state = {
2434
+ ...step,
2435
+ status,
2436
+ ready,
2437
+ complete,
2438
+ failed,
2439
+ blocked,
2440
+ nextActionKind,
2441
+ recommendedAction: getStepRecommendedAction(step, action, nextActionKind),
2442
+ action
2443
+ };
2444
+ byStepId.set(step.id, state);
2445
+ }
2446
+ return Array.from(byStepId.values());
2447
+ }
2448
+ function getRecommendedBuildStep(steps) {
2449
+ return steps.find((step) => step.nextActionKind === "retry_failed") ?? steps.find((step) => step.nextActionKind === "run_next_batch") ?? null;
2450
+ }
2451
+ function resolveBuildState(list, progress, actions) {
2452
+ const steps = deriveBuildStepStates(list, progress, actions);
2453
+ const recommendedStep = getRecommendedBuildStep(steps);
2454
+ return {
2455
+ steps,
2456
+ recommendedStep,
2457
+ recommendedAction: recommendedStep?.recommendedAction ?? null
2458
+ };
2459
+ }
2460
+ function PipelineSection({
2461
+ title,
2462
+ byStage,
2463
+ emptyText
2464
+ }) {
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
+ ] }),
2501
+ /* @__PURE__ */ jsx(
2502
+ Box,
2503
+ {
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
+ ))
2516
+ }
2517
+ ),
2518
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: statusSummary.join(" \xB7 ") })
2519
+ ] }, key);
2520
+ }) })
2521
+ ] });
2522
+ }
2523
+ function PipelineStagesCard({ list, progress }) {
2524
+ const hasAnyActivity = Object.keys(progress.byCompanyStage).length > 0 || Object.keys(progress.byContactStage).length > 0;
2525
+ return /* @__PURE__ */ jsx(Card, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
2526
+ /* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "center", children: [
2527
+ /* @__PURE__ */ jsx(Title, { order: 5, children: "Pipeline Stages" }),
2528
+ /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
2529
+ /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "filled", color: getStatusColor4(list.status), children: list.status }),
2530
+ list.launchedAt && /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
2531
+ "Launched ",
2532
+ formatDateTime4(list.launchedAt)
2533
+ ] }),
2534
+ list.completedAt && /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
2535
+ "Completed ",
2536
+ formatDateTime4(list.completedAt)
2537
+ ] })
2538
+ ] })
2539
+ ] }),
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
+ ),
2549
+ /* @__PURE__ */ jsx(
2550
+ PipelineSection,
2551
+ {
2552
+ title: `Contact Pipeline (${progress.totalMembers})`,
2553
+ byStage: progress.byContactStage,
2554
+ emptyText: "No contact-stage flags recorded yet."
2555
+ }
2556
+ )
2557
+ ] })
2558
+ ] }) });
2559
+ }
2560
+ function ListConfigCard({ list }) {
2561
+ const icp = list.icp ?? {};
2562
+ const scraping = list.scrapingConfig ?? {};
2563
+ const hasIcp = Object.values(icp).some((v) => v !== void 0 && v !== null && v !== "");
2564
+ const hasScraping = Object.values(scraping).some((v) => v !== void 0 && v !== null && v !== "");
2565
+ if (!hasIcp && !hasScraping) {
1210
2566
  return /* @__PURE__ */ jsx(Card, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
1211
2567
  /* @__PURE__ */ jsx(Title, { order: 5, children: "List Config" }),
1212
2568
  /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "No ICP rubric or scraping criteria configured for this list." })
@@ -1271,7 +2627,8 @@ function ListConfigCard({ list }) {
1271
2627
  }
1272
2628
  function OverviewTab({ list, progress }) {
1273
2629
  const hasMetadata = list.metadata && Object.keys(list.metadata).length > 0;
1274
- return /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
2630
+ return /* @__PURE__ */ jsxs(Stack, { gap: "md", p: "md", children: [
2631
+ /* @__PURE__ */ jsx(CardHeader, { icon: /* @__PURE__ */ jsx(IconBuilding, { size: 16 }), title: "Overview" }),
1275
2632
  /* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 1, sm: 2 }, children: [
1276
2633
  /* @__PURE__ */ jsx(StatCard, { label: "Companies", value: progress.totalCompanies, icon: IconBuilding }),
1277
2634
  /* @__PURE__ */ jsx(StatCard, { label: "Members", value: progress.totalMembers, icon: IconUsers })
@@ -1284,6 +2641,296 @@ function OverviewTab({ list, progress }) {
1284
2641
  ] }) })
1285
2642
  ] });
1286
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
+ }
2656
+ }
2657
+ function getBuildToneStyle(tone) {
2658
+ switch (tone) {
2659
+ case "complete":
2660
+ return {
2661
+ backgroundColor: "color-mix(in srgb, var(--color-success) 16%, transparent)",
2662
+ borderColor: "color-mix(in srgb, var(--color-success) 34%, var(--color-border))",
2663
+ color: "var(--color-success)"
2664
+ };
2665
+ case "failed":
2666
+ return {
2667
+ backgroundColor: "color-mix(in srgb, var(--color-error) 14%, transparent)",
2668
+ borderColor: "color-mix(in srgb, var(--color-error) 38%, var(--color-border))",
2669
+ color: "var(--color-error)"
2670
+ };
2671
+ case "ready":
2672
+ return {
2673
+ backgroundColor: "color-mix(in srgb, var(--color-primary) 14%, transparent)",
2674
+ borderColor: "color-mix(in srgb, var(--color-primary) 34%, var(--color-border))",
2675
+ color: "var(--color-primary)"
2676
+ };
2677
+ case "blocked":
2678
+ case "neutral":
2679
+ default:
2680
+ return {
2681
+ backgroundColor: "color-mix(in srgb, var(--color-surface-hover) 70%, transparent)",
2682
+ borderColor: "var(--color-border)",
2683
+ color: "var(--color-text-dimmed)"
2684
+ };
2685
+ }
2686
+ }
2687
+ function StatusPill({ label, tone }) {
2688
+ return /* @__PURE__ */ jsx(
2689
+ Box,
2690
+ {
2691
+ component: "span",
2692
+ px: 8,
2693
+ py: 3,
2694
+ style: {
2695
+ ...getBuildToneStyle(tone),
2696
+ borderRadius: 999,
2697
+ borderStyle: "solid",
2698
+ borderWidth: 1,
2699
+ display: "inline-flex",
2700
+ fontSize: 11,
2701
+ fontWeight: 700,
2702
+ lineHeight: 1,
2703
+ whiteSpace: "nowrap"
2704
+ },
2705
+ children: label
2706
+ }
2707
+ );
2708
+ }
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
+ );
2726
+ }
2727
+ function BuildStepProgressBar({ step }) {
2728
+ const segments = [
2729
+ { key: "complete", value: step.complete, color: "var(--color-success)" },
2730
+ { key: "ready", value: step.ready, color: "var(--color-primary)" },
2731
+ { key: "failed", value: step.failed, color: "var(--color-error)" },
2732
+ { key: "blocked", value: step.blocked, color: "var(--color-border)" }
2733
+ ].filter((segment) => segment.value > 0);
2734
+ const total = segments.reduce((sum, segment) => sum + segment.value, 0);
2735
+ return /* @__PURE__ */ jsx(
2736
+ Box,
2737
+ {
2738
+ h: 8,
2739
+ "aria-label": `${step.label} progress: ${step.complete} complete, ${step.ready} ready, ${step.failed} failed, ${step.blocked} blocked`,
2740
+ style: {
2741
+ backgroundColor: "color-mix(in srgb, var(--color-border) 65%, transparent)",
2742
+ borderRadius: 999,
2743
+ display: "flex",
2744
+ overflow: "hidden"
2745
+ },
2746
+ children: segments.map((segment) => /* @__PURE__ */ jsx(Box, { h: "100%", w: `${total > 0 ? segment.value / total * 100 : 0}%`, bg: segment.color }, segment.key))
2747
+ }
2748
+ );
2749
+ }
2750
+ function pluralize(value, singular, plural = `${singular}s`) {
2751
+ return `${value} ${value === 1 ? singular : plural}`;
2752
+ }
2753
+ function getStepEntityLabel(step, value) {
2754
+ if (step.primaryEntity === "company") return pluralize(value, "company", "companies");
2755
+ if (step.primaryEntity === "contact") return pluralize(value, "contact");
2756
+ return pluralize(value, "record");
2757
+ }
2758
+ function getStepActionSummary(step) {
2759
+ if (step.failed > 0) {
2760
+ return `${getStepEntityLabel(step, step.failed)} ${step.failed === 1 ? "needs" : "need"} retry before the next step can continue.`;
2761
+ }
2762
+ if (step.ready > 0) {
2763
+ return `${getStepEntityLabel(step, step.ready)} ${step.ready === 1 ? "is" : "are"} ready for the next bounded run.`;
2764
+ }
2765
+ if (step.blocked > 0) return step.emptyBlockedText;
2766
+ if (step.complete > 0) return `${step.label} is complete for the current cohort.`;
2767
+ return "No runnable records are available for this step.";
2768
+ }
2769
+ function getStepCompactSummary(step) {
2770
+ const parts = [
2771
+ step.complete > 0 ? `${step.complete} complete` : null,
2772
+ step.ready > 0 ? `${step.ready} ready` : null,
2773
+ step.failed > 0 ? `${step.failed} failed` : null,
2774
+ step.blocked > 0 ? `${step.blocked} waiting` : null
2775
+ ].filter(Boolean);
2776
+ return parts.length ? parts.join(" | ") : "No records yet";
2777
+ }
2778
+ function getDisplayStep(steps, recommendedStep) {
2779
+ return recommendedStep ?? steps.find((step) => step.status !== "complete") ?? steps[steps.length - 1] ?? null;
2780
+ }
2781
+ function BuildTab({
2782
+ steps,
2783
+ recommendedStep,
2784
+ onRunStep,
2785
+ onRetryStep,
2786
+ onViewRuns
2787
+ }) {
2788
+ const [selectedStepId, setSelectedStepId] = useState(null);
2789
+ const currentStep = getDisplayStep(steps, recommendedStep);
2790
+ const selectedStep = steps.find((step) => step.id === selectedStepId) ?? currentStep;
2791
+ const currentStepIndex = currentStep ? steps.findIndex((step) => step.id === currentStep.id) : -1;
2792
+ const selectedStepIndex = selectedStep ? steps.findIndex((step) => step.id === selectedStep.id) : -1;
2793
+ const selectedActionKind = selectedStep?.nextActionKind ?? "none";
2794
+ const canRunSelectedAction = !!selectedStep?.action && (selectedActionKind === "retry_failed" || selectedActionKind === "run_next_batch");
2795
+ const selectedActionIcon = selectedActionKind === "retry_failed" ? /* @__PURE__ */ jsx(IconRefresh, { size: 14 }) : /* @__PURE__ */ jsx(IconPlayerPlay, { size: 14 });
2796
+ const handleSelectedAction = () => {
2797
+ if (!selectedStep || !canRunSelectedAction) return;
2798
+ if (selectedActionKind === "retry_failed") {
2799
+ onRetryStep(selectedStep);
2800
+ return;
2801
+ }
2802
+ onRunStep(selectedStep);
2803
+ };
2804
+ return /* @__PURE__ */ jsxs(Stack, { gap: "md", p: "md", children: [
2805
+ /* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
2806
+ /* @__PURE__ */ jsx(CardHeader, { icon: /* @__PURE__ */ jsx(IconBolt, { size: 16 }), title: "Build" }),
2807
+ /* @__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." })
2808
+ ] }),
2809
+ currentStep ? /* @__PURE__ */ jsx(Card, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { gap: "xs", style: { minWidth: 0 }, children: [
2810
+ /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
2811
+ /* @__PURE__ */ jsx(
2812
+ StatusPill,
2813
+ {
2814
+ label: `Step ${currentStepIndex + 1} of ${steps.length}`,
2815
+ tone: currentStep.status === "failed" ? "failed" : "ready"
2816
+ }
2817
+ ),
2818
+ /* @__PURE__ */ jsx(StatusPill, { label: getBuildStatusLabel(currentStep.status), tone: currentStep.status })
2819
+ ] }),
2820
+ /* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
2821
+ /* @__PURE__ */ jsx(Title, { order: 4, children: currentStep.label }),
2822
+ /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: getStepActionSummary(currentStep) }),
2823
+ currentStep.recommendedAction?.defaultSize !== void 0 && currentStep.recommendedAction.maxSize !== void 0 ? /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
2824
+ "Runs up to ",
2825
+ currentStep.recommendedAction.defaultSize,
2826
+ " by default,",
2827
+ " ",
2828
+ currentStep.recommendedAction.maxSize,
2829
+ " max."
2830
+ ] }) : null,
2831
+ !currentStep.action && (currentStep.ready > 0 || currentStep.failed > 0) ? /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "No workflow is mapped for this step yet." }) : null
2832
+ ] })
2833
+ ] }) }) : null,
2834
+ /* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 1, md: 2 }, spacing: "md", children: [
2835
+ /* @__PURE__ */ jsx(Stack, { gap: "xs", children: steps.map((step, index) => {
2836
+ const isCurrent = currentStep?.id === step.id;
2837
+ const isSelected = selectedStep?.id === step.id;
2838
+ const isWaiting = step.status === "blocked";
2839
+ const isPassiveWaiting = isWaiting && !isSelected && !isCurrent;
2840
+ const stepTone = isSelected || isCurrent ? step.status : step.status === "complete" || isWaiting ? step.status : "neutral";
2841
+ return /* @__PURE__ */ jsx(
2842
+ UnstyledButton,
2843
+ {
2844
+ onClick: () => setSelectedStepId(step.id),
2845
+ style: {
2846
+ backgroundColor: isSelected ? "var(--active-background)" : isPassiveWaiting ? "color-mix(in srgb, var(--color-surface-hover) 38%, transparent)" : "transparent",
2847
+ border: isSelected ? "var(--active-border)" : "1px solid var(--color-border)",
2848
+ borderStyle: isPassiveWaiting ? "dashed" : "solid",
2849
+ borderRadius: 8,
2850
+ display: "block",
2851
+ opacity: isPassiveWaiting ? 0.72 : 1,
2852
+ padding: 12,
2853
+ textAlign: "left",
2854
+ width: "100%"
2855
+ },
2856
+ children: /* @__PURE__ */ jsxs(Group, { gap: "sm", align: "flex-start", wrap: "nowrap", children: [
2857
+ /* @__PURE__ */ jsx(
2858
+ Box,
2859
+ {
2860
+ w: 28,
2861
+ h: 28,
2862
+ style: {
2863
+ alignItems: "center",
2864
+ ...getBuildToneStyle(stepTone),
2865
+ borderRadius: 999,
2866
+ borderStyle: "solid",
2867
+ borderWidth: 1,
2868
+ display: "flex",
2869
+ flexShrink: 0,
2870
+ fontWeight: 700,
2871
+ justifyContent: "center"
2872
+ },
2873
+ children: index + 1
2874
+ }
2875
+ ),
2876
+ /* @__PURE__ */ jsxs(Stack, { gap: 3, style: { minWidth: 0, flex: 1 }, children: [
2877
+ /* @__PURE__ */ jsxs(Group, { gap: "xs", justify: "space-between", wrap: "nowrap", children: [
2878
+ /* @__PURE__ */ jsx(Text, { fw: 700, c: isPassiveWaiting ? "dimmed" : void 0, truncate: true, children: step.label }),
2879
+ isCurrent ? /* @__PURE__ */ jsx(StatusPill, { label: "Current", tone: step.status }) : null,
2880
+ !isCurrent && isWaiting ? /* @__PURE__ */ jsx(StatusPill, { label: "Waiting", tone: "blocked" }) : null
2881
+ ] }),
2882
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: getStepCompactSummary(step) })
2883
+ ] })
2884
+ ] })
2885
+ },
2886
+ step.id
2887
+ );
2888
+ }) }),
2889
+ selectedStep ? /* @__PURE__ */ jsx(Card, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
2890
+ /* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "flex-start", gap: "sm", children: [
2891
+ /* @__PURE__ */ jsxs(Stack, { gap: 2, style: { minWidth: 0 }, children: [
2892
+ /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", fw: 700, tt: "uppercase", children: [
2893
+ "Step ",
2894
+ selectedStepIndex + 1,
2895
+ " details"
2896
+ ] }),
2897
+ /* @__PURE__ */ jsx(Title, { order: 5, children: selectedStep.label })
2898
+ ] }),
2899
+ /* @__PURE__ */ jsx(StatusPill, { label: getBuildStatusLabel(selectedStep.status), tone: selectedStep.status })
2900
+ ] }),
2901
+ /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: selectedStep.description }),
2902
+ selectedStep.status === "blocked" ? /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: selectedStep.emptyBlockedText }) : null,
2903
+ /* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 2, sm: 4 }, spacing: "xs", children: [
2904
+ /* @__PURE__ */ jsx(CountBadge, { label: "Ready", value: selectedStep.ready, tone: "ready" }),
2905
+ /* @__PURE__ */ jsx(CountBadge, { label: "Complete", value: selectedStep.complete, tone: "complete" }),
2906
+ /* @__PURE__ */ jsx(CountBadge, { label: "Failed", value: selectedStep.failed, tone: "failed" }),
2907
+ /* @__PURE__ */ jsx(CountBadge, { label: "Waiting", value: selectedStep.blocked, tone: "blocked" })
2908
+ ] }),
2909
+ /* @__PURE__ */ jsx(BuildStepProgressBar, { step: selectedStep }),
2910
+ selectedStep.recommendedAction?.defaultSize !== void 0 && selectedStep.recommendedAction.maxSize !== void 0 ? /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
2911
+ "Batch size: ",
2912
+ selectedStep.recommendedAction.defaultSize,
2913
+ " default,",
2914
+ " ",
2915
+ selectedStep.recommendedAction.maxSize,
2916
+ " max."
2917
+ ] }) : null,
2918
+ /* @__PURE__ */ jsxs(Group, { gap: "xs", justify: "flex-end", wrap: "nowrap", children: [
2919
+ /* @__PURE__ */ jsx(
2920
+ Button,
2921
+ {
2922
+ leftSection: selectedActionIcon,
2923
+ disabled: !canRunSelectedAction,
2924
+ onClick: handleSelectedAction,
2925
+ children: selectedActionKind === "none" ? "No action" : getStepActionLabel(selectedActionKind)
2926
+ }
2927
+ ),
2928
+ /* @__PURE__ */ jsx(Button, { variant: "light", leftSection: /* @__PURE__ */ jsx(IconPlayerPlay, { size: 14 }), onClick: onViewRuns, children: "View runs" })
2929
+ ] })
2930
+ ] }) }) : null
2931
+ ] })
2932
+ ] });
2933
+ }
1287
2934
  function MembersTab({
1288
2935
  listId,
1289
2936
  progress,
@@ -1294,7 +2941,7 @@ function MembersTab({
1294
2941
  const companiesQuery = useCompanies({ listId, limit: 100, offset: 0 });
1295
2942
  const contacts = contactsQuery.data?.data ?? [];
1296
2943
  const companies = companiesQuery.data?.data ?? [];
1297
- return /* @__PURE__ */ jsxs(Paper, { withBorder: true, p: "md", children: [
2944
+ return /* @__PURE__ */ jsxs(Stack, { gap: "sm", p: "md", children: [
1298
2945
  /* @__PURE__ */ jsx(
1299
2946
  CardHeader,
1300
2947
  {
@@ -1332,9 +2979,9 @@ function MembersTab({
1332
2979
  /* @__PURE__ */ jsx(Table.Td, { children: contact.email }),
1333
2980
  /* @__PURE__ */ jsx(Table.Td, { children: contact.title ?? "\u2014" }),
1334
2981
  /* @__PURE__ */ jsx(Table.Td, { children: contact.company?.name ?? "\u2014" }),
1335
- /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: getStatusColor2(contact.status), children: contact.status }) }),
2982
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: getStatusColor4(contact.status), children: contact.status }) }),
1336
2983
  /* @__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" }) }),
1337
- /* @__PURE__ */ jsx(Table.Td, { children: formatDateTime2(contact.createdAt) })
2984
+ /* @__PURE__ */ jsx(Table.Td, { children: formatDateTime4(contact.createdAt) })
1338
2985
  ] }, contact.id);
1339
2986
  }) })
1340
2987
  ] }) }),
@@ -1356,66 +3003,14 @@ function MembersTab({
1356
3003
  /* @__PURE__ */ jsx(Table.Td, { children: company.domain ?? "\u2014" }),
1357
3004
  /* @__PURE__ */ jsx(Table.Td, { children: company.segment ?? "\u2014" }),
1358
3005
  /* @__PURE__ */ jsx(Table.Td, { children: company.contactCount }),
1359
- /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: getStatusColor2(company.status), children: company.status }) }),
3006
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: getStatusColor4(company.status), children: company.status }) }),
1360
3007
  /* @__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" }) }),
1361
- /* @__PURE__ */ jsx(Table.Td, { children: formatDateTime2(company.createdAt) })
3008
+ /* @__PURE__ */ jsx(Table.Td, { children: formatDateTime4(company.createdAt) })
1362
3009
  ] }, company.id);
1363
3010
  }) })
1364
3011
  ] }) })
1365
3012
  ] });
1366
3013
  }
1367
- function ArtifactsTab({ listId }) {
1368
- const { data, isLoading, error } = useArtifacts({ ownerKind: "list", ownerId: listId });
1369
- const artifacts = data?.artifacts ?? [];
1370
- if (isLoading) {
1371
- return /* @__PURE__ */ jsx(Center, { p: "xl", children: /* @__PURE__ */ jsx(Loader, {}) });
1372
- }
1373
- if (error) {
1374
- return /* @__PURE__ */ jsx(CenteredErrorState, { error, title: "Failed to load artifacts" });
1375
- }
1376
- if (!artifacts.length) {
1377
- return /* @__PURE__ */ jsx(Alert, { icon: /* @__PURE__ */ jsx(IconDatabase, { size: 16 }), color: "gray", variant: "light", children: "No artifacts recorded for this list yet." });
1378
- }
1379
- return /* @__PURE__ */ jsx(Stack, { gap: "sm", children: artifacts.map((artifact) => /* @__PURE__ */ jsx(Card, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
1380
- /* @__PURE__ */ jsxs(Group, { justify: "space-between", children: [
1381
- /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
1382
- /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: "blue", children: artifact.kind }),
1383
- /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
1384
- "v",
1385
- artifact.version
1386
- ] })
1387
- ] }),
1388
- /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: formatDateTime2(artifact.createdAt) })
1389
- ] }),
1390
- /* @__PURE__ */ jsx(JsonViewer, { data: artifact.content, maxHeight: 240 })
1391
- ] }) }, artifact.id)) });
1392
- }
1393
- function ExecutionHistorySection({
1394
- executions
1395
- }) {
1396
- if (!executions.length) {
1397
- return /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "No executions recorded for this list yet." });
1398
- }
1399
- return /* @__PURE__ */ jsxs(Table, { children: [
1400
- /* @__PURE__ */ jsx(Table.Thead, { children: /* @__PURE__ */ jsxs(Table.Tr, { children: [
1401
- /* @__PURE__ */ jsx(Table.Th, { children: "Resource" }),
1402
- /* @__PURE__ */ jsx(Table.Th, { children: "Status" }),
1403
- /* @__PURE__ */ jsx(Table.Th, { children: "Started" }),
1404
- /* @__PURE__ */ jsx(Table.Th, { children: "Completed" }),
1405
- /* @__PURE__ */ jsx(Table.Th, { children: "Duration" })
1406
- ] }) }),
1407
- /* @__PURE__ */ jsx(Table.Tbody, { children: executions.map((execution) => /* @__PURE__ */ jsxs(Table.Tr, { children: [
1408
- /* @__PURE__ */ jsxs(Table.Td, { children: [
1409
- /* @__PURE__ */ jsx(Text, { fw: 500, children: execution.resourceId }),
1410
- /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: execution.executionId })
1411
- ] }),
1412
- /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: getStatusColor2(execution.status), children: execution.status }) }),
1413
- /* @__PURE__ */ jsx(Table.Td, { children: formatDateTime2(execution.createdAt) }),
1414
- /* @__PURE__ */ jsx(Table.Td, { children: formatDateTime2(execution.completedAt) }),
1415
- /* @__PURE__ */ jsx(Table.Td, { children: execution.durationMs == null ? "n/a" : `${execution.durationMs} ms` })
1416
- ] }, execution.executionId)) })
1417
- ] });
1418
- }
1419
3014
  function ListDetailHeader({
1420
3015
  title,
1421
3016
  caption,
@@ -1425,6 +3020,7 @@ function ListDetailHeader({
1425
3020
  }
1426
3021
  function LeadGenListDetailPage({ listId }) {
1427
3022
  const navigate = useNavigate();
3023
+ const actions = useListActions();
1428
3024
  const rawSearch = useSearch({ strict: false });
1429
3025
  const memberId = rawSearch.member ?? null;
1430
3026
  const memberKind = rawSearch.memberKind ?? null;
@@ -1443,11 +3039,16 @@ function LeadGenListDetailPage({ listId }) {
1443
3039
  };
1444
3040
  const listQuery = useList(listId);
1445
3041
  const progressQuery = useListProgress(listId);
1446
- const executionsQuery = useListExecutions(listId);
1447
3042
  const deleteListMutation = useDeleteList();
3043
+ const updateListMutation = useUpdateList(listId);
1448
3044
  const [deleteModalOpen, setDeleteModalOpen] = useState(false);
1449
- const isLoading = listQuery.isLoading || progressQuery.isLoading || executionsQuery.isLoading;
1450
- const error = listQuery.error ?? progressQuery.error ?? executionsQuery.error;
3045
+ const [pipelineModalOpen, setPipelineModalOpen] = useState(false);
3046
+ const [selectedBuildTemplateId, setSelectedBuildTemplateId] = useState(DEFAULT_PROSPECTING_BUILD_TEMPLATE_ID);
3047
+ const [runModalOpen, setRunModalOpen] = useState(false);
3048
+ const [activeTab, setActiveTab] = useState("overview");
3049
+ const [initialRunResourceId, setInitialRunResourceId] = useState(null);
3050
+ const isLoading = listQuery.isLoading || progressQuery.isLoading;
3051
+ const error = listQuery.error ?? progressQuery.error;
1451
3052
  const backButton = /* @__PURE__ */ jsx(
1452
3053
  Button,
1453
3054
  {
@@ -1499,7 +3100,27 @@ function LeadGenListDetailPage({ listId }) {
1499
3100
  }
1500
3101
  const list = listQuery.data;
1501
3102
  const progress = progressQuery.data;
1502
- const executions = executionsQuery.data ?? [];
3103
+ const currentBuildTemplateId = list.metadata.buildPlanSnapshot?.templateId ?? DEFAULT_PROSPECTING_BUILD_TEMPLATE_ID;
3104
+ const currentBuildTemplateLabel = list.metadata.buildPlanSnapshot?.templateLabel ?? "Default lead generation";
3105
+ const selectedBuildTemplate = PROSPECTING_BUILD_TEMPLATE_OPTIONS.find(
3106
+ (template) => template.id === selectedBuildTemplateId
3107
+ );
3108
+ const isSameBuildTemplate = selectedBuildTemplateId === currentBuildTemplateId;
3109
+ const buildResolution = resolveBuildState(list, progress, actions);
3110
+ const buildSteps = buildResolution.steps;
3111
+ const recommendedBuildStep = buildResolution.recommendedStep;
3112
+ const openRunModal = (resourceId) => {
3113
+ const nextResourceId = resourceId ?? actions[0]?.resourceId ?? null;
3114
+ setInitialRunResourceId(nextResourceId);
3115
+ setRunModalOpen(true);
3116
+ };
3117
+ const closeRunModal = () => {
3118
+ setRunModalOpen(false);
3119
+ setInitialRunResourceId(null);
3120
+ };
3121
+ const openStepRun = (step) => {
3122
+ openRunModal(step.action?.resourceId ?? null);
3123
+ };
1503
3124
  const handleCopyListCommand = () => {
1504
3125
  void navigator.clipboard.writeText(`/acquisition --lead-gen list ${list.id}`);
1505
3126
  showSuccessNotification("Copied list command to clipboard");
@@ -1512,6 +3133,29 @@ function LeadGenListDetailPage({ listId }) {
1512
3133
  }
1513
3134
  });
1514
3135
  };
3136
+ const openPipelineModal = () => {
3137
+ setSelectedBuildTemplateId(currentBuildTemplateId);
3138
+ setPipelineModalOpen(true);
3139
+ };
3140
+ const closePipelineModal = () => {
3141
+ if (updateListMutation.isPending) return;
3142
+ setPipelineModalOpen(false);
3143
+ setSelectedBuildTemplateId(currentBuildTemplateId);
3144
+ };
3145
+ const handleChangePipeline = () => {
3146
+ if (isSameBuildTemplate) return;
3147
+ updateListMutation.mutate(
3148
+ {
3149
+ buildTemplateId: selectedBuildTemplateId,
3150
+ confirmBuildTemplateChange: true
3151
+ },
3152
+ {
3153
+ onSuccess: () => {
3154
+ setPipelineModalOpen(false);
3155
+ }
3156
+ }
3157
+ );
3158
+ };
1515
3159
  return /* @__PURE__ */ jsxs(SubshellContentContainer, { children: [
1516
3160
  /* @__PURE__ */ jsx(PageContainer, { children: /* @__PURE__ */ jsxs(Stack, { children: [
1517
3161
  /* @__PURE__ */ jsx(
@@ -1523,13 +3167,24 @@ function LeadGenListDetailPage({ listId }) {
1523
3167
  ),
1524
3168
  /* @__PURE__ */ jsxs(Group, { justify: "space-between", gap: "xs", wrap: "nowrap", children: [
1525
3169
  /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
1526
- /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "filled", color: getStatusColor2(list.status), children: list.status }),
3170
+ /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "filled", color: getStatusColor4(list.status), children: list.status }),
3171
+ /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: "violet", children: currentBuildTemplateLabel }),
1527
3172
  /* @__PURE__ */ jsxs(Text, { size: "sm", c: "dimmed", children: [
1528
3173
  "Created ",
1529
- formatDateTime2(list.createdAt)
3174
+ formatDateTime4(list.createdAt)
1530
3175
  ] })
1531
3176
  ] }),
1532
3177
  /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
3178
+ /* @__PURE__ */ jsx(
3179
+ Button,
3180
+ {
3181
+ size: "xs",
3182
+ variant: "light",
3183
+ leftSection: /* @__PURE__ */ jsx(IconSwitchHorizontal, { size: 14 }),
3184
+ onClick: openPipelineModal,
3185
+ children: "Change pipeline"
3186
+ }
3187
+ ),
1533
3188
  /* @__PURE__ */ jsxs(Group, { gap: 4, wrap: "nowrap", onClick: handleCopyListCommand, style: { cursor: "pointer" }, children: [
1534
3189
  /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", ff: "monospace", children: list.id }),
1535
3190
  /* @__PURE__ */ jsx(ActionIcon, { variant: "subtle", size: "sm", "aria-label": "Copy list command", children: /* @__PURE__ */ jsx(IconCopy, { size: 14 }) })
@@ -1549,22 +3204,72 @@ function LeadGenListDetailPage({ listId }) {
1549
3204
  backButton
1550
3205
  ] })
1551
3206
  ] }),
1552
- /* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsxs(Tabs, { defaultValue: "overview", children: [
3207
+ /* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsxs(Tabs, { value: activeTab, onChange: setActiveTab, children: [
1553
3208
  /* @__PURE__ */ jsxs(Tabs.List, { children: [
1554
3209
  /* @__PURE__ */ jsx(Tabs.Tab, { value: "overview", leftSection: /* @__PURE__ */ jsx(IconBuilding, { size: 14 }), children: "Overview" }),
1555
3210
  /* @__PURE__ */ jsx(Tabs.Tab, { value: "members", leftSection: /* @__PURE__ */ jsx(IconUsers, { size: 14 }), children: "Members" }),
1556
- /* @__PURE__ */ jsx(Tabs.Tab, { value: "artifacts", leftSection: /* @__PURE__ */ jsx(IconDatabase, { size: 14 }), children: "Artifacts" })
3211
+ /* @__PURE__ */ jsx(Tabs.Tab, { value: "build", leftSection: /* @__PURE__ */ jsx(IconBolt, { size: 14 }), children: "Build" }),
3212
+ /* @__PURE__ */ jsx(Tabs.Tab, { value: "runs", leftSection: /* @__PURE__ */ jsx(IconPlayerPlay, { size: 14 }), children: "Runs" })
1557
3213
  ] }),
1558
3214
  /* @__PURE__ */ jsx(Tabs.Panel, { value: "overview", pt: "sm", children: /* @__PURE__ */ jsx(OverviewTab, { list, progress }) }),
1559
3215
  /* @__PURE__ */ jsx(Tabs.Panel, { value: "members", pt: "sm", children: /* @__PURE__ */ jsx(MembersTab, { listId, progress, onMemberClick: handleMemberClick }) }),
1560
- /* @__PURE__ */ jsx(Tabs.Panel, { value: "artifacts", pt: "sm", children: /* @__PURE__ */ jsx(ArtifactsTab, { listId }) })
1561
- ] }) }),
1562
- /* @__PURE__ */ jsxs(Paper, { withBorder: true, p: "md", children: [
1563
- /* @__PURE__ */ jsx(CardHeader, { icon: /* @__PURE__ */ jsx(IconPlayerPlay, { size: 16 }), title: "Execution History" }),
1564
- /* @__PURE__ */ jsx(ExecutionHistorySection, { executions })
1565
- ] })
3216
+ /* @__PURE__ */ jsx(Tabs.Panel, { value: "build", pt: "sm", children: /* @__PURE__ */ jsx(
3217
+ BuildTab,
3218
+ {
3219
+ steps: buildSteps,
3220
+ recommendedStep: recommendedBuildStep,
3221
+ onRunStep: openStepRun,
3222
+ onRetryStep: openStepRun,
3223
+ onViewRuns: () => setActiveTab("runs")
3224
+ }
3225
+ ) }),
3226
+ /* @__PURE__ */ jsx(Tabs.Panel, { value: "runs", pt: "sm", children: /* @__PURE__ */ jsx(WorkflowRunsPanel, { listId, title: "Runs" }) })
3227
+ ] }) })
1566
3228
  ] }) }),
1567
3229
  /* @__PURE__ */ jsx(ListMemberDrawer, { memberId, memberKind, listId, onClose: handleDrawerClose }),
3230
+ /* @__PURE__ */ jsx(
3231
+ CustomModal,
3232
+ {
3233
+ opened: pipelineModalOpen,
3234
+ onClose: closePipelineModal,
3235
+ size: "md",
3236
+ loading: updateListMutation.isPending,
3237
+ children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
3238
+ /* @__PURE__ */ jsxs(Group, { gap: "sm", children: [
3239
+ /* @__PURE__ */ jsx(IconSwitchHorizontal, { size: 22 }),
3240
+ /* @__PURE__ */ jsx(Title, { order: 4, children: "Change build pipeline" })
3241
+ ] }),
3242
+ /* @__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." }) }),
3243
+ /* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
3244
+ /* @__PURE__ */ jsx(
3245
+ Select,
3246
+ {
3247
+ label: "Build Pipeline",
3248
+ data: BUILD_TEMPLATE_SELECT_OPTIONS2,
3249
+ value: selectedBuildTemplateId,
3250
+ onChange: (value) => setSelectedBuildTemplateId(value ?? currentBuildTemplateId),
3251
+ disabled: updateListMutation.isPending,
3252
+ required: true
3253
+ }
3254
+ ),
3255
+ selectedBuildTemplate?.description ? /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: selectedBuildTemplate.description }) : null
3256
+ ] }),
3257
+ /* @__PURE__ */ jsxs(Group, { justify: "flex-end", mt: "md", children: [
3258
+ /* @__PURE__ */ jsx(Button, { variant: "light", onClick: closePipelineModal, disabled: updateListMutation.isPending, children: "Cancel" }),
3259
+ /* @__PURE__ */ jsx(
3260
+ Button,
3261
+ {
3262
+ color: "yellow",
3263
+ loading: updateListMutation.isPending,
3264
+ disabled: isSameBuildTemplate,
3265
+ onClick: handleChangePipeline,
3266
+ children: "Confirm pipeline change"
3267
+ }
3268
+ )
3269
+ ] })
3270
+ ] })
3271
+ }
3272
+ ),
1568
3273
  /* @__PURE__ */ jsx(
1569
3274
  CustomModal,
1570
3275
  {
@@ -1590,6 +3295,23 @@ function LeadGenListDetailPage({ listId }) {
1590
3295
  ] })
1591
3296
  ] })
1592
3297
  }
3298
+ ),
3299
+ /* @__PURE__ */ jsx(
3300
+ RunWorkflowModal,
3301
+ {
3302
+ opened: runModalOpen,
3303
+ onClose: closeRunModal,
3304
+ list,
3305
+ actions,
3306
+ selection: EMPTY_SELECTION2,
3307
+ initialResourceId: initialRunResourceId,
3308
+ title: "Run build step",
3309
+ description: "Run the selected bounded build workflow for this list.",
3310
+ lockResourceSelection: true,
3311
+ onSubmitted: (_resourceId, executionId) => {
3312
+ showSuccessNotification(`Workflow run started: ${executionId}`);
3313
+ }
3314
+ }
1593
3315
  )
1594
3316
  ] });
1595
3317
  }
@@ -1951,4 +3673,4 @@ function LeadGenContactsPage() {
1951
3673
  ] }) });
1952
3674
  }
1953
3675
 
1954
- export { CompanyDetailModal, ContactDetailModal, LEAD_GEN_ITEMS, LEAD_GEN_ROUTE_LINKS, LeadGenCompaniesPage, LeadGenContactsPage, LeadGenListDetailPage, LeadGenListsPage, LeadGenOverviewPage, LeadGenRouteShell, LeadGenSidebar, LeadGenSidebarMiddle, LeadGenSidebarTop, formatDate, getEnrichmentColor, getEnrichmentStatus, getStateKeyColor, getStatusColor, leadGenManifest, useDeleteLists };
3676
+ 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 };