@elevasis/ui 1.26.0 → 1.27.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/charts/index.js +2 -2
- package/dist/{chunk-AWT255UH.js → chunk-2IJCM3VQ.js} +37 -37
- package/dist/chunk-5COLSYBE.js +199 -0
- package/dist/{chunk-RMPXGBNI.js → chunk-5JSR6TL5.js} +2 -2
- package/dist/chunk-BAGYETKM.js +635 -0
- package/dist/{chunk-L3GVDMCA.js → chunk-C27LLJM6.js} +3 -195
- package/dist/{chunk-O4UB5DQQ.js → chunk-F2J7675J.js} +1 -1
- package/dist/chunk-ITCEULI5.js +238 -0
- package/dist/{chunk-4WKWLFBZ.js → chunk-P5EWG45B.js} +1 -1
- package/dist/{chunk-BS4J2LAW.js → chunk-QTD5HPKD.js} +1 -1
- package/dist/{chunk-ZVJKIJFG.js → chunk-RCQPWA5X.js} +13 -42
- package/dist/chunk-TLAIQC7B.js +6382 -0
- package/dist/{chunk-FEZZ3IDU.js → chunk-TXPUIHX2.js} +10 -10
- package/dist/{chunk-L4XXM55J.js → chunk-W4VYXIN7.js} +142 -3
- package/dist/chunk-WJ7W7JU4.js +2115 -0
- package/dist/{chunk-YNGQ7U5H.js → chunk-WLNEJ6JJ.js} +2 -2
- package/dist/{chunk-4INR75ZS.js → chunk-Y2SYGFRF.js} +589 -65
- package/dist/components/index.d.ts +333 -73
- package/dist/components/index.js +838 -686
- package/dist/features/auth/index.d.ts +125 -0
- package/dist/features/auth/index.js +2 -2
- package/dist/features/dashboard/index.d.ts +28 -2
- package/dist/features/dashboard/index.js +21 -635
- package/dist/features/monitoring/index.d.ts +28 -1
- package/dist/features/monitoring/index.js +19 -529
- package/dist/features/operations/index.d.ts +51 -8
- package/dist/features/operations/index.js +25 -3760
- package/dist/features/settings/index.d.ts +153 -1
- package/dist/features/settings/index.js +19 -1438
- package/dist/hooks/index.d.ts +262 -25
- package/dist/hooks/index.js +12 -8
- package/dist/hooks/published.d.ts +137 -25
- package/dist/hooks/published.js +11 -7
- package/dist/index.d.ts +310 -28
- package/dist/index.js +12 -11
- package/dist/initialization/index.d.ts +125 -0
- package/dist/layout/index.d.ts +2 -0
- package/dist/layout/index.js +6 -5
- package/dist/organization/index.js +1 -2
- package/dist/profile/index.d.ts +125 -0
- package/dist/provider/index.d.ts +48 -3
- package/dist/provider/index.js +10 -4
- package/dist/provider/published.d.ts +48 -3
- package/dist/provider/published.js +8 -2
- package/dist/supabase/index.d.ts +242 -0
- package/dist/theme/index.js +2 -2
- package/dist/types/index.d.ts +126 -1
- package/package.json +1 -1
- package/dist/chunk-LR4WVA7W.js +0 -682
- package/dist/chunk-R7WLWGPO.js +0 -126
- package/dist/chunk-TCKIAHDC.js +0 -2626
- package/dist/chunk-V7XHGJQZ.js +0 -145
- package/dist/{chunk-WWEMNIHW.js → chunk-YYBM5LNJ.js} +1 -1
package/dist/components/index.js
CHANGED
|
@@ -1,43 +1,46 @@
|
|
|
1
1
|
import { useBreadcrumbs } from '../chunk-MG3NF7QL.js';
|
|
2
|
-
import
|
|
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 } from '../chunk-
|
|
5
|
-
export { CreateCredentialModal, CredentialList, CredentialSettings, MembershipFeaturePanel, MembershipStatusBadge, OAuthConnectModal, OrganizationMembershipsList, WebhookUrlDisplayModal } from '../chunk-
|
|
2
|
+
import '../chunk-C27LLJM6.js';
|
|
3
|
+
import { NotificationList } from '../chunk-Y2SYGFRF.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-Y2SYGFRF.js';
|
|
5
|
+
export { CreateCredentialModal, CredentialList, CredentialSettings, MembershipFeaturePanel, MembershipStatusBadge, OAuthConnectModal, OrganizationMembershipsList, WebhookUrlDisplayModal, settingsManifest } from '../chunk-WJ7W7JU4.js';
|
|
6
6
|
import { FilterBar } from '../chunk-PDHTXPSF.js';
|
|
7
7
|
export { FilterBar } from '../chunk-PDHTXPSF.js';
|
|
8
|
+
export { dashboardManifest } from '../chunk-BAGYETKM.js';
|
|
8
9
|
export { ResourceHealthChart, getHealthColor } from '../chunk-LGKLC5MG.js';
|
|
9
|
-
export { ActionModal, AgentDefinitionDisplay, AgentExecutionLogs, BaseExecutionLogs, BaseExecutionLogsHeader, BaseExecutionLogsStates, CheckpointGroup, CollapsibleJsonSection, CommandQueueSidebar, CommandQueueSidebarMiddle, CommandQueueSidebarTop, CommandQueueTaskRow, CommandViewEdge, CommandViewGraph, CommandViewNode, ConfigCard, ContentSections, ContextUsageBadge, ContractDisplay, ExecutionErrorSection, FormFieldRenderer, LogEntry, LogGroup, NewKnowledgeMapEdge, NewKnowledgeMapGraph, NewKnowledgeMapNode, ResourceDefinitionSection, ResourceErrorState, ResourceFilter, ResourceHeader, ResourceNotFoundState, SessionMemory, ToolsListDisplay, WorkflowDefinitionDisplay, WorkflowExecutionLogs, getExecutionStatusConfig, getIcon, getLogLevelConfig, iconMap, useNewKnowledgeMapLayout } from '../chunk-
|
|
10
|
-
import
|
|
11
|
-
|
|
10
|
+
export { ActionModal, AgentDefinitionDisplay, AgentExecutionLogs, BaseExecutionLogs, BaseExecutionLogsHeader, BaseExecutionLogsStates, CheckpointGroup, CollapsibleJsonSection, CommandQueueSidebar, CommandQueueSidebarMiddle, CommandQueueSidebarTop, CommandQueueTaskRow, CommandViewEdge, CommandViewGraph, CommandViewNode, ConfigCard, ContentSections, ContextUsageBadge, ContractDisplay, ExecutionErrorSection, FormFieldRenderer, LogEntry, LogGroup, NewKnowledgeMapEdge, NewKnowledgeMapGraph, NewKnowledgeMapNode, ResourceDefinitionSection, ResourceErrorState, ResourceFilter, ResourceHeader, ResourceNotFoundState, SessionMemory, ToolsListDisplay, WorkflowDefinitionDisplay, WorkflowExecutionLogs, getExecutionStatusConfig, getIcon, getLogLevelConfig, iconMap, operationsManifest, useNewKnowledgeMapLayout } from '../chunk-TLAIQC7B.js';
|
|
11
|
+
import '../chunk-ROSMICXG.js';
|
|
12
|
+
import { SubshellLoader, PageContainer, SubshellSidebarSection, SidebarListItem, CollapsibleSidebarGroup } from '../chunk-2IJCM3VQ.js';
|
|
13
|
+
export { ResourceHealthPanel } from '../chunk-P5EWG45B.js';
|
|
12
14
|
import { CustomModal } from '../chunk-GBMNCNHX.js';
|
|
13
15
|
export { ConfirmationInputModal, ConfirmationModal, CustomModal } from '../chunk-GBMNCNHX.js';
|
|
14
|
-
export { AgentExecutionTimeline, AgentExecutionVisualizer, AgentIterationEdge, AgentIterationNode, BaseEdge, BaseNode, EmptyVisualizer, ExecutionStats, ExecutionStatusBadge, GraphBackground, GraphContainer, GraphFitViewButton, GraphFitViewHandler, GraphLegend, TimelineAxis, TimelineBar, TimelineContainer, TimelineRow, UnifiedWorkflowEdge, UnifiedWorkflowGraph, UnifiedWorkflowNode, VisualizerContainer, WorkflowExecutionTimeline, getGraphBackgroundStyles, useGraphBackgroundStyles, useGraphTheme } from '../chunk-
|
|
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, getGraphBackgroundStyles, useGraphBackgroundStyles, useGraphTheme } from '../chunk-F2J7675J.js';
|
|
15
17
|
import '../chunk-JHVKGZ2P.js';
|
|
16
18
|
import { ListSkeleton, EmptyState, PageTitleCaption, StatCard, CenteredErrorState, CardHeader, ActivityTimeline, StatusBadge } from '../chunk-MCA6LOGM.js';
|
|
17
19
|
export { APIErrorAlert, ActivityTimeline, CardHeader, CenteredErrorState, CollapsibleSection, ContextViewer, CustomSelector, DetailCardSkeleton, EmptyState, GlowDot, JsonViewer, ListSkeleton, PageNotFound, PageTitleCaption, ResourceCard, StatCard, StatCardSkeleton, StatsCardSkeleton, StatusBadge, TabCountBadge, TimeRangeSelector, TrendIndicator, catalogItemToResourceDefinition } from '../chunk-MCA6LOGM.js';
|
|
18
20
|
export { StyledMarkdown } from '../chunk-3KMDHCAR.js';
|
|
19
21
|
export { NavigationButton } from '../chunk-NNKKBSJN.js';
|
|
20
|
-
import { AppShellLoader } from '../chunk-
|
|
21
|
-
import '../chunk-
|
|
22
|
-
import { useUpdateApiKey, useDeleteApiKey, useCreateApiKey, useListApiKeys, useActivateDeployment, useDeactivateDeployment, useDeleteDeployment, useListDeployments, useTasks, useProjects, useMilestones } from '../chunk-
|
|
23
|
-
import { usePaginationState, useDeploymentDocs, useResources, useCreateSchedule, useListSchedules, usePauseSchedule, useResumeSchedule, useCancelSchedule, useDeleteSchedule, useDealNotes, useCreateDealNote, useDeals, useSyncDealStage, dealKeys, useDealTasksDue, useCreateDealTask, useMarkAllAsRead, useNotifications, showErrorNotification, showSuccessNotification, showApiErrorNotification } from '../chunk-
|
|
24
|
-
export { showApiErrorNotification, showErrorNotification, showInfoNotification, showSuccessNotification, showWarningNotification } from '../chunk-
|
|
22
|
+
import { AppShellLoader } from '../chunk-YYBM5LNJ.js';
|
|
23
|
+
import '../chunk-QTD5HPKD.js';
|
|
24
|
+
import { useUpdateApiKey, useDeleteApiKey, useCreateApiKey, useListApiKeys, useActivateDeployment, useDeactivateDeployment, useDeleteDeployment, useListDeployments, useTasks, useProjects, useMilestones } from '../chunk-WLNEJ6JJ.js';
|
|
25
|
+
import { usePaginationState, useDeploymentDocs, useResources, useCreateSchedule, useListSchedules, usePauseSchedule, useResumeSchedule, useCancelSchedule, useDeleteSchedule, useDealNotes, useCreateDealNote, useDeals, useSyncDealStage, dealKeys, useDealTasksDue, useCreateDealTask, useMarkAllAsRead, useNotifications, showErrorNotification, showSuccessNotification, showApiErrorNotification } from '../chunk-RCQPWA5X.js';
|
|
26
|
+
export { showApiErrorNotification, showErrorNotification, showInfoNotification, showSuccessNotification, showWarningNotification } from '../chunk-RCQPWA5X.js';
|
|
25
27
|
import '../chunk-LXHZYSMQ.js';
|
|
26
|
-
import '../chunk-MHW43EOH.js';
|
|
27
28
|
export { Graph_module_css_default as graphStyles } from '../chunk-F6RBK7NJ.js';
|
|
28
29
|
export { CONTAINER_CONSTANTS, SHARED_VIZ_CONSTANTS } from '../chunk-XA34RETF.js';
|
|
29
30
|
import '../chunk-ELJIFLCB.js';
|
|
30
|
-
import '../chunk-
|
|
31
|
-
import '../chunk-SLVC5OJ2.js';
|
|
32
|
-
import '../chunk-RNP5R5I3.js';
|
|
33
|
-
import '../chunk-RMPXGBNI.js';
|
|
31
|
+
import '../chunk-5JSR6TL5.js';
|
|
34
32
|
export { ElevasisLoader } from '../chunk-SZHARWKU.js';
|
|
35
|
-
import '../chunk-
|
|
33
|
+
import '../chunk-TXPUIHX2.js';
|
|
36
34
|
import '../chunk-CYXZHBP4.js';
|
|
37
|
-
import '../chunk-
|
|
35
|
+
import '../chunk-ITCEULI5.js';
|
|
36
|
+
import { SubshellContainer, SubshellSidebar, SubshellRightSideContainer, SubshellContentContainer } from '../chunk-5COLSYBE.js';
|
|
38
37
|
import '../chunk-NVOCKXUQ.js';
|
|
39
|
-
import '../chunk-
|
|
38
|
+
import '../chunk-MHW43EOH.js';
|
|
39
|
+
import '../chunk-W4VYXIN7.js';
|
|
40
40
|
import { useAppearance } from '../chunk-QJ2KCHKX.js';
|
|
41
|
+
import '../chunk-QJ2S46NI.js';
|
|
42
|
+
import '../chunk-SLVC5OJ2.js';
|
|
43
|
+
import '../chunk-RNP5R5I3.js';
|
|
41
44
|
import { formatDateTime, PAGE_SIZE_DEFAULT, formatTimeAgo } from '../chunk-IOKL7BKE.js';
|
|
42
45
|
import '../chunk-RWQIFKMJ.js';
|
|
43
46
|
import '../chunk-ALA56RGZ.js';
|
|
@@ -47,7 +50,7 @@ import { useElevasisServices } from '../chunk-QEPXAWE2.js';
|
|
|
47
50
|
import '../chunk-BRJ3QZ4E.js';
|
|
48
51
|
import { useRouterContext } from '../chunk-Q7DJKLEN.js';
|
|
49
52
|
import { Table, Group, Text, Button, Stack, Title, TextInput, Alert, Tooltip, ActionIcon, Paper, Code, CopyButton, SimpleGrid, Badge, Loader, Pagination, useMantineTheme, Box, ScrollArea, Select, Center, Card, SegmentedControl, Switch, Textarea, Divider, Menu, Timeline, ThemeIcon, Tabs, Anchor, Breadcrumbs as Breadcrumbs$1, Drawer, UnstyledButton, Modal, RingProgress, Collapse, Popover, Indicator } from '@mantine/core';
|
|
50
|
-
import { IconChevronUp, IconChevronDown, IconSelector, IconTrash, IconPencil, IconAlertCircle, IconKey, IconCalendar, IconClock, IconAlertTriangle, IconExclamationMark, IconShieldLock, IconCheck, IconCopy, IconPlus, IconRocket, IconRefresh, IconPower, IconPlayerPlay, IconCircleCheck, IconTag, IconBook2, IconFileOff, IconList, IconCalendarRepeat, IconCalendarEvent, IconCalendarTime, IconRobot, IconGitBranch, IconSettings, IconExternalLink, IconDotsVertical, IconPlayerPause, IconPlayerStop, IconCalendarDue, IconCalendarStats, IconCalendarOff, IconListCheck, IconColumns, IconChecklist, IconHistory,
|
|
53
|
+
import { IconBriefcase, IconChevronUp, IconChevronDown, IconSelector, IconTrash, IconPencil, IconAlertCircle, IconKey, IconCalendar, IconClock, IconAlertTriangle, IconExclamationMark, IconShieldLock, IconCheck, IconCopy, IconPlus, IconRocket, IconRefresh, IconPower, IconPlayerPlay, IconCircleCheck, IconTag, IconBook2, IconFileOff, IconList, IconCalendarRepeat, IconCalendarEvent, IconCalendarTime, IconRobot, IconGitBranch, IconSettings, IconExternalLink, IconDotsVertical, IconPlayerPause, IconPlayerStop, IconCalendarDue, IconCalendarStats, IconCalendarOff, IconListCheck, IconAddressBook, IconTrophy, IconClockExclamation, IconUser, IconLayoutGrid, IconColumns, IconFileInvoice, IconChecklist, IconHistory, IconTarget, IconListDetails, IconBuilding, IconMailCheck, IconSearch, IconChartBar, IconTrendingUp, IconHeartbeat, IconFlag, IconFileText, IconInbox, IconLock, IconChevronRight, IconDownload, IconMessageCircle, IconBell, IconNotes, IconFolderOpen, IconFolder, IconCheckbox, IconMail, IconPhone, IconArrowRight, IconNote } from '@tabler/icons-react';
|
|
51
54
|
import * as runtime from 'react/jsx-runtime';
|
|
52
55
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
53
56
|
import { useDisclosure } from '@mantine/hooks';
|
|
@@ -3044,7 +3047,7 @@ var Breadcrumbs = ({ navItems }) => {
|
|
|
3044
3047
|
});
|
|
3045
3048
|
return /* @__PURE__ */ jsx(Breadcrumbs$1, { separator: "/", children: items });
|
|
3046
3049
|
};
|
|
3047
|
-
function DealKanbanCard({ deal, config
|
|
3050
|
+
function DealKanbanCard({ deal, config, onClick, onDragStart, onDragEnd }) {
|
|
3048
3051
|
const contactFirstName = deal.contact?.first_name || "";
|
|
3049
3052
|
const contactLastName = deal.contact?.last_name || "";
|
|
3050
3053
|
const contactName = [contactFirstName, contactLastName].filter(Boolean).join(" ");
|
|
@@ -3052,15 +3055,18 @@ function DealKanbanCard({ deal, config: _config, onClick, onDragStart, onDragEnd
|
|
|
3052
3055
|
const companyName = deal.contact?.company?.name || deal.discovery_data?.company || deal.contact_email?.split("@")[1] || null;
|
|
3053
3056
|
const hasInitialFee = typeof deal.initial_fee === "number" && deal.initial_fee > 0;
|
|
3054
3057
|
const hasMonthlyFee = typeof deal.monthly_fee === "number" && deal.monthly_fee > 0;
|
|
3058
|
+
const stageColor = (deal.cached_stage ? config[deal.cached_stage]?.color : null) ?? "gray";
|
|
3055
3059
|
return /* @__PURE__ */ jsx(
|
|
3056
|
-
|
|
3060
|
+
Paper,
|
|
3057
3061
|
{
|
|
3058
|
-
|
|
3062
|
+
p: "sm",
|
|
3059
3063
|
style: {
|
|
3064
|
+
border: "1px solid var(--color-border)",
|
|
3065
|
+
borderLeft: `3px solid var(--mantine-color-${stageColor}-6)`,
|
|
3060
3066
|
cursor: "grab",
|
|
3061
|
-
userSelect: "none"
|
|
3067
|
+
userSelect: "none",
|
|
3068
|
+
transition: `all var(--duration-fast) var(--easing)`
|
|
3062
3069
|
},
|
|
3063
|
-
withBorder: true,
|
|
3064
3070
|
draggable: true,
|
|
3065
3071
|
onDragStart: (e) => onDragStart(e, deal),
|
|
3066
3072
|
onDragEnd,
|
|
@@ -3086,37 +3092,14 @@ function DealKanbanCard({ deal, config: _config, onClick, onDragStart, onDragEnd
|
|
|
3086
3092
|
}
|
|
3087
3093
|
|
|
3088
3094
|
// src/components/acquisition/kanban/constants.ts
|
|
3089
|
-
var DEAL_STAGES = [
|
|
3090
|
-
"interested",
|
|
3091
|
-
"booked",
|
|
3092
|
-
"qualified",
|
|
3093
|
-
"demo_booked",
|
|
3094
|
-
"proposal",
|
|
3095
|
-
"proposal_sent",
|
|
3096
|
-
"proposal_signed",
|
|
3097
|
-
"payment_sent",
|
|
3098
|
-
"proposal_revision",
|
|
3099
|
-
"closed_won",
|
|
3100
|
-
"closed_lost",
|
|
3101
|
-
"nurturing",
|
|
3102
|
-
"cancelled",
|
|
3103
|
-
"no_show"
|
|
3104
|
-
];
|
|
3095
|
+
var DEAL_STAGES = ["interested", "proposal", "closing", "closed_won", "closed_lost", "nurturing"];
|
|
3105
3096
|
var DEFAULT_KANBAN_CONFIG = {
|
|
3106
3097
|
interested: { color: "blue" },
|
|
3107
|
-
booked: { color: "cyan" },
|
|
3108
|
-
qualified: { color: "teal" },
|
|
3109
|
-
demo_booked: { color: "indigo" },
|
|
3110
3098
|
proposal: { color: "yellow" },
|
|
3111
|
-
|
|
3112
|
-
proposal_signed: { color: "lime" },
|
|
3113
|
-
payment_sent: { color: "yellow" },
|
|
3114
|
-
proposal_revision: { color: "pink" },
|
|
3099
|
+
closing: { color: "lime" },
|
|
3115
3100
|
closed_won: { color: "green" },
|
|
3116
3101
|
closed_lost: { color: "red" },
|
|
3117
|
-
nurturing: { color: "grape" }
|
|
3118
|
-
cancelled: { color: "gray" },
|
|
3119
|
-
no_show: { color: "gray" }
|
|
3102
|
+
nurturing: { color: "grape" }
|
|
3120
3103
|
};
|
|
3121
3104
|
function formatStageLabel(stage) {
|
|
3122
3105
|
if (!stage) return "Unstaged";
|
|
@@ -3326,49 +3309,73 @@ function KanbanBoard({ config = DEFAULT_KANBAN_CONFIG, renderDrawerActions }) {
|
|
|
3326
3309
|
return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsx(Alert, { color: "red", children: "Failed to load deals" }) });
|
|
3327
3310
|
}
|
|
3328
3311
|
return /* @__PURE__ */ jsxs(SubshellContentContainer, { children: [
|
|
3329
|
-
/* @__PURE__ */ jsxs(Stack, {
|
|
3312
|
+
/* @__PURE__ */ jsxs(Stack, { children: [
|
|
3330
3313
|
/* @__PURE__ */ jsx(PageTitleCaption, { title: "Pipeline", caption: "Kanban view of your deal pipeline" }),
|
|
3331
|
-
/* @__PURE__ */ jsx(
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
{
|
|
3339
|
-
withBorder: true,
|
|
3340
|
-
style: {
|
|
3341
|
-
width: 320,
|
|
3342
|
-
minWidth: 320,
|
|
3343
|
-
backgroundColor: isDragTarget ? "color-mix(in srgb, var(--color-primary) 8%, var(--glass-background))" : "var(--glass-background)",
|
|
3344
|
-
border: isDragTarget ? "1px solid var(--color-primary)" : "1px solid var(--color-border)",
|
|
3345
|
-
transition: "background-color var(--duration-fast) var(--easing), border-color var(--duration-fast) var(--easing)"
|
|
3346
|
-
},
|
|
3347
|
-
onDragOver: (e) => handleDragOver(e, columnKey),
|
|
3348
|
-
onDragLeave: handleDragLeave,
|
|
3349
|
-
onDrop: (e) => handleDrop(e, columnKey),
|
|
3350
|
-
children: /* @__PURE__ */ jsxs(Stack, { gap: "sm", p: "xs", children: [
|
|
3351
|
-
/* @__PURE__ */ jsxs(Group, { justify: "space-between", wrap: "nowrap", children: [
|
|
3352
|
-
/* @__PURE__ */ jsx(Text, { size: "sm", fw: 600, tt: "uppercase", lineClamp: 1, children: formatStageLabel2(columnKey) }),
|
|
3353
|
-
/* @__PURE__ */ jsx(Badge, { variant: "light", color: badgeColor, size: "sm", style: { flexShrink: 0 }, children: columnDeals.length })
|
|
3354
|
-
] }),
|
|
3355
|
-
columnDeals.length === 0 && /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", ta: "center", py: "md", style: { opacity: 0.5 }, children: isUnstaged ? "No unstaged deals" : "Drop here" }),
|
|
3356
|
-
/* @__PURE__ */ jsx(Stack, { gap: "xs", children: columnDeals.map((deal) => /* @__PURE__ */ jsx(
|
|
3357
|
-
DealKanbanCard,
|
|
3358
|
-
{
|
|
3359
|
-
deal,
|
|
3360
|
-
config,
|
|
3361
|
-
onClick: handleCardClick,
|
|
3362
|
-
onDragStart: handleDragStart,
|
|
3363
|
-
onDragEnd: handleDragEnd
|
|
3364
|
-
},
|
|
3365
|
-
deal.id
|
|
3366
|
-
)) })
|
|
3367
|
-
] })
|
|
3314
|
+
/* @__PURE__ */ jsx(
|
|
3315
|
+
Paper,
|
|
3316
|
+
{
|
|
3317
|
+
p: "lg",
|
|
3318
|
+
style: {
|
|
3319
|
+
border: "1px solid var(--color-border)",
|
|
3320
|
+
overflow: "hidden"
|
|
3368
3321
|
},
|
|
3369
|
-
columnKey
|
|
3370
|
-
|
|
3371
|
-
|
|
3322
|
+
children: /* @__PURE__ */ jsx(ScrollArea, { scrollbarSize: 8, type: "auto", children: /* @__PURE__ */ jsx(Group, { align: "flex-start", wrap: "nowrap", gap: "sm", style: { paddingBottom: 16, minWidth: "max-content" }, children: columns.map((columnKey) => {
|
|
3323
|
+
const columnDeals = groupedDeals.get(columnKey) ?? [];
|
|
3324
|
+
const isUnstaged = columnKey === UNSTAGED_KEY;
|
|
3325
|
+
const isDragTarget = dragOverColumn === columnKey;
|
|
3326
|
+
const badgeColor = isUnstaged ? "gray" : config[columnKey]?.color ?? "gray";
|
|
3327
|
+
return /* @__PURE__ */ jsx(
|
|
3328
|
+
Box,
|
|
3329
|
+
{
|
|
3330
|
+
style: {
|
|
3331
|
+
width: 260,
|
|
3332
|
+
minWidth: 260,
|
|
3333
|
+
borderRadius: 8,
|
|
3334
|
+
padding: "10px 8px",
|
|
3335
|
+
backgroundColor: isDragTarget ? "color-mix(in srgb, var(--color-primary) 6%, transparent)" : "transparent",
|
|
3336
|
+
border: isDragTarget ? "1px solid var(--color-primary)" : "1px solid transparent",
|
|
3337
|
+
transition: `background-color var(--duration-fast) var(--easing), border-color var(--duration-fast) var(--easing)`
|
|
3338
|
+
},
|
|
3339
|
+
onDragOver: (e) => handleDragOver(e, columnKey),
|
|
3340
|
+
onDragLeave: handleDragLeave,
|
|
3341
|
+
onDrop: (e) => handleDrop(e, columnKey),
|
|
3342
|
+
children: /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
|
|
3343
|
+
/* @__PURE__ */ jsx(Group, { justify: "space-between", wrap: "nowrap", children: /* @__PURE__ */ jsxs(Group, { gap: 6, children: [
|
|
3344
|
+
/* @__PURE__ */ jsx(
|
|
3345
|
+
Box,
|
|
3346
|
+
{
|
|
3347
|
+
w: 10,
|
|
3348
|
+
h: 10,
|
|
3349
|
+
style: {
|
|
3350
|
+
borderRadius: "50%",
|
|
3351
|
+
background: `var(--mantine-color-${badgeColor}-6)`,
|
|
3352
|
+
flexShrink: 0
|
|
3353
|
+
}
|
|
3354
|
+
}
|
|
3355
|
+
),
|
|
3356
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", fw: 600, lineClamp: 1, children: formatStageLabel2(columnKey) }),
|
|
3357
|
+
/* @__PURE__ */ jsx(Badge, { size: "xs", variant: "light", color: badgeColor, children: columnDeals.length })
|
|
3358
|
+
] }) }),
|
|
3359
|
+
/* @__PURE__ */ jsx(Divider, {}),
|
|
3360
|
+
columnDeals.length === 0 && /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", ta: "center", py: "md", style: { opacity: 0.5 }, children: isUnstaged ? "No unstaged deals" : "Drop here" }),
|
|
3361
|
+
/* @__PURE__ */ jsx(Stack, { gap: "xs", children: columnDeals.map((deal) => /* @__PURE__ */ jsx(
|
|
3362
|
+
DealKanbanCard,
|
|
3363
|
+
{
|
|
3364
|
+
deal,
|
|
3365
|
+
config,
|
|
3366
|
+
onClick: handleCardClick,
|
|
3367
|
+
onDragStart: handleDragStart,
|
|
3368
|
+
onDragEnd: handleDragEnd
|
|
3369
|
+
},
|
|
3370
|
+
deal.id
|
|
3371
|
+
)) })
|
|
3372
|
+
] })
|
|
3373
|
+
},
|
|
3374
|
+
columnKey
|
|
3375
|
+
);
|
|
3376
|
+
}) }) })
|
|
3377
|
+
}
|
|
3378
|
+
)
|
|
3372
3379
|
] }),
|
|
3373
3380
|
/* @__PURE__ */ jsx(
|
|
3374
3381
|
DealDrawer,
|
|
@@ -3381,153 +3388,464 @@ function KanbanBoard({ config = DEFAULT_KANBAN_CONFIG, renderDrawerActions }) {
|
|
|
3381
3388
|
)
|
|
3382
3389
|
] });
|
|
3383
3390
|
}
|
|
3384
|
-
var
|
|
3385
|
-
"
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
const
|
|
3412
|
-
|
|
3413
|
-
const entry = stageMap.get(stage);
|
|
3414
|
-
entry.count += 1;
|
|
3415
|
-
entry.totalValue += getValue(deal);
|
|
3391
|
+
var CrmSidebarTop = () => {
|
|
3392
|
+
return /* @__PURE__ */ jsx(SubshellSidebarSection, { icon: IconAddressBook, label: "CRM" });
|
|
3393
|
+
};
|
|
3394
|
+
|
|
3395
|
+
// src/features/crm/workbench/constants.ts
|
|
3396
|
+
var SAVED_VIEW_PRESETS = [
|
|
3397
|
+
{
|
|
3398
|
+
id: "my-deals",
|
|
3399
|
+
label: "My Deals",
|
|
3400
|
+
iconName: "IconUser",
|
|
3401
|
+
target: "/crm/deals"
|
|
3402
|
+
},
|
|
3403
|
+
{
|
|
3404
|
+
id: "overdue",
|
|
3405
|
+
label: "Overdue",
|
|
3406
|
+
iconName: "IconClockExclamation",
|
|
3407
|
+
target: "/crm/deals"
|
|
3408
|
+
},
|
|
3409
|
+
{
|
|
3410
|
+
id: "won-this-month",
|
|
3411
|
+
label: "Won this month",
|
|
3412
|
+
iconName: "IconTrophy",
|
|
3413
|
+
target: "/crm/deals",
|
|
3414
|
+
urlFilters: { stage: "closed_won" },
|
|
3415
|
+
predicate: (deal) => {
|
|
3416
|
+
if (deal.cached_stage !== "closed_won") return false;
|
|
3417
|
+
const updated = new Date(deal.updated_at);
|
|
3418
|
+
const now = /* @__PURE__ */ new Date();
|
|
3419
|
+
return updated.getMonth() === now.getMonth() && updated.getFullYear() === now.getFullYear();
|
|
3416
3420
|
}
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
var CLOSED_STAGES = ["closed_won", "closed_lost", "cancelled"];
|
|
3425
|
-
var OPEN_EXCLUDED = CLOSED_STAGES;
|
|
3426
|
-
var ZERO_METRICS = {
|
|
3427
|
-
totalDeals: 0,
|
|
3428
|
-
openDeals: 0,
|
|
3429
|
-
wonDeals: 0,
|
|
3430
|
-
winRate: 0,
|
|
3431
|
-
avgDealSize: 0,
|
|
3432
|
-
totalPipelineValue: 0
|
|
3421
|
+
}
|
|
3422
|
+
];
|
|
3423
|
+
var KIND_ICONS = {
|
|
3424
|
+
call: IconPhone,
|
|
3425
|
+
email: IconMail,
|
|
3426
|
+
meeting: IconCalendar,
|
|
3427
|
+
other: IconCheckbox
|
|
3433
3428
|
};
|
|
3434
|
-
function
|
|
3435
|
-
|
|
3436
|
-
const
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3429
|
+
function formatDueLabel(dueAt) {
|
|
3430
|
+
if (!dueAt) return "";
|
|
3431
|
+
const date = new Date(dueAt);
|
|
3432
|
+
const now = /* @__PURE__ */ new Date();
|
|
3433
|
+
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
|
3434
|
+
const dueDay = new Date(date.getFullYear(), date.getMonth(), date.getDate());
|
|
3435
|
+
const diffDays = Math.round((dueDay.getTime() - today.getTime()) / 864e5);
|
|
3436
|
+
if (diffDays < 0) return `${Math.abs(diffDays)}d overdue`;
|
|
3437
|
+
if (diffDays === 0) {
|
|
3438
|
+
return date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
|
|
3439
|
+
}
|
|
3440
|
+
return `in ${diffDays}d`;
|
|
3441
|
+
}
|
|
3442
|
+
function TaskRow({ task, onClick }) {
|
|
3443
|
+
const KindIcon2 = KIND_ICONS[task.kind];
|
|
3444
|
+
const dueLabel = formatDueLabel(task.dueAt);
|
|
3445
|
+
const isOverdue = task.dueAt !== null && new Date(task.dueAt) < /* @__PURE__ */ new Date();
|
|
3446
|
+
return /* @__PURE__ */ jsx(
|
|
3447
|
+
UnstyledButton,
|
|
3448
|
+
{
|
|
3449
|
+
onClick,
|
|
3450
|
+
style: {
|
|
3451
|
+
display: "block",
|
|
3452
|
+
width: "100%",
|
|
3453
|
+
padding: "4px 6px",
|
|
3454
|
+
borderRadius: "var(--mantine-radius-sm)",
|
|
3455
|
+
transition: `background-color var(--duration-fast) var(--easing)`
|
|
3456
|
+
},
|
|
3457
|
+
onMouseEnter: (e) => {
|
|
3458
|
+
e.currentTarget.style.backgroundColor = "var(--color-surface-hover)";
|
|
3459
|
+
},
|
|
3460
|
+
onMouseLeave: (e) => {
|
|
3461
|
+
e.currentTarget.style.backgroundColor = "transparent";
|
|
3462
|
+
},
|
|
3463
|
+
children: /* @__PURE__ */ jsxs(Group, { gap: "xs", wrap: "nowrap", children: [
|
|
3464
|
+
/* @__PURE__ */ jsx(KindIcon2, { size: 14, style: { color: "var(--color-text-dimmed)", flexShrink: 0 } }),
|
|
3465
|
+
/* @__PURE__ */ jsx(Text, { size: "xs", truncate: true, style: { flex: 1, minWidth: 0 }, children: task.title }),
|
|
3466
|
+
dueLabel && /* @__PURE__ */ jsx(Badge, { size: "xs", variant: "light", color: isOverdue ? "red" : "gray", style: { flexShrink: 0 }, children: dueLabel })
|
|
3467
|
+
] })
|
|
3464
3468
|
}
|
|
3465
|
-
|
|
3466
|
-
const winRate = winRateDenominator === 0 ? 0 : wonDeals / winRateDenominator;
|
|
3467
|
-
const avgDealSize = wonFeeCount === 0 ? 0 : wonFeeSum / wonFeeCount;
|
|
3468
|
-
return {
|
|
3469
|
-
totalDeals: dealList.length,
|
|
3470
|
-
openDeals,
|
|
3471
|
-
wonDeals,
|
|
3472
|
-
winRate,
|
|
3473
|
-
avgDealSize,
|
|
3474
|
-
totalPipelineValue: pipelineValue
|
|
3475
|
-
};
|
|
3476
|
-
}, [deals]);
|
|
3477
|
-
return { data, isLoading, error };
|
|
3469
|
+
);
|
|
3478
3470
|
}
|
|
3479
|
-
function
|
|
3480
|
-
const {
|
|
3481
|
-
const
|
|
3482
|
-
const
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
isLoading:
|
|
3490
|
-
|
|
3491
|
-
|
|
3471
|
+
function MyTasksPanel({ onTaskClick, onSeeAll }) {
|
|
3472
|
+
const { data, isLoading, isError } = useDealTasksDue({ window: "today_and_overdue" });
|
|
3473
|
+
const tasks = data ?? [];
|
|
3474
|
+
const total = tasks.length;
|
|
3475
|
+
const visible = tasks.slice(0, 5);
|
|
3476
|
+
return /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
|
|
3477
|
+
/* @__PURE__ */ jsxs(Group, { justify: "space-between", mb: 4, children: [
|
|
3478
|
+
/* @__PURE__ */ jsx(Text, { fz: "xs", tt: "uppercase", c: "dimmed", fw: 600, children: "My Tasks" }),
|
|
3479
|
+
total > 0 && /* @__PURE__ */ jsx(Badge, { size: "xs", variant: "light", children: total })
|
|
3480
|
+
] }),
|
|
3481
|
+
isLoading && /* @__PURE__ */ jsx(Center, { py: 4, children: /* @__PURE__ */ jsx(Loader, { size: "xs" }) }),
|
|
3482
|
+
isError && /* @__PURE__ */ jsx(Text, { size: "xs", c: "red", children: "Failed to load tasks" }),
|
|
3483
|
+
!isLoading && !isError && visible.length === 0 && /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "No tasks due - you're caught up." }),
|
|
3484
|
+
!isLoading && !isError && visible.map((task) => /* @__PURE__ */ jsx(TaskRow, { task, onClick: () => onTaskClick(task.dealId) }, task.id)),
|
|
3485
|
+
onSeeAll && total > 5 && /* @__PURE__ */ jsx(UnstyledButton, { onClick: onSeeAll, mt: 2, style: { display: "inline-block" }, children: /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", style: { textDecoration: "underline" }, children: [
|
|
3486
|
+
"See all (",
|
|
3487
|
+
total,
|
|
3488
|
+
")"
|
|
3489
|
+
] }) })
|
|
3490
|
+
] }) });
|
|
3492
3491
|
}
|
|
3493
|
-
var
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
});
|
|
3498
|
-
var STAGE_LABELS = {
|
|
3499
|
-
interested: "Interested",
|
|
3500
|
-
booked: "Booked",
|
|
3501
|
-
qualified: "Qualified",
|
|
3502
|
-
demo_booked: "Demo Booked",
|
|
3503
|
-
proposal: "Proposal",
|
|
3504
|
-
proposal_sent: "Proposal Sent",
|
|
3505
|
-
proposal_signed: "Proposal Signed",
|
|
3506
|
-
payment_sent: "Payment Sent",
|
|
3507
|
-
closed_won: "Closed Won",
|
|
3508
|
-
proposal_revision: "Revision",
|
|
3509
|
-
closed_lost: "Closed Lost",
|
|
3510
|
-
nurturing: "Nurturing",
|
|
3511
|
-
cancelled: "Cancelled",
|
|
3512
|
-
no_show: "No Show"
|
|
3492
|
+
var ICON_MAP = {
|
|
3493
|
+
IconUser,
|
|
3494
|
+
IconClockExclamation,
|
|
3495
|
+
IconTrophy
|
|
3513
3496
|
};
|
|
3514
|
-
function
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3497
|
+
function SavedViewsPanel({ onViewClick }) {
|
|
3498
|
+
return /* @__PURE__ */ jsxs(Box, { children: [
|
|
3499
|
+
/* @__PURE__ */ jsx(Divider, { mb: "xs" }),
|
|
3500
|
+
/* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
|
|
3501
|
+
/* @__PURE__ */ jsx(Text, { fz: "xs", tt: "uppercase", c: "dimmed", fw: 600, mb: 4, children: "Saved Views" }),
|
|
3502
|
+
SAVED_VIEW_PRESETS.map((preset) => {
|
|
3503
|
+
const Icon = ICON_MAP[preset.iconName];
|
|
3504
|
+
return /* @__PURE__ */ jsx(
|
|
3505
|
+
UnstyledButton,
|
|
3506
|
+
{
|
|
3507
|
+
onClick: () => onViewClick(preset),
|
|
3508
|
+
style: {
|
|
3509
|
+
display: "block",
|
|
3510
|
+
width: "100%",
|
|
3511
|
+
padding: "4px 6px",
|
|
3512
|
+
borderRadius: "var(--mantine-radius-sm)",
|
|
3513
|
+
transition: `background-color var(--duration-fast) var(--easing)`
|
|
3514
|
+
},
|
|
3515
|
+
onMouseEnter: (e) => {
|
|
3516
|
+
e.currentTarget.style.backgroundColor = "var(--color-surface-hover)";
|
|
3517
|
+
},
|
|
3518
|
+
onMouseLeave: (e) => {
|
|
3519
|
+
e.currentTarget.style.backgroundColor = "transparent";
|
|
3520
|
+
},
|
|
3521
|
+
children: /* @__PURE__ */ jsxs(Group, { gap: "xs", wrap: "nowrap", children: [
|
|
3522
|
+
/* @__PURE__ */ jsx(Icon, { size: 14, style: { color: "var(--color-text-dimmed)", flexShrink: 0 } }),
|
|
3523
|
+
/* @__PURE__ */ jsx(Text, { size: "xs", children: preset.label })
|
|
3524
|
+
] })
|
|
3525
|
+
},
|
|
3526
|
+
preset.id
|
|
3527
|
+
);
|
|
3528
|
+
})
|
|
3529
|
+
] })
|
|
3530
|
+
] });
|
|
3531
|
+
}
|
|
3532
|
+
var KIND_OPTIONS = [
|
|
3533
|
+
{ value: "call", label: "Call" },
|
|
3534
|
+
{ value: "email", label: "Email" },
|
|
3535
|
+
{ value: "meeting", label: "Meeting" },
|
|
3536
|
+
{ value: "other", label: "Other" }
|
|
3537
|
+
];
|
|
3538
|
+
function buildDealLabel(deal) {
|
|
3539
|
+
const contact = deal.contact;
|
|
3540
|
+
if (!contact) return `Deal ${deal.id.slice(0, 8)}`;
|
|
3541
|
+
const name = `${contact.first_name ?? ""} ${contact.last_name ?? ""}`.trim();
|
|
3542
|
+
const company = contact.company?.name ?? "\u2014";
|
|
3543
|
+
return name ? `${name} \u2013 ${company}` : `Deal ${deal.id.slice(0, 8)}`;
|
|
3544
|
+
}
|
|
3545
|
+
function QuickCreateActions({ onNewDeal }) {
|
|
3546
|
+
const [open, setOpen] = useState(false);
|
|
3547
|
+
const [dealId, setDealId] = useState(null);
|
|
3548
|
+
const [title, setTitle] = useState("");
|
|
3549
|
+
const [description, setDescription] = useState("");
|
|
3550
|
+
const [kind, setKind] = useState("other");
|
|
3551
|
+
const [dueAt, setDueAt] = useState("");
|
|
3552
|
+
const { data: deals } = useDeals();
|
|
3553
|
+
const createTask = useCreateDealTask();
|
|
3554
|
+
const dealOptions = (deals ?? []).map((deal) => ({
|
|
3555
|
+
value: deal.id,
|
|
3556
|
+
label: buildDealLabel(deal)
|
|
3557
|
+
}));
|
|
3558
|
+
function resetForm() {
|
|
3559
|
+
setDealId(null);
|
|
3560
|
+
setTitle("");
|
|
3561
|
+
setDescription("");
|
|
3562
|
+
setKind("other");
|
|
3563
|
+
setDueAt("");
|
|
3564
|
+
}
|
|
3565
|
+
function handleClose() {
|
|
3566
|
+
setOpen(false);
|
|
3567
|
+
resetForm();
|
|
3568
|
+
}
|
|
3569
|
+
async function handleSubmit() {
|
|
3570
|
+
if (!dealId || !title) return;
|
|
3571
|
+
await createTask.mutateAsync({
|
|
3572
|
+
dealId,
|
|
3573
|
+
title,
|
|
3574
|
+
description: description || null,
|
|
3575
|
+
kind,
|
|
3576
|
+
dueAt: dueAt ? new Date(dueAt).toISOString() : null
|
|
3577
|
+
});
|
|
3578
|
+
handleClose();
|
|
3579
|
+
}
|
|
3580
|
+
const isSubmitDisabled = !dealId || !title || createTask.isPending;
|
|
3581
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3582
|
+
/* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
|
|
3583
|
+
/* @__PURE__ */ jsx(Text, { fz: "xs", tt: "uppercase", c: "dimmed", fw: 600, children: "QUICK CREATE" }),
|
|
3584
|
+
/* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
|
|
3585
|
+
/* @__PURE__ */ jsx(Button, { variant: "light", size: "xs", leftSection: /* @__PURE__ */ jsx(IconPlus, { size: 14 }), fullWidth: true, onClick: onNewDeal, children: "New Deal" }),
|
|
3586
|
+
/* @__PURE__ */ jsx(
|
|
3587
|
+
Button,
|
|
3588
|
+
{
|
|
3589
|
+
variant: "light",
|
|
3590
|
+
size: "xs",
|
|
3591
|
+
leftSection: /* @__PURE__ */ jsx(IconPlus, { size: 14 }),
|
|
3592
|
+
fullWidth: true,
|
|
3593
|
+
onClick: () => setOpen(true),
|
|
3594
|
+
children: "New Task"
|
|
3595
|
+
}
|
|
3596
|
+
)
|
|
3597
|
+
] })
|
|
3598
|
+
] }),
|
|
3599
|
+
/* @__PURE__ */ jsxs(Modal, { opened: open, onClose: handleClose, title: "New Task", size: "md", children: [
|
|
3600
|
+
/* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
|
|
3601
|
+
/* @__PURE__ */ jsx(
|
|
3602
|
+
Select,
|
|
3603
|
+
{
|
|
3604
|
+
label: "Deal",
|
|
3605
|
+
placeholder: "Select a deal",
|
|
3606
|
+
data: dealOptions,
|
|
3607
|
+
value: dealId,
|
|
3608
|
+
onChange: setDealId,
|
|
3609
|
+
searchable: true,
|
|
3610
|
+
required: true
|
|
3611
|
+
}
|
|
3612
|
+
),
|
|
3613
|
+
/* @__PURE__ */ jsx(
|
|
3614
|
+
TextInput,
|
|
3615
|
+
{
|
|
3616
|
+
label: "Title",
|
|
3617
|
+
placeholder: "Task title",
|
|
3618
|
+
value: title,
|
|
3619
|
+
onChange: (e) => setTitle(e.currentTarget.value),
|
|
3620
|
+
required: true
|
|
3621
|
+
}
|
|
3622
|
+
),
|
|
3623
|
+
/* @__PURE__ */ jsx(
|
|
3624
|
+
Textarea,
|
|
3625
|
+
{
|
|
3626
|
+
label: "Description",
|
|
3627
|
+
placeholder: "Optional description",
|
|
3628
|
+
value: description,
|
|
3629
|
+
onChange: (e) => setDescription(e.currentTarget.value),
|
|
3630
|
+
autosize: true,
|
|
3631
|
+
minRows: 2,
|
|
3632
|
+
maxRows: 5
|
|
3633
|
+
}
|
|
3634
|
+
),
|
|
3635
|
+
/* @__PURE__ */ jsx(
|
|
3636
|
+
Select,
|
|
3637
|
+
{
|
|
3638
|
+
label: "Kind",
|
|
3639
|
+
data: KIND_OPTIONS,
|
|
3640
|
+
value: kind,
|
|
3641
|
+
onChange: (v) => setKind(v ?? "other")
|
|
3642
|
+
}
|
|
3643
|
+
),
|
|
3644
|
+
/* @__PURE__ */ jsx(
|
|
3645
|
+
TextInput,
|
|
3646
|
+
{
|
|
3647
|
+
type: "datetime-local",
|
|
3648
|
+
label: "Due At",
|
|
3649
|
+
value: dueAt,
|
|
3650
|
+
onChange: (e) => setDueAt(e.currentTarget.value)
|
|
3651
|
+
}
|
|
3652
|
+
)
|
|
3653
|
+
] }),
|
|
3654
|
+
/* @__PURE__ */ jsxs(Group, { justify: "flex-end", mt: "md", children: [
|
|
3655
|
+
/* @__PURE__ */ jsx(Button, { variant: "default", onClick: handleClose, children: "Cancel" }),
|
|
3656
|
+
/* @__PURE__ */ jsx(Button, { onClick: handleSubmit, loading: createTask.isPending, disabled: isSubmitDisabled, children: "Create" })
|
|
3657
|
+
] })
|
|
3658
|
+
] })
|
|
3659
|
+
] });
|
|
3660
|
+
}
|
|
3661
|
+
var CRM_ITEMS = [
|
|
3662
|
+
{ label: "Overview", to: "/crm", icon: IconLayoutGrid, exact: true },
|
|
3663
|
+
{ label: "Pipeline", to: "/crm/pipeline", icon: IconColumns, exact: false },
|
|
3664
|
+
{ label: "Deals", to: "/crm/deals", icon: IconFileInvoice, exact: false }
|
|
3665
|
+
];
|
|
3666
|
+
function toSearchString(filters) {
|
|
3667
|
+
if (!filters) return "";
|
|
3668
|
+
const params = new URLSearchParams();
|
|
3669
|
+
if (filters.stage) params.set("stage", filters.stage);
|
|
3670
|
+
if (filters.search) params.set("search", filters.search);
|
|
3671
|
+
const query = params.toString();
|
|
3672
|
+
return query ? `?${query}` : "";
|
|
3673
|
+
}
|
|
3674
|
+
var CrmSidebarMiddle = () => {
|
|
3675
|
+
const { currentPath, navigate } = useRouterContext();
|
|
3676
|
+
return /* @__PURE__ */ jsxs(Stack, { gap: 0, style: { flex: 1, overflowY: "auto" }, children: [
|
|
3677
|
+
/* @__PURE__ */ jsx(Stack, { gap: 0, p: "sm", children: CRM_ITEMS.map((item) => {
|
|
3678
|
+
const isActive = item.exact ? currentPath === item.to || currentPath === `${item.to}/` : currentPath.startsWith(item.to);
|
|
3679
|
+
return /* @__PURE__ */ jsx(
|
|
3680
|
+
SidebarListItem,
|
|
3681
|
+
{
|
|
3682
|
+
icon: item.icon,
|
|
3683
|
+
label: item.label,
|
|
3684
|
+
isActive,
|
|
3685
|
+
onClick: () => navigate(item.to)
|
|
3686
|
+
},
|
|
3687
|
+
item.to
|
|
3688
|
+
);
|
|
3689
|
+
}) }),
|
|
3690
|
+
/* @__PURE__ */ jsx(Divider, { my: "xs" }),
|
|
3691
|
+
/* @__PURE__ */ jsxs(Stack, { gap: "md", p: "sm", children: [
|
|
3692
|
+
/* @__PURE__ */ jsx(
|
|
3693
|
+
MyTasksPanel,
|
|
3694
|
+
{
|
|
3695
|
+
onTaskClick: (dealId) => navigate(`/crm/deals/${dealId}`),
|
|
3696
|
+
onSeeAll: () => navigate("/crm/deals")
|
|
3697
|
+
}
|
|
3698
|
+
),
|
|
3699
|
+
/* @__PURE__ */ jsx(
|
|
3700
|
+
SavedViewsPanel,
|
|
3701
|
+
{
|
|
3702
|
+
onViewClick: (preset) => {
|
|
3703
|
+
const search = toSearchString(preset.urlFilters);
|
|
3704
|
+
navigate(`${preset.target}${search}`);
|
|
3705
|
+
}
|
|
3706
|
+
}
|
|
3707
|
+
),
|
|
3708
|
+
/* @__PURE__ */ jsx(QuickCreateActions, { onNewDeal: () => navigate("/crm/deals") })
|
|
3709
|
+
] })
|
|
3710
|
+
] });
|
|
3711
|
+
};
|
|
3712
|
+
var CrmSidebar = () => {
|
|
3713
|
+
return /* @__PURE__ */ jsxs(Stack, { gap: 0, style: { height: "100%", display: "flex", flexDirection: "column" }, children: [
|
|
3714
|
+
/* @__PURE__ */ jsx(CrmSidebarTop, {}),
|
|
3715
|
+
/* @__PURE__ */ jsx(CrmSidebarMiddle, {})
|
|
3716
|
+
] });
|
|
3717
|
+
};
|
|
3718
|
+
var PIPELINE_FUNNEL_ORDER = [
|
|
3719
|
+
"interested",
|
|
3720
|
+
"proposal",
|
|
3721
|
+
"closing",
|
|
3722
|
+
"closed_won",
|
|
3723
|
+
"closed_lost",
|
|
3724
|
+
"nurturing"
|
|
3725
|
+
];
|
|
3726
|
+
var defaultValueOf = (deal) => deal.initial_fee ?? 0;
|
|
3727
|
+
function useCrmPipelineSummary(opts) {
|
|
3728
|
+
const getValue = opts?.getDealValue ?? defaultValueOf;
|
|
3729
|
+
const { data: deals, isLoading, error } = useDeals();
|
|
3730
|
+
const data = useMemo(() => {
|
|
3731
|
+
const dealList = deals ?? [];
|
|
3732
|
+
const stageMap = /* @__PURE__ */ new Map();
|
|
3733
|
+
for (const stage of PIPELINE_FUNNEL_ORDER) {
|
|
3734
|
+
stageMap.set(stage, { count: 0, totalValue: 0 });
|
|
3735
|
+
}
|
|
3736
|
+
for (const deal of dealList) {
|
|
3737
|
+
const stage = deal.cached_stage;
|
|
3738
|
+
if (!stage || !stageMap.has(stage)) continue;
|
|
3739
|
+
const entry = stageMap.get(stage);
|
|
3740
|
+
entry.count += 1;
|
|
3741
|
+
entry.totalValue += getValue(deal);
|
|
3742
|
+
}
|
|
3743
|
+
return PIPELINE_FUNNEL_ORDER.map((stage) => {
|
|
3744
|
+
const entry = stageMap.get(stage);
|
|
3745
|
+
return { stage, count: entry.count, totalValue: entry.totalValue };
|
|
3746
|
+
});
|
|
3747
|
+
}, [deals, getValue]);
|
|
3748
|
+
return { data, isLoading, error };
|
|
3749
|
+
}
|
|
3750
|
+
var CLOSED_STAGES = ["closed_won", "closed_lost"];
|
|
3751
|
+
var OPEN_EXCLUDED = CLOSED_STAGES;
|
|
3752
|
+
var ZERO_METRICS = {
|
|
3753
|
+
totalDeals: 0,
|
|
3754
|
+
openDeals: 0,
|
|
3755
|
+
wonDeals: 0,
|
|
3756
|
+
winRate: 0,
|
|
3757
|
+
avgDealSize: 0,
|
|
3758
|
+
totalPipelineValue: 0
|
|
3759
|
+
};
|
|
3760
|
+
function useCrmQuickMetrics() {
|
|
3761
|
+
const { data: deals, isLoading, error } = useDeals();
|
|
3762
|
+
const data = useMemo(() => {
|
|
3763
|
+
const dealList = deals ?? [];
|
|
3764
|
+
if (dealList.length === 0) return ZERO_METRICS;
|
|
3765
|
+
let openDeals = 0;
|
|
3766
|
+
let wonDeals = 0;
|
|
3767
|
+
let lostDeals = 0;
|
|
3768
|
+
let wonFeeSum = 0;
|
|
3769
|
+
let wonFeeCount = 0;
|
|
3770
|
+
let pipelineValue = 0;
|
|
3771
|
+
for (const deal of dealList) {
|
|
3772
|
+
const stage = deal.cached_stage;
|
|
3773
|
+
const isOpen = !stage || !OPEN_EXCLUDED.includes(stage);
|
|
3774
|
+
const isWon = stage === "closed_won";
|
|
3775
|
+
const isLost = stage === "closed_lost";
|
|
3776
|
+
if (isOpen) {
|
|
3777
|
+
openDeals += 1;
|
|
3778
|
+
pipelineValue += deal.initial_fee ?? 0;
|
|
3779
|
+
}
|
|
3780
|
+
if (isWon) {
|
|
3781
|
+
wonDeals += 1;
|
|
3782
|
+
if (deal.initial_fee != null) {
|
|
3783
|
+
wonFeeSum += deal.initial_fee;
|
|
3784
|
+
wonFeeCount += 1;
|
|
3785
|
+
}
|
|
3786
|
+
}
|
|
3787
|
+
if (isLost) {
|
|
3788
|
+
lostDeals += 1;
|
|
3789
|
+
}
|
|
3790
|
+
}
|
|
3791
|
+
const winRateDenominator = wonDeals + lostDeals;
|
|
3792
|
+
const winRate = winRateDenominator === 0 ? 0 : wonDeals / winRateDenominator;
|
|
3793
|
+
const avgDealSize = wonFeeCount === 0 ? 0 : wonFeeSum / wonFeeCount;
|
|
3794
|
+
return {
|
|
3795
|
+
totalDeals: dealList.length,
|
|
3796
|
+
openDeals,
|
|
3797
|
+
wonDeals,
|
|
3798
|
+
winRate,
|
|
3799
|
+
avgDealSize,
|
|
3800
|
+
totalPipelineValue: pipelineValue
|
|
3801
|
+
};
|
|
3802
|
+
}, [deals]);
|
|
3803
|
+
return { data, isLoading, error };
|
|
3804
|
+
}
|
|
3805
|
+
function useRecentCrmActivity(opts) {
|
|
3806
|
+
const { apiRequest, isReady, organizationId } = useElevasisServices();
|
|
3807
|
+
const limit = opts?.limit ?? 20;
|
|
3808
|
+
const query = useQuery({
|
|
3809
|
+
queryKey: ["recent-crm-activity", organizationId, limit],
|
|
3810
|
+
queryFn: () => apiRequest(`/crm/recent-activity?limit=${limit}`),
|
|
3811
|
+
enabled: isReady
|
|
3812
|
+
});
|
|
3813
|
+
return {
|
|
3814
|
+
data: query.data?.entries ?? [],
|
|
3815
|
+
isLoading: query.isLoading,
|
|
3816
|
+
error: query.error
|
|
3817
|
+
};
|
|
3818
|
+
}
|
|
3819
|
+
var currencyFormatter = new Intl.NumberFormat("en-US", {
|
|
3820
|
+
style: "currency",
|
|
3821
|
+
currency: "USD",
|
|
3822
|
+
maximumFractionDigits: 0
|
|
3823
|
+
});
|
|
3824
|
+
var STAGE_LABELS = {
|
|
3825
|
+
interested: "Interested",
|
|
3826
|
+
proposal: "Proposal",
|
|
3827
|
+
closing: "Closing",
|
|
3828
|
+
closed_won: "Closed Won",
|
|
3829
|
+
closed_lost: "Closed Lost",
|
|
3830
|
+
nurturing: "Nurturing"
|
|
3831
|
+
};
|
|
3832
|
+
function PipelineFunnelWidget({ onStageClick, getDealValue }) {
|
|
3833
|
+
const { data, isLoading, error } = useCrmPipelineSummary({ getDealValue });
|
|
3834
|
+
if (isLoading) {
|
|
3835
|
+
return /* @__PURE__ */ jsx(Paper, { withBorder: true, p: "md", children: /* @__PURE__ */ jsx(Center, { p: "xl", children: /* @__PURE__ */ jsx(Loader, {}) }) });
|
|
3836
|
+
}
|
|
3837
|
+
if (error) {
|
|
3838
|
+
return /* @__PURE__ */ jsx(Paper, { withBorder: true, p: "md", children: /* @__PURE__ */ jsx(CenteredErrorState, { error, title: "Failed to load pipeline data" }) });
|
|
3839
|
+
}
|
|
3840
|
+
const totalDeals = data.reduce((sum, s) => sum + s.count, 0);
|
|
3841
|
+
const maxCount = Math.max(...data.map((s) => s.count), 1);
|
|
3842
|
+
if (totalDeals === 0) {
|
|
3843
|
+
return /* @__PURE__ */ jsxs(Paper, { withBorder: true, p: "md", children: [
|
|
3844
|
+
/* @__PURE__ */ jsx(CardHeader, { icon: /* @__PURE__ */ jsx(IconColumns, { size: 16 }), title: "Pipeline" }),
|
|
3845
|
+
/* @__PURE__ */ jsx(Center, { h: 200, children: /* @__PURE__ */ jsx(Alert, { icon: /* @__PURE__ */ jsx(IconAlertCircle, {}), color: "gray", variant: "light", children: "No deals in the pipeline yet" }) })
|
|
3846
|
+
] });
|
|
3847
|
+
}
|
|
3848
|
+
return /* @__PURE__ */ jsxs(Paper, { withBorder: true, p: "md", children: [
|
|
3531
3849
|
/* @__PURE__ */ jsx(
|
|
3532
3850
|
CardHeader,
|
|
3533
3851
|
{
|
|
@@ -3581,471 +3899,290 @@ function PipelineFunnelWidget({ onStageClick, getDealValue }) {
|
|
|
3581
3899
|
}) })
|
|
3582
3900
|
] });
|
|
3583
3901
|
}
|
|
3584
|
-
var MAX_VISIBLE = 5;
|
|
3585
|
-
function KindIcon({ kind }) {
|
|
3586
|
-
const size = 16;
|
|
3587
|
-
switch (kind) {
|
|
3588
|
-
case "call":
|
|
3589
|
-
return /* @__PURE__ */ jsx(IconPhone, { size });
|
|
3590
|
-
case "email":
|
|
3591
|
-
return /* @__PURE__ */ jsx(IconMail, { size });
|
|
3592
|
-
case "meeting":
|
|
3593
|
-
return /* @__PURE__ */ jsx(IconCalendar, { size });
|
|
3594
|
-
default:
|
|
3595
|
-
return /* @__PURE__ */ jsx(IconCheckbox, { size });
|
|
3596
|
-
}
|
|
3597
|
-
}
|
|
3598
|
-
function formatDueDate(dueAt) {
|
|
3599
|
-
if (!dueAt) return "No due date";
|
|
3600
|
-
return new Date(dueAt).toLocaleDateString();
|
|
3601
|
-
}
|
|
3602
|
-
function TasksDueWidget({ onTaskClick, onSeeAll }) {
|
|
3603
|
-
const { data: tasks, isLoading, error } = useDealTasksDue({ window: "today_and_overdue" });
|
|
3604
|
-
if (isLoading) {
|
|
3605
|
-
return /* @__PURE__ */ jsx(Paper, { withBorder: true, p: "md", children: /* @__PURE__ */ jsx(Center, { p: "xl", children: /* @__PURE__ */ jsx(Loader, {}) }) });
|
|
3606
|
-
}
|
|
3607
|
-
if (error) {
|
|
3608
|
-
return /* @__PURE__ */ jsx(Paper, { withBorder: true, p: "md", children: /* @__PURE__ */ jsx(CenteredErrorState, { error, title: "Failed to load tasks" }) });
|
|
3609
|
-
}
|
|
3610
|
-
const totalCount = tasks?.length ?? 0;
|
|
3611
|
-
const visibleTasks = (tasks ?? []).slice(0, MAX_VISIBLE);
|
|
3612
|
-
const hasMore = totalCount > MAX_VISIBLE;
|
|
3613
|
-
const seeAllLink = onSeeAll && hasMore ? /* @__PURE__ */ jsxs(Anchor, { size: "sm", onClick: onSeeAll, style: { cursor: "pointer" }, children: [
|
|
3614
|
-
"See all (",
|
|
3615
|
-
totalCount,
|
|
3616
|
-
")"
|
|
3617
|
-
] }) : void 0;
|
|
3618
|
-
if (totalCount === 0) {
|
|
3619
|
-
return /* @__PURE__ */ jsxs(Paper, { withBorder: true, p: "md", children: [
|
|
3620
|
-
/* @__PURE__ */ jsx(CardHeader, { icon: /* @__PURE__ */ jsx(IconChecklist, { size: 16 }), title: "Tasks Due" }),
|
|
3621
|
-
/* @__PURE__ */ jsx(Center, { h: 120, children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "No tasks due today" }) })
|
|
3622
|
-
] });
|
|
3623
|
-
}
|
|
3624
|
-
return /* @__PURE__ */ jsxs(Paper, { withBorder: true, p: "md", children: [
|
|
3625
|
-
/* @__PURE__ */ jsx(
|
|
3626
|
-
CardHeader,
|
|
3627
|
-
{
|
|
3628
|
-
icon: /* @__PURE__ */ jsx(IconChecklist, { size: 16 }),
|
|
3629
|
-
title: "Tasks Due",
|
|
3630
|
-
subtitle: `${totalCount} task${totalCount !== 1 ? "s" : ""}`,
|
|
3631
|
-
rightSection: seeAllLink
|
|
3632
|
-
}
|
|
3633
|
-
),
|
|
3634
|
-
/* @__PURE__ */ jsx(Stack, { gap: "xs", children: visibleTasks.map((task) => /* @__PURE__ */ jsx(
|
|
3635
|
-
Box,
|
|
3636
|
-
{
|
|
3637
|
-
onClick: () => onTaskClick(task.dealId),
|
|
3638
|
-
style: {
|
|
3639
|
-
cursor: "pointer",
|
|
3640
|
-
borderRadius: "var(--mantine-radius-sm)",
|
|
3641
|
-
padding: "6px 8px",
|
|
3642
|
-
transition: `background-color var(--duration-fast) var(--easing)`
|
|
3643
|
-
},
|
|
3644
|
-
onMouseEnter: (e) => {
|
|
3645
|
-
e.currentTarget.style.backgroundColor = "var(--color-surface-hover)";
|
|
3646
|
-
},
|
|
3647
|
-
onMouseLeave: (e) => {
|
|
3648
|
-
e.currentTarget.style.backgroundColor = "transparent";
|
|
3649
|
-
},
|
|
3650
|
-
children: /* @__PURE__ */ jsxs(Group, { gap: "sm", wrap: "nowrap", children: [
|
|
3651
|
-
/* @__PURE__ */ jsx(Text, { c: "dimmed", style: { flexShrink: 0 }, children: /* @__PURE__ */ jsx(KindIcon, { kind: task.kind }) }),
|
|
3652
|
-
/* @__PURE__ */ jsx(Text, { size: "sm", style: { flex: 1, minWidth: 0 }, truncate: true, children: task.title }),
|
|
3653
|
-
/* @__PURE__ */ jsx(Badge, { size: "xs", variant: "light", color: "gray", style: { flexShrink: 0 }, children: formatDueDate(task.dueAt) })
|
|
3654
|
-
] })
|
|
3655
|
-
},
|
|
3656
|
-
task.id
|
|
3657
|
-
)) })
|
|
3658
|
-
] });
|
|
3659
|
-
}
|
|
3660
|
-
function ActivityKindIcon({ kind }) {
|
|
3661
|
-
const size = 16;
|
|
3662
|
-
switch (kind) {
|
|
3663
|
-
case "note":
|
|
3664
|
-
return /* @__PURE__ */ jsx(IconNote, { size });
|
|
3665
|
-
case "stage_change":
|
|
3666
|
-
return /* @__PURE__ */ jsx(IconArrowRight, { size });
|
|
3667
|
-
case "deal_created":
|
|
3668
|
-
return /* @__PURE__ */ jsx(IconPlus, { size });
|
|
3669
|
-
}
|
|
3670
|
-
}
|
|
3671
|
-
function formatRelativeTime(occurredAt) {
|
|
3672
|
-
const date = new Date(occurredAt);
|
|
3673
|
-
const now = /* @__PURE__ */ new Date();
|
|
3674
|
-
const diffMs = now.getTime() - date.getTime();
|
|
3675
|
-
const diffMin = Math.floor(diffMs / 6e4);
|
|
3676
|
-
const diffHr = Math.floor(diffMin / 60);
|
|
3677
|
-
const diffDay = Math.floor(diffHr / 24);
|
|
3678
|
-
if (diffMin < 1) return "just now";
|
|
3679
|
-
if (diffMin < 60) return `${diffMin}m ago`;
|
|
3680
|
-
if (diffHr < 24) return `${diffHr}h ago`;
|
|
3681
|
-
if (diffDay < 7) return `${diffDay}d ago`;
|
|
3682
|
-
return date.toLocaleDateString();
|
|
3683
|
-
}
|
|
3684
|
-
function ActivityFeedWidget({ onDealClick, limit }) {
|
|
3685
|
-
const { data, isLoading, error } = useRecentCrmActivity({ limit: limit ?? 15 });
|
|
3686
|
-
if (isLoading) {
|
|
3687
|
-
return /* @__PURE__ */ jsx(Paper, { withBorder: true, p: "md", children: /* @__PURE__ */ jsx(Center, { p: "xl", children: /* @__PURE__ */ jsx(Loader, {}) }) });
|
|
3688
|
-
}
|
|
3689
|
-
if (error) {
|
|
3690
|
-
return /* @__PURE__ */ jsx(Paper, { withBorder: true, p: "md", children: /* @__PURE__ */ jsx(CenteredErrorState, { error, title: "Failed to load activity" }) });
|
|
3691
|
-
}
|
|
3692
|
-
if (!data.length) {
|
|
3693
|
-
return /* @__PURE__ */ jsxs(Paper, { withBorder: true, p: "md", children: [
|
|
3694
|
-
/* @__PURE__ */ jsx(CardHeader, { icon: /* @__PURE__ */ jsx(IconHistory, { size: 16 }), title: "Recent Activity" }),
|
|
3695
|
-
/* @__PURE__ */ jsx(Center, { h: 120, children: /* @__PURE__ */ jsx(Alert, { icon: /* @__PURE__ */ jsx(IconAlertCircle, {}), color: "gray", variant: "light", children: "No recent activity" }) })
|
|
3696
|
-
] });
|
|
3697
|
-
}
|
|
3698
|
-
return /* @__PURE__ */ jsxs(Paper, { withBorder: true, p: "md", children: [
|
|
3699
|
-
/* @__PURE__ */ jsx(CardHeader, { icon: /* @__PURE__ */ jsx(IconHistory, { size: 16 }), title: "Recent Activity" }),
|
|
3700
|
-
/* @__PURE__ */ jsx(Stack, { gap: 4, children: data.map((entry) => {
|
|
3701
|
-
const name = entry.contactName ?? entry.companyName ?? "Unknown";
|
|
3702
|
-
return /* @__PURE__ */ jsx(
|
|
3703
|
-
Box,
|
|
3704
|
-
{
|
|
3705
|
-
onClick: () => onDealClick(entry.dealId),
|
|
3706
|
-
style: {
|
|
3707
|
-
cursor: "pointer",
|
|
3708
|
-
borderRadius: "var(--mantine-radius-sm)",
|
|
3709
|
-
padding: "6px 8px",
|
|
3710
|
-
transition: `background-color var(--duration-fast) var(--easing)`
|
|
3711
|
-
},
|
|
3712
|
-
onMouseEnter: (e) => {
|
|
3713
|
-
e.currentTarget.style.backgroundColor = "var(--color-surface-hover)";
|
|
3714
|
-
},
|
|
3715
|
-
onMouseLeave: (e) => {
|
|
3716
|
-
e.currentTarget.style.backgroundColor = "transparent";
|
|
3717
|
-
},
|
|
3718
|
-
children: /* @__PURE__ */ jsxs(Group, { gap: "sm", wrap: "nowrap", align: "flex-start", children: [
|
|
3719
|
-
/* @__PURE__ */ jsx(Text, { c: "dimmed", style: { flexShrink: 0, paddingTop: 2 }, children: /* @__PURE__ */ jsx(ActivityKindIcon, { kind: entry.kind }) }),
|
|
3720
|
-
/* @__PURE__ */ jsx(Box, { style: { flex: 1, minWidth: 0 }, children: /* @__PURE__ */ jsxs(Group, { gap: "xs", wrap: "nowrap", children: [
|
|
3721
|
-
/* @__PURE__ */ jsx(Text, { size: "sm", fw: 600, truncate: true, style: { flexShrink: 0, maxWidth: 140 }, children: name }),
|
|
3722
|
-
/* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", truncate: true, style: { flex: 1, minWidth: 0 }, children: entry.description })
|
|
3723
|
-
] }) }),
|
|
3724
|
-
/* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", style: { flexShrink: 0, whiteSpace: "nowrap" }, children: formatRelativeTime(entry.occurredAt) })
|
|
3725
|
-
] })
|
|
3726
|
-
},
|
|
3727
|
-
entry.id
|
|
3728
|
-
);
|
|
3729
|
-
}) })
|
|
3730
|
-
] });
|
|
3731
|
-
}
|
|
3732
|
-
var currencyFormatter2 = new Intl.NumberFormat("en-US", {
|
|
3733
|
-
style: "currency",
|
|
3734
|
-
currency: "USD",
|
|
3735
|
-
maximumFractionDigits: 0
|
|
3736
|
-
});
|
|
3737
|
-
function formatPercent(value) {
|
|
3738
|
-
return `${Math.round(value * 100)}%`;
|
|
3739
|
-
}
|
|
3740
|
-
function StatTile({ label, value }) {
|
|
3741
|
-
return /* @__PURE__ */ jsx(Card, { padding: "sm", children: /* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
|
|
3742
|
-
/* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: label }),
|
|
3743
|
-
/* @__PURE__ */ jsx(Text, { fw: 700, size: "xl", children: value })
|
|
3744
|
-
] }) });
|
|
3902
|
+
var MAX_VISIBLE = 5;
|
|
3903
|
+
function KindIcon({ kind }) {
|
|
3904
|
+
const size = 16;
|
|
3905
|
+
switch (kind) {
|
|
3906
|
+
case "call":
|
|
3907
|
+
return /* @__PURE__ */ jsx(IconPhone, { size });
|
|
3908
|
+
case "email":
|
|
3909
|
+
return /* @__PURE__ */ jsx(IconMail, { size });
|
|
3910
|
+
case "meeting":
|
|
3911
|
+
return /* @__PURE__ */ jsx(IconCalendar, { size });
|
|
3912
|
+
default:
|
|
3913
|
+
return /* @__PURE__ */ jsx(IconCheckbox, { size });
|
|
3914
|
+
}
|
|
3745
3915
|
}
|
|
3746
|
-
function
|
|
3747
|
-
|
|
3748
|
-
return
|
|
3749
|
-
/* @__PURE__ */ jsx(StatTile, { label: "Total Pipeline Value", value: currencyFormatter2.format(data.totalPipelineValue) }),
|
|
3750
|
-
/* @__PURE__ */ jsx(StatTile, { label: "Win Rate", value: formatPercent(data.winRate) }),
|
|
3751
|
-
/* @__PURE__ */ jsx(StatTile, { label: "Open Deals", value: String(data.openDeals) }),
|
|
3752
|
-
/* @__PURE__ */ jsx(StatTile, { label: "Won This Period", value: String(data.wonDeals) })
|
|
3753
|
-
] }) });
|
|
3916
|
+
function formatDueDate(dueAt) {
|
|
3917
|
+
if (!dueAt) return "No due date";
|
|
3918
|
+
return new Date(dueAt).toLocaleDateString();
|
|
3754
3919
|
}
|
|
3755
|
-
function
|
|
3756
|
-
|
|
3757
|
-
|
|
3758
|
-
|
|
3759
|
-
|
|
3760
|
-
|
|
3761
|
-
})
|
|
3762
|
-
|
|
3763
|
-
|
|
3920
|
+
function TasksDueWidget({ onTaskClick, onSeeAll }) {
|
|
3921
|
+
const { data: tasks, isLoading, error } = useDealTasksDue({ window: "today_and_overdue" });
|
|
3922
|
+
if (isLoading) {
|
|
3923
|
+
return /* @__PURE__ */ jsx(Paper, { withBorder: true, p: "md", children: /* @__PURE__ */ jsx(Center, { p: "xl", children: /* @__PURE__ */ jsx(Loader, {}) }) });
|
|
3924
|
+
}
|
|
3925
|
+
if (error) {
|
|
3926
|
+
return /* @__PURE__ */ jsx(Paper, { withBorder: true, p: "md", children: /* @__PURE__ */ jsx(CenteredErrorState, { error, title: "Failed to load tasks" }) });
|
|
3927
|
+
}
|
|
3928
|
+
const totalCount = tasks?.length ?? 0;
|
|
3929
|
+
const visibleTasks = (tasks ?? []).slice(0, MAX_VISIBLE);
|
|
3930
|
+
const hasMore = totalCount > MAX_VISIBLE;
|
|
3931
|
+
const seeAllLink = onSeeAll && hasMore ? /* @__PURE__ */ jsxs(Anchor, { size: "sm", onClick: onSeeAll, style: { cursor: "pointer" }, children: [
|
|
3932
|
+
"See all (",
|
|
3933
|
+
totalCount,
|
|
3934
|
+
")"
|
|
3935
|
+
] }) : void 0;
|
|
3936
|
+
if (totalCount === 0) {
|
|
3937
|
+
return /* @__PURE__ */ jsxs(Paper, { withBorder: true, p: "md", children: [
|
|
3938
|
+
/* @__PURE__ */ jsx(CardHeader, { icon: /* @__PURE__ */ jsx(IconChecklist, { size: 16 }), title: "Tasks Due" }),
|
|
3939
|
+
/* @__PURE__ */ jsx(Center, { h: 120, children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "No tasks due today" }) })
|
|
3940
|
+
] });
|
|
3941
|
+
}
|
|
3942
|
+
return /* @__PURE__ */ jsxs(Paper, { withBorder: true, p: "md", children: [
|
|
3764
3943
|
/* @__PURE__ */ jsx(
|
|
3765
|
-
|
|
3944
|
+
CardHeader,
|
|
3766
3945
|
{
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
|
|
3946
|
+
icon: /* @__PURE__ */ jsx(IconChecklist, { size: 16 }),
|
|
3947
|
+
title: "Tasks Due",
|
|
3948
|
+
subtitle: `${totalCount} task${totalCount !== 1 ? "s" : ""}`,
|
|
3949
|
+
rightSection: seeAllLink
|
|
3770
3950
|
}
|
|
3771
3951
|
),
|
|
3772
|
-
/* @__PURE__ */ jsx(
|
|
3773
|
-
|
|
3774
|
-
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
|
|
3779
|
-
|
|
3780
|
-
|
|
3781
|
-
}
|
|
3782
|
-
|
|
3783
|
-
|
|
3784
|
-
|
|
3785
|
-
|
|
3786
|
-
|
|
3787
|
-
|
|
3788
|
-
|
|
3789
|
-
|
|
3790
|
-
|
|
3791
|
-
|
|
3792
|
-
|
|
3793
|
-
label: "Overdue",
|
|
3794
|
-
iconName: "IconClockExclamation",
|
|
3795
|
-
target: "/crm/deals"
|
|
3796
|
-
},
|
|
3797
|
-
{
|
|
3798
|
-
id: "won-this-month",
|
|
3799
|
-
label: "Won this month",
|
|
3800
|
-
iconName: "IconTrophy",
|
|
3801
|
-
target: "/crm/deals",
|
|
3802
|
-
urlFilters: { stage: "closed_won" },
|
|
3803
|
-
predicate: (deal) => {
|
|
3804
|
-
if (deal.cached_stage !== "closed_won") return false;
|
|
3805
|
-
const updated = new Date(deal.updated_at);
|
|
3806
|
-
const now = /* @__PURE__ */ new Date();
|
|
3807
|
-
return updated.getMonth() === now.getMonth() && updated.getFullYear() === now.getFullYear();
|
|
3808
|
-
}
|
|
3809
|
-
}
|
|
3810
|
-
];
|
|
3811
|
-
var KIND_ICONS = {
|
|
3812
|
-
call: IconPhone,
|
|
3813
|
-
email: IconMail,
|
|
3814
|
-
meeting: IconCalendar,
|
|
3815
|
-
other: IconCheckbox
|
|
3816
|
-
};
|
|
3817
|
-
function formatDueLabel(dueAt) {
|
|
3818
|
-
if (!dueAt) return "";
|
|
3819
|
-
const date = new Date(dueAt);
|
|
3820
|
-
const now = /* @__PURE__ */ new Date();
|
|
3821
|
-
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
|
3822
|
-
const dueDay = new Date(date.getFullYear(), date.getMonth(), date.getDate());
|
|
3823
|
-
const diffDays = Math.round((dueDay.getTime() - today.getTime()) / 864e5);
|
|
3824
|
-
if (diffDays < 0) return `${Math.abs(diffDays)}d overdue`;
|
|
3825
|
-
if (diffDays === 0) {
|
|
3826
|
-
return date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
|
|
3827
|
-
}
|
|
3828
|
-
return `in ${diffDays}d`;
|
|
3829
|
-
}
|
|
3830
|
-
function TaskRow({ task, onClick }) {
|
|
3831
|
-
const KindIcon2 = KIND_ICONS[task.kind];
|
|
3832
|
-
const dueLabel = formatDueLabel(task.dueAt);
|
|
3833
|
-
const isOverdue = task.dueAt !== null && new Date(task.dueAt) < /* @__PURE__ */ new Date();
|
|
3834
|
-
return /* @__PURE__ */ jsx(
|
|
3835
|
-
UnstyledButton,
|
|
3836
|
-
{
|
|
3837
|
-
onClick,
|
|
3838
|
-
style: {
|
|
3839
|
-
display: "block",
|
|
3840
|
-
width: "100%",
|
|
3841
|
-
padding: "4px 6px",
|
|
3842
|
-
borderRadius: "var(--mantine-radius-sm)",
|
|
3843
|
-
transition: `background-color var(--duration-fast) var(--easing)`
|
|
3844
|
-
},
|
|
3845
|
-
onMouseEnter: (e) => {
|
|
3846
|
-
e.currentTarget.style.backgroundColor = "var(--color-surface-hover)";
|
|
3847
|
-
},
|
|
3848
|
-
onMouseLeave: (e) => {
|
|
3849
|
-
e.currentTarget.style.backgroundColor = "transparent";
|
|
3952
|
+
/* @__PURE__ */ jsx(Stack, { gap: "xs", children: visibleTasks.map((task) => /* @__PURE__ */ jsx(
|
|
3953
|
+
Box,
|
|
3954
|
+
{
|
|
3955
|
+
onClick: () => onTaskClick(task.dealId),
|
|
3956
|
+
style: {
|
|
3957
|
+
cursor: "pointer",
|
|
3958
|
+
borderRadius: "var(--mantine-radius-sm)",
|
|
3959
|
+
padding: "6px 8px",
|
|
3960
|
+
transition: `background-color var(--duration-fast) var(--easing)`
|
|
3961
|
+
},
|
|
3962
|
+
onMouseEnter: (e) => {
|
|
3963
|
+
e.currentTarget.style.backgroundColor = "var(--color-surface-hover)";
|
|
3964
|
+
},
|
|
3965
|
+
onMouseLeave: (e) => {
|
|
3966
|
+
e.currentTarget.style.backgroundColor = "transparent";
|
|
3967
|
+
},
|
|
3968
|
+
children: /* @__PURE__ */ jsxs(Group, { gap: "sm", wrap: "nowrap", children: [
|
|
3969
|
+
/* @__PURE__ */ jsx(Text, { c: "dimmed", style: { flexShrink: 0 }, children: /* @__PURE__ */ jsx(KindIcon, { kind: task.kind }) }),
|
|
3970
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", style: { flex: 1, minWidth: 0 }, truncate: true, children: task.title }),
|
|
3971
|
+
/* @__PURE__ */ jsx(Badge, { size: "xs", variant: "light", color: "gray", style: { flexShrink: 0 }, children: formatDueDate(task.dueAt) })
|
|
3972
|
+
] })
|
|
3850
3973
|
},
|
|
3851
|
-
|
|
3852
|
-
|
|
3853
|
-
/* @__PURE__ */ jsx(Text, { size: "xs", truncate: true, style: { flex: 1, minWidth: 0 }, children: task.title }),
|
|
3854
|
-
dueLabel && /* @__PURE__ */ jsx(Badge, { size: "xs", variant: "light", color: isOverdue ? "red" : "gray", style: { flexShrink: 0 }, children: dueLabel })
|
|
3855
|
-
] })
|
|
3856
|
-
}
|
|
3857
|
-
);
|
|
3858
|
-
}
|
|
3859
|
-
function MyTasksPanel({ onTaskClick, onSeeAll }) {
|
|
3860
|
-
const { data, isLoading, isError } = useDealTasksDue({ window: "today_and_overdue" });
|
|
3861
|
-
const tasks = data ?? [];
|
|
3862
|
-
const total = tasks.length;
|
|
3863
|
-
const visible = tasks.slice(0, 5);
|
|
3864
|
-
return /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
|
|
3865
|
-
/* @__PURE__ */ jsxs(Group, { justify: "space-between", mb: 4, children: [
|
|
3866
|
-
/* @__PURE__ */ jsx(Text, { fz: "xs", tt: "uppercase", c: "dimmed", fw: 600, children: "My Tasks" }),
|
|
3867
|
-
total > 0 && /* @__PURE__ */ jsx(Badge, { size: "xs", variant: "light", children: total })
|
|
3868
|
-
] }),
|
|
3869
|
-
isLoading && /* @__PURE__ */ jsx(Center, { py: 4, children: /* @__PURE__ */ jsx(Loader, { size: "xs" }) }),
|
|
3870
|
-
isError && /* @__PURE__ */ jsx(Text, { size: "xs", c: "red", children: "Failed to load tasks" }),
|
|
3871
|
-
!isLoading && !isError && visible.length === 0 && /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "No tasks due \u2014 you're caught up." }),
|
|
3872
|
-
!isLoading && !isError && visible.map((task) => /* @__PURE__ */ jsx(TaskRow, { task, onClick: () => onTaskClick(task.dealId) }, task.id)),
|
|
3873
|
-
onSeeAll && total > 5 && /* @__PURE__ */ jsx(UnstyledButton, { onClick: onSeeAll, mt: 2, style: { display: "inline-block" }, children: /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", style: { textDecoration: "underline" }, children: [
|
|
3874
|
-
"See all (",
|
|
3875
|
-
total,
|
|
3876
|
-
")"
|
|
3877
|
-
] }) })
|
|
3878
|
-
] }) });
|
|
3879
|
-
}
|
|
3880
|
-
var ICON_MAP = {
|
|
3881
|
-
IconUser,
|
|
3882
|
-
IconClockExclamation,
|
|
3883
|
-
IconTrophy
|
|
3884
|
-
};
|
|
3885
|
-
function SavedViewsPanel({ onViewClick }) {
|
|
3886
|
-
return /* @__PURE__ */ jsxs(Box, { children: [
|
|
3887
|
-
/* @__PURE__ */ jsx(Divider, { mb: "xs" }),
|
|
3888
|
-
/* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
|
|
3889
|
-
/* @__PURE__ */ jsx(Text, { fz: "xs", tt: "uppercase", c: "dimmed", fw: 600, mb: 4, children: "Saved Views" }),
|
|
3890
|
-
SAVED_VIEW_PRESETS.map((preset) => {
|
|
3891
|
-
const Icon = ICON_MAP[preset.iconName];
|
|
3892
|
-
return /* @__PURE__ */ jsx(
|
|
3893
|
-
UnstyledButton,
|
|
3894
|
-
{
|
|
3895
|
-
onClick: () => onViewClick(preset),
|
|
3896
|
-
style: {
|
|
3897
|
-
display: "block",
|
|
3898
|
-
width: "100%",
|
|
3899
|
-
padding: "4px 6px",
|
|
3900
|
-
borderRadius: "var(--mantine-radius-sm)",
|
|
3901
|
-
transition: `background-color var(--duration-fast) var(--easing)`
|
|
3902
|
-
},
|
|
3903
|
-
onMouseEnter: (e) => {
|
|
3904
|
-
e.currentTarget.style.backgroundColor = "var(--color-surface-hover)";
|
|
3905
|
-
},
|
|
3906
|
-
onMouseLeave: (e) => {
|
|
3907
|
-
e.currentTarget.style.backgroundColor = "transparent";
|
|
3908
|
-
},
|
|
3909
|
-
children: /* @__PURE__ */ jsxs(Group, { gap: "xs", wrap: "nowrap", children: [
|
|
3910
|
-
/* @__PURE__ */ jsx(Icon, { size: 14, style: { color: "var(--color-text-dimmed)", flexShrink: 0 } }),
|
|
3911
|
-
/* @__PURE__ */ jsx(Text, { size: "xs", children: preset.label })
|
|
3912
|
-
] })
|
|
3913
|
-
},
|
|
3914
|
-
preset.id
|
|
3915
|
-
);
|
|
3916
|
-
})
|
|
3917
|
-
] })
|
|
3974
|
+
task.id
|
|
3975
|
+
)) })
|
|
3918
3976
|
] });
|
|
3919
3977
|
}
|
|
3920
|
-
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
|
|
3928
|
-
|
|
3929
|
-
|
|
3930
|
-
const company = contact.company?.name ?? "\u2014";
|
|
3931
|
-
return name ? `${name} \u2013 ${company}` : `Deal ${deal.id.slice(0, 8)}`;
|
|
3978
|
+
function ActivityKindIcon({ kind }) {
|
|
3979
|
+
const size = 16;
|
|
3980
|
+
switch (kind) {
|
|
3981
|
+
case "note":
|
|
3982
|
+
return /* @__PURE__ */ jsx(IconNote, { size });
|
|
3983
|
+
case "stage_change":
|
|
3984
|
+
return /* @__PURE__ */ jsx(IconArrowRight, { size });
|
|
3985
|
+
case "deal_created":
|
|
3986
|
+
return /* @__PURE__ */ jsx(IconPlus, { size });
|
|
3987
|
+
}
|
|
3932
3988
|
}
|
|
3933
|
-
function
|
|
3934
|
-
const
|
|
3935
|
-
const
|
|
3936
|
-
const
|
|
3937
|
-
const
|
|
3938
|
-
const
|
|
3939
|
-
const
|
|
3940
|
-
|
|
3941
|
-
|
|
3942
|
-
|
|
3943
|
-
|
|
3944
|
-
|
|
3945
|
-
|
|
3946
|
-
|
|
3947
|
-
|
|
3948
|
-
|
|
3949
|
-
|
|
3950
|
-
setKind("other");
|
|
3951
|
-
setDueAt("");
|
|
3989
|
+
function formatRelativeTime(occurredAt) {
|
|
3990
|
+
const date = new Date(occurredAt);
|
|
3991
|
+
const now = /* @__PURE__ */ new Date();
|
|
3992
|
+
const diffMs = now.getTime() - date.getTime();
|
|
3993
|
+
const diffMin = Math.floor(diffMs / 6e4);
|
|
3994
|
+
const diffHr = Math.floor(diffMin / 60);
|
|
3995
|
+
const diffDay = Math.floor(diffHr / 24);
|
|
3996
|
+
if (diffMin < 1) return "just now";
|
|
3997
|
+
if (diffMin < 60) return `${diffMin}m ago`;
|
|
3998
|
+
if (diffHr < 24) return `${diffHr}h ago`;
|
|
3999
|
+
if (diffDay < 7) return `${diffDay}d ago`;
|
|
4000
|
+
return date.toLocaleDateString();
|
|
4001
|
+
}
|
|
4002
|
+
function ActivityFeedWidget({ onDealClick, limit }) {
|
|
4003
|
+
const { data, isLoading, error } = useRecentCrmActivity({ limit: limit ?? 15 });
|
|
4004
|
+
if (isLoading) {
|
|
4005
|
+
return /* @__PURE__ */ jsx(Paper, { withBorder: true, p: "md", children: /* @__PURE__ */ jsx(Center, { p: "xl", children: /* @__PURE__ */ jsx(Loader, {}) }) });
|
|
3952
4006
|
}
|
|
3953
|
-
|
|
3954
|
-
|
|
3955
|
-
resetForm();
|
|
4007
|
+
if (error) {
|
|
4008
|
+
return /* @__PURE__ */ jsx(Paper, { withBorder: true, p: "md", children: /* @__PURE__ */ jsx(CenteredErrorState, { error, title: "Failed to load activity" }) });
|
|
3956
4009
|
}
|
|
3957
|
-
|
|
3958
|
-
|
|
3959
|
-
|
|
3960
|
-
|
|
3961
|
-
|
|
3962
|
-
description: description || null,
|
|
3963
|
-
kind,
|
|
3964
|
-
dueAt: dueAt ? new Date(dueAt).toISOString() : null
|
|
3965
|
-
});
|
|
3966
|
-
handleClose();
|
|
4010
|
+
if (!data.length) {
|
|
4011
|
+
return /* @__PURE__ */ jsxs(Paper, { withBorder: true, p: "md", children: [
|
|
4012
|
+
/* @__PURE__ */ jsx(CardHeader, { icon: /* @__PURE__ */ jsx(IconHistory, { size: 16 }), title: "Recent Activity" }),
|
|
4013
|
+
/* @__PURE__ */ jsx(Center, { h: 120, children: /* @__PURE__ */ jsx(Alert, { icon: /* @__PURE__ */ jsx(IconAlertCircle, {}), color: "gray", variant: "light", children: "No recent activity" }) })
|
|
4014
|
+
] });
|
|
3967
4015
|
}
|
|
3968
|
-
|
|
3969
|
-
|
|
3970
|
-
/* @__PURE__ */
|
|
3971
|
-
|
|
3972
|
-
/* @__PURE__ */
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
{
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
|
|
3986
|
-
|
|
3987
|
-
|
|
3988
|
-
|
|
3989
|
-
|
|
3990
|
-
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
-
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
|
|
3997
|
-
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
|
|
4005
|
-
|
|
4006
|
-
|
|
4007
|
-
|
|
4008
|
-
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
|
|
4012
|
-
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
|
|
4021
|
-
|
|
4022
|
-
|
|
4023
|
-
|
|
4024
|
-
|
|
4025
|
-
|
|
4026
|
-
|
|
4027
|
-
|
|
4028
|
-
|
|
4029
|
-
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
|
|
4035
|
-
|
|
4036
|
-
|
|
4037
|
-
|
|
4038
|
-
|
|
4039
|
-
|
|
4040
|
-
|
|
4041
|
-
|
|
4042
|
-
|
|
4043
|
-
|
|
4044
|
-
|
|
4016
|
+
return /* @__PURE__ */ jsxs(Paper, { withBorder: true, p: "md", children: [
|
|
4017
|
+
/* @__PURE__ */ jsx(CardHeader, { icon: /* @__PURE__ */ jsx(IconHistory, { size: 16 }), title: "Recent Activity" }),
|
|
4018
|
+
/* @__PURE__ */ jsx(Stack, { gap: 4, children: data.map((entry) => {
|
|
4019
|
+
const name = entry.contactName ?? entry.companyName ?? "Unknown";
|
|
4020
|
+
return /* @__PURE__ */ jsx(
|
|
4021
|
+
Box,
|
|
4022
|
+
{
|
|
4023
|
+
onClick: () => onDealClick(entry.dealId),
|
|
4024
|
+
style: {
|
|
4025
|
+
cursor: "pointer",
|
|
4026
|
+
borderRadius: "var(--mantine-radius-sm)",
|
|
4027
|
+
padding: "6px 8px",
|
|
4028
|
+
transition: `background-color var(--duration-fast) var(--easing)`
|
|
4029
|
+
},
|
|
4030
|
+
onMouseEnter: (e) => {
|
|
4031
|
+
e.currentTarget.style.backgroundColor = "var(--color-surface-hover)";
|
|
4032
|
+
},
|
|
4033
|
+
onMouseLeave: (e) => {
|
|
4034
|
+
e.currentTarget.style.backgroundColor = "transparent";
|
|
4035
|
+
},
|
|
4036
|
+
children: /* @__PURE__ */ jsxs(Group, { gap: "sm", wrap: "nowrap", align: "flex-start", children: [
|
|
4037
|
+
/* @__PURE__ */ jsx(Text, { c: "dimmed", style: { flexShrink: 0, paddingTop: 2 }, children: /* @__PURE__ */ jsx(ActivityKindIcon, { kind: entry.kind }) }),
|
|
4038
|
+
/* @__PURE__ */ jsx(Box, { style: { flex: 1, minWidth: 0 }, children: /* @__PURE__ */ jsxs(Group, { gap: "xs", wrap: "nowrap", children: [
|
|
4039
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", fw: 600, truncate: true, style: { flexShrink: 0, maxWidth: 140 }, children: name }),
|
|
4040
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", truncate: true, style: { flex: 1, minWidth: 0 }, children: entry.description })
|
|
4041
|
+
] }) }),
|
|
4042
|
+
/* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", style: { flexShrink: 0, whiteSpace: "nowrap" }, children: formatRelativeTime(entry.occurredAt) })
|
|
4043
|
+
] })
|
|
4044
|
+
},
|
|
4045
|
+
entry.id
|
|
4046
|
+
);
|
|
4047
|
+
}) })
|
|
4048
|
+
] });
|
|
4049
|
+
}
|
|
4050
|
+
var currencyFormatter2 = new Intl.NumberFormat("en-US", {
|
|
4051
|
+
style: "currency",
|
|
4052
|
+
currency: "USD",
|
|
4053
|
+
maximumFractionDigits: 0
|
|
4054
|
+
});
|
|
4055
|
+
function formatPercent(value) {
|
|
4056
|
+
return `${Math.round(value * 100)}%`;
|
|
4057
|
+
}
|
|
4058
|
+
function StatTile({ label, value }) {
|
|
4059
|
+
return /* @__PURE__ */ jsx(Card, { padding: "sm", children: /* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
|
|
4060
|
+
/* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: label }),
|
|
4061
|
+
/* @__PURE__ */ jsx(Text, { fw: 700, size: "xl", children: value })
|
|
4062
|
+
] }) });
|
|
4063
|
+
}
|
|
4064
|
+
function MetricsStrip() {
|
|
4065
|
+
const { data } = useCrmQuickMetrics();
|
|
4066
|
+
return /* @__PURE__ */ jsx(Paper, { withBorder: true, p: "md", children: /* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 2, sm: 4 }, children: [
|
|
4067
|
+
/* @__PURE__ */ jsx(StatTile, { label: "Total Pipeline Value", value: currencyFormatter2.format(data.totalPipelineValue) }),
|
|
4068
|
+
/* @__PURE__ */ jsx(StatTile, { label: "Win Rate", value: formatPercent(data.winRate) }),
|
|
4069
|
+
/* @__PURE__ */ jsx(StatTile, { label: "Open Deals", value: String(data.openDeals) }),
|
|
4070
|
+
/* @__PURE__ */ jsx(StatTile, { label: "Won This Period", value: String(data.wonDeals) })
|
|
4071
|
+
] }) });
|
|
4072
|
+
}
|
|
4073
|
+
function CrmOverview({
|
|
4074
|
+
onStageClick,
|
|
4075
|
+
onDealClick,
|
|
4076
|
+
onGoToPipeline,
|
|
4077
|
+
getDealValue,
|
|
4078
|
+
renderActions
|
|
4079
|
+
}) {
|
|
4080
|
+
const rightSection = renderActions ? renderActions() : /* @__PURE__ */ jsx(Button, { leftSection: /* @__PURE__ */ jsx(IconColumns, { size: 16 }), variant: "light", size: "sm", onClick: onGoToPipeline, children: "Go to Pipeline" });
|
|
4081
|
+
return /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
|
|
4082
|
+
/* @__PURE__ */ jsx(
|
|
4083
|
+
PageTitleCaption,
|
|
4084
|
+
{
|
|
4085
|
+
title: "CRM Overview",
|
|
4086
|
+
caption: "Pipeline health, tasks, and recent activity at a glance.",
|
|
4087
|
+
rightSection
|
|
4088
|
+
}
|
|
4089
|
+
),
|
|
4090
|
+
/* @__PURE__ */ jsx(MetricsStrip, {}),
|
|
4091
|
+
/* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 1, lg: 2 }, spacing: "md", children: [
|
|
4092
|
+
/* @__PURE__ */ jsx(PipelineFunnelWidget, { onStageClick, getDealValue }),
|
|
4093
|
+
/* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
|
|
4094
|
+
/* @__PURE__ */ jsx(TasksDueWidget, { onTaskClick: onDealClick }),
|
|
4095
|
+
/* @__PURE__ */ jsx(ActivityFeedWidget, { onDealClick })
|
|
4045
4096
|
] })
|
|
4046
4097
|
] })
|
|
4047
4098
|
] });
|
|
4048
4099
|
}
|
|
4100
|
+
|
|
4101
|
+
// src/features/crm/manifest.ts
|
|
4102
|
+
var crmManifest = {
|
|
4103
|
+
key: "crm",
|
|
4104
|
+
label: "CRM",
|
|
4105
|
+
sidebar: CrmSidebar,
|
|
4106
|
+
subshellRoutes: ["/crm"]
|
|
4107
|
+
};
|
|
4108
|
+
var LeadGenSidebarTop = () => {
|
|
4109
|
+
return /* @__PURE__ */ jsx(SubshellSidebarSection, { icon: IconTarget, label: "Lead Gen" });
|
|
4110
|
+
};
|
|
4111
|
+
var LEAD_GEN_ITEMS = [
|
|
4112
|
+
{ label: "Overview", to: "/lead-gen", icon: IconLayoutGrid, exact: true },
|
|
4113
|
+
{ label: "Batches", to: "/lead-gen/batches", icon: IconListDetails, exact: false },
|
|
4114
|
+
{ label: "Lists", to: "/lead-gen/lists", icon: IconList, exact: false },
|
|
4115
|
+
{ label: "Companies", to: "/lead-gen/companies", icon: IconBuilding, exact: false },
|
|
4116
|
+
{ label: "Contacts", to: "/lead-gen/contacts", icon: IconAddressBook, exact: false },
|
|
4117
|
+
{ label: "Deliverability", to: "/lead-gen/deliverability", icon: IconMailCheck, exact: false }
|
|
4118
|
+
];
|
|
4119
|
+
var LeadGenSidebarMiddle = () => {
|
|
4120
|
+
const { currentPath, navigate } = useRouterContext();
|
|
4121
|
+
return /* @__PURE__ */ jsx(Stack, { gap: 0, style: { flex: 1, overflowY: "auto" }, children: /* @__PURE__ */ jsx(Stack, { gap: 0, p: "sm", children: LEAD_GEN_ITEMS.map((item) => {
|
|
4122
|
+
const isActive = item.exact ? currentPath === item.to || currentPath === `${item.to}/` : currentPath.startsWith(item.to);
|
|
4123
|
+
return /* @__PURE__ */ jsx(
|
|
4124
|
+
SidebarListItem,
|
|
4125
|
+
{
|
|
4126
|
+
icon: item.icon,
|
|
4127
|
+
label: item.label,
|
|
4128
|
+
isActive,
|
|
4129
|
+
onClick: () => navigate(item.to)
|
|
4130
|
+
},
|
|
4131
|
+
item.to
|
|
4132
|
+
);
|
|
4133
|
+
}) }) });
|
|
4134
|
+
};
|
|
4135
|
+
var LeadGenSidebar = () => {
|
|
4136
|
+
return /* @__PURE__ */ jsxs(Stack, { gap: 0, style: { height: "100%", display: "flex", flexDirection: "column" }, children: [
|
|
4137
|
+
/* @__PURE__ */ jsx(LeadGenSidebarTop, {}),
|
|
4138
|
+
/* @__PURE__ */ jsx(LeadGenSidebarMiddle, {})
|
|
4139
|
+
] });
|
|
4140
|
+
};
|
|
4141
|
+
|
|
4142
|
+
// src/features/lead-gen/manifest.ts
|
|
4143
|
+
var leadGenManifest = {
|
|
4144
|
+
key: "lead-gen",
|
|
4145
|
+
label: "Lead Gen",
|
|
4146
|
+
sidebar: LeadGenSidebar,
|
|
4147
|
+
subshellRoutes: ["/lead-gen"]
|
|
4148
|
+
};
|
|
4149
|
+
var SEOSidebarTop = () => {
|
|
4150
|
+
return /* @__PURE__ */ jsx(SubshellSidebarSection, { icon: IconSearch, label: "SEO" });
|
|
4151
|
+
};
|
|
4152
|
+
var NAV_ITEMS = [
|
|
4153
|
+
{ label: "SEO Pages", to: "/seo", icon: IconChartBar, exact: true },
|
|
4154
|
+
{ label: "Metrics", to: "/seo/metrics", icon: IconTrendingUp, exact: false }
|
|
4155
|
+
];
|
|
4156
|
+
var SEOSidebarMiddle = () => {
|
|
4157
|
+
const { currentPath, navigate } = useRouterContext();
|
|
4158
|
+
return /* @__PURE__ */ jsx(Stack, { gap: "xs", p: "sm", style: { flex: 1, overflowY: "auto" }, children: NAV_ITEMS.map((item) => {
|
|
4159
|
+
const isActive = item.exact ? currentPath === item.to || currentPath === `${item.to}/` : currentPath.startsWith(item.to);
|
|
4160
|
+
return /* @__PURE__ */ jsx(
|
|
4161
|
+
SidebarListItem,
|
|
4162
|
+
{
|
|
4163
|
+
icon: item.icon,
|
|
4164
|
+
label: item.label,
|
|
4165
|
+
isActive,
|
|
4166
|
+
onClick: () => navigate(item.to)
|
|
4167
|
+
},
|
|
4168
|
+
item.to
|
|
4169
|
+
);
|
|
4170
|
+
}) });
|
|
4171
|
+
};
|
|
4172
|
+
var SEOSidebar = () => {
|
|
4173
|
+
return /* @__PURE__ */ jsxs(Stack, { gap: 0, style: { height: "100%", display: "flex", flexDirection: "column" }, children: [
|
|
4174
|
+
/* @__PURE__ */ jsx(SEOSidebarTop, {}),
|
|
4175
|
+
/* @__PURE__ */ jsx(SEOSidebarMiddle, {})
|
|
4176
|
+
] });
|
|
4177
|
+
};
|
|
4178
|
+
|
|
4179
|
+
// src/features/seo/manifest.ts
|
|
4180
|
+
var seoManifest = {
|
|
4181
|
+
key: "seo",
|
|
4182
|
+
label: "SEO",
|
|
4183
|
+
sidebar: SEOSidebar,
|
|
4184
|
+
subshellRoutes: ["/seo"]
|
|
4185
|
+
};
|
|
4049
4186
|
function ringColor(completed, total) {
|
|
4050
4187
|
if (total === 0) return "gray";
|
|
4051
4188
|
const pct = completed / total;
|
|
@@ -4345,10 +4482,13 @@ var ProjectsSidebarMiddle = ({ currentPath, onNavigate }) => {
|
|
|
4345
4482
|
] })
|
|
4346
4483
|
] });
|
|
4347
4484
|
};
|
|
4348
|
-
var ProjectsSidebar = ({ currentPath, onNavigate }) => {
|
|
4485
|
+
var ProjectsSidebar = ({ currentPath, onNavigate } = {}) => {
|
|
4486
|
+
const { currentPath: routerCurrentPath, navigate } = useRouterContext();
|
|
4487
|
+
const resolvedCurrentPath = currentPath ?? routerCurrentPath;
|
|
4488
|
+
const resolvedNavigate = onNavigate ?? navigate;
|
|
4349
4489
|
return /* @__PURE__ */ jsxs(Stack, { gap: 0, style: { height: "100%", display: "flex", flexDirection: "column" }, children: [
|
|
4350
4490
|
/* @__PURE__ */ jsx(ProjectsSidebarTop, {}),
|
|
4351
|
-
/* @__PURE__ */ jsx(ProjectsSidebarMiddle, { currentPath, onNavigate })
|
|
4491
|
+
/* @__PURE__ */ jsx(ProjectsSidebarMiddle, { currentPath: resolvedCurrentPath, onNavigate: resolvedNavigate })
|
|
4352
4492
|
] });
|
|
4353
4493
|
};
|
|
4354
4494
|
var taskStatusOptions = [
|
|
@@ -4486,6 +4626,18 @@ function UpcomingMilestonesPage() {
|
|
|
4486
4626
|
] }) })
|
|
4487
4627
|
] }) });
|
|
4488
4628
|
}
|
|
4629
|
+
var deliveryManifest = {
|
|
4630
|
+
key: "delivery",
|
|
4631
|
+
label: "Projects",
|
|
4632
|
+
navEntry: {
|
|
4633
|
+
label: "Projects",
|
|
4634
|
+
icon: IconBriefcase,
|
|
4635
|
+
link: "/projects",
|
|
4636
|
+
featureKey: "delivery"
|
|
4637
|
+
},
|
|
4638
|
+
sidebar: ProjectsSidebar,
|
|
4639
|
+
subshellRoutes: ["/projects"]
|
|
4640
|
+
};
|
|
4489
4641
|
function NotificationPanel({ notifications, isLoading, onClose, onNavigate }) {
|
|
4490
4642
|
const markAllAsRead = useMarkAllAsRead();
|
|
4491
4643
|
const hasUnread = notifications.some((n) => !n.read);
|
|
@@ -4538,4 +4690,4 @@ function NotificationBell({ unreadCount, onNavigate }) {
|
|
|
4538
4690
|
] });
|
|
4539
4691
|
}
|
|
4540
4692
|
|
|
4541
|
-
export { AbsoluteScheduleForm, ActivityFeedWidget, AllTasksPage, ApiKeyDisplayModal, ApiKeyList, ApiKeySettings, Breadcrumbs, CrashErrorFallback, CreateApiKeyModal, CreateScheduleModal, CrmOverview, DEAL_STAGES, DEFAULT_KANBAN_CONFIG, DealDrawer, DealKanbanCard, DeleteScheduleModal, DeploymentDetailModal, DeploymentList, DeploymentSettings, DeploymentStatusBadge, DocTreeNav, EditApiKeyModal, ErrorReportCard, HealthStatusCard, KanbanBoard, KnowledgeBasePage, MdxRenderer, MetricsStrip, MilestoneTimeline, MyTasksPanel, NotificationBell, NotificationPanel, PIPELINE_FUNNEL_ORDER, PipelineFunnelWidget, ProjectsSidebar, ProjectsSidebarMiddle, ProjectsSidebarTop, QuickCreateActions, RecurringScheduleForm, RelativeScheduleForm, RichTextEditor, SAVED_VIEW_PRESETS, SavedViewsPanel, ScheduleCard, ScheduleDetailModal, ScheduleTypeSelector, SortableHeader, TableSelectionToolbar, TaskCard, TaskScheduler, TasksDueWidget, UpcomingMilestonesPage, buildErrorReport, calculateProgress, formatStatusLabel, mdxComponents, milestoneStatusColors, noteTypeColors, projectStatusColors, taskStatusColors, taskTypeColors, useCrmPipelineSummary, useCrmQuickMetrics, useRecentCrmActivity };
|
|
4693
|
+
export { AbsoluteScheduleForm, ActivityFeedWidget, AllTasksPage, ApiKeyDisplayModal, ApiKeyList, ApiKeySettings, Breadcrumbs, CrashErrorFallback, CreateApiKeyModal, CreateScheduleModal, CrmOverview, CrmSidebar, CrmSidebarMiddle, CrmSidebarTop, DEAL_STAGES, DEFAULT_KANBAN_CONFIG, DealDrawer, DealKanbanCard, DeleteScheduleModal, DeploymentDetailModal, DeploymentList, DeploymentSettings, DeploymentStatusBadge, DocTreeNav, EditApiKeyModal, ErrorReportCard, HealthStatusCard, KanbanBoard, KnowledgeBasePage, LeadGenSidebar, LeadGenSidebarMiddle, LeadGenSidebarTop, MdxRenderer, MetricsStrip, MilestoneTimeline, MyTasksPanel, NotificationBell, NotificationPanel, PIPELINE_FUNNEL_ORDER, PipelineFunnelWidget, ProjectsSidebar, ProjectsSidebarMiddle, ProjectsSidebarTop, QuickCreateActions, RecurringScheduleForm, RelativeScheduleForm, RichTextEditor, SAVED_VIEW_PRESETS, SEOSidebar, SEOSidebarMiddle, SEOSidebarTop, SavedViewsPanel, ScheduleCard, ScheduleDetailModal, ScheduleTypeSelector, SortableHeader, TableSelectionToolbar, TaskCard, TaskScheduler, TasksDueWidget, UpcomingMilestonesPage, buildErrorReport, calculateProgress, crmManifest, deliveryManifest, formatStatusLabel, leadGenManifest, mdxComponents, milestoneStatusColors, noteTypeColors, projectStatusColors, seoManifest, taskStatusColors, taskTypeColors, useCrmPipelineSummary, useCrmQuickMetrics, useRecentCrmActivity };
|