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