@elevasis/ui 2.1.0 → 2.2.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.
@@ -1,30 +1,30 @@
1
1
  import { useBreadcrumbs } from '../chunk-MG3NF7QL.js';
2
2
  import '../chunk-SMJLS23U.js';
3
- import { NotificationList } from '../chunk-QRHLV74B.js';
4
- export { ActivityCard, ActivityFilters as ActivityFiltersBar, ActivityTable, BusinessImpactCard, CostBreakdownCard, CostByModelTable, CostMetricsCard, ErrorAnalysisCard, ErrorBreakdownTable, ExecutionBreakdownTable, ExecutionHealthCard, ExecutionLogsFilters as ExecutionLogsFilterBar, ExecutionLogsTable, NotificationItem, NotificationList, monitoringManifest } from '../chunk-QRHLV74B.js';
5
- export { CreateCredentialModal, CredentialList, CredentialSettings, MembershipFeaturePanel, MembershipStatusBadge, OAuthConnectModal, OrganizationMembershipsList, WebhookUrlDisplayModal, settingsManifest } from '../chunk-MVJ4TSSA.js';
3
+ import { NotificationList } from '../chunk-2JTCPVZX.js';
4
+ export { ActivityCard, ActivityFilters as ActivityFiltersBar, ActivityTable, BusinessImpactCard, CostBreakdownCard, CostByModelTable, CostMetricsCard, ErrorAnalysisCard, ErrorBreakdownTable, ExecutionBreakdownTable, ExecutionHealthCard, ExecutionLogsFilters as ExecutionLogsFilterBar, ExecutionLogsTable, NotificationItem, NotificationList, monitoringManifest } from '../chunk-2JTCPVZX.js';
5
+ export { CreateCredentialModal, CredentialList, CredentialSettings, MembershipFeaturePanel, MembershipStatusBadge, OAuthConnectModal, OrganizationMembershipsList, WebhookUrlDisplayModal, settingsManifest } from '../chunk-5KBVISVJ.js';
6
6
  import { FilterBar } from '../chunk-PDHTXPSF.js';
7
7
  export { FilterBar } from '../chunk-PDHTXPSF.js';
8
- import { ResourceExecuteDialog } from '../chunk-H762MTQ5.js';
9
- export { ActionModal, AgentDefinitionDisplay, AgentExecutionLogs, BaseExecutionLogs, BaseExecutionLogsHeader, BaseExecutionLogsStates, CheckpointGroup, CollapsibleJsonSection, CommandQueueSidebar, CommandQueueSidebarMiddle, CommandQueueSidebarTop, CommandQueueTaskRow, CommandViewEdge, CommandViewGraph, CommandViewNode, ConfigCard, ContentSections, ContextUsageBadge, ContractDisplay, ExecutionErrorSection, FormFieldRenderer, LogEntry, LogGroup, NewKnowledgeMapEdge, NewKnowledgeMapGraph, NewKnowledgeMapNode, OperationsSidebar, OperationsSidebarMiddle, OperationsSidebarTop, ResourceDefinitionSection, ResourceErrorState, ResourceFilter, ResourceHeader, ResourceNotFoundState, SessionMemory, ToolsListDisplay, WorkflowDefinitionDisplay, WorkflowExecutionLogs, getExecutionStatusConfig, getIcon, getLogLevelConfig, iconMap, operationsManifest, useNewKnowledgeMapLayout } from '../chunk-H762MTQ5.js';
8
+ import { ResourceExecuteDialog } from '../chunk-YOZEGIZA.js';
9
+ export { ActionModal, AgentDefinitionDisplay, AgentExecutionLogs, BaseExecutionLogs, BaseExecutionLogsHeader, BaseExecutionLogsStates, CheckpointGroup, CollapsibleJsonSection, CommandQueueSidebar, CommandQueueSidebarMiddle, CommandQueueSidebarTop, CommandQueueTaskRow, CommandViewEdge, CommandViewGraph, CommandViewNode, ConfigCard, ContentSections, ContextUsageBadge, ContractDisplay, ExecutionErrorSection, FormFieldRenderer, LogEntry, LogGroup, NewKnowledgeMapEdge, NewKnowledgeMapGraph, NewKnowledgeMapNode, OperationsSidebar, OperationsSidebarMiddle, OperationsSidebarTop, ResourceDefinitionSection, ResourceErrorState, ResourceFilter, ResourceHeader, ResourceNotFoundState, SessionMemory, ToolsListDisplay, WorkflowDefinitionDisplay, WorkflowExecutionLogs, getExecutionStatusConfig, getIcon, getLogLevelConfig, iconMap, operationsManifest, useNewKnowledgeMapLayout } from '../chunk-YOZEGIZA.js';
10
10
  import '../chunk-ROSMICXG.js';
11
11
  import { SubshellLoader, PageContainer, SubshellSidebarSection, SubshellNavItem, CollapsibleSidebarGroup } from '../chunk-OCP2MBTY.js';
12
- export { ResourceHealthPanel } from '../chunk-OKKGD3S6.js';
12
+ export { ResourceHealthPanel } from '../chunk-PFONCU6C.js';
13
13
  import { CustomModal } from '../chunk-GBMNCNHX.js';
14
14
  export { ConfirmationInputModal, ConfirmationModal, CustomModal } from '../chunk-GBMNCNHX.js';
15
- export { AgentExecutionTimeline, AgentExecutionVisualizer, AgentIterationEdge, AgentIterationNode, BaseEdge, BaseNode, EmptyVisualizer, ExecutionStats, ExecutionStatusBadge, GraphBackground, GraphContainer, GraphFitViewButton, GraphFitViewHandler, GraphLegend, TimelineAxis, TimelineBar, TimelineContainer, TimelineRow, UnifiedWorkflowEdge, UnifiedWorkflowGraph, UnifiedWorkflowNode, VisualizerContainer, WorkflowExecutionTimeline, dashboardManifest, getGraphBackgroundStyles, useGraphBackgroundStyles, useGraphTheme } from '../chunk-PQNEE57X.js';
15
+ export { AgentExecutionTimeline, AgentExecutionVisualizer, AgentIterationEdge, AgentIterationNode, BaseEdge, BaseNode, EmptyVisualizer, ExecutionStats, ExecutionStatusBadge, GraphBackground, GraphContainer, GraphFitViewButton, GraphFitViewHandler, GraphLegend, TimelineAxis, TimelineBar, TimelineContainer, TimelineRow, UnifiedWorkflowEdge, UnifiedWorkflowGraph, UnifiedWorkflowNode, VisualizerContainer, WorkflowExecutionTimeline, dashboardManifest, getGraphBackgroundStyles, useGraphBackgroundStyles, useGraphTheme } from '../chunk-O6KZ46EJ.js';
16
16
  export { ResourceHealthChart, getHealthColor } from '../chunk-LGKLC5MG.js';
17
17
  import '../chunk-KFICYU6S.js';
18
18
  import { AppShellLoader } from '../chunk-YEX4MQSY.js';
19
- import '../chunk-35QO7M43.js';
20
- import { useUpdateApiKey, useDeleteApiKey, useCreateApiKey, useListApiKeys, useActivateDeployment, useDeactivateDeployment, useDeleteDeployment, useListDeployments, useProjects } from '../chunk-PRLXFMNP.js';
21
- import { usePaginationState, useDeploymentDocs, useResources, useCreateSchedule, useListSchedules, usePauseSchedule, useResumeSchedule, useCancelSchedule, useDeleteSchedule, useDealNotes, useCreateDealNote, useDeals, useSyncDealStage, dealKeys, useDealTasksDue, useCreateDealTask, useDeleteDeal, useTableSort, sortData, useTableSelection, useDealDetail, showApiErrorNotification, acquisitionListKeys, showSuccessNotification, useListsTelemetry, useLists, useCreateList, useList, useListProgress, useListExecutions, useResourceDefinition, useDeleteProject, useMarkAllAsRead, useNotifications, showErrorNotification } from '../chunk-TZOGB3X4.js';
22
- export { showApiErrorNotification, showErrorNotification, showInfoNotification, showSuccessNotification, showWarningNotification } from '../chunk-TZOGB3X4.js';
19
+ import '../chunk-M66JAN7R.js';
20
+ import { useUpdateApiKey, useDeleteApiKey, useCreateApiKey, useListApiKeys, useActivateDeployment, useDeactivateDeployment, useDeleteDeployment, useListDeployments, useProjects } from '../chunk-JT3FN6TE.js';
21
+ import { usePaginationState, useDeploymentDocs, useResources, useCreateSchedule, useListSchedules, usePauseSchedule, useResumeSchedule, useCancelSchedule, useDeleteSchedule, useDealNotes, useCreateDealNote, useDeals, useSyncDealStage, dealKeys, useDealTasksDue, useCreateDealTask, useDeleteDeal, useTableSort, sortData, useTableSelection, useDealDetail, showApiErrorNotification, acquisitionListKeys, showSuccessNotification, useListsTelemetry, useLists, useCreateList, useList, useListProgress, useListExecutions, useResourceDefinition, useCompanies, useDeleteCompanies, useContacts, useDeleteContacts, useDeleteProject, useMarkAllAsRead, useNotifications, showErrorNotification } from '../chunk-J5TBNCMD.js';
22
+ export { showApiErrorNotification, showErrorNotification, showInfoNotification, showSuccessNotification, showWarningNotification } from '../chunk-J5TBNCMD.js';
23
23
  import '../chunk-LXHZYSMQ.js';
24
24
  export { Graph_module_css_default as graphStyles } from '../chunk-F6RBK7NJ.js';
25
25
  export { CONTAINER_CONSTANTS, SHARED_VIZ_CONSTANTS } from '../chunk-XA34RETF.js';
26
- import '../chunk-QITPFGWC.js';
27
- import '../chunk-RB34YOIX.js';
26
+ import '../chunk-LH7RCX4Y.js';
27
+ import '../chunk-3WMJWXZY.js';
28
28
  import '../chunk-CYXZHBP4.js';
29
29
  import '../chunk-MZPVNRPL.js';
30
30
  import { SubshellContainer, SubshellSidebar, SubshellRightSideContainer, SubshellContentContainer } from '../chunk-RX4UWZZR.js';
@@ -47,8 +47,8 @@ import '../chunk-DD3CCMCZ.js';
47
47
  import { useElevasisServices } from '../chunk-QEPXAWE2.js';
48
48
  import '../chunk-BRJ3QZ4E.js';
49
49
  import { useRouterContext } from '../chunk-Q7DJKLEN.js';
50
- import { Table, Group, Text, Button, Stack, Title, TextInput, Alert, Tooltip, ActionIcon, Paper, Code, CopyButton, SimpleGrid, Badge, Loader, Pagination, useMantineTheme, Box, ScrollArea, Select, Center, Card, SegmentedControl, Switch, Textarea, Divider, Menu, Timeline, ThemeIcon, Tabs, Anchor, Breadcrumbs as Breadcrumbs$1, Drawer, UnstyledButton, Modal, Checkbox, RingProgress, Collapse, Popover, Indicator } from '@mantine/core';
51
- import { IconAddressBook, IconBriefcase, IconTarget, IconChevronUp, IconChevronDown, IconSelector, IconTrash, IconPencil, IconAlertCircle, IconKey, IconCalendar, IconClock, IconAlertTriangle, IconExclamationMark, IconShieldLock, IconCheck, IconCopy, IconPlus, IconRocket, IconRefresh, IconPower, IconPlayerPlay, IconCircleCheck, IconTag, IconBook2, IconFileOff, IconList, IconCalendarRepeat, IconCalendarEvent, IconCalendarTime, IconRobot, IconGitBranch, IconSettings, IconExternalLink, IconDotsVertical, IconPlayerPause, IconPlayerStop, IconCalendarDue, IconCalendarStats, IconCalendarOff, IconListCheck, IconTrophy, IconClockExclamation, IconUser, IconLayoutGrid, IconColumns, IconFileInvoice, IconChecklist, IconHistory, IconSearch, IconTargetArrow, IconArrowLeft, IconFileText, IconX, IconBuilding, IconMailCheck, IconChartBar, IconBuildingFactory2, IconUsers, IconSparkles, IconClockHour4, IconTrendingUp, IconHeartbeat, IconFlag, IconInbox, IconLock, IconChevronRight, IconDownload, IconMessageCircle, IconBell, IconNotes, IconFolderOpen, IconFolder, IconCheckbox, IconMail, IconPhone, IconArrowRight, IconNote } from '@tabler/icons-react';
50
+ import { Table, Group, Text, Button, Stack, Title, TextInput, Alert, Tooltip, ActionIcon, Paper, Code, CopyButton, SimpleGrid, Badge, Loader, Pagination, useMantineTheme, Box, ScrollArea, Select, Center, Card, SegmentedControl, Switch, Textarea, Divider, Menu, Timeline, ThemeIcon, Tabs, Anchor, Breadcrumbs as Breadcrumbs$1, Drawer, UnstyledButton, Modal, Checkbox, Progress, RingProgress, Collapse, Popover, Indicator } from '@mantine/core';
51
+ import { IconAddressBook, IconBriefcase, IconTarget, IconChevronUp, IconChevronDown, IconSelector, IconTrash, IconPencil, IconAlertCircle, IconKey, IconCalendar, IconClock, IconAlertTriangle, IconExclamationMark, IconShieldLock, IconCheck, IconCopy, IconPlus, IconRocket, IconRefresh, IconPower, IconPlayerPlay, IconCircleCheck, IconTag, IconBook2, IconFileOff, IconList, IconCalendarRepeat, IconCalendarEvent, IconCalendarTime, IconRobot, IconGitBranch, IconSettings, IconExternalLink, IconDotsVertical, IconPlayerPause, IconPlayerStop, IconCalendarDue, IconCalendarStats, IconCalendarOff, IconListCheck, IconTrophy, IconClockExclamation, IconUser, IconLayoutGrid, IconColumns, IconFileInvoice, IconChecklist, IconHistory, IconSearch, IconTargetArrow, IconArrowLeft, IconFileText, IconX, IconBuilding, IconMailCheck, IconArrowRight, IconQuestionMark, IconSparkles, IconClockHour4, IconBuildingFactory2, IconUsers, IconChartBar, IconTrendingUp, IconHeartbeat, IconFlag, IconInbox, IconLock, IconChevronRight, IconDownload, IconMessageCircle, IconBell, IconNotes, IconFolderOpen, IconFolder, IconCheckbox, IconMail, IconPhone, IconNote } from '@tabler/icons-react';
52
52
  import * as runtime from 'react/jsx-runtime';
53
53
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
54
54
  import { useDisclosure } from '@mantine/hooks';
@@ -3089,22 +3089,11 @@ function DealKanbanCard({ deal, config, onClick, onDragStart, onDragEnd }) {
3089
3089
  }
3090
3090
  );
3091
3091
  }
3092
-
3093
- // src/components/acquisition/kanban/constants.ts
3094
- var DEAL_STAGES = ["interested", "proposal", "closing", "closed_won", "closed_lost", "nurturing"];
3095
- var DEFAULT_KANBAN_CONFIG = {
3096
- interested: { color: "blue" },
3097
- proposal: { color: "yellow" },
3098
- closing: { color: "lime" },
3099
- closed_won: { color: "green" },
3100
- closed_lost: { color: "red" },
3101
- nurturing: { color: "grape" }
3102
- };
3103
3092
  function formatStageLabel(stage) {
3104
3093
  if (!stage) return "Unstaged";
3105
3094
  return stage.split("_").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
3106
3095
  }
3107
- function DealDrawer({ deal, opened, onClose, renderActions }) {
3096
+ function DealDrawer({ deal, config, opened, onClose, renderActions }) {
3108
3097
  const [noteBody, setNoteBody] = useState("");
3109
3098
  const { data: notes, isLoading: notesLoading } = useDealNotes(deal?.id ?? "");
3110
3099
  const createNote = useCreateDealNote();
@@ -3116,7 +3105,7 @@ function DealDrawer({ deal, opened, onClose, renderActions }) {
3116
3105
  const email = deal.contact?.email || deal.contact_email || null;
3117
3106
  const companyName = deal.contact?.company?.name || deal.discovery_data?.company || null;
3118
3107
  const stage = deal.cached_stage;
3119
- const badgeColor = DEFAULT_KANBAN_CONFIG[stage]?.color ?? "gray";
3108
+ const badgeColor = (stage ? config?.[stage]?.color : null) ?? "gray";
3120
3109
  const activityLog = deal.activity_log || [];
3121
3110
  const hasInitialFee = typeof deal.initial_fee === "number" && deal.initial_fee > 0;
3122
3111
  const hasMonthlyFee = typeof deal.monthly_fee === "number" && deal.monthly_fee > 0;
@@ -3232,12 +3221,9 @@ function formatStageLabel2(stage) {
3232
3221
  if (stage === UNSTAGED_KEY) return "Unstaged";
3233
3222
  return stage.split("_").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
3234
3223
  }
3235
- function groupDealsByStage(deals) {
3224
+ function groupDealsByStage(deals, columns) {
3236
3225
  const map = /* @__PURE__ */ new Map();
3237
- for (const stage of DEAL_STAGES) {
3238
- map.set(stage, []);
3239
- }
3240
- map.set(UNSTAGED_KEY, []);
3226
+ for (const stage of columns) map.set(stage, []);
3241
3227
  for (const deal of deals) {
3242
3228
  const key = deal.cached_stage ?? UNSTAGED_KEY;
3243
3229
  const bucket = map.get(key);
@@ -3249,7 +3235,7 @@ function groupDealsByStage(deals) {
3249
3235
  }
3250
3236
  return map;
3251
3237
  }
3252
- function KanbanBoard({ config = DEFAULT_KANBAN_CONFIG, renderDrawerActions }) {
3238
+ function KanbanBoard({ config, renderDrawerActions }) {
3253
3239
  const { data: deals, isLoading, error } = useDeals();
3254
3240
  const syncStage = useSyncDealStage();
3255
3241
  const queryClient = useQueryClient();
@@ -3257,8 +3243,11 @@ function KanbanBoard({ config = DEFAULT_KANBAN_CONFIG, renderDrawerActions }) {
3257
3243
  const [drawerOpen, setDrawerOpen] = useState(false);
3258
3244
  const [dragOverColumn, setDragOverColumn] = useState(null);
3259
3245
  const draggingDealRef = useRef(null);
3260
- const groupedDeals = groupDealsByStage(deals ?? []);
3261
- const columns = [...DEAL_STAGES, UNSTAGED_KEY];
3246
+ const resolvedConfig = config ?? {};
3247
+ const configuredStages = Object.keys(resolvedConfig).filter((stage) => Boolean(resolvedConfig[stage]));
3248
+ const columns = [...configuredStages, UNSTAGED_KEY];
3249
+ const hasConfig = configuredStages.length > 0;
3250
+ const groupedDeals = groupDealsByStage(deals ?? [], columns);
3262
3251
  const handleDragStart = useCallback((e, deal) => {
3263
3252
  draggingDealRef.current = deal;
3264
3253
  e.dataTransfer.effectAllowed = "move";
@@ -3307,6 +3296,15 @@ function KanbanBoard({ config = DEFAULT_KANBAN_CONFIG, renderDrawerActions }) {
3307
3296
  if (error) {
3308
3297
  return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsx(Alert, { color: "red", children: "Failed to load deals" }) });
3309
3298
  }
3299
+ if (!hasConfig) {
3300
+ return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsxs(Stack, { children: [
3301
+ /* @__PURE__ */ jsx(PageTitleCaption, { title: "Pipeline", caption: "Kanban view of your deal pipeline" }),
3302
+ /* @__PURE__ */ jsx(Paper, { withBorder: true, p: "xl", children: /* @__PURE__ */ jsx(Center, { py: "xl", children: /* @__PURE__ */ jsxs(Stack, { gap: "xs", align: "center", children: [
3303
+ /* @__PURE__ */ jsx(Text, { fw: 600, children: "Configuration Required" }),
3304
+ /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", ta: "center", children: "Provide a kanban stage config before rendering the pipeline board." })
3305
+ ] }) }) })
3306
+ ] }) });
3307
+ }
3310
3308
  return /* @__PURE__ */ jsxs(SubshellContentContainer, { children: [
3311
3309
  /* @__PURE__ */ jsxs(Stack, { children: [
3312
3310
  /* @__PURE__ */ jsx(PageTitleCaption, { title: "Pipeline", caption: "Kanban view of your deal pipeline" }),
@@ -3322,7 +3320,7 @@ function KanbanBoard({ config = DEFAULT_KANBAN_CONFIG, renderDrawerActions }) {
3322
3320
  const columnDeals = groupedDeals.get(columnKey) ?? [];
3323
3321
  const isUnstaged = columnKey === UNSTAGED_KEY;
3324
3322
  const isDragTarget = dragOverColumn === columnKey;
3325
- const badgeColor = isUnstaged ? "gray" : config[columnKey]?.color ?? "gray";
3323
+ const badgeColor = isUnstaged ? "gray" : resolvedConfig[columnKey]?.color ?? "gray";
3326
3324
  return /* @__PURE__ */ jsx(
3327
3325
  Box,
3328
3326
  {
@@ -3361,7 +3359,7 @@ function KanbanBoard({ config = DEFAULT_KANBAN_CONFIG, renderDrawerActions }) {
3361
3359
  DealKanbanCard,
3362
3360
  {
3363
3361
  deal,
3364
- config,
3362
+ config: resolvedConfig,
3365
3363
  onClick: handleCardClick,
3366
3364
  onDragStart: handleDragStart,
3367
3365
  onDragEnd: handleDragEnd
@@ -3380,6 +3378,7 @@ function KanbanBoard({ config = DEFAULT_KANBAN_CONFIG, renderDrawerActions }) {
3380
3378
  DealDrawer,
3381
3379
  {
3382
3380
  deal: selectedDeal,
3381
+ config: resolvedConfig,
3383
3382
  opened: drawerOpen,
3384
3383
  onClose: () => setDrawerOpen(false),
3385
3384
  renderActions: renderDrawerActions
@@ -3387,6 +3386,17 @@ function KanbanBoard({ config = DEFAULT_KANBAN_CONFIG, renderDrawerActions }) {
3387
3386
  )
3388
3387
  ] });
3389
3388
  }
3389
+
3390
+ // src/components/acquisition/kanban/constants.ts
3391
+ var DEAL_STAGES = ["interested", "proposal", "closing", "closed_won", "closed_lost", "nurturing"];
3392
+ var DEFAULT_KANBAN_CONFIG = {
3393
+ interested: { color: "blue" },
3394
+ proposal: { color: "yellow" },
3395
+ closing: { color: "lime" },
3396
+ closed_won: { color: "green" },
3397
+ closed_lost: { color: "red" },
3398
+ nurturing: { color: "grape" }
3399
+ };
3390
3400
  var CrmSidebarTop = () => {
3391
3401
  return /* @__PURE__ */ jsx(SubshellSidebarSection, { icon: IconAddressBook, label: "CRM" });
3392
3402
  };
@@ -4546,8 +4556,9 @@ function LeadGenRouteShell({
4546
4556
  ] }) })
4547
4557
  ] }) }) });
4548
4558
  }
4549
- function formatDate2(dateString) {
4550
- return new Date(dateString).toLocaleDateString("en-US", {
4559
+ function formatDate2(dateValue) {
4560
+ const date = typeof dateValue === "string" ? new Date(dateValue) : dateValue;
4561
+ return date.toLocaleDateString("en-US", {
4551
4562
  month: "short",
4552
4563
  day: "numeric",
4553
4564
  year: "numeric"
@@ -4568,6 +4579,135 @@ function getEnrichmentColor(status) {
4568
4579
  return "gray";
4569
4580
  }
4570
4581
  }
4582
+ function getEnrichmentStatus(enrichmentData) {
4583
+ if (!enrichmentData || typeof enrichmentData !== "object") return "pending";
4584
+ const website = enrichmentData.website;
4585
+ const linkedin = enrichmentData.linkedin;
4586
+ if (website === "complete" && linkedin === "complete") return "complete";
4587
+ if (website === "failed" || linkedin === "failed") return "failed";
4588
+ return "pending";
4589
+ }
4590
+ function formatName(parts, fallback) {
4591
+ const name = parts.filter(Boolean).join(" ").trim();
4592
+ return name || fallback;
4593
+ }
4594
+ function CompanyDetailModal({ company, onClose }) {
4595
+ return /* @__PURE__ */ jsx(CustomModal, { opened: !!company, onClose, size: "xl", children: company ? /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
4596
+ /* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "flex-start", children: [
4597
+ /* @__PURE__ */ jsxs("div", { children: [
4598
+ /* @__PURE__ */ jsx(Title, { order: 3, children: company.name }),
4599
+ company.domain ? /* @__PURE__ */ jsxs(Anchor, { href: `https://${company.domain}`, target: "_blank", size: "sm", c: "dimmed", children: [
4600
+ company.domain,
4601
+ " ",
4602
+ /* @__PURE__ */ jsx(IconExternalLink, { size: 12, style: { verticalAlign: "middle" } })
4603
+ ] }) : null
4604
+ ] }),
4605
+ /* @__PURE__ */ jsx(ActionIcon, { variant: "subtle", onClick: onClose, children: /* @__PURE__ */ jsx(IconX, { size: 18 }) })
4606
+ ] }),
4607
+ /* @__PURE__ */ jsx(Divider, {}),
4608
+ /* @__PURE__ */ jsxs(Box, { children: [
4609
+ /* @__PURE__ */ jsx(Title, { order: 4, mb: "xs", children: "Firmographics" }),
4610
+ /* @__PURE__ */ jsxs(SimpleGrid, { cols: 3, children: [
4611
+ /* @__PURE__ */ jsxs("div", { children: [
4612
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "Segment" }),
4613
+ /* @__PURE__ */ jsx(Text, { size: "sm", children: company.segment || "-" })
4614
+ ] }),
4615
+ /* @__PURE__ */ jsxs("div", { children: [
4616
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "Category" }),
4617
+ /* @__PURE__ */ jsx(Text, { size: "sm", children: company.category || "-" })
4618
+ ] }),
4619
+ /* @__PURE__ */ jsxs("div", { children: [
4620
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "Employees" }),
4621
+ /* @__PURE__ */ jsx(Text, { size: "sm", children: company.numEmployees || "-" })
4622
+ ] }),
4623
+ /* @__PURE__ */ jsxs("div", { children: [
4624
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "Founded" }),
4625
+ /* @__PURE__ */ jsx(Text, { size: "sm", children: company.foundedYear || "-" })
4626
+ ] }),
4627
+ /* @__PURE__ */ jsxs("div", { children: [
4628
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "Location" }),
4629
+ /* @__PURE__ */ jsx(Text, { size: "sm", children: [company.locationCity, company.locationState].filter(Boolean).join(", ") || "-" })
4630
+ ] }),
4631
+ /* @__PURE__ */ jsxs("div", { children: [
4632
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "Contacts" }),
4633
+ /* @__PURE__ */ jsx(Text, { size: "sm", children: company.contactCount })
4634
+ ] })
4635
+ ] })
4636
+ ] }),
4637
+ company.enrichmentData ? /* @__PURE__ */ jsxs(Box, { children: [
4638
+ /* @__PURE__ */ jsx(Title, { order: 4, mb: "xs", children: "Enrichment Data" }),
4639
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", style: { whiteSpace: "pre-wrap" }, children: JSON.stringify(company.enrichmentData, null, 2) })
4640
+ ] }) : null,
4641
+ /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
4642
+ company.linkedinUrl ? /* @__PURE__ */ jsxs(Anchor, { href: company.linkedinUrl, target: "_blank", size: "sm", children: [
4643
+ "LinkedIn ",
4644
+ /* @__PURE__ */ jsx(IconExternalLink, { size: 12 })
4645
+ ] }) : null,
4646
+ company.website ? /* @__PURE__ */ jsxs(Anchor, { href: company.website, target: "_blank", size: "sm", children: [
4647
+ "Website ",
4648
+ /* @__PURE__ */ jsx(IconExternalLink, { size: 12 })
4649
+ ] }) : null
4650
+ ] }),
4651
+ /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
4652
+ "Created: ",
4653
+ formatDate2(company.createdAt),
4654
+ company.updatedAt && ` | Updated: ${formatDate2(company.updatedAt)}`
4655
+ ] })
4656
+ ] }) : null });
4657
+ }
4658
+ function ContactDetailModal({ contact, onClose }) {
4659
+ return /* @__PURE__ */ jsx(CustomModal, { opened: !!contact, onClose, size: "xl", children: contact ? /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
4660
+ /* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "flex-start", children: [
4661
+ /* @__PURE__ */ jsxs("div", { children: [
4662
+ /* @__PURE__ */ jsx(Title, { order: 3, children: formatName([contact.firstName, contact.lastName], contact.email) }),
4663
+ /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: contact.email }),
4664
+ contact.title ? /* @__PURE__ */ jsx(Text, { size: "sm", children: contact.title }) : null
4665
+ ] }),
4666
+ /* @__PURE__ */ jsx(ActionIcon, { variant: "subtle", onClick: onClose, children: /* @__PURE__ */ jsx(IconX, { size: 18 }) })
4667
+ ] }),
4668
+ /* @__PURE__ */ jsx(Divider, {}),
4669
+ /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
4670
+ /* @__PURE__ */ jsx(Badge, { color: getStatusColor3(contact.status), children: contact.status }),
4671
+ contact.openingLine ? /* @__PURE__ */ jsx(Badge, { color: "green", children: "Personalized" }) : null
4672
+ ] }),
4673
+ contact.company ? /* @__PURE__ */ jsxs(Box, { children: [
4674
+ /* @__PURE__ */ jsx(Title, { order: 4, mb: "xs", children: "Company" }),
4675
+ /* @__PURE__ */ jsxs(Card, { withBorder: true, children: [
4676
+ /* @__PURE__ */ jsx(Text, { fw: 500, children: contact.company.name }),
4677
+ contact.company.domain ? /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: contact.company.domain }) : null
4678
+ ] })
4679
+ ] }) : null,
4680
+ /* @__PURE__ */ jsxs(Box, { children: [
4681
+ /* @__PURE__ */ jsx(Title, { order: 4, mb: "xs", children: "Contact Information" }),
4682
+ /* @__PURE__ */ jsxs(SimpleGrid, { cols: 2, children: [
4683
+ /* @__PURE__ */ jsxs("div", { children: [
4684
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "Headline" }),
4685
+ /* @__PURE__ */ jsx(Text, { size: "sm", children: contact.headline || "-" })
4686
+ ] }),
4687
+ /* @__PURE__ */ jsxs("div", { children: [
4688
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "LinkedIn" }),
4689
+ contact.linkedinUrl ? /* @__PURE__ */ jsxs(Anchor, { href: contact.linkedinUrl, target: "_blank", size: "sm", children: [
4690
+ "View Profile ",
4691
+ /* @__PURE__ */ jsx(IconExternalLink, { size: 12 })
4692
+ ] }) : /* @__PURE__ */ jsx(Text, { size: "sm", children: "-" })
4693
+ ] })
4694
+ ] })
4695
+ ] }),
4696
+ contact.filterReason ? /* @__PURE__ */ jsxs(Box, { children: [
4697
+ /* @__PURE__ */ jsx(Title, { order: 4, mb: "xs", children: "Qualification" }),
4698
+ /* @__PURE__ */ jsx(Text, { size: "sm", c: "red", children: contact.filterReason })
4699
+ ] }) : null,
4700
+ contact.openingLine ? /* @__PURE__ */ jsxs(Box, { children: [
4701
+ /* @__PURE__ */ jsx(Title, { order: 4, mb: "xs", children: "Personalization" }),
4702
+ /* @__PURE__ */ jsx(Text, { size: "sm", children: contact.openingLine })
4703
+ ] }) : null,
4704
+ /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
4705
+ "Created: ",
4706
+ formatDate2(contact.createdAt),
4707
+ contact.updatedAt && ` | Updated: ${formatDate2(contact.updatedAt)}`
4708
+ ] })
4709
+ ] }) : null });
4710
+ }
4571
4711
  var LIST_TEMPLATE_OPTIONS = [
4572
4712
  {
4573
4713
  value: "blank",
@@ -4772,46 +4912,377 @@ function useDeleteLists() {
4772
4912
  });
4773
4913
  }
4774
4914
  var EM_DASH = "\u2014";
4915
+ function computeCompletionRatio(populated, personalized) {
4916
+ if (populated === 0) return null;
4917
+ return personalized / populated;
4918
+ }
4919
+ function formatPercentage(ratio, fractionDigits = 0) {
4920
+ if (ratio == null) return EM_DASH;
4921
+ return `${(ratio * 100).toFixed(fractionDigits)}%`;
4922
+ }
4923
+ function computeCompletionPercentage(populated, personalized) {
4924
+ return formatPercentage(computeCompletionRatio(populated, personalized));
4925
+ }
4926
+ function computeBounceRate(d) {
4927
+ const denominator = d.valid + d.risky + d.invalid + d.bounced;
4928
+ if (denominator === 0) return EM_DASH;
4929
+ return `${(d.bounced / denominator * 100).toFixed(1)}%`;
4930
+ }
4931
+ function computeBacklog(current, completed) {
4932
+ return Math.max(current - completed, 0);
4933
+ }
4934
+ function getOverviewStatus(list) {
4935
+ if ((list.activeWorkflows?.length ?? 0) > 0) {
4936
+ return { label: "Active Work", color: "green" };
4937
+ }
4938
+ if (list.stageCounts.personalized > 0 && list.stageCounts.personalized === list.stageCounts.uploaded) {
4939
+ return { label: "Complete", color: "blue" };
4940
+ }
4941
+ return { label: "Idle", color: "gray" };
4942
+ }
4943
+ function getNextFocus(list) {
4944
+ if ((list.activeWorkflows?.length ?? 0) > 0) {
4945
+ return { label: "Workflow running", count: list.activeWorkflows?.length ?? 0 };
4946
+ }
4947
+ if (list.totalCompanies === 0 && list.totalContacts === 0) {
4948
+ return { label: "Populate list", count: null };
4949
+ }
4950
+ const extractGap = computeBacklog(list.stageCounts.populated, list.stageCounts.extracted);
4951
+ if (extractGap > 0) {
4952
+ return { label: "Extract website data", count: extractGap };
4953
+ }
4954
+ const qualifyGap = computeBacklog(list.stageCounts.extracted, list.stageCounts.qualified);
4955
+ if (qualifyGap > 0) {
4956
+ return { label: "Qualify companies", count: qualifyGap };
4957
+ }
4958
+ const discoverGap = computeBacklog(list.stageCounts.qualified, list.stageCounts.discovered);
4959
+ if (discoverGap > 0) {
4960
+ return { label: "Discover contacts", count: discoverGap };
4961
+ }
4962
+ const verifyGap = computeBacklog(list.stageCounts.discovered, list.stageCounts.verified);
4963
+ if (verifyGap > 0) {
4964
+ return { label: "Verify emails", count: verifyGap };
4965
+ }
4966
+ const personalizeGap = computeBacklog(list.stageCounts.verified, list.stageCounts.personalized);
4967
+ if (personalizeGap > 0) {
4968
+ return { label: "Personalize outreach", count: personalizeGap };
4969
+ }
4970
+ const uploadGap = computeBacklog(list.stageCounts.personalized, list.stageCounts.uploaded);
4971
+ if (uploadGap > 0) {
4972
+ return { label: "Upload contacts", count: uploadGap };
4973
+ }
4974
+ return { label: "Complete", count: null };
4975
+ }
4976
+ function formatCountLabel(value, noun) {
4977
+ return `${value} ${noun}${value === 1 ? "" : "s"}`;
4978
+ }
4979
+ function getPrimaryAction({
4980
+ uploadBacklog,
4981
+ personalizationBacklog,
4982
+ verificationBacklog,
4983
+ deliverabilityRiskCount,
4984
+ activeListCount,
4985
+ totalLists
4986
+ }) {
4987
+ if (uploadBacklog > 0) {
4988
+ return {
4989
+ title: `Upload ${formatCountLabel(uploadBacklog, "personalized contact")}`,
4990
+ detail: "No verification blockers are left on the contacts already ready for outreach.",
4991
+ buttonLabel: "Review lists",
4992
+ buttonTo: "/lead-gen/lists",
4993
+ tone: "blue"
4994
+ };
4995
+ }
4996
+ if (personalizationBacklog > 0) {
4997
+ return {
4998
+ title: `Personalize ${formatCountLabel(personalizationBacklog, "verified contact")}`,
4999
+ detail: "Verification is done. The next bottleneck is drafting outreach copy.",
5000
+ buttonLabel: "Open lists",
5001
+ buttonTo: "/lead-gen/lists",
5002
+ tone: "blue"
5003
+ };
5004
+ }
5005
+ if (verificationBacklog > 0) {
5006
+ return {
5007
+ title: `Verify ${formatCountLabel(verificationBacklog, "discovered contact")}`,
5008
+ detail: "Email validation is the main blocker before personalization can continue.",
5009
+ buttonLabel: "Open lists",
5010
+ buttonTo: "/lead-gen/lists",
5011
+ tone: "orange"
5012
+ };
5013
+ }
5014
+ if (deliverabilityRiskCount > 0) {
5015
+ return {
5016
+ title: `Resolve ${formatCountLabel(deliverabilityRiskCount, "deliverability risk")}`,
5017
+ detail: "Risky, invalid, or bounced records are the main campaign health issue right now.",
5018
+ buttonLabel: "View deliverability",
5019
+ buttonTo: "/lead-gen/deliverability",
5020
+ tone: "orange"
5021
+ };
5022
+ }
5023
+ if (activeListCount > 0) {
5024
+ return {
5025
+ title: `Monitor ${formatCountLabel(activeListCount, "active list")}`,
5026
+ detail: "Workflows are running and there are no immediate contact-stage backlogs to clear.",
5027
+ buttonLabel: "Open lists",
5028
+ buttonTo: "/lead-gen/lists",
5029
+ tone: "green"
5030
+ };
5031
+ }
5032
+ if (totalLists > 0) {
5033
+ return {
5034
+ title: "No blockers right now",
5035
+ detail: "Lists are idle and the current pipeline does not show an urgent follow-up step.",
5036
+ buttonLabel: "Review lists",
5037
+ buttonTo: "/lead-gen/lists",
5038
+ tone: "gray"
5039
+ };
5040
+ }
5041
+ return {
5042
+ title: "Create your first list",
5043
+ detail: "Lead gen telemetry will appear here once a list starts moving through the pipeline.",
5044
+ buttonLabel: "Open lists",
5045
+ buttonTo: "/lead-gen/lists",
5046
+ tone: "gray"
5047
+ };
5048
+ }
5049
+ function CompactPipelineStage({
5050
+ label,
5051
+ value,
5052
+ ratio
5053
+ }) {
5054
+ return /* @__PURE__ */ jsx(Paper, { withBorder: true, p: "sm", children: /* @__PURE__ */ jsxs(Stack, { gap: 6, children: [
5055
+ /* @__PURE__ */ jsxs(Group, { justify: "space-between", gap: "xs", wrap: "nowrap", children: [
5056
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", tt: "uppercase", fw: 600, children: label }),
5057
+ /* @__PURE__ */ jsx(Badge, { size: "xs", variant: "light", color: "blue", children: formatPercentage(ratio) })
5058
+ ] }),
5059
+ /* @__PURE__ */ jsx(Title, { order: 4, children: value }),
5060
+ /* @__PURE__ */ jsx(Progress, { value: ratio == null ? 0 : ratio * 100, size: "sm", radius: "xl", color: "blue" })
5061
+ ] }) });
5062
+ }
4775
5063
  function LeadGenOverviewPage() {
4776
- const { data, isLoading, isError, error } = useListsTelemetry();
5064
+ const telemetryQuery = useListsTelemetry();
5065
+ const listsQuery = useLists();
5066
+ const data = telemetryQuery.data;
5067
+ const listMetaById = new Map((listsQuery.data ?? []).map((list) => [list.id, list]));
4777
5068
  const totalCompanies = data?.reduce((sum, list) => sum + list.totalCompanies, 0) ?? 0;
4778
5069
  const totalContacts = data?.reduce((sum, list) => sum + list.totalContacts, 0) ?? 0;
4779
- const completionLabel = (() => {
4780
- if (!data?.length) return EM_DASH;
4781
- const populated = data.reduce((sum, list) => sum + list.stageCounts.populated, 0);
4782
- const personalized = data.reduce((sum, list) => sum + list.stageCounts.personalized, 0);
4783
- return populated === 0 ? EM_DASH : `${Math.round(personalized / populated * 100)}%`;
4784
- })();
5070
+ const stageTotals = data?.reduce(
5071
+ (acc, list) => ({
5072
+ populated: acc.populated + list.stageCounts.populated,
5073
+ extracted: acc.extracted + list.stageCounts.extracted,
5074
+ qualified: acc.qualified + list.stageCounts.qualified,
5075
+ discovered: acc.discovered + list.stageCounts.discovered,
5076
+ verified: acc.verified + list.stageCounts.verified,
5077
+ personalized: acc.personalized + list.stageCounts.personalized,
5078
+ uploaded: acc.uploaded + list.stageCounts.uploaded
5079
+ }),
5080
+ { populated: 0, extracted: 0, qualified: 0, discovered: 0, verified: 0, personalized: 0, uploaded: 0 }
5081
+ ) ?? { populated: 0, extracted: 0, qualified: 0, discovered: 0, verified: 0, personalized: 0, uploaded: 0 };
5082
+ const deliverabilityTotals = data?.reduce(
5083
+ (acc, list) => ({
5084
+ valid: acc.valid + list.deliverability.valid,
5085
+ risky: acc.risky + list.deliverability.risky,
5086
+ invalid: acc.invalid + list.deliverability.invalid,
5087
+ unknown: acc.unknown + list.deliverability.unknown,
5088
+ bounced: acc.bounced + list.deliverability.bounced
5089
+ }),
5090
+ { valid: 0, risky: 0, invalid: 0, unknown: 0, bounced: 0 }
5091
+ ) ?? { risky: 0, invalid: 0, bounced: 0 };
5092
+ const activeListCount = data?.filter((list) => (list.activeWorkflows?.length ?? 0) > 0).length ?? 0;
5093
+ const verificationBacklog = computeBacklog(stageTotals.discovered, stageTotals.verified);
5094
+ const personalizationBacklog = computeBacklog(stageTotals.verified, stageTotals.personalized);
5095
+ const uploadBacklog = computeBacklog(stageTotals.personalized, stageTotals.uploaded);
5096
+ const deliverabilityRiskCount = deliverabilityTotals.risky + deliverabilityTotals.invalid + deliverabilityTotals.bounced;
5097
+ const totalLists = data?.length ?? 0;
5098
+ const summaryLine = [
5099
+ `${totalLists} ${totalLists === 1 ? "list" : "lists"} active`,
5100
+ `${formatCountLabel(uploadBacklog, "contact")} ready for upload`,
5101
+ deliverabilityRiskCount === 0 ? "no risks" : `${formatCountLabel(deliverabilityRiskCount, "risk")}`
5102
+ ].join(" \u2022 ");
5103
+ const primaryAction = getPrimaryAction({
5104
+ uploadBacklog,
5105
+ personalizationBacklog,
5106
+ verificationBacklog,
5107
+ deliverabilityRiskCount,
5108
+ activeListCount,
5109
+ totalLists
5110
+ });
5111
+ const pipelineStages = [
5112
+ {
5113
+ label: "Populated",
5114
+ value: stageTotals.populated,
5115
+ ratio: totalCompanies === 0 ? null : stageTotals.populated / totalCompanies
5116
+ },
5117
+ {
5118
+ label: "Qualified",
5119
+ value: stageTotals.qualified,
5120
+ ratio: totalCompanies === 0 ? null : stageTotals.qualified / totalCompanies
5121
+ },
5122
+ {
5123
+ label: "Verified",
5124
+ value: stageTotals.verified,
5125
+ ratio: totalContacts === 0 ? null : stageTotals.verified / totalContacts
5126
+ },
5127
+ {
5128
+ label: "Personalized",
5129
+ value: stageTotals.personalized,
5130
+ ratio: totalContacts === 0 ? null : stageTotals.personalized / totalContacts
5131
+ },
5132
+ {
5133
+ label: "Uploaded",
5134
+ value: stageTotals.uploaded,
5135
+ ratio: totalContacts === 0 ? null : stageTotals.uploaded / totalContacts
5136
+ }
5137
+ ];
5138
+ const overviewRows = data?.map((list) => {
5139
+ const listMeta = listMetaById.get(list.listId);
5140
+ const completionRatio = computeCompletionRatio(list.stageCounts.populated, list.stageCounts.personalized);
5141
+ const nextFocus = getNextFocus(list);
5142
+ return {
5143
+ ...list,
5144
+ name: listMeta?.name ?? `List ${list.listId.slice(0, 8)}`,
5145
+ completionRatio,
5146
+ nextFocus,
5147
+ status: getOverviewStatus(list)
5148
+ };
5149
+ }).sort((a, b) => {
5150
+ const aActive = a.status.label === "Active Work" ? 1 : 0;
5151
+ const bActive = b.status.label === "Active Work" ? 1 : 0;
5152
+ if (aActive !== bActive) return bActive - aActive;
5153
+ const aBacklog = computeBacklog(a.stageCounts.discovered, a.stageCounts.verified) + computeBacklog(a.stageCounts.verified, a.stageCounts.personalized) + computeBacklog(a.stageCounts.personalized, a.stageCounts.uploaded);
5154
+ const bBacklog = computeBacklog(b.stageCounts.discovered, b.stageCounts.verified) + computeBacklog(b.stageCounts.verified, b.stageCounts.personalized) + computeBacklog(b.stageCounts.personalized, b.stageCounts.uploaded);
5155
+ if (aBacklog !== bBacklog) return bBacklog - aBacklog;
5156
+ return b.totalContacts - a.totalContacts;
5157
+ }) ?? [];
4785
5158
  return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsx(PageContainer, { children: /* @__PURE__ */ jsxs(Stack, { children: [
4786
5159
  /* @__PURE__ */ jsx(
4787
5160
  PageTitleCaption,
4788
5161
  {
4789
5162
  title: "Lead Gen Overview",
4790
- caption: "Pipeline health, list roll-up, and active work at a glance"
5163
+ caption: summaryLine,
5164
+ rightSection: /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
5165
+ /* @__PURE__ */ jsx(Button, { component: Link$1, to: "/lead-gen/lists", size: "sm", variant: "light", children: "Lists" }),
5166
+ /* @__PURE__ */ jsx(Button, { component: Link$1, to: "/lead-gen/deliverability", size: "sm", variant: "light", children: "Deliverability" })
5167
+ ] })
4791
5168
  }
4792
5169
  ),
4793
- isLoading ? /* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsx(Center, { p: "xl", children: /* @__PURE__ */ jsx(Loader, {}) }) }) : isError ? /* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsx(CenteredErrorState, { 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: [
4794
- /* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 2, sm: 4 }, children: [
4795
- /* @__PURE__ */ jsx(StatCard, { variant: "hero", icon: IconChartBar, value: data.length, label: "Total Lists" }),
4796
- /* @__PURE__ */ jsx(StatCard, { variant: "hero", icon: IconBuildingFactory2, value: totalCompanies, label: "Total Companies" }),
4797
- /* @__PURE__ */ jsx(StatCard, { variant: "hero", icon: IconUsers, value: totalContacts, label: "Total Contacts" }),
4798
- /* @__PURE__ */ jsx(StatCard, { variant: "hero", icon: IconChecklist, value: completionLabel, label: "Overall Completion" })
5170
+ 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: [
5171
+ /* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 1, sm: 3 }, children: [
5172
+ /* @__PURE__ */ jsx(StatCard, { variant: "hero", icon: IconPlayerPlay, value: activeListCount, label: "Active Lists" }),
5173
+ /* @__PURE__ */ jsx(StatCard, { variant: "hero", icon: IconArrowRight, value: uploadBacklog, label: "Ready Contacts" }),
5174
+ /* @__PURE__ */ jsx(StatCard, { variant: "hero", icon: IconAlertCircle, value: deliverabilityRiskCount, label: "At Risk" })
4799
5175
  ] }),
4800
- /* @__PURE__ */ jsxs(Text, { size: "sm", c: "dimmed", children: [
4801
- "View per-list detail on the",
4802
- " ",
4803
- /* @__PURE__ */ jsx(Anchor, { component: Link$1, to: "/lead-gen/lists", size: "sm", children: "Lists" }),
4804
- " ",
4805
- "page."
4806
- ] })
5176
+ /* @__PURE__ */ jsx(Paper, { withBorder: true, p: "md", children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
5177
+ /* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "flex-start", gap: "md", children: [
5178
+ /* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
5179
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", tt: "uppercase", fw: 700, children: "Next Action" }),
5180
+ /* @__PURE__ */ jsx(Title, { order: 3, children: primaryAction.title }),
5181
+ /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", maw: 620, children: primaryAction.detail })
5182
+ ] }),
5183
+ /* @__PURE__ */ jsx(Button, { component: Link$1, to: primaryAction.buttonTo, size: "sm", variant: "light", children: primaryAction.buttonLabel })
5184
+ ] }),
5185
+ /* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 1, md: 3 }, children: [
5186
+ /* @__PURE__ */ jsxs(Paper, { withBorder: true, p: "sm", children: [
5187
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", tt: "uppercase", fw: 600, children: "Verification" }),
5188
+ /* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "flex-end", mt: 6, children: [
5189
+ /* @__PURE__ */ jsx(Title, { order: 4, children: verificationBacklog }),
5190
+ /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "waiting" })
5191
+ ] })
5192
+ ] }),
5193
+ /* @__PURE__ */ jsxs(Paper, { withBorder: true, p: "sm", children: [
5194
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", tt: "uppercase", fw: 600, children: "Personalization" }),
5195
+ /* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "flex-end", mt: 6, children: [
5196
+ /* @__PURE__ */ jsx(Title, { order: 4, children: personalizationBacklog }),
5197
+ /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "ready" })
5198
+ ] })
5199
+ ] }),
5200
+ /* @__PURE__ */ jsxs(Paper, { withBorder: true, p: "sm", children: [
5201
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", tt: "uppercase", fw: 600, children: "Completion" }),
5202
+ /* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "flex-end", mt: 6, children: [
5203
+ /* @__PURE__ */ jsx(Title, { order: 4, children: computeCompletionPercentage(stageTotals.populated, stageTotals.personalized) }),
5204
+ /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "personalized" })
5205
+ ] })
5206
+ ] })
5207
+ ] })
5208
+ ] }) }),
5209
+ /* @__PURE__ */ jsx(Paper, { withBorder: true, p: "md", children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
5210
+ /* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "flex-end", children: [
5211
+ /* @__PURE__ */ jsxs("div", { children: [
5212
+ /* @__PURE__ */ jsx(Title, { order: 3, children: "Pipeline" }),
5213
+ /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "Keep the funnel visible without giving every zero state its own section." })
5214
+ ] }),
5215
+ /* @__PURE__ */ jsxs(Badge, { variant: "light", color: "blue", children: [
5216
+ stageTotals.extracted,
5217
+ " extracted \u2022 ",
5218
+ stageTotals.discovered,
5219
+ " discovered"
5220
+ ] })
5221
+ ] }),
5222
+ /* @__PURE__ */ jsx(SimpleGrid, { cols: { base: 1, sm: 2, xl: 5 }, children: pipelineStages.map((stage) => /* @__PURE__ */ jsx(
5223
+ CompactPipelineStage,
5224
+ {
5225
+ label: stage.label,
5226
+ value: stage.value,
5227
+ ratio: stage.ratio
5228
+ },
5229
+ stage.label
5230
+ )) })
5231
+ ] }) }),
5232
+ /* @__PURE__ */ jsx(Paper, { withBorder: true, p: "md", children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
5233
+ /* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "flex-end", children: [
5234
+ /* @__PURE__ */ jsxs("div", { children: [
5235
+ /* @__PURE__ */ jsx(Title, { order: 3, children: "List Snapshot" }),
5236
+ /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "Ranked to surface active work first, then the largest unresolved backlogs." })
5237
+ ] }),
5238
+ /* @__PURE__ */ jsx(Button, { component: Link$1, to: "/lead-gen/lists", size: "sm", variant: "subtle", children: "Open lists" })
5239
+ ] }),
5240
+ /* @__PURE__ */ jsxs(Table, { children: [
5241
+ /* @__PURE__ */ jsx(Table.Thead, { children: /* @__PURE__ */ jsxs(Table.Tr, { children: [
5242
+ /* @__PURE__ */ jsx(Table.Th, { children: "List" }),
5243
+ /* @__PURE__ */ jsx(Table.Th, { children: "Companies" }),
5244
+ /* @__PURE__ */ jsx(Table.Th, { children: "Contacts" }),
5245
+ /* @__PURE__ */ jsx(Table.Th, { children: "Next Focus" }),
5246
+ /* @__PURE__ */ jsx(Table.Th, { children: "Progress" }),
5247
+ /* @__PURE__ */ jsx(Table.Th, { children: "Status" })
5248
+ ] }) }),
5249
+ /* @__PURE__ */ jsx(Table.Tbody, { children: overviewRows.map((list) => /* @__PURE__ */ jsxs(Table.Tr, { children: [
5250
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsxs(Stack, { gap: 2, children: [
5251
+ /* @__PURE__ */ jsx(Link$1, { to: "/lead-gen/lists/$listId", params: { listId: list.listId }, children: /* @__PURE__ */ jsx(Text, { size: "sm", fw: 600, component: "span", c: "blue.4", children: list.name }) }),
5252
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: list.listId })
5253
+ ] }) }),
5254
+ /* @__PURE__ */ jsx(Table.Td, { children: list.totalCompanies }),
5255
+ /* @__PURE__ */ jsx(Table.Td, { children: list.totalContacts }),
5256
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsxs(Stack, { gap: 2, children: [
5257
+ /* @__PURE__ */ jsx(Text, { size: "sm", fw: 500, children: list.nextFocus.label }),
5258
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: list.nextFocus.count == null ? "No outstanding count" : `${list.nextFocus.count} item${list.nextFocus.count === 1 ? "" : "s"} queued` })
5259
+ ] }) }),
5260
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsxs(Stack, { gap: "xs", miw: 150, children: [
5261
+ /* @__PURE__ */ jsxs(Group, { justify: "space-between", gap: "xs", children: [
5262
+ /* @__PURE__ */ jsx(Text, { size: "sm", children: formatPercentage(list.completionRatio) }),
5263
+ /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
5264
+ list.stageCounts.personalized,
5265
+ "/",
5266
+ list.stageCounts.populated || 0
5267
+ ] })
5268
+ ] }),
5269
+ /* @__PURE__ */ jsx(
5270
+ Progress,
5271
+ {
5272
+ value: list.completionRatio == null ? 0 : list.completionRatio * 100,
5273
+ size: "sm",
5274
+ radius: "xl",
5275
+ color: "blue"
5276
+ }
5277
+ )
5278
+ ] }) }),
5279
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: list.status.color, children: list.status.label }) })
5280
+ ] }, list.listId)) })
5281
+ ] })
5282
+ ] }) })
4807
5283
  ] })
4808
5284
  ] }) }) });
4809
5285
  }
4810
- function computeBounceRate(d) {
4811
- const denominator = d.valid + d.risky + d.invalid + d.bounced;
4812
- if (denominator === 0) return EM_DASH;
4813
- return `${(d.bounced / denominator * 100).toFixed(1)}%`;
4814
- }
4815
5286
  function LeadGenDeliverabilityPage() {
4816
5287
  const { data, isLoading, error } = useListsTelemetry();
4817
5288
  if (isLoading) {
@@ -4862,12 +5333,12 @@ function LeadGenDeliverabilityPage() {
4862
5333
  { valid: 0, risky: 0, invalid: 0, unknown: 0, bounced: 0 }
4863
5334
  );
4864
5335
  const summaryTiles = [
4865
- { label: "Valid", value: totals.valid },
4866
- { label: "Risky", value: totals.risky },
4867
- { label: "Invalid", value: totals.invalid },
4868
- { label: "Unknown", value: totals.unknown },
4869
- { label: "Bounced", value: totals.bounced },
4870
- { label: "Bounce Rate", value: computeBounceRate(totals) }
5336
+ { label: "Valid", value: totals.valid, icon: IconMailCheck },
5337
+ { label: "Risky", value: totals.risky, icon: IconAlertCircle },
5338
+ { label: "Invalid", value: totals.invalid, icon: IconX },
5339
+ { label: "Unknown", value: totals.unknown, icon: IconQuestionMark },
5340
+ { label: "Bounced", value: totals.bounced, icon: IconAlertCircle },
5341
+ { label: "Bounce Rate", value: computeBounceRate(totals), icon: IconChecklist }
4871
5342
  ];
4872
5343
  return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsx(PageContainer, { children: /* @__PURE__ */ jsxs(Stack, { children: [
4873
5344
  /* @__PURE__ */ jsx(
@@ -4877,10 +5348,7 @@ function LeadGenDeliverabilityPage() {
4877
5348
  caption: "Email validity breakdown and bounce health across lists"
4878
5349
  }
4879
5350
  ),
4880
- /* @__PURE__ */ jsx(Paper, { withBorder: true, p: "md", children: /* @__PURE__ */ jsx(SimpleGrid, { cols: 6, children: summaryTiles.map((tile) => /* @__PURE__ */ jsxs(Paper, { withBorder: true, p: "md", children: [
4881
- /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", mb: 4, children: tile.label }),
4882
- /* @__PURE__ */ jsx(Text, { fw: 600, size: "lg", children: tile.value })
4883
- ] }, tile.label)) }) }),
5351
+ /* @__PURE__ */ jsx(SimpleGrid, { cols: { base: 2, sm: 3, lg: 6 }, children: summaryTiles.map((tile) => /* @__PURE__ */ jsx(StatCard, { variant: "hero", icon: tile.icon, value: tile.value, label: tile.label }, tile.label)) }),
4884
5352
  /* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsxs(Table, { children: [
4885
5353
  /* @__PURE__ */ jsx(Table.Thead, { children: /* @__PURE__ */ jsxs(Table.Tr, { children: [
4886
5354
  /* @__PURE__ */ jsx(Table.Th, { children: "List ID" }),
@@ -5256,6 +5724,7 @@ function LeadGenListDetailPage({ listId }) {
5256
5724
  const list = listQuery.data;
5257
5725
  const progress = progressQuery.data;
5258
5726
  const executions = executionsQuery.data ?? [];
5727
+ const qualification = list.config.qualification;
5259
5728
  const pipelineSteps = list.config.pipeline?.steps?.slice().sort((a, b) => a.order - b.order) ?? [];
5260
5729
  const stageTiles = [
5261
5730
  { label: "Populated", value: progress.stageCounts.populated },
@@ -5336,22 +5805,24 @@ function LeadGenListDetailPage({ listId }) {
5336
5805
  /* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 1, lg: 2 }, children: [
5337
5806
  /* @__PURE__ */ jsx(Paper, { withBorder: true, p: "sm", children: /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
5338
5807
  /* @__PURE__ */ jsx(Text, { size: "sm", fw: 600, children: "Qualification" }),
5339
- /* @__PURE__ */ jsxs(Text, { size: "sm", children: [
5340
- "Target: ",
5341
- list.config.qualification.targetDescription
5342
- ] }),
5343
- /* @__PURE__ */ jsxs(Text, { size: "sm", children: [
5344
- "Minimum reviews: ",
5345
- list.config.qualification.minReviewCount
5346
- ] }),
5347
- /* @__PURE__ */ jsxs(Text, { size: "sm", children: [
5348
- "Minimum rating: ",
5349
- list.config.qualification.minRating
5350
- ] }),
5351
- /* @__PURE__ */ jsxs(Text, { size: "sm", children: [
5352
- "Exclude franchises: ",
5353
- list.config.qualification.excludeFranchises ? "Yes" : "No"
5354
- ] })
5808
+ qualification ? /* @__PURE__ */ jsxs(Fragment, { children: [
5809
+ /* @__PURE__ */ jsxs(Text, { size: "sm", children: [
5810
+ "Target: ",
5811
+ qualification.targetDescription
5812
+ ] }),
5813
+ /* @__PURE__ */ jsxs(Text, { size: "sm", children: [
5814
+ "Minimum reviews: ",
5815
+ qualification.minReviewCount
5816
+ ] }),
5817
+ /* @__PURE__ */ jsxs(Text, { size: "sm", children: [
5818
+ "Minimum rating: ",
5819
+ qualification.minRating
5820
+ ] }),
5821
+ /* @__PURE__ */ jsxs(Text, { size: "sm", children: [
5822
+ "Exclude franchises: ",
5823
+ qualification.excludeFranchises ? "Yes" : "No"
5824
+ ] })
5825
+ ] }) : /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "Qualification settings are not configured for this list." })
5355
5826
  ] }) }),
5356
5827
  /* @__PURE__ */ jsx(Paper, { withBorder: true, p: "sm", children: /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
5357
5828
  /* @__PURE__ */ jsx(Text, { size: "sm", fw: 600, children: "Enrichment + Personalization" }),
@@ -5458,31 +5929,386 @@ function LeadGenListDetailPage({ listId }) {
5458
5929
  )
5459
5930
  ] }) });
5460
5931
  }
5932
+ var PAGE_SIZE_DEFAULT3 = 20;
5461
5933
  function LeadGenCompaniesPage() {
5462
- return /* @__PURE__ */ jsx(
5463
- LeadGenRouteShell,
5464
- {
5465
- title: "Companies",
5466
- caption: "Shared lead-gen navigation shell",
5467
- body: /* @__PURE__ */ jsxs(Fragment, { children: [
5468
- /* @__PURE__ */ jsx(Text, { fw: 500, children: "Company records and enrichment workflows stay app-owned here." }),
5469
- /* @__PURE__ */ jsx(Text, { c: "dimmed", children: "The shared Lead Gen package keeps this route reachable so the navigation stays aligned across the template and derived workspaces without pretending to own the data-heavy company table." })
5470
- ] })
5471
- }
5934
+ const [companySearch, setCompanySearch] = useState("");
5935
+ const [segmentFilter, setSegmentFilter] = useState(null);
5936
+ const [categoryFilter, setCategoryFilter] = useState(null);
5937
+ const [statusFilter, setStatusFilter] = useState(null);
5938
+ const [selectedCompany, setSelectedCompany] = useState(null);
5939
+ const [showBatchDelete, setShowBatchDelete] = useState(false);
5940
+ const { data: companies, isLoading: companiesLoading } = useCompanies({
5941
+ search: companySearch || void 0,
5942
+ segment: segmentFilter || void 0,
5943
+ category: categoryFilter || void 0,
5944
+ status: statusFilter === "active" || statusFilter === "invalid" ? statusFilter : void 0
5945
+ });
5946
+ const deleteCompaniesMutation = useDeleteCompanies();
5947
+ const { sort, toggleSort } = useTableSort("company", "asc");
5948
+ const sortAccessors2 = useMemo(
5949
+ () => ({
5950
+ company: (company) => company.name,
5951
+ domain: (company) => company.domain || "",
5952
+ segment: (company) => company.segment || "",
5953
+ category: (company) => company.category || "",
5954
+ employees: (company) => company.numEmployees ?? 0,
5955
+ enrichment: (company) => getEnrichmentStatus(company.enrichmentData),
5956
+ contacts: (company) => company.contactCount
5957
+ }),
5958
+ []
5472
5959
  );
5960
+ const sortedCompanies = useMemo(
5961
+ () => sortData(companies ?? [], sort, sortAccessors2),
5962
+ [companies, sort, sortAccessors2]
5963
+ );
5964
+ const pagination = usePaginationState(PAGE_SIZE_DEFAULT3, [companySearch, segmentFilter, categoryFilter, statusFilter], sortedCompanies.length);
5965
+ const paginatedCompanies = useMemo(
5966
+ () => sortedCompanies.slice(pagination.offset, pagination.offset + PAGE_SIZE_DEFAULT3),
5967
+ [sortedCompanies, pagination.offset]
5968
+ );
5969
+ const selection = useTableSelection(paginatedCompanies, sortedCompanies);
5970
+ function handleBatchDelete() {
5971
+ deleteCompaniesMutation.mutate([...selection.selectedIds], {
5972
+ onSuccess: () => {
5973
+ selection.clear();
5974
+ setShowBatchDelete(false);
5975
+ }
5976
+ });
5977
+ }
5978
+ return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsxs(PageContainer, { children: [
5979
+ /* @__PURE__ */ jsx(Stack, { children: /* @__PURE__ */ jsx(PageTitleCaption, { title: "Companies", caption: "Company records and enrichment tracking" }) }),
5980
+ /* @__PURE__ */ jsx(Paper, { children: /* @__PURE__ */ jsxs(Stack, { children: [
5981
+ /* @__PURE__ */ jsxs(
5982
+ FilterBar,
5983
+ {
5984
+ actions: /* @__PURE__ */ jsx(
5985
+ TableSelectionToolbar,
5986
+ {
5987
+ selectedCount: selection.selectedCount,
5988
+ onDelete: () => setShowBatchDelete(true),
5989
+ isDeleting: deleteCompaniesMutation.isPending
5990
+ }
5991
+ ),
5992
+ children: [
5993
+ /* @__PURE__ */ jsx(
5994
+ Select,
5995
+ {
5996
+ placeholder: "All Segments",
5997
+ data: ["Technology", "Marketing", "Finance", "Healthcare"],
5998
+ value: segmentFilter,
5999
+ onChange: setSegmentFilter,
6000
+ style: { minWidth: 180 },
6001
+ size: "sm",
6002
+ clearable: true
6003
+ }
6004
+ ),
6005
+ /* @__PURE__ */ jsx(
6006
+ Select,
6007
+ {
6008
+ placeholder: "All Categories",
6009
+ data: ["SaaS", "Service Agency", "Software"],
6010
+ value: categoryFilter,
6011
+ onChange: setCategoryFilter,
6012
+ style: { minWidth: 160 },
6013
+ size: "sm",
6014
+ clearable: true
6015
+ }
6016
+ ),
6017
+ /* @__PURE__ */ jsx(
6018
+ Select,
6019
+ {
6020
+ placeholder: "All Statuses",
6021
+ data: [
6022
+ { value: "active", label: "Active" },
6023
+ { value: "invalid", label: "Invalid" }
6024
+ ],
6025
+ value: statusFilter,
6026
+ onChange: setStatusFilter,
6027
+ style: { minWidth: 160 },
6028
+ size: "sm",
6029
+ clearable: true
6030
+ }
6031
+ ),
6032
+ /* @__PURE__ */ jsx(
6033
+ TextInput,
6034
+ {
6035
+ placeholder: "Search by name or domain...",
6036
+ leftSection: /* @__PURE__ */ jsx(IconSearch, { size: 16 }),
6037
+ style: { minWidth: 250 },
6038
+ size: "sm",
6039
+ value: companySearch,
6040
+ onChange: (e) => setCompanySearch(e.target.value)
6041
+ }
6042
+ )
6043
+ ]
6044
+ }
6045
+ ),
6046
+ companiesLoading ? /* @__PURE__ */ jsx(Center, { p: "xl", children: /* @__PURE__ */ jsx(Loader, {}) }) : !sortedCompanies.length ? /* @__PURE__ */ jsx(EmptyState, { icon: IconBuildingFactory2, title: "No companies yet", description: "Add one to get started." }) : /* @__PURE__ */ jsxs(Table, { children: [
6047
+ /* @__PURE__ */ jsx(Table.Thead, { children: /* @__PURE__ */ jsxs(Table.Tr, { children: [
6048
+ /* @__PURE__ */ jsx(Table.Th, { w: 40, children: /* @__PURE__ */ jsx(
6049
+ Checkbox,
6050
+ {
6051
+ checked: selection.isPageAllSelected,
6052
+ indeterminate: selection.isPagePartiallySelected,
6053
+ onChange: selection.togglePage
6054
+ }
6055
+ ) }),
6056
+ /* @__PURE__ */ jsx(SortableHeader, { column: "company", sort, onToggle: toggleSort, children: "Company" }),
6057
+ /* @__PURE__ */ jsx(SortableHeader, { column: "domain", sort, onToggle: toggleSort, children: "Domain" }),
6058
+ /* @__PURE__ */ jsx(SortableHeader, { column: "segment", sort, onToggle: toggleSort, children: "Segment" }),
6059
+ /* @__PURE__ */ jsx(SortableHeader, { column: "category", sort, onToggle: toggleSort, children: "Category" }),
6060
+ /* @__PURE__ */ jsx(SortableHeader, { column: "employees", sort, onToggle: toggleSort, children: "Employees" }),
6061
+ /* @__PURE__ */ jsx(SortableHeader, { column: "enrichment", sort, onToggle: toggleSort, children: "Enrichment" }),
6062
+ /* @__PURE__ */ jsx(SortableHeader, { column: "contacts", sort, onToggle: toggleSort, children: "Contacts" })
6063
+ ] }) }),
6064
+ /* @__PURE__ */ jsx(Table.Tbody, { children: paginatedCompanies.map((company) => {
6065
+ const enrichStatus = getEnrichmentStatus(company.enrichmentData);
6066
+ return /* @__PURE__ */ jsxs(
6067
+ Table.Tr,
6068
+ {
6069
+ style: { cursor: "pointer" },
6070
+ onClick: () => setSelectedCompany(company),
6071
+ children: [
6072
+ /* @__PURE__ */ jsx(
6073
+ Table.Td,
6074
+ {
6075
+ onClick: (e) => {
6076
+ e.stopPropagation();
6077
+ selection.toggle(company.id);
6078
+ },
6079
+ children: /* @__PURE__ */ jsx(
6080
+ Checkbox,
6081
+ {
6082
+ checked: selection.isSelected(company.id),
6083
+ onChange: () => selection.toggle(company.id),
6084
+ onClick: (e) => e.stopPropagation()
6085
+ }
6086
+ )
6087
+ }
6088
+ ),
6089
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { fw: 500, children: company.name }) }),
6090
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: company.domain || "-" }) }),
6091
+ /* @__PURE__ */ jsx(Table.Td, { children: company.segment || "-" }),
6092
+ /* @__PURE__ */ jsx(Table.Td, { children: company.category || "-" }),
6093
+ /* @__PURE__ */ jsx(Table.Td, { children: company.numEmployees || "-" }),
6094
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Badge, { variant: "light", color: getEnrichmentColor(enrichStatus), size: "sm", children: enrichStatus }) }),
6095
+ /* @__PURE__ */ jsx(Table.Td, { children: company.contactCount })
6096
+ ]
6097
+ },
6098
+ company.id
6099
+ );
6100
+ }) })
6101
+ ] }),
6102
+ sortedCompanies.length > PAGE_SIZE_DEFAULT3 ? /* @__PURE__ */ jsx(Group, { justify: "center", children: /* @__PURE__ */ jsx(
6103
+ Pagination,
6104
+ {
6105
+ value: pagination.page,
6106
+ onChange: pagination.setPage,
6107
+ total: pagination.totalPages(sortedCompanies.length),
6108
+ size: "sm"
6109
+ }
6110
+ ) }) : null
6111
+ ] }) }),
6112
+ /* @__PURE__ */ jsx(CompanyDetailModal, { company: selectedCompany, onClose: () => setSelectedCompany(null) }),
6113
+ /* @__PURE__ */ jsx(CustomModal, { opened: showBatchDelete, onClose: () => setShowBatchDelete(false), size: "sm", children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
6114
+ /* @__PURE__ */ jsxs(Group, { gap: "sm", children: [
6115
+ /* @__PURE__ */ jsx(IconAlertTriangle, { size: 20, color: "var(--mantine-color-red-6)" }),
6116
+ /* @__PURE__ */ jsxs(Title, { order: 5, children: [
6117
+ "Delete ",
6118
+ selection.selectedCount,
6119
+ " companies?"
6120
+ ] })
6121
+ ] }),
6122
+ /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "This will permanently delete the selected companies and cannot be undone." }),
6123
+ /* @__PURE__ */ jsxs(Group, { justify: "flex-end", gap: "sm", children: [
6124
+ /* @__PURE__ */ jsx(Button, { variant: "default", size: "sm", onClick: () => setShowBatchDelete(false), children: "Cancel" }),
6125
+ /* @__PURE__ */ jsx(Button, { color: "red", size: "sm", loading: deleteCompaniesMutation.isPending, onClick: handleBatchDelete, children: "Delete" })
6126
+ ] })
6127
+ ] }) })
6128
+ ] }) });
5473
6129
  }
6130
+ var PAGE_SIZE_DEFAULT4 = 20;
5474
6131
  function LeadGenContactsPage() {
5475
- return /* @__PURE__ */ jsx(
5476
- LeadGenRouteShell,
5477
- {
5478
- title: "Contacts",
5479
- caption: "Shared lead-gen navigation shell",
5480
- body: /* @__PURE__ */ jsxs(Fragment, { children: [
5481
- /* @__PURE__ */ jsx(Text, { fw: 500, children: "Contact management stays app-owned here." }),
5482
- /* @__PURE__ */ jsx(Text, { c: "dimmed", children: "The shared package preserves the route and sibling links so every workspace keeps the same lead-gen surface, even when the detailed contact experience remains local." })
5483
- ] })
5484
- }
6132
+ const [contactSearch, setContactSearch] = useState("");
6133
+ const [statusFilter, setStatusFilter] = useState(null);
6134
+ const [listFilter, setListFilter] = useState(null);
6135
+ const [selectedContact, setSelectedContact] = useState(null);
6136
+ const [showBatchDelete, setShowBatchDelete] = useState(false);
6137
+ const { data: lists } = useLists();
6138
+ const { data: contacts, isLoading: contactsLoading } = useContacts({
6139
+ search: contactSearch || void 0,
6140
+ contactStatus: statusFilter === "active" || statusFilter === "invalid" ? statusFilter : void 0,
6141
+ listId: listFilter || void 0
6142
+ });
6143
+ const deleteContactsMutation = useDeleteContacts();
6144
+ const { sort, toggleSort } = useTableSort("name", "asc");
6145
+ const sortAccessors2 = useMemo(
6146
+ () => ({
6147
+ name: (contact) => [contact.firstName, contact.lastName].filter(Boolean).join(" ") || contact.email,
6148
+ email: (contact) => contact.email,
6149
+ company: (contact) => contact.company?.name || "",
6150
+ title: (contact) => contact.title || "",
6151
+ status: (contact) => contact.status || "",
6152
+ personalized: (contact) => contact.openingLine ? "yes" : "no"
6153
+ }),
6154
+ []
6155
+ );
6156
+ const sortedContacts = useMemo(
6157
+ () => sortData(contacts ?? [], sort, sortAccessors2),
6158
+ [contacts, sort, sortAccessors2]
6159
+ );
6160
+ const pagination = usePaginationState(PAGE_SIZE_DEFAULT4, [contactSearch, statusFilter, listFilter], sortedContacts.length);
6161
+ const paginatedContacts = useMemo(
6162
+ () => sortedContacts.slice(pagination.offset, pagination.offset + PAGE_SIZE_DEFAULT4),
6163
+ [sortedContacts, pagination.offset]
5485
6164
  );
6165
+ const selection = useTableSelection(paginatedContacts, sortedContacts);
6166
+ function handleBatchDelete() {
6167
+ deleteContactsMutation.mutate([...selection.selectedIds], {
6168
+ onSuccess: () => {
6169
+ selection.clear();
6170
+ setShowBatchDelete(false);
6171
+ }
6172
+ });
6173
+ }
6174
+ return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsxs(PageContainer, { children: [
6175
+ /* @__PURE__ */ jsx(Stack, { children: /* @__PURE__ */ jsx(PageTitleCaption, { title: "Contacts", caption: "Contact records and outreach tracking" }) }),
6176
+ /* @__PURE__ */ jsx(Paper, { children: /* @__PURE__ */ jsxs(Stack, { children: [
6177
+ /* @__PURE__ */ jsxs(
6178
+ FilterBar,
6179
+ {
6180
+ actions: /* @__PURE__ */ jsx(
6181
+ TableSelectionToolbar,
6182
+ {
6183
+ selectedCount: selection.selectedCount,
6184
+ onDelete: () => setShowBatchDelete(true),
6185
+ isDeleting: deleteContactsMutation.isPending
6186
+ }
6187
+ ),
6188
+ children: [
6189
+ /* @__PURE__ */ jsx(
6190
+ Select,
6191
+ {
6192
+ placeholder: "All Statuses",
6193
+ data: [
6194
+ { value: "active", label: "Active" },
6195
+ { value: "invalid", label: "Invalid" }
6196
+ ],
6197
+ value: statusFilter,
6198
+ onChange: setStatusFilter,
6199
+ style: { minWidth: 160 },
6200
+ size: "sm",
6201
+ clearable: true
6202
+ }
6203
+ ),
6204
+ /* @__PURE__ */ jsx(
6205
+ Select,
6206
+ {
6207
+ placeholder: "All Lists",
6208
+ data: (lists ?? []).map((list) => ({ value: list.id, label: list.name })),
6209
+ value: listFilter,
6210
+ onChange: setListFilter,
6211
+ style: { minWidth: 200 },
6212
+ size: "sm",
6213
+ clearable: true
6214
+ }
6215
+ ),
6216
+ /* @__PURE__ */ jsx(
6217
+ TextInput,
6218
+ {
6219
+ placeholder: "Search by name or email...",
6220
+ leftSection: /* @__PURE__ */ jsx(IconSearch, { size: 16 }),
6221
+ style: { minWidth: 250 },
6222
+ size: "sm",
6223
+ value: contactSearch,
6224
+ onChange: (e) => setContactSearch(e.target.value)
6225
+ }
6226
+ )
6227
+ ]
6228
+ }
6229
+ ),
6230
+ contactsLoading ? /* @__PURE__ */ jsx(Center, { p: "xl", children: /* @__PURE__ */ jsx(Loader, {}) }) : !sortedContacts.length ? /* @__PURE__ */ jsx(EmptyState, { icon: IconUsers, title: "No contacts yet", description: "Add one to get started." }) : /* @__PURE__ */ jsxs(Table, { children: [
6231
+ /* @__PURE__ */ jsx(Table.Thead, { children: /* @__PURE__ */ jsxs(Table.Tr, { children: [
6232
+ /* @__PURE__ */ jsx(Table.Th, { w: 40, children: /* @__PURE__ */ jsx(
6233
+ Checkbox,
6234
+ {
6235
+ checked: selection.isPageAllSelected,
6236
+ indeterminate: selection.isPagePartiallySelected,
6237
+ onChange: selection.togglePage
6238
+ }
6239
+ ) }),
6240
+ /* @__PURE__ */ jsx(SortableHeader, { column: "name", sort, onToggle: toggleSort, children: "Name" }),
6241
+ /* @__PURE__ */ jsx(SortableHeader, { column: "email", sort, onToggle: toggleSort, children: "Email" }),
6242
+ /* @__PURE__ */ jsx(SortableHeader, { column: "company", sort, onToggle: toggleSort, children: "Company" }),
6243
+ /* @__PURE__ */ jsx(SortableHeader, { column: "title", sort, onToggle: toggleSort, children: "Title" }),
6244
+ /* @__PURE__ */ jsx(SortableHeader, { column: "status", sort, onToggle: toggleSort, children: "Status" }),
6245
+ /* @__PURE__ */ jsx(SortableHeader, { column: "personalized", sort, onToggle: toggleSort, children: "Personalized" })
6246
+ ] }) }),
6247
+ /* @__PURE__ */ jsx(Table.Tbody, { children: paginatedContacts.map((contact) => {
6248
+ const fullName = [contact.firstName, contact.lastName].filter(Boolean).join(" ") || contact.email;
6249
+ const hasPersonalization = !!contact.openingLine;
6250
+ return /* @__PURE__ */ jsxs(
6251
+ Table.Tr,
6252
+ {
6253
+ style: { cursor: "pointer" },
6254
+ onClick: () => setSelectedContact(contact),
6255
+ children: [
6256
+ /* @__PURE__ */ jsx(
6257
+ Table.Td,
6258
+ {
6259
+ onClick: (e) => {
6260
+ e.stopPropagation();
6261
+ selection.toggle(contact.id);
6262
+ },
6263
+ children: /* @__PURE__ */ jsx(
6264
+ Checkbox,
6265
+ {
6266
+ checked: selection.isSelected(contact.id),
6267
+ onChange: () => selection.toggle(contact.id),
6268
+ onClick: (e) => e.stopPropagation()
6269
+ }
6270
+ )
6271
+ }
6272
+ ),
6273
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { fw: 500, children: fullName }) }),
6274
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: contact.email }) }),
6275
+ /* @__PURE__ */ jsx(Table.Td, { children: contact.company?.name || "-" }),
6276
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: contact.title || "-" }) }),
6277
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Badge, { variant: "light", color: getStatusColor3(contact.status), size: "sm", children: contact.status || "unknown" }) }),
6278
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Badge, { variant: "light", color: hasPersonalization ? "green" : "gray", size: "sm", children: hasPersonalization ? "Yes" : "No" }) })
6279
+ ]
6280
+ },
6281
+ contact.id
6282
+ );
6283
+ }) })
6284
+ ] }),
6285
+ sortedContacts.length > PAGE_SIZE_DEFAULT4 ? /* @__PURE__ */ jsx(Group, { justify: "center", children: /* @__PURE__ */ jsx(
6286
+ Pagination,
6287
+ {
6288
+ value: pagination.page,
6289
+ onChange: pagination.setPage,
6290
+ total: pagination.totalPages(sortedContacts.length),
6291
+ size: "sm"
6292
+ }
6293
+ ) }) : null
6294
+ ] }) }),
6295
+ /* @__PURE__ */ jsx(ContactDetailModal, { contact: selectedContact, onClose: () => setSelectedContact(null) }),
6296
+ /* @__PURE__ */ jsx(CustomModal, { opened: showBatchDelete, onClose: () => setShowBatchDelete(false), size: "sm", children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
6297
+ /* @__PURE__ */ jsxs(Group, { gap: "sm", children: [
6298
+ /* @__PURE__ */ jsx(IconAlertTriangle, { size: 20, color: "var(--mantine-color-red-6)" }),
6299
+ /* @__PURE__ */ jsxs(Title, { order: 5, children: [
6300
+ "Delete ",
6301
+ selection.selectedCount,
6302
+ " contacts?"
6303
+ ] })
6304
+ ] }),
6305
+ /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "This will permanently delete the selected contacts and cannot be undone." }),
6306
+ /* @__PURE__ */ jsxs(Group, { justify: "flex-end", gap: "sm", children: [
6307
+ /* @__PURE__ */ jsx(Button, { variant: "default", size: "sm", onClick: () => setShowBatchDelete(false), children: "Cancel" }),
6308
+ /* @__PURE__ */ jsx(Button, { color: "red", size: "sm", loading: deleteContactsMutation.isPending, onClick: handleBatchDelete, children: "Delete" })
6309
+ ] })
6310
+ ] }) })
6311
+ ] }) });
5486
6312
  }
5487
6313
  var SEOSidebarTop = () => {
5488
6314
  return /* @__PURE__ */ jsx(SubshellSidebarSection, { icon: IconSearch, label: "SEO" });
@@ -5940,6 +6766,10 @@ function ProjectsListPage({ onProjectClick } = {}) {
5940
6766
  /* @__PURE__ */ jsx(SortableHeader, { column: "updated", sort, onToggle: toggleSort, children: "Updated" })
5941
6767
  ] }) }),
5942
6768
  /* @__PURE__ */ jsx(Table.Tbody, { children: paginatedProjects.map((project) => {
6769
+ const completedMilestones = project.completedMilestones ?? 0;
6770
+ const milestoneCount = project.milestoneCount ?? 0;
6771
+ const completedTasks = project.completedTasks ?? 0;
6772
+ const taskCount = project.taskCount ?? 0;
5943
6773
  const rowProps = onProjectClick ? {
5944
6774
  style: { cursor: "pointer" },
5945
6775
  onClick: () => onProjectClick(project.id)
@@ -5955,14 +6785,14 @@ function ProjectsListPage({ onProjectClick } = {}) {
5955
6785
  /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { fw: 500, children: project.name }) }),
5956
6786
  /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Badge, { variant: "light", color: projectStatusColors[project.status || ""] || "gray", size: "sm", children: formatStatusLabel(project.status || "unknown") }) }),
5957
6787
  /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsxs(Text, { size: "sm", children: [
5958
- project.completedMilestones ?? 0,
6788
+ completedMilestones,
5959
6789
  "/",
5960
- project.milestoneCount
6790
+ milestoneCount
5961
6791
  ] }) }),
5962
6792
  /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsxs(Text, { size: "sm", children: [
5963
- project.completedTasks ?? 0,
6793
+ completedTasks,
5964
6794
  "/",
5965
- project.taskCount
6795
+ taskCount
5966
6796
  ] }) }),
5967
6797
  /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: formatTimeAgo(project.updated_at) }) })
5968
6798
  ] }, project.id);