@elevasis/ui 2.25.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.
- package/dist/app/index.css +11 -0
- package/dist/app/index.d.ts +457 -357
- package/dist/app/index.js +8 -7
- package/dist/auth/index.js +4 -4
- package/dist/charts/index.js +7 -7
- package/dist/{chunk-DDZOHLHB.js → chunk-26HQMR7S.js} +1 -1
- package/dist/{chunk-ZMXZ476Y.js → chunk-3AHEHVJ6.js} +3 -3
- package/dist/{chunk-LJWV4TWV.js → chunk-4ZFBVND2.js} +2 -2
- package/dist/{chunk-7D2HSSIW.js → chunk-7LJUTTXU.js} +5 -5
- package/dist/{chunk-ABV5LDDC.js → chunk-AZQY7AJR.js} +13 -24
- package/dist/{chunk-BIWHHWCJ.js → chunk-DWK2QIAK.js} +2 -1
- package/dist/{chunk-ZDKQNQ4X.js → chunk-GESXCQWY.js} +1 -1
- package/dist/{chunk-Z6FAH4XV.js → chunk-HKBEURCV.js} +1 -1
- package/dist/{chunk-HC2KV6BU.js → chunk-HOIT677G.js} +1 -1
- package/dist/chunk-IS53MXE4.js +230 -0
- package/dist/{chunk-WSC5LU3U.js → chunk-IX7LWINC.js} +6 -5
- package/dist/{chunk-AXXTN44Z.js → chunk-IYIZYMIE.js} +2 -2
- package/dist/{chunk-WWVSPOJY.js → chunk-JDQSCEEF.js} +1237 -642
- package/dist/{chunk-AZXSFDG2.js → chunk-JMI7L7Y7.js} +113 -63
- package/dist/{chunk-TSSKOQBX.js → chunk-KMAXFJPH.js} +2 -2
- package/dist/{chunk-HQ7M6PBW.js → chunk-KU7ZDWQ7.js} +1 -1
- package/dist/{chunk-ZGZZIR6K.js → chunk-L4RT57WU.js} +7 -7
- package/dist/{chunk-LK4MPIMK.js → chunk-MU4VPAMR.js} +2 -2
- package/dist/{chunk-3JCMO7SD.js → chunk-N55DVMAG.js} +1 -1
- package/dist/{chunk-EIOJNUPL.js → chunk-PNLJIPV5.js} +1 -1
- package/dist/{chunk-6Z3G4U2R.js → chunk-Q5BEODAT.js} +3 -2
- package/dist/{chunk-HVC2BTFO.js → chunk-QB2CC4VH.js} +1979 -257
- package/dist/{chunk-QJLRDTYS.js → chunk-RSG2O3HF.js} +932 -707
- package/dist/{chunk-M25JL54Z.js → chunk-RYTEQBAO.js} +1 -1
- package/dist/{chunk-XUYBOO32.js → chunk-U36X6NZM.js} +15 -7
- package/dist/{chunk-QSTH6T77.js → chunk-VKMNWHTL.js} +1 -1
- package/dist/{chunk-V3UOW2HG.js → chunk-VOVZLL23.js} +4 -4
- package/dist/{chunk-SLH2QLKV.js → chunk-WFTNY755.js} +1 -1
- package/dist/{chunk-DK2HVHCY.js → chunk-WKJ47GIW.js} +1 -1
- package/dist/chunk-X4WBGKJQ.js +138 -0
- package/dist/{chunk-QHSW4WHM.js → chunk-XTVZFT7U.js} +1 -1
- package/dist/components/index.css +11 -0
- package/dist/components/index.d.ts +478 -386
- package/dist/components/index.js +138 -336
- package/dist/components/navigation/index.css +11 -0
- package/dist/components/navigation/index.js +8 -8
- package/dist/features/auth/index.css +11 -0
- package/dist/features/auth/index.d.ts +369 -363
- package/dist/features/auth/index.js +7 -7
- package/dist/features/crm/index.css +11 -0
- package/dist/features/crm/index.d.ts +551 -360
- package/dist/features/crm/index.js +19 -19
- package/dist/features/dashboard/index.css +11 -0
- package/dist/features/dashboard/index.js +20 -20
- package/dist/features/delivery/index.css +11 -0
- package/dist/features/delivery/index.d.ts +363 -357
- package/dist/features/delivery/index.js +19 -19
- package/dist/features/lead-gen/index.css +11 -0
- package/dist/features/lead-gen/index.d.ts +160 -2
- package/dist/features/lead-gen/index.js +20 -19
- package/dist/features/monitoring/index.css +11 -0
- package/dist/features/monitoring/index.js +21 -21
- package/dist/features/monitoring/requests/index.css +11 -0
- package/dist/features/monitoring/requests/index.js +17 -17
- package/dist/features/operations/index.css +11 -0
- package/dist/features/operations/index.js +24 -23
- package/dist/features/seo/index.js +2 -2
- package/dist/features/settings/index.css +11 -0
- package/dist/features/settings/index.d.ts +369 -363
- package/dist/features/settings/index.js +19 -19
- package/dist/hooks/delivery/index.css +11 -0
- package/dist/hooks/delivery/index.d.ts +363 -357
- package/dist/hooks/delivery/index.js +2 -2
- package/dist/hooks/index.css +11 -0
- package/dist/hooks/index.d.ts +746 -399
- package/dist/hooks/index.js +17 -17
- package/dist/hooks/published.css +11 -0
- package/dist/hooks/published.d.ts +746 -399
- package/dist/hooks/published.js +17 -17
- package/dist/index.css +11 -0
- package/dist/index.d.ts +862 -381
- package/dist/index.js +17 -17
- package/dist/initialization/index.d.ts +369 -363
- package/dist/initialization/index.js +4 -4
- package/dist/layout/index.d.ts +6 -0
- package/dist/layout/index.js +5 -5
- package/dist/organization/index.css +11 -0
- package/dist/organization/index.js +4 -4
- package/dist/profile/index.d.ts +369 -363
- package/dist/profile/index.js +2 -2
- package/dist/provider/ElevasisServiceContext.d.ts +1 -0
- package/dist/provider/ElevasisServiceContext.js +1 -1
- package/dist/provider/index.css +11 -0
- package/dist/provider/index.d.ts +503 -362
- package/dist/provider/index.js +14 -14
- package/dist/provider/published.css +11 -0
- package/dist/provider/published.d.ts +494 -359
- package/dist/provider/published.js +12 -12
- package/dist/supabase/index.d.ts +369 -357
- package/dist/test-utils/index.js +1 -1
- package/dist/typeform/index.js +49 -20
- package/dist/types/index.d.ts +382 -363
- package/package.json +4 -4
- package/dist/chunk-CEWTOKE7.js +0 -109
- /package/dist/{chunk-IRW7JMQ4.js → chunk-5WWZXCS5.js} +0 -0
- /package/dist/{chunk-QJ2KCHKX.js → chunk-E565XMTQ.js} +0 -0
|
@@ -1,22 +1,24 @@
|
|
|
1
|
+
import { DEFAULT_ORGANIZATION_MODEL_PROSPECTING } from './chunk-IS53MXE4.js';
|
|
1
2
|
import { PageContainer } from './chunk-BZZCNLT6.js';
|
|
2
3
|
import { TableSelectionToolbar, SortableHeader } from './chunk-TUMSNGTX.js';
|
|
3
|
-
import { SubshellNavItem } from './chunk-
|
|
4
|
+
import { SubshellNavItem } from './chunk-X4WBGKJQ.js';
|
|
4
5
|
import { SubshellSidebarSection } from './chunk-IIMU5YAJ.js';
|
|
5
6
|
import { FilterBar } from './chunk-PDHTXPSF.js';
|
|
6
7
|
import { CustomModal } from './chunk-KVJ3LFH2.js';
|
|
7
|
-
import { acquisitionListKeys, useListsTelemetry, useLists, useCreateList, useTableSort, sortData, usePaginationState, useTableSelection, useList, useListProgress, useListExecutions, useDeleteList, useCompanyFacets, useCompanies, useDeleteCompanies, useContacts, useDeleteContacts,
|
|
8
|
-
import { showApiErrorNotification, showSuccessNotification } from './chunk-
|
|
9
|
-
import { LEAD_GEN_STAGE_CATALOG, LEAD_GEN_PIPELINE_DEFINITIONS, findPipeline } from './chunk-
|
|
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-
|
|
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-
|
|
14
|
-
import { Stack, Paper, Text, Anchor, Group, Title, ActionIcon, Divider, Box, SimpleGrid, Badge, Card,
|
|
15
|
-
import { IconLayoutGrid, IconList, IconBuilding, IconAddressBook, IconTarget, IconExternalLink, IconX, IconAlertCircle, IconPlayerPlay, IconArrowRight, IconSparkles, IconListDetails, IconPlus, IconSearch, IconAlertTriangle,
|
|
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 [
|
|
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
|
-
|
|
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
|
-
|
|
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: "
|
|
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
|
|
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:
|
|
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__ */
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
837
|
+
/* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
|
|
838
|
+
/* @__PURE__ */ jsx(
|
|
839
|
+
Select,
|
|
840
|
+
{
|
|
841
|
+
label: "Build Pipeline",
|
|
842
|
+
description: "Choose the sequence this list should follow.",
|
|
843
|
+
data: BUILD_TEMPLATE_SELECT_OPTIONS,
|
|
844
|
+
value: newBuildTemplateId,
|
|
845
|
+
onChange: (value) => setNewBuildTemplateId(value ?? DEFAULT_PROSPECTING_BUILD_TEMPLATE_ID),
|
|
846
|
+
disabled: createListMutation.isPending,
|
|
847
|
+
required: true
|
|
848
|
+
}
|
|
849
|
+
),
|
|
850
|
+
selectedBuildTemplate?.description ? /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: selectedBuildTemplate.description }) : null
|
|
851
|
+
] }),
|
|
852
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "The list will open in draft mode after creation so you can review configuration and run build steps from the list workspace." }),
|
|
814
853
|
/* @__PURE__ */ jsxs(Group, { justify: "flex-end", children: [
|
|
815
854
|
/* @__PURE__ */ jsx(Button, { variant: "light", onClick: resetCreateListModal, disabled: createListMutation.isPending, children: "Cancel" }),
|
|
816
855
|
/* @__PURE__ */ jsx(Button, { onClick: handleCreateList, loading: createListMutation.isPending, disabled: !newListName.trim(), children: "Create List" })
|
|
@@ -1045,168 +1084,1485 @@ function ListMemberDrawer({ memberId, memberKind, listId, onClose }) {
|
|
|
1045
1084
|
}
|
|
1046
1085
|
);
|
|
1047
1086
|
}
|
|
1048
|
-
function
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
}
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
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
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
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
|
|
1079
|
-
|
|
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
|
|
1097
|
-
function
|
|
1098
|
-
return
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
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
|
|
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
|
-
|
|
1334
|
+
entity,
|
|
1335
|
+
total,
|
|
1336
|
+
stages,
|
|
1337
|
+
progress,
|
|
1107
1338
|
emptyText
|
|
1108
1339
|
}) {
|
|
1109
|
-
const
|
|
1110
|
-
return /* @__PURE__ */ jsxs(Stack, { gap: "
|
|
1111
|
-
/* @__PURE__ */
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
].filter(Boolean);
|
|
1133
|
-
return /* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
|
|
1134
|
-
/* @__PURE__ */ jsxs(Group, { justify: "space-between", gap: "xs", children: [
|
|
1135
|
-
/* @__PURE__ */ jsx(Text, { size: "sm", children: label }),
|
|
1340
|
+
const Icon = entity === "company" ? IconBuilding : IconUsers;
|
|
1341
|
+
return /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
|
|
1342
|
+
/* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "center", gap: "xs", children: [
|
|
1343
|
+
/* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
|
|
1344
|
+
/* @__PURE__ */ jsx(
|
|
1345
|
+
Box,
|
|
1346
|
+
{
|
|
1347
|
+
w: 28,
|
|
1348
|
+
h: 28,
|
|
1349
|
+
style: {
|
|
1350
|
+
alignItems: "center",
|
|
1351
|
+
background: "color-mix(in srgb, var(--color-primary) 14%, transparent)",
|
|
1352
|
+
border: "1px solid color-mix(in srgb, var(--color-border) 75%, var(--color-primary))",
|
|
1353
|
+
borderRadius: 999,
|
|
1354
|
+
color: "var(--color-primary)",
|
|
1355
|
+
display: "flex",
|
|
1356
|
+
justifyContent: "center"
|
|
1357
|
+
},
|
|
1358
|
+
children: /* @__PURE__ */ jsx(Icon, { size: 15 })
|
|
1359
|
+
}
|
|
1360
|
+
),
|
|
1361
|
+
/* @__PURE__ */ jsxs(Stack, { gap: 0, children: [
|
|
1362
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", fw: 700, children: title }),
|
|
1136
1363
|
/* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
|
|
1137
|
-
attempted,
|
|
1138
|
-
" / ",
|
|
1139
1364
|
total,
|
|
1140
|
-
"
|
|
1141
|
-
|
|
1142
|
-
"%)"
|
|
1365
|
+
" ",
|
|
1366
|
+
entity === "company" ? "companies" : "contacts"
|
|
1143
1367
|
] })
|
|
1144
|
-
] })
|
|
1145
|
-
|
|
1368
|
+
] })
|
|
1369
|
+
] }),
|
|
1370
|
+
/* @__PURE__ */ jsxs(Badge, { size: "sm", variant: "outline", color: entity === "company" ? "blue" : "teal", children: [
|
|
1371
|
+
stages.length,
|
|
1372
|
+
" stages"
|
|
1373
|
+
] })
|
|
1374
|
+
] }),
|
|
1375
|
+
stages.length === 0 ? /* @__PURE__ */ jsx(
|
|
1376
|
+
Box,
|
|
1377
|
+
{
|
|
1378
|
+
p: "sm",
|
|
1379
|
+
style: {
|
|
1380
|
+
border: "1px dashed var(--color-border)",
|
|
1381
|
+
borderRadius: "var(--mantine-radius-md)"
|
|
1382
|
+
},
|
|
1383
|
+
children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: emptyText })
|
|
1384
|
+
}
|
|
1385
|
+
) : /* @__PURE__ */ jsx(ScrollArea, { type: "hover", offsetScrollbars: true, children: /* @__PURE__ */ jsx(
|
|
1386
|
+
Box,
|
|
1387
|
+
{
|
|
1388
|
+
style: {
|
|
1389
|
+
minWidth: Math.max(560, stages.length * 178),
|
|
1390
|
+
padding: "8px 2px 2px"
|
|
1391
|
+
},
|
|
1392
|
+
children: /* @__PURE__ */ jsx(
|
|
1146
1393
|
Box,
|
|
1147
1394
|
{
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1395
|
+
style: {
|
|
1396
|
+
alignItems: "start",
|
|
1397
|
+
display: "grid",
|
|
1398
|
+
gap: 0,
|
|
1399
|
+
gridTemplateColumns: `repeat(${stages.length}, minmax(150px, 1fr))`,
|
|
1400
|
+
position: "relative"
|
|
1401
|
+
},
|
|
1402
|
+
children: stages.map((stage, index) => {
|
|
1403
|
+
const counts = getStageProgress(progress, stage.key, entity);
|
|
1404
|
+
const accent = getLeadGenStageColorVar(stage.key);
|
|
1405
|
+
const completion = Math.round(percent(counts.success, counts.total));
|
|
1406
|
+
const attempted = Math.round(percent(counts.attempted, counts.total));
|
|
1407
|
+
return /* @__PURE__ */ jsxs(Stack, { gap: 8, px: "xs", style: { position: "relative", zIndex: 1 }, children: [
|
|
1408
|
+
/* @__PURE__ */ jsxs(Group, { gap: 8, wrap: "nowrap", children: [
|
|
1409
|
+
/* @__PURE__ */ jsx(
|
|
1410
|
+
Box,
|
|
1411
|
+
{
|
|
1412
|
+
w: 44,
|
|
1413
|
+
h: 44,
|
|
1414
|
+
style: {
|
|
1415
|
+
alignItems: "center",
|
|
1416
|
+
background: getLeadGenStageColorVar(stage.key, "background"),
|
|
1417
|
+
border: `1px solid ${getLeadGenStageColorVar(stage.key, "border")}`,
|
|
1418
|
+
borderRadius: 999,
|
|
1419
|
+
boxShadow: `0 0 0 4px ${getLeadGenStageColorVar(stage.key, "background")}`,
|
|
1420
|
+
color: accent,
|
|
1421
|
+
display: "flex",
|
|
1422
|
+
flexShrink: 0,
|
|
1423
|
+
fontSize: 13,
|
|
1424
|
+
fontWeight: 800,
|
|
1425
|
+
justifyContent: "center"
|
|
1426
|
+
},
|
|
1427
|
+
children: index + 1
|
|
1428
|
+
}
|
|
1429
|
+
),
|
|
1430
|
+
/* @__PURE__ */ jsxs(Stack, { gap: 1, style: { minWidth: 0 }, children: [
|
|
1431
|
+
/* @__PURE__ */ jsxs(Group, { gap: 6, wrap: "nowrap", children: [
|
|
1432
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", fw: 700, lineClamp: 1, children: stage.label }),
|
|
1433
|
+
/* @__PURE__ */ jsx(SourceBadge, { source: stage.source })
|
|
1434
|
+
] }),
|
|
1435
|
+
/* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", lineClamp: 1, children: counts.total > 0 ? `${counts.attempted} / ${counts.total} attempted` : `Ready for ${entity === "company" ? "company" : "contact"} rows` })
|
|
1436
|
+
] })
|
|
1437
|
+
] }),
|
|
1438
|
+
/* @__PURE__ */ jsx(ProgressRail, { counts, stageKey: stage.key }),
|
|
1439
|
+
/* @__PURE__ */ jsxs(Group, { gap: 10, wrap: "nowrap", children: [
|
|
1440
|
+
/* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
|
|
1441
|
+
counts.success,
|
|
1442
|
+
" success"
|
|
1443
|
+
] }),
|
|
1444
|
+
/* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
|
|
1445
|
+
attempted,
|
|
1446
|
+
"% attempted"
|
|
1447
|
+
] }),
|
|
1448
|
+
counts.error > 0 ? /* @__PURE__ */ jsxs(Text, { size: "xs", c: "red", children: [
|
|
1449
|
+
counts.error,
|
|
1450
|
+
" errors"
|
|
1451
|
+
] }) : null
|
|
1452
|
+
] }),
|
|
1453
|
+
/* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
|
|
1454
|
+
completion,
|
|
1455
|
+
"% complete"
|
|
1456
|
+
] })
|
|
1457
|
+
] }, stage.key);
|
|
1458
|
+
})
|
|
1160
1459
|
}
|
|
1161
|
-
)
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
}) })
|
|
1460
|
+
)
|
|
1461
|
+
}
|
|
1462
|
+
) })
|
|
1165
1463
|
] });
|
|
1166
1464
|
}
|
|
1167
|
-
function
|
|
1168
|
-
const
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1465
|
+
function PipelineFunnel({ progress, pipelineConfig, actions }) {
|
|
1466
|
+
const companyStages = buildLaneStages({ entity: "company", progress, pipelineConfig, actions });
|
|
1467
|
+
const contactStages = buildLaneStages({ entity: "contact", progress, pipelineConfig, actions });
|
|
1468
|
+
return /* @__PURE__ */ jsx(Paper, { withBorder: true, p: "md", children: /* @__PURE__ */ jsxs(Stack, { gap: "lg", children: [
|
|
1469
|
+
/* @__PURE__ */ jsx(CardHeader, { icon: /* @__PURE__ */ jsx(IconListDetails, { size: 16 }), title: "Pipeline" }),
|
|
1470
|
+
/* @__PURE__ */ jsx(
|
|
1471
|
+
TimelineLane,
|
|
1472
|
+
{
|
|
1473
|
+
title: "Company pipeline",
|
|
1474
|
+
entity: "company",
|
|
1475
|
+
total: progress.totalCompanies,
|
|
1476
|
+
stages: companyStages,
|
|
1477
|
+
progress,
|
|
1478
|
+
emptyText: "No company stages are configured, runnable, or recorded yet."
|
|
1479
|
+
}
|
|
1480
|
+
),
|
|
1481
|
+
/* @__PURE__ */ jsx(
|
|
1482
|
+
TimelineLane,
|
|
1483
|
+
{
|
|
1484
|
+
title: "Contact pipeline",
|
|
1485
|
+
entity: "contact",
|
|
1486
|
+
total: progress.totalMembers,
|
|
1487
|
+
stages: contactStages,
|
|
1488
|
+
progress,
|
|
1489
|
+
emptyText: "No contact stages are configured, runnable, or recorded yet."
|
|
1490
|
+
}
|
|
1491
|
+
)
|
|
1492
|
+
] }) });
|
|
1493
|
+
}
|
|
1494
|
+
function selectedCount(selection) {
|
|
1495
|
+
return selection.selectedCompanyIds.length + selection.selectedContactIds.length;
|
|
1496
|
+
}
|
|
1497
|
+
function buildInput(input, selection) {
|
|
1498
|
+
const base = input && typeof input === "object" && !Array.isArray(input) ? input : {};
|
|
1499
|
+
return {
|
|
1500
|
+
...base,
|
|
1501
|
+
selectedCompanyIds: selection.selectedCompanyIds,
|
|
1502
|
+
selectedContactIds: selection.selectedContactIds
|
|
1503
|
+
};
|
|
1504
|
+
}
|
|
1505
|
+
function RunWorkflowModal({
|
|
1506
|
+
opened,
|
|
1507
|
+
onClose,
|
|
1508
|
+
list,
|
|
1509
|
+
actions,
|
|
1510
|
+
selection,
|
|
1511
|
+
initialResourceId,
|
|
1512
|
+
title = "Run Workflow",
|
|
1513
|
+
description = "Start a workflow immediately for this list.",
|
|
1514
|
+
lockResourceSelection = false,
|
|
1515
|
+
onResourceChange,
|
|
1516
|
+
onSubmitted
|
|
1517
|
+
}) {
|
|
1518
|
+
const [selectedResourceId, setSelectedResourceId] = useState(initialResourceId ?? actions[0]?.resourceId ?? null);
|
|
1519
|
+
const selectedAction = useMemo(
|
|
1520
|
+
() => actions.find((action) => action.resourceId === selectedResourceId) ?? actions[0],
|
|
1521
|
+
[actions, selectedResourceId]
|
|
1522
|
+
);
|
|
1523
|
+
const execution = useWorkflowExecution({
|
|
1524
|
+
workflowId: selectedAction?.resourceId ?? "",
|
|
1525
|
+
listId: list.id
|
|
1526
|
+
});
|
|
1527
|
+
useEffect(() => {
|
|
1528
|
+
if (!opened) return;
|
|
1529
|
+
const nextResourceId = initialResourceId ?? actions[0]?.resourceId ?? null;
|
|
1530
|
+
setSelectedResourceId(nextResourceId);
|
|
1531
|
+
onResourceChange?.(nextResourceId);
|
|
1532
|
+
}, [actions, initialResourceId, onResourceChange, opened]);
|
|
1533
|
+
const actionOptions = actions.map((action) => ({
|
|
1534
|
+
value: action.resourceId,
|
|
1535
|
+
label: action.label
|
|
1536
|
+
}));
|
|
1537
|
+
const handleResourceChange = (value) => {
|
|
1538
|
+
setSelectedResourceId(value);
|
|
1539
|
+
onResourceChange?.(value);
|
|
1540
|
+
execution.reset();
|
|
1541
|
+
};
|
|
1542
|
+
const submitInput = async (input) => {
|
|
1543
|
+
if (!selectedAction) return;
|
|
1544
|
+
try {
|
|
1545
|
+
const result = await execution.execute({
|
|
1546
|
+
input: buildInput(input, selection)
|
|
1547
|
+
});
|
|
1548
|
+
onSubmitted?.(selectedAction.resourceId, result.executionId);
|
|
1549
|
+
onClose();
|
|
1550
|
+
} catch (error) {
|
|
1551
|
+
showApiErrorNotification(error);
|
|
1552
|
+
}
|
|
1553
|
+
};
|
|
1554
|
+
const Form = selectedAction?.inputForm;
|
|
1555
|
+
const count = selectedCount(selection);
|
|
1556
|
+
return /* @__PURE__ */ jsx(CustomModal, { opened, onClose: () => !execution.isPending && onClose(), size: "lg", loading: execution.isPending, children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
|
|
1557
|
+
/* @__PURE__ */ jsxs(Group, { gap: "sm", children: [
|
|
1558
|
+
/* @__PURE__ */ jsx(IconBolt, { size: 22 }),
|
|
1559
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1560
|
+
/* @__PURE__ */ jsx(Text, { fw: 600, children: title }),
|
|
1561
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: description })
|
|
1182
1562
|
] })
|
|
1183
1563
|
] }),
|
|
1184
|
-
!
|
|
1564
|
+
!actions.length ? /* @__PURE__ */ jsx(Alert, { icon: /* @__PURE__ */ jsx(IconAlertCircle, { size: 16 }), color: "gray", variant: "light", children: "No list builder workflows are registered." }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1185
1565
|
/* @__PURE__ */ jsx(
|
|
1186
|
-
|
|
1566
|
+
Select,
|
|
1187
1567
|
{
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1568
|
+
label: "Workflow",
|
|
1569
|
+
data: actionOptions,
|
|
1570
|
+
value: selectedAction?.resourceId ?? null,
|
|
1571
|
+
onChange: handleResourceChange,
|
|
1572
|
+
disabled: execution.isPending || lockResourceSelection,
|
|
1573
|
+
searchable: true
|
|
1191
1574
|
}
|
|
1192
1575
|
),
|
|
1193
|
-
/* @__PURE__ */
|
|
1194
|
-
|
|
1576
|
+
selectedAction ? /* @__PURE__ */ jsxs(Stack, { gap: 6, children: [
|
|
1577
|
+
/* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
|
|
1578
|
+
/* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", children: selectedAction.category }),
|
|
1579
|
+
selectedAction.stagesAffected.map((stage) => /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "outline", color: "gray", children: stage }, stage))
|
|
1580
|
+
] }),
|
|
1581
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: selectedAction.description })
|
|
1582
|
+
] }) : null,
|
|
1583
|
+
count > 0 ? /* @__PURE__ */ jsxs(Alert, { color: "blue", variant: "light", children: [
|
|
1584
|
+
"This run includes ",
|
|
1585
|
+
selection.selectedCompanyIds.length,
|
|
1586
|
+
" selected companies and",
|
|
1587
|
+
" ",
|
|
1588
|
+
selection.selectedContactIds.length,
|
|
1589
|
+
" selected contacts."
|
|
1590
|
+
] }) : null,
|
|
1591
|
+
Form && selectedAction ? /* @__PURE__ */ jsx(Form, { list, onSubmit: submitInput }) : selectedAction ? /* @__PURE__ */ jsx(Group, { justify: "flex-end", children: /* @__PURE__ */ jsxs(
|
|
1592
|
+
Button,
|
|
1195
1593
|
{
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1594
|
+
leftSection: /* @__PURE__ */ jsx(IconPlayerPlay, { size: 16 }),
|
|
1595
|
+
loading: execution.isPending,
|
|
1596
|
+
onClick: () => void submitInput(selectedAction.defaultInput?.(list) ?? { listId: list.id }),
|
|
1597
|
+
children: [
|
|
1598
|
+
"Run ",
|
|
1599
|
+
selectedAction.label
|
|
1600
|
+
]
|
|
1199
1601
|
}
|
|
1200
|
-
)
|
|
1602
|
+
) }) : null
|
|
1201
1603
|
] })
|
|
1202
1604
|
] }) });
|
|
1203
1605
|
}
|
|
1204
|
-
function
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1606
|
+
function formatDateTime2(value) {
|
|
1607
|
+
if (!value) return "Not yet";
|
|
1608
|
+
return new Date(value).toLocaleString("en-US", {
|
|
1609
|
+
month: "short",
|
|
1610
|
+
day: "numeric",
|
|
1611
|
+
year: "numeric",
|
|
1612
|
+
hour: "numeric",
|
|
1613
|
+
minute: "2-digit"
|
|
1614
|
+
});
|
|
1615
|
+
}
|
|
1616
|
+
function formatDuration(durationMs) {
|
|
1617
|
+
if (durationMs == null) return "n/a";
|
|
1618
|
+
if (durationMs < 1e3) return `${durationMs} ms`;
|
|
1619
|
+
const seconds = durationMs / 1e3;
|
|
1620
|
+
if (seconds < 60) return `${seconds.toFixed(1)} s`;
|
|
1621
|
+
return `${Math.floor(seconds / 60)}m ${Math.round(seconds % 60)}s`;
|
|
1622
|
+
}
|
|
1623
|
+
function getStatusColor2(status) {
|
|
1624
|
+
switch (status) {
|
|
1625
|
+
case "completed":
|
|
1626
|
+
case "success":
|
|
1627
|
+
case "succeeded":
|
|
1628
|
+
return "green";
|
|
1629
|
+
case "running":
|
|
1630
|
+
case "pending":
|
|
1631
|
+
case "queued":
|
|
1632
|
+
return "blue";
|
|
1633
|
+
case "failed":
|
|
1634
|
+
case "error":
|
|
1635
|
+
case "cancelled":
|
|
1636
|
+
return "red";
|
|
1637
|
+
default:
|
|
1638
|
+
return "gray";
|
|
1639
|
+
}
|
|
1640
|
+
}
|
|
1641
|
+
function hasObjectContent(value) {
|
|
1642
|
+
return typeof value === "object" && value !== null && Object.keys(value).length > 0;
|
|
1643
|
+
}
|
|
1644
|
+
function getRunDetails(run) {
|
|
1645
|
+
const payload = hasObjectContent(run.payload) ? run.payload : void 0;
|
|
1646
|
+
const input = run.input ?? run.inputs ?? payload?.input ?? payload?.inputs;
|
|
1647
|
+
const config = run.configSnapshot ?? run.config_snapshot ?? run.config ?? payload?.config_snapshot ?? payload?.configSnapshot ?? payload?.config;
|
|
1648
|
+
return { input, config };
|
|
1649
|
+
}
|
|
1650
|
+
function getSummaryKeys(value) {
|
|
1651
|
+
if (!hasObjectContent(value)) return [];
|
|
1652
|
+
return Object.keys(value).slice(0, 6);
|
|
1653
|
+
}
|
|
1654
|
+
function WorkflowRunsPanel({ listId, title = "Workflow Runs" }) {
|
|
1655
|
+
const navigate = useNavigate();
|
|
1656
|
+
const executionsQuery = useListExecutions(listId);
|
|
1657
|
+
const [expandedExecutionId, setExpandedExecutionId] = useState(null);
|
|
1658
|
+
const executions = useMemo(() => executionsQuery.data ?? [], [executionsQuery.data]);
|
|
1659
|
+
const openWorkflow = (resourceId) => {
|
|
1660
|
+
void navigate({ to: "/operations/resources/workflow/$workflowId", params: { workflowId: resourceId } });
|
|
1661
|
+
};
|
|
1662
|
+
return /* @__PURE__ */ jsxs(Stack, { gap: "sm", p: "md", children: [
|
|
1663
|
+
/* @__PURE__ */ jsx(
|
|
1664
|
+
CardHeader,
|
|
1665
|
+
{
|
|
1666
|
+
icon: /* @__PURE__ */ jsx(IconPlayerPlay, { size: 16 }),
|
|
1667
|
+
title,
|
|
1668
|
+
rightSection: /* @__PURE__ */ jsx(Tooltip, { label: "Refresh runs", children: /* @__PURE__ */ jsx(
|
|
1669
|
+
ActionIcon,
|
|
1670
|
+
{
|
|
1671
|
+
variant: "subtle",
|
|
1672
|
+
size: "sm",
|
|
1673
|
+
"aria-label": "Refresh workflow runs",
|
|
1674
|
+
loading: executionsQuery.isFetching,
|
|
1675
|
+
onClick: () => void executionsQuery.refetch(),
|
|
1676
|
+
children: /* @__PURE__ */ jsx(IconRefresh, { size: 14 })
|
|
1677
|
+
}
|
|
1678
|
+
) })
|
|
1679
|
+
}
|
|
1680
|
+
),
|
|
1681
|
+
executionsQuery.isLoading ? /* @__PURE__ */ jsx(Center, { p: "lg", children: /* @__PURE__ */ jsx(Loader, { size: "sm" }) }) : !executions.length ? /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "No workflow runs recorded for this list." }) : /* @__PURE__ */ jsx(Table.ScrollContainer, { minWidth: 720, children: /* @__PURE__ */ jsxs(Table, { highlightOnHover: true, children: [
|
|
1682
|
+
/* @__PURE__ */ jsx(Table.Thead, { children: /* @__PURE__ */ jsxs(Table.Tr, { children: [
|
|
1683
|
+
/* @__PURE__ */ jsx(Table.Th, {}),
|
|
1684
|
+
/* @__PURE__ */ jsx(Table.Th, { children: "Workflow" }),
|
|
1685
|
+
/* @__PURE__ */ jsx(Table.Th, { children: "Status" }),
|
|
1686
|
+
/* @__PURE__ */ jsx(Table.Th, { children: "Started" }),
|
|
1687
|
+
/* @__PURE__ */ jsx(Table.Th, { children: "Completed" }),
|
|
1688
|
+
/* @__PURE__ */ jsx(Table.Th, { children: "Duration" })
|
|
1689
|
+
] }) }),
|
|
1690
|
+
/* @__PURE__ */ jsx(Table.Tbody, { children: executions.map((execution) => {
|
|
1691
|
+
const details = getRunDetails(execution);
|
|
1692
|
+
const hasDetails = details.input !== void 0 || details.config !== void 0;
|
|
1693
|
+
const expanded = expandedExecutionId === execution.executionId;
|
|
1694
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
1695
|
+
/* @__PURE__ */ jsxs(Table.Tr, { children: [
|
|
1696
|
+
/* @__PURE__ */ jsx(Table.Td, { w: 32, children: /* @__PURE__ */ jsx(
|
|
1697
|
+
ThemeIcon,
|
|
1698
|
+
{
|
|
1699
|
+
variant: "light",
|
|
1700
|
+
color: "blue",
|
|
1701
|
+
size: "md",
|
|
1702
|
+
role: hasDetails ? "button" : void 0,
|
|
1703
|
+
tabIndex: hasDetails ? 0 : void 0,
|
|
1704
|
+
"aria-label": hasDetails ? expanded ? "Collapse run details" : "Expand run details" : void 0,
|
|
1705
|
+
onClick: hasDetails ? () => setExpandedExecutionId(expanded ? null : execution.executionId) : void 0,
|
|
1706
|
+
onKeyDown: hasDetails ? (event) => {
|
|
1707
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
1708
|
+
event.preventDefault();
|
|
1709
|
+
setExpandedExecutionId(expanded ? null : execution.executionId);
|
|
1710
|
+
}
|
|
1711
|
+
} : void 0,
|
|
1712
|
+
style: {
|
|
1713
|
+
cursor: hasDetails ? "pointer" : "default",
|
|
1714
|
+
opacity: hasDetails ? 1 : 0.7
|
|
1715
|
+
},
|
|
1716
|
+
children: expanded ? /* @__PURE__ */ jsx(IconChevronDown, { size: 14 }) : /* @__PURE__ */ jsx(IconChevronRight, { size: 14 })
|
|
1717
|
+
}
|
|
1718
|
+
) }),
|
|
1719
|
+
/* @__PURE__ */ jsxs(Table.Td, { children: [
|
|
1720
|
+
execution.resourceId ? /* @__PURE__ */ jsx(
|
|
1721
|
+
Text,
|
|
1722
|
+
{
|
|
1723
|
+
fw: 500,
|
|
1724
|
+
role: "button",
|
|
1725
|
+
tabIndex: 0,
|
|
1726
|
+
c: "blue",
|
|
1727
|
+
onClick: () => openWorkflow(execution.resourceId),
|
|
1728
|
+
onKeyDown: (event) => {
|
|
1729
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
1730
|
+
event.preventDefault();
|
|
1731
|
+
openWorkflow(execution.resourceId);
|
|
1732
|
+
}
|
|
1733
|
+
},
|
|
1734
|
+
style: { cursor: "pointer" },
|
|
1735
|
+
children: execution.resourceId
|
|
1736
|
+
}
|
|
1737
|
+
) : /* @__PURE__ */ jsx(Text, { fw: 500, children: "Unknown workflow" }),
|
|
1738
|
+
/* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", ff: "monospace", children: execution.executionId })
|
|
1739
|
+
] }),
|
|
1740
|
+
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: getStatusColor2(execution.status), children: execution.status }) }),
|
|
1741
|
+
/* @__PURE__ */ jsx(Table.Td, { children: formatDateTime2(execution.createdAt) }),
|
|
1742
|
+
/* @__PURE__ */ jsx(Table.Td, { children: formatDateTime2(execution.completedAt) }),
|
|
1743
|
+
/* @__PURE__ */ jsx(Table.Td, { children: formatDuration(execution.durationMs) })
|
|
1744
|
+
] }),
|
|
1745
|
+
/* @__PURE__ */ jsx(Table.Tr, { children: /* @__PURE__ */ jsx(Table.Td, { colSpan: 6, p: 0, children: /* @__PURE__ */ jsx(Collapse, { in: expanded, children: /* @__PURE__ */ jsxs(Stack, { gap: "sm", p: "sm", children: [
|
|
1746
|
+
details.input !== void 0 ? /* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
|
|
1747
|
+
/* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
|
|
1748
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", fw: 600, children: "Input" }),
|
|
1749
|
+
getSummaryKeys(details.input).map((key) => /* @__PURE__ */ jsx(Badge, { size: "xs", variant: "light", color: "gray", children: key }, key))
|
|
1750
|
+
] }),
|
|
1751
|
+
/* @__PURE__ */ jsx(JsonViewer, { data: details.input, maxHeight: 220, fontSize: "0.75rem" })
|
|
1752
|
+
] }) : null,
|
|
1753
|
+
details.config !== void 0 ? /* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
|
|
1754
|
+
/* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
|
|
1755
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", fw: 600, children: "Config" }),
|
|
1756
|
+
getSummaryKeys(details.config).map((key) => /* @__PURE__ */ jsx(Badge, { size: "xs", variant: "light", color: "gray", children: key }, key))
|
|
1757
|
+
] }),
|
|
1758
|
+
/* @__PURE__ */ jsx(JsonViewer, { data: details.config, maxHeight: 220, fontSize: "0.75rem" })
|
|
1759
|
+
] }) : null
|
|
1760
|
+
] }) }) }) })
|
|
1761
|
+
] }, execution.executionId);
|
|
1762
|
+
}) })
|
|
1763
|
+
] }) })
|
|
1764
|
+
] });
|
|
1765
|
+
}
|
|
1766
|
+
var EMPTY_SELECTION = {
|
|
1767
|
+
selectedCompanyIds: [],
|
|
1768
|
+
selectedContactIds: []
|
|
1769
|
+
};
|
|
1770
|
+
function formatDateTime3(value) {
|
|
1771
|
+
if (!value) return "Not yet";
|
|
1772
|
+
return new Date(value).toLocaleString("en-US", {
|
|
1773
|
+
month: "short",
|
|
1774
|
+
day: "numeric",
|
|
1775
|
+
year: "numeric",
|
|
1776
|
+
hour: "numeric",
|
|
1777
|
+
minute: "2-digit"
|
|
1778
|
+
});
|
|
1779
|
+
}
|
|
1780
|
+
function getStatusColor3(status) {
|
|
1781
|
+
switch (status) {
|
|
1782
|
+
case "completed":
|
|
1783
|
+
case "success":
|
|
1784
|
+
case "launched":
|
|
1785
|
+
return "green";
|
|
1786
|
+
case "running":
|
|
1787
|
+
case "pending":
|
|
1788
|
+
case "enriching":
|
|
1789
|
+
return "blue";
|
|
1790
|
+
case "failed":
|
|
1791
|
+
case "error":
|
|
1792
|
+
case "archived":
|
|
1793
|
+
return "red";
|
|
1794
|
+
case "closing":
|
|
1795
|
+
return "yellow";
|
|
1796
|
+
default:
|
|
1797
|
+
return "gray";
|
|
1798
|
+
}
|
|
1799
|
+
}
|
|
1800
|
+
function ListBuilderPage({ listId }) {
|
|
1801
|
+
const navigate = useNavigate();
|
|
1802
|
+
const actions = useListActions();
|
|
1803
|
+
const [runModalOpen, setRunModalOpen] = useState(false);
|
|
1804
|
+
const [initialRunResourceId, setInitialRunResourceId] = useState(null);
|
|
1805
|
+
const listQuery = useList(listId);
|
|
1806
|
+
const progressQuery = useListProgress(listId);
|
|
1807
|
+
const executionsQuery = useListExecutions(listId);
|
|
1808
|
+
const isLoading = listQuery.isLoading || progressQuery.isLoading || executionsQuery.isLoading;
|
|
1809
|
+
const error = listQuery.error ?? progressQuery.error ?? executionsQuery.error;
|
|
1810
|
+
const executions = useMemo(() => executionsQuery.data ?? [], [executionsQuery.data]);
|
|
1811
|
+
const openRunModal = (resourceId) => {
|
|
1812
|
+
const nextResourceId = actions[0]?.resourceId ?? null;
|
|
1813
|
+
setInitialRunResourceId(nextResourceId);
|
|
1814
|
+
setRunModalOpen(true);
|
|
1815
|
+
};
|
|
1816
|
+
const closeRunModal = () => {
|
|
1817
|
+
setRunModalOpen(false);
|
|
1818
|
+
setInitialRunResourceId(null);
|
|
1819
|
+
};
|
|
1820
|
+
const copyListCommand = (id) => {
|
|
1821
|
+
void navigator.clipboard.writeText(`/acquisition --lead-gen list ${id}`);
|
|
1822
|
+
showSuccessNotification("Copied list command to clipboard");
|
|
1823
|
+
};
|
|
1824
|
+
const backButton = /* @__PURE__ */ jsx(
|
|
1825
|
+
Button,
|
|
1826
|
+
{
|
|
1827
|
+
variant: "light",
|
|
1828
|
+
size: "xs",
|
|
1829
|
+
leftSection: /* @__PURE__ */ jsx(IconArrowLeft, { size: 16 }),
|
|
1830
|
+
onClick: () => navigate({ to: "/lead-gen/lists" }),
|
|
1831
|
+
children: "Lists"
|
|
1832
|
+
}
|
|
1833
|
+
);
|
|
1834
|
+
if (isLoading) {
|
|
1835
|
+
return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsx(PageContainer, { children: /* @__PURE__ */ jsxs(Stack, { children: [
|
|
1836
|
+
/* @__PURE__ */ jsx(PageTitleCaption, { title: "List Builder", caption: "Lead-gen workspace", rightSection: backButton }),
|
|
1837
|
+
/* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsx(Center, { p: "xl", children: /* @__PURE__ */ jsx(Loader, {}) }) })
|
|
1838
|
+
] }) }) });
|
|
1839
|
+
}
|
|
1840
|
+
if (error) {
|
|
1841
|
+
return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsx(PageContainer, { children: /* @__PURE__ */ jsxs(Stack, { children: [
|
|
1842
|
+
/* @__PURE__ */ jsx(PageTitleCaption, { title: "List Builder", caption: "Lead-gen workspace", rightSection: backButton }),
|
|
1843
|
+
/* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsx(CenteredErrorState, { error, title: "Failed to load list builder" }) })
|
|
1844
|
+
] }) }) });
|
|
1845
|
+
}
|
|
1846
|
+
if (!listQuery.data || !progressQuery.data) {
|
|
1847
|
+
return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsx(PageContainer, { children: /* @__PURE__ */ jsxs(Stack, { children: [
|
|
1848
|
+
/* @__PURE__ */ jsx(PageTitleCaption, { title: "List Not Found", caption: "The requested lead-gen list is unavailable." }),
|
|
1849
|
+
/* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsx(Center, { h: 240, children: /* @__PURE__ */ jsx(Alert, { icon: /* @__PURE__ */ jsx(IconAlertCircle, { size: 16 }), color: "gray", variant: "light", children: "The requested list could not be found." }) }) })
|
|
1850
|
+
] }) }) });
|
|
1851
|
+
}
|
|
1852
|
+
const list = listQuery.data;
|
|
1853
|
+
const progress = progressQuery.data;
|
|
1854
|
+
return /* @__PURE__ */ jsxs(SubshellContentContainer, { children: [
|
|
1855
|
+
/* @__PURE__ */ jsx(PageContainer, { children: /* @__PURE__ */ jsxs(Stack, { children: [
|
|
1856
|
+
/* @__PURE__ */ jsx(
|
|
1857
|
+
PageTitleCaption,
|
|
1858
|
+
{
|
|
1859
|
+
title: list.name,
|
|
1860
|
+
caption: list.description ?? "Lead-gen list builder",
|
|
1861
|
+
rightSection: /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
|
|
1862
|
+
/* @__PURE__ */ jsx(Button, { size: "xs", leftSection: /* @__PURE__ */ jsx(IconBolt, { size: 16 }), onClick: () => openRunModal(), children: "Run Workflow" }),
|
|
1863
|
+
backButton
|
|
1864
|
+
] })
|
|
1865
|
+
}
|
|
1866
|
+
),
|
|
1867
|
+
/* @__PURE__ */ jsxs(Group, { justify: "space-between", gap: "xs", children: [
|
|
1868
|
+
/* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
|
|
1869
|
+
/* @__PURE__ */ jsx(Badge, { size: "sm", variant: "filled", color: getStatusColor3(list.status), children: list.status }),
|
|
1870
|
+
/* @__PURE__ */ jsxs(Text, { size: "sm", c: "dimmed", children: [
|
|
1871
|
+
"Created ",
|
|
1872
|
+
formatDateTime3(list.createdAt)
|
|
1873
|
+
] })
|
|
1874
|
+
] }),
|
|
1875
|
+
/* @__PURE__ */ jsx(Group, { gap: "xs", children: /* @__PURE__ */ jsxs(Group, { gap: 4, wrap: "nowrap", onClick: () => copyListCommand(list.id), style: { cursor: "pointer" }, children: [
|
|
1876
|
+
/* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", ff: "monospace", children: list.id }),
|
|
1877
|
+
/* @__PURE__ */ jsx(ActionIcon, { variant: "subtle", size: "sm", "aria-label": "Copy list command", children: /* @__PURE__ */ jsx(IconCopy, { size: 14 }) })
|
|
1878
|
+
] }) })
|
|
1879
|
+
] }),
|
|
1880
|
+
/* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 1, sm: 2, lg: 4 }, children: [
|
|
1881
|
+
/* @__PURE__ */ jsx(StatCard, { label: "Companies", value: progress.totalCompanies, icon: IconBuilding }),
|
|
1882
|
+
/* @__PURE__ */ jsx(StatCard, { label: "Contacts", value: progress.totalMembers, icon: IconUsers }),
|
|
1883
|
+
/* @__PURE__ */ jsx(StatCard, { label: "Workflows", value: actions.length, icon: IconBolt }),
|
|
1884
|
+
/* @__PURE__ */ jsx(StatCard, { label: "Runs", value: executions.length, icon: IconPlayerPlay })
|
|
1885
|
+
] }),
|
|
1886
|
+
/* @__PURE__ */ jsx(PipelineFunnel, { progress, pipelineConfig: list.pipelineConfig, actions }),
|
|
1887
|
+
/* @__PURE__ */ jsx(WorkflowRunsPanel, { listId })
|
|
1888
|
+
] }) }),
|
|
1889
|
+
/* @__PURE__ */ jsx(
|
|
1890
|
+
RunWorkflowModal,
|
|
1891
|
+
{
|
|
1892
|
+
opened: runModalOpen,
|
|
1893
|
+
onClose: closeRunModal,
|
|
1894
|
+
list,
|
|
1895
|
+
actions,
|
|
1896
|
+
selection: EMPTY_SELECTION,
|
|
1897
|
+
initialResourceId: initialRunResourceId,
|
|
1898
|
+
onSubmitted: (_resourceId, executionId) => {
|
|
1899
|
+
showSuccessNotification(`Workflow run started: ${executionId}`);
|
|
1900
|
+
}
|
|
1901
|
+
}
|
|
1902
|
+
)
|
|
1903
|
+
] });
|
|
1904
|
+
}
|
|
1905
|
+
function CompanyCleanupForm({ list, onSubmit }) {
|
|
1906
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
1907
|
+
const form = useForm({
|
|
1908
|
+
initialValues: {
|
|
1909
|
+
targetDescription: "",
|
|
1910
|
+
tag: "",
|
|
1911
|
+
dryRun: true,
|
|
1912
|
+
batchSize: 20
|
|
1913
|
+
},
|
|
1914
|
+
validate: {
|
|
1915
|
+
targetDescription: (value) => value.trim().length > 0 ? null : "Target description is required.",
|
|
1916
|
+
batchSize: (value) => Number.isInteger(value) && value >= 1 ? null : "Use a whole number of at least 1."
|
|
1917
|
+
}
|
|
1918
|
+
});
|
|
1919
|
+
const handleSubmit = form.onSubmit(async (values) => {
|
|
1920
|
+
setIsSubmitting(true);
|
|
1921
|
+
try {
|
|
1922
|
+
const tag = values.tag.trim();
|
|
1923
|
+
await onSubmit({
|
|
1924
|
+
listId: list.id,
|
|
1925
|
+
targetDescription: values.targetDescription.trim(),
|
|
1926
|
+
...tag ? { tag } : {},
|
|
1927
|
+
dryRun: values.dryRun,
|
|
1928
|
+
batchSize: values.batchSize
|
|
1929
|
+
});
|
|
1930
|
+
} finally {
|
|
1931
|
+
setIsSubmitting(false);
|
|
1932
|
+
}
|
|
1933
|
+
});
|
|
1934
|
+
return /* @__PURE__ */ jsx("form", { onSubmit: handleSubmit, children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
|
|
1935
|
+
/* @__PURE__ */ jsx(
|
|
1936
|
+
Textarea,
|
|
1937
|
+
{
|
|
1938
|
+
label: "Target description",
|
|
1939
|
+
placeholder: "independent veterinary clinics in Orange County",
|
|
1940
|
+
minRows: 3,
|
|
1941
|
+
autosize: true,
|
|
1942
|
+
disabled: isSubmitting,
|
|
1943
|
+
required: true,
|
|
1944
|
+
...form.getInputProps("targetDescription")
|
|
1945
|
+
}
|
|
1946
|
+
),
|
|
1947
|
+
/* @__PURE__ */ jsx(TextInput, { label: "Tag kept companies", placeholder: "vet-clinic", disabled: isSubmitting, ...form.getInputProps("tag") }),
|
|
1948
|
+
/* @__PURE__ */ jsx(
|
|
1949
|
+
NumberInput,
|
|
1950
|
+
{
|
|
1951
|
+
label: "Batch size",
|
|
1952
|
+
min: 1,
|
|
1953
|
+
allowDecimal: false,
|
|
1954
|
+
disabled: isSubmitting,
|
|
1955
|
+
required: true,
|
|
1956
|
+
...form.getInputProps("batchSize")
|
|
1957
|
+
}
|
|
1958
|
+
),
|
|
1959
|
+
/* @__PURE__ */ jsx(Switch, { label: "Dry run", disabled: isSubmitting, ...form.getInputProps("dryRun", { type: "checkbox" }) }),
|
|
1960
|
+
/* @__PURE__ */ jsx(Group, { justify: "flex-end", children: /* @__PURE__ */ jsx(
|
|
1961
|
+
Button,
|
|
1962
|
+
{
|
|
1963
|
+
type: "submit",
|
|
1964
|
+
loading: isSubmitting,
|
|
1965
|
+
disabled: !form.values.targetDescription.trim(),
|
|
1966
|
+
leftSection: /* @__PURE__ */ jsx(IconPlayerPlay, { size: 16 }),
|
|
1967
|
+
children: "Run Company Cleanup"
|
|
1968
|
+
}
|
|
1969
|
+
) })
|
|
1970
|
+
] }) });
|
|
1971
|
+
}
|
|
1972
|
+
function optionalNumber(value) {
|
|
1973
|
+
return value === "" ? void 0 : value;
|
|
1974
|
+
}
|
|
1975
|
+
function EmailDiscoveryForm({ list, onSubmit }) {
|
|
1976
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
1977
|
+
const form = useForm({
|
|
1978
|
+
initialValues: {
|
|
1979
|
+
maxCompanies: "",
|
|
1980
|
+
dryRun: false,
|
|
1981
|
+
chain: false,
|
|
1982
|
+
concurrency: 1,
|
|
1983
|
+
credentialName: "",
|
|
1984
|
+
tombaTimeoutMs: 15e3
|
|
1985
|
+
},
|
|
1986
|
+
validate: {
|
|
1987
|
+
maxCompanies: (value) => value === "" || Number.isInteger(value) && value >= 1 && value <= 500 ? null : "Use a whole number from 1 to 500.",
|
|
1988
|
+
concurrency: (value) => Number.isInteger(value) && value >= 1 && value <= 10 ? null : "Use a whole number from 1 to 10.",
|
|
1989
|
+
tombaTimeoutMs: (value) => Number.isInteger(value) && value >= 1e3 ? null : "Use a timeout of at least 1000 ms."
|
|
1990
|
+
}
|
|
1991
|
+
});
|
|
1992
|
+
const handleSubmit = form.onSubmit(async (values) => {
|
|
1993
|
+
setIsSubmitting(true);
|
|
1994
|
+
try {
|
|
1995
|
+
const credentialName = values.credentialName.trim();
|
|
1996
|
+
await onSubmit({
|
|
1997
|
+
listId: list.id,
|
|
1998
|
+
...optionalNumber(values.maxCompanies) !== void 0 ? { maxCompanies: optionalNumber(values.maxCompanies) } : {},
|
|
1999
|
+
dryRun: values.dryRun,
|
|
2000
|
+
chain: values.chain,
|
|
2001
|
+
concurrency: values.concurrency,
|
|
2002
|
+
...credentialName ? { credentialName } : {},
|
|
2003
|
+
tombaTimeoutMs: values.tombaTimeoutMs
|
|
2004
|
+
});
|
|
2005
|
+
} finally {
|
|
2006
|
+
setIsSubmitting(false);
|
|
2007
|
+
}
|
|
2008
|
+
});
|
|
2009
|
+
return /* @__PURE__ */ jsx("form", { onSubmit: handleSubmit, children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
|
|
2010
|
+
/* @__PURE__ */ jsx(
|
|
2011
|
+
NumberInput,
|
|
2012
|
+
{
|
|
2013
|
+
label: "Max companies",
|
|
2014
|
+
description: "Leave empty to process every eligible company.",
|
|
2015
|
+
min: 1,
|
|
2016
|
+
max: 500,
|
|
2017
|
+
allowDecimal: false,
|
|
2018
|
+
disabled: isSubmitting,
|
|
2019
|
+
...form.getInputProps("maxCompanies")
|
|
2020
|
+
}
|
|
2021
|
+
),
|
|
2022
|
+
/* @__PURE__ */ jsx(
|
|
2023
|
+
NumberInput,
|
|
2024
|
+
{
|
|
2025
|
+
label: "Concurrency",
|
|
2026
|
+
min: 1,
|
|
2027
|
+
max: 10,
|
|
2028
|
+
allowDecimal: false,
|
|
2029
|
+
disabled: isSubmitting,
|
|
2030
|
+
required: true,
|
|
2031
|
+
...form.getInputProps("concurrency")
|
|
2032
|
+
}
|
|
2033
|
+
),
|
|
2034
|
+
/* @__PURE__ */ jsx(
|
|
2035
|
+
TextInput,
|
|
2036
|
+
{
|
|
2037
|
+
label: "Tomba credential",
|
|
2038
|
+
placeholder: "Default credential",
|
|
2039
|
+
disabled: isSubmitting,
|
|
2040
|
+
...form.getInputProps("credentialName")
|
|
2041
|
+
}
|
|
2042
|
+
),
|
|
2043
|
+
/* @__PURE__ */ jsx(
|
|
2044
|
+
NumberInput,
|
|
2045
|
+
{
|
|
2046
|
+
label: "Tomba timeout",
|
|
2047
|
+
suffix: " ms",
|
|
2048
|
+
min: 1e3,
|
|
2049
|
+
step: 1e3,
|
|
2050
|
+
allowDecimal: false,
|
|
2051
|
+
disabled: isSubmitting,
|
|
2052
|
+
required: true,
|
|
2053
|
+
...form.getInputProps("tombaTimeoutMs")
|
|
2054
|
+
}
|
|
2055
|
+
),
|
|
2056
|
+
/* @__PURE__ */ jsx(Switch, { label: "Dry run", disabled: isSubmitting, ...form.getInputProps("dryRun", { type: "checkbox" }) }),
|
|
2057
|
+
/* @__PURE__ */ jsx(
|
|
2058
|
+
Switch,
|
|
2059
|
+
{
|
|
2060
|
+
label: "Run email verification after discovery",
|
|
2061
|
+
disabled: isSubmitting,
|
|
2062
|
+
...form.getInputProps("chain", { type: "checkbox" })
|
|
2063
|
+
}
|
|
2064
|
+
),
|
|
2065
|
+
/* @__PURE__ */ jsx(Group, { justify: "flex-end", children: /* @__PURE__ */ jsx(Button, { type: "submit", loading: isSubmitting, leftSection: /* @__PURE__ */ jsx(IconPlayerPlay, { size: 16 }), children: "Run Email Discovery" }) })
|
|
2066
|
+
] }) });
|
|
2067
|
+
}
|
|
2068
|
+
function EmailVerificationForm({ list, onSubmit }) {
|
|
2069
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
2070
|
+
const form = useForm({
|
|
2071
|
+
initialValues: {
|
|
2072
|
+
limit: 100,
|
|
2073
|
+
skipAlreadyVerified: true
|
|
2074
|
+
},
|
|
2075
|
+
validate: {
|
|
2076
|
+
limit: (value) => Number.isInteger(value) && value >= 1 && value <= 500 ? null : "Use a whole number from 1 to 500."
|
|
2077
|
+
}
|
|
2078
|
+
});
|
|
2079
|
+
const handleSubmit = form.onSubmit(async (values) => {
|
|
2080
|
+
setIsSubmitting(true);
|
|
2081
|
+
try {
|
|
2082
|
+
await onSubmit({
|
|
2083
|
+
listId: list.id,
|
|
2084
|
+
limit: values.limit,
|
|
2085
|
+
skipAlreadyVerified: values.skipAlreadyVerified
|
|
2086
|
+
});
|
|
2087
|
+
} finally {
|
|
2088
|
+
setIsSubmitting(false);
|
|
2089
|
+
}
|
|
2090
|
+
});
|
|
2091
|
+
return /* @__PURE__ */ jsx("form", { onSubmit: handleSubmit, children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
|
|
2092
|
+
/* @__PURE__ */ jsx(
|
|
2093
|
+
NumberInput,
|
|
2094
|
+
{
|
|
2095
|
+
label: "Contact limit",
|
|
2096
|
+
min: 1,
|
|
2097
|
+
max: 500,
|
|
2098
|
+
allowDecimal: false,
|
|
2099
|
+
disabled: isSubmitting,
|
|
2100
|
+
required: true,
|
|
2101
|
+
...form.getInputProps("limit")
|
|
2102
|
+
}
|
|
2103
|
+
),
|
|
2104
|
+
/* @__PURE__ */ jsx(
|
|
2105
|
+
Switch,
|
|
2106
|
+
{
|
|
2107
|
+
label: "Skip already verified contacts",
|
|
2108
|
+
disabled: isSubmitting,
|
|
2109
|
+
...form.getInputProps("skipAlreadyVerified", { type: "checkbox" })
|
|
2110
|
+
}
|
|
2111
|
+
),
|
|
2112
|
+
/* @__PURE__ */ jsx(Group, { justify: "flex-end", children: /* @__PURE__ */ jsx(Button, { type: "submit", loading: isSubmitting, leftSection: /* @__PURE__ */ jsx(IconPlayerPlay, { size: 16 }), children: "Run Email Verification" }) })
|
|
2113
|
+
] }) });
|
|
2114
|
+
}
|
|
2115
|
+
function WebsiteExtractForm({ list, onSubmit }) {
|
|
2116
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
2117
|
+
const form = useForm({
|
|
2118
|
+
initialValues: {
|
|
2119
|
+
limit: 200,
|
|
2120
|
+
concurrency: 10,
|
|
2121
|
+
chain: false
|
|
2122
|
+
},
|
|
2123
|
+
validate: {
|
|
2124
|
+
limit: (value) => Number.isInteger(value) && value >= 1 && value <= 200 ? null : "Use a whole number from 1 to 200.",
|
|
2125
|
+
concurrency: (value) => Number.isInteger(value) && value >= 1 ? null : "Use a whole number of at least 1."
|
|
2126
|
+
}
|
|
2127
|
+
});
|
|
2128
|
+
const handleSubmit = form.onSubmit(async (values) => {
|
|
2129
|
+
setIsSubmitting(true);
|
|
2130
|
+
try {
|
|
2131
|
+
await onSubmit({
|
|
2132
|
+
listId: list.id,
|
|
2133
|
+
limit: values.limit,
|
|
2134
|
+
concurrency: values.concurrency,
|
|
2135
|
+
chain: values.chain
|
|
2136
|
+
});
|
|
2137
|
+
} finally {
|
|
2138
|
+
setIsSubmitting(false);
|
|
2139
|
+
}
|
|
2140
|
+
});
|
|
2141
|
+
return /* @__PURE__ */ jsx("form", { onSubmit: handleSubmit, children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
|
|
2142
|
+
/* @__PURE__ */ jsx(
|
|
2143
|
+
NumberInput,
|
|
2144
|
+
{
|
|
2145
|
+
label: "Company limit",
|
|
2146
|
+
min: 1,
|
|
2147
|
+
max: 200,
|
|
2148
|
+
allowDecimal: false,
|
|
2149
|
+
disabled: isSubmitting,
|
|
2150
|
+
required: true,
|
|
2151
|
+
...form.getInputProps("limit")
|
|
2152
|
+
}
|
|
2153
|
+
),
|
|
2154
|
+
/* @__PURE__ */ jsx(
|
|
2155
|
+
NumberInput,
|
|
2156
|
+
{
|
|
2157
|
+
label: "Concurrency",
|
|
2158
|
+
min: 1,
|
|
2159
|
+
allowDecimal: false,
|
|
2160
|
+
disabled: isSubmitting,
|
|
2161
|
+
required: true,
|
|
2162
|
+
...form.getInputProps("concurrency")
|
|
2163
|
+
}
|
|
2164
|
+
),
|
|
2165
|
+
/* @__PURE__ */ jsx(
|
|
2166
|
+
Switch,
|
|
2167
|
+
{
|
|
2168
|
+
label: "Run company qualification after extraction",
|
|
2169
|
+
disabled: isSubmitting,
|
|
2170
|
+
...form.getInputProps("chain", { type: "checkbox" })
|
|
2171
|
+
}
|
|
2172
|
+
),
|
|
2173
|
+
/* @__PURE__ */ jsx(Group, { justify: "flex-end", children: /* @__PURE__ */ jsx(Button, { type: "submit", loading: isSubmitting, leftSection: /* @__PURE__ */ jsx(IconPlayerPlay, { size: 16 }), children: "Run Website Extract" }) })
|
|
2174
|
+
] }) });
|
|
2175
|
+
}
|
|
2176
|
+
function formatDateTime4(value) {
|
|
2177
|
+
if (!value) return "Not yet";
|
|
2178
|
+
return new Date(value).toLocaleString("en-US", {
|
|
2179
|
+
month: "short",
|
|
2180
|
+
day: "numeric",
|
|
2181
|
+
year: "numeric",
|
|
2182
|
+
hour: "numeric",
|
|
2183
|
+
minute: "2-digit"
|
|
2184
|
+
});
|
|
2185
|
+
}
|
|
2186
|
+
function contactDisplayName(firstName, lastName) {
|
|
2187
|
+
const full = [firstName, lastName].filter(Boolean).join(" ").trim();
|
|
2188
|
+
return full || "\u2014";
|
|
2189
|
+
}
|
|
2190
|
+
function getStatusColor4(status) {
|
|
2191
|
+
switch (status) {
|
|
2192
|
+
case "draft":
|
|
2193
|
+
return "gray";
|
|
2194
|
+
case "enriching":
|
|
2195
|
+
return "blue";
|
|
2196
|
+
case "launched":
|
|
2197
|
+
return "green";
|
|
2198
|
+
case "closing":
|
|
2199
|
+
return "yellow";
|
|
2200
|
+
case "archived":
|
|
2201
|
+
return "red";
|
|
2202
|
+
default:
|
|
2203
|
+
return "gray";
|
|
2204
|
+
}
|
|
2205
|
+
}
|
|
2206
|
+
function getMemberStateColor2(stateKey) {
|
|
2207
|
+
switch (stateKey) {
|
|
2208
|
+
case "personalized":
|
|
2209
|
+
case "verified":
|
|
2210
|
+
case "qualified":
|
|
2211
|
+
case "interested":
|
|
2212
|
+
return "green";
|
|
2213
|
+
case "uploaded":
|
|
2214
|
+
case "discovered":
|
|
2215
|
+
case "extracted":
|
|
2216
|
+
case "populated":
|
|
2217
|
+
return "blue";
|
|
2218
|
+
case "pending":
|
|
2219
|
+
return "gray";
|
|
2220
|
+
default:
|
|
2221
|
+
return "gray";
|
|
2222
|
+
}
|
|
2223
|
+
}
|
|
2224
|
+
var ORPHAN_STAGE_ORDER = 9999;
|
|
2225
|
+
var EMPTY_SELECTION2 = {
|
|
2226
|
+
selectedCompanyIds: [],
|
|
2227
|
+
selectedContactIds: []
|
|
2228
|
+
};
|
|
2229
|
+
var BUILD_TEMPLATE_SELECT_OPTIONS2 = PROSPECTING_BUILD_TEMPLATE_OPTIONS.map((template) => ({
|
|
2230
|
+
value: template.id,
|
|
2231
|
+
label: template.label
|
|
2232
|
+
}));
|
|
2233
|
+
var MVP_BUILD_STEPS = [
|
|
2234
|
+
{
|
|
2235
|
+
id: "source-companies",
|
|
2236
|
+
label: "Source companies",
|
|
2237
|
+
description: "Add or import the company cohort for this list.",
|
|
2238
|
+
primaryEntity: "company",
|
|
2239
|
+
outputs: ["company"],
|
|
2240
|
+
stageKey: "populated",
|
|
2241
|
+
dependencyMode: "per-record-eligibility",
|
|
2242
|
+
capabilityKey: "lead-gen.company.source",
|
|
2243
|
+
defaultBatchSize: 100,
|
|
2244
|
+
maxBatchSize: 250,
|
|
2245
|
+
emptyBlockedText: "No company source has been configured or populated yet."
|
|
2246
|
+
},
|
|
2247
|
+
{
|
|
2248
|
+
id: "analyze-websites",
|
|
2249
|
+
label: "Analyze websites",
|
|
2250
|
+
description: "Extract website intelligence for sourced companies.",
|
|
2251
|
+
primaryEntity: "company",
|
|
2252
|
+
outputs: ["company"],
|
|
2253
|
+
stageKey: "extracted",
|
|
2254
|
+
dependsOn: ["source-companies"],
|
|
2255
|
+
dependencyMode: "per-record-eligibility",
|
|
2256
|
+
capabilityKey: "lead-gen.company.website-extract",
|
|
2257
|
+
defaultBatchSize: 50,
|
|
2258
|
+
maxBatchSize: 100,
|
|
2259
|
+
emptyBlockedText: "Source companies before website analysis can run."
|
|
2260
|
+
},
|
|
2261
|
+
{
|
|
2262
|
+
id: "qualify-companies",
|
|
2263
|
+
label: "Qualify companies",
|
|
2264
|
+
description: "Apply the ICP rubric to companies with enough context.",
|
|
2265
|
+
primaryEntity: "company",
|
|
2266
|
+
outputs: ["company"],
|
|
2267
|
+
stageKey: "qualified",
|
|
2268
|
+
dependsOn: ["analyze-websites"],
|
|
2269
|
+
dependencyMode: "per-record-eligibility",
|
|
2270
|
+
capabilityKey: "lead-gen.company.qualify",
|
|
2271
|
+
defaultBatchSize: 100,
|
|
2272
|
+
maxBatchSize: 250,
|
|
2273
|
+
emptyBlockedText: "Analyze websites before qualification can run."
|
|
2274
|
+
},
|
|
2275
|
+
{
|
|
2276
|
+
id: "find-contacts",
|
|
2277
|
+
label: "Find contacts",
|
|
2278
|
+
description: "Discover contacts for qualified companies.",
|
|
2279
|
+
primaryEntity: "contact",
|
|
2280
|
+
outputs: ["contact"],
|
|
2281
|
+
stageKey: "discovered",
|
|
2282
|
+
dependsOn: ["qualify-companies"],
|
|
2283
|
+
dependencyMode: "per-record-eligibility",
|
|
2284
|
+
capabilityKey: "lead-gen.contact.discover",
|
|
2285
|
+
defaultBatchSize: 50,
|
|
2286
|
+
maxBatchSize: 100,
|
|
2287
|
+
emptyBlockedText: "Qualify companies before contact discovery can run."
|
|
2288
|
+
},
|
|
2289
|
+
{
|
|
2290
|
+
id: "verify-emails",
|
|
2291
|
+
label: "Verify emails",
|
|
2292
|
+
description: "Verify deliverability for discovered contact emails.",
|
|
2293
|
+
primaryEntity: "contact",
|
|
2294
|
+
outputs: ["contact"],
|
|
2295
|
+
stageKey: "verified",
|
|
2296
|
+
dependsOn: ["find-contacts"],
|
|
2297
|
+
dependencyMode: "per-record-eligibility",
|
|
2298
|
+
capabilityKey: "lead-gen.contact.verify-email",
|
|
2299
|
+
defaultBatchSize: 100,
|
|
2300
|
+
maxBatchSize: 500,
|
|
2301
|
+
emptyBlockedText: "Find contacts before email verification can run."
|
|
2302
|
+
},
|
|
2303
|
+
{
|
|
2304
|
+
id: "personalize",
|
|
2305
|
+
label: "Personalize",
|
|
2306
|
+
description: "Generate outreach personalization for verified contacts.",
|
|
2307
|
+
primaryEntity: "contact",
|
|
2308
|
+
outputs: ["contact"],
|
|
2309
|
+
stageKey: "personalized",
|
|
2310
|
+
dependsOn: ["verify-emails"],
|
|
2311
|
+
dependencyMode: "per-record-eligibility",
|
|
2312
|
+
capabilityKey: "lead-gen.contact.personalize",
|
|
2313
|
+
defaultBatchSize: 25,
|
|
2314
|
+
maxBatchSize: 100,
|
|
2315
|
+
emptyBlockedText: "Verify emails before personalization can run."
|
|
2316
|
+
},
|
|
2317
|
+
{
|
|
2318
|
+
id: "review",
|
|
2319
|
+
label: "Review",
|
|
2320
|
+
description: "Review personalized records before outreach handoff.",
|
|
2321
|
+
primaryEntity: "contact",
|
|
2322
|
+
outputs: ["export"],
|
|
2323
|
+
stageKey: "uploaded",
|
|
2324
|
+
dependsOn: ["personalize"],
|
|
2325
|
+
dependencyMode: "per-record-eligibility",
|
|
2326
|
+
capabilityKey: "lead-gen.review.outreach-ready",
|
|
2327
|
+
defaultBatchSize: 25,
|
|
2328
|
+
maxBatchSize: 100,
|
|
2329
|
+
emptyBlockedText: "Personalize contacts before records are ready for review."
|
|
2330
|
+
}
|
|
2331
|
+
];
|
|
2332
|
+
function sortStageKeys(keys) {
|
|
2333
|
+
return keys.slice().sort((a, b) => {
|
|
2334
|
+
const oa = LEAD_GEN_STAGE_CATALOG[a]?.order ?? ORPHAN_STAGE_ORDER;
|
|
2335
|
+
const ob = LEAD_GEN_STAGE_CATALOG[b]?.order ?? ORPHAN_STAGE_ORDER;
|
|
2336
|
+
return oa - ob || a.localeCompare(b);
|
|
2337
|
+
});
|
|
2338
|
+
}
|
|
2339
|
+
function getStageProgress2(progress, step) {
|
|
2340
|
+
return step.primaryEntity === "company" ? progress.byCompanyStage[step.stageKey] : progress.byContactStage[step.stageKey];
|
|
2341
|
+
}
|
|
2342
|
+
function getEntityTotal(progress, entity) {
|
|
2343
|
+
return entity === "company" ? progress.totalCompanies : progress.totalMembers;
|
|
2344
|
+
}
|
|
2345
|
+
function findActionForStep(actions, step) {
|
|
2346
|
+
return actions.find((action) => action.capabilityKey === step.capabilityKey) ?? actions.find((action) => action.stagesAffected?.includes(step.stageKey));
|
|
2347
|
+
}
|
|
2348
|
+
function normalizeBuildPlanStep(step) {
|
|
2349
|
+
const fallback = MVP_BUILD_STEPS.find((item) => item.id === step.id || item.stageKey === step.stageKey);
|
|
2350
|
+
return {
|
|
2351
|
+
id: step.id,
|
|
2352
|
+
label: step.label,
|
|
2353
|
+
description: step.description ?? fallback?.description ?? `Run ${step.label.toLowerCase()} for this list.`,
|
|
2354
|
+
primaryEntity: step.primaryEntity,
|
|
2355
|
+
outputs: step.outputs,
|
|
2356
|
+
stageKey: step.stageKey,
|
|
2357
|
+
dependsOn: step.dependsOn,
|
|
2358
|
+
dependencyMode: step.dependencyMode,
|
|
2359
|
+
capabilityKey: step.capabilityKey,
|
|
2360
|
+
defaultBatchSize: step.defaultBatchSize,
|
|
2361
|
+
maxBatchSize: step.maxBatchSize,
|
|
2362
|
+
emptyBlockedText: fallback?.emptyBlockedText ?? "Complete prerequisite build steps before this action can run."
|
|
2363
|
+
};
|
|
2364
|
+
}
|
|
2365
|
+
function isCurrentBuildPlanStep(step) {
|
|
2366
|
+
if (!step || typeof step !== "object") return false;
|
|
2367
|
+
const candidate = step;
|
|
2368
|
+
return (candidate.primaryEntity === "company" || candidate.primaryEntity === "contact") && Array.isArray(candidate.outputs) && candidate.outputs.length > 0 && candidate.dependencyMode === "per-record-eligibility";
|
|
2369
|
+
}
|
|
2370
|
+
function resolveBuildPlanSteps(list) {
|
|
2371
|
+
const snapshot = list.metadata.buildPlanSnapshot;
|
|
2372
|
+
const snapshotSteps = snapshot?.steps;
|
|
2373
|
+
if (snapshotSteps?.length && snapshotSteps.every(isCurrentBuildPlanStep)) {
|
|
2374
|
+
return snapshotSteps.map(normalizeBuildPlanStep);
|
|
2375
|
+
}
|
|
2376
|
+
if (snapshot?.templateId) {
|
|
2377
|
+
const templateSnapshot = createBuildPlanSnapshotFromTemplateId(snapshot.templateId);
|
|
2378
|
+
if (templateSnapshot?.steps.length) return templateSnapshot.steps.map(normalizeBuildPlanStep);
|
|
2379
|
+
}
|
|
2380
|
+
return MVP_BUILD_STEPS;
|
|
2381
|
+
}
|
|
2382
|
+
function getPrerequisiteSteps(step, byStepId) {
|
|
2383
|
+
return (step.dependsOn ?? []).flatMap((dependencyId) => {
|
|
2384
|
+
const dependency = byStepId.get(dependencyId);
|
|
2385
|
+
return dependency ? [dependency] : [];
|
|
2386
|
+
});
|
|
2387
|
+
}
|
|
2388
|
+
function getStepActionKind(ready, failed, action) {
|
|
2389
|
+
if (!action) return "none";
|
|
2390
|
+
if (failed > 0) return "retry_failed";
|
|
2391
|
+
if (ready > 0) return "run_next_batch";
|
|
2392
|
+
return "none";
|
|
2393
|
+
}
|
|
2394
|
+
function getStepActionLabel(kind) {
|
|
2395
|
+
switch (kind) {
|
|
2396
|
+
case "retry_failed":
|
|
2397
|
+
return "Retry failed";
|
|
2398
|
+
case "run_next_batch":
|
|
2399
|
+
return "Run next batch";
|
|
2400
|
+
case "advanced":
|
|
2401
|
+
return "Advanced";
|
|
2402
|
+
case "none":
|
|
2403
|
+
default:
|
|
2404
|
+
return "No action";
|
|
2405
|
+
}
|
|
2406
|
+
}
|
|
2407
|
+
function getStepRecommendedAction(step, action, kind) {
|
|
2408
|
+
if (!action || kind === "none") return void 0;
|
|
2409
|
+
return {
|
|
2410
|
+
kind,
|
|
2411
|
+
label: getStepActionLabel(kind),
|
|
2412
|
+
capabilityKey: action.capabilityKey ?? step.capabilityKey,
|
|
2413
|
+
defaultSize: step.defaultBatchSize,
|
|
2414
|
+
maxSize: step.maxBatchSize
|
|
2415
|
+
};
|
|
2416
|
+
}
|
|
2417
|
+
function deriveBuildStepStates(list, progress, actions) {
|
|
2418
|
+
const byStepId = /* @__PURE__ */ new Map();
|
|
2419
|
+
for (const step of resolveBuildPlanSteps(list)) {
|
|
2420
|
+
const stageProgress = getStageProgress2(progress, step);
|
|
2421
|
+
const entityTotal = getEntityTotal(progress, step.primaryEntity);
|
|
2422
|
+
const prerequisites = getPrerequisiteSteps(step, byStepId);
|
|
2423
|
+
const hasDependencies = (step.dependsOn?.length ?? 0) > 0;
|
|
2424
|
+
const eligibleTotal = hasDependencies ? prerequisites.reduce((sum, prerequisite) => sum + prerequisite.complete, 0) : entityTotal > 0 ? entityTotal : step.outputs.includes(step.primaryEntity) ? step.defaultBatchSize : 0;
|
|
2425
|
+
const total = Math.max(stageProgress?.total ?? 0, entityTotal, eligibleTotal);
|
|
2426
|
+
const complete = stageProgress ? stageProgress.success + stageProgress.noResult + stageProgress.skipped + stageProgress.other : 0;
|
|
2427
|
+
const failed = stageProgress?.error ?? 0;
|
|
2428
|
+
const ready = Math.max(0, eligibleTotal - complete - failed);
|
|
2429
|
+
const blocked = hasDependencies ? Math.max(entityTotal - eligibleTotal, 0) : 0;
|
|
2430
|
+
const status = failed > 0 ? "failed" : ready > 0 ? "ready" : blocked > 0 || total === 0 ? "blocked" : "complete";
|
|
2431
|
+
const action = findActionForStep(actions, step);
|
|
2432
|
+
const nextActionKind = getStepActionKind(ready, failed, action);
|
|
2433
|
+
const state = {
|
|
2434
|
+
...step,
|
|
2435
|
+
status,
|
|
2436
|
+
ready,
|
|
2437
|
+
complete,
|
|
2438
|
+
failed,
|
|
2439
|
+
blocked,
|
|
2440
|
+
nextActionKind,
|
|
2441
|
+
recommendedAction: getStepRecommendedAction(step, action, nextActionKind),
|
|
2442
|
+
action
|
|
2443
|
+
};
|
|
2444
|
+
byStepId.set(step.id, state);
|
|
2445
|
+
}
|
|
2446
|
+
return Array.from(byStepId.values());
|
|
2447
|
+
}
|
|
2448
|
+
function getRecommendedBuildStep(steps) {
|
|
2449
|
+
return steps.find((step) => step.nextActionKind === "retry_failed") ?? steps.find((step) => step.nextActionKind === "run_next_batch") ?? null;
|
|
2450
|
+
}
|
|
2451
|
+
function resolveBuildState(list, progress, actions) {
|
|
2452
|
+
const steps = deriveBuildStepStates(list, progress, actions);
|
|
2453
|
+
const recommendedStep = getRecommendedBuildStep(steps);
|
|
2454
|
+
return {
|
|
2455
|
+
steps,
|
|
2456
|
+
recommendedStep,
|
|
2457
|
+
recommendedAction: recommendedStep?.recommendedAction ?? null
|
|
2458
|
+
};
|
|
2459
|
+
}
|
|
2460
|
+
function PipelineSection({
|
|
2461
|
+
title,
|
|
2462
|
+
byStage,
|
|
2463
|
+
emptyText
|
|
2464
|
+
}) {
|
|
2465
|
+
const orderedKeys = sortStageKeys(Object.keys(byStage));
|
|
2466
|
+
return /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
|
|
2467
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", fw: 600, children: title }),
|
|
2468
|
+
orderedKeys.length === 0 ? /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: emptyText }) : /* @__PURE__ */ jsx(Stack, { gap: "xs", children: orderedKeys.map((key) => {
|
|
2469
|
+
const entry = byStage[key];
|
|
2470
|
+
const total = entry?.total ?? 0;
|
|
2471
|
+
const attempted = entry?.attempted ?? 0;
|
|
2472
|
+
const percent2 = total > 0 ? Math.round(attempted / total * 100) : 0;
|
|
2473
|
+
const label = LEAD_GEN_STAGE_CATALOG[key]?.label ?? key;
|
|
2474
|
+
const segments = [
|
|
2475
|
+
{ key: "success", value: entry?.success ?? 0, color: "var(--mantine-color-green-6)" },
|
|
2476
|
+
{ key: "noResult", value: entry?.noResult ?? 0, color: "var(--mantine-color-gray-6)" },
|
|
2477
|
+
{ key: "skipped", value: entry?.skipped ?? 0, color: "var(--mantine-color-yellow-6)" },
|
|
2478
|
+
{ key: "error", value: entry?.error ?? 0, color: "var(--mantine-color-red-6)" },
|
|
2479
|
+
{ key: "other", value: entry?.other ?? 0, color: "var(--mantine-color-blue-6)" }
|
|
2480
|
+
].filter((segment) => segment.value > 0);
|
|
2481
|
+
const statusSummary = [
|
|
2482
|
+
`${entry?.success ?? 0} success`,
|
|
2483
|
+
`${entry?.noResult ?? 0} no result`,
|
|
2484
|
+
`${entry?.skipped ?? 0} skipped`,
|
|
2485
|
+
`${entry?.error ?? 0} errors`,
|
|
2486
|
+
(entry?.other ?? 0) > 0 ? `${entry?.other ?? 0} other` : null,
|
|
2487
|
+
(entry?.notAttempted ?? 0) > 0 ? `${entry?.notAttempted ?? 0} not attempted` : null
|
|
2488
|
+
].filter(Boolean);
|
|
2489
|
+
return /* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
|
|
2490
|
+
/* @__PURE__ */ jsxs(Group, { justify: "space-between", gap: "xs", children: [
|
|
2491
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", children: label }),
|
|
2492
|
+
/* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
|
|
2493
|
+
attempted,
|
|
2494
|
+
" / ",
|
|
2495
|
+
total,
|
|
2496
|
+
" attempted (",
|
|
2497
|
+
percent2,
|
|
2498
|
+
"%)"
|
|
2499
|
+
] })
|
|
2500
|
+
] }),
|
|
2501
|
+
/* @__PURE__ */ jsx(
|
|
2502
|
+
Box,
|
|
2503
|
+
{
|
|
2504
|
+
h: 8,
|
|
2505
|
+
bg: "var(--mantine-color-dark-4)",
|
|
2506
|
+
style: { borderRadius: 999, display: "flex", overflow: "hidden" },
|
|
2507
|
+
children: segments.map((segment) => /* @__PURE__ */ jsx(
|
|
2508
|
+
Box,
|
|
2509
|
+
{
|
|
2510
|
+
h: "100%",
|
|
2511
|
+
w: `${total > 0 ? segment.value / total * 100 : 0}%`,
|
|
2512
|
+
bg: segment.color
|
|
2513
|
+
},
|
|
2514
|
+
segment.key
|
|
2515
|
+
))
|
|
2516
|
+
}
|
|
2517
|
+
),
|
|
2518
|
+
/* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: statusSummary.join(" \xB7 ") })
|
|
2519
|
+
] }, key);
|
|
2520
|
+
}) })
|
|
2521
|
+
] });
|
|
2522
|
+
}
|
|
2523
|
+
function PipelineStagesCard({ list, progress }) {
|
|
2524
|
+
const hasAnyActivity = Object.keys(progress.byCompanyStage).length > 0 || Object.keys(progress.byContactStage).length > 0;
|
|
2525
|
+
return /* @__PURE__ */ jsx(Card, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
|
|
2526
|
+
/* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "center", children: [
|
|
2527
|
+
/* @__PURE__ */ jsx(Title, { order: 5, children: "Pipeline Stages" }),
|
|
2528
|
+
/* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
|
|
2529
|
+
/* @__PURE__ */ jsx(Badge, { size: "sm", variant: "filled", color: getStatusColor4(list.status), children: list.status }),
|
|
2530
|
+
list.launchedAt && /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
|
|
2531
|
+
"Launched ",
|
|
2532
|
+
formatDateTime4(list.launchedAt)
|
|
2533
|
+
] }),
|
|
2534
|
+
list.completedAt && /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
|
|
2535
|
+
"Completed ",
|
|
2536
|
+
formatDateTime4(list.completedAt)
|
|
2537
|
+
] })
|
|
2538
|
+
] })
|
|
2539
|
+
] }),
|
|
2540
|
+
!hasAnyActivity ? /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "No pipeline activity yet \u2014 stages appear here as workflows write processing_state flags." }) : /* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 1, md: 2 }, spacing: "md", children: [
|
|
2541
|
+
/* @__PURE__ */ jsx(
|
|
2542
|
+
PipelineSection,
|
|
2543
|
+
{
|
|
2544
|
+
title: `Company Pipeline (${progress.totalCompanies})`,
|
|
2545
|
+
byStage: progress.byCompanyStage,
|
|
2546
|
+
emptyText: "No company-stage flags recorded yet."
|
|
2547
|
+
}
|
|
2548
|
+
),
|
|
2549
|
+
/* @__PURE__ */ jsx(
|
|
2550
|
+
PipelineSection,
|
|
2551
|
+
{
|
|
2552
|
+
title: `Contact Pipeline (${progress.totalMembers})`,
|
|
2553
|
+
byStage: progress.byContactStage,
|
|
2554
|
+
emptyText: "No contact-stage flags recorded yet."
|
|
2555
|
+
}
|
|
2556
|
+
)
|
|
2557
|
+
] })
|
|
2558
|
+
] }) });
|
|
2559
|
+
}
|
|
2560
|
+
function ListConfigCard({ list }) {
|
|
2561
|
+
const icp = list.icp ?? {};
|
|
2562
|
+
const scraping = list.scrapingConfig ?? {};
|
|
2563
|
+
const hasIcp = Object.values(icp).some((v) => v !== void 0 && v !== null && v !== "");
|
|
2564
|
+
const hasScraping = Object.values(scraping).some((v) => v !== void 0 && v !== null && v !== "");
|
|
2565
|
+
if (!hasIcp && !hasScraping) {
|
|
1210
2566
|
return /* @__PURE__ */ jsx(Card, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
|
|
1211
2567
|
/* @__PURE__ */ jsx(Title, { order: 5, children: "List Config" }),
|
|
1212
2568
|
/* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "No ICP rubric or scraping criteria configured for this list." })
|
|
@@ -1271,7 +2627,8 @@ function ListConfigCard({ list }) {
|
|
|
1271
2627
|
}
|
|
1272
2628
|
function OverviewTab({ list, progress }) {
|
|
1273
2629
|
const hasMetadata = list.metadata && Object.keys(list.metadata).length > 0;
|
|
1274
|
-
return /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
|
|
2630
|
+
return /* @__PURE__ */ jsxs(Stack, { gap: "md", p: "md", children: [
|
|
2631
|
+
/* @__PURE__ */ jsx(CardHeader, { icon: /* @__PURE__ */ jsx(IconBuilding, { size: 16 }), title: "Overview" }),
|
|
1275
2632
|
/* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 1, sm: 2 }, children: [
|
|
1276
2633
|
/* @__PURE__ */ jsx(StatCard, { label: "Companies", value: progress.totalCompanies, icon: IconBuilding }),
|
|
1277
2634
|
/* @__PURE__ */ jsx(StatCard, { label: "Members", value: progress.totalMembers, icon: IconUsers })
|
|
@@ -1284,6 +2641,296 @@ function OverviewTab({ list, progress }) {
|
|
|
1284
2641
|
] }) })
|
|
1285
2642
|
] });
|
|
1286
2643
|
}
|
|
2644
|
+
function getBuildStatusLabel(status) {
|
|
2645
|
+
switch (status) {
|
|
2646
|
+
case "failed":
|
|
2647
|
+
return "Needs retry";
|
|
2648
|
+
case "blocked":
|
|
2649
|
+
return "Waiting";
|
|
2650
|
+
case "complete":
|
|
2651
|
+
return "Complete";
|
|
2652
|
+
case "ready":
|
|
2653
|
+
default:
|
|
2654
|
+
return "Ready";
|
|
2655
|
+
}
|
|
2656
|
+
}
|
|
2657
|
+
function getBuildToneStyle(tone) {
|
|
2658
|
+
switch (tone) {
|
|
2659
|
+
case "complete":
|
|
2660
|
+
return {
|
|
2661
|
+
backgroundColor: "color-mix(in srgb, var(--color-success) 16%, transparent)",
|
|
2662
|
+
borderColor: "color-mix(in srgb, var(--color-success) 34%, var(--color-border))",
|
|
2663
|
+
color: "var(--color-success)"
|
|
2664
|
+
};
|
|
2665
|
+
case "failed":
|
|
2666
|
+
return {
|
|
2667
|
+
backgroundColor: "color-mix(in srgb, var(--color-error) 14%, transparent)",
|
|
2668
|
+
borderColor: "color-mix(in srgb, var(--color-error) 38%, var(--color-border))",
|
|
2669
|
+
color: "var(--color-error)"
|
|
2670
|
+
};
|
|
2671
|
+
case "ready":
|
|
2672
|
+
return {
|
|
2673
|
+
backgroundColor: "color-mix(in srgb, var(--color-primary) 14%, transparent)",
|
|
2674
|
+
borderColor: "color-mix(in srgb, var(--color-primary) 34%, var(--color-border))",
|
|
2675
|
+
color: "var(--color-primary)"
|
|
2676
|
+
};
|
|
2677
|
+
case "blocked":
|
|
2678
|
+
case "neutral":
|
|
2679
|
+
default:
|
|
2680
|
+
return {
|
|
2681
|
+
backgroundColor: "color-mix(in srgb, var(--color-surface-hover) 70%, transparent)",
|
|
2682
|
+
borderColor: "var(--color-border)",
|
|
2683
|
+
color: "var(--color-text-dimmed)"
|
|
2684
|
+
};
|
|
2685
|
+
}
|
|
2686
|
+
}
|
|
2687
|
+
function StatusPill({ label, tone }) {
|
|
2688
|
+
return /* @__PURE__ */ jsx(
|
|
2689
|
+
Box,
|
|
2690
|
+
{
|
|
2691
|
+
component: "span",
|
|
2692
|
+
px: 8,
|
|
2693
|
+
py: 3,
|
|
2694
|
+
style: {
|
|
2695
|
+
...getBuildToneStyle(tone),
|
|
2696
|
+
borderRadius: 999,
|
|
2697
|
+
borderStyle: "solid",
|
|
2698
|
+
borderWidth: 1,
|
|
2699
|
+
display: "inline-flex",
|
|
2700
|
+
fontSize: 11,
|
|
2701
|
+
fontWeight: 700,
|
|
2702
|
+
lineHeight: 1,
|
|
2703
|
+
whiteSpace: "nowrap"
|
|
2704
|
+
},
|
|
2705
|
+
children: label
|
|
2706
|
+
}
|
|
2707
|
+
);
|
|
2708
|
+
}
|
|
2709
|
+
function CountBadge({ label, value, tone }) {
|
|
2710
|
+
const toneStyle = getBuildToneStyle(tone);
|
|
2711
|
+
return /* @__PURE__ */ jsxs(
|
|
2712
|
+
Box,
|
|
2713
|
+
{
|
|
2714
|
+
p: "xs",
|
|
2715
|
+
style: {
|
|
2716
|
+
backgroundColor: "var(--color-surface)",
|
|
2717
|
+
border: "1px solid var(--color-border)",
|
|
2718
|
+
borderRadius: 8
|
|
2719
|
+
},
|
|
2720
|
+
children: [
|
|
2721
|
+
/* @__PURE__ */ jsx(Text, { size: "lg", fw: 700, style: { color: toneStyle.color }, children: value }),
|
|
2722
|
+
/* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", tt: "uppercase", fw: 700, children: label })
|
|
2723
|
+
]
|
|
2724
|
+
}
|
|
2725
|
+
);
|
|
2726
|
+
}
|
|
2727
|
+
function BuildStepProgressBar({ step }) {
|
|
2728
|
+
const segments = [
|
|
2729
|
+
{ key: "complete", value: step.complete, color: "var(--color-success)" },
|
|
2730
|
+
{ key: "ready", value: step.ready, color: "var(--color-primary)" },
|
|
2731
|
+
{ key: "failed", value: step.failed, color: "var(--color-error)" },
|
|
2732
|
+
{ key: "blocked", value: step.blocked, color: "var(--color-border)" }
|
|
2733
|
+
].filter((segment) => segment.value > 0);
|
|
2734
|
+
const total = segments.reduce((sum, segment) => sum + segment.value, 0);
|
|
2735
|
+
return /* @__PURE__ */ jsx(
|
|
2736
|
+
Box,
|
|
2737
|
+
{
|
|
2738
|
+
h: 8,
|
|
2739
|
+
"aria-label": `${step.label} progress: ${step.complete} complete, ${step.ready} ready, ${step.failed} failed, ${step.blocked} blocked`,
|
|
2740
|
+
style: {
|
|
2741
|
+
backgroundColor: "color-mix(in srgb, var(--color-border) 65%, transparent)",
|
|
2742
|
+
borderRadius: 999,
|
|
2743
|
+
display: "flex",
|
|
2744
|
+
overflow: "hidden"
|
|
2745
|
+
},
|
|
2746
|
+
children: segments.map((segment) => /* @__PURE__ */ jsx(Box, { h: "100%", w: `${total > 0 ? segment.value / total * 100 : 0}%`, bg: segment.color }, segment.key))
|
|
2747
|
+
}
|
|
2748
|
+
);
|
|
2749
|
+
}
|
|
2750
|
+
function pluralize(value, singular, plural = `${singular}s`) {
|
|
2751
|
+
return `${value} ${value === 1 ? singular : plural}`;
|
|
2752
|
+
}
|
|
2753
|
+
function getStepEntityLabel(step, value) {
|
|
2754
|
+
if (step.primaryEntity === "company") return pluralize(value, "company", "companies");
|
|
2755
|
+
if (step.primaryEntity === "contact") return pluralize(value, "contact");
|
|
2756
|
+
return pluralize(value, "record");
|
|
2757
|
+
}
|
|
2758
|
+
function getStepActionSummary(step) {
|
|
2759
|
+
if (step.failed > 0) {
|
|
2760
|
+
return `${getStepEntityLabel(step, step.failed)} ${step.failed === 1 ? "needs" : "need"} retry before the next step can continue.`;
|
|
2761
|
+
}
|
|
2762
|
+
if (step.ready > 0) {
|
|
2763
|
+
return `${getStepEntityLabel(step, step.ready)} ${step.ready === 1 ? "is" : "are"} ready for the next bounded run.`;
|
|
2764
|
+
}
|
|
2765
|
+
if (step.blocked > 0) return step.emptyBlockedText;
|
|
2766
|
+
if (step.complete > 0) return `${step.label} is complete for the current cohort.`;
|
|
2767
|
+
return "No runnable records are available for this step.";
|
|
2768
|
+
}
|
|
2769
|
+
function getStepCompactSummary(step) {
|
|
2770
|
+
const parts = [
|
|
2771
|
+
step.complete > 0 ? `${step.complete} complete` : null,
|
|
2772
|
+
step.ready > 0 ? `${step.ready} ready` : null,
|
|
2773
|
+
step.failed > 0 ? `${step.failed} failed` : null,
|
|
2774
|
+
step.blocked > 0 ? `${step.blocked} waiting` : null
|
|
2775
|
+
].filter(Boolean);
|
|
2776
|
+
return parts.length ? parts.join(" | ") : "No records yet";
|
|
2777
|
+
}
|
|
2778
|
+
function getDisplayStep(steps, recommendedStep) {
|
|
2779
|
+
return recommendedStep ?? steps.find((step) => step.status !== "complete") ?? steps[steps.length - 1] ?? null;
|
|
2780
|
+
}
|
|
2781
|
+
function BuildTab({
|
|
2782
|
+
steps,
|
|
2783
|
+
recommendedStep,
|
|
2784
|
+
onRunStep,
|
|
2785
|
+
onRetryStep,
|
|
2786
|
+
onViewRuns
|
|
2787
|
+
}) {
|
|
2788
|
+
const [selectedStepId, setSelectedStepId] = useState(null);
|
|
2789
|
+
const currentStep = getDisplayStep(steps, recommendedStep);
|
|
2790
|
+
const selectedStep = steps.find((step) => step.id === selectedStepId) ?? currentStep;
|
|
2791
|
+
const currentStepIndex = currentStep ? steps.findIndex((step) => step.id === currentStep.id) : -1;
|
|
2792
|
+
const selectedStepIndex = selectedStep ? steps.findIndex((step) => step.id === selectedStep.id) : -1;
|
|
2793
|
+
const selectedActionKind = selectedStep?.nextActionKind ?? "none";
|
|
2794
|
+
const canRunSelectedAction = !!selectedStep?.action && (selectedActionKind === "retry_failed" || selectedActionKind === "run_next_batch");
|
|
2795
|
+
const selectedActionIcon = selectedActionKind === "retry_failed" ? /* @__PURE__ */ jsx(IconRefresh, { size: 14 }) : /* @__PURE__ */ jsx(IconPlayerPlay, { size: 14 });
|
|
2796
|
+
const handleSelectedAction = () => {
|
|
2797
|
+
if (!selectedStep || !canRunSelectedAction) return;
|
|
2798
|
+
if (selectedActionKind === "retry_failed") {
|
|
2799
|
+
onRetryStep(selectedStep);
|
|
2800
|
+
return;
|
|
2801
|
+
}
|
|
2802
|
+
onRunStep(selectedStep);
|
|
2803
|
+
};
|
|
2804
|
+
return /* @__PURE__ */ jsxs(Stack, { gap: "md", p: "md", children: [
|
|
2805
|
+
/* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
|
|
2806
|
+
/* @__PURE__ */ jsx(CardHeader, { icon: /* @__PURE__ */ jsx(IconBolt, { size: 16 }), title: "Build" }),
|
|
2807
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: currentStep ? `${currentStep.label} is the current step in this list build.` : "No build steps are available for this list." })
|
|
2808
|
+
] }),
|
|
2809
|
+
currentStep ? /* @__PURE__ */ jsx(Card, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { gap: "xs", style: { minWidth: 0 }, children: [
|
|
2810
|
+
/* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
|
|
2811
|
+
/* @__PURE__ */ jsx(
|
|
2812
|
+
StatusPill,
|
|
2813
|
+
{
|
|
2814
|
+
label: `Step ${currentStepIndex + 1} of ${steps.length}`,
|
|
2815
|
+
tone: currentStep.status === "failed" ? "failed" : "ready"
|
|
2816
|
+
}
|
|
2817
|
+
),
|
|
2818
|
+
/* @__PURE__ */ jsx(StatusPill, { label: getBuildStatusLabel(currentStep.status), tone: currentStep.status })
|
|
2819
|
+
] }),
|
|
2820
|
+
/* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
|
|
2821
|
+
/* @__PURE__ */ jsx(Title, { order: 4, children: currentStep.label }),
|
|
2822
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: getStepActionSummary(currentStep) }),
|
|
2823
|
+
currentStep.recommendedAction?.defaultSize !== void 0 && currentStep.recommendedAction.maxSize !== void 0 ? /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
|
|
2824
|
+
"Runs up to ",
|
|
2825
|
+
currentStep.recommendedAction.defaultSize,
|
|
2826
|
+
" by default,",
|
|
2827
|
+
" ",
|
|
2828
|
+
currentStep.recommendedAction.maxSize,
|
|
2829
|
+
" max."
|
|
2830
|
+
] }) : null,
|
|
2831
|
+
!currentStep.action && (currentStep.ready > 0 || currentStep.failed > 0) ? /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "No workflow is mapped for this step yet." }) : null
|
|
2832
|
+
] })
|
|
2833
|
+
] }) }) : null,
|
|
2834
|
+
/* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 1, md: 2 }, spacing: "md", children: [
|
|
2835
|
+
/* @__PURE__ */ jsx(Stack, { gap: "xs", children: steps.map((step, index) => {
|
|
2836
|
+
const isCurrent = currentStep?.id === step.id;
|
|
2837
|
+
const isSelected = selectedStep?.id === step.id;
|
|
2838
|
+
const isWaiting = step.status === "blocked";
|
|
2839
|
+
const isPassiveWaiting = isWaiting && !isSelected && !isCurrent;
|
|
2840
|
+
const stepTone = isSelected || isCurrent ? step.status : step.status === "complete" || isWaiting ? step.status : "neutral";
|
|
2841
|
+
return /* @__PURE__ */ jsx(
|
|
2842
|
+
UnstyledButton,
|
|
2843
|
+
{
|
|
2844
|
+
onClick: () => setSelectedStepId(step.id),
|
|
2845
|
+
style: {
|
|
2846
|
+
backgroundColor: isSelected ? "var(--active-background)" : isPassiveWaiting ? "color-mix(in srgb, var(--color-surface-hover) 38%, transparent)" : "transparent",
|
|
2847
|
+
border: isSelected ? "var(--active-border)" : "1px solid var(--color-border)",
|
|
2848
|
+
borderStyle: isPassiveWaiting ? "dashed" : "solid",
|
|
2849
|
+
borderRadius: 8,
|
|
2850
|
+
display: "block",
|
|
2851
|
+
opacity: isPassiveWaiting ? 0.72 : 1,
|
|
2852
|
+
padding: 12,
|
|
2853
|
+
textAlign: "left",
|
|
2854
|
+
width: "100%"
|
|
2855
|
+
},
|
|
2856
|
+
children: /* @__PURE__ */ jsxs(Group, { gap: "sm", align: "flex-start", wrap: "nowrap", children: [
|
|
2857
|
+
/* @__PURE__ */ jsx(
|
|
2858
|
+
Box,
|
|
2859
|
+
{
|
|
2860
|
+
w: 28,
|
|
2861
|
+
h: 28,
|
|
2862
|
+
style: {
|
|
2863
|
+
alignItems: "center",
|
|
2864
|
+
...getBuildToneStyle(stepTone),
|
|
2865
|
+
borderRadius: 999,
|
|
2866
|
+
borderStyle: "solid",
|
|
2867
|
+
borderWidth: 1,
|
|
2868
|
+
display: "flex",
|
|
2869
|
+
flexShrink: 0,
|
|
2870
|
+
fontWeight: 700,
|
|
2871
|
+
justifyContent: "center"
|
|
2872
|
+
},
|
|
2873
|
+
children: index + 1
|
|
2874
|
+
}
|
|
2875
|
+
),
|
|
2876
|
+
/* @__PURE__ */ jsxs(Stack, { gap: 3, style: { minWidth: 0, flex: 1 }, children: [
|
|
2877
|
+
/* @__PURE__ */ jsxs(Group, { gap: "xs", justify: "space-between", wrap: "nowrap", children: [
|
|
2878
|
+
/* @__PURE__ */ jsx(Text, { fw: 700, c: isPassiveWaiting ? "dimmed" : void 0, truncate: true, children: step.label }),
|
|
2879
|
+
isCurrent ? /* @__PURE__ */ jsx(StatusPill, { label: "Current", tone: step.status }) : null,
|
|
2880
|
+
!isCurrent && isWaiting ? /* @__PURE__ */ jsx(StatusPill, { label: "Waiting", tone: "blocked" }) : null
|
|
2881
|
+
] }),
|
|
2882
|
+
/* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: getStepCompactSummary(step) })
|
|
2883
|
+
] })
|
|
2884
|
+
] })
|
|
2885
|
+
},
|
|
2886
|
+
step.id
|
|
2887
|
+
);
|
|
2888
|
+
}) }),
|
|
2889
|
+
selectedStep ? /* @__PURE__ */ jsx(Card, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
|
|
2890
|
+
/* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "flex-start", gap: "sm", children: [
|
|
2891
|
+
/* @__PURE__ */ jsxs(Stack, { gap: 2, style: { minWidth: 0 }, children: [
|
|
2892
|
+
/* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", fw: 700, tt: "uppercase", children: [
|
|
2893
|
+
"Step ",
|
|
2894
|
+
selectedStepIndex + 1,
|
|
2895
|
+
" details"
|
|
2896
|
+
] }),
|
|
2897
|
+
/* @__PURE__ */ jsx(Title, { order: 5, children: selectedStep.label })
|
|
2898
|
+
] }),
|
|
2899
|
+
/* @__PURE__ */ jsx(StatusPill, { label: getBuildStatusLabel(selectedStep.status), tone: selectedStep.status })
|
|
2900
|
+
] }),
|
|
2901
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: selectedStep.description }),
|
|
2902
|
+
selectedStep.status === "blocked" ? /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: selectedStep.emptyBlockedText }) : null,
|
|
2903
|
+
/* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 2, sm: 4 }, spacing: "xs", children: [
|
|
2904
|
+
/* @__PURE__ */ jsx(CountBadge, { label: "Ready", value: selectedStep.ready, tone: "ready" }),
|
|
2905
|
+
/* @__PURE__ */ jsx(CountBadge, { label: "Complete", value: selectedStep.complete, tone: "complete" }),
|
|
2906
|
+
/* @__PURE__ */ jsx(CountBadge, { label: "Failed", value: selectedStep.failed, tone: "failed" }),
|
|
2907
|
+
/* @__PURE__ */ jsx(CountBadge, { label: "Waiting", value: selectedStep.blocked, tone: "blocked" })
|
|
2908
|
+
] }),
|
|
2909
|
+
/* @__PURE__ */ jsx(BuildStepProgressBar, { step: selectedStep }),
|
|
2910
|
+
selectedStep.recommendedAction?.defaultSize !== void 0 && selectedStep.recommendedAction.maxSize !== void 0 ? /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
|
|
2911
|
+
"Batch size: ",
|
|
2912
|
+
selectedStep.recommendedAction.defaultSize,
|
|
2913
|
+
" default,",
|
|
2914
|
+
" ",
|
|
2915
|
+
selectedStep.recommendedAction.maxSize,
|
|
2916
|
+
" max."
|
|
2917
|
+
] }) : null,
|
|
2918
|
+
/* @__PURE__ */ jsxs(Group, { gap: "xs", justify: "flex-end", wrap: "nowrap", children: [
|
|
2919
|
+
/* @__PURE__ */ jsx(
|
|
2920
|
+
Button,
|
|
2921
|
+
{
|
|
2922
|
+
leftSection: selectedActionIcon,
|
|
2923
|
+
disabled: !canRunSelectedAction,
|
|
2924
|
+
onClick: handleSelectedAction,
|
|
2925
|
+
children: selectedActionKind === "none" ? "No action" : getStepActionLabel(selectedActionKind)
|
|
2926
|
+
}
|
|
2927
|
+
),
|
|
2928
|
+
/* @__PURE__ */ jsx(Button, { variant: "light", leftSection: /* @__PURE__ */ jsx(IconPlayerPlay, { size: 14 }), onClick: onViewRuns, children: "View runs" })
|
|
2929
|
+
] })
|
|
2930
|
+
] }) }) : null
|
|
2931
|
+
] })
|
|
2932
|
+
] });
|
|
2933
|
+
}
|
|
1287
2934
|
function MembersTab({
|
|
1288
2935
|
listId,
|
|
1289
2936
|
progress,
|
|
@@ -1294,7 +2941,7 @@ function MembersTab({
|
|
|
1294
2941
|
const companiesQuery = useCompanies({ listId, limit: 100, offset: 0 });
|
|
1295
2942
|
const contacts = contactsQuery.data?.data ?? [];
|
|
1296
2943
|
const companies = companiesQuery.data?.data ?? [];
|
|
1297
|
-
return /* @__PURE__ */ jsxs(
|
|
2944
|
+
return /* @__PURE__ */ jsxs(Stack, { gap: "sm", p: "md", children: [
|
|
1298
2945
|
/* @__PURE__ */ jsx(
|
|
1299
2946
|
CardHeader,
|
|
1300
2947
|
{
|
|
@@ -1332,9 +2979,9 @@ function MembersTab({
|
|
|
1332
2979
|
/* @__PURE__ */ jsx(Table.Td, { children: contact.email }),
|
|
1333
2980
|
/* @__PURE__ */ jsx(Table.Td, { children: contact.title ?? "\u2014" }),
|
|
1334
2981
|
/* @__PURE__ */ jsx(Table.Td, { children: contact.company?.name ?? "\u2014" }),
|
|
1335
|
-
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color:
|
|
2982
|
+
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: getStatusColor4(contact.status), children: contact.status }) }),
|
|
1336
2983
|
/* @__PURE__ */ jsx(Table.Td, { children: memberState ? /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "dot", color: getMemberStateColor2(memberState), children: memberState }) : /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "\u2014" }) }),
|
|
1337
|
-
/* @__PURE__ */ jsx(Table.Td, { children:
|
|
2984
|
+
/* @__PURE__ */ jsx(Table.Td, { children: formatDateTime4(contact.createdAt) })
|
|
1338
2985
|
] }, contact.id);
|
|
1339
2986
|
}) })
|
|
1340
2987
|
] }) }),
|
|
@@ -1356,66 +3003,14 @@ function MembersTab({
|
|
|
1356
3003
|
/* @__PURE__ */ jsx(Table.Td, { children: company.domain ?? "\u2014" }),
|
|
1357
3004
|
/* @__PURE__ */ jsx(Table.Td, { children: company.segment ?? "\u2014" }),
|
|
1358
3005
|
/* @__PURE__ */ jsx(Table.Td, { children: company.contactCount }),
|
|
1359
|
-
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color:
|
|
3006
|
+
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: getStatusColor4(company.status), children: company.status }) }),
|
|
1360
3007
|
/* @__PURE__ */ jsx(Table.Td, { children: memberState ? /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "dot", color: getMemberStateColor2(memberState), children: memberState }) : /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "\u2014" }) }),
|
|
1361
|
-
/* @__PURE__ */ jsx(Table.Td, { children:
|
|
3008
|
+
/* @__PURE__ */ jsx(Table.Td, { children: formatDateTime4(company.createdAt) })
|
|
1362
3009
|
] }, company.id);
|
|
1363
3010
|
}) })
|
|
1364
3011
|
] }) })
|
|
1365
3012
|
] });
|
|
1366
3013
|
}
|
|
1367
|
-
function ArtifactsTab({ listId }) {
|
|
1368
|
-
const { data, isLoading, error } = useArtifacts({ ownerKind: "list", ownerId: listId });
|
|
1369
|
-
const artifacts = data?.artifacts ?? [];
|
|
1370
|
-
if (isLoading) {
|
|
1371
|
-
return /* @__PURE__ */ jsx(Center, { p: "xl", children: /* @__PURE__ */ jsx(Loader, {}) });
|
|
1372
|
-
}
|
|
1373
|
-
if (error) {
|
|
1374
|
-
return /* @__PURE__ */ jsx(CenteredErrorState, { error, title: "Failed to load artifacts" });
|
|
1375
|
-
}
|
|
1376
|
-
if (!artifacts.length) {
|
|
1377
|
-
return /* @__PURE__ */ jsx(Alert, { icon: /* @__PURE__ */ jsx(IconDatabase, { size: 16 }), color: "gray", variant: "light", children: "No artifacts recorded for this list yet." });
|
|
1378
|
-
}
|
|
1379
|
-
return /* @__PURE__ */ jsx(Stack, { gap: "sm", children: artifacts.map((artifact) => /* @__PURE__ */ jsx(Card, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
|
|
1380
|
-
/* @__PURE__ */ jsxs(Group, { justify: "space-between", children: [
|
|
1381
|
-
/* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
|
|
1382
|
-
/* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: "blue", children: artifact.kind }),
|
|
1383
|
-
/* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
|
|
1384
|
-
"v",
|
|
1385
|
-
artifact.version
|
|
1386
|
-
] })
|
|
1387
|
-
] }),
|
|
1388
|
-
/* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: formatDateTime2(artifact.createdAt) })
|
|
1389
|
-
] }),
|
|
1390
|
-
/* @__PURE__ */ jsx(JsonViewer, { data: artifact.content, maxHeight: 240 })
|
|
1391
|
-
] }) }, artifact.id)) });
|
|
1392
|
-
}
|
|
1393
|
-
function ExecutionHistorySection({
|
|
1394
|
-
executions
|
|
1395
|
-
}) {
|
|
1396
|
-
if (!executions.length) {
|
|
1397
|
-
return /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "No executions recorded for this list yet." });
|
|
1398
|
-
}
|
|
1399
|
-
return /* @__PURE__ */ jsxs(Table, { children: [
|
|
1400
|
-
/* @__PURE__ */ jsx(Table.Thead, { children: /* @__PURE__ */ jsxs(Table.Tr, { children: [
|
|
1401
|
-
/* @__PURE__ */ jsx(Table.Th, { children: "Resource" }),
|
|
1402
|
-
/* @__PURE__ */ jsx(Table.Th, { children: "Status" }),
|
|
1403
|
-
/* @__PURE__ */ jsx(Table.Th, { children: "Started" }),
|
|
1404
|
-
/* @__PURE__ */ jsx(Table.Th, { children: "Completed" }),
|
|
1405
|
-
/* @__PURE__ */ jsx(Table.Th, { children: "Duration" })
|
|
1406
|
-
] }) }),
|
|
1407
|
-
/* @__PURE__ */ jsx(Table.Tbody, { children: executions.map((execution) => /* @__PURE__ */ jsxs(Table.Tr, { children: [
|
|
1408
|
-
/* @__PURE__ */ jsxs(Table.Td, { children: [
|
|
1409
|
-
/* @__PURE__ */ jsx(Text, { fw: 500, children: execution.resourceId }),
|
|
1410
|
-
/* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: execution.executionId })
|
|
1411
|
-
] }),
|
|
1412
|
-
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: getStatusColor2(execution.status), children: execution.status }) }),
|
|
1413
|
-
/* @__PURE__ */ jsx(Table.Td, { children: formatDateTime2(execution.createdAt) }),
|
|
1414
|
-
/* @__PURE__ */ jsx(Table.Td, { children: formatDateTime2(execution.completedAt) }),
|
|
1415
|
-
/* @__PURE__ */ jsx(Table.Td, { children: execution.durationMs == null ? "n/a" : `${execution.durationMs} ms` })
|
|
1416
|
-
] }, execution.executionId)) })
|
|
1417
|
-
] });
|
|
1418
|
-
}
|
|
1419
3014
|
function ListDetailHeader({
|
|
1420
3015
|
title,
|
|
1421
3016
|
caption,
|
|
@@ -1425,6 +3020,7 @@ function ListDetailHeader({
|
|
|
1425
3020
|
}
|
|
1426
3021
|
function LeadGenListDetailPage({ listId }) {
|
|
1427
3022
|
const navigate = useNavigate();
|
|
3023
|
+
const actions = useListActions();
|
|
1428
3024
|
const rawSearch = useSearch({ strict: false });
|
|
1429
3025
|
const memberId = rawSearch.member ?? null;
|
|
1430
3026
|
const memberKind = rawSearch.memberKind ?? null;
|
|
@@ -1443,11 +3039,16 @@ function LeadGenListDetailPage({ listId }) {
|
|
|
1443
3039
|
};
|
|
1444
3040
|
const listQuery = useList(listId);
|
|
1445
3041
|
const progressQuery = useListProgress(listId);
|
|
1446
|
-
const executionsQuery = useListExecutions(listId);
|
|
1447
3042
|
const deleteListMutation = useDeleteList();
|
|
3043
|
+
const updateListMutation = useUpdateList(listId);
|
|
1448
3044
|
const [deleteModalOpen, setDeleteModalOpen] = useState(false);
|
|
1449
|
-
const
|
|
1450
|
-
const
|
|
3045
|
+
const [pipelineModalOpen, setPipelineModalOpen] = useState(false);
|
|
3046
|
+
const [selectedBuildTemplateId, setSelectedBuildTemplateId] = useState(DEFAULT_PROSPECTING_BUILD_TEMPLATE_ID);
|
|
3047
|
+
const [runModalOpen, setRunModalOpen] = useState(false);
|
|
3048
|
+
const [activeTab, setActiveTab] = useState("overview");
|
|
3049
|
+
const [initialRunResourceId, setInitialRunResourceId] = useState(null);
|
|
3050
|
+
const isLoading = listQuery.isLoading || progressQuery.isLoading;
|
|
3051
|
+
const error = listQuery.error ?? progressQuery.error;
|
|
1451
3052
|
const backButton = /* @__PURE__ */ jsx(
|
|
1452
3053
|
Button,
|
|
1453
3054
|
{
|
|
@@ -1499,7 +3100,27 @@ function LeadGenListDetailPage({ listId }) {
|
|
|
1499
3100
|
}
|
|
1500
3101
|
const list = listQuery.data;
|
|
1501
3102
|
const progress = progressQuery.data;
|
|
1502
|
-
const
|
|
3103
|
+
const currentBuildTemplateId = list.metadata.buildPlanSnapshot?.templateId ?? DEFAULT_PROSPECTING_BUILD_TEMPLATE_ID;
|
|
3104
|
+
const currentBuildTemplateLabel = list.metadata.buildPlanSnapshot?.templateLabel ?? "Default lead generation";
|
|
3105
|
+
const selectedBuildTemplate = PROSPECTING_BUILD_TEMPLATE_OPTIONS.find(
|
|
3106
|
+
(template) => template.id === selectedBuildTemplateId
|
|
3107
|
+
);
|
|
3108
|
+
const isSameBuildTemplate = selectedBuildTemplateId === currentBuildTemplateId;
|
|
3109
|
+
const buildResolution = resolveBuildState(list, progress, actions);
|
|
3110
|
+
const buildSteps = buildResolution.steps;
|
|
3111
|
+
const recommendedBuildStep = buildResolution.recommendedStep;
|
|
3112
|
+
const openRunModal = (resourceId) => {
|
|
3113
|
+
const nextResourceId = resourceId ?? actions[0]?.resourceId ?? null;
|
|
3114
|
+
setInitialRunResourceId(nextResourceId);
|
|
3115
|
+
setRunModalOpen(true);
|
|
3116
|
+
};
|
|
3117
|
+
const closeRunModal = () => {
|
|
3118
|
+
setRunModalOpen(false);
|
|
3119
|
+
setInitialRunResourceId(null);
|
|
3120
|
+
};
|
|
3121
|
+
const openStepRun = (step) => {
|
|
3122
|
+
openRunModal(step.action?.resourceId ?? null);
|
|
3123
|
+
};
|
|
1503
3124
|
const handleCopyListCommand = () => {
|
|
1504
3125
|
void navigator.clipboard.writeText(`/acquisition --lead-gen list ${list.id}`);
|
|
1505
3126
|
showSuccessNotification("Copied list command to clipboard");
|
|
@@ -1512,6 +3133,29 @@ function LeadGenListDetailPage({ listId }) {
|
|
|
1512
3133
|
}
|
|
1513
3134
|
});
|
|
1514
3135
|
};
|
|
3136
|
+
const openPipelineModal = () => {
|
|
3137
|
+
setSelectedBuildTemplateId(currentBuildTemplateId);
|
|
3138
|
+
setPipelineModalOpen(true);
|
|
3139
|
+
};
|
|
3140
|
+
const closePipelineModal = () => {
|
|
3141
|
+
if (updateListMutation.isPending) return;
|
|
3142
|
+
setPipelineModalOpen(false);
|
|
3143
|
+
setSelectedBuildTemplateId(currentBuildTemplateId);
|
|
3144
|
+
};
|
|
3145
|
+
const handleChangePipeline = () => {
|
|
3146
|
+
if (isSameBuildTemplate) return;
|
|
3147
|
+
updateListMutation.mutate(
|
|
3148
|
+
{
|
|
3149
|
+
buildTemplateId: selectedBuildTemplateId,
|
|
3150
|
+
confirmBuildTemplateChange: true
|
|
3151
|
+
},
|
|
3152
|
+
{
|
|
3153
|
+
onSuccess: () => {
|
|
3154
|
+
setPipelineModalOpen(false);
|
|
3155
|
+
}
|
|
3156
|
+
}
|
|
3157
|
+
);
|
|
3158
|
+
};
|
|
1515
3159
|
return /* @__PURE__ */ jsxs(SubshellContentContainer, { children: [
|
|
1516
3160
|
/* @__PURE__ */ jsx(PageContainer, { children: /* @__PURE__ */ jsxs(Stack, { children: [
|
|
1517
3161
|
/* @__PURE__ */ jsx(
|
|
@@ -1523,13 +3167,24 @@ function LeadGenListDetailPage({ listId }) {
|
|
|
1523
3167
|
),
|
|
1524
3168
|
/* @__PURE__ */ jsxs(Group, { justify: "space-between", gap: "xs", wrap: "nowrap", children: [
|
|
1525
3169
|
/* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
|
|
1526
|
-
/* @__PURE__ */ jsx(Badge, { size: "sm", variant: "filled", color:
|
|
3170
|
+
/* @__PURE__ */ jsx(Badge, { size: "sm", variant: "filled", color: getStatusColor4(list.status), children: list.status }),
|
|
3171
|
+
/* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: "violet", children: currentBuildTemplateLabel }),
|
|
1527
3172
|
/* @__PURE__ */ jsxs(Text, { size: "sm", c: "dimmed", children: [
|
|
1528
3173
|
"Created ",
|
|
1529
|
-
|
|
3174
|
+
formatDateTime4(list.createdAt)
|
|
1530
3175
|
] })
|
|
1531
3176
|
] }),
|
|
1532
3177
|
/* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
|
|
3178
|
+
/* @__PURE__ */ jsx(
|
|
3179
|
+
Button,
|
|
3180
|
+
{
|
|
3181
|
+
size: "xs",
|
|
3182
|
+
variant: "light",
|
|
3183
|
+
leftSection: /* @__PURE__ */ jsx(IconSwitchHorizontal, { size: 14 }),
|
|
3184
|
+
onClick: openPipelineModal,
|
|
3185
|
+
children: "Change pipeline"
|
|
3186
|
+
}
|
|
3187
|
+
),
|
|
1533
3188
|
/* @__PURE__ */ jsxs(Group, { gap: 4, wrap: "nowrap", onClick: handleCopyListCommand, style: { cursor: "pointer" }, children: [
|
|
1534
3189
|
/* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", ff: "monospace", children: list.id }),
|
|
1535
3190
|
/* @__PURE__ */ jsx(ActionIcon, { variant: "subtle", size: "sm", "aria-label": "Copy list command", children: /* @__PURE__ */ jsx(IconCopy, { size: 14 }) })
|
|
@@ -1549,22 +3204,72 @@ function LeadGenListDetailPage({ listId }) {
|
|
|
1549
3204
|
backButton
|
|
1550
3205
|
] })
|
|
1551
3206
|
] }),
|
|
1552
|
-
/* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsxs(Tabs, {
|
|
3207
|
+
/* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsxs(Tabs, { value: activeTab, onChange: setActiveTab, children: [
|
|
1553
3208
|
/* @__PURE__ */ jsxs(Tabs.List, { children: [
|
|
1554
3209
|
/* @__PURE__ */ jsx(Tabs.Tab, { value: "overview", leftSection: /* @__PURE__ */ jsx(IconBuilding, { size: 14 }), children: "Overview" }),
|
|
1555
3210
|
/* @__PURE__ */ jsx(Tabs.Tab, { value: "members", leftSection: /* @__PURE__ */ jsx(IconUsers, { size: 14 }), children: "Members" }),
|
|
1556
|
-
/* @__PURE__ */ jsx(Tabs.Tab, { value: "
|
|
3211
|
+
/* @__PURE__ */ jsx(Tabs.Tab, { value: "build", leftSection: /* @__PURE__ */ jsx(IconBolt, { size: 14 }), children: "Build" }),
|
|
3212
|
+
/* @__PURE__ */ jsx(Tabs.Tab, { value: "runs", leftSection: /* @__PURE__ */ jsx(IconPlayerPlay, { size: 14 }), children: "Runs" })
|
|
1557
3213
|
] }),
|
|
1558
3214
|
/* @__PURE__ */ jsx(Tabs.Panel, { value: "overview", pt: "sm", children: /* @__PURE__ */ jsx(OverviewTab, { list, progress }) }),
|
|
1559
3215
|
/* @__PURE__ */ jsx(Tabs.Panel, { value: "members", pt: "sm", children: /* @__PURE__ */ jsx(MembersTab, { listId, progress, onMemberClick: handleMemberClick }) }),
|
|
1560
|
-
/* @__PURE__ */ jsx(Tabs.Panel, { value: "
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
3216
|
+
/* @__PURE__ */ jsx(Tabs.Panel, { value: "build", pt: "sm", children: /* @__PURE__ */ jsx(
|
|
3217
|
+
BuildTab,
|
|
3218
|
+
{
|
|
3219
|
+
steps: buildSteps,
|
|
3220
|
+
recommendedStep: recommendedBuildStep,
|
|
3221
|
+
onRunStep: openStepRun,
|
|
3222
|
+
onRetryStep: openStepRun,
|
|
3223
|
+
onViewRuns: () => setActiveTab("runs")
|
|
3224
|
+
}
|
|
3225
|
+
) }),
|
|
3226
|
+
/* @__PURE__ */ jsx(Tabs.Panel, { value: "runs", pt: "sm", children: /* @__PURE__ */ jsx(WorkflowRunsPanel, { listId, title: "Runs" }) })
|
|
3227
|
+
] }) })
|
|
1566
3228
|
] }) }),
|
|
1567
3229
|
/* @__PURE__ */ jsx(ListMemberDrawer, { memberId, memberKind, listId, onClose: handleDrawerClose }),
|
|
3230
|
+
/* @__PURE__ */ jsx(
|
|
3231
|
+
CustomModal,
|
|
3232
|
+
{
|
|
3233
|
+
opened: pipelineModalOpen,
|
|
3234
|
+
onClose: closePipelineModal,
|
|
3235
|
+
size: "md",
|
|
3236
|
+
loading: updateListMutation.isPending,
|
|
3237
|
+
children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
|
|
3238
|
+
/* @__PURE__ */ jsxs(Group, { gap: "sm", children: [
|
|
3239
|
+
/* @__PURE__ */ jsx(IconSwitchHorizontal, { size: 22 }),
|
|
3240
|
+
/* @__PURE__ */ jsx(Title, { order: 4, children: "Change build pipeline" })
|
|
3241
|
+
] }),
|
|
3242
|
+
/* @__PURE__ */ jsx(Alert, { icon: /* @__PURE__ */ jsx(IconAlertCircle, { size: 16 }), color: "yellow", variant: "light", children: /* @__PURE__ */ jsx(Text, { size: "sm", children: "Changing the pipeline replaces this list's build plan snapshot. Existing records stay in place, but future build steps on the Build tab will follow the newly selected pipeline." }) }),
|
|
3243
|
+
/* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
|
|
3244
|
+
/* @__PURE__ */ jsx(
|
|
3245
|
+
Select,
|
|
3246
|
+
{
|
|
3247
|
+
label: "Build Pipeline",
|
|
3248
|
+
data: BUILD_TEMPLATE_SELECT_OPTIONS2,
|
|
3249
|
+
value: selectedBuildTemplateId,
|
|
3250
|
+
onChange: (value) => setSelectedBuildTemplateId(value ?? currentBuildTemplateId),
|
|
3251
|
+
disabled: updateListMutation.isPending,
|
|
3252
|
+
required: true
|
|
3253
|
+
}
|
|
3254
|
+
),
|
|
3255
|
+
selectedBuildTemplate?.description ? /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: selectedBuildTemplate.description }) : null
|
|
3256
|
+
] }),
|
|
3257
|
+
/* @__PURE__ */ jsxs(Group, { justify: "flex-end", mt: "md", children: [
|
|
3258
|
+
/* @__PURE__ */ jsx(Button, { variant: "light", onClick: closePipelineModal, disabled: updateListMutation.isPending, children: "Cancel" }),
|
|
3259
|
+
/* @__PURE__ */ jsx(
|
|
3260
|
+
Button,
|
|
3261
|
+
{
|
|
3262
|
+
color: "yellow",
|
|
3263
|
+
loading: updateListMutation.isPending,
|
|
3264
|
+
disabled: isSameBuildTemplate,
|
|
3265
|
+
onClick: handleChangePipeline,
|
|
3266
|
+
children: "Confirm pipeline change"
|
|
3267
|
+
}
|
|
3268
|
+
)
|
|
3269
|
+
] })
|
|
3270
|
+
] })
|
|
3271
|
+
}
|
|
3272
|
+
),
|
|
1568
3273
|
/* @__PURE__ */ jsx(
|
|
1569
3274
|
CustomModal,
|
|
1570
3275
|
{
|
|
@@ -1590,6 +3295,23 @@ function LeadGenListDetailPage({ listId }) {
|
|
|
1590
3295
|
] })
|
|
1591
3296
|
] })
|
|
1592
3297
|
}
|
|
3298
|
+
),
|
|
3299
|
+
/* @__PURE__ */ jsx(
|
|
3300
|
+
RunWorkflowModal,
|
|
3301
|
+
{
|
|
3302
|
+
opened: runModalOpen,
|
|
3303
|
+
onClose: closeRunModal,
|
|
3304
|
+
list,
|
|
3305
|
+
actions,
|
|
3306
|
+
selection: EMPTY_SELECTION2,
|
|
3307
|
+
initialResourceId: initialRunResourceId,
|
|
3308
|
+
title: "Run build step",
|
|
3309
|
+
description: "Run the selected bounded build workflow for this list.",
|
|
3310
|
+
lockResourceSelection: true,
|
|
3311
|
+
onSubmitted: (_resourceId, executionId) => {
|
|
3312
|
+
showSuccessNotification(`Workflow run started: ${executionId}`);
|
|
3313
|
+
}
|
|
3314
|
+
}
|
|
1593
3315
|
)
|
|
1594
3316
|
] });
|
|
1595
3317
|
}
|
|
@@ -1951,4 +3673,4 @@ function LeadGenContactsPage() {
|
|
|
1951
3673
|
] }) });
|
|
1952
3674
|
}
|
|
1953
3675
|
|
|
1954
|
-
export { CompanyDetailModal, ContactDetailModal, LEAD_GEN_ITEMS, LEAD_GEN_ROUTE_LINKS, LeadGenCompaniesPage, LeadGenContactsPage, LeadGenListDetailPage, LeadGenListsPage, LeadGenOverviewPage, LeadGenRouteShell, LeadGenSidebar, LeadGenSidebarMiddle, LeadGenSidebarTop, formatDate, getEnrichmentColor, getEnrichmentStatus, getStateKeyColor, getStatusColor, leadGenManifest, useDeleteLists };
|
|
3676
|
+
export { CompanyCleanupForm, CompanyDetailModal, ContactDetailModal, EmailDiscoveryForm, EmailVerificationForm, LEAD_GEN_ITEMS, LEAD_GEN_ROUTE_LINKS, LeadGenCompaniesPage, LeadGenContactsPage, LeadGenListDetailPage, LeadGenListsPage, LeadGenOverviewPage, LeadGenRouteShell, LeadGenSidebar, LeadGenSidebarMiddle, LeadGenSidebarTop, ListBuilderIndexPage, ListBuilderPage, RunWorkflowModal, WebsiteExtractForm, formatDate, getEnrichmentColor, getEnrichmentStatus, getStateKeyColor, getStatusColor, leadGenManifest, useDeleteLists };
|