@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.
- package/dist/{chunk-QRHLV74B.js → chunk-2JTCPVZX.js} +2 -2
- package/dist/{chunk-RB34YOIX.js → chunk-3WMJWXZY.js} +2 -2
- package/dist/{chunk-MVJ4TSSA.js → chunk-5KBVISVJ.js} +3 -3
- package/dist/{chunk-TZOGB3X4.js → chunk-J5TBNCMD.js} +250 -1
- package/dist/{chunk-PRLXFMNP.js → chunk-JT3FN6TE.js} +1 -1
- package/dist/{chunk-QITPFGWC.js → chunk-LH7RCX4Y.js} +1 -1
- package/dist/{chunk-35QO7M43.js → chunk-M66JAN7R.js} +1 -1
- package/dist/{chunk-PQNEE57X.js → chunk-O6KZ46EJ.js} +3 -3
- package/dist/{chunk-OKKGD3S6.js → chunk-PFONCU6C.js} +1 -1
- package/dist/{chunk-H762MTQ5.js → chunk-YOZEGIZA.js} +4 -4
- package/dist/components/index.d.ts +5 -3
- package/dist/components/index.js +946 -116
- package/dist/features/dashboard/index.js +5 -5
- package/dist/features/monitoring/index.js +6 -6
- package/dist/features/operations/index.js +7 -7
- package/dist/features/settings/index.js +6 -6
- package/dist/hooks/index.d.ts +299 -2
- package/dist/hooks/index.js +4 -4
- package/dist/hooks/published.d.ts +299 -2
- package/dist/hooks/published.js +3 -3
- package/dist/index.d.ts +299 -2
- package/dist/index.js +5 -5
- package/dist/provider/index.js +2 -2
- package/dist/theme/index.js +2 -2
- package/package.json +1 -1
package/dist/components/index.js
CHANGED
|
@@ -1,30 +1,30 @@
|
|
|
1
1
|
import { useBreadcrumbs } from '../chunk-MG3NF7QL.js';
|
|
2
2
|
import '../chunk-SMJLS23U.js';
|
|
3
|
-
import { NotificationList } from '../chunk-
|
|
4
|
-
export { ActivityCard, ActivityFilters as ActivityFiltersBar, ActivityTable, BusinessImpactCard, CostBreakdownCard, CostByModelTable, CostMetricsCard, ErrorAnalysisCard, ErrorBreakdownTable, ExecutionBreakdownTable, ExecutionHealthCard, ExecutionLogsFilters as ExecutionLogsFilterBar, ExecutionLogsTable, NotificationItem, NotificationList, monitoringManifest } from '../chunk-
|
|
5
|
-
export { CreateCredentialModal, CredentialList, CredentialSettings, MembershipFeaturePanel, MembershipStatusBadge, OAuthConnectModal, OrganizationMembershipsList, WebhookUrlDisplayModal, settingsManifest } from '../chunk-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
20
|
-
import { useUpdateApiKey, useDeleteApiKey, useCreateApiKey, useListApiKeys, useActivateDeployment, useDeactivateDeployment, useDeleteDeployment, useListDeployments, useProjects } from '../chunk-
|
|
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-
|
|
22
|
-
export { showApiErrorNotification, showErrorNotification, showInfoNotification, showSuccessNotification, showWarningNotification } from '../chunk-
|
|
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-
|
|
27
|
-
import '../chunk-
|
|
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,
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
3261
|
-
const
|
|
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" :
|
|
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(
|
|
4550
|
-
|
|
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
|
|
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
|
|
4780
|
-
|
|
4781
|
-
|
|
4782
|
-
|
|
4783
|
-
|
|
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:
|
|
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:
|
|
4795
|
-
/* @__PURE__ */ jsx(StatCard, { variant: "hero", icon:
|
|
4796
|
-
/* @__PURE__ */ jsx(StatCard, { variant: "hero", icon:
|
|
4797
|
-
/* @__PURE__ */ jsx(StatCard, { variant: "hero", icon:
|
|
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__ */
|
|
4801
|
-
"
|
|
4802
|
-
|
|
4803
|
-
|
|
4804
|
-
|
|
4805
|
-
|
|
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(
|
|
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(
|
|
5340
|
-
|
|
5341
|
-
|
|
5342
|
-
|
|
5343
|
-
|
|
5344
|
-
|
|
5345
|
-
|
|
5346
|
-
|
|
5347
|
-
|
|
5348
|
-
|
|
5349
|
-
|
|
5350
|
-
|
|
5351
|
-
|
|
5352
|
-
|
|
5353
|
-
|
|
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
|
-
|
|
5463
|
-
|
|
5464
|
-
|
|
5465
|
-
|
|
5466
|
-
|
|
5467
|
-
|
|
5468
|
-
|
|
5469
|
-
|
|
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
|
-
|
|
5476
|
-
|
|
5477
|
-
|
|
5478
|
-
|
|
5479
|
-
|
|
5480
|
-
|
|
5481
|
-
|
|
5482
|
-
|
|
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
|
-
|
|
6788
|
+
completedMilestones,
|
|
5959
6789
|
"/",
|
|
5960
|
-
|
|
6790
|
+
milestoneCount
|
|
5961
6791
|
] }) }),
|
|
5962
6792
|
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsxs(Text, { size: "sm", children: [
|
|
5963
|
-
|
|
6793
|
+
completedTasks,
|
|
5964
6794
|
"/",
|
|
5965
|
-
|
|
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);
|