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