@elevasis/ui 2.24.0 → 2.25.1

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 (103) hide show
  1. package/dist/app/index.css +11 -0
  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-4NWNS7TX.js → chunk-3AHEHVJ6.js} +3 -3
  8. package/dist/{chunk-LJWV4TWV.js → chunk-4ZFBVND2.js} +2 -2
  9. package/dist/{chunk-FUEXGRFR.js → chunk-7LJUTTXU.js} +5 -5
  10. package/dist/{chunk-KCJ6VATY.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-MTR6AN2C.js → chunk-IX7LWINC.js} +6 -5
  17. package/dist/{chunk-AXXTN44Z.js → chunk-IYIZYMIE.js} +2 -2
  18. package/dist/{chunk-VGNAV3TH.js → chunk-JDQSCEEF.js} +1237 -642
  19. package/dist/chunk-JMI7L7Y7.js +524 -0
  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-YBZT7MJR.js → chunk-L4RT57WU.js} +7 -7
  23. package/dist/{chunk-2WZ635SS.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-M2HWJY6O.js → chunk-QB2CC4VH.js} +2045 -243
  28. package/dist/{chunk-QULLZ5PE.js → chunk-RSG2O3HF.js} +893 -642
  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-KLFIJDTD.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 +11 -0
  38. package/dist/components/index.d.ts +478 -386
  39. package/dist/components/index.js +138 -267
  40. package/dist/components/navigation/index.css +11 -0
  41. package/dist/components/navigation/index.js +8 -8
  42. package/dist/features/auth/index.css +11 -0
  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 +11 -0
  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 +11 -0
  49. package/dist/features/dashboard/index.js +20 -20
  50. package/dist/features/delivery/index.css +11 -0
  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 +11 -0
  54. package/dist/features/lead-gen/index.d.ts +174 -4
  55. package/dist/features/lead-gen/index.js +20 -20
  56. package/dist/features/monitoring/index.css +11 -0
  57. package/dist/features/monitoring/index.js +21 -21
  58. package/dist/features/monitoring/requests/index.css +11 -0
  59. package/dist/features/monitoring/requests/index.js +17 -17
  60. package/dist/features/operations/index.css +11 -0
  61. package/dist/features/operations/index.js +24 -24
  62. package/dist/features/seo/index.js +2 -2
  63. package/dist/features/settings/index.css +11 -0
  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 +11 -0
  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 +11 -0
  70. package/dist/hooks/index.d.ts +769 -401
  71. package/dist/hooks/index.js +17 -17
  72. package/dist/hooks/published.css +11 -0
  73. package/dist/hooks/published.d.ts +769 -401
  74. package/dist/hooks/published.js +17 -17
  75. package/dist/index.css +11 -0
  76. package/dist/index.d.ts +885 -383
  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 +11 -0
  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 +11 -0
  89. package/dist/provider/index.d.ts +503 -362
  90. package/dist/provider/index.js +14 -14
  91. package/dist/provider/published.css +11 -0
  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 +2 -2
  99. package/dist/chunk-CEWTOKE7.js +0 -109
  100. package/dist/chunk-OWHQ65EQ.js +0 -211
  101. package/dist/chunk-UDJE54WN.js +0 -209
  102. /package/dist/{chunk-IRW7JMQ4.js → chunk-5WWZXCS5.js} +0 -0
  103. /package/dist/{chunk-QJ2KCHKX.js → chunk-E565XMTQ.js} +0 -0
@@ -1,22 +1,24 @@
1
- import { LEAD_GEN_STAGE_CATALOG, LEAD_GEN_PIPELINE_DEFINITIONS, findPipeline } from './chunk-OWHQ65EQ.js';
1
+ import { DEFAULT_ORGANIZATION_MODEL_PROSPECTING } from './chunk-IS53MXE4.js';
2
2
  import { PageContainer } from './chunk-BZZCNLT6.js';
3
3
  import { TableSelectionToolbar, SortableHeader } from './chunk-TUMSNGTX.js';
4
- import { SubshellNavItem } from './chunk-CEWTOKE7.js';
4
+ import { SubshellNavItem } from './chunk-X4WBGKJQ.js';
5
5
  import { SubshellSidebarSection } from './chunk-IIMU5YAJ.js';
6
6
  import { FilterBar } from './chunk-PDHTXPSF.js';
7
7
  import { CustomModal } from './chunk-KVJ3LFH2.js';
8
- import { acquisitionListKeys, useListsTelemetry, useLists, useCreateList, useTableSort, sortData, usePaginationState, useTableSelection, useList, useListProgress, useListExecutions, useCompanyFacets, useCompanies, useDeleteCompanies, useContacts, useDeleteContacts, useArtifacts, useListMembers, useListMember, useTransitionListMember, useDeriveActions } from './chunk-QULLZ5PE.js';
9
- import { showApiErrorNotification, showSuccessNotification } from './chunk-Z6FAH4XV.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, Tabs, SegmentedControl, Drawer, Progress } from '@mantine/core';
15
- import { IconLayoutGrid, IconList, IconBuilding, IconAddressBook, IconTarget, IconExternalLink, IconX, IconAlertCircle, IconPlayerPlay, IconArrowRight, IconSparkles, IconListDetails, IconPlus, IconSearch, IconAlertTriangle, IconCopy, 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,149 +1084,1498 @@ 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 done = entry?.done ?? 0;
1115
- const total = entry?.total ?? 0;
1116
- const percent = total > 0 ? Math.round(done / total * 100) : 0;
1117
- const label = LEAD_GEN_STAGE_CATALOG[key]?.label ?? key;
1118
- return /* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
1119
- /* @__PURE__ */ jsxs(Group, { justify: "space-between", gap: "xs", children: [
1120
- /* @__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 }),
1121
1363
  /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
1122
- done,
1123
- " / ",
1124
1364
  total,
1125
- " (",
1126
- percent,
1127
- "%)"
1365
+ " ",
1366
+ entity === "company" ? "companies" : "contacts"
1128
1367
  ] })
1129
- ] }),
1130
- /* @__PURE__ */ jsx(Progress, { value: percent, size: "sm", color: getMemberStateColor2(key) })
1131
- ] }, key);
1132
- }) })
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(
1393
+ Box,
1394
+ {
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
+ })
1459
+ }
1460
+ )
1461
+ }
1462
+ ) })
1133
1463
  ] });
1134
1464
  }
1135
- function PipelineStagesCard({ list, progress }) {
1136
- const hasAnyActivity = Object.keys(progress.byCompanyStage).length > 0 || Object.keys(progress.byContactStage).length > 0;
1137
- return /* @__PURE__ */ jsx(Card, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
1138
- /* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "center", children: [
1139
- /* @__PURE__ */ jsx(Title, { order: 5, children: "Pipeline Stages" }),
1140
- /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
1141
- /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "filled", color: getStatusColor2(list.status), children: list.status }),
1142
- list.launchedAt && /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
1143
- "Launched ",
1144
- formatDateTime2(list.launchedAt)
1145
- ] }),
1146
- list.completedAt && /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
1147
- "Completed ",
1148
- formatDateTime2(list.completedAt)
1149
- ] })
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 })
1150
1562
  ] })
1151
1563
  ] }),
1152
- !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: [
1153
1565
  /* @__PURE__ */ jsx(
1154
- PipelineSection,
1566
+ Select,
1155
1567
  {
1156
- title: `Company Pipeline (${progress.totalCompanies})`,
1157
- byStage: progress.byCompanyStage,
1158
- 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
1159
1574
  }
1160
1575
  ),
1161
- /* @__PURE__ */ jsx(
1162
- 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,
1163
1593
  {
1164
- title: `Contact Pipeline (${progress.totalMembers})`,
1165
- byStage: progress.byContactStage,
1166
- 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
+ ]
1167
1601
  }
1168
- )
1602
+ ) }) : null
1169
1603
  ] })
1170
1604
  ] }) });
1171
1605
  }
1172
- function ListConfigCard({ list }) {
1173
- const icp = list.icp ?? {};
1174
- const scraping = list.scrapingConfig ?? {};
1175
- const hasIcp = Object.values(icp).some((v) => v !== void 0 && v !== null && v !== "");
1176
- const hasScraping = Object.values(scraping).some((v) => v !== void 0 && v !== null && v !== "");
1177
- if (!hasIcp && !hasScraping) {
1178
- return /* @__PURE__ */ jsx(Card, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
1179
- /* @__PURE__ */ jsx(Title, { order: 5, children: "List Config" }),
1180
- /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "No ICP rubric or scraping criteria configured for this list." })
1181
- ] }) });
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";
1182
1639
  }
1183
- return /* @__PURE__ */ jsx(Card, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
1184
- /* @__PURE__ */ jsx(Title, { order: 5, children: "List Config" }),
1185
- /* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 1, md: 2 }, spacing: "md", children: [
1186
- hasIcp && /* @__PURE__ */ jsxs(Stack, { gap: 6, children: [
1187
- /* @__PURE__ */ jsx(Text, { size: "sm", fw: 600, children: "ICP Rubric" }),
1188
- icp.qualificationRubricKey && /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
1189
- /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "Rubric Key" }),
1190
- /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", children: icp.qualificationRubricKey })
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) {
2566
+ return /* @__PURE__ */ jsx(Card, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
2567
+ /* @__PURE__ */ jsx(Title, { order: 5, children: "List Config" }),
2568
+ /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "No ICP rubric or scraping criteria configured for this list." })
2569
+ ] }) });
2570
+ }
2571
+ return /* @__PURE__ */ jsx(Card, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
2572
+ /* @__PURE__ */ jsx(Title, { order: 5, children: "List Config" }),
2573
+ /* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 1, md: 2 }, spacing: "md", children: [
2574
+ hasIcp && /* @__PURE__ */ jsxs(Stack, { gap: 6, children: [
2575
+ /* @__PURE__ */ jsx(Text, { size: "sm", fw: 600, children: "ICP Rubric" }),
2576
+ icp.qualificationRubricKey && /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
2577
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "Rubric Key" }),
2578
+ /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", children: icp.qualificationRubricKey })
1191
2579
  ] }),
1192
2580
  icp.targetDescription && /* @__PURE__ */ jsxs(Stack, { gap: 2, children: [
1193
2581
  /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "Target" }),
@@ -1239,19 +2627,310 @@ function ListConfigCard({ list }) {
1239
2627
  }
1240
2628
  function OverviewTab({ list, progress }) {
1241
2629
  const hasMetadata = list.metadata && Object.keys(list.metadata).length > 0;
1242
- return /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
1243
- /* @__PURE__ */ jsx(PipelineStagesCard, { list, progress }),
1244
- /* @__PURE__ */ jsx(ListConfigCard, { list }),
2630
+ return /* @__PURE__ */ jsxs(Stack, { gap: "md", p: "md", children: [
2631
+ /* @__PURE__ */ jsx(CardHeader, { icon: /* @__PURE__ */ jsx(IconBuilding, { size: 16 }), title: "Overview" }),
1245
2632
  /* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 1, sm: 2 }, children: [
1246
2633
  /* @__PURE__ */ jsx(StatCard, { label: "Companies", value: progress.totalCompanies, icon: IconBuilding }),
1247
2634
  /* @__PURE__ */ jsx(StatCard, { label: "Members", value: progress.totalMembers, icon: IconUsers })
1248
2635
  ] }),
2636
+ /* @__PURE__ */ jsx(PipelineStagesCard, { list, progress }),
2637
+ /* @__PURE__ */ jsx(ListConfigCard, { list }),
1249
2638
  hasMetadata && /* @__PURE__ */ jsx(Card, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
1250
2639
  /* @__PURE__ */ jsx(Title, { order: 5, children: "Metadata / Qualification Signals" }),
1251
2640
  /* @__PURE__ */ jsx(JsonViewer, { data: list.metadata, maxHeight: 320 })
1252
2641
  ] }) })
1253
2642
  ] });
1254
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
+ }
1255
2934
  function MembersTab({
1256
2935
  listId,
1257
2936
  progress,
@@ -1262,7 +2941,7 @@ function MembersTab({
1262
2941
  const companiesQuery = useCompanies({ listId, limit: 100, offset: 0 });
1263
2942
  const contacts = contactsQuery.data?.data ?? [];
1264
2943
  const companies = companiesQuery.data?.data ?? [];
1265
- return /* @__PURE__ */ jsxs(Paper, { withBorder: true, p: "md", children: [
2944
+ return /* @__PURE__ */ jsxs(Stack, { gap: "sm", p: "md", children: [
1266
2945
  /* @__PURE__ */ jsx(
1267
2946
  CardHeader,
1268
2947
  {
@@ -1300,9 +2979,9 @@ function MembersTab({
1300
2979
  /* @__PURE__ */ jsx(Table.Td, { children: contact.email }),
1301
2980
  /* @__PURE__ */ jsx(Table.Td, { children: contact.title ?? "\u2014" }),
1302
2981
  /* @__PURE__ */ jsx(Table.Td, { children: contact.company?.name ?? "\u2014" }),
1303
- /* @__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 }) }),
1304
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" }) }),
1305
- /* @__PURE__ */ jsx(Table.Td, { children: formatDateTime2(contact.createdAt) })
2984
+ /* @__PURE__ */ jsx(Table.Td, { children: formatDateTime4(contact.createdAt) })
1306
2985
  ] }, contact.id);
1307
2986
  }) })
1308
2987
  ] }) }),
@@ -1324,66 +3003,14 @@ function MembersTab({
1324
3003
  /* @__PURE__ */ jsx(Table.Td, { children: company.domain ?? "\u2014" }),
1325
3004
  /* @__PURE__ */ jsx(Table.Td, { children: company.segment ?? "\u2014" }),
1326
3005
  /* @__PURE__ */ jsx(Table.Td, { children: company.contactCount }),
1327
- /* @__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 }) }),
1328
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" }) }),
1329
- /* @__PURE__ */ jsx(Table.Td, { children: formatDateTime2(company.createdAt) })
3008
+ /* @__PURE__ */ jsx(Table.Td, { children: formatDateTime4(company.createdAt) })
1330
3009
  ] }, company.id);
1331
3010
  }) })
1332
3011
  ] }) })
1333
3012
  ] });
1334
3013
  }
1335
- function ArtifactsTab({ listId }) {
1336
- const { data, isLoading, error } = useArtifacts({ ownerKind: "list", ownerId: listId });
1337
- const artifacts = data?.artifacts ?? [];
1338
- if (isLoading) {
1339
- return /* @__PURE__ */ jsx(Center, { p: "xl", children: /* @__PURE__ */ jsx(Loader, {}) });
1340
- }
1341
- if (error) {
1342
- return /* @__PURE__ */ jsx(CenteredErrorState, { error, title: "Failed to load artifacts" });
1343
- }
1344
- if (!artifacts.length) {
1345
- return /* @__PURE__ */ jsx(Alert, { icon: /* @__PURE__ */ jsx(IconDatabase, { size: 16 }), color: "gray", variant: "light", children: "No artifacts recorded for this list yet." });
1346
- }
1347
- return /* @__PURE__ */ jsx(Stack, { gap: "sm", children: artifacts.map((artifact) => /* @__PURE__ */ jsx(Card, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
1348
- /* @__PURE__ */ jsxs(Group, { justify: "space-between", children: [
1349
- /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
1350
- /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: "blue", children: artifact.kind }),
1351
- /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
1352
- "v",
1353
- artifact.version
1354
- ] })
1355
- ] }),
1356
- /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: formatDateTime2(artifact.createdAt) })
1357
- ] }),
1358
- /* @__PURE__ */ jsx(JsonViewer, { data: artifact.content, maxHeight: 240 })
1359
- ] }) }, artifact.id)) });
1360
- }
1361
- function ExecutionHistorySection({
1362
- executions
1363
- }) {
1364
- if (!executions.length) {
1365
- return /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "No executions recorded for this list yet." });
1366
- }
1367
- return /* @__PURE__ */ jsxs(Table, { children: [
1368
- /* @__PURE__ */ jsx(Table.Thead, { children: /* @__PURE__ */ jsxs(Table.Tr, { children: [
1369
- /* @__PURE__ */ jsx(Table.Th, { children: "Resource" }),
1370
- /* @__PURE__ */ jsx(Table.Th, { children: "Status" }),
1371
- /* @__PURE__ */ jsx(Table.Th, { children: "Started" }),
1372
- /* @__PURE__ */ jsx(Table.Th, { children: "Completed" }),
1373
- /* @__PURE__ */ jsx(Table.Th, { children: "Duration" })
1374
- ] }) }),
1375
- /* @__PURE__ */ jsx(Table.Tbody, { children: executions.map((execution) => /* @__PURE__ */ jsxs(Table.Tr, { children: [
1376
- /* @__PURE__ */ jsxs(Table.Td, { children: [
1377
- /* @__PURE__ */ jsx(Text, { fw: 500, children: execution.resourceId }),
1378
- /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: execution.executionId })
1379
- ] }),
1380
- /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: getStatusColor2(execution.status), children: execution.status }) }),
1381
- /* @__PURE__ */ jsx(Table.Td, { children: formatDateTime2(execution.createdAt) }),
1382
- /* @__PURE__ */ jsx(Table.Td, { children: formatDateTime2(execution.completedAt) }),
1383
- /* @__PURE__ */ jsx(Table.Td, { children: execution.durationMs == null ? "n/a" : `${execution.durationMs} ms` })
1384
- ] }, execution.executionId)) })
1385
- ] });
1386
- }
1387
3014
  function ListDetailHeader({
1388
3015
  title,
1389
3016
  caption,
@@ -1393,6 +3020,7 @@ function ListDetailHeader({
1393
3020
  }
1394
3021
  function LeadGenListDetailPage({ listId }) {
1395
3022
  const navigate = useNavigate();
3023
+ const actions = useListActions();
1396
3024
  const rawSearch = useSearch({ strict: false });
1397
3025
  const memberId = rawSearch.member ?? null;
1398
3026
  const memberKind = rawSearch.memberKind ?? null;
@@ -1411,9 +3039,16 @@ function LeadGenListDetailPage({ listId }) {
1411
3039
  };
1412
3040
  const listQuery = useList(listId);
1413
3041
  const progressQuery = useListProgress(listId);
1414
- const executionsQuery = useListExecutions(listId);
1415
- const isLoading = listQuery.isLoading || progressQuery.isLoading || executionsQuery.isLoading;
1416
- const error = listQuery.error ?? progressQuery.error ?? executionsQuery.error;
3042
+ const deleteListMutation = useDeleteList();
3043
+ const updateListMutation = useUpdateList(listId);
3044
+ const [deleteModalOpen, setDeleteModalOpen] = useState(false);
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;
1417
3052
  const backButton = /* @__PURE__ */ jsx(
1418
3053
  Button,
1419
3054
  {
@@ -1465,11 +3100,62 @@ function LeadGenListDetailPage({ listId }) {
1465
3100
  }
1466
3101
  const list = listQuery.data;
1467
3102
  const progress = progressQuery.data;
1468
- 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
+ };
1469
3124
  const handleCopyListCommand = () => {
1470
3125
  void navigator.clipboard.writeText(`/acquisition --lead-gen list ${list.id}`);
1471
3126
  showSuccessNotification("Copied list command to clipboard");
1472
3127
  };
3128
+ const handleDeleteList = () => {
3129
+ deleteListMutation.mutate(list.id, {
3130
+ onSuccess: () => {
3131
+ setDeleteModalOpen(false);
3132
+ void navigate({ to: "/lead-gen/lists" });
3133
+ }
3134
+ });
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
+ };
1473
3159
  return /* @__PURE__ */ jsxs(SubshellContentContainer, { children: [
1474
3160
  /* @__PURE__ */ jsx(PageContainer, { children: /* @__PURE__ */ jsxs(Stack, { children: [
1475
3161
  /* @__PURE__ */ jsx(
@@ -1481,36 +3167,152 @@ function LeadGenListDetailPage({ listId }) {
1481
3167
  ),
1482
3168
  /* @__PURE__ */ jsxs(Group, { justify: "space-between", gap: "xs", wrap: "nowrap", children: [
1483
3169
  /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
1484
- /* @__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 }),
1485
3172
  /* @__PURE__ */ jsxs(Text, { size: "sm", c: "dimmed", children: [
1486
3173
  "Created ",
1487
- formatDateTime2(list.createdAt)
3174
+ formatDateTime4(list.createdAt)
1488
3175
  ] })
1489
3176
  ] }),
1490
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
+ ),
1491
3188
  /* @__PURE__ */ jsxs(Group, { gap: 4, wrap: "nowrap", onClick: handleCopyListCommand, style: { cursor: "pointer" }, children: [
1492
3189
  /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", ff: "monospace", children: list.id }),
1493
3190
  /* @__PURE__ */ jsx(ActionIcon, { variant: "subtle", size: "sm", "aria-label": "Copy list command", children: /* @__PURE__ */ jsx(IconCopy, { size: 14 }) })
1494
3191
  ] }),
3192
+ /* @__PURE__ */ jsx(Tooltip, { label: "Delete list", children: /* @__PURE__ */ jsx(
3193
+ ActionIcon,
3194
+ {
3195
+ variant: "subtle",
3196
+ color: "red",
3197
+ size: "sm",
3198
+ "aria-label": "Delete list",
3199
+ loading: deleteListMutation.isPending,
3200
+ onClick: () => setDeleteModalOpen(true),
3201
+ children: /* @__PURE__ */ jsx(IconTrash, { size: 14 })
3202
+ }
3203
+ ) }),
1495
3204
  backButton
1496
3205
  ] })
1497
3206
  ] }),
1498
- /* @__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: [
1499
3208
  /* @__PURE__ */ jsxs(Tabs.List, { children: [
1500
3209
  /* @__PURE__ */ jsx(Tabs.Tab, { value: "overview", leftSection: /* @__PURE__ */ jsx(IconBuilding, { size: 14 }), children: "Overview" }),
1501
3210
  /* @__PURE__ */ jsx(Tabs.Tab, { value: "members", leftSection: /* @__PURE__ */ jsx(IconUsers, { size: 14 }), children: "Members" }),
1502
- /* @__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" })
1503
3213
  ] }),
1504
3214
  /* @__PURE__ */ jsx(Tabs.Panel, { value: "overview", pt: "sm", children: /* @__PURE__ */ jsx(OverviewTab, { list, progress }) }),
1505
3215
  /* @__PURE__ */ jsx(Tabs.Panel, { value: "members", pt: "sm", children: /* @__PURE__ */ jsx(MembersTab, { listId, progress, onMemberClick: handleMemberClick }) }),
1506
- /* @__PURE__ */ jsx(Tabs.Panel, { value: "artifacts", pt: "sm", children: /* @__PURE__ */ jsx(ArtifactsTab, { listId }) })
1507
- ] }) }),
1508
- /* @__PURE__ */ jsxs(Paper, { withBorder: true, p: "md", children: [
1509
- /* @__PURE__ */ jsx(CardHeader, { icon: /* @__PURE__ */ jsx(IconPlayerPlay, { size: 16 }), title: "Execution History" }),
1510
- /* @__PURE__ */ jsx(ExecutionHistorySection, { executions })
1511
- ] })
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
+ ] }) })
1512
3228
  ] }) }),
1513
- /* @__PURE__ */ jsx(ListMemberDrawer, { memberId, memberKind, listId, onClose: handleDrawerClose })
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
+ ),
3273
+ /* @__PURE__ */ jsx(
3274
+ CustomModal,
3275
+ {
3276
+ opened: deleteModalOpen,
3277
+ onClose: () => !deleteListMutation.isPending && setDeleteModalOpen(false),
3278
+ size: "sm",
3279
+ loading: deleteListMutation.isPending,
3280
+ children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
3281
+ /* @__PURE__ */ jsxs(Group, { gap: "sm", children: [
3282
+ /* @__PURE__ */ jsx(IconTrash, { size: 22, color: "var(--color-error)" }),
3283
+ /* @__PURE__ */ jsx(Title, { order: 4, children: "Delete list" })
3284
+ ] }),
3285
+ /* @__PURE__ */ jsxs(Text, { size: "sm", children: [
3286
+ "Are you sure you want to delete",
3287
+ " ",
3288
+ /* @__PURE__ */ jsx(Text, { span: true, fw: 600, children: list.name }),
3289
+ "?"
3290
+ ] }),
3291
+ /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "This removes the list and its list membership links. Companies and contacts remain in the shared lead-gen database." }),
3292
+ /* @__PURE__ */ jsxs(Group, { justify: "flex-end", mt: "md", children: [
3293
+ /* @__PURE__ */ jsx(Button, { variant: "light", onClick: () => setDeleteModalOpen(false), disabled: deleteListMutation.isPending, children: "Cancel" }),
3294
+ /* @__PURE__ */ jsx(Button, { color: "red", loading: deleteListMutation.isPending, onClick: handleDeleteList, children: "Delete" })
3295
+ ] })
3296
+ ] })
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
+ }
3315
+ )
1514
3316
  ] });
1515
3317
  }
1516
3318
  var PAGE_SIZE_DEFAULT2 = 20;
@@ -1871,4 +3673,4 @@ function LeadGenContactsPage() {
1871
3673
  ] }) });
1872
3674
  }
1873
3675
 
1874
- 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 };