@elevasis/ui 1.26.1 → 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.
Files changed (53) hide show
  1. package/dist/charts/index.js +2 -2
  2. package/dist/{chunk-AWT255UH.js → chunk-2IJCM3VQ.js} +37 -37
  3. package/dist/chunk-5COLSYBE.js +199 -0
  4. package/dist/{chunk-RMPXGBNI.js → chunk-5JSR6TL5.js} +2 -2
  5. package/dist/chunk-BAGYETKM.js +635 -0
  6. package/dist/{chunk-L3GVDMCA.js → chunk-C27LLJM6.js} +3 -195
  7. package/dist/{chunk-O4UB5DQQ.js → chunk-F2J7675J.js} +1 -1
  8. package/dist/chunk-ITCEULI5.js +238 -0
  9. package/dist/{chunk-4WKWLFBZ.js → chunk-P5EWG45B.js} +1 -1
  10. package/dist/{chunk-BS4J2LAW.js → chunk-QTD5HPKD.js} +1 -1
  11. package/dist/{chunk-ZVJKIJFG.js → chunk-RCQPWA5X.js} +13 -42
  12. package/dist/chunk-TLAIQC7B.js +6382 -0
  13. package/dist/{chunk-FEZZ3IDU.js → chunk-TXPUIHX2.js} +10 -10
  14. package/dist/{chunk-L4XXM55J.js → chunk-W4VYXIN7.js} +142 -3
  15. package/dist/chunk-WJ7W7JU4.js +2115 -0
  16. package/dist/{chunk-YNGQ7U5H.js → chunk-WLNEJ6JJ.js} +2 -2
  17. package/dist/{chunk-4INR75ZS.js → chunk-Y2SYGFRF.js} +589 -65
  18. package/dist/components/index.d.ts +333 -73
  19. package/dist/components/index.js +838 -686
  20. package/dist/features/auth/index.d.ts +125 -0
  21. package/dist/features/auth/index.js +2 -2
  22. package/dist/features/dashboard/index.d.ts +28 -2
  23. package/dist/features/dashboard/index.js +21 -635
  24. package/dist/features/monitoring/index.d.ts +28 -1
  25. package/dist/features/monitoring/index.js +19 -529
  26. package/dist/features/operations/index.d.ts +51 -8
  27. package/dist/features/operations/index.js +25 -3760
  28. package/dist/features/settings/index.d.ts +153 -1
  29. package/dist/features/settings/index.js +19 -1438
  30. package/dist/hooks/index.d.ts +262 -25
  31. package/dist/hooks/index.js +12 -8
  32. package/dist/hooks/published.d.ts +137 -25
  33. package/dist/hooks/published.js +11 -7
  34. package/dist/index.d.ts +310 -28
  35. package/dist/index.js +12 -11
  36. package/dist/initialization/index.d.ts +125 -0
  37. package/dist/layout/index.d.ts +2 -0
  38. package/dist/layout/index.js +6 -5
  39. package/dist/organization/index.js +1 -2
  40. package/dist/profile/index.d.ts +125 -0
  41. package/dist/provider/index.d.ts +48 -3
  42. package/dist/provider/index.js +10 -4
  43. package/dist/provider/published.d.ts +48 -3
  44. package/dist/provider/published.js +8 -2
  45. package/dist/supabase/index.d.ts +242 -0
  46. package/dist/theme/index.js +2 -2
  47. package/dist/types/index.d.ts +126 -1
  48. package/package.json +3 -3
  49. package/dist/chunk-LR4WVA7W.js +0 -682
  50. package/dist/chunk-R7WLWGPO.js +0 -126
  51. package/dist/chunk-TCKIAHDC.js +0 -2626
  52. package/dist/chunk-V7XHGJQZ.js +0 -145
  53. package/dist/{chunk-WWEMNIHW.js → chunk-YYBM5LNJ.js} +1 -1
@@ -1,43 +1,46 @@
1
1
  import { useBreadcrumbs } from '../chunk-MG3NF7QL.js';
2
- import { SubshellContainer, SubshellSidebar, SubshellRightSideContainer, SubshellContentContainer } from '../chunk-L3GVDMCA.js';
3
- import { NotificationList } from '../chunk-4INR75ZS.js';
4
- export { ActivityCard, ActivityFilters as ActivityFiltersBar, ActivityTable, BusinessImpactCard, CostBreakdownCard, CostByModelTable, CostMetricsCard, ErrorAnalysisCard, ErrorBreakdownTable, ExecutionBreakdownTable, ExecutionHealthCard, ExecutionLogsFilters as ExecutionLogsFilterBar, ExecutionLogsTable, NotificationItem, NotificationList } from '../chunk-4INR75ZS.js';
5
- export { CreateCredentialModal, CredentialList, CredentialSettings, MembershipFeaturePanel, MembershipStatusBadge, OAuthConnectModal, OrganizationMembershipsList, WebhookUrlDisplayModal } from '../chunk-LR4WVA7W.js';
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-TCKIAHDC.js';
10
- import { SubshellLoader, PageContainer, SubshellSidebarSection, SidebarListItem, CollapsibleSidebarGroup } from '../chunk-AWT255UH.js';
11
- export { ResourceHealthPanel } from '../chunk-4WKWLFBZ.js';
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-O4UB5DQQ.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, 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-WWEMNIHW.js';
21
- import '../chunk-QJ2S46NI.js';
22
- import { useUpdateApiKey, useDeleteApiKey, useCreateApiKey, useListApiKeys, useActivateDeployment, useDeactivateDeployment, useDeleteDeployment, useListDeployments, useTasks, useProjects, useMilestones } from '../chunk-YNGQ7U5H.js';
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-ZVJKIJFG.js';
24
- export { showApiErrorNotification, showErrorNotification, showInfoNotification, showSuccessNotification, showWarningNotification } from '../chunk-ZVJKIJFG.js';
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-L4XXM55J.js';
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-FEZZ3IDU.js';
33
+ import '../chunk-TXPUIHX2.js';
36
34
  import '../chunk-CYXZHBP4.js';
37
- import '../chunk-R7WLWGPO.js';
35
+ import '../chunk-ITCEULI5.js';
36
+ import { SubshellContainer, SubshellSidebar, SubshellRightSideContainer, SubshellContentContainer } from '../chunk-5COLSYBE.js';
38
37
  import '../chunk-NVOCKXUQ.js';
39
- import '../chunk-V7XHGJQZ.js';
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, IconTrophy, IconClockExclamation, IconUser, IconHeartbeat, IconFlag, IconFileText, IconInbox, IconLock, IconChevronRight, IconDownload, IconBriefcase, IconMessageCircle, IconBell, IconNotes, IconFolderOpen, IconFolder, IconCheckbox, IconMail, IconPhone, IconArrowRight, IconNote } from '@tabler/icons-react';
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: _config, onClick, onDragStart, onDragEnd }) {
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
- Card,
3060
+ Paper,
3057
3061
  {
3058
- padding: "sm",
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
- proposal_sent: { color: "orange" },
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, { style: { height: "100%" }, children: [
3312
+ /* @__PURE__ */ jsxs(Stack, { children: [
3330
3313
  /* @__PURE__ */ jsx(PageTitleCaption, { title: "Pipeline", caption: "Kanban view of your deal pipeline" }),
3331
- /* @__PURE__ */ jsx(ScrollArea, { style: { flex: 1 }, 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) => {
3332
- const columnDeals = groupedDeals.get(columnKey) ?? [];
3333
- const isUnstaged = columnKey === UNSTAGED_KEY;
3334
- const isDragTarget = dragOverColumn === columnKey;
3335
- const badgeColor = isUnstaged ? "gray" : config[columnKey]?.color ?? "gray";
3336
- return /* @__PURE__ */ jsx(
3337
- Paper,
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 PIPELINE_FUNNEL_ORDER = [
3385
- "interested",
3386
- "booked",
3387
- "qualified",
3388
- "demo_booked",
3389
- "proposal",
3390
- "proposal_sent",
3391
- "proposal_signed",
3392
- "payment_sent",
3393
- "closed_won",
3394
- "proposal_revision",
3395
- "closed_lost",
3396
- "nurturing",
3397
- "cancelled",
3398
- "no_show"
3399
- ];
3400
- var defaultValueOf = (deal) => deal.initial_fee ?? 0;
3401
- function useCrmPipelineSummary(opts) {
3402
- const getValue = opts?.getDealValue ?? defaultValueOf;
3403
- const { data: deals, isLoading, error } = useDeals();
3404
- const data = useMemo(() => {
3405
- const dealList = deals ?? [];
3406
- const stageMap = /* @__PURE__ */ new Map();
3407
- for (const stage of PIPELINE_FUNNEL_ORDER) {
3408
- stageMap.set(stage, { count: 0, totalValue: 0 });
3409
- }
3410
- for (const deal of dealList) {
3411
- const stage = deal.cached_stage;
3412
- if (!stage || !stageMap.has(stage)) continue;
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
- return PIPELINE_FUNNEL_ORDER.map((stage) => {
3418
- const entry = stageMap.get(stage);
3419
- return { stage, count: entry.count, totalValue: entry.totalValue };
3420
- });
3421
- }, [deals, getValue]);
3422
- return { data, isLoading, error };
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 useCrmQuickMetrics() {
3435
- const { data: deals, isLoading, error } = useDeals();
3436
- const data = useMemo(() => {
3437
- const dealList = deals ?? [];
3438
- if (dealList.length === 0) return ZERO_METRICS;
3439
- let openDeals = 0;
3440
- let wonDeals = 0;
3441
- let lostDeals = 0;
3442
- let wonFeeSum = 0;
3443
- let wonFeeCount = 0;
3444
- let pipelineValue = 0;
3445
- for (const deal of dealList) {
3446
- const stage = deal.cached_stage;
3447
- const isOpen = !stage || !OPEN_EXCLUDED.includes(stage);
3448
- const isWon = stage === "closed_won";
3449
- const isLost = stage === "closed_lost";
3450
- if (isOpen) {
3451
- openDeals += 1;
3452
- pipelineValue += deal.initial_fee ?? 0;
3453
- }
3454
- if (isWon) {
3455
- wonDeals += 1;
3456
- if (deal.initial_fee != null) {
3457
- wonFeeSum += deal.initial_fee;
3458
- wonFeeCount += 1;
3459
- }
3460
- }
3461
- if (isLost) {
3462
- lostDeals += 1;
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
- const winRateDenominator = wonDeals + lostDeals;
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 useRecentCrmActivity(opts) {
3480
- const { apiRequest, isReady, organizationId } = useElevasisServices();
3481
- const limit = opts?.limit ?? 20;
3482
- const query = useQuery({
3483
- queryKey: ["recent-crm-activity", organizationId, limit],
3484
- queryFn: () => apiRequest(`/crm/recent-activity?limit=${limit}`),
3485
- enabled: isReady
3486
- });
3487
- return {
3488
- data: query.data?.entries ?? [],
3489
- isLoading: query.isLoading,
3490
- error: query.error
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 currencyFormatter = new Intl.NumberFormat("en-US", {
3494
- style: "currency",
3495
- currency: "USD",
3496
- maximumFractionDigits: 0
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 PipelineFunnelWidget({ onStageClick, getDealValue }) {
3515
- const { data, isLoading, error } = useCrmPipelineSummary({ getDealValue });
3516
- if (isLoading) {
3517
- return /* @__PURE__ */ jsx(Paper, { withBorder: true, p: "md", children: /* @__PURE__ */ jsx(Center, { p: "xl", children: /* @__PURE__ */ jsx(Loader, {}) }) });
3518
- }
3519
- if (error) {
3520
- return /* @__PURE__ */ jsx(Paper, { withBorder: true, p: "md", children: /* @__PURE__ */ jsx(CenteredErrorState, { error, title: "Failed to load pipeline data" }) });
3521
- }
3522
- const totalDeals = data.reduce((sum, s) => sum + s.count, 0);
3523
- const maxCount = Math.max(...data.map((s) => s.count), 1);
3524
- if (totalDeals === 0) {
3525
- return /* @__PURE__ */ jsxs(Paper, { withBorder: true, p: "md", children: [
3526
- /* @__PURE__ */ jsx(CardHeader, { icon: /* @__PURE__ */ jsx(IconColumns, { size: 16 }), title: "Pipeline" }),
3527
- /* @__PURE__ */ jsx(Center, { h: 200, children: /* @__PURE__ */ jsx(Alert, { icon: /* @__PURE__ */ jsx(IconAlertCircle, {}), color: "gray", variant: "light", children: "No deals in the pipeline yet" }) })
3528
- ] });
3529
- }
3530
- return /* @__PURE__ */ jsxs(Paper, { withBorder: true, p: "md", children: [
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 MetricsStrip() {
3747
- const { data } = useCrmQuickMetrics();
3748
- return /* @__PURE__ */ jsx(Paper, { withBorder: true, p: "md", children: /* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 2, sm: 4 }, children: [
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 CrmOverview({
3756
- onStageClick,
3757
- onDealClick,
3758
- onGoToPipeline,
3759
- getDealValue,
3760
- renderActions
3761
- }) {
3762
- const rightSection = renderActions ? renderActions() : /* @__PURE__ */ jsx(Button, { leftSection: /* @__PURE__ */ jsx(IconColumns, { size: 16 }), variant: "light", size: "sm", onClick: onGoToPipeline, children: "Go to Pipeline" });
3763
- return /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
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
- PageTitleCaption,
3944
+ CardHeader,
3766
3945
  {
3767
- title: "CRM Overview",
3768
- caption: "Pipeline health, tasks, and recent activity at a glance.",
3769
- rightSection
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(MetricsStrip, {}),
3773
- /* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 1, lg: 2 }, spacing: "md", children: [
3774
- /* @__PURE__ */ jsx(PipelineFunnelWidget, { onStageClick, getDealValue }),
3775
- /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
3776
- /* @__PURE__ */ jsx(TasksDueWidget, { onTaskClick: onDealClick }),
3777
- /* @__PURE__ */ jsx(ActivityFeedWidget, { onDealClick })
3778
- ] })
3779
- ] })
3780
- ] });
3781
- }
3782
-
3783
- // src/features/acquisition/workbench/constants.ts
3784
- var SAVED_VIEW_PRESETS = [
3785
- {
3786
- id: "my-deals",
3787
- label: "My Deals",
3788
- iconName: "IconUser",
3789
- target: "/crm/deals"
3790
- },
3791
- {
3792
- id: "overdue",
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
- children: /* @__PURE__ */ jsxs(Group, { gap: "xs", wrap: "nowrap", children: [
3852
- /* @__PURE__ */ jsx(KindIcon2, { size: 14, style: { color: "var(--color-text-dimmed)", flexShrink: 0 } }),
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
- var KIND_OPTIONS = [
3921
- { value: "call", label: "Call" },
3922
- { value: "email", label: "Email" },
3923
- { value: "meeting", label: "Meeting" },
3924
- { value: "other", label: "Other" }
3925
- ];
3926
- function buildDealLabel(deal) {
3927
- const contact = deal.contact;
3928
- if (!contact) return `Deal ${deal.id.slice(0, 8)}`;
3929
- const name = `${contact.first_name ?? ""} ${contact.last_name ?? ""}`.trim();
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 QuickCreateActions({ onNewDeal }) {
3934
- const [open, setOpen] = useState(false);
3935
- const [dealId, setDealId] = useState(null);
3936
- const [title, setTitle] = useState("");
3937
- const [description, setDescription] = useState("");
3938
- const [kind, setKind] = useState("other");
3939
- const [dueAt, setDueAt] = useState("");
3940
- const { data: deals } = useDeals();
3941
- const createTask = useCreateDealTask();
3942
- const dealOptions = (deals ?? []).map((deal) => ({
3943
- value: deal.id,
3944
- label: buildDealLabel(deal)
3945
- }));
3946
- function resetForm() {
3947
- setDealId(null);
3948
- setTitle("");
3949
- setDescription("");
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
- function handleClose() {
3954
- setOpen(false);
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
- async function handleSubmit() {
3958
- if (!dealId || !title) return;
3959
- await createTask.mutateAsync({
3960
- dealId,
3961
- title,
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
- const isSubmitDisabled = !dealId || !title || createTask.isPending;
3969
- return /* @__PURE__ */ jsxs(Fragment, { children: [
3970
- /* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
3971
- /* @__PURE__ */ jsx(Text, { fz: "xs", tt: "uppercase", c: "dimmed", fw: 600, children: "QUICK CREATE" }),
3972
- /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
3973
- /* @__PURE__ */ jsx(Button, { variant: "light", size: "xs", leftSection: /* @__PURE__ */ jsx(IconPlus, { size: 14 }), fullWidth: true, onClick: onNewDeal, children: "New Deal" }),
3974
- /* @__PURE__ */ jsx(
3975
- Button,
3976
- {
3977
- variant: "light",
3978
- size: "xs",
3979
- leftSection: /* @__PURE__ */ jsx(IconPlus, { size: 14 }),
3980
- fullWidth: true,
3981
- onClick: () => setOpen(true),
3982
- children: "New Task"
3983
- }
3984
- )
3985
- ] })
3986
- ] }),
3987
- /* @__PURE__ */ jsxs(Modal, { opened: open, onClose: handleClose, title: "New Task", size: "md", children: [
3988
- /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
3989
- /* @__PURE__ */ jsx(
3990
- Select,
3991
- {
3992
- label: "Deal",
3993
- placeholder: "Select a deal",
3994
- data: dealOptions,
3995
- value: dealId,
3996
- onChange: setDealId,
3997
- searchable: true,
3998
- required: true
3999
- }
4000
- ),
4001
- /* @__PURE__ */ jsx(
4002
- TextInput,
4003
- {
4004
- label: "Title",
4005
- placeholder: "Task title",
4006
- value: title,
4007
- onChange: (e) => setTitle(e.currentTarget.value),
4008
- required: true
4009
- }
4010
- ),
4011
- /* @__PURE__ */ jsx(
4012
- Textarea,
4013
- {
4014
- label: "Description",
4015
- placeholder: "Optional description",
4016
- value: description,
4017
- onChange: (e) => setDescription(e.currentTarget.value),
4018
- autosize: true,
4019
- minRows: 2,
4020
- maxRows: 5
4021
- }
4022
- ),
4023
- /* @__PURE__ */ jsx(
4024
- Select,
4025
- {
4026
- label: "Kind",
4027
- data: KIND_OPTIONS,
4028
- value: kind,
4029
- onChange: (v) => setKind(v ?? "other")
4030
- }
4031
- ),
4032
- /* @__PURE__ */ jsx(
4033
- TextInput,
4034
- {
4035
- type: "datetime-local",
4036
- label: "Due At",
4037
- value: dueAt,
4038
- onChange: (e) => setDueAt(e.currentTarget.value)
4039
- }
4040
- )
4041
- ] }),
4042
- /* @__PURE__ */ jsxs(Group, { justify: "flex-end", mt: "md", children: [
4043
- /* @__PURE__ */ jsx(Button, { variant: "default", onClick: handleClose, children: "Cancel" }),
4044
- /* @__PURE__ */ jsx(Button, { onClick: handleSubmit, loading: createTask.isPending, disabled: isSubmitDisabled, children: "Create" })
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 };