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