@elevasis/ui 2.2.0 → 2.3.0
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-J5TBNCMD.js → chunk-2XWEOJSX.js} +3 -3
- package/dist/{chunk-RB34YOIX.js → chunk-47YILFON.js} +98 -70
- package/dist/{chunk-MZPVNRPL.js → chunk-ISHNN42L.js} +134 -10
- package/dist/{chunk-PFONCU6C.js → chunk-IWFIKQR5.js} +1 -1
- package/dist/{chunk-QITPFGWC.js → chunk-JT7WDIZI.js} +2 -2
- package/dist/{chunk-7IE3KXKV.js → chunk-NKV5MEWQ.js} +3877 -1543
- package/dist/{chunk-MXVA7U2I.js → chunk-PEATQEEP.js} +3 -3
- package/dist/{chunk-JT3FN6TE.js → chunk-Q3FTQP2M.js} +2 -2
- package/dist/{chunk-BIZNOFO4.js → chunk-SWIAK47F.js} +3 -3
- package/dist/{chunk-35QO7M43.js → chunk-VNUOQQNY.js} +1 -1
- package/dist/{chunk-2JTCPVZX.js → chunk-ZY4MWZW2.js} +2 -2
- package/dist/components/index.css +7 -0
- package/dist/components/index.d.ts +16 -4
- package/dist/components/index.js +1214 -120
- package/dist/features/auth/index.css +7 -0
- package/dist/features/dashboard/index.css +7 -0
- package/dist/features/dashboard/index.d.ts +4 -0
- package/dist/features/dashboard/index.js +6 -6
- package/dist/features/monitoring/index.css +7 -0
- package/dist/features/monitoring/index.d.ts +4 -0
- package/dist/features/monitoring/index.js +7 -7
- package/dist/features/operations/index.css +7 -0
- package/dist/features/operations/index.d.ts +26 -14
- package/dist/features/operations/index.js +8 -8
- package/dist/features/settings/index.css +7 -0
- package/dist/features/settings/index.d.ts +4 -0
- package/dist/features/settings/index.js +7 -7
- package/dist/hooks/index.css +7 -0
- package/dist/hooks/index.d.ts +4 -1
- package/dist/hooks/index.js +5 -5
- package/dist/hooks/published.css +7 -0
- package/dist/hooks/published.d.ts +4 -1
- package/dist/hooks/published.js +4 -4
- package/dist/index.css +7 -0
- package/dist/index.d.ts +307 -249
- package/dist/index.js +6 -6
- package/dist/provider/index.css +7 -0
- package/dist/provider/index.d.ts +58 -2
- package/dist/provider/index.js +3 -3
- package/dist/provider/published.d.ts +58 -2
- package/dist/provider/published.js +1 -1
- package/dist/theme/index.js +2 -2
- package/package.json +9 -3
package/dist/components/index.js
CHANGED
|
@@ -1,32 +1,34 @@
|
|
|
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-ZY4MWZW2.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-ZY4MWZW2.js';
|
|
5
|
+
export { CreateCredentialModal, CredentialList, CredentialSettings, MembershipFeaturePanel, MembershipStatusBadge, OAuthConnectModal, OrganizationMembershipsList, WebhookUrlDisplayModal, settingsManifest } from '../chunk-PEATQEEP.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,
|
|
8
|
+
import { ResourceExecuteDialog } from '../chunk-NKV5MEWQ.js';
|
|
9
|
+
export { ActionModal, AgentDefinitionDisplay, AgentExecutionLogs, BaseExecutionLogs, BaseExecutionLogsHeader, BaseExecutionLogsStates, CheckpointGroup, CollapsibleJsonSection, CommandQueueSidebar, CommandQueueSidebarMiddle, CommandQueueSidebarTop, CommandQueueTaskRow, 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-NKV5MEWQ.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-IWFIKQR5.js';
|
|
13
13
|
import { CustomModal } from '../chunk-GBMNCNHX.js';
|
|
14
14
|
export { ConfirmationInputModal, ConfirmationModal, CustomModal } from '../chunk-GBMNCNHX.js';
|
|
15
|
-
|
|
15
|
+
import { BaseNode, useGraphTheme, BaseEdge, GraphBackground, GraphLegend, GraphFitViewButton } from '../chunk-SWIAK47F.js';
|
|
16
|
+
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-SWIAK47F.js';
|
|
16
17
|
export { ResourceHealthChart, getHealthColor } from '../chunk-LGKLC5MG.js';
|
|
17
18
|
import '../chunk-KFICYU6S.js';
|
|
18
19
|
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, useCompanies, useDeleteCompanies, useContacts, useDeleteContacts, useDeleteProject, useMarkAllAsRead, useNotifications, showErrorNotification } from '../chunk-
|
|
22
|
-
export { showApiErrorNotification, showErrorNotification, showInfoNotification, showSuccessNotification, showWarningNotification } from '../chunk-
|
|
20
|
+
import '../chunk-VNUOQQNY.js';
|
|
21
|
+
import { useUpdateApiKey, useDeleteApiKey, useCreateApiKey, useListApiKeys, useActivateDeployment, useDeactivateDeployment, useDeleteDeployment, useListDeployments, useProjects, useProject, useProjectNotes, useDeleteProject as useDeleteProject$1, useUpdateMilestone, useCreateNote } from '../chunk-Q3FTQP2M.js';
|
|
22
|
+
import { useCommandViewLayout, 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-2XWEOJSX.js';
|
|
23
|
+
export { showApiErrorNotification, showErrorNotification, showInfoNotification, showSuccessNotification, showWarningNotification } from '../chunk-2XWEOJSX.js';
|
|
23
24
|
import '../chunk-LXHZYSMQ.js';
|
|
25
|
+
import { Graph_module_css_default, useDirectedChainHighlighting, useNodeSelection, GRAPH_CONSTANTS } from '../chunk-F6RBK7NJ.js';
|
|
24
26
|
export { Graph_module_css_default as graphStyles } from '../chunk-F6RBK7NJ.js';
|
|
25
27
|
export { CONTAINER_CONSTANTS, SHARED_VIZ_CONSTANTS } from '../chunk-XA34RETF.js';
|
|
26
|
-
import '../chunk-
|
|
27
|
-
import '../chunk-
|
|
28
|
+
import '../chunk-JT7WDIZI.js';
|
|
29
|
+
import '../chunk-47YILFON.js';
|
|
28
30
|
import '../chunk-CYXZHBP4.js';
|
|
29
|
-
import '../chunk-
|
|
31
|
+
import '../chunk-ISHNN42L.js';
|
|
30
32
|
import { SubshellContainer, SubshellSidebar, SubshellRightSideContainer, SubshellContentContainer } from '../chunk-RX4UWZZR.js';
|
|
31
33
|
import { ListSkeleton, EmptyState, PageTitleCaption, StatCard, CenteredErrorState, CardHeader, ActivityTimeline, StatusBadge } from '../chunk-Y3D3WFJG.js';
|
|
32
34
|
export { APIErrorAlert, ActivityTimeline, CardHeader, CenteredErrorState, CollapsibleSection, ContextViewer, CustomSelector, DetailCardSkeleton, ElevasisLoader, EmptyState, FeatureUnavailableState, GlowDot, JsonViewer, ListSkeleton, PageNotFound, PageTitleCaption, ResourceCard, StatCard, StatCardSkeleton, StatsCardSkeleton, StatusBadge, TabCountBadge, TimeRangeSelector, TrendIndicator, catalogItemToResourceDefinition } from '../chunk-Y3D3WFJG.js';
|
|
@@ -40,21 +42,23 @@ import { useAppearance } from '../chunk-QJ2KCHKX.js';
|
|
|
40
42
|
import '../chunk-DT3QYZVU.js';
|
|
41
43
|
import '../chunk-SLVC5OJ2.js';
|
|
42
44
|
import '../chunk-RNP5R5I3.js';
|
|
43
|
-
import { formatDateTime, PAGE_SIZE_DEFAULT, formatTimeAgo } from '../chunk-IOKL7BKE.js';
|
|
45
|
+
import { getResourceIcon, getResourceColor, formatDateTime, PAGE_SIZE_DEFAULT, formatTimeAgo, formatDate } from '../chunk-IOKL7BKE.js';
|
|
44
46
|
import '../chunk-MTJ43R2E.js';
|
|
45
47
|
import { useInitialization } from '../chunk-TUXTSEAF.js';
|
|
46
48
|
import '../chunk-DD3CCMCZ.js';
|
|
47
49
|
import { useElevasisServices } from '../chunk-QEPXAWE2.js';
|
|
48
50
|
import '../chunk-BRJ3QZ4E.js';
|
|
49
51
|
import { useRouterContext } from '../chunk-Q7DJKLEN.js';
|
|
50
|
-
import {
|
|
51
|
-
import { IconAddressBook, IconBriefcase, IconTarget, IconChevronUp, IconChevronDown, IconSelector, IconTrash, IconPencil,
|
|
52
|
+
import { Stack, Group, ThemeIcon, Text, Badge, Box, Table, Button, Title, TextInput, Alert, Tooltip, ActionIcon, Paper, Code, CopyButton, SimpleGrid, Loader, Pagination, useMantineTheme, ScrollArea, Select, Center, Card, SegmentedControl, Switch, Textarea, Divider, Menu, Timeline, Tabs, Breadcrumbs as Breadcrumbs$1, Drawer, UnstyledButton, Modal, Checkbox, Anchor, Progress, RingProgress, Collapse, Popover, Indicator } from '@mantine/core';
|
|
53
|
+
import { IconBrain, IconDatabase, IconMessage, IconAlertCircle, IconCircleX, IconCircleCheck, IconBolt, IconHandClick, IconClock, IconWebhook, IconExternalLink, IconAddressBook, IconBriefcase, IconTarget, IconChevronUp, IconChevronDown, IconSelector, IconTrash, IconPencil, IconKey, IconCalendar, IconAlertTriangle, IconExclamationMark, IconShieldLock, IconCheck, IconCopy, IconPlus, IconRocket, IconRefresh, IconPower, IconPlayerPlay, IconTag, IconBook2, IconFileOff, IconList, IconCalendarRepeat, IconCalendarEvent, IconCalendarTime, IconRobot, IconGitBranch, IconSettings, 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
54
|
import * as runtime from 'react/jsx-runtime';
|
|
53
|
-
import {
|
|
55
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
54
56
|
import { useDisclosure } from '@mantine/hooks';
|
|
55
57
|
import { useQueryClient, useQuery, useMutation } from '@tanstack/react-query';
|
|
56
|
-
import {
|
|
58
|
+
import { memo, forwardRef, useMemo, useImperativeHandle, useState, useEffect, useCallback, useRef } from 'react';
|
|
57
59
|
import { useForm } from '@mantine/form';
|
|
60
|
+
import { useReactFlow, ReactFlow, ReactFlowProvider } from '@xyflow/react';
|
|
61
|
+
import '@xyflow/react/dist/style.css';
|
|
58
62
|
import { Link, RichTextEditor as RichTextEditor$1 } from '@mantine/tiptap';
|
|
59
63
|
import { useEditor } from '@tiptap/react';
|
|
60
64
|
import Placeholder from '@tiptap/extension-placeholder';
|
|
@@ -1737,7 +1741,7 @@ function getStatusColor(status) {
|
|
|
1737
1741
|
return "gray";
|
|
1738
1742
|
}
|
|
1739
1743
|
}
|
|
1740
|
-
function
|
|
1744
|
+
function formatDate2(date) {
|
|
1741
1745
|
if (!date) return "N/A";
|
|
1742
1746
|
const d = typeof date === "string" ? new Date(date) : date;
|
|
1743
1747
|
return d.toLocaleString("en-US", {
|
|
@@ -1777,7 +1781,7 @@ function ScheduleConfigDetails({ schedule }) {
|
|
|
1777
1781
|
] }),
|
|
1778
1782
|
config.endAt && /* @__PURE__ */ jsxs(Table.Tr, { children: [
|
|
1779
1783
|
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { c: "dimmed", children: "Ends" }) }),
|
|
1780
|
-
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { children:
|
|
1784
|
+
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { children: formatDate2(config.endAt) }) })
|
|
1781
1785
|
] }),
|
|
1782
1786
|
/* @__PURE__ */ jsxs(Table.Tr, { children: [
|
|
1783
1787
|
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { c: "dimmed", children: "Overdue" }) }),
|
|
@@ -1799,7 +1803,7 @@ function ScheduleConfigDetails({ schedule }) {
|
|
|
1799
1803
|
/* @__PURE__ */ jsxs(Table.Tr, { children: [
|
|
1800
1804
|
/* @__PURE__ */ jsx(Table.Td, { w: 120, children: /* @__PURE__ */ jsx(Text, { c: "dimmed", children: "Anchor" }) }),
|
|
1801
1805
|
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsxs(Text, { children: [
|
|
1802
|
-
|
|
1806
|
+
formatDate2(config.anchorAt),
|
|
1803
1807
|
config.anchorLabel && ` (${config.anchorLabel})`
|
|
1804
1808
|
] }) })
|
|
1805
1809
|
] }),
|
|
@@ -1820,7 +1824,7 @@ function ScheduleConfigDetails({ schedule }) {
|
|
|
1820
1824
|
] }),
|
|
1821
1825
|
/* @__PURE__ */ jsx(Stack, { gap: 4, children: config.items.map((item, i) => /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
|
|
1822
1826
|
/* @__PURE__ */ jsx(Badge, { size: "xs", variant: "light", color: i === schedule.currentStep ? "blue" : "gray", children: i + 1 }),
|
|
1823
|
-
/* @__PURE__ */ jsx(Text, { children:
|
|
1827
|
+
/* @__PURE__ */ jsx(Text, { children: formatDate2(item.runAt) }),
|
|
1824
1828
|
item.label && /* @__PURE__ */ jsxs(Text, { c: "dimmed", children: [
|
|
1825
1829
|
"\u2014 ",
|
|
1826
1830
|
item.label
|
|
@@ -1873,14 +1877,14 @@ function ScheduleDetailModal({ opened, onClose, schedule, resourceStatus }) {
|
|
|
1873
1877
|
/* @__PURE__ */ jsx(IconClock, { size: 12, color: "var(--color-text-subtle)" }),
|
|
1874
1878
|
/* @__PURE__ */ jsx(Text, { c: "dimmed", children: "Next Run" })
|
|
1875
1879
|
] }) }),
|
|
1876
|
-
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { children:
|
|
1880
|
+
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { children: formatDate2(schedule.nextRunAt) }) })
|
|
1877
1881
|
] }),
|
|
1878
1882
|
/* @__PURE__ */ jsxs(Table.Tr, { children: [
|
|
1879
1883
|
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsxs(Group, { gap: 4, children: [
|
|
1880
1884
|
/* @__PURE__ */ jsx(IconCalendar, { size: 12, color: "var(--color-text-subtle)" }),
|
|
1881
1885
|
/* @__PURE__ */ jsx(Text, { c: "dimmed", children: "Last Run" })
|
|
1882
1886
|
] }) }),
|
|
1883
|
-
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { children:
|
|
1887
|
+
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { children: formatDate2(schedule.lastRunAt) }) })
|
|
1884
1888
|
] }),
|
|
1885
1889
|
/* @__PURE__ */ jsxs(Table.Tr, { children: [
|
|
1886
1890
|
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsxs(Group, { gap: 4, children: [
|
|
@@ -1907,11 +1911,11 @@ function ScheduleDetailModal({ opened, onClose, schedule, resourceStatus }) {
|
|
|
1907
1911
|
/* @__PURE__ */ jsxs(Group, { gap: "lg", children: [
|
|
1908
1912
|
/* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
|
|
1909
1913
|
"Created ",
|
|
1910
|
-
|
|
1914
|
+
formatDate2(schedule.createdAt)
|
|
1911
1915
|
] }),
|
|
1912
1916
|
/* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
|
|
1913
1917
|
"Updated ",
|
|
1914
|
-
|
|
1918
|
+
formatDate2(schedule.updatedAt)
|
|
1915
1919
|
] })
|
|
1916
1920
|
] }),
|
|
1917
1921
|
/* @__PURE__ */ jsx(
|
|
@@ -2423,6 +2427,392 @@ var TaskScheduler = () => {
|
|
|
2423
2427
|
)
|
|
2424
2428
|
] });
|
|
2425
2429
|
};
|
|
2430
|
+
var triggerTypeIcons = {
|
|
2431
|
+
webhook: IconWebhook,
|
|
2432
|
+
schedule: IconClock,
|
|
2433
|
+
manual: IconHandClick,
|
|
2434
|
+
event: IconBolt
|
|
2435
|
+
};
|
|
2436
|
+
var connectionStatusIcons = {
|
|
2437
|
+
connected: IconCircleCheck,
|
|
2438
|
+
disconnected: IconCircleX,
|
|
2439
|
+
error: IconAlertCircle
|
|
2440
|
+
};
|
|
2441
|
+
var statBubbleBase = {
|
|
2442
|
+
minWidth: 20,
|
|
2443
|
+
height: 20,
|
|
2444
|
+
borderRadius: 10,
|
|
2445
|
+
display: "flex",
|
|
2446
|
+
alignItems: "center",
|
|
2447
|
+
justifyContent: "center",
|
|
2448
|
+
fontSize: 11,
|
|
2449
|
+
fontWeight: 600,
|
|
2450
|
+
padding: "0 6px",
|
|
2451
|
+
color: "white",
|
|
2452
|
+
boxShadow: "0 2px 4px rgba(0,0,0,0.2)"
|
|
2453
|
+
};
|
|
2454
|
+
var CommandViewNode = memo(function CommandViewNode2({ data, selected }) {
|
|
2455
|
+
const Icon = getResourceIcon(data.type);
|
|
2456
|
+
const color = getResourceColor(data.type);
|
|
2457
|
+
const successCount = (data.type === "agent" || data.type === "workflow") && data.stats ? data.stats.successCount : 0;
|
|
2458
|
+
const failureCount = (data.type === "agent" || data.type === "workflow") && data.stats ? data.stats.failureCount : 0;
|
|
2459
|
+
const warningCount = (data.type === "agent" || data.type === "workflow") && data.stats ? data.stats.warningCount : 0;
|
|
2460
|
+
const pendingCount = data.type === "human" && data.stats ? data.stats.pendingCount : 0;
|
|
2461
|
+
const showBubbles = successCount > 0 || failureCount > 0 || warningCount > 0 || pendingCount > 0;
|
|
2462
|
+
return /* @__PURE__ */ jsxs("div", { style: { position: "relative" }, children: [
|
|
2463
|
+
showBubbles && /* @__PURE__ */ jsxs(
|
|
2464
|
+
"div",
|
|
2465
|
+
{
|
|
2466
|
+
style: {
|
|
2467
|
+
position: "absolute",
|
|
2468
|
+
top: -8,
|
|
2469
|
+
right: -8,
|
|
2470
|
+
display: "flex",
|
|
2471
|
+
gap: 4,
|
|
2472
|
+
zIndex: 10
|
|
2473
|
+
},
|
|
2474
|
+
children: [
|
|
2475
|
+
successCount > 0 && /* @__PURE__ */ jsx(
|
|
2476
|
+
"div",
|
|
2477
|
+
{
|
|
2478
|
+
style: {
|
|
2479
|
+
...statBubbleBase,
|
|
2480
|
+
background: "linear-gradient(135deg, var(--mantine-color-green-5), var(--mantine-color-green-7))"
|
|
2481
|
+
},
|
|
2482
|
+
children: successCount
|
|
2483
|
+
}
|
|
2484
|
+
),
|
|
2485
|
+
warningCount > 0 && /* @__PURE__ */ jsx(
|
|
2486
|
+
"div",
|
|
2487
|
+
{
|
|
2488
|
+
style: {
|
|
2489
|
+
...statBubbleBase,
|
|
2490
|
+
background: "linear-gradient(135deg, var(--mantine-color-yellow-5), var(--mantine-color-yellow-7))"
|
|
2491
|
+
},
|
|
2492
|
+
children: warningCount
|
|
2493
|
+
}
|
|
2494
|
+
),
|
|
2495
|
+
failureCount > 0 && /* @__PURE__ */ jsx(
|
|
2496
|
+
"div",
|
|
2497
|
+
{
|
|
2498
|
+
style: {
|
|
2499
|
+
...statBubbleBase,
|
|
2500
|
+
background: "linear-gradient(135deg, var(--mantine-color-red-5), var(--mantine-color-red-7))"
|
|
2501
|
+
},
|
|
2502
|
+
children: failureCount
|
|
2503
|
+
}
|
|
2504
|
+
),
|
|
2505
|
+
pendingCount > 0 && /* @__PURE__ */ jsx(
|
|
2506
|
+
"div",
|
|
2507
|
+
{
|
|
2508
|
+
style: {
|
|
2509
|
+
...statBubbleBase,
|
|
2510
|
+
background: "linear-gradient(135deg, var(--mantine-color-orange-5), var(--mantine-color-orange-7))"
|
|
2511
|
+
},
|
|
2512
|
+
children: pendingCount
|
|
2513
|
+
}
|
|
2514
|
+
)
|
|
2515
|
+
]
|
|
2516
|
+
}
|
|
2517
|
+
),
|
|
2518
|
+
/* @__PURE__ */ jsx(BaseNode, { color, selected, highlighted: Boolean(data.highlighted), children: /* @__PURE__ */ jsxs(Stack, { gap: 8, children: [
|
|
2519
|
+
/* @__PURE__ */ jsxs(Group, { gap: "xs", wrap: "nowrap", children: [
|
|
2520
|
+
/* @__PURE__ */ jsx(
|
|
2521
|
+
ThemeIcon,
|
|
2522
|
+
{
|
|
2523
|
+
size: "md",
|
|
2524
|
+
variant: "gradient",
|
|
2525
|
+
gradient: { from: `${color}.4`, to: `${color}.6`, deg: 135 },
|
|
2526
|
+
className: Graph_module_css_default.nodeIcon,
|
|
2527
|
+
children: /* @__PURE__ */ jsx(Icon, { size: 16 })
|
|
2528
|
+
}
|
|
2529
|
+
),
|
|
2530
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", fw: 600, truncate: true, style: { flex: 1, fontFamily: "var(--elevasis-font-family-subtitle)" }, children: data.name })
|
|
2531
|
+
] }),
|
|
2532
|
+
/* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", lineClamp: 2, children: data.description }),
|
|
2533
|
+
/* @__PURE__ */ jsxs(Group, { gap: 4, wrap: "wrap", children: [
|
|
2534
|
+
/* @__PURE__ */ jsx(
|
|
2535
|
+
Badge,
|
|
2536
|
+
{
|
|
2537
|
+
size: "xs",
|
|
2538
|
+
variant: "gradient",
|
|
2539
|
+
gradient: data.status === "prod" ? { from: "green.5", to: "green.7", deg: 135 } : { from: "blue.5", to: "blue.7", deg: 135 },
|
|
2540
|
+
className: data.status === "prod" ? Graph_module_css_default.badgeProd : "",
|
|
2541
|
+
style: {
|
|
2542
|
+
textTransform: "uppercase",
|
|
2543
|
+
letterSpacing: "0.5px"
|
|
2544
|
+
},
|
|
2545
|
+
children: data.status
|
|
2546
|
+
}
|
|
2547
|
+
),
|
|
2548
|
+
data.type === "agent" && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2549
|
+
/* @__PURE__ */ jsxs(
|
|
2550
|
+
Badge,
|
|
2551
|
+
{
|
|
2552
|
+
size: "xs",
|
|
2553
|
+
variant: "outline",
|
|
2554
|
+
color: "gray",
|
|
2555
|
+
className: Graph_module_css_default.badge,
|
|
2556
|
+
style: { backdropFilter: "blur(4px)" },
|
|
2557
|
+
children: [
|
|
2558
|
+
data.toolCount,
|
|
2559
|
+
" tools"
|
|
2560
|
+
]
|
|
2561
|
+
}
|
|
2562
|
+
),
|
|
2563
|
+
data.hasKnowledgeMap && /* @__PURE__ */ jsx(
|
|
2564
|
+
Badge,
|
|
2565
|
+
{
|
|
2566
|
+
size: "xs",
|
|
2567
|
+
variant: "gradient",
|
|
2568
|
+
gradient: { from: "grape.5", to: "grape.7", deg: 135 },
|
|
2569
|
+
leftSection: /* @__PURE__ */ jsx(IconBrain, { size: 10 }),
|
|
2570
|
+
className: Graph_module_css_default.badge,
|
|
2571
|
+
children: "KM"
|
|
2572
|
+
}
|
|
2573
|
+
),
|
|
2574
|
+
data.hasMemory && /* @__PURE__ */ jsx(
|
|
2575
|
+
Badge,
|
|
2576
|
+
{
|
|
2577
|
+
size: "xs",
|
|
2578
|
+
variant: "gradient",
|
|
2579
|
+
gradient: { from: "cyan.5", to: "cyan.7", deg: 135 },
|
|
2580
|
+
leftSection: /* @__PURE__ */ jsx(IconDatabase, { size: 10 }),
|
|
2581
|
+
className: Graph_module_css_default.badge,
|
|
2582
|
+
children: "Mem"
|
|
2583
|
+
}
|
|
2584
|
+
),
|
|
2585
|
+
data.sessionCapable && /* @__PURE__ */ jsx(
|
|
2586
|
+
Badge,
|
|
2587
|
+
{
|
|
2588
|
+
size: "xs",
|
|
2589
|
+
variant: "gradient",
|
|
2590
|
+
gradient: { from: "blue.5", to: "blue.7", deg: 135 },
|
|
2591
|
+
leftSection: /* @__PURE__ */ jsx(IconMessage, { size: 10 }),
|
|
2592
|
+
className: Graph_module_css_default.badge,
|
|
2593
|
+
children: "Session"
|
|
2594
|
+
}
|
|
2595
|
+
)
|
|
2596
|
+
] }),
|
|
2597
|
+
data.type === "workflow" && /* @__PURE__ */ jsxs(
|
|
2598
|
+
Badge,
|
|
2599
|
+
{
|
|
2600
|
+
size: "xs",
|
|
2601
|
+
variant: "outline",
|
|
2602
|
+
color: "gray",
|
|
2603
|
+
className: Graph_module_css_default.badge,
|
|
2604
|
+
style: { backdropFilter: "blur(4px)" },
|
|
2605
|
+
children: [
|
|
2606
|
+
data.stepCount,
|
|
2607
|
+
" steps"
|
|
2608
|
+
]
|
|
2609
|
+
}
|
|
2610
|
+
),
|
|
2611
|
+
data.type === "integration" && /* @__PURE__ */ jsx(
|
|
2612
|
+
Badge,
|
|
2613
|
+
{
|
|
2614
|
+
size: "xs",
|
|
2615
|
+
variant: "gradient",
|
|
2616
|
+
gradient: data.connectionStatus === "connected" ? { from: "green.5", to: "green.7", deg: 135 } : data.connectionStatus === "error" ? { from: "red.5", to: "red.7", deg: 135 } : { from: "gray.5", to: "gray.7", deg: 135 },
|
|
2617
|
+
leftSection: (() => {
|
|
2618
|
+
const StatusIcon = connectionStatusIcons[data.connectionStatus];
|
|
2619
|
+
return /* @__PURE__ */ jsx(StatusIcon, { size: 10 });
|
|
2620
|
+
})(),
|
|
2621
|
+
className: Graph_module_css_default.badge,
|
|
2622
|
+
children: data.connectionStatus
|
|
2623
|
+
}
|
|
2624
|
+
),
|
|
2625
|
+
data.type === "trigger" && /* @__PURE__ */ jsx(
|
|
2626
|
+
Badge,
|
|
2627
|
+
{
|
|
2628
|
+
size: "xs",
|
|
2629
|
+
variant: "outline",
|
|
2630
|
+
color: "gray",
|
|
2631
|
+
leftSection: (() => {
|
|
2632
|
+
const TriggerIcon = triggerTypeIcons[data.triggerType] || IconBolt;
|
|
2633
|
+
return /* @__PURE__ */ jsx(TriggerIcon, { size: 10 });
|
|
2634
|
+
})(),
|
|
2635
|
+
className: Graph_module_css_default.badge,
|
|
2636
|
+
style: { backdropFilter: "blur(4px)" },
|
|
2637
|
+
children: data.triggerType
|
|
2638
|
+
}
|
|
2639
|
+
),
|
|
2640
|
+
data.type === "external" && /* @__PURE__ */ jsx(
|
|
2641
|
+
Badge,
|
|
2642
|
+
{
|
|
2643
|
+
size: "xs",
|
|
2644
|
+
variant: "outline",
|
|
2645
|
+
color: "gray",
|
|
2646
|
+
leftSection: /* @__PURE__ */ jsx(IconExternalLink, { size: 10 }),
|
|
2647
|
+
className: Graph_module_css_default.badge,
|
|
2648
|
+
style: { backdropFilter: "blur(4px)" },
|
|
2649
|
+
children: data.platform
|
|
2650
|
+
}
|
|
2651
|
+
),
|
|
2652
|
+
data.type === "human" && /* @__PURE__ */ jsx(
|
|
2653
|
+
Badge,
|
|
2654
|
+
{
|
|
2655
|
+
size: "xs",
|
|
2656
|
+
variant: "gradient",
|
|
2657
|
+
gradient: { from: "yellow.5", to: "orange.6", deg: 135 },
|
|
2658
|
+
leftSection: /* @__PURE__ */ jsx(IconHandClick, { size: 10 }),
|
|
2659
|
+
className: Graph_module_css_default.badge,
|
|
2660
|
+
children: "Approval Required"
|
|
2661
|
+
}
|
|
2662
|
+
)
|
|
2663
|
+
] })
|
|
2664
|
+
] }) })
|
|
2665
|
+
] });
|
|
2666
|
+
});
|
|
2667
|
+
var relationshipColorMap = {
|
|
2668
|
+
triggers: { color: "edgeTriggers", glow: "edgeTriggersGlow" },
|
|
2669
|
+
uses: { color: "edgeUses", glow: "edgeUsesGlow" },
|
|
2670
|
+
approval: { color: "edgeApproval", glow: "edgeApprovalGlow" }
|
|
2671
|
+
};
|
|
2672
|
+
function getEdgeColors(relationship, colors) {
|
|
2673
|
+
const mapping = relationshipColorMap[relationship] ?? relationshipColorMap.approval;
|
|
2674
|
+
return {
|
|
2675
|
+
edgeColor: colors[mapping.color],
|
|
2676
|
+
glowColor: colors[mapping.glow]
|
|
2677
|
+
};
|
|
2678
|
+
}
|
|
2679
|
+
var CommandViewEdge = memo(function CommandViewEdge2({
|
|
2680
|
+
id,
|
|
2681
|
+
sourceX,
|
|
2682
|
+
sourceY,
|
|
2683
|
+
targetX,
|
|
2684
|
+
targetY,
|
|
2685
|
+
sourcePosition,
|
|
2686
|
+
targetPosition,
|
|
2687
|
+
data,
|
|
2688
|
+
selected
|
|
2689
|
+
}) {
|
|
2690
|
+
const colors = useGraphTheme();
|
|
2691
|
+
const relationship = data?.relationship || "uses";
|
|
2692
|
+
const { edgeColor, glowColor } = getEdgeColors(relationship, colors);
|
|
2693
|
+
return /* @__PURE__ */ jsx(
|
|
2694
|
+
BaseEdge,
|
|
2695
|
+
{
|
|
2696
|
+
id,
|
|
2697
|
+
sourceX,
|
|
2698
|
+
sourceY,
|
|
2699
|
+
targetX,
|
|
2700
|
+
targetY,
|
|
2701
|
+
sourcePosition,
|
|
2702
|
+
targetPosition,
|
|
2703
|
+
color: edgeColor,
|
|
2704
|
+
glowColor,
|
|
2705
|
+
label: data?.label || relationship,
|
|
2706
|
+
animated: data?.animated ?? false,
|
|
2707
|
+
selected,
|
|
2708
|
+
dimmed: data?.dimmed,
|
|
2709
|
+
edgeIndex: data?.edgeIndex,
|
|
2710
|
+
totalEdges: data?.totalEdges
|
|
2711
|
+
}
|
|
2712
|
+
);
|
|
2713
|
+
});
|
|
2714
|
+
var nodeTypes = {
|
|
2715
|
+
commandView: CommandViewNode
|
|
2716
|
+
};
|
|
2717
|
+
var edgeTypes = {
|
|
2718
|
+
commandView: CommandViewEdge
|
|
2719
|
+
};
|
|
2720
|
+
var CommandViewGraphInner = forwardRef(function CommandViewGraphInner2({ graph, height, selectedNodeId, onNodeSelect }, ref) {
|
|
2721
|
+
const { fitView } = useReactFlow();
|
|
2722
|
+
const { nodes: layoutNodes, edges: layoutEdges } = useCommandViewLayout(graph);
|
|
2723
|
+
const { nodes, edges, handleNodeMouseEnter, handleNodeMouseLeave } = useDirectedChainHighlighting(
|
|
2724
|
+
layoutNodes,
|
|
2725
|
+
layoutEdges,
|
|
2726
|
+
{ selectedNodeId }
|
|
2727
|
+
);
|
|
2728
|
+
const { handleNodeClick, handlePaneClick } = useNodeSelection(selectedNodeId ?? null, onNodeSelect ?? (() => {
|
|
2729
|
+
}));
|
|
2730
|
+
const nodesWithSelection = useMemo(
|
|
2731
|
+
() => nodes.map((node) => ({
|
|
2732
|
+
...node,
|
|
2733
|
+
selected: node.id === selectedNodeId
|
|
2734
|
+
})),
|
|
2735
|
+
[nodes, selectedNodeId]
|
|
2736
|
+
);
|
|
2737
|
+
useImperativeHandle(ref, () => ({
|
|
2738
|
+
fitView: () => fitView({ padding: 0.15, duration: 300 })
|
|
2739
|
+
}));
|
|
2740
|
+
return /* @__PURE__ */ jsx(
|
|
2741
|
+
Box,
|
|
2742
|
+
{
|
|
2743
|
+
className: Graph_module_css_default.graphContainer,
|
|
2744
|
+
style: {
|
|
2745
|
+
width: "100%",
|
|
2746
|
+
height,
|
|
2747
|
+
border: "1px solid var(--color-border)",
|
|
2748
|
+
borderRadius: "var(--mantine-radius-default)",
|
|
2749
|
+
overflow: "hidden"
|
|
2750
|
+
},
|
|
2751
|
+
children: /* @__PURE__ */ jsxs(
|
|
2752
|
+
ReactFlow,
|
|
2753
|
+
{
|
|
2754
|
+
nodes: nodesWithSelection,
|
|
2755
|
+
edges,
|
|
2756
|
+
nodeTypes,
|
|
2757
|
+
edgeTypes,
|
|
2758
|
+
onNodeMouseEnter: handleNodeMouseEnter,
|
|
2759
|
+
onNodeMouseLeave: handleNodeMouseLeave,
|
|
2760
|
+
onNodeClick: onNodeSelect ? handleNodeClick : void 0,
|
|
2761
|
+
onPaneClick: onNodeSelect ? handlePaneClick : void 0,
|
|
2762
|
+
fitView: true,
|
|
2763
|
+
fitViewOptions: { padding: 0.15 },
|
|
2764
|
+
proOptions: { hideAttribution: true },
|
|
2765
|
+
minZoom: GRAPH_CONSTANTS.MIN_ZOOM,
|
|
2766
|
+
maxZoom: GRAPH_CONSTANTS.MAX_ZOOM,
|
|
2767
|
+
nodesDraggable: false,
|
|
2768
|
+
nodesConnectable: false,
|
|
2769
|
+
elementsSelectable: !!onNodeSelect,
|
|
2770
|
+
selectNodesOnDrag: false,
|
|
2771
|
+
panOnDrag: true,
|
|
2772
|
+
zoomOnScroll: true,
|
|
2773
|
+
zoomOnPinch: true,
|
|
2774
|
+
panOnScroll: false,
|
|
2775
|
+
children: [
|
|
2776
|
+
/* @__PURE__ */ jsx(GraphBackground, {}),
|
|
2777
|
+
/* @__PURE__ */ jsx(
|
|
2778
|
+
GraphLegend,
|
|
2779
|
+
{
|
|
2780
|
+
title: "",
|
|
2781
|
+
position: "bottom-right",
|
|
2782
|
+
items: [
|
|
2783
|
+
{ color: "orange", label: "Triggers" },
|
|
2784
|
+
{ color: "violet", label: "Agents" },
|
|
2785
|
+
{ color: "blue", label: "Workflows" },
|
|
2786
|
+
{ color: "teal", label: "Integrations" },
|
|
2787
|
+
{ color: "yellow", label: "Human" },
|
|
2788
|
+
{ color: "gray", label: "External" }
|
|
2789
|
+
]
|
|
2790
|
+
}
|
|
2791
|
+
),
|
|
2792
|
+
/* @__PURE__ */ jsx(
|
|
2793
|
+
GraphLegend,
|
|
2794
|
+
{
|
|
2795
|
+
title: "",
|
|
2796
|
+
position: "bottom-left",
|
|
2797
|
+
items: [
|
|
2798
|
+
{ color: "blue", label: "Triggers", type: "line" },
|
|
2799
|
+
{ color: "teal", label: "Uses", type: "line" },
|
|
2800
|
+
{ color: "yellow", label: "Requires Approval", type: "line" }
|
|
2801
|
+
]
|
|
2802
|
+
}
|
|
2803
|
+
),
|
|
2804
|
+
/* @__PURE__ */ jsx(GraphFitViewButton, { padding: 0.15, variant: "mantine", duration: 300 })
|
|
2805
|
+
]
|
|
2806
|
+
}
|
|
2807
|
+
)
|
|
2808
|
+
}
|
|
2809
|
+
);
|
|
2810
|
+
});
|
|
2811
|
+
var CommandViewGraph = forwardRef(
|
|
2812
|
+
function CommandViewGraph2(props, ref) {
|
|
2813
|
+
return /* @__PURE__ */ jsx(ReactFlowProvider, { children: /* @__PURE__ */ jsx(CommandViewGraphInner, { ref, ...props }) });
|
|
2814
|
+
}
|
|
2815
|
+
);
|
|
2426
2816
|
var tokens = {
|
|
2427
2817
|
cardBg: "rgba(16, 16, 20, 0.92)",
|
|
2428
2818
|
cardBorder: "rgba(255, 255, 255, 0.06)",
|
|
@@ -3029,13 +3419,13 @@ var Breadcrumbs = ({ navItems }) => {
|
|
|
3029
3419
|
return /* @__PURE__ */ jsx(Text, { size: "sm", fw: 500, c: "var(--color-text)", children: item.label }, item.path || item.label);
|
|
3030
3420
|
}
|
|
3031
3421
|
return item.path ? /* @__PURE__ */ jsx(
|
|
3032
|
-
|
|
3422
|
+
Text,
|
|
3033
3423
|
{
|
|
3034
3424
|
component: Link4,
|
|
3035
3425
|
to: item.path,
|
|
3036
3426
|
size: "sm",
|
|
3427
|
+
c: "var(--color-text-subtle)",
|
|
3037
3428
|
style: {
|
|
3038
|
-
color: "var(--color-text-subtle)",
|
|
3039
3429
|
textDecoration: "none",
|
|
3040
3430
|
fontWeight: 500
|
|
3041
3431
|
},
|
|
@@ -3044,7 +3434,18 @@ var Breadcrumbs = ({ navItems }) => {
|
|
|
3044
3434
|
item.path
|
|
3045
3435
|
) : /* @__PURE__ */ jsx(Text, { size: "sm", c: "var(--color-text-subtle)", children: item.label }, item.label);
|
|
3046
3436
|
});
|
|
3047
|
-
return /* @__PURE__ */ jsx(
|
|
3437
|
+
return /* @__PURE__ */ jsx(
|
|
3438
|
+
Breadcrumbs$1,
|
|
3439
|
+
{
|
|
3440
|
+
separator: "/",
|
|
3441
|
+
styles: {
|
|
3442
|
+
separator: {
|
|
3443
|
+
color: "var(--color-text-subtle)"
|
|
3444
|
+
}
|
|
3445
|
+
},
|
|
3446
|
+
children: items
|
|
3447
|
+
}
|
|
3448
|
+
);
|
|
3048
3449
|
};
|
|
3049
3450
|
function DealKanbanCard({ deal, config, onClick, onDragStart, onDragEnd }) {
|
|
3050
3451
|
const contactFirstName = deal.contact?.first_name || "";
|
|
@@ -3089,22 +3490,11 @@ function DealKanbanCard({ deal, config, onClick, onDragStart, onDragEnd }) {
|
|
|
3089
3490
|
}
|
|
3090
3491
|
);
|
|
3091
3492
|
}
|
|
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
3493
|
function formatStageLabel(stage) {
|
|
3104
3494
|
if (!stage) return "Unstaged";
|
|
3105
3495
|
return stage.split("_").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
3106
3496
|
}
|
|
3107
|
-
function DealDrawer({ deal, opened, onClose, renderActions }) {
|
|
3497
|
+
function DealDrawer({ deal, config, opened, onClose, renderActions }) {
|
|
3108
3498
|
const [noteBody, setNoteBody] = useState("");
|
|
3109
3499
|
const { data: notes, isLoading: notesLoading } = useDealNotes(deal?.id ?? "");
|
|
3110
3500
|
const createNote = useCreateDealNote();
|
|
@@ -3116,7 +3506,7 @@ function DealDrawer({ deal, opened, onClose, renderActions }) {
|
|
|
3116
3506
|
const email = deal.contact?.email || deal.contact_email || null;
|
|
3117
3507
|
const companyName = deal.contact?.company?.name || deal.discovery_data?.company || null;
|
|
3118
3508
|
const stage = deal.cached_stage;
|
|
3119
|
-
const badgeColor =
|
|
3509
|
+
const badgeColor = (stage ? config?.[stage]?.color : null) ?? "gray";
|
|
3120
3510
|
const activityLog = deal.activity_log || [];
|
|
3121
3511
|
const hasInitialFee = typeof deal.initial_fee === "number" && deal.initial_fee > 0;
|
|
3122
3512
|
const hasMonthlyFee = typeof deal.monthly_fee === "number" && deal.monthly_fee > 0;
|
|
@@ -3232,12 +3622,9 @@ function formatStageLabel2(stage) {
|
|
|
3232
3622
|
if (stage === UNSTAGED_KEY) return "Unstaged";
|
|
3233
3623
|
return stage.split("_").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
3234
3624
|
}
|
|
3235
|
-
function groupDealsByStage(deals) {
|
|
3625
|
+
function groupDealsByStage(deals, columns) {
|
|
3236
3626
|
const map = /* @__PURE__ */ new Map();
|
|
3237
|
-
for (const stage of
|
|
3238
|
-
map.set(stage, []);
|
|
3239
|
-
}
|
|
3240
|
-
map.set(UNSTAGED_KEY, []);
|
|
3627
|
+
for (const stage of columns) map.set(stage, []);
|
|
3241
3628
|
for (const deal of deals) {
|
|
3242
3629
|
const key = deal.cached_stage ?? UNSTAGED_KEY;
|
|
3243
3630
|
const bucket = map.get(key);
|
|
@@ -3249,7 +3636,7 @@ function groupDealsByStage(deals) {
|
|
|
3249
3636
|
}
|
|
3250
3637
|
return map;
|
|
3251
3638
|
}
|
|
3252
|
-
function KanbanBoard({ config
|
|
3639
|
+
function KanbanBoard({ config, renderDrawerActions }) {
|
|
3253
3640
|
const { data: deals, isLoading, error } = useDeals();
|
|
3254
3641
|
const syncStage = useSyncDealStage();
|
|
3255
3642
|
const queryClient = useQueryClient();
|
|
@@ -3257,8 +3644,11 @@ function KanbanBoard({ config = DEFAULT_KANBAN_CONFIG, renderDrawerActions }) {
|
|
|
3257
3644
|
const [drawerOpen, setDrawerOpen] = useState(false);
|
|
3258
3645
|
const [dragOverColumn, setDragOverColumn] = useState(null);
|
|
3259
3646
|
const draggingDealRef = useRef(null);
|
|
3260
|
-
const
|
|
3261
|
-
const
|
|
3647
|
+
const resolvedConfig = config ?? {};
|
|
3648
|
+
const configuredStages = Object.keys(resolvedConfig).filter((stage) => Boolean(resolvedConfig[stage]));
|
|
3649
|
+
const columns = [...configuredStages, UNSTAGED_KEY];
|
|
3650
|
+
const hasConfig = configuredStages.length > 0;
|
|
3651
|
+
const groupedDeals = groupDealsByStage(deals ?? [], columns);
|
|
3262
3652
|
const handleDragStart = useCallback((e, deal) => {
|
|
3263
3653
|
draggingDealRef.current = deal;
|
|
3264
3654
|
e.dataTransfer.effectAllowed = "move";
|
|
@@ -3307,6 +3697,15 @@ function KanbanBoard({ config = DEFAULT_KANBAN_CONFIG, renderDrawerActions }) {
|
|
|
3307
3697
|
if (error) {
|
|
3308
3698
|
return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsx(Alert, { color: "red", children: "Failed to load deals" }) });
|
|
3309
3699
|
}
|
|
3700
|
+
if (!hasConfig) {
|
|
3701
|
+
return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsxs(Stack, { children: [
|
|
3702
|
+
/* @__PURE__ */ jsx(PageTitleCaption, { title: "Pipeline", caption: "Kanban view of your deal pipeline" }),
|
|
3703
|
+
/* @__PURE__ */ jsx(Paper, { withBorder: true, p: "xl", children: /* @__PURE__ */ jsx(Center, { py: "xl", children: /* @__PURE__ */ jsxs(Stack, { gap: "xs", align: "center", children: [
|
|
3704
|
+
/* @__PURE__ */ jsx(Text, { fw: 600, children: "Configuration Required" }),
|
|
3705
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", ta: "center", children: "Provide a kanban stage config before rendering the pipeline board." })
|
|
3706
|
+
] }) }) })
|
|
3707
|
+
] }) });
|
|
3708
|
+
}
|
|
3310
3709
|
return /* @__PURE__ */ jsxs(SubshellContentContainer, { children: [
|
|
3311
3710
|
/* @__PURE__ */ jsxs(Stack, { children: [
|
|
3312
3711
|
/* @__PURE__ */ jsx(PageTitleCaption, { title: "Pipeline", caption: "Kanban view of your deal pipeline" }),
|
|
@@ -3322,7 +3721,7 @@ function KanbanBoard({ config = DEFAULT_KANBAN_CONFIG, renderDrawerActions }) {
|
|
|
3322
3721
|
const columnDeals = groupedDeals.get(columnKey) ?? [];
|
|
3323
3722
|
const isUnstaged = columnKey === UNSTAGED_KEY;
|
|
3324
3723
|
const isDragTarget = dragOverColumn === columnKey;
|
|
3325
|
-
const badgeColor = isUnstaged ? "gray" :
|
|
3724
|
+
const badgeColor = isUnstaged ? "gray" : resolvedConfig[columnKey]?.color ?? "gray";
|
|
3326
3725
|
return /* @__PURE__ */ jsx(
|
|
3327
3726
|
Box,
|
|
3328
3727
|
{
|
|
@@ -3361,7 +3760,7 @@ function KanbanBoard({ config = DEFAULT_KANBAN_CONFIG, renderDrawerActions }) {
|
|
|
3361
3760
|
DealKanbanCard,
|
|
3362
3761
|
{
|
|
3363
3762
|
deal,
|
|
3364
|
-
config,
|
|
3763
|
+
config: resolvedConfig,
|
|
3365
3764
|
onClick: handleCardClick,
|
|
3366
3765
|
onDragStart: handleDragStart,
|
|
3367
3766
|
onDragEnd: handleDragEnd
|
|
@@ -3380,6 +3779,7 @@ function KanbanBoard({ config = DEFAULT_KANBAN_CONFIG, renderDrawerActions }) {
|
|
|
3380
3779
|
DealDrawer,
|
|
3381
3780
|
{
|
|
3382
3781
|
deal: selectedDeal,
|
|
3782
|
+
config: resolvedConfig,
|
|
3383
3783
|
opened: drawerOpen,
|
|
3384
3784
|
onClose: () => setDrawerOpen(false),
|
|
3385
3785
|
renderActions: renderDrawerActions
|
|
@@ -3387,6 +3787,17 @@ function KanbanBoard({ config = DEFAULT_KANBAN_CONFIG, renderDrawerActions }) {
|
|
|
3387
3787
|
)
|
|
3388
3788
|
] });
|
|
3389
3789
|
}
|
|
3790
|
+
|
|
3791
|
+
// src/components/acquisition/kanban/constants.ts
|
|
3792
|
+
var DEAL_STAGES = ["interested", "proposal", "closing", "closed_won", "closed_lost", "nurturing"];
|
|
3793
|
+
var DEFAULT_KANBAN_CONFIG = {
|
|
3794
|
+
interested: { color: "blue" },
|
|
3795
|
+
proposal: { color: "yellow" },
|
|
3796
|
+
closing: { color: "lime" },
|
|
3797
|
+
closed_won: { color: "green" },
|
|
3798
|
+
closed_lost: { color: "red" },
|
|
3799
|
+
nurturing: { color: "grape" }
|
|
3800
|
+
};
|
|
3390
3801
|
var CrmSidebarTop = () => {
|
|
3391
3802
|
return /* @__PURE__ */ jsx(SubshellSidebarSection, { icon: IconAddressBook, label: "CRM" });
|
|
3392
3803
|
};
|
|
@@ -4546,7 +4957,7 @@ function LeadGenRouteShell({
|
|
|
4546
4957
|
] }) })
|
|
4547
4958
|
] }) }) });
|
|
4548
4959
|
}
|
|
4549
|
-
function
|
|
4960
|
+
function formatDate3(dateValue) {
|
|
4550
4961
|
const date = typeof dateValue === "string" ? new Date(dateValue) : dateValue;
|
|
4551
4962
|
return date.toLocaleDateString("en-US", {
|
|
4552
4963
|
month: "short",
|
|
@@ -4640,8 +5051,8 @@ function CompanyDetailModal({ company, onClose }) {
|
|
|
4640
5051
|
] }),
|
|
4641
5052
|
/* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
|
|
4642
5053
|
"Created: ",
|
|
4643
|
-
|
|
4644
|
-
company.updatedAt && ` | Updated: ${
|
|
5054
|
+
formatDate3(company.createdAt),
|
|
5055
|
+
company.updatedAt && ` | Updated: ${formatDate3(company.updatedAt)}`
|
|
4645
5056
|
] })
|
|
4646
5057
|
] }) : null });
|
|
4647
5058
|
}
|
|
@@ -4693,8 +5104,8 @@ function ContactDetailModal({ contact, onClose }) {
|
|
|
4693
5104
|
] }) : null,
|
|
4694
5105
|
/* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
|
|
4695
5106
|
"Created: ",
|
|
4696
|
-
|
|
4697
|
-
contact.updatedAt && ` | Updated: ${
|
|
5107
|
+
formatDate3(contact.createdAt),
|
|
5108
|
+
contact.updatedAt && ` | Updated: ${formatDate3(contact.updatedAt)}`
|
|
4698
5109
|
] })
|
|
4699
5110
|
] }) : null });
|
|
4700
5111
|
}
|
|
@@ -4902,12 +5313,159 @@ function useDeleteLists() {
|
|
|
4902
5313
|
});
|
|
4903
5314
|
}
|
|
4904
5315
|
var EM_DASH = "\u2014";
|
|
5316
|
+
function computeCompletionRatio(populated, personalized) {
|
|
5317
|
+
if (populated === 0) return null;
|
|
5318
|
+
return personalized / populated;
|
|
5319
|
+
}
|
|
5320
|
+
function formatPercentage(ratio, fractionDigits = 0) {
|
|
5321
|
+
if (ratio == null) return EM_DASH;
|
|
5322
|
+
return `${(ratio * 100).toFixed(fractionDigits)}%`;
|
|
5323
|
+
}
|
|
4905
5324
|
function computeCompletionPercentage(populated, personalized) {
|
|
4906
|
-
|
|
4907
|
-
|
|
5325
|
+
return formatPercentage(computeCompletionRatio(populated, personalized));
|
|
5326
|
+
}
|
|
5327
|
+
function computeBounceRate(d) {
|
|
5328
|
+
const denominator = d.valid + d.risky + d.invalid + d.bounced;
|
|
5329
|
+
if (denominator === 0) return EM_DASH;
|
|
5330
|
+
return `${(d.bounced / denominator * 100).toFixed(1)}%`;
|
|
5331
|
+
}
|
|
5332
|
+
function computeBacklog(current, completed) {
|
|
5333
|
+
return Math.max(current - completed, 0);
|
|
5334
|
+
}
|
|
5335
|
+
function getOverviewStatus(list) {
|
|
5336
|
+
if ((list.activeWorkflows?.length ?? 0) > 0) {
|
|
5337
|
+
return { label: "Active Work", color: "green" };
|
|
5338
|
+
}
|
|
5339
|
+
if (list.stageCounts.personalized > 0 && list.stageCounts.personalized === list.stageCounts.uploaded) {
|
|
5340
|
+
return { label: "Complete", color: "blue" };
|
|
5341
|
+
}
|
|
5342
|
+
return { label: "Idle", color: "gray" };
|
|
5343
|
+
}
|
|
5344
|
+
function getNextFocus(list) {
|
|
5345
|
+
if ((list.activeWorkflows?.length ?? 0) > 0) {
|
|
5346
|
+
return { label: "Workflow running", count: list.activeWorkflows?.length ?? 0 };
|
|
5347
|
+
}
|
|
5348
|
+
if (list.totalCompanies === 0 && list.totalContacts === 0) {
|
|
5349
|
+
return { label: "Populate list", count: null };
|
|
5350
|
+
}
|
|
5351
|
+
const extractGap = computeBacklog(list.stageCounts.populated, list.stageCounts.extracted);
|
|
5352
|
+
if (extractGap > 0) {
|
|
5353
|
+
return { label: "Extract website data", count: extractGap };
|
|
5354
|
+
}
|
|
5355
|
+
const qualifyGap = computeBacklog(list.stageCounts.extracted, list.stageCounts.qualified);
|
|
5356
|
+
if (qualifyGap > 0) {
|
|
5357
|
+
return { label: "Qualify companies", count: qualifyGap };
|
|
5358
|
+
}
|
|
5359
|
+
const discoverGap = computeBacklog(list.stageCounts.qualified, list.stageCounts.discovered);
|
|
5360
|
+
if (discoverGap > 0) {
|
|
5361
|
+
return { label: "Discover contacts", count: discoverGap };
|
|
5362
|
+
}
|
|
5363
|
+
const verifyGap = computeBacklog(list.stageCounts.discovered, list.stageCounts.verified);
|
|
5364
|
+
if (verifyGap > 0) {
|
|
5365
|
+
return { label: "Verify emails", count: verifyGap };
|
|
5366
|
+
}
|
|
5367
|
+
const personalizeGap = computeBacklog(list.stageCounts.verified, list.stageCounts.personalized);
|
|
5368
|
+
if (personalizeGap > 0) {
|
|
5369
|
+
return { label: "Personalize outreach", count: personalizeGap };
|
|
5370
|
+
}
|
|
5371
|
+
const uploadGap = computeBacklog(list.stageCounts.personalized, list.stageCounts.uploaded);
|
|
5372
|
+
if (uploadGap > 0) {
|
|
5373
|
+
return { label: "Upload contacts", count: uploadGap };
|
|
5374
|
+
}
|
|
5375
|
+
return { label: "Complete", count: null };
|
|
5376
|
+
}
|
|
5377
|
+
function formatCountLabel(value, noun) {
|
|
5378
|
+
return `${value} ${noun}${value === 1 ? "" : "s"}`;
|
|
5379
|
+
}
|
|
5380
|
+
function getPrimaryAction({
|
|
5381
|
+
uploadBacklog,
|
|
5382
|
+
personalizationBacklog,
|
|
5383
|
+
verificationBacklog,
|
|
5384
|
+
deliverabilityRiskCount,
|
|
5385
|
+
activeListCount,
|
|
5386
|
+
totalLists
|
|
5387
|
+
}) {
|
|
5388
|
+
if (uploadBacklog > 0) {
|
|
5389
|
+
return {
|
|
5390
|
+
title: `Upload ${formatCountLabel(uploadBacklog, "personalized contact")}`,
|
|
5391
|
+
detail: "No verification blockers are left on the contacts already ready for outreach.",
|
|
5392
|
+
buttonLabel: "Review lists",
|
|
5393
|
+
buttonTo: "/lead-gen/lists",
|
|
5394
|
+
tone: "blue"
|
|
5395
|
+
};
|
|
5396
|
+
}
|
|
5397
|
+
if (personalizationBacklog > 0) {
|
|
5398
|
+
return {
|
|
5399
|
+
title: `Personalize ${formatCountLabel(personalizationBacklog, "verified contact")}`,
|
|
5400
|
+
detail: "Verification is done. The next bottleneck is drafting outreach copy.",
|
|
5401
|
+
buttonLabel: "Open lists",
|
|
5402
|
+
buttonTo: "/lead-gen/lists",
|
|
5403
|
+
tone: "blue"
|
|
5404
|
+
};
|
|
5405
|
+
}
|
|
5406
|
+
if (verificationBacklog > 0) {
|
|
5407
|
+
return {
|
|
5408
|
+
title: `Verify ${formatCountLabel(verificationBacklog, "discovered contact")}`,
|
|
5409
|
+
detail: "Email validation is the main blocker before personalization can continue.",
|
|
5410
|
+
buttonLabel: "Open lists",
|
|
5411
|
+
buttonTo: "/lead-gen/lists",
|
|
5412
|
+
tone: "orange"
|
|
5413
|
+
};
|
|
5414
|
+
}
|
|
5415
|
+
if (deliverabilityRiskCount > 0) {
|
|
5416
|
+
return {
|
|
5417
|
+
title: `Resolve ${formatCountLabel(deliverabilityRiskCount, "deliverability risk")}`,
|
|
5418
|
+
detail: "Risky, invalid, or bounced records are the main campaign health issue right now.",
|
|
5419
|
+
buttonLabel: "View deliverability",
|
|
5420
|
+
buttonTo: "/lead-gen/deliverability",
|
|
5421
|
+
tone: "orange"
|
|
5422
|
+
};
|
|
5423
|
+
}
|
|
5424
|
+
if (activeListCount > 0) {
|
|
5425
|
+
return {
|
|
5426
|
+
title: `Monitor ${formatCountLabel(activeListCount, "active list")}`,
|
|
5427
|
+
detail: "Workflows are running and there are no immediate contact-stage backlogs to clear.",
|
|
5428
|
+
buttonLabel: "Open lists",
|
|
5429
|
+
buttonTo: "/lead-gen/lists",
|
|
5430
|
+
tone: "green"
|
|
5431
|
+
};
|
|
5432
|
+
}
|
|
5433
|
+
if (totalLists > 0) {
|
|
5434
|
+
return {
|
|
5435
|
+
title: "No blockers right now",
|
|
5436
|
+
detail: "Lists are idle and the current pipeline does not show an urgent follow-up step.",
|
|
5437
|
+
buttonLabel: "Review lists",
|
|
5438
|
+
buttonTo: "/lead-gen/lists",
|
|
5439
|
+
tone: "gray"
|
|
5440
|
+
};
|
|
5441
|
+
}
|
|
5442
|
+
return {
|
|
5443
|
+
title: "Create your first list",
|
|
5444
|
+
detail: "Lead gen telemetry will appear here once a list starts moving through the pipeline.",
|
|
5445
|
+
buttonLabel: "Open lists",
|
|
5446
|
+
buttonTo: "/lead-gen/lists",
|
|
5447
|
+
tone: "gray"
|
|
5448
|
+
};
|
|
5449
|
+
}
|
|
5450
|
+
function CompactPipelineStage({
|
|
5451
|
+
label,
|
|
5452
|
+
value,
|
|
5453
|
+
ratio
|
|
5454
|
+
}) {
|
|
5455
|
+
return /* @__PURE__ */ jsx(Paper, { withBorder: true, p: "sm", children: /* @__PURE__ */ jsxs(Stack, { gap: 6, children: [
|
|
5456
|
+
/* @__PURE__ */ jsxs(Group, { justify: "space-between", gap: "xs", wrap: "nowrap", children: [
|
|
5457
|
+
/* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", tt: "uppercase", fw: 600, children: label }),
|
|
5458
|
+
/* @__PURE__ */ jsx(Badge, { size: "xs", variant: "light", color: "blue", children: formatPercentage(ratio) })
|
|
5459
|
+
] }),
|
|
5460
|
+
/* @__PURE__ */ jsx(Title, { order: 4, children: value }),
|
|
5461
|
+
/* @__PURE__ */ jsx(Progress, { value: ratio == null ? 0 : ratio * 100, size: "sm", radius: "xl", color: "blue" })
|
|
5462
|
+
] }) });
|
|
4908
5463
|
}
|
|
4909
5464
|
function LeadGenOverviewPage() {
|
|
4910
|
-
const
|
|
5465
|
+
const telemetryQuery = useListsTelemetry();
|
|
5466
|
+
const listsQuery = useLists();
|
|
5467
|
+
const data = telemetryQuery.data;
|
|
5468
|
+
const listMetaById = new Map((listsQuery.data ?? []).map((list) => [list.id, list]));
|
|
4911
5469
|
const totalCompanies = data?.reduce((sum, list) => sum + list.totalCompanies, 0) ?? 0;
|
|
4912
5470
|
const totalContacts = data?.reduce((sum, list) => sum + list.totalContacts, 0) ?? 0;
|
|
4913
5471
|
const stageTotals = data?.reduce(
|
|
@@ -4922,82 +5480,210 @@ function LeadGenOverviewPage() {
|
|
|
4922
5480
|
}),
|
|
4923
5481
|
{ populated: 0, extracted: 0, qualified: 0, discovered: 0, verified: 0, personalized: 0, uploaded: 0 }
|
|
4924
5482
|
) ?? { populated: 0, extracted: 0, qualified: 0, discovered: 0, verified: 0, personalized: 0, uploaded: 0 };
|
|
4925
|
-
const
|
|
4926
|
-
|
|
4927
|
-
|
|
4928
|
-
|
|
4929
|
-
|
|
4930
|
-
|
|
4931
|
-
|
|
4932
|
-
|
|
4933
|
-
{
|
|
4934
|
-
|
|
4935
|
-
|
|
4936
|
-
|
|
5483
|
+
const deliverabilityTotals = data?.reduce(
|
|
5484
|
+
(acc, list) => ({
|
|
5485
|
+
valid: acc.valid + list.deliverability.valid,
|
|
5486
|
+
risky: acc.risky + list.deliverability.risky,
|
|
5487
|
+
invalid: acc.invalid + list.deliverability.invalid,
|
|
5488
|
+
unknown: acc.unknown + list.deliverability.unknown,
|
|
5489
|
+
bounced: acc.bounced + list.deliverability.bounced
|
|
5490
|
+
}),
|
|
5491
|
+
{ valid: 0, risky: 0, invalid: 0, unknown: 0, bounced: 0 }
|
|
5492
|
+
) ?? { risky: 0, invalid: 0, bounced: 0 };
|
|
5493
|
+
const activeListCount = data?.filter((list) => (list.activeWorkflows?.length ?? 0) > 0).length ?? 0;
|
|
5494
|
+
const verificationBacklog = computeBacklog(stageTotals.discovered, stageTotals.verified);
|
|
5495
|
+
const personalizationBacklog = computeBacklog(stageTotals.verified, stageTotals.personalized);
|
|
5496
|
+
const uploadBacklog = computeBacklog(stageTotals.personalized, stageTotals.uploaded);
|
|
5497
|
+
const deliverabilityRiskCount = deliverabilityTotals.risky + deliverabilityTotals.invalid + deliverabilityTotals.bounced;
|
|
5498
|
+
const totalLists = data?.length ?? 0;
|
|
5499
|
+
const summaryLine = [
|
|
5500
|
+
`${totalLists} ${totalLists === 1 ? "list" : "lists"} active`,
|
|
5501
|
+
`${formatCountLabel(uploadBacklog, "contact")} ready for upload`,
|
|
5502
|
+
deliverabilityRiskCount === 0 ? "no risks" : `${formatCountLabel(deliverabilityRiskCount, "risk")}`
|
|
5503
|
+
].join(" \u2022 ");
|
|
5504
|
+
const primaryAction = getPrimaryAction({
|
|
5505
|
+
uploadBacklog,
|
|
5506
|
+
personalizationBacklog,
|
|
5507
|
+
verificationBacklog,
|
|
5508
|
+
deliverabilityRiskCount,
|
|
5509
|
+
activeListCount,
|
|
5510
|
+
totalLists
|
|
5511
|
+
});
|
|
5512
|
+
const pipelineStages = [
|
|
5513
|
+
{
|
|
5514
|
+
label: "Populated",
|
|
5515
|
+
value: stageTotals.populated,
|
|
5516
|
+
ratio: totalCompanies === 0 ? null : stageTotals.populated / totalCompanies
|
|
5517
|
+
},
|
|
5518
|
+
{
|
|
5519
|
+
label: "Qualified",
|
|
5520
|
+
value: stageTotals.qualified,
|
|
5521
|
+
ratio: totalCompanies === 0 ? null : stageTotals.qualified / totalCompanies
|
|
5522
|
+
},
|
|
5523
|
+
{
|
|
5524
|
+
label: "Verified",
|
|
5525
|
+
value: stageTotals.verified,
|
|
5526
|
+
ratio: totalContacts === 0 ? null : stageTotals.verified / totalContacts
|
|
5527
|
+
},
|
|
5528
|
+
{
|
|
5529
|
+
label: "Personalized",
|
|
5530
|
+
value: stageTotals.personalized,
|
|
5531
|
+
ratio: totalContacts === 0 ? null : stageTotals.personalized / totalContacts
|
|
5532
|
+
},
|
|
5533
|
+
{
|
|
5534
|
+
label: "Uploaded",
|
|
5535
|
+
value: stageTotals.uploaded,
|
|
5536
|
+
ratio: totalContacts === 0 ? null : stageTotals.uploaded / totalContacts
|
|
5537
|
+
}
|
|
4937
5538
|
];
|
|
5539
|
+
const overviewRows = data?.map((list) => {
|
|
5540
|
+
const listMeta = listMetaById.get(list.listId);
|
|
5541
|
+
const completionRatio = computeCompletionRatio(list.stageCounts.populated, list.stageCounts.personalized);
|
|
5542
|
+
const nextFocus = getNextFocus(list);
|
|
5543
|
+
return {
|
|
5544
|
+
...list,
|
|
5545
|
+
name: listMeta?.name ?? `List ${list.listId.slice(0, 8)}`,
|
|
5546
|
+
completionRatio,
|
|
5547
|
+
nextFocus,
|
|
5548
|
+
status: getOverviewStatus(list)
|
|
5549
|
+
};
|
|
5550
|
+
}).sort((a, b) => {
|
|
5551
|
+
const aActive = a.status.label === "Active Work" ? 1 : 0;
|
|
5552
|
+
const bActive = b.status.label === "Active Work" ? 1 : 0;
|
|
5553
|
+
if (aActive !== bActive) return bActive - aActive;
|
|
5554
|
+
const aBacklog = computeBacklog(a.stageCounts.discovered, a.stageCounts.verified) + computeBacklog(a.stageCounts.verified, a.stageCounts.personalized) + computeBacklog(a.stageCounts.personalized, a.stageCounts.uploaded);
|
|
5555
|
+
const bBacklog = computeBacklog(b.stageCounts.discovered, b.stageCounts.verified) + computeBacklog(b.stageCounts.verified, b.stageCounts.personalized) + computeBacklog(b.stageCounts.personalized, b.stageCounts.uploaded);
|
|
5556
|
+
if (aBacklog !== bBacklog) return bBacklog - aBacklog;
|
|
5557
|
+
return b.totalContacts - a.totalContacts;
|
|
5558
|
+
}) ?? [];
|
|
4938
5559
|
return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsx(PageContainer, { children: /* @__PURE__ */ jsxs(Stack, { children: [
|
|
4939
5560
|
/* @__PURE__ */ jsx(
|
|
4940
5561
|
PageTitleCaption,
|
|
4941
5562
|
{
|
|
4942
5563
|
title: "Lead Gen Overview",
|
|
4943
|
-
caption:
|
|
5564
|
+
caption: summaryLine,
|
|
5565
|
+
rightSection: /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
|
|
5566
|
+
/* @__PURE__ */ jsx(Button, { component: Link$1, to: "/lead-gen/lists", size: "sm", variant: "light", children: "Lists" }),
|
|
5567
|
+
/* @__PURE__ */ jsx(Button, { component: Link$1, to: "/lead-gen/deliverability", size: "sm", variant: "light", children: "Deliverability" })
|
|
5568
|
+
] })
|
|
4944
5569
|
}
|
|
4945
5570
|
),
|
|
4946
|
-
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: [
|
|
4947
|
-
/* @__PURE__ */ jsxs(SimpleGrid, { cols: { base:
|
|
4948
|
-
/* @__PURE__ */ jsx(StatCard, { variant: "hero", icon:
|
|
4949
|
-
/* @__PURE__ */ jsx(StatCard, { variant: "hero", icon:
|
|
4950
|
-
/* @__PURE__ */ jsx(StatCard, { variant: "hero", icon:
|
|
4951
|
-
/* @__PURE__ */ jsx(StatCard, { variant: "hero", icon: IconChecklist, value: completionLabel, label: "Overall Completion" })
|
|
4952
|
-
] }),
|
|
4953
|
-
/* @__PURE__ */ jsxs(Text, { size: "sm", c: "dimmed", children: [
|
|
4954
|
-
"View per-list detail on the",
|
|
4955
|
-
" ",
|
|
4956
|
-
/* @__PURE__ */ jsx(Anchor, { component: Link$1, to: "/lead-gen/lists", size: "sm", children: "Lists" }),
|
|
4957
|
-
" ",
|
|
4958
|
-
"page."
|
|
5571
|
+
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: [
|
|
5572
|
+
/* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 1, sm: 3 }, children: [
|
|
5573
|
+
/* @__PURE__ */ jsx(StatCard, { variant: "hero", icon: IconPlayerPlay, value: activeListCount, label: "Active Lists" }),
|
|
5574
|
+
/* @__PURE__ */ jsx(StatCard, { variant: "hero", icon: IconArrowRight, value: uploadBacklog, label: "Ready Contacts" }),
|
|
5575
|
+
/* @__PURE__ */ jsx(StatCard, { variant: "hero", icon: IconAlertCircle, value: deliverabilityRiskCount, label: "At Risk" })
|
|
4959
5576
|
] }),
|
|
4960
5577
|
/* @__PURE__ */ jsx(Paper, { withBorder: true, p: "md", children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
|
|
4961
|
-
/* @__PURE__ */
|
|
4962
|
-
|
|
4963
|
-
|
|
4964
|
-
|
|
4965
|
-
|
|
5578
|
+
/* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "flex-start", gap: "md", children: [
|
|
5579
|
+
/* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
|
|
5580
|
+
/* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", tt: "uppercase", fw: 700, children: "Next Action" }),
|
|
5581
|
+
/* @__PURE__ */ jsx(Title, { order: 3, children: primaryAction.title }),
|
|
5582
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", maw: 620, children: primaryAction.detail })
|
|
5583
|
+
] }),
|
|
5584
|
+
/* @__PURE__ */ jsx(Button, { component: Link$1, to: primaryAction.buttonTo, size: "sm", variant: "light", children: primaryAction.buttonLabel })
|
|
5585
|
+
] }),
|
|
5586
|
+
/* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 1, md: 3 }, children: [
|
|
5587
|
+
/* @__PURE__ */ jsxs(Paper, { withBorder: true, p: "sm", children: [
|
|
5588
|
+
/* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", tt: "uppercase", fw: 600, children: "Verification" }),
|
|
5589
|
+
/* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "flex-end", mt: 6, children: [
|
|
5590
|
+
/* @__PURE__ */ jsx(Title, { order: 4, children: verificationBacklog }),
|
|
5591
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "waiting" })
|
|
5592
|
+
] })
|
|
5593
|
+
] }),
|
|
5594
|
+
/* @__PURE__ */ jsxs(Paper, { withBorder: true, p: "sm", children: [
|
|
5595
|
+
/* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", tt: "uppercase", fw: 600, children: "Personalization" }),
|
|
5596
|
+
/* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "flex-end", mt: 6, children: [
|
|
5597
|
+
/* @__PURE__ */ jsx(Title, { order: 4, children: personalizationBacklog }),
|
|
5598
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "ready" })
|
|
5599
|
+
] })
|
|
5600
|
+
] }),
|
|
5601
|
+
/* @__PURE__ */ jsxs(Paper, { withBorder: true, p: "sm", children: [
|
|
5602
|
+
/* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", tt: "uppercase", fw: 600, children: "Completion" }),
|
|
5603
|
+
/* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "flex-end", mt: 6, children: [
|
|
5604
|
+
/* @__PURE__ */ jsx(Title, { order: 4, children: computeCompletionPercentage(stageTotals.populated, stageTotals.personalized) }),
|
|
5605
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "personalized" })
|
|
5606
|
+
] })
|
|
5607
|
+
] })
|
|
5608
|
+
] })
|
|
5609
|
+
] }) }),
|
|
5610
|
+
/* @__PURE__ */ jsx(Paper, { withBorder: true, p: "md", children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
|
|
5611
|
+
/* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "flex-end", children: [
|
|
5612
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
5613
|
+
/* @__PURE__ */ jsx(Title, { order: 3, children: "Pipeline" }),
|
|
5614
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "Keep the funnel visible without giving every zero state its own section." })
|
|
5615
|
+
] }),
|
|
5616
|
+
/* @__PURE__ */ jsxs(Badge, { variant: "light", color: "blue", children: [
|
|
5617
|
+
stageTotals.extracted,
|
|
5618
|
+
" extracted \u2022 ",
|
|
5619
|
+
stageTotals.discovered,
|
|
5620
|
+
" discovered"
|
|
5621
|
+
] })
|
|
5622
|
+
] }),
|
|
5623
|
+
/* @__PURE__ */ jsx(SimpleGrid, { cols: { base: 1, sm: 2, xl: 5 }, children: pipelineStages.map((stage) => /* @__PURE__ */ jsx(
|
|
5624
|
+
CompactPipelineStage,
|
|
5625
|
+
{
|
|
5626
|
+
label: stage.label,
|
|
5627
|
+
value: stage.value,
|
|
5628
|
+
ratio: stage.ratio
|
|
5629
|
+
},
|
|
5630
|
+
stage.label
|
|
5631
|
+
)) })
|
|
4966
5632
|
] }) }),
|
|
4967
5633
|
/* @__PURE__ */ jsx(Paper, { withBorder: true, p: "md", children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
|
|
4968
|
-
/* @__PURE__ */
|
|
5634
|
+
/* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "flex-end", children: [
|
|
5635
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
5636
|
+
/* @__PURE__ */ jsx(Title, { order: 3, children: "List Snapshot" }),
|
|
5637
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "Ranked to surface active work first, then the largest unresolved backlogs." })
|
|
5638
|
+
] }),
|
|
5639
|
+
/* @__PURE__ */ jsx(Button, { component: Link$1, to: "/lead-gen/lists", size: "sm", variant: "subtle", children: "Open lists" })
|
|
5640
|
+
] }),
|
|
4969
5641
|
/* @__PURE__ */ jsxs(Table, { children: [
|
|
4970
5642
|
/* @__PURE__ */ jsx(Table.Thead, { children: /* @__PURE__ */ jsxs(Table.Tr, { children: [
|
|
4971
5643
|
/* @__PURE__ */ jsx(Table.Th, { children: "List" }),
|
|
4972
5644
|
/* @__PURE__ */ jsx(Table.Th, { children: "Companies" }),
|
|
4973
5645
|
/* @__PURE__ */ jsx(Table.Th, { children: "Contacts" }),
|
|
4974
|
-
/* @__PURE__ */ jsx(Table.Th, { children: "
|
|
5646
|
+
/* @__PURE__ */ jsx(Table.Th, { children: "Next Focus" }),
|
|
5647
|
+
/* @__PURE__ */ jsx(Table.Th, { children: "Progress" }),
|
|
4975
5648
|
/* @__PURE__ */ jsx(Table.Th, { children: "Status" })
|
|
4976
5649
|
] }) }),
|
|
4977
|
-
/* @__PURE__ */ jsx(Table.Tbody, { children:
|
|
4978
|
-
|
|
4979
|
-
list.
|
|
4980
|
-
list.
|
|
4981
|
-
)
|
|
4982
|
-
|
|
4983
|
-
|
|
4984
|
-
|
|
4985
|
-
/* @__PURE__ */ jsx(
|
|
4986
|
-
/* @__PURE__ */ jsx(
|
|
4987
|
-
|
|
4988
|
-
|
|
4989
|
-
|
|
4990
|
-
|
|
5650
|
+
/* @__PURE__ */ jsx(Table.Tbody, { children: overviewRows.map((list) => /* @__PURE__ */ jsxs(Table.Tr, { children: [
|
|
5651
|
+
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsxs(Stack, { gap: 2, children: [
|
|
5652
|
+
/* @__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 }) }),
|
|
5653
|
+
/* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: list.listId })
|
|
5654
|
+
] }) }),
|
|
5655
|
+
/* @__PURE__ */ jsx(Table.Td, { children: list.totalCompanies }),
|
|
5656
|
+
/* @__PURE__ */ jsx(Table.Td, { children: list.totalContacts }),
|
|
5657
|
+
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsxs(Stack, { gap: 2, children: [
|
|
5658
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", fw: 500, children: list.nextFocus.label }),
|
|
5659
|
+
/* @__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` })
|
|
5660
|
+
] }) }),
|
|
5661
|
+
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsxs(Stack, { gap: "xs", miw: 150, children: [
|
|
5662
|
+
/* @__PURE__ */ jsxs(Group, { justify: "space-between", gap: "xs", children: [
|
|
5663
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", children: formatPercentage(list.completionRatio) }),
|
|
5664
|
+
/* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
|
|
5665
|
+
list.stageCounts.personalized,
|
|
5666
|
+
"/",
|
|
5667
|
+
list.stageCounts.populated || 0
|
|
5668
|
+
] })
|
|
5669
|
+
] }),
|
|
5670
|
+
/* @__PURE__ */ jsx(
|
|
5671
|
+
Progress,
|
|
5672
|
+
{
|
|
5673
|
+
value: list.completionRatio == null ? 0 : list.completionRatio * 100,
|
|
5674
|
+
size: "sm",
|
|
5675
|
+
radius: "xl",
|
|
5676
|
+
color: "blue"
|
|
5677
|
+
}
|
|
5678
|
+
)
|
|
5679
|
+
] }) }),
|
|
5680
|
+
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: list.status.color, children: list.status.label }) })
|
|
5681
|
+
] }, list.listId)) })
|
|
4991
5682
|
] })
|
|
4992
5683
|
] }) })
|
|
4993
5684
|
] })
|
|
4994
5685
|
] }) }) });
|
|
4995
5686
|
}
|
|
4996
|
-
function computeBounceRate(d) {
|
|
4997
|
-
const denominator = d.valid + d.risky + d.invalid + d.bounced;
|
|
4998
|
-
if (denominator === 0) return EM_DASH;
|
|
4999
|
-
return `${(d.bounced / denominator * 100).toFixed(1)}%`;
|
|
5000
|
-
}
|
|
5001
5687
|
function LeadGenDeliverabilityPage() {
|
|
5002
5688
|
const { data, isLoading, error } = useListsTelemetry();
|
|
5003
5689
|
if (isLoading) {
|
|
@@ -5218,7 +5904,7 @@ function LeadGenListsPage() {
|
|
|
5218
5904
|
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { fw: 500, children: list.name }) }),
|
|
5219
5905
|
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: list.description || "-" }) }),
|
|
5220
5906
|
/* @__PURE__ */ jsx(Table.Td, { children: contactCountByListId.get(list.id) ?? 0 }),
|
|
5221
|
-
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children:
|
|
5907
|
+
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: formatDate3(list.createdAt) }) })
|
|
5222
5908
|
]
|
|
5223
5909
|
},
|
|
5224
5910
|
list.id
|
|
@@ -6073,7 +6759,7 @@ function ringValue(completed, total) {
|
|
|
6073
6759
|
if (total === 0) return 0;
|
|
6074
6760
|
return Math.round(completed / total * 100);
|
|
6075
6761
|
}
|
|
6076
|
-
function
|
|
6762
|
+
function formatDate4(iso) {
|
|
6077
6763
|
return new Date(iso).toLocaleDateString();
|
|
6078
6764
|
}
|
|
6079
6765
|
function daysRelative(targetEndDate) {
|
|
@@ -6161,17 +6847,17 @@ function HealthStatusCard({
|
|
|
6161
6847
|
] }),
|
|
6162
6848
|
/* @__PURE__ */ jsx(Stack, { gap: 2, children: startDate && targetEndDate ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
6163
6849
|
/* @__PURE__ */ jsxs(Text, { size: "sm", fw: 500, children: [
|
|
6164
|
-
|
|
6850
|
+
formatDate4(startDate),
|
|
6165
6851
|
" \u2192 ",
|
|
6166
|
-
|
|
6852
|
+
formatDate4(targetEndDate)
|
|
6167
6853
|
] }),
|
|
6168
6854
|
timeInfo && /* @__PURE__ */ jsx(Text, { size: "xs", c: timeInfo.overdue ? "red" : "dimmed", children: timeInfo.label })
|
|
6169
6855
|
] }) : startDate ? /* @__PURE__ */ jsxs(Text, { size: "sm", fw: 500, children: [
|
|
6170
6856
|
"Started ",
|
|
6171
|
-
|
|
6857
|
+
formatDate4(startDate)
|
|
6172
6858
|
] }) : targetEndDate ? /* @__PURE__ */ jsxs(Text, { size: "sm", fw: 500, children: [
|
|
6173
6859
|
"Due ",
|
|
6174
|
-
|
|
6860
|
+
formatDate4(targetEndDate)
|
|
6175
6861
|
] }) : /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "No dates set" }) })
|
|
6176
6862
|
] }) })
|
|
6177
6863
|
] });
|
|
@@ -6557,6 +7243,414 @@ function ProjectsListPage({ onProjectClick } = {}) {
|
|
|
6557
7243
|
)
|
|
6558
7244
|
] });
|
|
6559
7245
|
}
|
|
7246
|
+
var noteTypeOptions = [
|
|
7247
|
+
{ value: "call_note", label: "Call Note" },
|
|
7248
|
+
{ value: "status_update", label: "Status Update" },
|
|
7249
|
+
{ value: "issue", label: "Issue" },
|
|
7250
|
+
{ value: "blocker", label: "Blocker" }
|
|
7251
|
+
];
|
|
7252
|
+
function parseChecklist(milestone) {
|
|
7253
|
+
const raw = milestone.checklist;
|
|
7254
|
+
if (!Array.isArray(raw)) return [];
|
|
7255
|
+
return raw;
|
|
7256
|
+
}
|
|
7257
|
+
function MilestoneChecklist({ milestone }) {
|
|
7258
|
+
const items = parseChecklist(milestone);
|
|
7259
|
+
const { mutate: updateMilestone } = useUpdateMilestone();
|
|
7260
|
+
const [newItemLabel, setNewItemLabel] = useState("");
|
|
7261
|
+
const [adding, setAdding] = useState(false);
|
|
7262
|
+
function saveChecklist(updated) {
|
|
7263
|
+
const allComplete = updated.length > 0 && updated.every((item) => item.completed);
|
|
7264
|
+
const wasComplete = milestone.status === "completed";
|
|
7265
|
+
const updates = {
|
|
7266
|
+
checklist: updated
|
|
7267
|
+
};
|
|
7268
|
+
if (allComplete && !wasComplete) {
|
|
7269
|
+
updates.status = "completed";
|
|
7270
|
+
updates.completed_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
7271
|
+
} else if (!allComplete && wasComplete) {
|
|
7272
|
+
updates.status = "in_progress";
|
|
7273
|
+
updates.completed_at = null;
|
|
7274
|
+
}
|
|
7275
|
+
updateMilestone({ id: milestone.id, updates });
|
|
7276
|
+
}
|
|
7277
|
+
function toggleItem(itemId) {
|
|
7278
|
+
const updated = items.map((item) => item.id === itemId ? { ...item, completed: !item.completed } : item);
|
|
7279
|
+
saveChecklist(updated);
|
|
7280
|
+
}
|
|
7281
|
+
function addItem() {
|
|
7282
|
+
const label = newItemLabel.trim();
|
|
7283
|
+
if (!label) return;
|
|
7284
|
+
const newItem = { id: crypto.randomUUID(), label, completed: false };
|
|
7285
|
+
saveChecklist([...items, newItem]);
|
|
7286
|
+
setNewItemLabel("");
|
|
7287
|
+
}
|
|
7288
|
+
function removeItem(itemId) {
|
|
7289
|
+
saveChecklist(items.filter((item) => item.id !== itemId));
|
|
7290
|
+
}
|
|
7291
|
+
const completedCount = items.filter((item) => item.completed).length;
|
|
7292
|
+
return /* @__PURE__ */ jsxs(Stack, { gap: "xs", mt: "sm", children: [
|
|
7293
|
+
items.length > 0 && /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
|
|
7294
|
+
completedCount,
|
|
7295
|
+
"/",
|
|
7296
|
+
items.length,
|
|
7297
|
+
" complete"
|
|
7298
|
+
] }),
|
|
7299
|
+
items.map((item) => /* @__PURE__ */ jsxs(Group, { gap: "xs", wrap: "nowrap", children: [
|
|
7300
|
+
/* @__PURE__ */ jsx(
|
|
7301
|
+
Checkbox,
|
|
7302
|
+
{
|
|
7303
|
+
size: "xs",
|
|
7304
|
+
checked: item.completed,
|
|
7305
|
+
onChange: () => toggleItem(item.id),
|
|
7306
|
+
label: /* @__PURE__ */ jsx(
|
|
7307
|
+
Text,
|
|
7308
|
+
{
|
|
7309
|
+
size: "sm",
|
|
7310
|
+
td: item.completed ? "line-through" : void 0,
|
|
7311
|
+
c: item.completed ? "dimmed" : "var(--color-text-subtle)",
|
|
7312
|
+
children: item.label
|
|
7313
|
+
}
|
|
7314
|
+
),
|
|
7315
|
+
styles: { input: { cursor: "pointer" }, label: { cursor: "pointer" } }
|
|
7316
|
+
}
|
|
7317
|
+
),
|
|
7318
|
+
/* @__PURE__ */ jsx(ActionIcon, { variant: "subtle", size: "xs", onClick: () => removeItem(item.id), style: { opacity: 0.4 }, children: /* @__PURE__ */ jsx(IconX, { size: 12 }) })
|
|
7319
|
+
] }, item.id)),
|
|
7320
|
+
adding ? /* @__PURE__ */ jsx(
|
|
7321
|
+
TextInput,
|
|
7322
|
+
{
|
|
7323
|
+
size: "xs",
|
|
7324
|
+
placeholder: "Item label...",
|
|
7325
|
+
value: newItemLabel,
|
|
7326
|
+
onChange: (event) => setNewItemLabel(event.target.value),
|
|
7327
|
+
onKeyDown: (event) => {
|
|
7328
|
+
if (event.key === "Enter") {
|
|
7329
|
+
addItem();
|
|
7330
|
+
setAdding(false);
|
|
7331
|
+
}
|
|
7332
|
+
if (event.key === "Escape") {
|
|
7333
|
+
setNewItemLabel("");
|
|
7334
|
+
setAdding(false);
|
|
7335
|
+
}
|
|
7336
|
+
},
|
|
7337
|
+
onBlur: () => {
|
|
7338
|
+
if (newItemLabel.trim()) addItem();
|
|
7339
|
+
setAdding(false);
|
|
7340
|
+
},
|
|
7341
|
+
autoFocus: true,
|
|
7342
|
+
style: { maxWidth: 250 }
|
|
7343
|
+
}
|
|
7344
|
+
) : /* @__PURE__ */ jsx(Group, { children: /* @__PURE__ */ jsx(Button, { variant: "subtle", size: "compact-xs", leftSection: /* @__PURE__ */ jsx(IconPlus, { size: 12 }), onClick: () => setAdding(true), children: "Add item" }) })
|
|
7345
|
+
] });
|
|
7346
|
+
}
|
|
7347
|
+
function AddNoteModal({ opened, onClose, projectId }) {
|
|
7348
|
+
const [noteType, setNoteType] = useState("call_note");
|
|
7349
|
+
const [content, setContent] = useState("");
|
|
7350
|
+
const [summary, setSummary] = useState("");
|
|
7351
|
+
const { mutate: createNote, isPending } = useCreateNote();
|
|
7352
|
+
function handleSubmit() {
|
|
7353
|
+
if (!content.trim() || !noteType) return;
|
|
7354
|
+
createNote(
|
|
7355
|
+
{
|
|
7356
|
+
project_id: projectId,
|
|
7357
|
+
type: noteType,
|
|
7358
|
+
content: content.trim(),
|
|
7359
|
+
summary: summary.trim() || null
|
|
7360
|
+
},
|
|
7361
|
+
{
|
|
7362
|
+
onSuccess: () => {
|
|
7363
|
+
setContent("");
|
|
7364
|
+
setSummary("");
|
|
7365
|
+
setNoteType("call_note");
|
|
7366
|
+
onClose();
|
|
7367
|
+
}
|
|
7368
|
+
}
|
|
7369
|
+
);
|
|
7370
|
+
}
|
|
7371
|
+
return /* @__PURE__ */ jsx(CustomModal, { opened, onClose, size: "md", loading: isPending, children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
|
|
7372
|
+
/* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "flex-start", children: [
|
|
7373
|
+
/* @__PURE__ */ jsx(Title, { order: 3, children: "Add Note" }),
|
|
7374
|
+
/* @__PURE__ */ jsx(ActionIcon, { variant: "subtle", onClick: onClose, disabled: isPending, children: /* @__PURE__ */ jsx(IconX, { size: 18 }) })
|
|
7375
|
+
] }),
|
|
7376
|
+
/* @__PURE__ */ jsx(Select, { label: "Type", data: noteTypeOptions, value: noteType, onChange: setNoteType, required: true }),
|
|
7377
|
+
/* @__PURE__ */ jsx(
|
|
7378
|
+
TextInput,
|
|
7379
|
+
{
|
|
7380
|
+
label: "Summary",
|
|
7381
|
+
placeholder: "Brief summary (optional)",
|
|
7382
|
+
value: summary,
|
|
7383
|
+
onChange: (event) => setSummary(event.target.value)
|
|
7384
|
+
}
|
|
7385
|
+
),
|
|
7386
|
+
/* @__PURE__ */ jsx(
|
|
7387
|
+
Textarea,
|
|
7388
|
+
{
|
|
7389
|
+
label: "Content",
|
|
7390
|
+
placeholder: "Note content...",
|
|
7391
|
+
minRows: 4,
|
|
7392
|
+
value: content,
|
|
7393
|
+
onChange: (event) => setContent(event.target.value),
|
|
7394
|
+
required: true
|
|
7395
|
+
}
|
|
7396
|
+
),
|
|
7397
|
+
/* @__PURE__ */ jsxs(Group, { justify: "flex-end", children: [
|
|
7398
|
+
/* @__PURE__ */ jsx(Button, { variant: "light", onClick: onClose, disabled: isPending, children: "Cancel" }),
|
|
7399
|
+
/* @__PURE__ */ jsx(Button, { onClick: handleSubmit, loading: isPending, disabled: !content.trim() || !noteType, children: "Add Note" })
|
|
7400
|
+
] })
|
|
7401
|
+
] }) });
|
|
7402
|
+
}
|
|
7403
|
+
function ProjectDetailPage({ projectId, onBack, backLabel = "Projects" }) {
|
|
7404
|
+
const { data: project, isLoading, error } = useProject(projectId);
|
|
7405
|
+
const { data: notes } = useProjectNotes({ projectId });
|
|
7406
|
+
const { mutate: deleteProject, isPending: isDeleting } = useDeleteProject$1();
|
|
7407
|
+
const [addNoteOpen, setAddNoteOpen] = useState(false);
|
|
7408
|
+
const [deleteModalOpen, setDeleteModalOpen] = useState(false);
|
|
7409
|
+
if (isLoading) {
|
|
7410
|
+
return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsx(Center, { style: { flex: 1 }, children: /* @__PURE__ */ jsx(Loader, {}) }) });
|
|
7411
|
+
}
|
|
7412
|
+
if (error) {
|
|
7413
|
+
return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsx(Center, { style: { flex: 1 }, children: /* @__PURE__ */ jsx(Alert, { color: "red", children: "Error loading project" }) }) });
|
|
7414
|
+
}
|
|
7415
|
+
if (!project) {
|
|
7416
|
+
return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsx(Center, { style: { flex: 1 }, children: /* @__PURE__ */ jsx(Alert, { color: "yellow", children: "Project not found" }) }) });
|
|
7417
|
+
}
|
|
7418
|
+
const milestones = project.milestones ?? [];
|
|
7419
|
+
const tasks = project.tasks ?? [];
|
|
7420
|
+
const totalMilestones = milestones.length;
|
|
7421
|
+
const completedMilestones = milestones.filter((milestone) => milestone.status === "completed").length;
|
|
7422
|
+
const totalTasks = tasks.length;
|
|
7423
|
+
const approvedTasks = tasks.filter((task) => task.status === "approved").length;
|
|
7424
|
+
const milestoneProgress = calculateProgress(completedMilestones, totalMilestones);
|
|
7425
|
+
const taskProgress = calculateProgress(approvedTasks, totalTasks);
|
|
7426
|
+
const companyName = project.company?.name ?? null;
|
|
7427
|
+
return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsx(PageContainer, { children: /* @__PURE__ */ jsxs(Stack, { children: [
|
|
7428
|
+
/* @__PURE__ */ jsx(
|
|
7429
|
+
PageTitleCaption,
|
|
7430
|
+
{
|
|
7431
|
+
title: project.name,
|
|
7432
|
+
caption: [companyName, formatStatusLabel(project.status || "unknown")].filter(Boolean).join(" - "),
|
|
7433
|
+
rightSection: /* @__PURE__ */ jsx(Group, { children: /* @__PURE__ */ jsx(Button, { variant: "light", size: "sm", leftSection: /* @__PURE__ */ jsx(IconArrowLeft, { size: 16 }), onClick: onBack, children: backLabel }) })
|
|
7434
|
+
}
|
|
7435
|
+
),
|
|
7436
|
+
/* @__PURE__ */ jsxs(SimpleGrid, { cols: 4, children: [
|
|
7437
|
+
/* @__PURE__ */ jsx(
|
|
7438
|
+
StatCard,
|
|
7439
|
+
{
|
|
7440
|
+
variant: "hero",
|
|
7441
|
+
icon: IconHeartbeat,
|
|
7442
|
+
value: formatStatusLabel(project.status || "unknown"),
|
|
7443
|
+
label: "Status",
|
|
7444
|
+
valueColor: `var(--mantine-color-${projectStatusColors[project.status || ""] || "gray"}-6)`
|
|
7445
|
+
}
|
|
7446
|
+
),
|
|
7447
|
+
/* @__PURE__ */ jsx(StatCard, { variant: "hero", icon: IconFlag, value: `${completedMilestones}/${totalMilestones}`, label: "Milestones", children: /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
|
|
7448
|
+
milestoneProgress,
|
|
7449
|
+
"% complete"
|
|
7450
|
+
] }) }),
|
|
7451
|
+
/* @__PURE__ */ jsx(StatCard, { variant: "hero", icon: IconChecklist, value: `${approvedTasks}/${totalTasks}`, label: "Tasks", children: /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
|
|
7452
|
+
taskProgress,
|
|
7453
|
+
"% complete"
|
|
7454
|
+
] }) }),
|
|
7455
|
+
/* @__PURE__ */ jsx(StatCard, { variant: "hero", icon: IconCalendar, value: project.start_date ? formatDate(project.start_date) : "-", label: "Timeline", children: project.target_end_date && /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
|
|
7456
|
+
"to ",
|
|
7457
|
+
formatDate(project.target_end_date)
|
|
7458
|
+
] }) })
|
|
7459
|
+
] }),
|
|
7460
|
+
/* @__PURE__ */ jsxs(
|
|
7461
|
+
Paper,
|
|
7462
|
+
{
|
|
7463
|
+
p: "md",
|
|
7464
|
+
style: {
|
|
7465
|
+
border: "1px solid var(--color-border)",
|
|
7466
|
+
borderRadius: "var(--mantine-radius-default)"
|
|
7467
|
+
},
|
|
7468
|
+
children: [
|
|
7469
|
+
/* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
|
|
7470
|
+
/* @__PURE__ */ jsxs(Group, { justify: "space-between", wrap: "wrap", children: [
|
|
7471
|
+
/* @__PURE__ */ jsxs(Group, { gap: "sm", children: [
|
|
7472
|
+
/* @__PURE__ */ jsx(Title, { order: 3, children: "Project Details" }),
|
|
7473
|
+
/* @__PURE__ */ jsx(Badge, { variant: "light", color: projectStatusColors[project.status || ""] || "gray", children: formatStatusLabel(project.status || "unknown") })
|
|
7474
|
+
] }),
|
|
7475
|
+
/* @__PURE__ */ jsxs(Group, { gap: 4, children: [
|
|
7476
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", ff: "monospace", c: "var(--color-text-subtle)", children: project.id }),
|
|
7477
|
+
/* @__PURE__ */ jsx(CopyButton, { value: project.id, children: ({ copied, copy }) => /* @__PURE__ */ jsx(Tooltip, { label: copied ? "Copied!" : "Copy ID", withArrow: true, children: /* @__PURE__ */ jsx(ActionIcon, { onClick: copy, variant: "subtle", size: "xs", color: copied ? "green" : "var(--color-text-subtle)", children: copied ? /* @__PURE__ */ jsx(IconCheck, { size: 14 }) : /* @__PURE__ */ jsx(IconCopy, { size: 14 }) }) }) })
|
|
7478
|
+
] })
|
|
7479
|
+
] }),
|
|
7480
|
+
/* @__PURE__ */ jsxs(Group, { gap: "lg", children: [
|
|
7481
|
+
project.start_date && /* @__PURE__ */ jsxs(Group, { gap: 4, children: [
|
|
7482
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", fw: 500, children: "Started:" }),
|
|
7483
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", children: formatDate(project.start_date) })
|
|
7484
|
+
] }),
|
|
7485
|
+
project.target_end_date && /* @__PURE__ */ jsxs(Group, { gap: 4, children: [
|
|
7486
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", fw: 500, children: "Target:" }),
|
|
7487
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", children: formatDate(project.target_end_date) })
|
|
7488
|
+
] }),
|
|
7489
|
+
project.contract_value != null && /* @__PURE__ */ jsxs(Group, { gap: 4, children: [
|
|
7490
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", fw: 500, children: "Contract:" }),
|
|
7491
|
+
/* @__PURE__ */ jsxs(Text, { size: "sm", children: [
|
|
7492
|
+
"$",
|
|
7493
|
+
project.contract_value.toLocaleString(),
|
|
7494
|
+
project.metadata && project.metadata.rate_type === "hourly" ? "/hr" : ""
|
|
7495
|
+
] })
|
|
7496
|
+
] }),
|
|
7497
|
+
project.company && /* @__PURE__ */ jsxs(Group, { gap: 4, children: [
|
|
7498
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", fw: 500, children: "Company:" }),
|
|
7499
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", children: project.company.name })
|
|
7500
|
+
] })
|
|
7501
|
+
] })
|
|
7502
|
+
] }),
|
|
7503
|
+
/* @__PURE__ */ jsx(Divider, { my: "md" }),
|
|
7504
|
+
/* @__PURE__ */ jsxs(Tabs, { defaultValue: "status", children: [
|
|
7505
|
+
/* @__PURE__ */ jsxs(Tabs.List, { children: [
|
|
7506
|
+
/* @__PURE__ */ jsx(Tabs.Tab, { value: "status", children: "Status" }),
|
|
7507
|
+
/* @__PURE__ */ jsx(Tabs.Tab, { value: "details", children: "Details" }),
|
|
7508
|
+
/* @__PURE__ */ jsx(Tabs.Tab, { value: "notes", children: "Notes" }),
|
|
7509
|
+
/* @__PURE__ */ jsx(Tabs.Tab, { value: "activity", children: "Activity" })
|
|
7510
|
+
] }),
|
|
7511
|
+
/* @__PURE__ */ jsx(Tabs.Panel, { value: "status", pt: "md", children: milestones.length === 0 ? /* @__PURE__ */ jsx(Text, { c: "dimmed", size: "sm", children: "No milestones yet." }) : /* @__PURE__ */ jsx(
|
|
7512
|
+
Timeline,
|
|
7513
|
+
{
|
|
7514
|
+
active: completedMilestones,
|
|
7515
|
+
bulletSize: 20,
|
|
7516
|
+
color: "var(--color-primary)",
|
|
7517
|
+
styles: {
|
|
7518
|
+
itemBullet: {
|
|
7519
|
+
borderColor: "color-mix(in srgb, var(--color-primary) 40%, transparent)",
|
|
7520
|
+
backgroundColor: "var(--color-surface)"
|
|
7521
|
+
},
|
|
7522
|
+
item: {
|
|
7523
|
+
"--_item-border-color": "color-mix(in srgb, var(--color-primary) 25%, transparent)"
|
|
7524
|
+
},
|
|
7525
|
+
itemTitle: {
|
|
7526
|
+
color: "var(--color-text)"
|
|
7527
|
+
}
|
|
7528
|
+
},
|
|
7529
|
+
children: milestones.map((milestone) => {
|
|
7530
|
+
const milestoneTasks = tasks.filter((task) => task.milestone_id === milestone.id);
|
|
7531
|
+
return /* @__PURE__ */ jsxs(
|
|
7532
|
+
Timeline.Item,
|
|
7533
|
+
{
|
|
7534
|
+
bullet: milestone.status === "completed" ? /* @__PURE__ */ jsx(
|
|
7535
|
+
"div",
|
|
7536
|
+
{
|
|
7537
|
+
style: {
|
|
7538
|
+
width: 12,
|
|
7539
|
+
height: 12,
|
|
7540
|
+
borderRadius: "50%",
|
|
7541
|
+
backgroundColor: "var(--color-primary)",
|
|
7542
|
+
boxShadow: "0 0 8px var(--color-primary), 0 0 16px color-mix(in srgb, var(--color-primary) 50%, transparent)"
|
|
7543
|
+
}
|
|
7544
|
+
}
|
|
7545
|
+
) : void 0,
|
|
7546
|
+
title: /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
|
|
7547
|
+
/* @__PURE__ */ jsx(Text, { fw: 500, children: milestone.name }),
|
|
7548
|
+
/* @__PURE__ */ jsx(Badge, { variant: "light", color: milestoneStatusColors[milestone.status || ""] || "gray", size: "sm", children: formatStatusLabel(milestone.status || "unknown") }),
|
|
7549
|
+
milestone.due_date && /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
|
|
7550
|
+
"Due ",
|
|
7551
|
+
formatDate(milestone.due_date)
|
|
7552
|
+
] })
|
|
7553
|
+
] }),
|
|
7554
|
+
children: [
|
|
7555
|
+
milestone.description && /* @__PURE__ */ jsx(Text, { size: "sm", mt: "xs", children: milestone.description }),
|
|
7556
|
+
/* @__PURE__ */ jsx(MilestoneChecklist, { milestone }),
|
|
7557
|
+
milestoneTasks.length > 0 && /* @__PURE__ */ jsx(Stack, { gap: "xs", mt: "sm", children: milestoneTasks.map((task) => /* @__PURE__ */ jsx(Card, { withBorder: true, p: "xs", children: /* @__PURE__ */ jsxs(Group, { gap: "xs", justify: "space-between", children: [
|
|
7558
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", fw: 500, children: task.name }),
|
|
7559
|
+
/* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
|
|
7560
|
+
/* @__PURE__ */ jsx(Badge, { variant: "light", color: taskTypeColors[task.type || ""] || "gray", size: "xs", children: formatStatusLabel(task.type || "other") }),
|
|
7561
|
+
/* @__PURE__ */ jsx(Badge, { variant: "light", color: taskStatusColors[task.status || ""] || "gray", size: "xs", children: formatStatusLabel(task.status || "pending") })
|
|
7562
|
+
] })
|
|
7563
|
+
] }) }, task.id)) })
|
|
7564
|
+
]
|
|
7565
|
+
},
|
|
7566
|
+
milestone.id
|
|
7567
|
+
);
|
|
7568
|
+
})
|
|
7569
|
+
}
|
|
7570
|
+
) }),
|
|
7571
|
+
/* @__PURE__ */ jsx(Tabs.Panel, { value: "details", pt: "md", children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
|
|
7572
|
+
/* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
|
|
7573
|
+
/* @__PURE__ */ jsx(Title, { order: 3, children: "Project Details" }),
|
|
7574
|
+
project.description && /* @__PURE__ */ jsxs("div", { children: [
|
|
7575
|
+
/* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "Description" }),
|
|
7576
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", children: project.description })
|
|
7577
|
+
] }),
|
|
7578
|
+
project.contract_value != null && /* @__PURE__ */ jsxs("div", { children: [
|
|
7579
|
+
/* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "Contract Value" }),
|
|
7580
|
+
/* @__PURE__ */ jsxs(Text, { size: "sm", fw: 500, children: [
|
|
7581
|
+
"$",
|
|
7582
|
+
project.contract_value.toLocaleString()
|
|
7583
|
+
] })
|
|
7584
|
+
] }),
|
|
7585
|
+
/* @__PURE__ */ jsxs(SimpleGrid, { cols: 3, children: [
|
|
7586
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
7587
|
+
/* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "Start Date" }),
|
|
7588
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", children: formatDate(project.start_date) })
|
|
7589
|
+
] }),
|
|
7590
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
7591
|
+
/* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "Target End Date" }),
|
|
7592
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", children: formatDate(project.target_end_date) })
|
|
7593
|
+
] }),
|
|
7594
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
7595
|
+
/* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "Actual End Date" }),
|
|
7596
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", children: formatDate(project.actual_end_date) })
|
|
7597
|
+
] })
|
|
7598
|
+
] })
|
|
7599
|
+
] }) }),
|
|
7600
|
+
project.company && /* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
|
|
7601
|
+
/* @__PURE__ */ jsx(Title, { order: 3, children: "Company" }),
|
|
7602
|
+
/* @__PURE__ */ jsx(Text, { fw: 500, children: project.company.name }),
|
|
7603
|
+
project.company.domain && /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: project.company.domain })
|
|
7604
|
+
] }) })
|
|
7605
|
+
] }) }),
|
|
7606
|
+
/* @__PURE__ */ jsx(Tabs.Panel, { value: "notes", pt: "md", children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
|
|
7607
|
+
/* @__PURE__ */ jsx(Group, { justify: "flex-end", children: /* @__PURE__ */ jsx(Button, { variant: "light", size: "sm", leftSection: /* @__PURE__ */ jsx(IconPlus, { size: 16 }), onClick: () => setAddNoteOpen(true), children: "Add Note" }) }),
|
|
7608
|
+
!notes?.length ? /* @__PURE__ */ jsx(Text, { c: "dimmed", size: "sm", children: "No notes yet." }) : /* @__PURE__ */ jsx(Stack, { gap: "sm", children: notes.map((note) => /* @__PURE__ */ jsx(Card, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
|
|
7609
|
+
/* @__PURE__ */ jsxs(Group, { justify: "space-between", children: [
|
|
7610
|
+
/* @__PURE__ */ jsx(Badge, { variant: "light", color: noteTypeColors[note.type || ""] || "gray", size: "sm", children: formatStatusLabel(note.type || "note") }),
|
|
7611
|
+
/* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: formatTimeAgo(note.occurred_at) })
|
|
7612
|
+
] }),
|
|
7613
|
+
note.summary && /* @__PURE__ */ jsx(Text, { size: "sm", fw: 500, children: note.summary }),
|
|
7614
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", children: note.content })
|
|
7615
|
+
] }) }, note.id)) })
|
|
7616
|
+
] }) }),
|
|
7617
|
+
/* @__PURE__ */ jsx(Tabs.Panel, { value: "activity", pt: "md", children: /* @__PURE__ */ jsx(Text, { c: "dimmed", size: "sm", children: "Activity feed coming soon." }) })
|
|
7618
|
+
] })
|
|
7619
|
+
]
|
|
7620
|
+
}
|
|
7621
|
+
),
|
|
7622
|
+
/* @__PURE__ */ jsx(AddNoteModal, { opened: addNoteOpen, onClose: () => setAddNoteOpen(false), projectId }),
|
|
7623
|
+
/* @__PURE__ */ jsx(CustomModal, { opened: deleteModalOpen, onClose: () => setDeleteModalOpen(false), size: "sm", children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
|
|
7624
|
+
/* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "flex-start", children: [
|
|
7625
|
+
/* @__PURE__ */ jsx(Title, { order: 4, children: "Delete project" }),
|
|
7626
|
+
/* @__PURE__ */ jsx(ActionIcon, { variant: "subtle", onClick: () => setDeleteModalOpen(false), children: /* @__PURE__ */ jsx(IconX, { size: 18 }) })
|
|
7627
|
+
] }),
|
|
7628
|
+
/* @__PURE__ */ jsxs(Text, { size: "sm", children: [
|
|
7629
|
+
"Are you sure you want to delete ",
|
|
7630
|
+
/* @__PURE__ */ jsx("strong", { children: project.name }),
|
|
7631
|
+
"? This cannot be undone."
|
|
7632
|
+
] }),
|
|
7633
|
+
/* @__PURE__ */ jsx(Divider, {}),
|
|
7634
|
+
/* @__PURE__ */ jsxs(Group, { justify: "flex-end", children: [
|
|
7635
|
+
/* @__PURE__ */ jsx(Button, { variant: "default", onClick: () => setDeleteModalOpen(false), disabled: isDeleting, children: "Cancel" }),
|
|
7636
|
+
/* @__PURE__ */ jsx(
|
|
7637
|
+
Button,
|
|
7638
|
+
{
|
|
7639
|
+
color: "red",
|
|
7640
|
+
loading: isDeleting,
|
|
7641
|
+
onClick: () => deleteProject(project.id, {
|
|
7642
|
+
onSuccess: () => {
|
|
7643
|
+
setDeleteModalOpen(false);
|
|
7644
|
+
onBack();
|
|
7645
|
+
}
|
|
7646
|
+
}),
|
|
7647
|
+
children: "Delete project"
|
|
7648
|
+
}
|
|
7649
|
+
)
|
|
7650
|
+
] })
|
|
7651
|
+
] }) })
|
|
7652
|
+
] }) }) });
|
|
7653
|
+
}
|
|
6560
7654
|
function NotificationPanel({ notifications, isLoading, onClose, onNavigate }) {
|
|
6561
7655
|
const markAllAsRead = useMarkAllAsRead();
|
|
6562
7656
|
const hasUnread = notifications.some((n) => !n.read);
|
|
@@ -6609,4 +7703,4 @@ function NotificationBell({ unreadCount, onNavigate }) {
|
|
|
6609
7703
|
] });
|
|
6610
7704
|
}
|
|
6611
7705
|
|
|
6612
|
-
export { AbsoluteScheduleForm, ActivityFeedWidget, ApiKeyDisplayModal, ApiKeyList, ApiKeySettings, Breadcrumbs, CrashErrorFallback, CreateApiKeyModal, CreateScheduleModal, CrmOverview, CrmSidebar, CrmSidebarMiddle, CrmSidebarTop, DEAL_STAGES, DEFAULT_KANBAN_CONFIG, DealDetailPage, DealDrawer, DealKanbanCard, DealsListPage, DeleteScheduleModal, DeploymentDetailModal, DeploymentList, DeploymentSettings, DeploymentStatusBadge, DocTreeNav, EditApiKeyModal, ErrorReportCard, HealthStatusCard, KanbanBoard, KnowledgeBasePage, LEAD_GEN_ROUTE_LINKS, LIST_TEMPLATE_OPTIONS, LeadGenCompaniesPage, LeadGenContactsPage, LeadGenDeliverabilityPage, LeadGenListDetailPage, LeadGenListsPage, LeadGenOverviewPage, LeadGenRouteShell, LeadGenSidebar, LeadGenSidebarMiddle, LeadGenSidebarTop, MdxRenderer, MetricsStrip, MilestoneTimeline, MyTasksPanel, NotificationBell, NotificationPanel, PIPELINE_FUNNEL_ORDER, PipelineFunnelWidget, ProjectsListPage, ProjectsSidebar, ProjectsSidebarMiddle, ProjectsSidebarTop, QuickCreateActions, RecurringScheduleForm, RelativeScheduleForm, RichTextEditor, SAVED_VIEW_PRESETS, SEOSidebar, SEOSidebarMiddle, SEOSidebarTop, SavedViewsPanel, ScheduleCard, ScheduleDetailModal, ScheduleTypeSelector, SortableHeader, TableSelectionToolbar, TaskCard, TaskScheduler, TasksDueWidget, buildErrorReport, buildListConfig, calculateProgress, crmManifest, deliveryManifest, formatStatusLabel, getEnrichmentColor, getStatusColor3 as getStatusColor, leadGenManifest, mdxComponents, milestoneStatusColors, noteTypeColors, projectStatusColors, seoManifest, taskStatusColors, taskTypeColors, useCrmPipelineSummary, useCrmQuickMetrics, useDeleteLists, useRecentCrmActivity };
|
|
7706
|
+
export { AbsoluteScheduleForm, ActivityFeedWidget, ApiKeyDisplayModal, ApiKeyList, ApiKeySettings, Breadcrumbs, CommandViewEdge, CommandViewGraph, CommandViewNode, CrashErrorFallback, CreateApiKeyModal, CreateScheduleModal, CrmOverview, CrmSidebar, CrmSidebarMiddle, CrmSidebarTop, DEAL_STAGES, DEFAULT_KANBAN_CONFIG, DealDetailPage, DealDrawer, DealKanbanCard, DealsListPage, DeleteScheduleModal, DeploymentDetailModal, DeploymentList, DeploymentSettings, DeploymentStatusBadge, DocTreeNav, EditApiKeyModal, ErrorReportCard, HealthStatusCard, KanbanBoard, KnowledgeBasePage, LEAD_GEN_ROUTE_LINKS, LIST_TEMPLATE_OPTIONS, LeadGenCompaniesPage, LeadGenContactsPage, LeadGenDeliverabilityPage, LeadGenListDetailPage, LeadGenListsPage, LeadGenOverviewPage, LeadGenRouteShell, LeadGenSidebar, LeadGenSidebarMiddle, LeadGenSidebarTop, MdxRenderer, MetricsStrip, MilestoneTimeline, MyTasksPanel, NotificationBell, NotificationPanel, PIPELINE_FUNNEL_ORDER, PipelineFunnelWidget, ProjectDetailPage, ProjectsListPage, ProjectsSidebar, ProjectsSidebarMiddle, ProjectsSidebarTop, QuickCreateActions, RecurringScheduleForm, RelativeScheduleForm, RichTextEditor, SAVED_VIEW_PRESETS, SEOSidebar, SEOSidebarMiddle, SEOSidebarTop, SavedViewsPanel, ScheduleCard, ScheduleDetailModal, ScheduleTypeSelector, SortableHeader, TableSelectionToolbar, TaskCard, TaskScheduler, TasksDueWidget, buildErrorReport, buildListConfig, calculateProgress, crmManifest, deliveryManifest, formatStatusLabel, getEnrichmentColor, getStatusColor3 as getStatusColor, leadGenManifest, mdxComponents, milestoneStatusColors, noteTypeColors, projectStatusColors, seoManifest, taskStatusColors, taskTypeColors, useCrmPipelineSummary, useCrmQuickMetrics, useDeleteLists, useRecentCrmActivity };
|