@elevasis/ui 2.33.2 → 2.35.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 (116) hide show
  1. package/dist/api/index.d.ts +9 -2
  2. package/dist/api/index.js +3 -3
  3. package/dist/app/index.css +452 -0
  4. package/dist/app/index.d.ts +1255 -8
  5. package/dist/app/index.js +151 -13
  6. package/dist/charts/index.js +3 -6
  7. package/dist/chunk-26HFM4MH.js +41449 -0
  8. package/dist/{chunk-52K5RFDH.js → chunk-4U3XAWCN.js} +1328 -2492
  9. package/dist/{chunk-ND5TDV2J.js → chunk-57OZ3AEG.js} +1 -1
  10. package/dist/{chunk-E4WQGJNS.js → chunk-7FPLLSHN.js} +14 -1
  11. package/dist/{chunk-RQA2EVN3.js → chunk-AKW7KISS.js} +39 -3
  12. package/dist/chunk-AUDNF2Q7.js +2050 -0
  13. package/dist/{chunk-TYRUKGGD.js → chunk-GX6XBRRF.js} +1 -2
  14. package/dist/{chunk-V6SZ4ECN.js → chunk-LUYVRATI.js} +257 -6
  15. package/dist/{chunk-X4WBGKJQ.js → chunk-R3VCBZDC.js} +50 -3
  16. package/dist/chunk-SIQ3P4OR.js +1764 -0
  17. package/dist/{chunk-Z2K2EAPL.js → chunk-VDOOGGBA.js} +5 -6
  18. package/dist/{chunk-3FV6HBXS.js → chunk-WF7CONXF.js} +23 -23
  19. package/dist/chunk-YYX7OPZQ.js +25 -0
  20. package/dist/components/index.d.ts +69 -92
  21. package/dist/components/index.js +20 -3216
  22. package/dist/components/navigation/index.js +25 -7
  23. package/dist/execution/index.d.ts +9 -9
  24. package/dist/execution/index.js +1 -2
  25. package/dist/features/auth/index.js +23 -2
  26. package/dist/features/clients/index.js +20 -31
  27. package/dist/features/crm/index.js +20 -35
  28. package/dist/features/dashboard/index.d.ts +68 -91
  29. package/dist/features/dashboard/index.js +20 -33
  30. package/dist/features/delivery/index.js +20 -35
  31. package/dist/features/knowledge/index.js +25 -11
  32. package/dist/features/lead-gen/index.d.ts +9 -9
  33. package/dist/features/lead-gen/index.js +20 -36
  34. package/dist/features/monitoring/index.js +20 -35
  35. package/dist/features/monitoring/requests/index.js +20 -30
  36. package/dist/features/operations/index.d.ts +197 -188
  37. package/dist/features/operations/index.js +18 -42
  38. package/dist/features/seo/index.js +3 -4
  39. package/dist/features/settings/index.js +20 -32
  40. package/dist/graph/index.js +1 -1
  41. package/dist/hooks/delivery/index.js +30 -2
  42. package/dist/hooks/index.d.ts +105 -112
  43. package/dist/hooks/index.js +20 -26
  44. package/dist/hooks/operations/command-view/utils/transformCommandViewData.d.ts +55 -62
  45. package/dist/hooks/published.d.ts +105 -112
  46. package/dist/hooks/published.js +20 -25
  47. package/dist/index.css +532 -532
  48. package/dist/index.d.ts +10261 -6793
  49. package/dist/index.js +22 -31
  50. package/dist/knowledge/index.d.ts +51 -59
  51. package/dist/knowledge/index.js +40 -211
  52. package/dist/{knowledge-search-index-VMAW7FLR.js → knowledge-search-index-ORIJCEZX.js} +3 -3
  53. package/dist/layout/index.js +4 -10
  54. package/dist/organization/index.js +27 -2
  55. package/dist/provider/index.d.ts +71 -52
  56. package/dist/provider/index.js +20 -20
  57. package/dist/provider/published.d.ts +39 -47
  58. package/dist/provider/published.js +20 -15
  59. package/dist/test-utils/index.d.ts +2 -0
  60. package/dist/test-utils/index.js +16 -4
  61. package/dist/test-utils/setup.js +38 -0
  62. package/dist/theme/index.js +2 -3
  63. package/dist/theme/presets/index.d.ts +28 -3
  64. package/dist/theme/presets/index.js +1 -1
  65. package/dist/typeform/index.js +1 -2049
  66. package/dist/types/index.d.ts +88 -95
  67. package/dist/utils/index.d.ts +46 -69
  68. package/dist/utils/index.js +1 -1
  69. package/dist/zustand/index.d.ts +21 -8
  70. package/dist/zustand/index.js +32 -1
  71. package/package.json +5 -5
  72. package/dist/chunk-2ZZ72TAB.js +0 -2281
  73. package/dist/chunk-32I2RCGC.js +0 -85
  74. package/dist/chunk-3MEXPLWT.js +0 -265
  75. package/dist/chunk-3ZMAGTWF.js +0 -18
  76. package/dist/chunk-44I4LOH6.js +0 -1593
  77. package/dist/chunk-4DYOXEH6.js +0 -951
  78. package/dist/chunk-7M2VOCYN.js +0 -1
  79. package/dist/chunk-A4VDJJCV.js +0 -1864
  80. package/dist/chunk-BZZCNLT6.js +0 -12
  81. package/dist/chunk-CLDCYJQT.js +0 -1
  82. package/dist/chunk-E565XMTQ.js +0 -17
  83. package/dist/chunk-EPTHX4VZ.js +0 -749
  84. package/dist/chunk-GWGQI6V4.js +0 -447
  85. package/dist/chunk-HUJCU55S.js +0 -159
  86. package/dist/chunk-IBWMR4TI.js +0 -469
  87. package/dist/chunk-IIMU5YAJ.js +0 -53
  88. package/dist/chunk-IOXOPMYS.js +0 -145
  89. package/dist/chunk-J2UD7BOH.js +0 -347
  90. package/dist/chunk-JA5ECJJB.js +0 -387
  91. package/dist/chunk-JFL3GRD4.js +0 -39
  92. package/dist/chunk-KW7ZNQD7.js +0 -126
  93. package/dist/chunk-LGKLC5MG.js +0 -44
  94. package/dist/chunk-N55DVMAG.js +0 -14
  95. package/dist/chunk-O56ESZCQ.js +0 -1874
  96. package/dist/chunk-OIBHQH5Q.js +0 -96
  97. package/dist/chunk-PDHTXPSF.js +0 -12
  98. package/dist/chunk-QDFJSUG3.js +0 -13
  99. package/dist/chunk-R2XR4FCV.js +0 -48
  100. package/dist/chunk-R66W5UDG.js +0 -26
  101. package/dist/chunk-RYTEQBAO.js +0 -37
  102. package/dist/chunk-SZHARWKU.js +0 -15
  103. package/dist/chunk-T3J6U77J.js +0 -12056
  104. package/dist/chunk-TBVLQRXT.js +0 -68
  105. package/dist/chunk-TGVAIWIL.js +0 -1778
  106. package/dist/chunk-TKAYX2SP.js +0 -204
  107. package/dist/chunk-TUMSNGTX.js +0 -35
  108. package/dist/chunk-VGU4ZFYZ.js +0 -4752
  109. package/dist/chunk-VNAZTCHA.js +0 -65
  110. package/dist/chunk-VNFR57DF.js +0 -87
  111. package/dist/chunk-WKW6B5ID.js +0 -29
  112. package/dist/chunk-XCYKC6OZ.js +0 -1
  113. package/dist/chunk-XZGSCABI.js +0 -383
  114. package/dist/chunk-ZMK5Z6KE.js +0 -5198
  115. /package/dist/{chunk-2RJMVWFJ.js → chunk-GEFWMU26.js} +0 -0
  116. /package/dist/{chunk-22UVE3RA.js → chunk-HENXLGVD.js} +0 -0
@@ -1,3229 +1,33 @@
1
- import { useBreadcrumbs } from '../chunk-32I2RCGC.js';
2
- import { showAuthError } from '../chunk-2ZZ72TAB.js';
3
- export { CreateCredentialModal, CredentialList, CredentialSettings, MembershipFeaturePanel, MembershipStatusBadge, OAuthConnectModal, OrganizationMembershipsList, WebhookUrlDisplayModal, settingsManifest, showAuthError } from '../chunk-2ZZ72TAB.js';
4
- import '../chunk-V6SZ4ECN.js';
5
- import { setDealReferrer, compareDealsByPriority } from '../chunk-A4VDJJCV.js';
6
- export { ActivityFeedWidget, CompanyDetailPage, ContactDetailPage, CrmOverview, CrmSidebar, CrmSidebarTop, DealDetailPage, DealsListPage, MetricsStrip, PIPELINE_FUNNEL_ORDER, PipelineFunnelWidget, crmManifest, useCrmPipelineSummary, useCrmQuickMetrics, useRecentCrmActivity } from '../chunk-A4VDJJCV.js';
7
- export { AllTasksPage, HealthStatusCard, MilestoneTimeline, ProjectDetailPage, ProjectsListPage, ProjectsSidebar, ProjectsSidebarMiddle, ProjectsSidebarTop, TaskCard, UpcomingMilestonesPage, deliveryManifest } from '../chunk-44I4LOH6.js';
8
- export { LEAD_GEN_ROUTE_LINKS, LeadGenCompaniesPage, LeadGenContactsPage, LeadGenListDetailPage, LeadGenListsPage, LeadGenOverviewPage, LeadGenRouteShell, LeadGenSidebar, LeadGenSidebarTop, StepConfigForm, TabSection, getEnrichmentColor, getStatusColor, leadGenManifest, useDeleteLists } from '../chunk-VGU4ZFYZ.js';
9
- export { CrmSidebarMiddle, LeadGenSidebarMiddle, MyTasksPanel, QuickCreateActions, SAVED_VIEW_PRESETS, SavedViewsPanel } from '../chunk-J2UD7BOH.js';
10
- export { ActionModal, AgentDefinitionDisplay, AgentExecutionLogs, BaseExecutionLogs, BaseExecutionLogsHeader, BaseExecutionLogsStates, CheckpointGroup, CollapsibleJsonSection, CommandQueueSidebar, CommandQueueSidebarMiddle, CommandQueueSidebarTop, CommandQueueTaskRow, ConfigCard, ContentSections, ContextUsageBadge, ContractDisplay, ExecutionErrorSection, LogEntry, LogGroup, NewKnowledgeMapEdge, NewKnowledgeMapGraph, NewKnowledgeMapNode, OperationsSidebar, OperationsSidebarMiddle, OperationsSidebarTop, ResourceDefinitionSection, ResourceErrorState, ResourceFilter, ResourceHeader, ResourceNotFoundState, SessionMemory, ToolsListDisplay, WorkflowDefinitionDisplay, WorkflowExecutionLogs, getExecutionStatusConfig, getIcon, getLogLevelConfig, iconMap, operationsManifest, useNewKnowledgeMapLayout } from '../chunk-T3J6U77J.js';
1
+ export { AbsoluteScheduleForm, ActionModal, ActivityCard, ActivityFeedWidget, ActivityFilters as ActivityFiltersBar, ActivityTable, AgentDefinitionDisplay, AgentExecutionLogs, AgentExecutionTimeline, AgentExecutionVisualizer, AgentIterationEdge, AgentIterationNode, AllTasksPage, ApiKeyDisplayModal, ApiKeyList, ApiKeySettings, AppErrorBoundary, BaseEdge, BaseExecutionLogs, BaseExecutionLogsHeader, BaseExecutionLogsStates, BaseNode, Breadcrumbs, BusinessImpactCard, Can, CheckpointGroup, CollapsibleJsonSection, CommandQueueSidebar, CommandQueueSidebarMiddle, CommandQueueSidebarTop, CommandQueueTaskRow, CompanyDetailPage, ConfigCard, ConfirmationInputModal, ConfirmationModal, ContactDetailPage, ContentSections, ContextUsageBadge, ContractDisplay, CostBreakdownCard, CostByModelTable, CostMetricsCard, CrashErrorFallback, CreateApiKeyModal, CreateCredentialModal, CreateRoleModal, CreateScheduleModal, CredentialList, CredentialSettings, CrmOverview, CrmSidebar, CrmSidebarMiddle, CrmSidebarTop, CustomModal, DEAL_STAGES, DEFAULT_KANBAN_CONFIG, DealDetailPage, DealKanbanCard, DealsListPage, DeleteScheduleModal, DeploymentDetailModal, DeploymentList, DeploymentSettings, DeploymentStatusBadge, EditApiKeyModal, EmptyVisualizer, ErrorAnalysisCard, ErrorBreakdownTable, ErrorReportCard, ExecutionBreakdownTable, ExecutionErrorSection, ExecutionHealthCard, ExecutionLogsFilters as ExecutionLogsFilterBar, ExecutionLogsTable, ExecutionStats, ExecutionStatusBadge, FilterBar, GraphBackground, GraphContainer, GraphFitViewButton, GraphFitViewHandler, GraphLegend, HealthStatusCard, KanbanBoard, LEAD_GEN_ROUTE_LINKS, LeadGenCompaniesPage, LeadGenContactsPage, LeadGenListDetailPage, LeadGenListsPage, LeadGenOverviewPage, LeadGenRouteShell, LeadGenSidebar, LeadGenSidebarMiddle, LeadGenSidebarTop, LogEntry, LogGroup, MdxRenderer, MembershipFeaturePanel, MembershipStatusBadge, MetricsStrip, MilestoneTimeline, MyTasksPanel, NewKnowledgeMapEdge, NewKnowledgeMapGraph, NewKnowledgeMapNode, NoAccessState, NotificationBell, NotificationItem, NotificationList, NotificationPanel, OAuthConnectModal, OperationsSidebar, OperationsSidebarMiddle, OperationsSidebarTop, OrganizationMembershipsList, PIPELINE_FUNNEL_ORDER, PermissionMatrix, PipelineFunnelWidget, ProjectDetailPage, ProjectsListPage, ProjectsSidebar, ProjectsSidebarMiddle, ProjectsSidebarTop, QuickCreateActions, RecurringScheduleForm, RelativeScheduleForm, ResourceDefinitionSection, ResourceErrorState, ResourceFilter, ResourceHeader, ResourceHealthChart, ResourceHealthPanel, ResourceNotFoundState, RichTextEditor, RoleBadge, RunResourceButton, SAVED_VIEW_PRESETS, SavedViewsPanel, ScheduleCard, ScheduleDetailModal, ScheduleTypeSelector, SessionMemory, SortableHeader, StepConfigForm, TabSection, TableSelectionToolbar, TaskCard, TaskScheduler, TimelineAxis, TimelineBar, TimelineContainer, TimelineRow, ToolsListDisplay, UnifiedWorkflowEdge, UnifiedWorkflowGraph, UnifiedWorkflowNode, UpcomingMilestonesPage, VisualizerContainer, WebhookUrlDisplayModal, WorkflowDefinitionDisplay, WorkflowExecutionLogs, WorkflowExecutionTimeline, ZodFormRenderer, buildErrorReport, calculateProgress, crmManifest, deliveryManifest, formatStatusLabel, getEnrichmentColor, getExecutionStatusConfig, getGraphBackgroundStyles, getHealthColor, getIcon, getLogLevelConfig, getStatusColor, iconMap, leadGenManifest, mdxComponents, milestoneStatusColors, monitoringManifest, noteTypeColors, operationsManifest, projectStatusColors, settingsManifest, showApiErrorNotification, showAuthError, showErrorNotification, showInfoNotification, showSuccessNotification, showWarningNotification, taskStatusColors, taskTypeColors, useCrmPipelineSummary, useCrmQuickMetrics, useDeleteLists, useGraphBackgroundStyles, useGraphTheme, useNewKnowledgeMapLayout, useRecentCrmActivity } from '../chunk-26HFM4MH.js';
11
2
  import '../chunk-CXY7FMUM.js';
3
+ export { APIErrorAlert, ActivityTimeline, CardHeader, CenteredErrorState, CollapsibleSection, ContextViewer, CustomSelector, DetailCardSkeleton, ElevasisLoader, EmptyState, FeatureUnavailableState, GlowDot, JsonViewer, ListSkeleton, PageNotFound, PageTitleCaption, ResourceCard, StatCard, StatCardSkeleton, StatsCardSkeleton, StatusBadge, TabCountBadge, TimeRangeSelector, TrendIndicator } from '../chunk-SIQ3P4OR.js';
4
+ export { StyledMarkdown } from '../chunk-3KMDHCAR.js';
5
+ import '../chunk-AKW7KISS.js';
6
+ import '../chunk-WF7CONXF.js';
12
7
  import '../chunk-ZTWA5H77.js';
13
- import '../chunk-KW7ZNQD7.js';
14
- import '../chunk-N55DVMAG.js';
15
- import '../chunk-CLDCYJQT.js';
16
- export { AgentExecutionTimeline, AgentExecutionVisualizer, AgentIterationEdge, AgentIterationNode, BaseEdge, BaseNode, EmptyVisualizer, ExecutionStats, ExecutionStatusBadge, GraphBackground, GraphContainer, GraphFitViewButton, GraphFitViewHandler, GraphLegend, RunResourceButton, TimelineAxis, TimelineBar, TimelineContainer, TimelineRow, UnifiedWorkflowEdge, UnifiedWorkflowGraph, UnifiedWorkflowNode, VisualizerContainer, WorkflowExecutionTimeline, getGraphBackgroundStyles, useGraphBackgroundStyles, useGraphTheme } from '../chunk-O56ESZCQ.js';
17
- export { ZodFormRenderer } from '../chunk-3MEXPLWT.js';
18
- import { NotificationList } from '../chunk-TGVAIWIL.js';
19
- export { ActivityCard, ActivityFilters as ActivityFiltersBar, ActivityTable, BusinessImpactCard, CostBreakdownCard, CostByModelTable, CostMetricsCard, ErrorAnalysisCard, ErrorBreakdownTable, ExecutionBreakdownTable, ExecutionHealthCard, ExecutionLogsFilters as ExecutionLogsFilterBar, ExecutionLogsTable, NotificationItem, NotificationList, monitoringManifest } from '../chunk-TGVAIWIL.js';
20
- export { ResourceHealthPanel } from '../chunk-TBVLQRXT.js';
21
- export { ResourceHealthChart, getHealthColor } from '../chunk-LGKLC5MG.js';
22
- import '../chunk-4DYOXEH6.js';
23
- export { ConfirmationInputModal, ConfirmationModal } from '../chunk-VNFR57DF.js';
24
- export { SEOSidebar, SEOSidebarMiddle, SEOSidebarTop, seoManifest } from '../chunk-TYRUKGGD.js';
25
- import '../chunk-X4WBGKJQ.js';
26
- import '../chunk-IIMU5YAJ.js';
27
- import { AppShellLoader } from '../chunk-RYTEQBAO.js';
28
- export { calculateProgress, formatStatusLabel, milestoneStatusColors, noteTypeColors, projectStatusColors, taskStatusColors, taskTypeColors } from '../chunk-R2XR4FCV.js';
29
- import '../chunk-BZZCNLT6.js';
30
- export { SortableHeader, TableSelectionToolbar } from '../chunk-TUMSNGTX.js';
31
- import { FilterBar } from '../chunk-PDHTXPSF.js';
32
- export { FilterBar } from '../chunk-PDHTXPSF.js';
33
- import { CustomModal } from '../chunk-R66W5UDG.js';
34
- export { CustomModal } from '../chunk-R66W5UDG.js';
35
- import '../chunk-JFL3GRD4.js';
36
- export { Graph_module_css_default as graphStyles } from '../chunk-22UVE3RA.js';
37
- export { CONTAINER_CONSTANTS, SHARED_VIZ_CONSTANTS } from '../chunk-E4WQGJNS.js';
38
- import '../chunk-7M2VOCYN.js';
39
- import { useOrganizationPermissions, useCreateOrgRole, usePermissionCatalog, useUpdateApiKey, useDeleteApiKey, useCreateApiKey, useListApiKeys, usePaginationState, useActivateDeployment, useDeactivateDeployment, useDeleteDeployment, useListDeployments, useResources, useCreateSchedule, useListSchedules, usePauseSchedule, useResumeSchedule, useCancelSchedule, useDeleteSchedule, useDeals, useTransitionItem, dealKeys, useMarkAllAsRead, useNotifications } from '../chunk-ZMK5Z6KE.js';
40
- import '../chunk-VNAZTCHA.js';
41
- import { showSuccessNotification, showErrorNotification, showApiErrorNotification } from '../chunk-XZGSCABI.js';
42
- export { showApiErrorNotification, showErrorNotification, showInfoNotification, showSuccessNotification, showWarningNotification } from '../chunk-XZGSCABI.js';
8
+ import '../chunk-AUDNF2Q7.js';
9
+ import '../chunk-6M6OLGQY.js';
43
10
  import '../chunk-BRXELOHC.js';
44
- import '../chunk-3ZMAGTWF.js';
45
- import '../chunk-QDFJSUG3.js';
46
- import '../chunk-OIBHQH5Q.js';
47
- import '../chunk-RQA2EVN3.js';
48
- import '../chunk-3FV6HBXS.js';
49
- import '../chunk-WLOQ4IBG.js';
50
- import '../chunk-WKW6B5ID.js';
51
- import { ListSkeleton, EmptyState, PageTitleCaption, StatCard, CenteredErrorState, CardHeader } from '../chunk-EPTHX4VZ.js';
52
- export { APIErrorAlert, ActivityTimeline, CardHeader, CenteredErrorState, CollapsibleSection, ContextViewer, CustomSelector, DetailCardSkeleton, EmptyState, FeatureUnavailableState, GlowDot, JsonViewer, ListSkeleton, PageNotFound, PageTitleCaption, ResourceCard, StatCard, StatCardSkeleton, StatsCardSkeleton, StatusBadge, TabCountBadge, TimeRangeSelector, TrendIndicator } from '../chunk-EPTHX4VZ.js';
53
- export { StyledMarkdown } from '../chunk-3KMDHCAR.js';
54
- import '../chunk-GWGQI6V4.js';
55
- export { ElevasisLoader } from '../chunk-SZHARWKU.js';
56
- import '../chunk-IBWMR4TI.js';
57
- import { SubshellContentContainer } from '../chunk-TKAYX2SP.js';
11
+ import '../chunk-4U3XAWCN.js';
12
+ import '../chunk-LUYVRATI.js';
58
13
  export { NavigationButton } from '../chunk-NYBEU5TE.js';
59
- import '../chunk-IOXOPMYS.js';
60
- import '../chunk-52K5RFDH.js';
61
- import '../chunk-ND5TDV2J.js';
62
- import '../chunk-2IFYDILW.js';
63
- import { useRouterContext } from '../chunk-Q7DJKLEN.js';
64
- export { RoleBadge } from '../chunk-JA5ECJJB.js';
65
- import '../chunk-HUJCU55S.js';
66
- import { useAppearance } from '../chunk-E565XMTQ.js';
14
+ import '../chunk-WLOQ4IBG.js';
15
+ import '../chunk-57OZ3AEG.js';
67
16
  import '../chunk-JBWJ6WHZ.js';
17
+ export { SEOSidebar, SEOSidebarMiddle, SEOSidebarTop, seoManifest } from '../chunk-GX6XBRRF.js';
18
+ import '../chunk-R3VCBZDC.js';
68
19
  import '../chunk-DT3QYZVU.js';
20
+ import '../chunk-2IFYDILW.js';
21
+ import '../chunk-Q7DJKLEN.js';
22
+ export { Graph_module_css_default as graphStyles } from '../chunk-HENXLGVD.js';
23
+ export { CONTAINER_CONSTANTS, SHARED_VIZ_CONSTANTS } from '../chunk-7FPLLSHN.js';
69
24
  import '../chunk-RNP5R5I3.js';
70
- import { formatDateTime, PAGE_SIZE_DEFAULT, formatTimeAgo } from '../chunk-2RJMVWFJ.js';
25
+ import '../chunk-GEFWMU26.js';
71
26
  import '../chunk-KRWALB24.js';
72
27
  import '../chunk-VKIZUUPM.js';
73
- import { useInitialization } from '../chunk-533DUEQY.js';
28
+ import '../chunk-533DUEQY.js';
74
29
  import '../chunk-DD3CCMCZ.js';
75
30
  import '../chunk-2Q2JQSQO.js';
76
- import { useElevasisServices } from '../chunk-KJ3QUBNU.js';
31
+ import '../chunk-KJ3QUBNU.js';
77
32
  import '../chunk-BRJ3QZ4E.js';
78
33
  import '../chunk-I2KLQ2HA.js';
79
- import * as runtime from 'react/jsx-runtime';
80
- import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
81
- import { Stack, Card, Title, Checkbox, Group, Text, Tooltip, Center, TextInput, Textarea, Loader, Alert, Button, Table, ActionIcon, Paper, Code, CopyButton, SimpleGrid, Badge, Pagination, SegmentedControl, Switch, Select, Divider, Menu, Timeline, ThemeIcon, Tabs, ScrollArea, Breadcrumbs as Breadcrumbs$1, Box, Popover, Indicator } from '@mantine/core';
82
- import { IconLock, IconPencil, IconAlertCircle, IconKey, IconCalendar, IconClock, IconTrash, IconAlertTriangle, IconExclamationMark, IconShieldLock, IconCheck, IconCopy, IconPlus, IconRocket, IconRefresh, IconPower, IconPlayerPlay, IconCircleCheck, IconTag, IconCalendarRepeat, IconCalendarEvent, IconCalendarTime, IconRobot, IconGitBranch, IconSettings, IconExternalLink, IconDotsVertical, IconPlayerPause, IconPlayerStop, IconCalendarDue, IconCalendarStats, IconCalendarOff, IconListCheck, IconBell } from '@tabler/icons-react';
83
- import { useForm } from '@mantine/form';
84
- import { useRef, useEffect, useState, useMemo, useCallback, Component } from 'react';
85
- import { z } from 'zod';
86
- import { useDisclosure } from '@mantine/hooks';
87
- import { useQueryClient, useQuery } from '@tanstack/react-query';
88
- import { Link, RichTextEditor as RichTextEditor$1 } from '@mantine/tiptap';
89
- import { useEditor } from '@tiptap/react';
90
- import Placeholder from '@tiptap/extension-placeholder';
91
- import StarterKit from '@tiptap/starter-kit';
92
- import { Prism } from 'react-syntax-highlighter';
93
- import { oneDark } from 'react-syntax-highlighter/dist/esm/styles/prism';
94
- import { useNavigate } from '@tanstack/react-router';
95
-
96
- function Can({ permission, fallback = null, children }) {
97
- const { hasPermission, isReady } = useOrganizationPermissions();
98
- if (!isReady) return null;
99
- return hasPermission(permission) ? /* @__PURE__ */ jsx(Fragment, { children }) : /* @__PURE__ */ jsx(Fragment, { children: fallback });
100
- }
101
- function groupByDomain(catalog) {
102
- const groups = /* @__PURE__ */ new Map();
103
- for (const row of catalog) {
104
- const domain = row.key.split(".")[0] ?? row.key;
105
- const existing = groups.get(domain);
106
- if (existing) {
107
- existing.push(row);
108
- } else {
109
- groups.set(domain, [row]);
110
- }
111
- }
112
- return groups;
113
- }
114
- var DOMAIN_LABELS = {
115
- org: "Organization",
116
- members: "Members",
117
- roles: "Roles",
118
- secrets: "Secrets",
119
- operations: "Operations",
120
- work: "Work"
121
- };
122
- function domainLabel(domain) {
123
- return DOMAIN_LABELS[domain] ?? domain.charAt(0).toUpperCase() + domain.slice(1);
124
- }
125
- function PermissionMatrix({ catalog, selectedKeys, onChange, systemLocked }) {
126
- const groups = groupByDomain(catalog);
127
- const isDisplayOnly = !onChange || systemLocked;
128
- function handleToggle(key, checked) {
129
- if (!onChange || systemLocked) return;
130
- if (checked) {
131
- onChange([...selectedKeys, key]);
132
- } else {
133
- onChange(selectedKeys.filter((k) => k !== key));
134
- }
135
- }
136
- return /* @__PURE__ */ jsx(Stack, { gap: "md", children: Array.from(groups.entries()).map(([domain, rows]) => /* @__PURE__ */ jsxs(Card, { withBorder: true, padding: "md", children: [
137
- /* @__PURE__ */ jsx(Title, { order: 5, mb: "sm", style: { textTransform: "capitalize" }, children: domainLabel(domain) }),
138
- /* @__PURE__ */ jsx(Stack, { gap: "xs", children: rows.map((row) => {
139
- const isLocked = !row.isOrgGrantable;
140
- const isChecked = selectedKeys.includes(row.key);
141
- const isDisabled = isDisplayOnly || isLocked;
142
- const checkbox = /* @__PURE__ */ jsx(
143
- Checkbox,
144
- {
145
- checked: isChecked,
146
- disabled: isDisabled,
147
- onChange: (e) => handleToggle(row.key, e.currentTarget.checked),
148
- label: /* @__PURE__ */ jsxs(Group, { gap: 6, wrap: "nowrap", children: [
149
- /* @__PURE__ */ jsx(Text, { size: "sm", ff: "monospace", c: isLocked ? "var(--color-text-subtle)" : void 0, children: row.key }),
150
- isLocked && /* @__PURE__ */ jsx(IconLock, { size: 14, style: { color: "var(--color-text-subtle)", flexShrink: 0 } }),
151
- /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: row.description })
152
- ] })
153
- },
154
- row.key
155
- );
156
- if (isLocked) {
157
- return /* @__PURE__ */ jsx(Tooltip, { label: "Reserved for built-in roles", position: "right", children: /* @__PURE__ */ jsx("div", { children: checkbox }) }, row.key);
158
- }
159
- return checkbox;
160
- }) })
161
- ] }, domain)) });
162
- }
163
- function NoAccessState() {
164
- return /* @__PURE__ */ jsx(Center, { style: { flex: 1, minHeight: "100%", width: "100%", padding: 24 }, children: /* @__PURE__ */ jsxs(Stack, { align: "center", gap: "xs", style: { maxWidth: 480, textAlign: "center" }, children: [
165
- /* @__PURE__ */ jsx(IconLock, { size: 44, stroke: 1.8, color: "var(--color-text-subtle)" }),
166
- /* @__PURE__ */ jsx(Title, { order: 3, children: "Access restricted" }),
167
- /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "You don't have permission to view this page. Contact an org admin if you need access." })
168
- ] }) });
169
- }
170
- var CreateRoleFormSchema = z.object({
171
- name: z.string().trim().min(2).max(50),
172
- slug: z.string().regex(/^[a-z0-9-]+$/, "lowercase, numbers, dashes only").min(2).max(50),
173
- description: z.string().max(200).optional(),
174
- permissionKeys: z.array(z.string())
175
- });
176
- function toSlug(name) {
177
- return name.toLowerCase().replace(/\s+/g, "-");
178
- }
179
- var nameSchema = CreateRoleFormSchema.shape.name;
180
- var slugSchema = CreateRoleFormSchema.shape.slug;
181
- var descriptionSchema = z.string().max(200);
182
- function extractError(result) {
183
- return result.error.issues[0]?.message ?? "Invalid value";
184
- }
185
- function CreateRoleModal({ opened, onClose, onCreated }) {
186
- const createMutation = useCreateOrgRole();
187
- const catalog = usePermissionCatalog();
188
- const slugDirtyRef = useRef(false);
189
- const form = useForm({
190
- initialValues: {
191
- name: "",
192
- slug: "",
193
- description: "",
194
- permissionKeys: []
195
- },
196
- validate: {
197
- name: (value) => {
198
- const r = nameSchema.safeParse(value);
199
- return r.success ? null : extractError(r);
200
- },
201
- slug: (value) => {
202
- const r = slugSchema.safeParse(value);
203
- return r.success ? null : extractError(r);
204
- },
205
- description: (value) => {
206
- if (!value) return null;
207
- const r = descriptionSchema.safeParse(value);
208
- return r.success ? null : extractError(r);
209
- },
210
- permissionKeys: () => null
211
- }
212
- });
213
- useEffect(() => {
214
- if (!slugDirtyRef.current) {
215
- form.setFieldValue("slug", toSlug(form.values.name));
216
- }
217
- }, [form.values.name]);
218
- const handleSlugChange = (e) => {
219
- slugDirtyRef.current = true;
220
- form.setFieldValue("slug", e.currentTarget.value);
221
- };
222
- const handleClose = () => {
223
- if (!createMutation.isPending) {
224
- form.reset();
225
- slugDirtyRef.current = false;
226
- onClose();
227
- }
228
- };
229
- const onSubmit = form.onSubmit(async (values) => {
230
- try {
231
- const role = await createMutation.mutateAsync({
232
- name: values.name,
233
- slug: values.slug,
234
- description: values.description || void 0,
235
- permissionKeys: values.permissionKeys
236
- });
237
- showSuccessNotification(`Role "${role.name}" created.`);
238
- onCreated?.(role.id);
239
- handleClose();
240
- } catch (err) {
241
- showAuthError(err);
242
- }
243
- });
244
- const grantableCatalog = (catalog.data?.permissions ?? []).filter((p) => p.isOrgGrantable);
245
- return /* @__PURE__ */ jsx(CustomModal, { opened, onClose: handleClose, size: "lg", loading: createMutation.isPending, children: /* @__PURE__ */ jsx("form", { onSubmit, children: /* @__PURE__ */ jsxs(Stack, { p: "md", gap: "md", children: [
246
- /* @__PURE__ */ jsx(Title, { order: 4, children: "Create custom role" }),
247
- /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
248
- /* @__PURE__ */ jsx(
249
- TextInput,
250
- {
251
- label: "Display name",
252
- placeholder: "e.g. Content Editor",
253
- required: true,
254
- disabled: createMutation.isPending,
255
- ...form.getInputProps("name")
256
- }
257
- ),
258
- /* @__PURE__ */ jsx(
259
- TextInput,
260
- {
261
- label: "Slug",
262
- placeholder: "e.g. content-editor",
263
- description: "Lowercase letters, numbers, and dashes only",
264
- required: true,
265
- disabled: createMutation.isPending,
266
- ...form.getInputProps("slug"),
267
- onChange: handleSlugChange
268
- }
269
- ),
270
- /* @__PURE__ */ jsx(
271
- Textarea,
272
- {
273
- label: "Description",
274
- placeholder: "Optional description",
275
- minRows: 2,
276
- disabled: createMutation.isPending,
277
- ...form.getInputProps("description")
278
- }
279
- )
280
- ] }),
281
- /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
282
- /* @__PURE__ */ jsx(Title, { order: 5, children: "Permissions" }),
283
- catalog.isLoading && /* @__PURE__ */ jsx(Loader, { size: "sm" }),
284
- catalog.isError && /* @__PURE__ */ jsx(Alert, { color: "red", children: /* @__PURE__ */ jsx(Text, { size: "sm", children: "Failed to load permission catalog. Please close and try again." }) }),
285
- !catalog.isLoading && !catalog.isError && /* @__PURE__ */ jsx(
286
- PermissionMatrix,
287
- {
288
- catalog: grantableCatalog,
289
- selectedKeys: form.values.permissionKeys,
290
- onChange: (keys) => form.setFieldValue("permissionKeys", keys)
291
- }
292
- )
293
- ] }),
294
- /* @__PURE__ */ jsxs(Group, { justify: "flex-end", mt: "md", children: [
295
- /* @__PURE__ */ jsx(Button, { variant: "default", onClick: handleClose, disabled: createMutation.isPending, children: "Cancel" }),
296
- /* @__PURE__ */ jsx(Button, { type: "submit", loading: createMutation.isPending, children: "Create role" })
297
- ] })
298
- ] }) }) });
299
- }
300
- function EditApiKeyModal({ apiKey, onClose }) {
301
- const [name, setName] = useState("");
302
- const [nameError, setNameError] = useState(null);
303
- const updateMutation = useUpdateApiKey();
304
- useEffect(() => {
305
- if (apiKey) {
306
- setName(apiKey.name);
307
- setNameError(null);
308
- }
309
- }, [apiKey]);
310
- if (!apiKey) return null;
311
- const trimmedName = name.trim();
312
- const hasChanged = trimmedName !== apiKey.name;
313
- const isValid = trimmedName.length >= 2 && trimmedName.length <= 100;
314
- const handleClose = () => {
315
- if (!updateMutation.isPending) {
316
- onClose();
317
- }
318
- };
319
- const handleNameChange = (value) => {
320
- setName(value);
321
- if (nameError) setNameError(null);
322
- };
323
- const handleSave = async () => {
324
- if (trimmedName.length < 2) {
325
- setNameError("Name must be at least 2 characters");
326
- return;
327
- }
328
- if (trimmedName.length > 100) {
329
- setNameError("Name must be 100 characters or less");
330
- return;
331
- }
332
- setNameError(null);
333
- try {
334
- await updateMutation.mutateAsync({ keyId: apiKey.id, name: trimmedName });
335
- onClose();
336
- } catch {
337
- }
338
- };
339
- return /* @__PURE__ */ jsx(CustomModal, { opened: true, onClose: handleClose, size: "md", loading: updateMutation.isPending, children: /* @__PURE__ */ jsxs(Stack, { children: [
340
- /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
341
- /* @__PURE__ */ jsx(IconPencil, { size: 24 }),
342
- /* @__PURE__ */ jsx(Title, { order: 3, children: "Edit API Key" })
343
- ] }),
344
- /* @__PURE__ */ jsx(
345
- TextInput,
346
- {
347
- label: "Name",
348
- value: name,
349
- onChange: (e) => handleNameChange(e.target.value),
350
- disabled: updateMutation.isPending,
351
- maxLength: 100,
352
- error: nameError
353
- }
354
- ),
355
- updateMutation.error && /* @__PURE__ */ jsx(Alert, { icon: /* @__PURE__ */ jsx(IconAlertCircle, { size: 16 }), color: "red", title: "Update Failed", children: "Failed to rename API key" }),
356
- /* @__PURE__ */ jsxs(Group, { justify: "space-between", gap: "sm", children: [
357
- /* @__PURE__ */ jsx(Button, { variant: "light", onClick: handleClose, disabled: updateMutation.isPending, children: "Cancel" }),
358
- /* @__PURE__ */ jsx(Button, { onClick: handleSave, loading: updateMutation.isPending, disabled: !hasChanged || !isValid, children: "Save" })
359
- ] })
360
- ] }) });
361
- }
362
- function ApiKeyList({ keys, isLoading }) {
363
- const [deleteKey, setDeleteKey] = useState(null);
364
- const [editKey, setEditKey] = useState(null);
365
- const deleteMutation = useDeleteApiKey();
366
- const handleDelete = () => {
367
- if (deleteKey) {
368
- deleteMutation.mutate(deleteKey.id);
369
- setDeleteKey(null);
370
- }
371
- };
372
- const handleCloseDelete = () => {
373
- if (!deleteMutation.isPending) {
374
- setDeleteKey(null);
375
- }
376
- };
377
- if (isLoading) {
378
- return /* @__PURE__ */ jsx(ListSkeleton, { rows: 3, rowHeight: 50 });
379
- }
380
- if (keys.length === 0) {
381
- return /* @__PURE__ */ jsx(
382
- EmptyState,
383
- {
384
- icon: IconKey,
385
- title: "No API keys yet",
386
- description: "Create your first API key to enable external integrations"
387
- }
388
- );
389
- }
390
- return /* @__PURE__ */ jsxs(Fragment, { children: [
391
- /* @__PURE__ */ jsxs(Table, { children: [
392
- /* @__PURE__ */ jsx(Table.Thead, { children: /* @__PURE__ */ jsxs(Table.Tr, { children: [
393
- /* @__PURE__ */ jsx(Table.Th, { children: "Name" }),
394
- /* @__PURE__ */ jsx(Table.Th, { children: "Created" }),
395
- /* @__PURE__ */ jsx(Table.Th, { children: "Last Used" }),
396
- /* @__PURE__ */ jsx(Table.Th, { w: 80 })
397
- ] }) }),
398
- /* @__PURE__ */ jsx(Table.Tbody, { children: keys.map((key) => /* @__PURE__ */ jsxs(Table.Tr, { children: [
399
- /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
400
- /* @__PURE__ */ jsx(IconKey, { size: 16, style: { opacity: 0.6 } }),
401
- /* @__PURE__ */ jsx(Text, { fw: 500, children: key.name })
402
- ] }) }),
403
- /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsxs(Group, { gap: 4, children: [
404
- /* @__PURE__ */ jsx(IconCalendar, { size: 14, style: { opacity: 0.5 } }),
405
- /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: formatDateTime(key.created_at) })
406
- ] }) }),
407
- /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsxs(Group, { gap: 4, children: [
408
- /* @__PURE__ */ jsx(IconClock, { size: 14, style: { opacity: 0.5 } }),
409
- /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: formatDateTime(key.last_used_at) })
410
- ] }) }),
411
- /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsxs(Group, { gap: "xs", justify: "flex-end", children: [
412
- /* @__PURE__ */ jsx(Tooltip, { label: "Edit API key", children: /* @__PURE__ */ jsx(
413
- ActionIcon,
414
- {
415
- variant: "subtle",
416
- color: "gray",
417
- onClick: () => setEditKey(key),
418
- disabled: deleteMutation.isPending,
419
- children: /* @__PURE__ */ jsx(IconPencil, { size: 16 })
420
- }
421
- ) }),
422
- /* @__PURE__ */ jsx(Tooltip, { label: "Delete API key", position: "left", children: /* @__PURE__ */ jsx(
423
- ActionIcon,
424
- {
425
- variant: "subtle",
426
- color: "red",
427
- onClick: () => setDeleteKey(key),
428
- disabled: deleteMutation.isPending,
429
- children: /* @__PURE__ */ jsx(IconTrash, { size: 16 })
430
- }
431
- ) })
432
- ] }) })
433
- ] }, key.id)) })
434
- ] }),
435
- /* @__PURE__ */ jsx(CustomModal, { opened: !!deleteKey, onClose: handleCloseDelete, size: "md", loading: deleteMutation.isPending, children: /* @__PURE__ */ jsxs(Stack, { children: [
436
- /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
437
- /* @__PURE__ */ jsx(IconAlertTriangle, { size: 24, color: "var(--color-error)" }),
438
- /* @__PURE__ */ jsx(Title, { order: 3, children: "Delete API Key" })
439
- ] }),
440
- /* @__PURE__ */ jsxs(Text, { size: "sm", children: [
441
- "Are you sure you want to delete",
442
- " ",
443
- /* @__PURE__ */ jsxs(Text, { span: true, fw: 600, children: [
444
- '"',
445
- deleteKey?.name,
446
- '"'
447
- ] }),
448
- "?"
449
- ] }),
450
- /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "This action cannot be undone. Any integrations using this API key will stop working immediately." }),
451
- /* @__PURE__ */ jsxs(Group, { justify: "space-between", gap: "sm", children: [
452
- /* @__PURE__ */ jsx(Button, { variant: "light", onClick: handleCloseDelete, disabled: deleteMutation.isPending, children: "Cancel" }),
453
- /* @__PURE__ */ jsx(
454
- Button,
455
- {
456
- color: "red",
457
- onClick: handleDelete,
458
- loading: deleteMutation.isPending,
459
- leftSection: /* @__PURE__ */ jsx(IconTrash, { size: 16 }),
460
- children: "Delete API Key"
461
- }
462
- )
463
- ] })
464
- ] }) }),
465
- /* @__PURE__ */ jsx(EditApiKeyModal, { apiKey: editKey, onClose: () => setEditKey(null) })
466
- ] });
467
- }
468
- function CreateApiKeyModal({ opened, onClose, onSuccess }) {
469
- const createApiKey = useCreateApiKey();
470
- const form = useForm({
471
- initialValues: {
472
- name: ""
473
- },
474
- validate: {
475
- name: (value) => {
476
- if (!value.trim()) return "API key name is required";
477
- if (value.trim().length < 2) return "Name must be at least 2 characters";
478
- if (value.trim().length > 100) return "Name must be less than 100 characters";
479
- return null;
480
- }
481
- }
482
- });
483
- const handleSubmit = async (values) => {
484
- const data = {
485
- name: values.name.trim()
486
- };
487
- try {
488
- const result = await createApiKey.mutateAsync(data);
489
- form.reset();
490
- onSuccess(result);
491
- } catch (error) {
492
- console.error("Failed to create API key:", error);
493
- }
494
- };
495
- const handleClose = () => {
496
- if (!createApiKey.isPending) {
497
- form.reset();
498
- onClose();
499
- }
500
- };
501
- return /* @__PURE__ */ jsx(CustomModal, { opened, onClose: handleClose, size: "md", loading: createApiKey.isPending, children: /* @__PURE__ */ jsx("form", { onSubmit: form.onSubmit(handleSubmit), children: /* @__PURE__ */ jsxs(Stack, { children: [
502
- /* @__PURE__ */ jsx(Title, { order: 3, children: "Create New API Key" }),
503
- /* @__PURE__ */ jsx(Alert, { icon: /* @__PURE__ */ jsx(IconExclamationMark, { size: 16 }), color: "blue", children: "API keys allow external services to execute production workflows in your organization. Keep them secure and never share them publicly." }),
504
- /* @__PURE__ */ jsx(
505
- TextInput,
506
- {
507
- label: "Key Name",
508
- description: "A descriptive name to identify this key",
509
- placeholder: "e.g., Zapier Integration, GitHub Actions",
510
- leftSection: /* @__PURE__ */ jsx(IconKey, { size: 16 }),
511
- required: true,
512
- ...form.getInputProps("name"),
513
- disabled: createApiKey.isPending
514
- }
515
- ),
516
- createApiKey.error && /* @__PURE__ */ jsx(Alert, { icon: /* @__PURE__ */ jsx(IconExclamationMark, { size: 16 }), color: "red", children: createApiKey.error instanceof Error ? createApiKey.error.message : "An error occurred while creating the API key" }),
517
- /* @__PURE__ */ jsxs(Group, { justify: "flex-end", gap: "sm", children: [
518
- /* @__PURE__ */ jsx(Button, { variant: "subtle", onClick: handleClose, disabled: createApiKey.isPending, children: "Cancel" }),
519
- /* @__PURE__ */ jsx(Button, { type: "submit", loading: createApiKey.isPending, leftSection: /* @__PURE__ */ jsx(IconKey, { size: 16 }), children: "Create API Key" })
520
- ] })
521
- ] }) }) });
522
- }
523
- function ApiKeyDisplayModal({ opened, apiKey, onClose }) {
524
- return /* @__PURE__ */ jsx(CustomModal, { opened, onClose, size: "lg", loading: false, children: /* @__PURE__ */ jsxs(Stack, { children: [
525
- /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
526
- /* @__PURE__ */ jsx(IconShieldLock, { size: 24, color: "var(--color-success)" }),
527
- /* @__PURE__ */ jsx(Title, { order: 3, children: "API Key Created Successfully" })
528
- ] }),
529
- /* @__PURE__ */ jsxs(Alert, { icon: /* @__PURE__ */ jsx(IconAlertCircle, {}), color: "red", variant: "light", children: [
530
- /* @__PURE__ */ jsx(Text, { fw: 600, size: "sm", style: { fontFamily: "var(--mantine-font-family-headings)" }, children: "Save this key immediately!" }),
531
- /* @__PURE__ */ jsx(Text, { size: "sm", mt: 4, children: "This is the only time you will see this key. It cannot be retrieved again. Store it securely in your application's environment variables or secret management system." })
532
- ] }),
533
- /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
534
- /* @__PURE__ */ jsx(Text, { size: "sm", fw: 500, children: "Your new API Key:" }),
535
- /* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsxs(Group, { gap: "xs", wrap: "nowrap", children: [
536
- /* @__PURE__ */ jsx(IconKey, { size: 16, style: { opacity: 0.6, flexShrink: 0 } }),
537
- /* @__PURE__ */ jsx(
538
- Code,
539
- {
540
- block: true,
541
- style: {
542
- fontSize: "13px",
543
- wordBreak: "break-all",
544
- backgroundColor: "transparent",
545
- padding: 0,
546
- flex: 1
547
- },
548
- children: apiKey.key
549
- }
550
- ),
551
- /* @__PURE__ */ jsx(CopyButton, { value: apiKey.key, timeout: 2e3, children: ({ copied, copy }) => /* @__PURE__ */ jsx(
552
- ActionIcon,
553
- {
554
- color: copied ? "teal" : "gray",
555
- variant: copied ? "filled" : "subtle",
556
- onClick: copy,
557
- size: "lg",
558
- children: copied ? /* @__PURE__ */ jsx(IconCheck, { size: 18 }) : /* @__PURE__ */ jsx(IconCopy, { size: 18 })
559
- }
560
- ) })
561
- ] }) })
562
- ] }),
563
- /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
564
- /* @__PURE__ */ jsx(Text, { size: "sm", fw: 500, children: "How to use this key:" }),
565
- /* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsx(Code, { block: true, style: { fontSize: "12px", backgroundColor: "transparent" }, children: `
566
- # Using Authorization header
567
- curl -X POST https://api.elevasis.io/webhooks/execute \\
568
- -H "Authorization: Bearer ${apiKey.key}" \\
569
- -H "Content-Type: application/json" \\
570
- -d '{"resourceId": "workflow-id", "input": {...}}'` }) })
571
- ] }),
572
- /* @__PURE__ */ jsx(Group, { justify: "right", children: /* @__PURE__ */ jsx(Button, { onClick: onClose, size: "sm", leftSection: /* @__PURE__ */ jsx(IconCheck, { size: 16 }), children: "I've saved my key" }) })
573
- ] }) });
574
- }
575
- function ApiKeySettings() {
576
- const { organizationReady } = useInitialization();
577
- const [showCreateModal, setShowCreateModal] = useState(false);
578
- const [newApiKey, setNewApiKey] = useState(null);
579
- const { data: apiKeys = [], isLoading } = useListApiKeys();
580
- if (!organizationReady) return /* @__PURE__ */ jsx(AppShellLoader, {});
581
- const handleKeyCreated = (result) => {
582
- setNewApiKey(result);
583
- setShowCreateModal(false);
584
- };
585
- const totalKeys = apiKeys.length;
586
- const recentlyUsed = apiKeys.filter((key) => {
587
- if (!key.last_used_at) return false;
588
- const lastUsed = new Date(key.last_used_at);
589
- const dayAgo = new Date(Date.now() - 24 * 60 * 60 * 1e3);
590
- return lastUsed > dayAgo;
591
- }).length;
592
- return /* @__PURE__ */ jsxs(Stack, { children: [
593
- /* @__PURE__ */ jsx(
594
- PageTitleCaption,
595
- {
596
- title: "API Keys",
597
- caption: "Manage API keys for external service integrations",
598
- rightSection: /* @__PURE__ */ jsx(Button, { leftSection: /* @__PURE__ */ jsx(IconPlus, { size: 16 }), onClick: () => setShowCreateModal(true), variant: "light", children: "Create API Key" })
599
- }
600
- ),
601
- /* @__PURE__ */ jsxs(SimpleGrid, { cols: 2, children: [
602
- /* @__PURE__ */ jsx(StatCard, { variant: "hero", icon: IconKey, value: totalKeys, label: "Total Keys", isLoading }),
603
- /* @__PURE__ */ jsx(StatCard, { variant: "hero", icon: IconClock, value: recentlyUsed, label: "Used in 24h", isLoading })
604
- ] }),
605
- /* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsx(ApiKeyList, { keys: apiKeys, isLoading }) }),
606
- /* @__PURE__ */ jsx(
607
- CreateApiKeyModal,
608
- {
609
- opened: showCreateModal,
610
- onClose: () => setShowCreateModal(false),
611
- onSuccess: handleKeyCreated
612
- }
613
- ),
614
- newApiKey && /* @__PURE__ */ jsx(ApiKeyDisplayModal, { opened: true, apiKey: newApiKey, onClose: () => setNewApiKey(null) })
615
- ] });
616
- }
617
- var statusConfig = {
618
- active: { color: "green", label: "Active" },
619
- deploying: { color: "blue", label: "Deploying" },
620
- failed: { color: "red", label: "Failed" },
621
- stopped: { color: "gray", label: "Stopped" },
622
- rolled_back: { color: "yellow", label: "Rolled Back" }
623
- };
624
- function DeploymentStatusBadge({ status }) {
625
- const config = statusConfig[status] ?? { color: "gray", label: status };
626
- return /* @__PURE__ */ jsx(Badge, { variant: "light", color: config.color, size: "sm", children: config.label });
627
- }
628
- function DetailRow({ label, children }) {
629
- return /* @__PURE__ */ jsxs(Group, { justify: "space-between", children: [
630
- /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", fw: 500, children: label }),
631
- /* @__PURE__ */ jsx("div", { children })
632
- ] });
633
- }
634
- function DeploymentDetailModal({ deployment, opened, onClose }) {
635
- if (!deployment) return null;
636
- return /* @__PURE__ */ jsx(CustomModal, { opened, onClose, size: "lg", children: /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
637
- /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
638
- /* @__PURE__ */ jsx(IconRocket, { size: 24, color: "var(--color-primary)" }),
639
- /* @__PURE__ */ jsx(Title, { order: 3, children: "Deployment Details" })
640
- ] }),
641
- /* @__PURE__ */ jsx(DetailRow, { label: "ID", children: /* @__PURE__ */ jsx(Code, { children: deployment.id }) }),
642
- /* @__PURE__ */ jsx(DetailRow, { label: "Status", children: /* @__PURE__ */ jsx(DeploymentStatusBadge, { status: deployment.status }) }),
643
- /* @__PURE__ */ jsx(DetailRow, { label: "SDK Version", children: /* @__PURE__ */ jsx(Text, { size: "sm", children: deployment.sdkVersion }) }),
644
- /* @__PURE__ */ jsx(DetailRow, { label: "Deployment Version", children: /* @__PURE__ */ jsx(Text, { size: "sm", children: deployment.deploymentVersion ? `v${deployment.deploymentVersion}` : "\u2014" }) }),
645
- /* @__PURE__ */ jsx(DetailRow, { label: "Created", children: /* @__PURE__ */ jsx(Text, { size: "sm", children: formatDateTime(deployment.createdAt) }) }),
646
- /* @__PURE__ */ jsx(DetailRow, { label: "Updated", children: /* @__PURE__ */ jsx(Text, { size: "sm", children: formatDateTime(deployment.updatedAt) }) }),
647
- deployment.tarballPath && /* @__PURE__ */ jsx(DetailRow, { label: "Bundle Path", children: /* @__PURE__ */ jsx(Code, { fz: "xs", children: deployment.tarballPath }) }),
648
- deployment.errorMessage && /* @__PURE__ */ jsx(Alert, { icon: /* @__PURE__ */ jsx(IconAlertTriangle, { size: 16 }), title: "Error", color: "red", variant: "light", mt: "xs", children: /* @__PURE__ */ jsx(Text, { size: "sm", children: deployment.errorMessage }) })
649
- ] }) });
650
- }
651
- function DeploymentList({ deployments, isLoading }) {
652
- const [selected, setSelected] = useState(null);
653
- const [activateTarget, setActivateTarget] = useState(null);
654
- const [deactivateTarget, setDeactivateTarget] = useState(null);
655
- const [deleteTarget, setDeleteTarget] = useState(null);
656
- const { page, setPage, offset, totalPages } = usePaginationState(PAGE_SIZE_DEFAULT, void 0, deployments.length);
657
- const paginatedDeployments = deployments.slice(offset, offset + PAGE_SIZE_DEFAULT);
658
- const total = totalPages(deployments.length);
659
- const activateMutation = useActivateDeployment();
660
- const deactivateMutation = useDeactivateDeployment();
661
- const deleteMutation = useDeleteDeployment();
662
- const handleCloseActivate = () => {
663
- if (!activateMutation.isPending) setActivateTarget(null);
664
- };
665
- const handleCloseDeactivate = () => {
666
- if (!deactivateMutation.isPending) setDeactivateTarget(null);
667
- };
668
- const handleCloseDelete = () => {
669
- if (!deleteMutation.isPending) setDeleteTarget(null);
670
- };
671
- const handleConfirmActivate = () => {
672
- if (!activateTarget) return;
673
- activateMutation.mutate(activateTarget.id, { onSuccess: () => setActivateTarget(null) });
674
- };
675
- const handleConfirmDeactivate = () => {
676
- if (!deactivateTarget) return;
677
- deactivateMutation.mutate(deactivateTarget.id, { onSuccess: () => setDeactivateTarget(null) });
678
- };
679
- const handleConfirmDelete = () => {
680
- if (!deleteTarget) return;
681
- deleteMutation.mutate(deleteTarget.id, { onSuccess: () => setDeleteTarget(null) });
682
- };
683
- if (isLoading) {
684
- return /* @__PURE__ */ jsx(ListSkeleton, { rows: 3, rowHeight: 50 });
685
- }
686
- if (deployments.length === 0) {
687
- return /* @__PURE__ */ jsx(
688
- EmptyState,
689
- {
690
- icon: IconRocket,
691
- title: "No deployments yet",
692
- description: "Deploy your first bundle using the SDK CLI: elevasis deploy"
693
- }
694
- );
695
- }
696
- const anyMutationPending = activateMutation.isPending || deactivateMutation.isPending || deleteMutation.isPending;
697
- const mutatingId = activateMutation.isPending ? activateMutation.variables : deactivateMutation.isPending ? deactivateMutation.variables : deleteMutation.isPending ? deleteMutation.variables : null;
698
- return /* @__PURE__ */ jsxs(Fragment, { children: [
699
- /* @__PURE__ */ jsxs(Table, { children: [
700
- /* @__PURE__ */ jsx(Table.Thead, { children: /* @__PURE__ */ jsxs(Table.Tr, { children: [
701
- /* @__PURE__ */ jsx(Table.Th, { children: "Status" }),
702
- /* @__PURE__ */ jsx(Table.Th, { children: "Deployment Version" }),
703
- /* @__PURE__ */ jsx(Table.Th, { children: "SDK Version" }),
704
- /* @__PURE__ */ jsx(Table.Th, { children: "Created" }),
705
- /* @__PURE__ */ jsx(Table.Th, { children: "Updated" }),
706
- /* @__PURE__ */ jsx(Table.Th, { w: 100 })
707
- ] }) }),
708
- /* @__PURE__ */ jsx(Table.Tbody, { children: paginatedDeployments.map((deployment) => {
709
- const isMutating = deployment.id === mutatingId;
710
- return /* @__PURE__ */ jsxs(
711
- Table.Tr,
712
- {
713
- onClick: () => setSelected(deployment),
714
- style: { cursor: "pointer", opacity: isMutating ? 0.6 : 1, transition: "opacity 150ms" },
715
- children: [
716
- /* @__PURE__ */ jsx(Table.Td, { children: isMutating ? /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
717
- /* @__PURE__ */ jsx(Loader, { size: 14 }),
718
- /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "Updating\u2026" })
719
- ] }) : /* @__PURE__ */ jsx(DeploymentStatusBadge, { status: deployment.status }) }),
720
- /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", fw: 500, children: deployment.deploymentVersion ?? "\u2014" }) }),
721
- /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", fw: 500, children: deployment.sdkVersion }) }),
722
- /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsxs(Group, { gap: 4, children: [
723
- /* @__PURE__ */ jsx(IconCalendar, { size: 14, style: { opacity: 0.5 } }),
724
- /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: formatDateTime(deployment.createdAt) })
725
- ] }) }),
726
- /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsxs(Group, { gap: 4, children: [
727
- /* @__PURE__ */ jsx(IconRefresh, { size: 14, style: { opacity: 0.5 } }),
728
- /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: formatDateTime(deployment.updatedAt) })
729
- ] }) }),
730
- /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsxs(Group, { gap: "xs", justify: "flex-end", children: [
731
- deployment.status === "active" && /* @__PURE__ */ jsx(Tooltip, { label: "Deactivate deployment", children: /* @__PURE__ */ jsx(
732
- ActionIcon,
733
- {
734
- variant: "subtle",
735
- color: "orange",
736
- onClick: (e) => {
737
- e.stopPropagation();
738
- setDeactivateTarget(deployment);
739
- },
740
- disabled: anyMutationPending,
741
- children: /* @__PURE__ */ jsx(IconPower, { size: 16 })
742
- }
743
- ) }),
744
- (deployment.status === "stopped" || deployment.status === "rolled_back") && /* @__PURE__ */ jsxs(Fragment, { children: [
745
- /* @__PURE__ */ jsx(Tooltip, { label: "Activate deployment", children: /* @__PURE__ */ jsx(
746
- ActionIcon,
747
- {
748
- variant: "subtle",
749
- color: "green",
750
- onClick: (e) => {
751
- e.stopPropagation();
752
- setActivateTarget(deployment);
753
- },
754
- disabled: anyMutationPending,
755
- children: /* @__PURE__ */ jsx(IconPlayerPlay, { size: 16 })
756
- }
757
- ) }),
758
- /* @__PURE__ */ jsx(Tooltip, { label: "Delete deployment", position: "left", children: /* @__PURE__ */ jsx(
759
- ActionIcon,
760
- {
761
- variant: "subtle",
762
- color: "red",
763
- onClick: (e) => {
764
- e.stopPropagation();
765
- setDeleteTarget(deployment);
766
- },
767
- disabled: anyMutationPending,
768
- children: /* @__PURE__ */ jsx(IconTrash, { size: 16 })
769
- }
770
- ) })
771
- ] }),
772
- deployment.status === "failed" && /* @__PURE__ */ jsx(Tooltip, { label: "Delete deployment", position: "left", children: /* @__PURE__ */ jsx(
773
- ActionIcon,
774
- {
775
- variant: "subtle",
776
- color: "red",
777
- onClick: (e) => {
778
- e.stopPropagation();
779
- setDeleteTarget(deployment);
780
- },
781
- disabled: anyMutationPending,
782
- children: /* @__PURE__ */ jsx(IconTrash, { size: 16 })
783
- }
784
- ) })
785
- ] }) })
786
- ]
787
- },
788
- deployment.id
789
- );
790
- }) })
791
- ] }),
792
- total > 1 && /* @__PURE__ */ jsx(Group, { justify: "center", mt: "md", children: /* @__PURE__ */ jsx(Pagination, { total, value: page, onChange: setPage }) }),
793
- /* @__PURE__ */ jsx(DeploymentDetailModal, { deployment: selected, opened: !!selected, onClose: () => setSelected(null) }),
794
- /* @__PURE__ */ jsx(
795
- CustomModal,
796
- {
797
- opened: !!activateTarget,
798
- onClose: handleCloseActivate,
799
- size: "sm",
800
- loading: activateMutation.isPending,
801
- children: /* @__PURE__ */ jsxs(Stack, { children: [
802
- /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
803
- /* @__PURE__ */ jsx(IconPlayerPlay, { size: 24, color: "var(--mantine-color-green-6)" }),
804
- /* @__PURE__ */ jsx(Title, { order: 3, children: "Activate Deployment" })
805
- ] }),
806
- /* @__PURE__ */ jsxs(Text, { size: "sm", children: [
807
- "Are you sure you want to activate deployment",
808
- " ",
809
- /* @__PURE__ */ jsxs(Text, { span: true, fw: 600, children: [
810
- '"',
811
- activateTarget?.id.slice(0, 8),
812
- '..."'
813
- ] }),
814
- "?"
815
- ] }),
816
- /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "This will stop the currently active deployment and replace it with this version." }),
817
- /* @__PURE__ */ jsxs(Group, { justify: "space-between", gap: "sm", children: [
818
- /* @__PURE__ */ jsx(Button, { variant: "light", onClick: handleCloseActivate, disabled: activateMutation.isPending, children: "Cancel" }),
819
- /* @__PURE__ */ jsx(
820
- Button,
821
- {
822
- color: "green",
823
- onClick: handleConfirmActivate,
824
- loading: activateMutation.isPending,
825
- leftSection: /* @__PURE__ */ jsx(IconPlayerPlay, { size: 16 }),
826
- children: "Activate"
827
- }
828
- )
829
- ] })
830
- ] })
831
- }
832
- ),
833
- /* @__PURE__ */ jsx(
834
- CustomModal,
835
- {
836
- opened: !!deactivateTarget,
837
- onClose: handleCloseDeactivate,
838
- size: "sm",
839
- loading: deactivateMutation.isPending,
840
- children: /* @__PURE__ */ jsxs(Stack, { children: [
841
- /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
842
- /* @__PURE__ */ jsx(IconPower, { size: 24, color: "var(--mantine-color-orange-6)" }),
843
- /* @__PURE__ */ jsx(Title, { order: 3, children: "Deactivate Deployment" })
844
- ] }),
845
- /* @__PURE__ */ jsxs(Text, { size: "sm", children: [
846
- "Are you sure you want to deactivate deployment",
847
- " ",
848
- /* @__PURE__ */ jsxs(Text, { span: true, fw: 600, children: [
849
- '"',
850
- deactivateTarget?.id.slice(0, 8),
851
- '..."'
852
- ] }),
853
- "?"
854
- ] }),
855
- /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "This will stop all workflow and agent executions for this deployment. No resources will be available until a deployment is activated." }),
856
- /* @__PURE__ */ jsxs(Group, { justify: "space-between", gap: "sm", children: [
857
- /* @__PURE__ */ jsx(Button, { variant: "light", onClick: handleCloseDeactivate, disabled: deactivateMutation.isPending, children: "Cancel" }),
858
- /* @__PURE__ */ jsx(
859
- Button,
860
- {
861
- color: "orange",
862
- onClick: handleConfirmDeactivate,
863
- loading: deactivateMutation.isPending,
864
- leftSection: /* @__PURE__ */ jsx(IconPower, { size: 16 }),
865
- children: "Deactivate"
866
- }
867
- )
868
- ] })
869
- ] })
870
- }
871
- ),
872
- /* @__PURE__ */ jsx(CustomModal, { opened: !!deleteTarget, onClose: handleCloseDelete, size: "sm", loading: deleteMutation.isPending, children: /* @__PURE__ */ jsxs(Stack, { children: [
873
- /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
874
- /* @__PURE__ */ jsx(IconAlertTriangle, { size: 24, color: "var(--color-error)" }),
875
- /* @__PURE__ */ jsx(Title, { order: 3, children: "Delete Deployment" })
876
- ] }),
877
- /* @__PURE__ */ jsxs(Text, { size: "sm", children: [
878
- "Are you sure you want to delete deployment",
879
- " ",
880
- /* @__PURE__ */ jsxs(Text, { span: true, fw: 600, children: [
881
- '"',
882
- deleteTarget?.id.slice(0, 8),
883
- '..."'
884
- ] }),
885
- "?"
886
- ] }),
887
- /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "This action cannot be undone. The deployment bundle will be permanently removed." }),
888
- /* @__PURE__ */ jsxs(Group, { justify: "space-between", gap: "sm", children: [
889
- /* @__PURE__ */ jsx(Button, { variant: "light", onClick: handleCloseDelete, disabled: deleteMutation.isPending, children: "Cancel" }),
890
- /* @__PURE__ */ jsx(
891
- Button,
892
- {
893
- color: "red",
894
- onClick: handleConfirmDelete,
895
- loading: deleteMutation.isPending,
896
- leftSection: /* @__PURE__ */ jsx(IconTrash, { size: 16 }),
897
- children: "Delete"
898
- }
899
- )
900
- ] })
901
- ] }) })
902
- ] });
903
- }
904
- function DeploymentSettings() {
905
- const { isReady } = useElevasisServices();
906
- const { data: deployments = [], isLoading } = useListDeployments();
907
- if (!isReady) return /* @__PURE__ */ jsx(AppShellLoader, {});
908
- const totalDeployments = deployments.length;
909
- const activeDeployment = deployments.find((d) => d.status === "active");
910
- return /* @__PURE__ */ jsxs(Stack, { children: [
911
- /* @__PURE__ */ jsx(PageTitleCaption, { title: "Deployments", caption: "View deployment history for externally deployed SDK bundles" }),
912
- /* @__PURE__ */ jsxs(SimpleGrid, { cols: 3, children: [
913
- /* @__PURE__ */ jsx(
914
- StatCard,
915
- {
916
- variant: "hero",
917
- icon: IconRocket,
918
- value: totalDeployments,
919
- label: "Total Deployments",
920
- isLoading
921
- }
922
- ),
923
- /* @__PURE__ */ jsx(
924
- StatCard,
925
- {
926
- variant: "hero",
927
- icon: IconCircleCheck,
928
- value: activeDeployment ? activeDeployment.sdkVersion : "None",
929
- label: "Active SDK Deployment",
930
- isLoading
931
- }
932
- ),
933
- /* @__PURE__ */ jsx(
934
- StatCard,
935
- {
936
- variant: "hero",
937
- icon: IconTag,
938
- value: activeDeployment?.deploymentVersion ?? "None",
939
- label: "Active Deployment Version",
940
- isLoading
941
- }
942
- )
943
- ] }),
944
- /* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsx(DeploymentList, { deployments, isLoading }) })
945
- ] });
946
- }
947
- function ScheduleTypeSelector({ value, onChange, disabled }) {
948
- return /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
949
- /* @__PURE__ */ jsx(Text, { size: "sm", fw: 500, children: "Schedule Type" }),
950
- /* @__PURE__ */ jsx(
951
- SegmentedControl,
952
- {
953
- value,
954
- onChange: (v) => onChange(v),
955
- disabled,
956
- data: [
957
- {
958
- value: "recurring",
959
- label: /* @__PURE__ */ jsxs(Stack, { gap: 4, align: "center", children: [
960
- /* @__PURE__ */ jsx(IconCalendarRepeat, { size: 20 }),
961
- /* @__PURE__ */ jsx(Text, { size: "xs", children: "Recurring" })
962
- ] })
963
- },
964
- {
965
- value: "relative",
966
- label: /* @__PURE__ */ jsxs(Stack, { gap: 4, align: "center", children: [
967
- /* @__PURE__ */ jsx(IconCalendarEvent, { size: 20 }),
968
- /* @__PURE__ */ jsx(Text, { size: "xs", children: "Relative" })
969
- ] })
970
- },
971
- {
972
- value: "absolute",
973
- label: /* @__PURE__ */ jsxs(Stack, { gap: 4, align: "center", children: [
974
- /* @__PURE__ */ jsx(IconCalendarTime, { size: 20 }),
975
- /* @__PURE__ */ jsx(Text, { size: "xs", children: "Absolute" })
976
- ] })
977
- }
978
- ],
979
- fullWidth: true
980
- }
981
- ),
982
- /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
983
- value === "recurring" && "Run on a schedule (daily, weekly, or cron expression)",
984
- value === "relative" && "Run at offsets from an anchor date (e.g., 3 days before event)",
985
- value === "absolute" && "Run at specific dates and times"
986
- ] })
987
- ] });
988
- }
989
- function toDateTimeLocal(isoString) {
990
- if (!isoString) return "";
991
- const date = new Date(isoString);
992
- return date.toISOString().slice(0, 16);
993
- }
994
- function fromDateTimeLocal(dateTimeLocal) {
995
- if (!dateTimeLocal) return null;
996
- return new Date(dateTimeLocal).toISOString();
997
- }
998
- function RecurringScheduleForm({ value, onChange, disabled }) {
999
- const [useCron, setUseCron] = useState(!!value.cron);
1000
- const handleChange = (updates) => {
1001
- onChange({ ...value, ...updates });
1002
- };
1003
- return /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
1004
- /* @__PURE__ */ jsx(
1005
- Switch,
1006
- {
1007
- label: "Use cron expression",
1008
- description: "Advanced scheduling with cron syntax",
1009
- checked: useCron,
1010
- onChange: (e) => {
1011
- setUseCron(e.currentTarget.checked);
1012
- if (e.currentTarget.checked) {
1013
- handleChange({ cron: "0 9 * * *", interval: void 0, time: void 0 });
1014
- } else {
1015
- handleChange({ cron: void 0, interval: "daily", time: "09:00" });
1016
- }
1017
- },
1018
- disabled
1019
- }
1020
- ),
1021
- useCron ? /* @__PURE__ */ jsx(
1022
- TextInput,
1023
- {
1024
- label: "Cron Expression",
1025
- placeholder: "0 9 * * 1 (Every Monday at 9am)",
1026
- value: value.cron || "",
1027
- onChange: (e) => handleChange({ cron: e.currentTarget.value }),
1028
- description: "Format: minute hour day month weekday",
1029
- disabled
1030
- }
1031
- ) : /* @__PURE__ */ jsxs(Group, { grow: true, children: [
1032
- /* @__PURE__ */ jsx(
1033
- Select,
1034
- {
1035
- label: "Interval",
1036
- data: [
1037
- { value: "daily", label: "Daily" },
1038
- { value: "weekly", label: "Weekly" },
1039
- { value: "monthly", label: "Monthly" }
1040
- ],
1041
- value: value.interval || "daily",
1042
- onChange: (v) => handleChange({ interval: v }),
1043
- disabled
1044
- }
1045
- ),
1046
- /* @__PURE__ */ jsx(
1047
- TextInput,
1048
- {
1049
- label: "Time",
1050
- type: "time",
1051
- value: value.time || "09:00",
1052
- onChange: (e) => handleChange({ time: e.currentTarget.value }),
1053
- disabled
1054
- }
1055
- )
1056
- ] }),
1057
- /* @__PURE__ */ jsx(
1058
- Select,
1059
- {
1060
- label: "Timezone",
1061
- placeholder: "Select timezone",
1062
- data: [
1063
- { value: "UTC", label: "UTC" },
1064
- { value: "America/New_York", label: "Eastern Time" },
1065
- { value: "America/Chicago", label: "Central Time" },
1066
- { value: "America/Denver", label: "Mountain Time" },
1067
- { value: "America/Los_Angeles", label: "Pacific Time" },
1068
- { value: "Europe/London", label: "London" },
1069
- { value: "Europe/Paris", label: "Paris" },
1070
- { value: "Asia/Tokyo", label: "Tokyo" }
1071
- ],
1072
- value: value.timezone || "UTC",
1073
- onChange: (v) => handleChange({ timezone: v || "UTC" }),
1074
- searchable: true,
1075
- disabled
1076
- }
1077
- ),
1078
- /* @__PURE__ */ jsx(
1079
- TextInput,
1080
- {
1081
- label: "End Date (Optional)",
1082
- type: "datetime-local",
1083
- value: toDateTimeLocal(value.endAt),
1084
- onChange: (e) => handleChange({ endAt: fromDateTimeLocal(e.currentTarget.value) }),
1085
- disabled
1086
- }
1087
- )
1088
- ] });
1089
- }
1090
- function toDateTimeLocal2(isoString) {
1091
- if (!isoString) return "";
1092
- const date = new Date(isoString);
1093
- return date.toISOString().slice(0, 16);
1094
- }
1095
- function fromDateTimeLocal2(dateTimeLocal) {
1096
- if (!dateTimeLocal) return void 0;
1097
- return new Date(dateTimeLocal).toISOString();
1098
- }
1099
- function RelativeScheduleForm({ value, onChange, disabled }) {
1100
- const items = value.items || [];
1101
- const handleAnchorChange = (dateTimeLocal) => {
1102
- onChange({ ...value, anchorAt: fromDateTimeLocal2(dateTimeLocal) });
1103
- };
1104
- const handleItemChange = (index, updates) => {
1105
- const newItems = [...items];
1106
- newItems[index] = { ...newItems[index], ...updates };
1107
- onChange({ ...value, items: newItems });
1108
- };
1109
- const handleAddItem = () => {
1110
- const newItems = [...items, { offset: "+1d", payload: {}, label: "" }];
1111
- onChange({ ...value, items: newItems });
1112
- };
1113
- const handleRemoveItem = (index) => {
1114
- const newItems = items.filter((_, i) => i !== index);
1115
- onChange({ ...value, items: newItems });
1116
- };
1117
- return /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
1118
- /* @__PURE__ */ jsx(
1119
- TextInput,
1120
- {
1121
- label: "Anchor Date",
1122
- description: "Reference date for calculating run times",
1123
- type: "datetime-local",
1124
- value: toDateTimeLocal2(value.anchorAt),
1125
- onChange: (e) => handleAnchorChange(e.currentTarget.value),
1126
- required: true,
1127
- disabled
1128
- }
1129
- ),
1130
- /* @__PURE__ */ jsx(
1131
- TextInput,
1132
- {
1133
- label: "Anchor Label (Optional)",
1134
- placeholder: "e.g., meeting_date, deadline, event_start",
1135
- value: value.anchorLabel || "",
1136
- onChange: (e) => onChange({ ...value, anchorLabel: e.currentTarget.value || void 0 }),
1137
- disabled
1138
- }
1139
- ),
1140
- /* @__PURE__ */ jsxs(Stack, { children: [
1141
- /* @__PURE__ */ jsxs(Group, { justify: "space-between", children: [
1142
- /* @__PURE__ */ jsx(Text, { size: "sm", fw: 500, children: "Schedule Items" }),
1143
- /* @__PURE__ */ jsx(
1144
- Button,
1145
- {
1146
- size: "xs",
1147
- variant: "light",
1148
- leftSection: /* @__PURE__ */ jsx(IconPlus, { size: 14 }),
1149
- onClick: handleAddItem,
1150
- disabled,
1151
- children: "Add Item"
1152
- }
1153
- )
1154
- ] }),
1155
- items.length === 0 && /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: 'No items yet. Click "Add Item" to create a schedule step.' }),
1156
- items.map((item, index) => /* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
1157
- /* @__PURE__ */ jsxs(Group, { justify: "space-between", children: [
1158
- /* @__PURE__ */ jsxs(Text, { size: "sm", fw: 500, children: [
1159
- "Step ",
1160
- index + 1
1161
- ] }),
1162
- /* @__PURE__ */ jsx(
1163
- ActionIcon,
1164
- {
1165
- size: "sm",
1166
- color: "red",
1167
- variant: "subtle",
1168
- onClick: () => handleRemoveItem(index),
1169
- disabled,
1170
- children: /* @__PURE__ */ jsx(IconTrash, { size: 14 })
1171
- }
1172
- )
1173
- ] }),
1174
- /* @__PURE__ */ jsxs(Group, { grow: true, children: [
1175
- /* @__PURE__ */ jsx(
1176
- TextInput,
1177
- {
1178
- label: "Offset",
1179
- placeholder: "+3d, -2h, +30m",
1180
- value: item.offset,
1181
- onChange: (e) => handleItemChange(index, { offset: e.currentTarget.value }),
1182
- description: "+ after anchor, - before anchor",
1183
- disabled
1184
- }
1185
- ),
1186
- /* @__PURE__ */ jsx(
1187
- TextInput,
1188
- {
1189
- label: "Label (Optional)",
1190
- placeholder: "e.g., Thank you, Follow up",
1191
- value: item.label || "",
1192
- onChange: (e) => handleItemChange(index, { label: e.currentTarget.value || void 0 }),
1193
- disabled
1194
- }
1195
- )
1196
- ] }),
1197
- /* @__PURE__ */ jsx(
1198
- Textarea,
1199
- {
1200
- label: "Payload (JSON)",
1201
- placeholder: '{"key": "value"}',
1202
- value: JSON.stringify(item.payload, null, 2),
1203
- onChange: (e) => {
1204
- try {
1205
- const payload = JSON.parse(e.currentTarget.value);
1206
- handleItemChange(index, { payload });
1207
- } catch {
1208
- }
1209
- },
1210
- minRows: 2,
1211
- disabled
1212
- }
1213
- )
1214
- ] }) }, index))
1215
- ] })
1216
- ] });
1217
- }
1218
- function toDateTimeLocal3(isoString) {
1219
- if (!isoString) return "";
1220
- const date = new Date(isoString);
1221
- return date.toISOString().slice(0, 16);
1222
- }
1223
- function fromDateTimeLocal3(dateTimeLocal) {
1224
- if (!dateTimeLocal) return "";
1225
- return new Date(dateTimeLocal).toISOString();
1226
- }
1227
- function AbsoluteScheduleForm({ value, onChange, disabled }) {
1228
- const items = value.items || [];
1229
- const handleItemChange = (index, updates) => {
1230
- const newItems = [...items];
1231
- newItems[index] = { ...newItems[index], ...updates };
1232
- onChange({ ...value, items: newItems });
1233
- };
1234
- const handleAddItem = () => {
1235
- const newItems = [...items, { runAt: (/* @__PURE__ */ new Date()).toISOString(), payload: {}, label: "" }];
1236
- onChange({ ...value, items: newItems });
1237
- };
1238
- const handleRemoveItem = (index) => {
1239
- const newItems = items.filter((_, i) => i !== index);
1240
- onChange({ ...value, items: newItems });
1241
- };
1242
- return /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
1243
- /* @__PURE__ */ jsxs(Group, { justify: "space-between", children: [
1244
- /* @__PURE__ */ jsx(Text, { size: "sm", fw: 500, children: "Scheduled Executions" }),
1245
- /* @__PURE__ */ jsx(
1246
- Button,
1247
- {
1248
- size: "xs",
1249
- variant: "light",
1250
- leftSection: /* @__PURE__ */ jsx(IconPlus, { size: 14 }),
1251
- onClick: handleAddItem,
1252
- disabled,
1253
- children: "Add Execution"
1254
- }
1255
- )
1256
- ] }),
1257
- items.length === 0 && /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: 'No executions yet. Click "Add Execution" to schedule a run.' }),
1258
- items.map((item, index) => /* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
1259
- /* @__PURE__ */ jsxs(Group, { justify: "space-between", children: [
1260
- /* @__PURE__ */ jsxs(Text, { size: "sm", fw: 500, children: [
1261
- "Execution ",
1262
- index + 1
1263
- ] }),
1264
- /* @__PURE__ */ jsx(
1265
- ActionIcon,
1266
- {
1267
- size: "sm",
1268
- color: "red",
1269
- variant: "subtle",
1270
- onClick: () => handleRemoveItem(index),
1271
- disabled,
1272
- children: /* @__PURE__ */ jsx(IconTrash, { size: 14 })
1273
- }
1274
- )
1275
- ] }),
1276
- /* @__PURE__ */ jsxs(Group, { grow: true, children: [
1277
- /* @__PURE__ */ jsx(
1278
- TextInput,
1279
- {
1280
- label: "Run At",
1281
- type: "datetime-local",
1282
- value: toDateTimeLocal3(item.runAt),
1283
- onChange: (e) => handleItemChange(index, { runAt: fromDateTimeLocal3(e.currentTarget.value) }),
1284
- required: true,
1285
- disabled
1286
- }
1287
- ),
1288
- /* @__PURE__ */ jsx(
1289
- TextInput,
1290
- {
1291
- label: "Label (Optional)",
1292
- placeholder: "e.g., Launch, Phase 2",
1293
- value: item.label || "",
1294
- onChange: (e) => handleItemChange(index, { label: e.currentTarget.value || void 0 }),
1295
- disabled
1296
- }
1297
- )
1298
- ] }),
1299
- /* @__PURE__ */ jsx(
1300
- Textarea,
1301
- {
1302
- label: "Payload (JSON)",
1303
- placeholder: '{"key": "value"}',
1304
- value: JSON.stringify(item.payload, null, 2),
1305
- onChange: (e) => {
1306
- try {
1307
- const payload = JSON.parse(e.currentTarget.value);
1308
- handleItemChange(index, { payload });
1309
- } catch {
1310
- }
1311
- },
1312
- minRows: 2,
1313
- disabled
1314
- }
1315
- )
1316
- ] }) }, index))
1317
- ] });
1318
- }
1319
- var defaultRecurringConfig = {
1320
- type: "recurring",
1321
- interval: "daily",
1322
- time: "09:00",
1323
- timezone: "UTC",
1324
- payload: {}
1325
- };
1326
- var defaultRelativeConfig = {
1327
- type: "relative",
1328
- items: []
1329
- };
1330
- var defaultAbsoluteConfig = {
1331
- type: "absolute",
1332
- items: []
1333
- };
1334
- function CreateScheduleModal({ opened, onClose }) {
1335
- const [name, setName] = useState("");
1336
- const [description, setDescription] = useState("");
1337
- const [resourceId, setResourceId] = useState("");
1338
- const [scheduleType, setScheduleType] = useState("recurring");
1339
- const [recurringConfig, setRecurringConfig] = useState(defaultRecurringConfig);
1340
- const [relativeConfig, setRelativeConfig] = useState(defaultRelativeConfig);
1341
- const [absoluteConfig, setAbsoluteConfig] = useState(defaultAbsoluteConfig);
1342
- const { data: resourcesData } = useResources();
1343
- const createSchedule = useCreateSchedule();
1344
- const allResources = useMemo(() => {
1345
- if (!resourcesData) return [];
1346
- const workflows = (resourcesData.workflows || []).map((w) => ({
1347
- id: w.resourceId,
1348
- type: "workflow"
1349
- }));
1350
- const agents = (resourcesData.agents || []).map((a) => ({
1351
- id: a.resourceId,
1352
- type: "agent"
1353
- }));
1354
- return [...workflows, ...agents];
1355
- }, [resourcesData]);
1356
- const resourceOptions = useMemo(() => {
1357
- if (!resourcesData) return [];
1358
- const workflowOptions = (resourcesData.workflows || []).filter((w) => w?.resourceId).map((w) => ({
1359
- value: w.resourceId,
1360
- label: `Workflow: ${w.resourceId}`
1361
- }));
1362
- const agentOptions = (resourcesData.agents || []).filter((a) => a?.resourceId).map((a) => ({
1363
- value: a.resourceId,
1364
- label: `Agent: ${a.resourceId}`
1365
- }));
1366
- return [...workflowOptions, ...agentOptions];
1367
- }, [resourcesData]);
1368
- const handleScheduleTypeChange = (newType) => {
1369
- setScheduleType(newType);
1370
- };
1371
- const buildScheduleConfig = () => {
1372
- switch (scheduleType) {
1373
- case "recurring": {
1374
- if (!recurringConfig.timezone) {
1375
- showErrorNotification("Timezone is required");
1376
- return null;
1377
- }
1378
- const config = {
1379
- type: "recurring",
1380
- timezone: recurringConfig.timezone,
1381
- payload: recurringConfig.payload || {},
1382
- endAt: recurringConfig.endAt
1383
- };
1384
- if (recurringConfig.cron) {
1385
- config.cron = recurringConfig.cron;
1386
- } else {
1387
- config.interval = recurringConfig.interval || "daily";
1388
- config.time = recurringConfig.time || "09:00";
1389
- }
1390
- return config;
1391
- }
1392
- case "relative": {
1393
- if (!relativeConfig.anchorAt) {
1394
- showErrorNotification("Anchor date is required for relative schedules");
1395
- return null;
1396
- }
1397
- if (!relativeConfig.items || relativeConfig.items.length === 0) {
1398
- showErrorNotification("At least one schedule item is required");
1399
- return null;
1400
- }
1401
- return {
1402
- type: "relative",
1403
- anchorAt: relativeConfig.anchorAt,
1404
- anchorLabel: relativeConfig.anchorLabel,
1405
- items: relativeConfig.items
1406
- };
1407
- }
1408
- case "absolute": {
1409
- if (!absoluteConfig.items || absoluteConfig.items.length === 0) {
1410
- showErrorNotification("At least one scheduled execution is required");
1411
- return null;
1412
- }
1413
- return {
1414
- type: "absolute",
1415
- items: absoluteConfig.items
1416
- };
1417
- }
1418
- default:
1419
- return null;
1420
- }
1421
- };
1422
- const handleSubmit = async (e) => {
1423
- e.preventDefault();
1424
- try {
1425
- const resource = allResources.find((r) => r.id === resourceId);
1426
- if (!resource) {
1427
- showErrorNotification("Please select a valid resource");
1428
- return;
1429
- }
1430
- if (!name.trim()) {
1431
- showErrorNotification("Schedule name is required");
1432
- return;
1433
- }
1434
- const scheduleConfig = buildScheduleConfig();
1435
- if (!scheduleConfig) {
1436
- return;
1437
- }
1438
- const input = {
1439
- name: name.trim(),
1440
- description: description.trim() || void 0,
1441
- target: {
1442
- resourceType: resource.type,
1443
- resourceId: resource.id
1444
- },
1445
- scheduleConfig,
1446
- maxRetries: 3,
1447
- originResourceType: "api",
1448
- originResourceId: "task-scheduler-ui"
1449
- };
1450
- await createSchedule.mutateAsync(input);
1451
- showSuccessNotification("Schedule created successfully");
1452
- handleClose();
1453
- } catch (error) {
1454
- showApiErrorNotification(error);
1455
- }
1456
- };
1457
- const handleClose = () => {
1458
- if (!createSchedule.isPending) {
1459
- setName("");
1460
- setDescription("");
1461
- setResourceId("");
1462
- setScheduleType("recurring");
1463
- setRecurringConfig(defaultRecurringConfig);
1464
- setRelativeConfig(defaultRelativeConfig);
1465
- setAbsoluteConfig(defaultAbsoluteConfig);
1466
- onClose();
1467
- }
1468
- };
1469
- return /* @__PURE__ */ jsx(CustomModal, { opened, onClose: handleClose, size: "lg", loading: createSchedule.isPending, children: /* @__PURE__ */ jsx("form", { onSubmit: handleSubmit, children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
1470
- /* @__PURE__ */ jsx(Title, { order: 3, children: "Create Schedule" }),
1471
- /* @__PURE__ */ jsx(
1472
- TextInput,
1473
- {
1474
- label: "Schedule Name",
1475
- placeholder: "e.g., Daily Sales Report, Weekly Sync",
1476
- value: name,
1477
- onChange: (e) => setName(e.currentTarget.value),
1478
- required: true,
1479
- disabled: createSchedule.isPending
1480
- }
1481
- ),
1482
- /* @__PURE__ */ jsx(
1483
- Textarea,
1484
- {
1485
- label: "Description (Optional)",
1486
- placeholder: "Describe what this schedule does...",
1487
- value: description,
1488
- onChange: (e) => setDescription(e.currentTarget.value),
1489
- disabled: createSchedule.isPending
1490
- }
1491
- ),
1492
- /* @__PURE__ */ jsx(
1493
- Select,
1494
- {
1495
- label: "Resource to Execute",
1496
- placeholder: resourcesData ? "Select an agent or workflow" : "Loading resources...",
1497
- data: resourceOptions,
1498
- value: resourceId,
1499
- onChange: (value) => setResourceId(value || ""),
1500
- searchable: true,
1501
- required: true,
1502
- disabled: !resourcesData || createSchedule.isPending
1503
- }
1504
- ),
1505
- /* @__PURE__ */ jsx(Divider, {}),
1506
- /* @__PURE__ */ jsx(
1507
- ScheduleTypeSelector,
1508
- {
1509
- value: scheduleType,
1510
- onChange: handleScheduleTypeChange,
1511
- disabled: createSchedule.isPending
1512
- }
1513
- ),
1514
- /* @__PURE__ */ jsx(Divider, {}),
1515
- scheduleType === "recurring" && /* @__PURE__ */ jsx(
1516
- RecurringScheduleForm,
1517
- {
1518
- value: recurringConfig,
1519
- onChange: setRecurringConfig,
1520
- disabled: createSchedule.isPending
1521
- }
1522
- ),
1523
- scheduleType === "relative" && /* @__PURE__ */ jsx(
1524
- RelativeScheduleForm,
1525
- {
1526
- value: relativeConfig,
1527
- onChange: setRelativeConfig,
1528
- disabled: createSchedule.isPending
1529
- }
1530
- ),
1531
- scheduleType === "absolute" && /* @__PURE__ */ jsx(
1532
- AbsoluteScheduleForm,
1533
- {
1534
- value: absoluteConfig,
1535
- onChange: setAbsoluteConfig,
1536
- disabled: createSchedule.isPending
1537
- }
1538
- ),
1539
- /* @__PURE__ */ jsxs(Group, { justify: "flex-end", mt: "md", children: [
1540
- /* @__PURE__ */ jsx(Button, { variant: "light", onClick: handleClose, type: "button", disabled: createSchedule.isPending, children: "Cancel" }),
1541
- /* @__PURE__ */ jsx(Button, { type: "submit", loading: createSchedule.isPending, children: "Create Schedule" })
1542
- ] })
1543
- ] }) }) });
1544
- }
1545
- function DeleteScheduleModal({ opened, onClose, onConfirm, schedule, isDeleting }) {
1546
- const handleClose = () => {
1547
- if (!isDeleting) {
1548
- onClose();
1549
- }
1550
- };
1551
- return /* @__PURE__ */ jsx(CustomModal, { opened, onClose: handleClose, size: "sm", loading: isDeleting, children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
1552
- /* @__PURE__ */ jsxs(Group, { gap: "sm", children: [
1553
- /* @__PURE__ */ jsx(IconAlertTriangle, { size: 24, color: "var(--color-error)" }),
1554
- /* @__PURE__ */ jsx(Title, { order: 4, children: "Delete Schedule" })
1555
- ] }),
1556
- /* @__PURE__ */ jsxs(Text, { size: "sm", children: [
1557
- "Are you sure you want to delete",
1558
- " ",
1559
- /* @__PURE__ */ jsx(Text, { span: true, fw: 600, children: schedule?.name }),
1560
- "?"
1561
- ] }),
1562
- /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "This action cannot be undone. Any pending executions will be cancelled." }),
1563
- /* @__PURE__ */ jsxs(Group, { justify: "flex-end", mt: "md", children: [
1564
- /* @__PURE__ */ jsx(Button, { variant: "light", onClick: handleClose, disabled: isDeleting, children: "Cancel" }),
1565
- /* @__PURE__ */ jsx(Button, { color: "red", onClick: onConfirm, loading: isDeleting, children: "Delete" })
1566
- ] })
1567
- ] }) });
1568
- }
1569
- function getStatusColor2(status) {
1570
- switch (status) {
1571
- case "active":
1572
- return "green";
1573
- case "paused":
1574
- return "yellow";
1575
- case "completed":
1576
- return "blue";
1577
- case "cancelled":
1578
- return "gray";
1579
- }
1580
- }
1581
- function formatDate(date) {
1582
- if (!date) return "N/A";
1583
- const d = typeof date === "string" ? new Date(date) : date;
1584
- return d.toLocaleString("en-US", {
1585
- month: "short",
1586
- day: "numeric",
1587
- year: "numeric",
1588
- hour: "numeric",
1589
- minute: "2-digit",
1590
- timeZoneName: "short"
1591
- });
1592
- }
1593
- function ScheduleConfigDetails({ schedule }) {
1594
- const config = schedule.scheduleConfig;
1595
- switch (config.type) {
1596
- case "recurring":
1597
- return /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
1598
- /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
1599
- /* @__PURE__ */ jsx(IconCalendarRepeat, { size: 16, color: "var(--color-text-subtle)" }),
1600
- /* @__PURE__ */ jsx(Text, { fw: 500, children: "Recurring Schedule" })
1601
- ] }),
1602
- /* @__PURE__ */ jsx(Table, { withRowBorders: false, verticalSpacing: 4, children: /* @__PURE__ */ jsxs(Table.Tbody, { children: [
1603
- config.cron && /* @__PURE__ */ jsxs(Table.Tr, { children: [
1604
- /* @__PURE__ */ jsx(Table.Td, { w: 120, children: /* @__PURE__ */ jsx(Text, { c: "dimmed", children: "Cron" }) }),
1605
- /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Code, { children: config.cron }) })
1606
- ] }),
1607
- config.interval && /* @__PURE__ */ jsxs(Table.Tr, { children: [
1608
- /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { c: "dimmed", children: "Interval" }) }),
1609
- /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsxs(Text, { children: [
1610
- config.interval,
1611
- " at ",
1612
- config.time
1613
- ] }) })
1614
- ] }),
1615
- /* @__PURE__ */ jsxs(Table.Tr, { children: [
1616
- /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { c: "dimmed", children: "Timezone" }) }),
1617
- /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { children: config.timezone }) })
1618
- ] }),
1619
- config.endAt && /* @__PURE__ */ jsxs(Table.Tr, { children: [
1620
- /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { c: "dimmed", children: "Ends" }) }),
1621
- /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { children: formatDate(config.endAt) }) })
1622
- ] }),
1623
- /* @__PURE__ */ jsxs(Table.Tr, { children: [
1624
- /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { c: "dimmed", children: "Overdue" }) }),
1625
- /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Badge, { size: "xs", variant: "light", color: config.overduePolicy === "execute" ? "orange" : "gray", children: config.overduePolicy ?? "skip" }) })
1626
- ] }),
1627
- Object.keys(config.payload).length > 0 && /* @__PURE__ */ jsxs(Table.Tr, { children: [
1628
- /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { c: "dimmed", children: "Payload" }) }),
1629
- /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Code, { block: true, children: JSON.stringify(config.payload, null, 2) }) })
1630
- ] })
1631
- ] }) })
1632
- ] });
1633
- case "relative":
1634
- return /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
1635
- /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
1636
- /* @__PURE__ */ jsx(IconCalendarEvent, { size: 16, color: "var(--color-text-subtle)" }),
1637
- /* @__PURE__ */ jsx(Text, { fw: 500, children: "Relative Schedule" })
1638
- ] }),
1639
- /* @__PURE__ */ jsx(Table, { withRowBorders: false, verticalSpacing: 4, children: /* @__PURE__ */ jsxs(Table.Tbody, { children: [
1640
- /* @__PURE__ */ jsxs(Table.Tr, { children: [
1641
- /* @__PURE__ */ jsx(Table.Td, { w: 120, children: /* @__PURE__ */ jsx(Text, { c: "dimmed", children: "Anchor" }) }),
1642
- /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsxs(Text, { children: [
1643
- formatDate(config.anchorAt),
1644
- config.anchorLabel && ` (${config.anchorLabel})`
1645
- ] }) })
1646
- ] }),
1647
- /* @__PURE__ */ jsxs(Table.Tr, { children: [
1648
- /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { c: "dimmed", children: "Steps" }) }),
1649
- /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Stack, { gap: 4, children: config.items.map((item, i) => /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
1650
- /* @__PURE__ */ jsx(Badge, { size: "xs", variant: "light", color: i === schedule.currentStep ? "blue" : "gray", children: item.offset }),
1651
- item.label && /* @__PURE__ */ jsx(Text, { c: "dimmed", children: item.label })
1652
- ] }, i)) }) })
1653
- ] })
1654
- ] }) })
1655
- ] });
1656
- case "absolute":
1657
- return /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
1658
- /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
1659
- /* @__PURE__ */ jsx(IconCalendarTime, { size: 16, color: "var(--color-text-subtle)" }),
1660
- /* @__PURE__ */ jsx(Text, { fw: 500, children: "Absolute Schedule" })
1661
- ] }),
1662
- /* @__PURE__ */ jsx(Stack, { gap: 4, children: config.items.map((item, i) => /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
1663
- /* @__PURE__ */ jsx(Badge, { size: "xs", variant: "light", color: i === schedule.currentStep ? "blue" : "gray", children: i + 1 }),
1664
- /* @__PURE__ */ jsx(Text, { children: formatDate(item.runAt) }),
1665
- item.label && /* @__PURE__ */ jsxs(Text, { c: "dimmed", children: [
1666
- "\u2014 ",
1667
- item.label
1668
- ] })
1669
- ] }, i)) })
1670
- ] });
1671
- }
1672
- }
1673
- function ScheduleDetailModal({ opened, onClose, schedule, resourceStatus }) {
1674
- const ResourceIcon = schedule?.target.resourceType === "agent" ? IconRobot : IconGitBranch;
1675
- const resourceNotFound = resourceStatus === void 0;
1676
- return /* @__PURE__ */ jsx(CustomModal, { opened, onClose, size: "lg", children: schedule && /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
1677
- /* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "flex-start", children: [
1678
- /* @__PURE__ */ jsxs(Group, { gap: "sm", children: [
1679
- /* @__PURE__ */ jsx(ResourceIcon, { size: 24, color: "var(--color-primary)" }),
1680
- /* @__PURE__ */ jsxs("div", { children: [
1681
- /* @__PURE__ */ jsx(Title, { order: 4, style: { fontFamily: "var(--elevasis-font-family-subtitle)" }, children: schedule.name }),
1682
- schedule.description && /* @__PURE__ */ jsx(Text, { c: "dimmed", mt: 2, children: schedule.description })
1683
- ] })
1684
- ] }),
1685
- /* @__PURE__ */ jsx(Badge, { variant: "light", color: getStatusColor2(schedule.status), children: schedule.status })
1686
- ] }),
1687
- resourceNotFound && /* @__PURE__ */ jsxs(Alert, { icon: /* @__PURE__ */ jsx(IconAlertTriangle, { size: 18 }), color: "red", variant: "light", title: "Resource not found", children: [
1688
- "The target resource ",
1689
- /* @__PURE__ */ jsx(Code, { children: schedule.target.resourceId }),
1690
- " does not exist in the current deployment. This schedule will fail when it tries to execute. Consider deleting or updating it."
1691
- ] }),
1692
- /* @__PURE__ */ jsx(Divider, {}),
1693
- /* @__PURE__ */ jsxs(Group, { gap: "lg", children: [
1694
- /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
1695
- /* @__PURE__ */ jsx(ResourceIcon, { size: 14, color: "var(--color-text-subtle)" }),
1696
- /* @__PURE__ */ jsx(Text, { c: "dimmed", children: "Target" })
1697
- ] }),
1698
- /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
1699
- /* @__PURE__ */ jsx(Badge, { size: "xs", variant: "light", radius: "sm", children: schedule.target.resourceType }),
1700
- /* @__PURE__ */ jsx(Code, { children: schedule.target.resourceId })
1701
- ] })
1702
- ] }),
1703
- /* @__PURE__ */ jsx(Divider, {}),
1704
- /* @__PURE__ */ jsx(ScheduleConfigDetails, { schedule }),
1705
- /* @__PURE__ */ jsx(Divider, {}),
1706
- /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
1707
- /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
1708
- /* @__PURE__ */ jsx(IconSettings, { size: 16, color: "var(--color-text-subtle)" }),
1709
- /* @__PURE__ */ jsx(Text, { fw: 500, children: "Execution Details" })
1710
- ] }),
1711
- /* @__PURE__ */ jsx(Table, { withRowBorders: false, verticalSpacing: 4, children: /* @__PURE__ */ jsxs(Table.Tbody, { children: [
1712
- /* @__PURE__ */ jsxs(Table.Tr, { children: [
1713
- /* @__PURE__ */ jsx(Table.Td, { w: 140, children: /* @__PURE__ */ jsxs(Group, { gap: 4, children: [
1714
- /* @__PURE__ */ jsx(IconClock, { size: 12, color: "var(--color-text-subtle)" }),
1715
- /* @__PURE__ */ jsx(Text, { c: "dimmed", children: "Next Run" })
1716
- ] }) }),
1717
- /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { children: formatDate(schedule.nextRunAt) }) })
1718
- ] }),
1719
- /* @__PURE__ */ jsxs(Table.Tr, { children: [
1720
- /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsxs(Group, { gap: 4, children: [
1721
- /* @__PURE__ */ jsx(IconCalendar, { size: 12, color: "var(--color-text-subtle)" }),
1722
- /* @__PURE__ */ jsx(Text, { c: "dimmed", children: "Last Run" })
1723
- ] }) }),
1724
- /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { children: formatDate(schedule.lastRunAt) }) })
1725
- ] }),
1726
- /* @__PURE__ */ jsxs(Table.Tr, { children: [
1727
- /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsxs(Group, { gap: 4, children: [
1728
- /* @__PURE__ */ jsx(IconRefresh, { size: 12, color: "var(--color-text-subtle)" }),
1729
- /* @__PURE__ */ jsx(Text, { c: "dimmed", children: "Max Retries" })
1730
- ] }) }),
1731
- /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { children: schedule.maxRetries }) })
1732
- ] }),
1733
- schedule.idempotencyKey && /* @__PURE__ */ jsxs(Table.Tr, { children: [
1734
- /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsxs(Group, { gap: 4, children: [
1735
- /* @__PURE__ */ jsx(IconKey, { size: 12, color: "var(--color-text-subtle)" }),
1736
- /* @__PURE__ */ jsx(Text, { c: "dimmed", children: "Idempotency" })
1737
- ] }) }),
1738
- /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Code, { children: schedule.idempotencyKey }) })
1739
- ] }),
1740
- schedule.lastExecutionId && /* @__PURE__ */ jsxs(Table.Tr, { children: [
1741
- /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { c: "dimmed", children: "Last Execution" }) }),
1742
- /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Code, { children: schedule.lastExecutionId }) })
1743
- ] })
1744
- ] }) })
1745
- ] }),
1746
- /* @__PURE__ */ jsx(Divider, {}),
1747
- /* @__PURE__ */ jsxs(Group, { justify: "space-between", children: [
1748
- /* @__PURE__ */ jsxs(Group, { gap: "lg", children: [
1749
- /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
1750
- "Created ",
1751
- formatDate(schedule.createdAt)
1752
- ] }),
1753
- /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
1754
- "Updated ",
1755
- formatDate(schedule.updatedAt)
1756
- ] })
1757
- ] }),
1758
- /* @__PURE__ */ jsx(
1759
- Button,
1760
- {
1761
- variant: "light",
1762
- size: "xs",
1763
- rightSection: /* @__PURE__ */ jsx(IconExternalLink, { size: 14 }),
1764
- component: "a",
1765
- href: schedule.target.resourceType === "agent" ? `/operations/resources/agent/${schedule.target.resourceId}` : `/operations/resources/workflow/${schedule.target.resourceId}`,
1766
- target: "_blank",
1767
- disabled: resourceNotFound,
1768
- children: "Go to Resource"
1769
- }
1770
- )
1771
- ] })
1772
- ] }) });
1773
- }
1774
- function getScheduleTypeIcon(config) {
1775
- switch (config.type) {
1776
- case "recurring":
1777
- return IconCalendarRepeat;
1778
- case "relative":
1779
- return IconCalendarEvent;
1780
- case "absolute":
1781
- return IconCalendarTime;
1782
- }
1783
- }
1784
- function getScheduleTypeLabel(config) {
1785
- switch (config.type) {
1786
- case "recurring":
1787
- if (config.cron) return `Cron: ${config.cron}`;
1788
- if (config.interval)
1789
- return `${config.interval.charAt(0).toUpperCase() + config.interval.slice(1)} at ${config.time}`;
1790
- return "Recurring";
1791
- case "relative":
1792
- return `${config.items.length} steps from anchor`;
1793
- case "absolute":
1794
- return `${config.items.length} scheduled runs`;
1795
- }
1796
- }
1797
- function getStatusColor3(status) {
1798
- switch (status) {
1799
- case "active":
1800
- return "green";
1801
- case "paused":
1802
- return "yellow";
1803
- case "completed":
1804
- return "blue";
1805
- case "cancelled":
1806
- return "gray";
1807
- }
1808
- }
1809
- function formatNextRun(date) {
1810
- if (!date) return "";
1811
- const now = /* @__PURE__ */ new Date();
1812
- const diffMs = date.getTime() - now.getTime();
1813
- const diffDays = Math.floor(diffMs / (1e3 * 60 * 60 * 24));
1814
- const diffHours = Math.floor(diffMs % (1e3 * 60 * 60 * 24) / (1e3 * 60 * 60));
1815
- if (diffMs < 0) return "Overdue";
1816
- if (diffDays === 0) {
1817
- if (diffHours === 0) return "Less than an hour";
1818
- return `In ${diffHours}h`;
1819
- }
1820
- if (diffDays === 1) return "Tomorrow";
1821
- if (diffDays <= 7) return `In ${diffDays}d`;
1822
- return date.toLocaleDateString("en-US", {
1823
- month: "short",
1824
- day: "numeric"
1825
- });
1826
- }
1827
- function ScheduleCard({
1828
- schedule,
1829
- resourceStatus,
1830
- onPause,
1831
- onResume,
1832
- onCancel,
1833
- onDelete,
1834
- onClick,
1835
- isLoading
1836
- }) {
1837
- const [hovered, setHovered] = useState(false);
1838
- const TypeIcon = getScheduleTypeIcon(schedule.scheduleConfig);
1839
- const ResourceIcon = schedule.target.resourceType === "agent" ? IconRobot : IconGitBranch;
1840
- const nextRunAt = schedule.nextRunAt ? typeof schedule.nextRunAt === "string" ? new Date(schedule.nextRunAt) : schedule.nextRunAt : void 0;
1841
- const nextRunLabel = nextRunAt && schedule.status === "active" ? formatNextRun(nextRunAt) : "";
1842
- return /* @__PURE__ */ jsx(
1843
- Card,
1844
- {
1845
- style: {
1846
- cursor: "pointer",
1847
- transition: "background 150ms ease",
1848
- background: hovered ? "var(--active-background)" : "var(--color-surface)"
1849
- },
1850
- onClick: (e) => {
1851
- if (e.target.closest("[data-menu-dropdown]") || e.target.closest("button")) {
1852
- return;
1853
- }
1854
- onClick?.(schedule);
1855
- },
1856
- onMouseEnter: () => setHovered(true),
1857
- onMouseLeave: () => setHovered(false),
1858
- children: /* @__PURE__ */ jsxs(
1859
- "div",
1860
- {
1861
- style: {
1862
- display: "grid",
1863
- gridTemplateColumns: "18px minmax(120px, 1.5fr) minmax(100px, 1fr) minmax(80px, 1fr) 270px",
1864
- alignItems: "center",
1865
- gap: "var(--mantine-spacing-sm)"
1866
- },
1867
- children: [
1868
- /* @__PURE__ */ jsx(ResourceIcon, { size: 18, color: "var(--color-primary)" }),
1869
- /* @__PURE__ */ jsx(Text, { fw: 600, size: "md", truncate: true, style: { fontFamily: "var(--elevasis-font-family-subtitle)" }, children: schedule.name }),
1870
- /* @__PURE__ */ jsxs(Group, { gap: 6, wrap: "nowrap", children: [
1871
- /* @__PURE__ */ jsx(TypeIcon, { size: 14, color: "var(--color-text-subtle)", style: { flexShrink: 0 } }),
1872
- /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", truncate: true, children: getScheduleTypeLabel(schedule.scheduleConfig) })
1873
- ] }),
1874
- /* @__PURE__ */ jsxs(Group, { gap: 6, wrap: "nowrap", style: { minWidth: 0 }, children: [
1875
- /* @__PURE__ */ jsx(Tooltip, { label: resourceStatus ? `Deployed: ${resourceStatus}` : "Resource not found", children: /* @__PURE__ */ jsx(
1876
- "div",
1877
- {
1878
- style: {
1879
- width: 8,
1880
- height: 8,
1881
- borderRadius: "50%",
1882
- flexShrink: 0,
1883
- backgroundColor: resourceStatus ? resourceStatus === "prod" ? "var(--color-success)" : "var(--color-primary)" : "var(--mantine-color-red-6)"
1884
- }
1885
- }
1886
- ) }),
1887
- /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", truncate: true, ff: "monospace", style: { minWidth: 0 }, children: schedule.target.resourceId })
1888
- ] }),
1889
- /* @__PURE__ */ jsxs(Group, { gap: 8, wrap: "nowrap", justify: "flex-end", children: [
1890
- /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", children: schedule.target.resourceType }),
1891
- nextRunLabel && /* @__PURE__ */ jsxs(Group, { gap: 4, wrap: "nowrap", children: [
1892
- /* @__PURE__ */ jsx(IconClock, { size: 12, color: "var(--color-text-subtle)" }),
1893
- /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", style: { whiteSpace: "nowrap" }, children: nextRunLabel })
1894
- ] }),
1895
- /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: getStatusColor3(schedule.status), children: schedule.status }),
1896
- /* @__PURE__ */ jsxs(Menu, { position: "bottom-end", withinPortal: true, children: [
1897
- /* @__PURE__ */ jsx(Menu.Target, { children: /* @__PURE__ */ jsx(ActionIcon, { variant: "subtle", size: "sm", disabled: isLoading, children: /* @__PURE__ */ jsx(IconDotsVertical, { size: 16 }) }) }),
1898
- /* @__PURE__ */ jsxs(Menu.Dropdown, { children: [
1899
- schedule.status === "active" && /* @__PURE__ */ jsx(Menu.Item, { leftSection: /* @__PURE__ */ jsx(IconPlayerPause, { size: 14 }), onClick: () => onPause(schedule.id), children: "Pause" }),
1900
- schedule.status === "paused" && /* @__PURE__ */ jsx(Menu.Item, { leftSection: /* @__PURE__ */ jsx(IconPlayerPlay, { size: 14 }), onClick: () => onResume(schedule.id), children: "Resume" }),
1901
- (schedule.status === "active" || schedule.status === "paused") && /* @__PURE__ */ jsx(Menu.Item, { leftSection: /* @__PURE__ */ jsx(IconPlayerStop, { size: 14 }), onClick: () => onCancel(schedule.id), children: "Cancel" }),
1902
- /* @__PURE__ */ jsx(Menu.Item, { leftSection: /* @__PURE__ */ jsx(IconTrash, { size: 14 }), color: "red", onClick: () => onDelete(schedule.id), children: "Delete" })
1903
- ] })
1904
- ] })
1905
- ] })
1906
- ]
1907
- }
1908
- )
1909
- }
1910
- );
1911
- }
1912
- var TaskScheduler = () => {
1913
- const { isReady } = useElevasisServices();
1914
- const [isModalOpen, setIsModalOpen] = useState(false);
1915
- const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
1916
- const [scheduleToDelete, setScheduleToDelete] = useState(null);
1917
- const [selectedSchedule, setSelectedSchedule] = useState(null);
1918
- const [viewMode, setViewMode] = useState("buckets");
1919
- const [statusFilter, setStatusFilter] = useState("all");
1920
- const [typeFilter, setTypeFilter] = useState("all");
1921
- const queryFilters = useMemo(() => {
1922
- const filters = {};
1923
- if (statusFilter !== "all") {
1924
- filters.status = statusFilter;
1925
- }
1926
- return filters;
1927
- }, [statusFilter]);
1928
- const { data, isLoading, error } = useListSchedules(queryFilters);
1929
- const pauseSchedule = usePauseSchedule();
1930
- const resumeSchedule = useResumeSchedule();
1931
- const cancelSchedule = useCancelSchedule();
1932
- const deleteSchedule = useDeleteSchedule();
1933
- const filteredSchedules = useMemo(() => {
1934
- if (!data?.schedules) return [];
1935
- if (typeFilter === "all") return data.schedules;
1936
- return data.schedules.filter((s) => s.scheduleConfig.type === typeFilter);
1937
- }, [data?.schedules, typeFilter]);
1938
- const isActionLoading = pauseSchedule.isPending || resumeSchedule.isPending || cancelSchedule.isPending || deleteSchedule.isPending;
1939
- const schedulesByType = useMemo(() => {
1940
- if (!data?.schedules) return { recurring: 0, relative: 0, absolute: 0 };
1941
- return data.schedules.reduce(
1942
- (acc, s) => {
1943
- acc[s.scheduleConfig.type]++;
1944
- return acc;
1945
- },
1946
- { recurring: 0, relative: 0, absolute: 0 }
1947
- );
1948
- }, [data?.schedules]);
1949
- const schedulesByStatus = useMemo(() => {
1950
- if (!data?.schedules) return { active: 0, paused: 0, completed: 0, cancelled: 0 };
1951
- return data.schedules.reduce(
1952
- (acc, s) => {
1953
- if (s.status === "active") acc.active++;
1954
- else if (s.status === "paused") acc.paused++;
1955
- else if (s.status === "completed") acc.completed++;
1956
- else if (s.status === "cancelled") acc.cancelled++;
1957
- return acc;
1958
- },
1959
- { active: 0, paused: 0, completed: 0, cancelled: 0 }
1960
- );
1961
- }, [data?.schedules]);
1962
- const { data: resourcesData } = useResources();
1963
- const resourceStatusMap = useMemo(() => {
1964
- const map = /* @__PURE__ */ new Map();
1965
- if (!resourcesData) return map;
1966
- for (const w of resourcesData.workflows) map.set(w.resourceId, w.status);
1967
- for (const a of resourcesData.agents) map.set(a.resourceId, a.status);
1968
- return map;
1969
- }, [resourcesData]);
1970
- const handleCardClick = useCallback((schedule) => {
1971
- setSelectedSchedule(schedule);
1972
- }, []);
1973
- if (isLoading || !isReady) {
1974
- return /* @__PURE__ */ jsx(AppShellLoader, {});
1975
- }
1976
- const handlePause = async (scheduleId) => {
1977
- await pauseSchedule.mutateAsync(scheduleId);
1978
- };
1979
- const handleResume = async (scheduleId) => {
1980
- await resumeSchedule.mutateAsync(scheduleId);
1981
- };
1982
- const handleCancel = async (scheduleId) => {
1983
- await cancelSchedule.mutateAsync(scheduleId);
1984
- };
1985
- const handleDelete = (scheduleId) => {
1986
- const schedule = data?.schedules.find((s) => s.id === scheduleId);
1987
- if (schedule) {
1988
- setScheduleToDelete(schedule);
1989
- setIsDeleteModalOpen(true);
1990
- }
1991
- };
1992
- const handleConfirmDelete = async () => {
1993
- if (scheduleToDelete) {
1994
- await deleteSchedule.mutateAsync(scheduleToDelete.id);
1995
- setIsDeleteModalOpen(false);
1996
- setScheduleToDelete(null);
1997
- }
1998
- };
1999
- const handleCloseDeleteModal = () => {
2000
- setIsDeleteModalOpen(false);
2001
- setScheduleToDelete(null);
2002
- };
2003
- const formatNextRun2 = (schedule) => {
2004
- if (!schedule.nextRunAt) return "Not scheduled";
2005
- const date = typeof schedule.nextRunAt === "string" ? new Date(schedule.nextRunAt) : schedule.nextRunAt;
2006
- const now = /* @__PURE__ */ new Date();
2007
- const diffMs = date.getTime() - now.getTime();
2008
- const diffDays = Math.floor(diffMs / (1e3 * 60 * 60 * 24));
2009
- if (diffMs < 0) return "Overdue";
2010
- if (diffDays === 0) {
2011
- return `Today at ${date.toLocaleTimeString("en-US", { hour: "numeric", minute: "2-digit" })}`;
2012
- } else if (diffDays === 1) {
2013
- return `Tomorrow at ${date.toLocaleTimeString("en-US", { hour: "numeric", minute: "2-digit" })}`;
2014
- } else if (diffDays > 0 && diffDays <= 7) {
2015
- return `In ${diffDays} days at ${date.toLocaleTimeString("en-US", { hour: "numeric", minute: "2-digit" })}`;
2016
- } else {
2017
- return date.toLocaleString("en-US", {
2018
- month: "short",
2019
- day: "numeric",
2020
- hour: "numeric",
2021
- minute: "2-digit"
2022
- });
2023
- }
2024
- };
2025
- const groupByTimeBucket = (schedules) => {
2026
- const now = /* @__PURE__ */ new Date();
2027
- const buckets = {
2028
- today: [],
2029
- tomorrow: [],
2030
- thisWeek: [],
2031
- later: [],
2032
- noNextRun: []
2033
- };
2034
- schedules.forEach((schedule) => {
2035
- if (!schedule.nextRunAt) {
2036
- buckets.noNextRun.push(schedule);
2037
- return;
2038
- }
2039
- const nextRun = typeof schedule.nextRunAt === "string" ? new Date(schedule.nextRunAt) : schedule.nextRunAt;
2040
- const diffMs = nextRun.getTime() - now.getTime();
2041
- const diffDays = Math.floor(diffMs / (1e3 * 60 * 60 * 24));
2042
- if (diffDays < 0) {
2043
- buckets.today.push(schedule);
2044
- } else if (diffDays === 0) {
2045
- buckets.today.push(schedule);
2046
- } else if (diffDays === 1) {
2047
- buckets.tomorrow.push(schedule);
2048
- } else if (diffDays <= 7) {
2049
- buckets.thisWeek.push(schedule);
2050
- } else {
2051
- buckets.later.push(schedule);
2052
- }
2053
- });
2054
- return buckets;
2055
- };
2056
- if (error) {
2057
- return /* @__PURE__ */ jsx(Paper, { children: /* @__PURE__ */ jsx(CenteredErrorState, { error, title: "Failed to load schedules" }) });
2058
- }
2059
- const timeBuckets = groupByTimeBucket(filteredSchedules);
2060
- const renderScheduleCards = (schedules) => schedules.map((schedule) => /* @__PURE__ */ jsx(
2061
- ScheduleCard,
2062
- {
2063
- schedule,
2064
- resourceStatus: resourceStatusMap.get(schedule.target.resourceId),
2065
- onPause: handlePause,
2066
- onResume: handleResume,
2067
- onCancel: handleCancel,
2068
- onDelete: handleDelete,
2069
- onClick: handleCardClick,
2070
- isLoading: isActionLoading
2071
- },
2072
- schedule.id
2073
- ));
2074
- return /* @__PURE__ */ jsxs(Stack, { children: [
2075
- /* @__PURE__ */ jsx(
2076
- PageTitleCaption,
2077
- {
2078
- title: "Task Scheduler",
2079
- caption: "Schedule agents and workflows to run automatically",
2080
- rightSection: /* @__PURE__ */ jsx(
2081
- SegmentedControl,
2082
- {
2083
- value: viewMode,
2084
- onChange: (value) => setViewMode(value),
2085
- data: [
2086
- { label: "Buckets", value: "buckets" },
2087
- { label: "Timeline", value: "timeline" }
2088
- ]
2089
- }
2090
- )
2091
- }
2092
- ),
2093
- /* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 2, sm: 5 }, children: [
2094
- /* @__PURE__ */ jsx(StatCard, { variant: "hero", label: "Active", value: schedulesByStatus.active, icon: IconCalendarEvent }),
2095
- /* @__PURE__ */ jsx(StatCard, { variant: "hero", label: "Paused", value: schedulesByStatus.paused, icon: IconCalendarDue }),
2096
- /* @__PURE__ */ jsx(StatCard, { variant: "hero", label: "Completed", value: schedulesByStatus.completed, icon: IconCalendarStats }),
2097
- /* @__PURE__ */ jsx(StatCard, { variant: "hero", label: "Cancelled", value: schedulesByStatus.cancelled, icon: IconCalendarOff }),
2098
- /* @__PURE__ */ jsx(StatCard, { variant: "hero", label: "Total", value: data?.schedules.length || 0, icon: IconListCheck })
2099
- ] }),
2100
- /* @__PURE__ */ jsx(Paper, { children: /* @__PURE__ */ jsxs(Stack, { children: [
2101
- /* @__PURE__ */ jsxs(
2102
- FilterBar,
2103
- {
2104
- actions: /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
2105
- /* @__PURE__ */ jsxs(Badge, { size: "sm", variant: "light", leftSection: /* @__PURE__ */ jsx(IconCalendarRepeat, { size: 12 }), children: [
2106
- schedulesByType.recurring,
2107
- " Recurring"
2108
- ] }),
2109
- /* @__PURE__ */ jsxs(Badge, { size: "sm", variant: "light", leftSection: /* @__PURE__ */ jsx(IconCalendarEvent, { size: 12 }), children: [
2110
- schedulesByType.relative,
2111
- " Relative"
2112
- ] }),
2113
- /* @__PURE__ */ jsxs(Badge, { size: "sm", variant: "light", leftSection: /* @__PURE__ */ jsx(IconCalendarTime, { size: 12 }), children: [
2114
- schedulesByType.absolute,
2115
- " Absolute"
2116
- ] })
2117
- ] }),
2118
- children: [
2119
- /* @__PURE__ */ jsx(
2120
- Select,
2121
- {
2122
- size: "sm",
2123
- placeholder: "Status",
2124
- data: [
2125
- { value: "all", label: "All Statuses" },
2126
- { value: "active", label: "Active" },
2127
- { value: "paused", label: "Paused" },
2128
- { value: "completed", label: "Completed" },
2129
- { value: "cancelled", label: "Cancelled" }
2130
- ],
2131
- value: statusFilter,
2132
- onChange: (v) => setStatusFilter(v || "all"),
2133
- clearable: false,
2134
- style: { minWidth: 150 }
2135
- }
2136
- ),
2137
- /* @__PURE__ */ jsx(
2138
- Select,
2139
- {
2140
- size: "sm",
2141
- placeholder: "Type",
2142
- data: [
2143
- { value: "all", label: "All Types" },
2144
- { value: "recurring", label: "Recurring" },
2145
- { value: "relative", label: "Relative" },
2146
- { value: "absolute", label: "Absolute" }
2147
- ],
2148
- value: typeFilter,
2149
- onChange: (v) => setTypeFilter(v || "all"),
2150
- clearable: false,
2151
- style: { minWidth: 150 }
2152
- }
2153
- )
2154
- ]
2155
- }
2156
- ),
2157
- viewMode === "timeline" && filteredSchedules.length > 0 && /* @__PURE__ */ jsxs(Stack, { children: [
2158
- /* @__PURE__ */ jsx(CardHeader, { icon: /* @__PURE__ */ jsx(IconCalendarEvent, { size: 18 }), title: "All Schedules" }),
2159
- /* @__PURE__ */ jsx(Timeline, { bulletSize: 24, lineWidth: 2, children: [...filteredSchedules].sort((a, b) => {
2160
- const aDate = a.nextRunAt ? typeof a.nextRunAt === "string" ? new Date(a.nextRunAt) : a.nextRunAt : new Date(9999, 0);
2161
- const bDate = b.nextRunAt ? typeof b.nextRunAt === "string" ? new Date(b.nextRunAt) : b.nextRunAt : new Date(9999, 0);
2162
- return aDate.getTime() - bDate.getTime();
2163
- }).map((schedule) => {
2164
- const nextRun = schedule.nextRunAt ? typeof schedule.nextRunAt === "string" ? new Date(schedule.nextRunAt) : schedule.nextRunAt : null;
2165
- const isOverdue = nextRun && nextRun.getTime() < Date.now();
2166
- return /* @__PURE__ */ jsxs(
2167
- Timeline.Item,
2168
- {
2169
- style: { cursor: "pointer" },
2170
- onClick: () => handleCardClick(schedule),
2171
- bullet: /* @__PURE__ */ jsx(ThemeIcon, { size: 24, radius: "xl", children: /* @__PURE__ */ jsx(IconClock, { size: 12 }) }),
2172
- title: /* @__PURE__ */ jsxs(Group, { justify: "space-between", wrap: "nowrap", children: [
2173
- /* @__PURE__ */ jsx(Text, { fw: 600, style: { fontFamily: "var(--mantine-font-family-headings)" }, children: schedule.name }),
2174
- /* @__PURE__ */ jsxs(Group, { gap: "xs", wrap: "nowrap", children: [
2175
- /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: isOverdue ? "orange" : void 0, children: formatNextRun2(schedule) }),
2176
- /* @__PURE__ */ jsx(
2177
- Badge,
2178
- {
2179
- size: "sm",
2180
- variant: "light",
2181
- color: schedule.status === "active" ? "green" : "yellow",
2182
- children: schedule.status
2183
- }
2184
- )
2185
- ] })
2186
- ] }),
2187
- children: [
2188
- schedule.description && /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", mt: 4, children: schedule.description }),
2189
- /* @__PURE__ */ jsxs(Group, { gap: "xs", mt: "xs", children: [
2190
- /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", children: schedule.target.resourceType }),
2191
- /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "outline", children: schedule.scheduleConfig.type })
2192
- ] })
2193
- ]
2194
- },
2195
- schedule.id
2196
- );
2197
- }) })
2198
- ] }),
2199
- viewMode === "buckets" && /* @__PURE__ */ jsxs(Fragment, { children: [
2200
- timeBuckets.today.length > 0 && /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
2201
- /* @__PURE__ */ jsxs(Group, { justify: "space-between", children: [
2202
- /* @__PURE__ */ jsx(Title, { order: 4, children: "Today" }),
2203
- /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", children: timeBuckets.today.length })
2204
- ] }),
2205
- renderScheduleCards(timeBuckets.today)
2206
- ] }),
2207
- timeBuckets.tomorrow.length > 0 && /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
2208
- /* @__PURE__ */ jsxs(Group, { justify: "space-between", children: [
2209
- /* @__PURE__ */ jsx(Title, { order: 4, children: "Tomorrow" }),
2210
- /* @__PURE__ */ jsx(Badge, { color: "orange", size: "sm", variant: "light", children: timeBuckets.tomorrow.length })
2211
- ] }),
2212
- renderScheduleCards(timeBuckets.tomorrow)
2213
- ] }),
2214
- timeBuckets.thisWeek.length > 0 && /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
2215
- /* @__PURE__ */ jsxs(Group, { justify: "space-between", children: [
2216
- /* @__PURE__ */ jsx(Title, { order: 4, children: "This Week" }),
2217
- /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", children: timeBuckets.thisWeek.length })
2218
- ] }),
2219
- renderScheduleCards(timeBuckets.thisWeek)
2220
- ] }),
2221
- timeBuckets.later.length > 0 && /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
2222
- /* @__PURE__ */ jsxs(Group, { justify: "space-between", children: [
2223
- /* @__PURE__ */ jsx(Title, { order: 4, children: "Later" }),
2224
- /* @__PURE__ */ jsx(Badge, { color: "gray", size: "sm", variant: "light", children: timeBuckets.later.length })
2225
- ] }),
2226
- renderScheduleCards(timeBuckets.later)
2227
- ] }),
2228
- timeBuckets.noNextRun.length > 0 && /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
2229
- /* @__PURE__ */ jsxs(Group, { justify: "space-between", children: [
2230
- /* @__PURE__ */ jsx(Text, { fw: 600, size: "md", c: "dimmed", children: "No Scheduled Run" }),
2231
- /* @__PURE__ */ jsx(Badge, { color: "gray", size: "sm", variant: "light", children: timeBuckets.noNextRun.length })
2232
- ] }),
2233
- renderScheduleCards(timeBuckets.noNextRun)
2234
- ] })
2235
- ] }),
2236
- filteredSchedules.length === 0 && /* @__PURE__ */ jsx(
2237
- EmptyState,
2238
- {
2239
- icon: IconClock,
2240
- title: "No schedules found",
2241
- description: statusFilter !== "all" || typeFilter !== "all" ? "No schedules match your filters. Try adjusting the filters or create a new schedule." : "No schedules yet. Use the CLI to create automated task execution."
2242
- }
2243
- )
2244
- ] }) }),
2245
- /* @__PURE__ */ jsx(CreateScheduleModal, { opened: isModalOpen, onClose: () => setIsModalOpen(false) }),
2246
- /* @__PURE__ */ jsx(
2247
- DeleteScheduleModal,
2248
- {
2249
- opened: isDeleteModalOpen,
2250
- onClose: handleCloseDeleteModal,
2251
- onConfirm: handleConfirmDelete,
2252
- schedule: scheduleToDelete,
2253
- isDeleting: deleteSchedule.isPending
2254
- }
2255
- ),
2256
- /* @__PURE__ */ jsx(
2257
- ScheduleDetailModal,
2258
- {
2259
- opened: !!selectedSchedule,
2260
- onClose: () => setSelectedSchedule(null),
2261
- schedule: selectedSchedule,
2262
- resourceStatus: selectedSchedule ? resourceStatusMap.get(selectedSchedule.target.resourceId) : void 0
2263
- }
2264
- )
2265
- ] });
2266
- };
2267
- var tokens = {
2268
- cardBg: "rgba(16, 16, 20, 0.92)",
2269
- cardBorder: "rgba(255, 255, 255, 0.06)",
2270
- accentRed: "#ff5555",
2271
- accentRedDim: "rgba(255, 85, 85, 0.15)",
2272
- textPrimary: "#e8e8e8",
2273
- textSecondary: "rgba(255, 255, 255, 0.5)",
2274
- textTertiary: "rgba(255, 255, 255, 0.3)",
2275
- codeBg: "rgba(0, 0, 0, 0.35)",
2276
- codeBorder: "rgba(255, 255, 255, 0.05)",
2277
- fileHighlight: "#7dd3fc",
2278
- separator: "rgba(255, 255, 255, 0.06)",
2279
- mono: "'JetBrains Mono', 'Fira Code', 'Cascadia Code', ui-monospace, monospace",
2280
- sans: "'Inter', -apple-system, system-ui, sans-serif"
2281
- };
2282
- function parseStackFrames(stack) {
2283
- return stack.split("\n").filter((line) => line.trim().startsWith("at ")).map((line) => {
2284
- const trimmed = line.trim();
2285
- const parenMatch = trimmed.match(/^at\s+(.+?)\s+\((.+)\)$/);
2286
- if (parenMatch) {
2287
- return { fn: parenMatch[1], file: parenMatch[2], raw: trimmed };
2288
- }
2289
- const simpleMatch = trimmed.match(/^at\s+(.+)$/);
2290
- if (simpleMatch) {
2291
- return { fn: "", file: simpleMatch[1], raw: trimmed };
2292
- }
2293
- return { fn: "", file: "", raw: trimmed };
2294
- });
2295
- }
2296
- var COMPONENT_NOISE = /* @__PURE__ */ new Set([
2297
- "div",
2298
- "main",
2299
- "span",
2300
- "section",
2301
- "Lazy",
2302
- "Suspense",
2303
- "SafeFragment",
2304
- "MatchImpl",
2305
- "MatchInnerImpl",
2306
- "OutletImpl",
2307
- "CatchBoundaryImpl",
2308
- "CatchBoundary",
2309
- "MatchesInner",
2310
- "Matches",
2311
- "SplitComponent"
2312
- ]);
2313
- function isNoiseComponent(name) {
2314
- if (COMPONENT_NOISE.has(name)) return true;
2315
- if (name.startsWith("@mantine/") || name.startsWith("@tanstack/")) return true;
2316
- return false;
2317
- }
2318
- function parseComponentStack(stack) {
2319
- return stack.trim().split("\n").map((line) => line.trim()).filter((line) => line.startsWith("at ")).map((line) => {
2320
- const match = line.match(/^at\s+(\S+)/);
2321
- return match ? match[1] : line.replace(/^at\s+/, "");
2322
- }).filter((name) => !isNoiseComponent(name));
2323
- }
2324
- function SectionLabel({ children }) {
2325
- return /* @__PURE__ */ jsx(
2326
- "div",
2327
- {
2328
- style: {
2329
- fontSize: 10,
2330
- fontFamily: tokens.sans,
2331
- fontWeight: 600,
2332
- textTransform: "uppercase",
2333
- letterSpacing: "0.08em",
2334
- color: tokens.textTertiary,
2335
- marginBottom: 6
2336
- },
2337
- children
2338
- }
2339
- );
2340
- }
2341
- function CodeBlock({ children, style }) {
2342
- return /* @__PURE__ */ jsx(
2343
- "div",
2344
- {
2345
- style: {
2346
- background: tokens.codeBg,
2347
- border: `1px solid ${tokens.codeBorder}`,
2348
- borderRadius: 6,
2349
- padding: "10px 14px",
2350
- fontFamily: tokens.mono,
2351
- fontSize: 12,
2352
- lineHeight: 1.6,
2353
- color: tokens.textPrimary,
2354
- overflowX: "auto",
2355
- ...style
2356
- },
2357
- children
2358
- }
2359
- );
2360
- }
2361
- function StackFrameRow({ frame }) {
2362
- return /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 8, alignItems: "baseline" }, children: [
2363
- frame.fn && /* @__PURE__ */ jsx("span", { style: { color: tokens.textPrimary, flexShrink: 0 }, children: frame.fn }),
2364
- /* @__PURE__ */ jsx("span", { style: { color: tokens.fileHighlight, opacity: 0.7, fontSize: 11 }, children: frame.file })
2365
- ] });
2366
- }
2367
- function Separator() {
2368
- return /* @__PURE__ */ jsx("div", { style: { height: 1, background: tokens.separator, margin: "4px 0" } });
2369
- }
2370
- function ErrorReportCard({
2371
- errorName,
2372
- errorMessage,
2373
- errorStack,
2374
- componentStack,
2375
- eventId,
2376
- url,
2377
- timestamp,
2378
- reportText,
2379
- appVersion
2380
- }) {
2381
- const [expanded, setExpanded] = useState(true);
2382
- const frames = errorStack ? parseStackFrames(errorStack) : [];
2383
- const components = componentStack ? parseComponentStack(componentStack) : [];
2384
- return /* @__PURE__ */ jsxs(
2385
- "div",
2386
- {
2387
- style: {
2388
- background: tokens.cardBg,
2389
- border: `1px solid ${tokens.cardBorder}`,
2390
- borderTop: `3px solid ${tokens.accentRed}`,
2391
- borderRadius: 10,
2392
- maxWidth: 720,
2393
- width: "100%",
2394
- fontFamily: tokens.sans,
2395
- backdropFilter: "blur(20px)",
2396
- boxShadow: "0 25px 60px rgba(0, 0, 0, 0.5), 0 0 40px rgba(255, 85, 85, 0.05)"
2397
- },
2398
- children: [
2399
- /* @__PURE__ */ jsxs("div", { style: { padding: "20px 24px 0" }, children: [
2400
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 12 }, children: [
2401
- /* @__PURE__ */ jsx("div", { style: { display: "flex", alignItems: "center", gap: 10 }, children: /* @__PURE__ */ jsx(
2402
- "span",
2403
- {
2404
- style: {
2405
- background: tokens.accentRedDim,
2406
- color: tokens.accentRed,
2407
- fontSize: 12,
2408
- fontWeight: 600,
2409
- fontFamily: tokens.mono,
2410
- padding: "3px 10px",
2411
- borderRadius: 4,
2412
- letterSpacing: "0.02em"
2413
- },
2414
- children: errorName
2415
- }
2416
- ) }),
2417
- /* @__PURE__ */ jsx(CopyButton, { value: reportText, timeout: 2e3, children: ({ copied, copy }) => /* @__PURE__ */ jsx(Tooltip, { label: copied ? "Copied!" : "Copy error report", position: "left", children: /* @__PURE__ */ jsxs(
2418
- "button",
2419
- {
2420
- onClick: copy,
2421
- style: {
2422
- background: copied ? "rgba(45, 212, 191, 0.15)" : "rgba(255, 255, 255, 0.06)",
2423
- border: `1px solid ${copied ? "rgba(45, 212, 191, 0.3)" : "rgba(255, 255, 255, 0.08)"}`,
2424
- borderRadius: 6,
2425
- padding: "5px 12px",
2426
- color: copied ? "#2dd4bf" : tokens.textSecondary,
2427
- cursor: "pointer",
2428
- display: "flex",
2429
- alignItems: "center",
2430
- gap: 6,
2431
- fontSize: 12,
2432
- fontFamily: tokens.sans,
2433
- transition: "all 0.15s ease"
2434
- },
2435
- children: [
2436
- copied ? /* @__PURE__ */ jsx(IconCheck, { size: 13 }) : /* @__PURE__ */ jsx(IconCopy, { size: 13 }),
2437
- copied ? "Copied" : "Copy"
2438
- ]
2439
- }
2440
- ) }) })
2441
- ] }),
2442
- /* @__PURE__ */ jsx(
2443
- "div",
2444
- {
2445
- style: {
2446
- fontSize: 16,
2447
- fontWeight: 500,
2448
- color: tokens.textPrimary,
2449
- lineHeight: 1.5,
2450
- marginBottom: 16
2451
- },
2452
- children: errorMessage
2453
- }
2454
- ),
2455
- /* @__PURE__ */ jsxs(
2456
- "div",
2457
- {
2458
- style: {
2459
- background: tokens.codeBg,
2460
- border: `1px solid ${tokens.codeBorder}`,
2461
- borderRadius: 6,
2462
- padding: "8px 12px",
2463
- fontFamily: tokens.mono,
2464
- fontSize: 12,
2465
- color: tokens.fileHighlight,
2466
- marginBottom: 16,
2467
- display: "flex",
2468
- alignItems: "center",
2469
- gap: 8
2470
- },
2471
- children: [
2472
- /* @__PURE__ */ jsx("span", { style: { color: tokens.textTertiary, fontSize: 10, flexShrink: 0 }, children: "URL" }),
2473
- /* @__PURE__ */ jsx("span", { style: { opacity: 0.8 }, children: url })
2474
- ]
2475
- }
2476
- )
2477
- ] }),
2478
- /* @__PURE__ */ jsx("div", { style: { padding: "0 24px" }, children: /* @__PURE__ */ jsxs(
2479
- "button",
2480
- {
2481
- onClick: () => setExpanded((v) => !v),
2482
- style: {
2483
- background: "none",
2484
- border: "none",
2485
- color: tokens.textTertiary,
2486
- fontSize: 11,
2487
- fontFamily: tokens.sans,
2488
- cursor: "pointer",
2489
- padding: "4px 0",
2490
- display: "flex",
2491
- alignItems: "center",
2492
- gap: 4,
2493
- letterSpacing: "0.05em",
2494
- textTransform: "uppercase",
2495
- fontWeight: 600
2496
- },
2497
- children: [
2498
- expanded ? "- Hide" : "+ Show",
2499
- " details"
2500
- ]
2501
- }
2502
- ) }),
2503
- expanded && /* @__PURE__ */ jsxs("div", { style: { padding: "8px 24px 20px" }, children: [
2504
- frames.length > 0 && /* @__PURE__ */ jsxs("div", { style: { marginBottom: 16 }, children: [
2505
- /* @__PURE__ */ jsx(SectionLabel, { children: "Stack Trace" }),
2506
- /* @__PURE__ */ jsx(CodeBlock, { style: { maxHeight: 220, overflowY: "auto" }, children: frames.map((frame, i) => /* @__PURE__ */ jsx(StackFrameRow, { frame }, i)) })
2507
- ] }),
2508
- components.length > 0 && /* @__PURE__ */ jsxs("div", { style: { marginBottom: 16 }, children: [
2509
- /* @__PURE__ */ jsx(SectionLabel, { children: "Component Tree" }),
2510
- /* @__PURE__ */ jsx(CodeBlock, { style: { maxHeight: 120, overflowY: "auto" }, children: /* @__PURE__ */ jsx("div", { style: { display: "flex", flexWrap: "wrap", gap: "2px 0", alignItems: "center" }, children: components.map((name, i) => /* @__PURE__ */ jsxs("span", { style: { display: "inline-flex", alignItems: "center" }, children: [
2511
- /* @__PURE__ */ jsx(
2512
- "span",
2513
- {
2514
- style: {
2515
- color: i === 0 ? tokens.accentRed : tokens.textPrimary,
2516
- fontWeight: i === 0 ? 600 : 400
2517
- },
2518
- children: name
2519
- }
2520
- ),
2521
- i < components.length - 1 && /* @__PURE__ */ jsx("span", { style: { color: tokens.textTertiary, margin: "0 6px", fontSize: 10 }, children: "\u203A" })
2522
- ] }, i)) }) })
2523
- ] }),
2524
- /* @__PURE__ */ jsx(Separator, {}),
2525
- /* @__PURE__ */ jsxs(
2526
- "div",
2527
- {
2528
- style: {
2529
- display: "grid",
2530
- gridTemplateColumns: "auto 1fr",
2531
- gap: "4px 16px",
2532
- fontSize: 11,
2533
- fontFamily: tokens.mono,
2534
- paddingTop: 12
2535
- },
2536
- children: [
2537
- appVersion && /* @__PURE__ */ jsxs(Fragment, { children: [
2538
- /* @__PURE__ */ jsx("span", { style: { color: tokens.textTertiary }, children: "version" }),
2539
- /* @__PURE__ */ jsx("span", { style: { color: tokens.textSecondary }, children: appVersion })
2540
- ] }),
2541
- /* @__PURE__ */ jsx("span", { style: { color: tokens.textTertiary }, children: "timestamp" }),
2542
- /* @__PURE__ */ jsx("span", { style: { color: tokens.textSecondary }, children: timestamp }),
2543
- /* @__PURE__ */ jsx("span", { style: { color: tokens.textTertiary }, children: "viewport" }),
2544
- /* @__PURE__ */ jsxs("span", { style: { color: tokens.textSecondary }, children: [
2545
- window.innerWidth,
2546
- "x",
2547
- window.innerHeight
2548
- ] }),
2549
- eventId && /* @__PURE__ */ jsxs(Fragment, { children: [
2550
- /* @__PURE__ */ jsx("span", { style: { color: tokens.textTertiary }, children: "sentry" }),
2551
- /* @__PURE__ */ jsx("span", { style: { color: tokens.textSecondary }, children: eventId })
2552
- ] })
2553
- ]
2554
- }
2555
- )
2556
- ] }),
2557
- /* @__PURE__ */ jsx(
2558
- "div",
2559
- {
2560
- style: {
2561
- borderTop: `1px solid ${tokens.separator}`,
2562
- padding: "12px 24px",
2563
- display: "flex",
2564
- justifyContent: "center"
2565
- },
2566
- children: /* @__PURE__ */ jsx(
2567
- "button",
2568
- {
2569
- onClick: () => window.location.reload(),
2570
- style: {
2571
- background: "none",
2572
- border: "none",
2573
- color: tokens.textTertiary,
2574
- fontSize: 12,
2575
- fontFamily: tokens.sans,
2576
- cursor: "pointer",
2577
- padding: "2px 8px",
2578
- textDecoration: "underline",
2579
- textUnderlineOffset: 3
2580
- },
2581
- children: "Reload page"
2582
- }
2583
- )
2584
- }
2585
- )
2586
- ]
2587
- }
2588
- );
2589
- }
2590
- function getErrorName(error) {
2591
- if (error instanceof Error) {
2592
- return error.constructor.name === "Error" ? "Error" : error.constructor.name;
2593
- }
2594
- return "Error";
2595
- }
2596
- function buildErrorReport(opts) {
2597
- const { error, componentStack, eventId } = opts;
2598
- const errorMessage = error instanceof Error ? error.message : String(error);
2599
- const errorStack = error instanceof Error ? error.stack : void 0;
2600
- const url = opts.url ?? window.location.href;
2601
- const sections = [
2602
- `## Error Report`,
2603
- `**Timestamp:** ${(/* @__PURE__ */ new Date()).toISOString()}`,
2604
- `**URL:** ${url}`,
2605
- opts.appVersion && `**App Version:** ${opts.appVersion}`,
2606
- eventId && `**Sentry Event ID:** ${eventId}`,
2607
- `**User Agent:** ${navigator.userAgent}`,
2608
- `**Viewport:** ${window.innerWidth}x${window.innerHeight}`,
2609
- "",
2610
- `### Error`,
2611
- "```",
2612
- `${getErrorName(error)}: ${errorMessage}`,
2613
- "```",
2614
- "",
2615
- errorStack && `### Stack Trace`,
2616
- errorStack && "```",
2617
- errorStack,
2618
- errorStack && "```",
2619
- errorStack && "",
2620
- componentStack && `### Component Stack`,
2621
- componentStack && "```",
2622
- componentStack?.trim(),
2623
- componentStack && "```"
2624
- ];
2625
- return sections.filter(Boolean).join("\n");
2626
- }
2627
- function CrashErrorFallback({ error, componentStack, eventId, appVersion }) {
2628
- const { background } = useAppearance();
2629
- const errorMessage = error instanceof Error ? error.message : String(error);
2630
- const errorStack = error instanceof Error ? error.stack : void 0;
2631
- const errorName = getErrorName(error);
2632
- const reportText = buildErrorReport({ error, componentStack, eventId, appVersion });
2633
- const timestamp = (/* @__PURE__ */ new Date()).toISOString();
2634
- return /* @__PURE__ */ jsxs(
2635
- "div",
2636
- {
2637
- style: {
2638
- position: "fixed",
2639
- inset: 0,
2640
- overflow: "auto",
2641
- background: "var(--color-background)",
2642
- display: "flex",
2643
- alignItems: "center",
2644
- justifyContent: "center",
2645
- padding: 24
2646
- },
2647
- children: [
2648
- background,
2649
- /* @__PURE__ */ jsx("div", { style: { position: "relative", zIndex: 1, width: "100%", display: "flex", justifyContent: "center" }, children: /* @__PURE__ */ jsx(
2650
- ErrorReportCard,
2651
- {
2652
- errorName,
2653
- errorMessage,
2654
- errorStack,
2655
- componentStack,
2656
- eventId,
2657
- url: window.location.href,
2658
- timestamp,
2659
- reportText,
2660
- appVersion
2661
- }
2662
- ) })
2663
- ]
2664
- }
2665
- );
2666
- }
2667
- var AppErrorBoundary = class extends Component {
2668
- state = { error: null, componentStack: "" };
2669
- static getDerivedStateFromError(error) {
2670
- return { error };
2671
- }
2672
- componentDidCatch(error, errorInfo) {
2673
- this.setState({ componentStack: errorInfo.componentStack ?? "" });
2674
- this.props.onError?.(error, errorInfo);
2675
- }
2676
- render() {
2677
- if (this.state.error !== null) {
2678
- const props = { error: this.state.error, componentStack: this.state.componentStack };
2679
- return this.props.fallback ? this.props.fallback(props) : /* @__PURE__ */ jsx(CrashErrorFallback, { ...props, appVersion: this.props.appVersion });
2680
- }
2681
- return this.props.children;
2682
- }
2683
- };
2684
- function RichTextEditor({ content, onChange, placeholder }) {
2685
- const editor = useEditor({
2686
- extensions: [
2687
- StarterKit,
2688
- Link,
2689
- Placeholder.configure({
2690
- placeholder: placeholder || "Write something..."
2691
- })
2692
- ],
2693
- content,
2694
- onUpdate: ({ editor: editor2 }) => onChange(editor2.getHTML())
2695
- });
2696
- return /* @__PURE__ */ jsxs(RichTextEditor$1, { editor, children: [
2697
- /* @__PURE__ */ jsxs(RichTextEditor$1.Toolbar, { sticky: true, stickyOffset: 60, children: [
2698
- /* @__PURE__ */ jsxs(RichTextEditor$1.ControlsGroup, { children: [
2699
- /* @__PURE__ */ jsx(RichTextEditor$1.Bold, {}),
2700
- /* @__PURE__ */ jsx(RichTextEditor$1.Italic, {}),
2701
- /* @__PURE__ */ jsx(RichTextEditor$1.Strikethrough, {})
2702
- ] }),
2703
- /* @__PURE__ */ jsxs(RichTextEditor$1.ControlsGroup, { children: [
2704
- /* @__PURE__ */ jsx(RichTextEditor$1.H2, {}),
2705
- /* @__PURE__ */ jsx(RichTextEditor$1.H3, {})
2706
- ] }),
2707
- /* @__PURE__ */ jsxs(RichTextEditor$1.ControlsGroup, { children: [
2708
- /* @__PURE__ */ jsx(RichTextEditor$1.BulletList, {}),
2709
- /* @__PURE__ */ jsx(RichTextEditor$1.OrderedList, {})
2710
- ] }),
2711
- /* @__PURE__ */ jsxs(RichTextEditor$1.ControlsGroup, { children: [
2712
- /* @__PURE__ */ jsx(RichTextEditor$1.Link, {}),
2713
- /* @__PURE__ */ jsx(RichTextEditor$1.Unlink, {})
2714
- ] })
2715
- ] }),
2716
- /* @__PURE__ */ jsx(RichTextEditor$1.Content, {})
2717
- ] });
2718
- }
2719
-
2720
- // ../../node_modules/.pnpm/@mdx-js+mdx@3.1.1/node_modules/@mdx-js/mdx/lib/run.js
2721
- var AsyncFunction = Object.getPrototypeOf(run).constructor;
2722
- async function run(code, options) {
2723
- return new AsyncFunction(String(code))(options);
2724
- }
2725
- var customCodeTheme = Object.fromEntries(
2726
- Object.entries(oneDark).map(([key, value]) => [
2727
- key,
2728
- typeof value === "object" && value !== null ? { ...value, background: "none", backgroundColor: "none" } : value
2729
- ])
2730
- );
2731
- function Callout({ type = "info", children }) {
2732
- const colorMap = { info: "blue", warning: "yellow", error: "red" };
2733
- const color = colorMap[type] ?? "blue";
2734
- return /* @__PURE__ */ jsx(
2735
- Paper,
2736
- {
2737
- withBorder: true,
2738
- p: "sm",
2739
- my: "xs",
2740
- style: {
2741
- borderLeft: `3px solid var(--mantine-color-${color}-6)`,
2742
- backgroundColor: `var(--mantine-color-${color}-light)`
2743
- },
2744
- children
2745
- }
2746
- );
2747
- }
2748
- function TabsWrapper({ children }) {
2749
- const tabs = [];
2750
- const childArray = Array.isArray(children) ? children : [children];
2751
- for (const child of childArray) {
2752
- if (child && typeof child === "object" && "props" in child && child.props?.label) {
2753
- tabs.push({ label: child.props.label, content: child.props.children });
2754
- }
2755
- }
2756
- if (tabs.length === 0) return /* @__PURE__ */ jsx(Fragment, { children });
2757
- return /* @__PURE__ */ jsxs(Tabs, { defaultValue: tabs[0]?.label, my: "xs", children: [
2758
- /* @__PURE__ */ jsx(Tabs.List, { children: tabs.map((tab) => /* @__PURE__ */ jsx(Tabs.Tab, { value: tab.label, children: tab.label }, tab.label)) }),
2759
- tabs.map((tab) => /* @__PURE__ */ jsx(Tabs.Panel, { value: tab.label, pt: "xs", children: tab.content }, tab.label))
2760
- ] });
2761
- }
2762
- function Tab({ children }) {
2763
- return /* @__PURE__ */ jsx(Fragment, { children });
2764
- }
2765
- function slugify(text) {
2766
- return String(text).toLowerCase().replace(/[^\w\s-]/g, "").replace(/\s+/g, "-").trim();
2767
- }
2768
- var mdxComponents = {
2769
- h1: ({ children }) => /* @__PURE__ */ jsx(Title, { order: 3, id: slugify(children), mb: "xs", mt: "md", children }),
2770
- h2: ({ children }) => /* @__PURE__ */ jsx(Title, { order: 3, id: slugify(children), mb: "xs", mt: "sm", children }),
2771
- p: ({ children }) => /* @__PURE__ */ jsx(
2772
- "p",
2773
- {
2774
- style: {
2775
- margin: "0.5rem 0",
2776
- fontSize: "var(--mantine-font-size-md)",
2777
- lineHeight: 1.65,
2778
- color: "var(--color-text-dimmed)"
2779
- },
2780
- children
2781
- }
2782
- ),
2783
- ul: ({ children }) => /* @__PURE__ */ jsx("ul", { style: { margin: "0.5rem 0", paddingLeft: "1.5rem" }, children }),
2784
- ol: ({ children }) => /* @__PURE__ */ jsx("ol", { style: { margin: "0.5rem 0", paddingLeft: "1.5rem" }, children }),
2785
- li: ({ children }) => /* @__PURE__ */ jsx("li", { style: { marginBottom: "0.25rem", fontSize: "var(--mantine-font-size-md)", color: "var(--color-text-dimmed)" }, children }),
2786
- code: ({ className, children }) => {
2787
- const match = /language-(\w+)/.exec(className || "");
2788
- const codeString = String(children).replace(/\n$/, "");
2789
- if (match) {
2790
- return /* @__PURE__ */ jsx(
2791
- Prism,
2792
- {
2793
- style: customCodeTheme,
2794
- language: match[1],
2795
- PreTag: "div",
2796
- customStyle: {
2797
- margin: "0.5rem 0",
2798
- borderRadius: "var(--mantine-radius-default)",
2799
- fontSize: "0.8rem"
2800
- },
2801
- children: codeString
2802
- }
2803
- );
2804
- }
2805
- return /* @__PURE__ */ jsx(Code, { children });
2806
- },
2807
- pre: ({ children }) => /* @__PURE__ */ jsx(ScrollArea, { type: "auto", my: "xs", children }),
2808
- hr: () => /* @__PURE__ */ jsx(
2809
- "hr",
2810
- {
2811
- style: {
2812
- border: "none",
2813
- borderTop: "1px solid var(--color-border)",
2814
- margin: "1rem 0"
2815
- }
2816
- }
2817
- ),
2818
- blockquote: ({ children }) => /* @__PURE__ */ jsx(
2819
- Paper,
2820
- {
2821
- withBorder: true,
2822
- p: "sm",
2823
- my: "xs",
2824
- style: {
2825
- borderLeft: "3px solid var(--color-primary)",
2826
- backgroundColor: "var(--color-surface)"
2827
- },
2828
- children
2829
- }
2830
- ),
2831
- table: ({ children }) => /* @__PURE__ */ jsx(ScrollArea, { type: "auto", my: "xs", children: /* @__PURE__ */ jsx(
2832
- Table,
2833
- {
2834
- striped: true,
2835
- highlightOnHover: true,
2836
- withTableBorder: true,
2837
- withColumnBorders: true,
2838
- fz: "sm",
2839
- styles: {
2840
- table: {
2841
- "--table-border-color": "var(--color-border)",
2842
- "--table-striped-color": "var(--color-surface-hover)",
2843
- "--table-highlight-on-hover-color": "var(--color-surface-hover)"
2844
- }
2845
- },
2846
- children
2847
- }
2848
- ) }),
2849
- thead: ({ children }) => /* @__PURE__ */ jsx(Table.Thead, { children }),
2850
- tbody: ({ children }) => /* @__PURE__ */ jsx(Table.Tbody, { children }),
2851
- tr: ({ children }) => /* @__PURE__ */ jsx(Table.Tr, { children }),
2852
- th: ({ children }) => /* @__PURE__ */ jsx(Table.Th, { style: { whiteSpace: "nowrap" }, children }),
2853
- td: ({ children }) => /* @__PURE__ */ jsx(Table.Td, { style: { color: "var(--color-text-dimmed)" }, children }),
2854
- Callout,
2855
- Tabs: TabsWrapper,
2856
- Tab
2857
- };
2858
- function MdxRenderer({ compiledSource }) {
2859
- const [Content, setContent] = useState(null);
2860
- const [error, setError] = useState(null);
2861
- useEffect(() => {
2862
- let cancelled = false;
2863
- run(compiledSource, { ...runtime, baseUrl: import.meta.url }).then((mod) => {
2864
- if (!cancelled) setContent(() => mod.default);
2865
- }).catch((err) => {
2866
- if (!cancelled) setError(err instanceof Error ? err.message : String(err));
2867
- });
2868
- return () => {
2869
- cancelled = true;
2870
- };
2871
- }, [compiledSource]);
2872
- if (error) {
2873
- return /* @__PURE__ */ jsxs("div", { style: { color: "var(--color-error)", padding: "1rem" }, children: [
2874
- "Error rendering documentation: ",
2875
- error
2876
- ] });
2877
- }
2878
- if (!Content) return null;
2879
- return /* @__PURE__ */ jsx(Content, { components: mdxComponents });
2880
- }
2881
- var Breadcrumbs = (props) => {
2882
- const { Link: Link2 } = useRouterContext();
2883
- const breadcrumbs = useBreadcrumbs(props);
2884
- const items = breadcrumbs.map((item) => {
2885
- const isActive = item.isActive;
2886
- if (isActive) {
2887
- return /* @__PURE__ */ jsx(Text, { size: "sm", fw: 500, c: "var(--color-text)", children: item.label }, item.path || item.label);
2888
- }
2889
- return item.path ? /* @__PURE__ */ jsx(
2890
- Text,
2891
- {
2892
- component: Link2,
2893
- to: item.path,
2894
- size: "sm",
2895
- c: "var(--color-text-subtle)",
2896
- style: {
2897
- textDecoration: "none",
2898
- fontWeight: 500
2899
- },
2900
- children: item.label
2901
- },
2902
- item.path
2903
- ) : /* @__PURE__ */ jsx(Text, { size: "sm", c: "var(--color-text-subtle)", children: item.label }, item.label);
2904
- });
2905
- return /* @__PURE__ */ jsx(
2906
- Breadcrumbs$1,
2907
- {
2908
- separator: "/",
2909
- styles: {
2910
- separator: {
2911
- color: "var(--color-text-subtle)"
2912
- }
2913
- },
2914
- children: items
2915
- }
2916
- );
2917
- };
2918
- function formatStateLabel(stateKey) {
2919
- if (!stateKey) return "No state";
2920
- return stateKey.split("_").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
2921
- }
2922
- function DealKanbanCard({ deal, config, onClick, onDragStart, onDragEnd }) {
2923
- const contactFirstName = deal.contact?.first_name || "";
2924
- const contactLastName = deal.contact?.last_name || "";
2925
- const contactName = [contactFirstName, contactLastName].filter(Boolean).join(" ");
2926
- const displayName = contactName || deal.contact_email || "Unknown";
2927
- const companyName = deal.contact?.company?.name || deal.discovery_data?.company || deal.contact_email?.split("@")[1] || null;
2928
- const hasInitialFee = typeof deal.initial_fee === "number" && deal.initial_fee > 0;
2929
- const hasMonthlyFee = typeof deal.monthly_fee === "number" && deal.monthly_fee > 0;
2930
- const stageColor = (deal.stage_key ? config[deal.stage_key]?.color : null) ?? "gray";
2931
- const stateLabel = formatStateLabel(deal.state_key);
2932
- return /* @__PURE__ */ jsx(
2933
- Paper,
2934
- {
2935
- p: "sm",
2936
- style: {
2937
- border: "1px solid var(--color-border)",
2938
- borderLeft: `3px solid var(--mantine-color-${stageColor}-6)`,
2939
- cursor: "grab",
2940
- userSelect: "none",
2941
- transition: `all var(--duration-fast) var(--easing)`
2942
- },
2943
- draggable: true,
2944
- onDragStart: (e) => onDragStart(e, deal),
2945
- onDragEnd,
2946
- onClick: () => onClick(deal),
2947
- children: /* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
2948
- /* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "flex-start", gap: "xs", wrap: "nowrap", children: [
2949
- /* @__PURE__ */ jsx(Text, { size: "sm", fw: 600, lineClamp: 1, style: { minWidth: 0, flex: 1 }, children: displayName }),
2950
- /* @__PURE__ */ jsx(Tooltip, { label: deal.priority.reason, withArrow: true, children: /* @__PURE__ */ jsx(
2951
- Badge,
2952
- {
2953
- variant: "light",
2954
- size: "xs",
2955
- color: deal.priority.color,
2956
- style: { flexShrink: 0, maxWidth: 112, textTransform: "none" },
2957
- children: /* @__PURE__ */ jsx(Box, { component: "span", style: { display: "block", maxWidth: 88, overflow: "hidden", textOverflow: "ellipsis" }, children: deal.priority.label })
2958
- }
2959
- ) })
2960
- ] }),
2961
- /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", lineClamp: 1, children: [
2962
- "State: ",
2963
- stateLabel
2964
- ] }),
2965
- companyName && /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", lineClamp: 1, children: companyName }),
2966
- /* @__PURE__ */ jsxs(Group, { gap: 4, wrap: "wrap", children: [
2967
- hasInitialFee && /* @__PURE__ */ jsxs(Badge, { variant: "outline", size: "xs", color: "green", children: [
2968
- "$",
2969
- deal.initial_fee.toLocaleString()
2970
- ] }),
2971
- hasMonthlyFee && /* @__PURE__ */ jsxs(Badge, { variant: "outline", size: "xs", color: "teal", children: [
2972
- "$",
2973
- deal.monthly_fee.toLocaleString(),
2974
- "/mo"
2975
- ] })
2976
- ] }),
2977
- /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: formatTimeAgo(deal.updated_at) })
2978
- ] })
2979
- }
2980
- );
2981
- }
2982
- var UNSTAGED_KEY = "unstaged";
2983
- var KANBAN_FETCH_LIMIT = 250;
2984
- function formatStageLabel(stage) {
2985
- if (stage === UNSTAGED_KEY) return "Unstaged";
2986
- return stage.split("_").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
2987
- }
2988
- function groupDealsByStage(deals, columns) {
2989
- const map = /* @__PURE__ */ new Map();
2990
- for (const stage of columns) map.set(stage, []);
2991
- for (const deal of deals) {
2992
- const key = deal.stage_key ?? UNSTAGED_KEY;
2993
- const bucket = map.get(key);
2994
- if (bucket) {
2995
- bucket.push(deal);
2996
- } else {
2997
- map.get(UNSTAGED_KEY).push(deal);
2998
- }
2999
- }
3000
- for (const bucket of map.values()) {
3001
- bucket.sort((a, b) => compareDealsByPriority(a, b));
3002
- }
3003
- return map;
3004
- }
3005
- function KanbanBoard({ config }) {
3006
- const navigate = useNavigate();
3007
- const {
3008
- data: deals,
3009
- total,
3010
- isLoading,
3011
- error
3012
- } = useDeals({
3013
- limit: KANBAN_FETCH_LIMIT,
3014
- offset: 0
3015
- });
3016
- const transitionItem = useTransitionItem();
3017
- const queryClient = useQueryClient();
3018
- const [dragOverColumn, setDragOverColumn] = useState(null);
3019
- const draggingDealRef = useRef(null);
3020
- const resolvedConfig = config ?? {};
3021
- const configuredStages = Object.keys(resolvedConfig).filter(
3022
- (stage) => Boolean(resolvedConfig[stage])
3023
- );
3024
- const columns = [...configuredStages, UNSTAGED_KEY];
3025
- const hasConfig = configuredStages.length > 0;
3026
- const groupedDeals = groupDealsByStage(deals ?? [], columns);
3027
- const handleDragStart = useCallback((e, deal) => {
3028
- draggingDealRef.current = deal;
3029
- e.dataTransfer.effectAllowed = "move";
3030
- e.dataTransfer.setData("text/plain", deal.id);
3031
- }, []);
3032
- const handleDragOver = useCallback((e, column) => {
3033
- if (column === UNSTAGED_KEY) {
3034
- e.dataTransfer.dropEffect = "none";
3035
- return;
3036
- }
3037
- e.preventDefault();
3038
- e.dataTransfer.dropEffect = "move";
3039
- setDragOverColumn(column);
3040
- }, []);
3041
- const handleDragLeave = useCallback(() => {
3042
- setDragOverColumn(null);
3043
- }, []);
3044
- const handleDrop = useCallback(
3045
- (e, targetStage) => {
3046
- e.preventDefault();
3047
- setDragOverColumn(null);
3048
- if (targetStage === UNSTAGED_KEY) return;
3049
- const deal = draggingDealRef.current;
3050
- draggingDealRef.current = null;
3051
- if (!deal) return;
3052
- if (deal.stage_key === targetStage) return;
3053
- queryClient.setQueriesData({ queryKey: dealKeys.lists() }, (old) => {
3054
- if (!old) return old;
3055
- return {
3056
- ...old,
3057
- data: old.data.map((d) => d.id === deal.id ? { ...d, stage_key: targetStage } : d)
3058
- };
3059
- });
3060
- transitionItem.mutate({ dealId: deal.id, pipelineKey: "crm", stageKey: targetStage });
3061
- },
3062
- [queryClient, transitionItem]
3063
- );
3064
- const handleDragEnd = useCallback(() => {
3065
- draggingDealRef.current = null;
3066
- setDragOverColumn(null);
3067
- }, []);
3068
- const handleCardClick = useCallback(
3069
- (deal) => {
3070
- setDealReferrer("pipeline");
3071
- void navigate({ to: "/crm/deals/$dealId", params: { dealId: deal.id } });
3072
- },
3073
- [navigate]
3074
- );
3075
- if (isLoading) {
3076
- return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsx(Center, { style: { flex: 1 }, children: /* @__PURE__ */ jsx(Loader, {}) }) });
3077
- }
3078
- if (error) {
3079
- return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsx(Alert, { color: "red", children: "Failed to load deals" }) });
3080
- }
3081
- if (!hasConfig) {
3082
- return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsxs(Stack, { children: [
3083
- /* @__PURE__ */ jsx(PageTitleCaption, { title: "Pipeline", caption: "Kanban view of your deal pipeline" }),
3084
- /* @__PURE__ */ jsx(Paper, { withBorder: true, p: "xl", children: /* @__PURE__ */ jsx(Center, { py: "xl", children: /* @__PURE__ */ jsxs(Stack, { gap: "xs", align: "center", children: [
3085
- /* @__PURE__ */ jsx(Text, { fw: 600, children: "Configuration Required" }),
3086
- /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", ta: "center", children: "Provide a kanban stage config before rendering the pipeline board." })
3087
- ] }) }) })
3088
- ] }) });
3089
- }
3090
- return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsxs(Stack, { children: [
3091
- /* @__PURE__ */ jsx(PageTitleCaption, { title: "Pipeline", caption: "Kanban view of your deal pipeline" }),
3092
- total > deals.length && /* @__PURE__ */ jsxs(Alert, { color: "yellow", variant: "light", children: [
3093
- "Showing the first ",
3094
- deals.length,
3095
- " of ",
3096
- total,
3097
- " deals in the board. This view is temporarily bounded to the list endpoint until full-board pagination lands."
3098
- ] }),
3099
- /* @__PURE__ */ jsx(
3100
- Paper,
3101
- {
3102
- p: "lg",
3103
- style: {
3104
- border: "1px solid var(--color-border)",
3105
- overflow: "hidden"
3106
- },
3107
- 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) => {
3108
- const columnDeals = groupedDeals.get(columnKey) ?? [];
3109
- const isUnstaged = columnKey === UNSTAGED_KEY;
3110
- const isDragTarget = dragOverColumn === columnKey;
3111
- const badgeColor = isUnstaged ? "gray" : resolvedConfig[columnKey]?.color ?? "gray";
3112
- return /* @__PURE__ */ jsx(
3113
- Box,
3114
- {
3115
- style: {
3116
- width: 260,
3117
- minWidth: 260,
3118
- borderRadius: 8,
3119
- padding: "10px 8px",
3120
- backgroundColor: isDragTarget ? "color-mix(in srgb, var(--color-primary) 6%, transparent)" : "transparent",
3121
- border: isDragTarget ? "1px solid var(--color-primary)" : "1px solid transparent",
3122
- transition: `background-color var(--duration-fast) var(--easing), border-color var(--duration-fast) var(--easing)`
3123
- },
3124
- onDragOver: (e) => handleDragOver(e, columnKey),
3125
- onDragLeave: handleDragLeave,
3126
- onDrop: (e) => handleDrop(e, columnKey),
3127
- children: /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
3128
- /* @__PURE__ */ jsx(Group, { justify: "space-between", wrap: "nowrap", children: /* @__PURE__ */ jsxs(Group, { gap: 6, children: [
3129
- /* @__PURE__ */ jsx(
3130
- Box,
3131
- {
3132
- w: 10,
3133
- h: 10,
3134
- style: {
3135
- borderRadius: "50%",
3136
- background: `var(--mantine-color-${badgeColor}-6)`,
3137
- flexShrink: 0
3138
- }
3139
- }
3140
- ),
3141
- /* @__PURE__ */ jsx(Text, { size: "sm", fw: 600, lineClamp: 1, children: formatStageLabel(columnKey) }),
3142
- /* @__PURE__ */ jsx(Badge, { size: "xs", variant: "light", color: badgeColor, children: columnDeals.length })
3143
- ] }) }),
3144
- /* @__PURE__ */ jsx(Divider, {}),
3145
- 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" }),
3146
- /* @__PURE__ */ jsx(Stack, { gap: "xs", children: columnDeals.map((deal) => /* @__PURE__ */ jsx(
3147
- DealKanbanCard,
3148
- {
3149
- deal,
3150
- config: resolvedConfig,
3151
- onClick: handleCardClick,
3152
- onDragStart: handleDragStart,
3153
- onDragEnd: handleDragEnd
3154
- },
3155
- deal.id
3156
- )) })
3157
- ] })
3158
- },
3159
- columnKey
3160
- );
3161
- }) }) })
3162
- }
3163
- )
3164
- ] }) });
3165
- }
3166
-
3167
- // src/components/acquisition/kanban/constants.ts
3168
- var DEAL_STAGES = ["interested", "proposal", "closing", "closed_won", "closed_lost", "nurturing"];
3169
- var DEFAULT_KANBAN_CONFIG = {
3170
- interested: { color: "blue" },
3171
- proposal: { color: "yellow" },
3172
- closing: { color: "lime" },
3173
- closed_won: { color: "green" },
3174
- closed_lost: { color: "red" },
3175
- nurturing: { color: "grape" }
3176
- };
3177
- function NotificationPanel({ notifications, isLoading, onClose, onNavigate }) {
3178
- const markAllAsRead = useMarkAllAsRead();
3179
- const hasUnread = notifications.some((n) => !n.read);
3180
- const handleMarkAllAsRead = async () => {
3181
- await markAllAsRead.mutateAsync();
3182
- };
3183
- return /* @__PURE__ */ jsxs(Box, { children: [
3184
- /* @__PURE__ */ jsxs(Group, { justify: "space-between", p: "md", pb: "sm", children: [
3185
- /* @__PURE__ */ jsx(Text, { size: "sm", fw: 600, style: { fontFamily: "var(--mantine-font-family-headings)" }, children: "Notifications" }),
3186
- hasUnread && /* @__PURE__ */ jsx(Button, { variant: "subtle", size: "xs", onClick: handleMarkAllAsRead, loading: markAllAsRead.isPending, children: "Mark all as read" })
3187
- ] }),
3188
- /* @__PURE__ */ jsx(Divider, {}),
3189
- /* @__PURE__ */ jsx(ScrollArea, { h: 400, type: "auto", children: /* @__PURE__ */ jsx(
3190
- NotificationList,
3191
- {
3192
- notifications,
3193
- isLoading,
3194
- onClose,
3195
- onNavigate
3196
- }
3197
- ) })
3198
- ] });
3199
- }
3200
- function NotificationBell({ unreadCount, onNavigate }) {
3201
- const [opened, { toggle, close }] = useDisclosure(false);
3202
- const { apiRequest, isReady, workOSOrganizationId } = useElevasisServices();
3203
- const { data: apiCount = 0 } = useQuery({
3204
- queryKey: ["notifications", "unread-count", workOSOrganizationId],
3205
- queryFn: async () => {
3206
- const response = await apiRequest("/notifications/unread-count");
3207
- return response.count;
3208
- },
3209
- enabled: isReady,
3210
- staleTime: 3e4
3211
- });
3212
- const { data, isLoading } = useNotifications({ limit: 20 });
3213
- const notifications = data?.notifications ?? [];
3214
- const count = unreadCount ?? apiCount;
3215
- return /* @__PURE__ */ jsxs(Popover, { opened, onChange: toggle, position: "bottom-end", width: 400, children: [
3216
- /* @__PURE__ */ jsx(Tooltip, { label: "Notifications", disabled: opened, children: /* @__PURE__ */ jsx(Popover.Target, { children: /* @__PURE__ */ jsx(Indicator, { label: count > 99 ? "99+" : count, disabled: count === 0, size: 16, offset: 4, children: /* @__PURE__ */ jsx(ActionIcon, { variant: "subtle", size: "lg", onClick: toggle, children: /* @__PURE__ */ jsx(IconBell, { size: 20, color: "var(--color-text-subtle)" }) }) }) }) }),
3217
- /* @__PURE__ */ jsx(Popover.Dropdown, { p: 0, children: /* @__PURE__ */ jsx(
3218
- NotificationPanel,
3219
- {
3220
- notifications,
3221
- isLoading,
3222
- onClose: close,
3223
- onNavigate
3224
- }
3225
- ) })
3226
- ] });
3227
- }
3228
-
3229
- export { AbsoluteScheduleForm, ApiKeyDisplayModal, ApiKeyList, ApiKeySettings, AppErrorBoundary, Breadcrumbs, Can, CrashErrorFallback, CreateApiKeyModal, CreateRoleModal, CreateScheduleModal, DEAL_STAGES, DEFAULT_KANBAN_CONFIG, DealKanbanCard, DeleteScheduleModal, DeploymentDetailModal, DeploymentList, DeploymentSettings, DeploymentStatusBadge, EditApiKeyModal, ErrorReportCard, KanbanBoard, MdxRenderer, NoAccessState, NotificationBell, NotificationPanel, PermissionMatrix, RecurringScheduleForm, RelativeScheduleForm, RichTextEditor, ScheduleCard, ScheduleDetailModal, ScheduleTypeSelector, TaskScheduler, buildErrorReport, mdxComponents };