@oneuptime/common 11.3.28 → 11.4.1

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 (244) hide show
  1. package/Models/DatabaseModels/AIRun.ts +50 -0
  2. package/Models/DatabaseModels/Project.ts +60 -0
  3. package/Server/API/AIAgentTaskAPI.ts +10 -24
  4. package/Server/API/AIInvestigationAPI.ts +148 -0
  5. package/Server/API/TelemetryAPI.ts +4 -3
  6. package/Server/Infrastructure/Postgres/SchemaMigrations/1783598145111-AddEnableAutomaticIncidentInvestigationToProject.ts +19 -0
  7. package/Server/Infrastructure/Postgres/SchemaMigrations/1783615884106-AddInvestigationSubjectToAIRun.ts +33 -0
  8. package/Server/Infrastructure/Postgres/SchemaMigrations/1783632447975-AddEnableAutomaticAlertInvestigationToProject.ts +19 -0
  9. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
  10. package/Server/Services/AIAgentTaskService.ts +62 -0
  11. package/Server/Services/AlertService.ts +25 -0
  12. package/Server/Services/AnalyticsDatabaseService.ts +27 -0
  13. package/Server/Services/IncidentService.ts +25 -0
  14. package/Server/Services/IncidentStateTimelineService.ts +19 -0
  15. package/Server/Services/LogAggregationService.ts +29 -11
  16. package/Server/Services/MetricService.ts +603 -13
  17. package/Server/Utils/AI/Chat/ObservabilityAssistant.ts +119 -11
  18. package/Server/Utils/AI/Sentinel/AlertInvestigationRunner.ts +141 -0
  19. package/Server/Utils/AI/Sentinel/IncidentInvestigationRunner.ts +178 -0
  20. package/Server/Utils/AI/Sentinel/IncidentPostmortemRunner.ts +105 -0
  21. package/Server/Utils/AI/Sentinel/README.md +10 -0
  22. package/Server/Utils/AI/Sentinel/SentinelInvestigationEngine.ts +405 -0
  23. package/Server/Utils/AI/Sentinel/SentinelMemory.ts +167 -0
  24. package/Server/Utils/AI/Toolbox/Index.ts +11 -0
  25. package/Server/Utils/AI/Toolbox/SentinelActionTools.ts +577 -0
  26. package/Server/Utils/Monitor/Criteria/APIRequestCriteria.ts +44 -0
  27. package/Server/Utils/Monitor/MonitorMetricUtil.ts +108 -2
  28. package/Tests/Server/API/BaseAPI.test.ts +1 -0
  29. package/Tests/Server/Services/AIAgentTaskServiceClaim.test.ts +146 -0
  30. package/Tests/Server/Services/LogAggregationService.test.ts +22 -0
  31. package/Tests/Server/Services/MetricServiceAggregate.test.ts +194 -0
  32. package/Tests/Types/AI/AIAgentTaskStatus.test.ts +86 -0
  33. package/Tests/Types/BaseDatabase/AggregationIntervalUtil.test.ts +127 -0
  34. package/Tests/Types/BaseDatabase/DatabaseCommonInteractionPropsUtil.test.ts +152 -0
  35. package/Tests/Types/OnCallDutyPolicy/ScheduleShiftUtil.test.ts +260 -0
  36. package/Tests/Types/Trace/TraceRecordingRuleDefinition.test.ts +308 -0
  37. package/Tests/UI/Components/Dropdown.test.tsx +17 -0
  38. package/Tests/UI/Components/EntityDropdown.test.tsx +130 -0
  39. package/Tests/UI/Components/Modal.test.tsx +144 -2
  40. package/Tests/UI/Components/TelemetrySearchBar.test.tsx +90 -0
  41. package/Tests/UI/Utils/Theme.test.ts +161 -0
  42. package/Types/BaseDatabase/AggregateBy.ts +12 -0
  43. package/Types/Dashboard/DashboardComponentType.ts +1 -0
  44. package/Types/Dashboard/DashboardComponents/ComponentArgument.ts +1 -0
  45. package/Types/Dashboard/DashboardComponents/DashboardLogChartComponent.ts +19 -0
  46. package/Types/Dashboard/DashboardTemplates.ts +9 -1
  47. package/Types/Date.ts +46 -0
  48. package/Types/Monitor/CriteriaFilter.ts +4 -0
  49. package/Types/Monitor/HttpPhaseTimings.ts +16 -0
  50. package/Types/Monitor/MonitorMetricType.ts +17 -0
  51. package/Types/Monitor/PingMonitor/PingMonitorResponse.ts +20 -0
  52. package/Types/OnCallDutyPolicy/ScheduleShiftUtil.ts +257 -0
  53. package/Types/Probe/ProbeMonitorResponse.ts +14 -0
  54. package/UI/Components/Calendar/Calendar.tsx +8 -2
  55. package/UI/Components/Charts/ChartGroup/ChartGroup.tsx +5 -5
  56. package/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.tsx +8 -5
  57. package/UI/Components/Charts/ChartLibrary/BarChart/BarChart.tsx +4 -1
  58. package/UI/Components/Charts/ChartLibrary/LineChart/LineChart.tsx +6 -3
  59. package/UI/Components/CodeBlock/CodeBlock.tsx +2 -1
  60. package/UI/Components/CodeEditor/CodeEditor.tsx +3 -0
  61. package/UI/Components/Divider/Divider.tsx +1 -1
  62. package/UI/Components/Dropdown/Dropdown.tsx +13 -4
  63. package/UI/Components/EntityDropdown/EntityDropdown.tsx +110 -2
  64. package/UI/Components/ErrorBoundary.tsx +2 -1
  65. package/UI/Components/EventItem/EventItem.tsx +2 -2
  66. package/UI/Components/GanttChart/Bar/Index.tsx +1 -1
  67. package/UI/Components/Graphs/UptimeBarTooltip.tsx +30 -23
  68. package/UI/Components/Header/Header.tsx +7 -0
  69. package/UI/Components/Header/HeaderIconDropdownButton.tsx +2 -0
  70. package/UI/Components/Label/Label.tsx +2 -2
  71. package/UI/Components/LogsViewer/components/LogsAnalyticsView.tsx +22 -10
  72. package/UI/Components/LogsViewer/components/LogsHistogram.tsx +9 -3
  73. package/UI/Components/Markdown.tsx/MarkdownEditor.tsx +8 -8
  74. package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +44 -20
  75. package/UI/Components/Modal/ConfirmModal.tsx +1 -1
  76. package/UI/Components/Modal/Modal.tsx +213 -121
  77. package/UI/Components/Modal/ModalFooter.tsx +33 -22
  78. package/UI/Components/ModelTable/TableView.tsx +1 -1
  79. package/UI/Components/MonitorGraphs/UptimeBarDayModal.tsx +20 -12
  80. package/UI/Components/Pill/Pill.tsx +21 -13
  81. package/UI/Components/StatusBubble/StatusBubble.tsx +3 -3
  82. package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +7 -1
  83. package/UI/Components/TelemetryViewer/components/TelemetryHistogram.tsx +9 -3
  84. package/UI/Components/TelemetryViewer/components/TelemetrySearchBar.tsx +62 -4
  85. package/UI/Components/Workflow/Component.tsx +53 -33
  86. package/UI/Components/Workflow/ComponentPortViewer.tsx +6 -6
  87. package/UI/Components/Workflow/ComponentReturnValueViewer.tsx +8 -8
  88. package/UI/Components/Workflow/ComponentsModal.tsx +16 -9
  89. package/UI/Components/Workflow/Workflow.tsx +13 -11
  90. package/UI/Utils/Theme.ts +140 -0
  91. package/Utils/Dashboard/Components/DashboardLogChartComponent.ts +104 -0
  92. package/Utils/Dashboard/Components/Index.ts +7 -0
  93. package/Utils/Monitor/MonitorMetricType.ts +70 -2
  94. package/build/dist/Models/DatabaseModels/AIRun.js +52 -0
  95. package/build/dist/Models/DatabaseModels/AIRun.js.map +1 -1
  96. package/build/dist/Models/DatabaseModels/Project.js +62 -0
  97. package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
  98. package/build/dist/Server/API/AIAgentTaskAPI.js +8 -23
  99. package/build/dist/Server/API/AIAgentTaskAPI.js.map +1 -1
  100. package/build/dist/Server/API/AIInvestigationAPI.js +107 -0
  101. package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -0
  102. package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
  103. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783598145111-AddEnableAutomaticIncidentInvestigationToProject.js +12 -0
  104. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783598145111-AddEnableAutomaticIncidentInvestigationToProject.js.map +1 -0
  105. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783615884106-AddInvestigationSubjectToAIRun.js +18 -0
  106. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783615884106-AddInvestigationSubjectToAIRun.js.map +1 -0
  107. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783632447975-AddEnableAutomaticAlertInvestigationToProject.js +12 -0
  108. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783632447975-AddEnableAutomaticAlertInvestigationToProject.js.map +1 -0
  109. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
  110. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  111. package/build/dist/Server/Services/AIAgentTaskService.js +59 -0
  112. package/build/dist/Server/Services/AIAgentTaskService.js.map +1 -1
  113. package/build/dist/Server/Services/AlertService.js +24 -0
  114. package/build/dist/Server/Services/AlertService.js.map +1 -1
  115. package/build/dist/Server/Services/AnalyticsDatabaseService.js +19 -0
  116. package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
  117. package/build/dist/Server/Services/IncidentService.js +24 -0
  118. package/build/dist/Server/Services/IncidentService.js.map +1 -1
  119. package/build/dist/Server/Services/IncidentStateTimelineService.js +19 -0
  120. package/build/dist/Server/Services/IncidentStateTimelineService.js.map +1 -1
  121. package/build/dist/Server/Services/LogAggregationService.js +21 -7
  122. package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
  123. package/build/dist/Server/Services/MetricService.js +449 -7
  124. package/build/dist/Server/Services/MetricService.js.map +1 -1
  125. package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js +59 -11
  126. package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js.map +1 -1
  127. package/build/dist/Server/Utils/AI/Sentinel/AlertInvestigationRunner.js +119 -0
  128. package/build/dist/Server/Utils/AI/Sentinel/AlertInvestigationRunner.js.map +1 -0
  129. package/build/dist/Server/Utils/AI/Sentinel/IncidentInvestigationRunner.js +147 -0
  130. package/build/dist/Server/Utils/AI/Sentinel/IncidentInvestigationRunner.js.map +1 -0
  131. package/build/dist/Server/Utils/AI/Sentinel/IncidentPostmortemRunner.js +93 -0
  132. package/build/dist/Server/Utils/AI/Sentinel/IncidentPostmortemRunner.js.map +1 -0
  133. package/build/dist/Server/Utils/AI/Sentinel/SentinelInvestigationEngine.js +311 -0
  134. package/build/dist/Server/Utils/AI/Sentinel/SentinelInvestigationEngine.js.map +1 -0
  135. package/build/dist/Server/Utils/AI/Sentinel/SentinelMemory.js +143 -0
  136. package/build/dist/Server/Utils/AI/Sentinel/SentinelMemory.js.map +1 -0
  137. package/build/dist/Server/Utils/AI/Toolbox/Index.js +6 -0
  138. package/build/dist/Server/Utils/AI/Toolbox/Index.js.map +1 -1
  139. package/build/dist/Server/Utils/AI/Toolbox/SentinelActionTools.js +451 -0
  140. package/build/dist/Server/Utils/AI/Toolbox/SentinelActionTools.js.map +1 -0
  141. package/build/dist/Server/Utils/Monitor/Criteria/APIRequestCriteria.js +29 -0
  142. package/build/dist/Server/Utils/Monitor/Criteria/APIRequestCriteria.js.map +1 -1
  143. package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js +86 -2
  144. package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js.map +1 -1
  145. package/build/dist/Types/Dashboard/DashboardComponentType.js +1 -0
  146. package/build/dist/Types/Dashboard/DashboardComponentType.js.map +1 -1
  147. package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js +1 -0
  148. package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js.map +1 -1
  149. package/build/dist/Types/Dashboard/DashboardComponents/DashboardLogChartComponent.js +2 -0
  150. package/build/dist/Types/Dashboard/DashboardComponents/DashboardLogChartComponent.js.map +1 -0
  151. package/build/dist/Types/Dashboard/DashboardTemplates.js +9 -1
  152. package/build/dist/Types/Dashboard/DashboardTemplates.js.map +1 -1
  153. package/build/dist/Types/Date.js +32 -0
  154. package/build/dist/Types/Date.js.map +1 -1
  155. package/build/dist/Types/Monitor/CriteriaFilter.js +4 -0
  156. package/build/dist/Types/Monitor/CriteriaFilter.js.map +1 -1
  157. package/build/dist/Types/Monitor/HttpPhaseTimings.js +2 -0
  158. package/build/dist/Types/Monitor/HttpPhaseTimings.js.map +1 -0
  159. package/build/dist/Types/Monitor/MonitorMetricType.js +15 -0
  160. package/build/dist/Types/Monitor/MonitorMetricType.js.map +1 -1
  161. package/build/dist/Types/Monitor/PingMonitor/PingMonitorResponse.js +2 -0
  162. package/build/dist/Types/Monitor/PingMonitor/PingMonitorResponse.js.map +1 -0
  163. package/build/dist/Types/OnCallDutyPolicy/ScheduleShiftUtil.js +159 -0
  164. package/build/dist/Types/OnCallDutyPolicy/ScheduleShiftUtil.js.map +1 -0
  165. package/build/dist/UI/Components/Calendar/Calendar.js +2 -2
  166. package/build/dist/UI/Components/Calendar/Calendar.js.map +1 -1
  167. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js +2 -2
  168. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js.map +1 -1
  169. package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js +7 -4
  170. package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js.map +1 -1
  171. package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js +4 -1
  172. package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js.map +1 -1
  173. package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js +6 -3
  174. package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js.map +1 -1
  175. package/build/dist/UI/Components/CodeBlock/CodeBlock.js +1 -1
  176. package/build/dist/UI/Components/CodeBlock/CodeBlock.js.map +1 -1
  177. package/build/dist/UI/Components/CodeEditor/CodeEditor.js +3 -1
  178. package/build/dist/UI/Components/CodeEditor/CodeEditor.js.map +1 -1
  179. package/build/dist/UI/Components/Divider/Divider.js +1 -1
  180. package/build/dist/UI/Components/Divider/Divider.js.map +1 -1
  181. package/build/dist/UI/Components/Dropdown/Dropdown.js +10 -4
  182. package/build/dist/UI/Components/Dropdown/Dropdown.js.map +1 -1
  183. package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js +76 -3
  184. package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js.map +1 -1
  185. package/build/dist/UI/Components/ErrorBoundary.js +2 -1
  186. package/build/dist/UI/Components/ErrorBoundary.js.map +1 -1
  187. package/build/dist/UI/Components/EventItem/EventItem.js +2 -2
  188. package/build/dist/UI/Components/EventItem/EventItem.js.map +1 -1
  189. package/build/dist/UI/Components/GanttChart/Bar/Index.js +1 -1
  190. package/build/dist/UI/Components/GanttChart/Bar/Index.js.map +1 -1
  191. package/build/dist/UI/Components/Graphs/UptimeBarTooltip.js +29 -23
  192. package/build/dist/UI/Components/Graphs/UptimeBarTooltip.js.map +1 -1
  193. package/build/dist/UI/Components/Header/Header.js +1 -0
  194. package/build/dist/UI/Components/Header/Header.js.map +1 -1
  195. package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js +1 -1
  196. package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js.map +1 -1
  197. package/build/dist/UI/Components/Label/Label.js +2 -2
  198. package/build/dist/UI/Components/Label/Label.js.map +1 -1
  199. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js +20 -8
  200. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js.map +1 -1
  201. package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js +8 -2
  202. package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js.map +1 -1
  203. package/build/dist/UI/Components/Markdown.tsx/MarkdownEditor.js +8 -8
  204. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +40 -20
  205. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js.map +1 -1
  206. package/build/dist/UI/Components/Modal/ConfirmModal.js +1 -1
  207. package/build/dist/UI/Components/Modal/ConfirmModal.js.map +1 -1
  208. package/build/dist/UI/Components/Modal/Modal.js +109 -51
  209. package/build/dist/UI/Components/Modal/Modal.js.map +1 -1
  210. package/build/dist/UI/Components/Modal/ModalFooter.js +13 -10
  211. package/build/dist/UI/Components/Modal/ModalFooter.js.map +1 -1
  212. package/build/dist/UI/Components/ModelTable/TableView.js +1 -1
  213. package/build/dist/UI/Components/ModelTable/TableView.js.map +1 -1
  214. package/build/dist/UI/Components/MonitorGraphs/UptimeBarDayModal.js +17 -12
  215. package/build/dist/UI/Components/MonitorGraphs/UptimeBarDayModal.js.map +1 -1
  216. package/build/dist/UI/Components/Pill/Pill.js +12 -13
  217. package/build/dist/UI/Components/Pill/Pill.js.map +1 -1
  218. package/build/dist/UI/Components/StatusBubble/StatusBubble.js +3 -3
  219. package/build/dist/UI/Components/StatusBubble/StatusBubble.js.map +1 -1
  220. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +1 -1
  221. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -1
  222. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js +8 -2
  223. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js.map +1 -1
  224. package/build/dist/UI/Components/TelemetryViewer/components/TelemetrySearchBar.js +36 -6
  225. package/build/dist/UI/Components/TelemetryViewer/components/TelemetrySearchBar.js.map +1 -1
  226. package/build/dist/UI/Components/Workflow/Component.js +43 -33
  227. package/build/dist/UI/Components/Workflow/Component.js.map +1 -1
  228. package/build/dist/UI/Components/Workflow/ComponentPortViewer.js +6 -6
  229. package/build/dist/UI/Components/Workflow/ComponentPortViewer.js.map +1 -1
  230. package/build/dist/UI/Components/Workflow/ComponentReturnValueViewer.js +8 -8
  231. package/build/dist/UI/Components/Workflow/ComponentReturnValueViewer.js.map +1 -1
  232. package/build/dist/UI/Components/Workflow/ComponentsModal.js +15 -9
  233. package/build/dist/UI/Components/Workflow/ComponentsModal.js.map +1 -1
  234. package/build/dist/UI/Components/Workflow/Workflow.js +13 -11
  235. package/build/dist/UI/Components/Workflow/Workflow.js.map +1 -1
  236. package/build/dist/UI/Utils/Theme.js +106 -0
  237. package/build/dist/UI/Utils/Theme.js.map +1 -0
  238. package/build/dist/Utils/Dashboard/Components/DashboardLogChartComponent.js +92 -0
  239. package/build/dist/Utils/Dashboard/Components/DashboardLogChartComponent.js.map +1 -0
  240. package/build/dist/Utils/Dashboard/Components/Index.js +4 -0
  241. package/build/dist/Utils/Dashboard/Components/Index.js.map +1 -1
  242. package/build/dist/Utils/Monitor/MonitorMetricType.js +70 -3
  243. package/build/dist/Utils/Monitor/MonitorMetricType.js.map +1 -1
  244. package/package.json +1 -1
@@ -70,6 +70,7 @@ SyntaxHighlighter.registerLanguage("http", http);
70
70
  import mermaid from "mermaid";
71
71
  import DOMPurify from "dompurify";
72
72
  import OneUptimeDate from "../../../Types/Date";
73
+ import { Theme, useTheme } from "../../Utils/Theme";
73
74
 
74
75
  /*
75
76
  * ISO 8601 timestamps produced by OneUptimeDate.toString() — the
@@ -80,22 +81,37 @@ import OneUptimeDate from "../../../Types/Date";
80
81
  const ISO_8601_REGEX: RegExp =
81
82
  /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,6})?Z$/;
82
83
 
83
- // Initialize mermaid
84
- mermaid.initialize({
85
- startOnLoad: false,
86
- theme: "default",
87
- securityLevel: "strict",
88
- fontFamily: "inherit",
89
- themeVariables: {
90
- background: "#ffffff",
91
- primaryColor: "#e0f2fe",
92
- primaryTextColor: "#1e293b",
93
- primaryBorderColor: "#0ea5e9",
94
- lineColor: "#64748b",
95
- secondaryColor: "#f1f5f9",
96
- tertiaryColor: "#ffffff",
97
- },
98
- });
84
+ const initializeMermaid: (theme: Theme) => void = (theme: Theme): void => {
85
+ const isDark: boolean = theme === Theme.Dark;
86
+
87
+ mermaid.initialize({
88
+ startOnLoad: false,
89
+ theme: isDark ? "dark" : "default",
90
+ securityLevel: "strict",
91
+ fontFamily: "inherit",
92
+ themeVariables: isDark
93
+ ? {
94
+ background: "#111827",
95
+ primaryColor: "#1e3a5f",
96
+ primaryTextColor: "#f8fafc",
97
+ primaryBorderColor: "#38bdf8",
98
+ lineColor: "#94a3b8",
99
+ secondaryColor: "#1e293b",
100
+ tertiaryColor: "#0f172a",
101
+ }
102
+ : {
103
+ background: "#ffffff",
104
+ primaryColor: "#e0f2fe",
105
+ primaryTextColor: "#1e293b",
106
+ primaryBorderColor: "#0ea5e9",
107
+ lineColor: "#64748b",
108
+ secondaryColor: "#f1f5f9",
109
+ tertiaryColor: "#ffffff",
110
+ },
111
+ });
112
+ };
113
+
114
+ initializeMermaid(Theme.Light);
99
115
 
100
116
  // Mermaid diagram component
101
117
  const MermaidDiagram: FunctionComponent<{ chart: string }> = ({
@@ -105,22 +121,26 @@ const MermaidDiagram: FunctionComponent<{ chart: string }> = ({
105
121
  }) => {
106
122
  const containerRef: React.RefObject<HTMLDivElement> =
107
123
  useRef<HTMLDivElement>(null);
124
+ const theme: Theme = useTheme();
108
125
 
109
126
  useEffect(() => {
127
+ let isCancelled: boolean = false;
128
+
110
129
  const renderDiagram: () => Promise<void> = async (): Promise<void> => {
111
130
  if (containerRef.current) {
112
131
  containerRef.current.innerHTML = "";
113
132
  try {
133
+ initializeMermaid(theme);
114
134
  const id: string = `mermaid-${Math.random().toString(36).substr(2, 9)}`;
115
135
  const { svg } = await mermaid.render(id, chart);
116
- if (containerRef.current) {
136
+ if (containerRef.current && !isCancelled) {
117
137
  containerRef.current.innerHTML = DOMPurify.sanitize(svg, {
118
138
  USE_PROFILES: { svg: true, svgFilters: true },
119
139
  ADD_TAGS: ["foreignObject"],
120
140
  });
121
141
  }
122
142
  } catch (error) {
123
- if (containerRef.current) {
143
+ if (containerRef.current && !isCancelled) {
124
144
  const errorMessage: string = String(error);
125
145
  const errorEl: HTMLPreElement = document.createElement("pre");
126
146
  errorEl.className = "text-red-500";
@@ -131,8 +151,12 @@ const MermaidDiagram: FunctionComponent<{ chart: string }> = ({
131
151
  }
132
152
  }
133
153
  };
134
- renderDiagram();
135
- }, [chart]);
154
+ void renderDiagram();
155
+
156
+ return (): void => {
157
+ isCancelled = true;
158
+ };
159
+ }, [chart, theme]);
136
160
 
137
161
  return (
138
162
  <div
@@ -48,7 +48,7 @@ const ConfirmModal: FunctionComponent<ComponentProps> = (
48
48
  >
49
49
  <div
50
50
  data-testid="confirm-modal-description"
51
- className="text-gray-500 mt-5 text-sm whitespace-pre-wrap break-words max-h-96 overflow-y-auto pr-1"
51
+ className="max-h-96 overflow-y-auto whitespace-pre-wrap break-words pr-1 text-sm leading-6 text-gray-600"
52
52
  >
53
53
  {translatedDescription}
54
54
  </div>
@@ -1,4 +1,4 @@
1
- import Button, { ButtonStyleType } from "../Button/Button";
1
+ import { ButtonStyleType } from "../Button/Button";
2
2
  import ButtonType from "../Button/ButtonTypes";
3
3
  import Icon, { IconType, SizeProp, ThickProp } from "../Icon/Icon";
4
4
  import Loader, { LoaderType } from "../Loader/Loader";
@@ -11,6 +11,7 @@ import React, {
11
11
  FunctionComponent,
12
12
  ReactElement,
13
13
  useEffect,
14
+ useId,
14
15
  useRef,
15
16
  } from "react";
16
17
 
@@ -56,162 +57,253 @@ const Modal: FunctionComponent<ComponentProps> = (
56
57
  const translatedCloseButtonText: string | undefined = translateString(
57
58
  props.closeButtonText,
58
59
  );
60
+ const translatedCloseLabel: string = translateString("Close") || "Close";
59
61
  const modalRef: React.RefObject<HTMLDivElement> =
60
62
  useRef<HTMLDivElement>(null);
63
+ const previouslyFocusedElementRef: React.MutableRefObject<HTMLElement | null> =
64
+ useRef<HTMLElement | null>(
65
+ typeof document !== "undefined" &&
66
+ document.activeElement instanceof HTMLElement
67
+ ? document.activeElement
68
+ : null,
69
+ );
70
+ const onCloseRef: React.MutableRefObject<(() => void) | undefined> = useRef<
71
+ (() => void) | undefined
72
+ >(props.onClose);
73
+ const titleId: string = useId();
74
+ const descriptionId: string = useId();
61
75
 
62
- // Handle Escape key to close modal
63
76
  useEffect(() => {
64
- const handleEscapeKey: (event: KeyboardEvent) => void = (
77
+ onCloseRef.current = props.onClose;
78
+ }, [props.onClose]);
79
+
80
+ useEffect(() => {
81
+ const modal: HTMLDivElement | null = modalRef.current;
82
+ if (!modal) {
83
+ return;
84
+ }
85
+
86
+ const getFocusableElements: () => Array<HTMLElement> =
87
+ (): Array<HTMLElement> => {
88
+ return Array.from(
89
+ modal.querySelectorAll<HTMLElement>(
90
+ 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])',
91
+ ),
92
+ ).filter((element: HTMLElement) => {
93
+ return (
94
+ !element.hasAttribute("disabled") &&
95
+ element.getAttribute("aria-hidden") !== "true"
96
+ );
97
+ });
98
+ };
99
+
100
+ const isTopmostDialog: () => boolean = (): boolean => {
101
+ const openDialogs: Array<Element> = Array.from(
102
+ document.querySelectorAll('[role="dialog"][aria-modal="true"]'),
103
+ );
104
+
105
+ return openDialogs[openDialogs.length - 1] === modal;
106
+ };
107
+
108
+ const initialFocusElement: HTMLElement | undefined =
109
+ getFocusableElements().find((element: HTMLElement) => {
110
+ return element.getAttribute("data-testid") !== "close-button";
111
+ });
112
+
113
+ if (isTopmostDialog()) {
114
+ (initialFocusElement || modal).focus();
115
+ }
116
+
117
+ const handleKeyDown: (event: KeyboardEvent) => void = (
65
118
  event: KeyboardEvent,
66
119
  ): void => {
67
- if (event.key === "Escape" && props.onClose) {
68
- props.onClose();
120
+ if (event.defaultPrevented || !isTopmostDialog()) {
121
+ return;
122
+ }
123
+
124
+ if (event.key === "Escape" && onCloseRef.current) {
125
+ event.preventDefault();
126
+ onCloseRef.current();
127
+ return;
128
+ }
129
+
130
+ if (event.key !== "Tab") {
131
+ return;
132
+ }
133
+
134
+ const focusableElements: Array<HTMLElement> = getFocusableElements();
135
+
136
+ if (focusableElements.length === 0) {
137
+ event.preventDefault();
138
+ modal.focus();
139
+ return;
140
+ }
141
+
142
+ const firstFocusableElement: HTMLElement = focusableElements[0]!;
143
+ const lastFocusableElement: HTMLElement =
144
+ focusableElements[focusableElements.length - 1]!;
145
+ const activeElement: Element | null = document.activeElement;
146
+
147
+ if (
148
+ event.shiftKey &&
149
+ (activeElement === firstFocusableElement ||
150
+ !modal.contains(activeElement))
151
+ ) {
152
+ event.preventDefault();
153
+ lastFocusableElement.focus();
154
+ } else if (
155
+ !event.shiftKey &&
156
+ (activeElement === lastFocusableElement ||
157
+ !modal.contains(activeElement))
158
+ ) {
159
+ event.preventDefault();
160
+ firstFocusableElement.focus();
69
161
  }
70
162
  };
71
163
 
72
- document.addEventListener("keydown", handleEscapeKey);
164
+ document.addEventListener("keydown", handleKeyDown);
165
+
73
166
  return () => {
74
- document.removeEventListener("keydown", handleEscapeKey);
75
- };
76
- }, [props.onClose]);
167
+ document.removeEventListener("keydown", handleKeyDown);
77
168
 
78
- // Focus trap and initial focus
79
- useEffect(() => {
80
- const modal: HTMLDivElement | null = modalRef.current;
81
- if (modal) {
82
- // Focus the first focusable element in the modal, excluding the close button
83
- const focusableElements: NodeListOf<Element> = modal.querySelectorAll(
84
- 'button:not([data-testid="close-button"]), [href], input, select, textarea, [tabindex]:not([tabindex="-1"])',
85
- );
86
- const firstFocusable: HTMLElement | undefined = focusableElements[0] as
87
- | HTMLElement
88
- | undefined;
89
- if (firstFocusable) {
90
- firstFocusable.focus();
169
+ if (previouslyFocusedElementRef.current?.isConnected) {
170
+ previouslyFocusedElementRef.current.focus();
91
171
  }
92
- }
172
+ };
93
173
  }, []);
94
174
 
95
- let iconBgColor: string = "bg-indigo-100";
175
+ let iconBgColor: string = "bg-indigo-50";
96
176
  let iconColor: string = "text-indigo-600";
177
+ let iconRingColor: string = "ring-indigo-100";
97
178
 
98
179
  if (props.iconType === IconType.Info) {
99
- iconBgColor = "bg-indigo-100";
180
+ iconBgColor = "bg-indigo-50";
100
181
  iconColor = "text-indigo-600";
182
+ iconRingColor = "ring-indigo-100";
101
183
  } else if (props.iconType === IconType.Warning) {
102
- iconBgColor = "bg-yellow-100";
103
- iconColor = "text-yellow-600";
184
+ iconBgColor = "bg-yellow-50";
185
+ iconColor = "text-yellow-700";
186
+ iconRingColor = "ring-yellow-100";
104
187
  } else if (props.iconType === IconType.Success) {
105
- iconBgColor = "bg-green-100";
188
+ iconBgColor = "bg-green-50";
106
189
  iconColor = "text-green-600";
190
+ iconRingColor = "ring-green-100";
107
191
  } else if (props.iconType === IconType.Danger) {
108
- iconBgColor = "bg-red-100";
192
+ iconBgColor = "bg-red-50";
109
193
  iconColor = "text-red-600";
194
+ iconRingColor = "ring-red-100";
195
+ }
196
+
197
+ let modalWidthClassName: string = "sm:max-w-lg md:max-w-lg";
198
+
199
+ if (props.modalWidth === ModalWidth.Medium) {
200
+ modalWidthClassName = "sm:max-w-3xl md:max-w-3xl";
201
+ } else if (props.modalWidth === ModalWidth.Large) {
202
+ modalWidthClassName = "sm:max-w-7xl md:max-w-7xl";
110
203
  }
111
204
 
112
205
  return (
113
- <div
114
- ref={modalRef}
115
- className="relative z-20"
116
- aria-labelledby="modal-title"
117
- aria-describedby={props.description ? "modal-description" : undefined}
118
- role="dialog"
119
- aria-modal="true"
120
- >
121
- <div className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity"></div>
206
+ <div className="relative z-50">
207
+ <div
208
+ className="fixed inset-0 bg-gray-950/45 backdrop-blur-[2px]"
209
+ data-testid="modal-backdrop"
210
+ aria-hidden="true"
211
+ />
122
212
 
123
- <div className="fixed inset-0 z-20 overflow-y-auto">
124
- <div className="flex min-h-screen items-end justify-center p-0 text-center md:items-center md:p-4">
213
+ <div className="fixed inset-0 z-50 overflow-y-auto">
214
+ <div className="flex min-h-full items-end justify-center p-0 text-center sm:items-center sm:p-6">
125
215
  <div
126
- className={`relative transform bg-white text-left shadow-xl transition-all w-full h-full md:rounded-lg md:my-8 ${
127
- props.modalWidth && props.modalWidth === ModalWidth.Large
128
- ? "md:max-w-7xl"
129
- : ""
130
- } ${
131
- props.modalWidth && props.modalWidth === ModalWidth.Medium
132
- ? "md:max-w-3xl"
133
- : ""
134
- } ${!props.modalWidth ? "md:max-w-lg" : ""} `}
216
+ ref={modalRef}
217
+ className={`relative flex max-h-[calc(100vh-1rem)] w-full flex-col rounded-t-2xl border border-gray-200/80 bg-white text-left shadow-2xl ring-1 ring-black/5 sm:my-8 sm:max-h-[calc(100vh-3rem)] sm:rounded-xl ${modalWidthClassName}`}
135
218
  data-testid="modal"
219
+ aria-labelledby={titleId}
220
+ aria-describedby={translatedDescription ? descriptionId : undefined}
221
+ role="dialog"
222
+ aria-modal="true"
223
+ tabIndex={-1}
136
224
  >
137
- {props.onClose && (
138
- <div className="absolute top-0 right-0 z-10 pt-4 pr-4 md:hidden lg:block">
139
- <Button
140
- buttonStyle={ButtonStyleType.ICON}
141
- icon={IconProp.Close}
142
- iconSize={SizeProp.Large}
143
- title="Close"
144
- dataTestId="close-button"
145
- onClick={props.onClose}
146
- />
225
+ <div className="relative flex shrink-0 flex-col gap-4 rounded-t-2xl border-b border-gray-100 bg-white px-5 py-4 sm:flex-row sm:items-start sm:justify-between sm:gap-6 sm:rounded-t-xl sm:px-6">
226
+ <div
227
+ className={`flex min-w-0 flex-1 items-start gap-3 ${
228
+ props.onClose ? "pr-9 sm:pr-0" : ""
229
+ }`}
230
+ >
231
+ {props.icon && (
232
+ <div
233
+ className={`flex h-9 w-9 flex-shrink-0 items-center justify-center rounded-lg ${iconBgColor} ring-1 ring-inset ${iconRingColor}`}
234
+ data-testid="icon"
235
+ >
236
+ <Icon
237
+ thick={ThickProp.Thick}
238
+ type={
239
+ props.iconType === undefined
240
+ ? IconType.Info
241
+ : props.iconType
242
+ }
243
+ className={`${iconColor} h-5 w-5`}
244
+ icon={props.icon}
245
+ size={SizeProp.Five}
246
+ />
247
+ </div>
248
+ )}
249
+ <div className="min-w-0 flex-1">
250
+ <h3
251
+ data-testid="modal-title"
252
+ className="text-base font-semibold leading-6 tracking-tight text-gray-900"
253
+ id={titleId}
254
+ >
255
+ {translatedTitle}
256
+ </h3>
257
+ {translatedDescription && (
258
+ <p
259
+ id={descriptionId}
260
+ data-testid="modal-description"
261
+ className="mt-0.5 text-sm leading-5 text-gray-500"
262
+ >
263
+ {translatedDescription}
264
+ </p>
265
+ )}
266
+ </div>
147
267
  </div>
148
- )}
149
- <div className="p-4 md:p-6">
150
- {props.icon && (
268
+ {props.rightElement && (
151
269
  <div
152
- className={`mx-auto flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full ${iconBgColor} md:mx-0 md:h-10 md:w-10`}
153
- data-testid="icon"
270
+ data-testid="right-element"
271
+ className={`flex-shrink-0 ${props.onClose ? "pr-9" : ""}`}
154
272
  >
155
- <Icon
156
- thick={ThickProp.Thick}
157
- type={
158
- props.iconType === undefined
159
- ? IconType.Info
160
- : props.iconType
161
- }
162
- className={`${iconColor} h-6 w-6 stroke-2`}
163
- icon={props.icon}
164
- size={SizeProp.Large}
165
- />
273
+ {props.rightElement}
166
274
  </div>
167
275
  )}
168
- <div className="text-left md:mt-0 md:ml-4 md:mr-4">
169
- <div className="flex flex-col md:flex-row md:justify-between">
170
- <div className="flex-1">
171
- <h3
172
- data-testid="modal-title"
173
- className={`text-lg font-medium leading-6 text-gray-900 ${
174
- props.icon ? "mt-4 md:ml-10 md:-mt-8 md:mb-5" : ""
175
- }`}
176
- id="modal-title"
177
- >
178
- {translatedTitle}
179
- </h3>
180
- {translatedDescription && (
181
- <p
182
- id="modal-description"
183
- data-testid="modal-description"
184
- className="text-sm leading-6 text-gray-500 mt-2"
185
- >
186
- {translatedDescription}
187
- </p>
188
- )}
276
+ {props.onClose && (
277
+ <button
278
+ type="button"
279
+ title={translatedCloseLabel}
280
+ aria-label={translatedCloseLabel}
281
+ data-testid="close-button"
282
+ onClick={props.onClose}
283
+ className="absolute right-4 top-4 inline-flex h-8 w-8 items-center justify-center rounded-md text-gray-400 transition-colors hover:bg-gray-100 hover:text-gray-700 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2"
284
+ >
285
+ <Icon icon={IconProp.Close} className="h-4 w-4" />
286
+ </button>
287
+ )}
288
+ </div>
289
+
290
+ <div
291
+ className="min-h-0 flex-1 overflow-y-auto overscroll-contain px-5 py-5 sm:px-6"
292
+ data-testid="modal-content"
293
+ >
294
+ <ModalBody error={props.error}>
295
+ {!props.isBodyLoading ? (
296
+ props.children
297
+ ) : (
298
+ <div className="modal-body flex justify-center py-16">
299
+ <Loader
300
+ loaderType={LoaderType.Bar}
301
+ color={VeryLightGray}
302
+ size={200}
303
+ />
189
304
  </div>
190
- {props.rightElement && (
191
- <div
192
- data-testid="right-element"
193
- className="mt-4 md:mt-0 lg:mr-4"
194
- >
195
- {props.rightElement}
196
- </div>
197
- )}
198
- </div>
199
- <div className="mt-2">
200
- <ModalBody error={props.error}>
201
- {!props.isBodyLoading ? (
202
- props.children
203
- ) : (
204
- <div className="modal-body mt-20 mb-20 flex justify-center">
205
- <Loader
206
- loaderType={LoaderType.Bar}
207
- color={VeryLightGray}
208
- size={200}
209
- />
210
- </div>
211
- )}
212
- </ModalBody>
213
- </div>
214
- </div>
305
+ )}
306
+ </ModalBody>
215
307
  </div>
216
308
  <ModalFooter
217
309
  submitButtonType={
@@ -18,14 +18,42 @@ export interface ComponentProps {
18
18
  const ModalFooter: FunctionComponent<ComponentProps> = (
19
19
  props: ComponentProps,
20
20
  ): ReactElement => {
21
+ if (!props.onClose && !props.onSubmit && !props.leftFooterElement) {
22
+ return <></>;
23
+ }
24
+
21
25
  return (
22
- <div className="bg-gray-50 px-4 py-3 flex flex-row gap-3 md:px-6 md:justify-between">
26
+ <div
27
+ className="flex shrink-0 flex-col gap-3 rounded-b-2xl border-t border-gray-100 bg-gray-50/70 px-5 py-3 sm:flex-row sm:items-center sm:justify-between sm:rounded-b-xl sm:px-6"
28
+ data-testid="modal-footer"
29
+ >
23
30
  {props.leftFooterElement ? (
24
- <div className="flex flex-row gap-3">{props.leftFooterElement}</div>
31
+ <div className="flex w-full flex-col gap-2 sm:w-auto sm:flex-row sm:flex-wrap sm:items-center [&>div]:w-full [&>div]:flex-col [&_button]:!w-full sm:[&>div]:w-auto sm:[&>div]:flex-row sm:[&_button]:!ml-0 sm:[&_button]:!w-auto">
32
+ {props.leftFooterElement}
33
+ </div>
25
34
  ) : (
26
- <div />
35
+ <div className="hidden sm:block" />
27
36
  )}
28
- <div className="flex flex-row gap-3 md:flex-row-reverse">
37
+ <div className="flex w-full flex-col gap-2 sm:w-auto sm:flex-row sm:items-center">
38
+ {props.onClose ? (
39
+ <Button
40
+ buttonStyle={
41
+ props.closeButtonStyleType
42
+ ? props.closeButtonStyleType
43
+ : ButtonStyleType.NORMAL
44
+ }
45
+ title={props.closeButtonText ? props.closeButtonText : "Cancel"}
46
+ data-dismiss="modal"
47
+ onClick={() => {
48
+ props.onClose?.();
49
+ }}
50
+ dataTestId="modal-footer-close-button"
51
+ className="sm:!w-auto md:!ml-0"
52
+ />
53
+ ) : (
54
+ <></>
55
+ )}
56
+
29
57
  {props.onSubmit ? (
30
58
  <Button
31
59
  buttonStyle={
@@ -47,24 +75,7 @@ const ModalFooter: FunctionComponent<ComponentProps> = (
47
75
  : ButtonType.Button
48
76
  }
49
77
  dataTestId="modal-footer-submit-button"
50
- />
51
- ) : (
52
- <></>
53
- )}
54
-
55
- {props.onClose ? (
56
- <Button
57
- buttonStyle={
58
- props.closeButtonStyleType
59
- ? props.closeButtonStyleType
60
- : ButtonStyleType.NORMAL
61
- }
62
- title={props.closeButtonText ? props.closeButtonText : "Cancel"}
63
- data-dismiss="modal"
64
- onClick={() => {
65
- props.onClose?.();
66
- }}
67
- dataTestId="modal-footer-close-button"
78
+ className="sm:!w-auto md:!ml-0"
68
79
  />
69
80
  ) : (
70
81
  <></>
@@ -226,7 +226,7 @@ const TableViewElement: <T extends DatabaseBaseModel | AnalyticsBaseModel>(
226
226
  const getMenuContents: GetReactElementArrayFunction =
227
227
  (): Array<ReactElement> => {
228
228
  if (isLoading) {
229
- return [<BarLoader key={-1} />];
229
+ return [<BarLoader key={-1} color="var(--ou-link, #4f46e5)" />];
230
230
  }
231
231
 
232
232
  const elements: Array<ReactElement> = [];