@oneuptime/common 10.0.70 → 10.0.72

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 (144) hide show
  1. package/Models/DatabaseModels/Alert.ts +55 -0
  2. package/Models/DatabaseModels/Incident.ts +55 -0
  3. package/Models/DatabaseModels/KubernetesCluster.ts +6 -4
  4. package/Models/DatabaseModels/Project.ts +5 -5
  5. package/Models/DatabaseModels/StatusPage.ts +80 -0
  6. package/Server/API/StatusPageAPI.ts +4 -0
  7. package/Server/Infrastructure/Postgres/SchemaMigrations/1776881254913-DedupeKubernetesClustersAndAddUniqueIndex.ts +6 -3
  8. package/Server/Infrastructure/Postgres/SchemaMigrations/1776886248361-MigrationName.ts +17 -0
  9. package/Server/Infrastructure/Postgres/SchemaMigrations/1776940714709-MigrationName.ts +41 -0
  10. package/Server/Infrastructure/Postgres/SchemaMigrations/1776971364783-AddStatusPageLanguageSettings.ts +25 -0
  11. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
  12. package/Server/Services/AIBillingService.ts +2 -2
  13. package/Server/Services/AnalyticsDatabaseService.ts +17 -7
  14. package/Server/Services/BillingService.ts +116 -48
  15. package/Server/Services/NotificationService.ts +2 -2
  16. package/Server/Types/Database/QueryUtil.ts +13 -7
  17. package/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.ts +175 -29
  18. package/Server/Utils/Monitor/Criteria/ServerMonitorCriteria.ts +71 -0
  19. package/Server/Utils/Monitor/MonitorAlert.ts +170 -7
  20. package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +171 -2
  21. package/Server/Utils/Monitor/MonitorIncident.ts +212 -8
  22. package/Server/Utils/Monitor/MonitorMetricUtil.ts +423 -1
  23. package/Server/Utils/Monitor/MonitorResource.ts +2 -0
  24. package/Server/Utils/Monitor/MonitorTemplateUtil.ts +99 -0
  25. package/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.test.ts +268 -0
  26. package/Types/BaseDatabase/IncludesNone.ts +1 -4
  27. package/Types/Email.ts +50 -0
  28. package/Types/Infrastructure/BasicMetrics.ts +75 -0
  29. package/Types/Metrics/MetricQueryData.ts +11 -0
  30. package/Types/Monitor/CriteriaFilter.ts +10 -0
  31. package/Types/Monitor/MetricMonitor/MetricCriteriaContext.ts +11 -0
  32. package/Types/Monitor/MetricMonitor/MetricMonitorResponse.ts +10 -0
  33. package/Types/Monitor/MetricMonitor/MetricSeriesResult.ts +20 -0
  34. package/Types/Monitor/MonitorMetricType.ts +34 -0
  35. package/Types/Monitor/ServerMonitor/ServerMonitorResponse.ts +8 -0
  36. package/Types/Probe/ProbeApiIngestResponse.ts +25 -0
  37. package/Types/StatusPage/StatusPageLanguage.ts +29 -0
  38. package/UI/Components/Charts/Area/AreaChart.tsx +17 -12
  39. package/UI/Components/Charts/Bar/BarChart.tsx +16 -11
  40. package/UI/Components/Charts/ChartGroup/ChartGroup.tsx +23 -0
  41. package/UI/Components/Charts/Line/LineChart.tsx +16 -11
  42. package/UI/Components/Filters/DateFilter.tsx +16 -8
  43. package/UI/Components/Filters/EntityFilter.tsx +33 -18
  44. package/UI/Components/Filters/FilterViewer.tsx +7 -5
  45. package/UI/Components/Filters/FiltersForm.tsx +27 -5
  46. package/UI/Components/Filters/NumberFilter.tsx +3 -2
  47. package/UI/Components/Filters/TextFilter.tsx +5 -4
  48. package/UI/Components/ModelTable/BaseModelTable.tsx +5 -3
  49. package/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.ts +453 -0
  50. package/UI/Components/MonitorTemplateVariables/TemplateVariablesModal.tsx +229 -0
  51. package/Utils/Metrics/MetricSeriesFingerprint.ts +97 -0
  52. package/Utils/Monitor/MonitorMetricType.ts +309 -19
  53. package/build/dist/Models/DatabaseModels/Alert.js +57 -0
  54. package/build/dist/Models/DatabaseModels/Alert.js.map +1 -1
  55. package/build/dist/Models/DatabaseModels/Incident.js +57 -0
  56. package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
  57. package/build/dist/Models/DatabaseModels/KubernetesCluster.js +6 -4
  58. package/build/dist/Models/DatabaseModels/KubernetesCluster.js.map +1 -1
  59. package/build/dist/Models/DatabaseModels/Project.js +5 -5
  60. package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
  61. package/build/dist/Models/DatabaseModels/StatusPage.js +82 -0
  62. package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
  63. package/build/dist/Server/API/StatusPageAPI.js +4 -0
  64. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  65. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776881254913-DedupeKubernetesClustersAndAddUniqueIndex.js +4 -2
  66. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776881254913-DedupeKubernetesClustersAndAddUniqueIndex.js.map +1 -1
  67. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776886248361-MigrationName.js +12 -0
  68. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776886248361-MigrationName.js.map +1 -0
  69. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776940714709-MigrationName.js +22 -0
  70. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776940714709-MigrationName.js.map +1 -0
  71. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776971364783-AddStatusPageLanguageSettings.js +14 -0
  72. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776971364783-AddStatusPageLanguageSettings.js.map +1 -0
  73. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +4 -0
  74. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  75. package/build/dist/Server/Services/AIBillingService.js +2 -2
  76. package/build/dist/Server/Services/AIBillingService.js.map +1 -1
  77. package/build/dist/Server/Services/AnalyticsDatabaseService.js +14 -4
  78. package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
  79. package/build/dist/Server/Services/BillingService.js +99 -39
  80. package/build/dist/Server/Services/BillingService.js.map +1 -1
  81. package/build/dist/Server/Services/NotificationService.js +2 -2
  82. package/build/dist/Server/Services/NotificationService.js.map +1 -1
  83. package/build/dist/Server/Types/Database/QueryUtil.js +13 -7
  84. package/build/dist/Server/Types/Database/QueryUtil.js.map +1 -1
  85. package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js +132 -30
  86. package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js.map +1 -1
  87. package/build/dist/Server/Utils/Monitor/Criteria/ServerMonitorCriteria.js +58 -7
  88. package/build/dist/Server/Utils/Monitor/Criteria/ServerMonitorCriteria.js.map +1 -1
  89. package/build/dist/Server/Utils/Monitor/MonitorAlert.js +134 -12
  90. package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
  91. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +112 -0
  92. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  93. package/build/dist/Server/Utils/Monitor/MonitorIncident.js +159 -15
  94. package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
  95. package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js +373 -0
  96. package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js.map +1 -1
  97. package/build/dist/Server/Utils/Monitor/MonitorResource.js +2 -0
  98. package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
  99. package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js +65 -0
  100. package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js.map +1 -1
  101. package/build/dist/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.test.js +199 -0
  102. package/build/dist/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.test.js.map +1 -1
  103. package/build/dist/Types/BaseDatabase/IncludesNone.js.map +1 -1
  104. package/build/dist/Types/Email.js +42 -0
  105. package/build/dist/Types/Email.js.map +1 -1
  106. package/build/dist/Types/Monitor/CriteriaFilter.js +10 -0
  107. package/build/dist/Types/Monitor/CriteriaFilter.js.map +1 -1
  108. package/build/dist/Types/Monitor/MetricMonitor/MetricSeriesResult.js +2 -0
  109. package/build/dist/Types/Monitor/MetricMonitor/MetricSeriesResult.js.map +1 -0
  110. package/build/dist/Types/Monitor/MonitorMetricType.js +28 -0
  111. package/build/dist/Types/Monitor/MonitorMetricType.js.map +1 -1
  112. package/build/dist/Types/StatusPage/StatusPageLanguage.js +21 -0
  113. package/build/dist/Types/StatusPage/StatusPageLanguage.js.map +1 -0
  114. package/build/dist/UI/Components/Charts/Area/AreaChart.js +13 -12
  115. package/build/dist/UI/Components/Charts/Area/AreaChart.js.map +1 -1
  116. package/build/dist/UI/Components/Charts/Bar/BarChart.js +12 -11
  117. package/build/dist/UI/Components/Charts/Bar/BarChart.js.map +1 -1
  118. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js +11 -3
  119. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js.map +1 -1
  120. package/build/dist/UI/Components/Charts/Line/LineChart.js +12 -11
  121. package/build/dist/UI/Components/Charts/Line/LineChart.js.map +1 -1
  122. package/build/dist/UI/Components/Filters/DateFilter.js +1 -4
  123. package/build/dist/UI/Components/Filters/DateFilter.js.map +1 -1
  124. package/build/dist/UI/Components/Filters/EntityFilter.js +21 -14
  125. package/build/dist/UI/Components/Filters/EntityFilter.js.map +1 -1
  126. package/build/dist/UI/Components/Filters/FilterViewer.js +1 -2
  127. package/build/dist/UI/Components/Filters/FilterViewer.js.map +1 -1
  128. package/build/dist/UI/Components/Filters/FiltersForm.js +7 -3
  129. package/build/dist/UI/Components/Filters/FiltersForm.js.map +1 -1
  130. package/build/dist/UI/Components/Filters/NumberFilter.js +0 -1
  131. package/build/dist/UI/Components/Filters/NumberFilter.js.map +1 -1
  132. package/build/dist/UI/Components/Filters/TextFilter.js +5 -4
  133. package/build/dist/UI/Components/Filters/TextFilter.js.map +1 -1
  134. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +5 -3
  135. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  136. package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.js +383 -0
  137. package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.js.map +1 -0
  138. package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesModal.js +109 -0
  139. package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesModal.js.map +1 -0
  140. package/build/dist/Utils/Metrics/MetricSeriesFingerprint.js +81 -0
  141. package/build/dist/Utils/Metrics/MetricSeriesFingerprint.js.map +1 -0
  142. package/build/dist/Utils/Monitor/MonitorMetricType.js +287 -19
  143. package/build/dist/Utils/Monitor/MonitorMetricType.js.map +1 -1
  144. package/package.json +1 -1
@@ -1,4 +1,5 @@
1
1
  import { AreaChart } from "../ChartLibrary/AreaChart/AreaChart";
2
+ import { AvailableChartColorsKeys } from "../ChartLibrary/Utils/ChartColors";
2
3
  import React, {
3
4
  FunctionComponent,
4
5
  ReactElement,
@@ -17,6 +18,19 @@ import ExemplarPoint from "../Types/ExemplarPoint";
17
18
  import XAxisUtil from "../Utils/XAxis";
18
19
  import NoDataMessage from "../ChartGroup/NoDataMessage";
19
20
 
21
+ export const AreaChartPalette: Array<AvailableChartColorsKeys> = [
22
+ "blue",
23
+ "emerald",
24
+ "violet",
25
+ "amber",
26
+ "cyan",
27
+ "pink",
28
+ "lime",
29
+ "fuchsia",
30
+ "indigo",
31
+ "rose",
32
+ ];
33
+
20
34
  export interface ComponentProps {
21
35
  data: Array<SeriesPoint>;
22
36
  xAxis: XAxis;
@@ -27,6 +41,7 @@ export interface ComponentProps {
27
41
  referenceLines?: Array<ChartReferenceLineProps> | undefined;
28
42
  exemplarPoints?: Array<ExemplarPoint> | undefined;
29
43
  onExemplarClick?: ((exemplar: ExemplarPoint) => void) | undefined;
44
+ showLegend?: boolean | undefined;
30
45
  }
31
46
 
32
47
  export interface AreaInternalProps extends ComponentProps {
@@ -93,20 +108,10 @@ const AreaChartElement: FunctionComponent<AreaInternalProps> = (
93
108
  tickGap={1}
94
109
  index={"Time"}
95
110
  categories={categories}
96
- colors={[
97
- "blue",
98
- "emerald",
99
- "violet",
100
- "amber",
101
- "cyan",
102
- "pink",
103
- "lime",
104
- "fuchsia",
105
- "indigo",
106
- "rose",
107
- ]}
111
+ colors={AreaChartPalette}
108
112
  valueFormatter={props.yAxis.options.formatter || undefined}
109
113
  showTooltip={true}
114
+ showLegend={props.showLegend !== false}
110
115
  connectNulls={true}
111
116
  curve={props.curve || ChartCurve.MONOTONE}
112
117
  syncid={props.sync ? props.syncid : undefined}
@@ -1,4 +1,5 @@
1
1
  import { BarChart } from "../ChartLibrary/BarChart/BarChart";
2
+ import { AvailableChartColorsKeys } from "../ChartLibrary/Utils/ChartColors";
2
3
  import React, { FunctionComponent, ReactElement, useEffect } from "react";
3
4
  import SeriesPoint from "../Types/SeriesPoints";
4
5
  import { XAxis } from "../Types/XAxis/XAxis";
@@ -8,6 +9,18 @@ import DataPointUtil from "../Utils/DataPoint";
8
9
  import ChartReferenceLineProps from "../Types/ReferenceLineProps";
9
10
  import NoDataMessage from "../ChartGroup/NoDataMessage";
10
11
 
12
+ export const BarChartPalette: Array<AvailableChartColorsKeys> = [
13
+ "indigo",
14
+ "rose",
15
+ "emerald",
16
+ "amber",
17
+ "cyan",
18
+ "gray",
19
+ "pink",
20
+ "lime",
21
+ "fuchsia",
22
+ ];
23
+
11
24
  export interface ComponentProps {
12
25
  data: Array<SeriesPoint>;
13
26
  xAxis: XAxis;
@@ -15,6 +28,7 @@ export interface ComponentProps {
15
28
  sync: boolean;
16
29
  heightInPx?: number | undefined;
17
30
  referenceLines?: Array<ChartReferenceLineProps> | undefined;
31
+ showLegend?: boolean | undefined;
18
32
  }
19
33
 
20
34
  export interface BarInternalProps extends ComponentProps {
@@ -61,19 +75,10 @@ const BarChartElement: FunctionComponent<BarInternalProps> = (
61
75
  tickGap={1}
62
76
  index={"Time"}
63
77
  categories={categories}
64
- colors={[
65
- "indigo",
66
- "rose",
67
- "emerald",
68
- "amber",
69
- "cyan",
70
- "gray",
71
- "pink",
72
- "lime",
73
- "fuchsia",
74
- ]}
78
+ colors={BarChartPalette}
75
79
  valueFormatter={props.yAxis.options.formatter || undefined}
76
80
  showTooltip={true}
81
+ showLegend={props.showLegend !== false}
77
82
  yAxisWidth={60}
78
83
  syncid={props.sync ? props.syncid : undefined}
79
84
  onValueChange={() => {}}
@@ -36,6 +36,13 @@ export interface Chart {
36
36
  metricInfo?: ChartMetricInfo | undefined;
37
37
  exemplarPoints?: Array<ExemplarPoint> | undefined;
38
38
  onExemplarClick?: ((exemplar: ExemplarPoint) => void) | undefined;
39
+ /**
40
+ * Optional control panel rendered between the chart title and the
41
+ * chart body. Used by per-series-grouped metric charts to surface a
42
+ * search box, per-series toggles, and a "show all" escape hatch so
43
+ * the chart stays usable at thousands of unique label combinations.
44
+ */
45
+ seriesControls?: ReactElement | undefined;
39
46
  }
40
47
 
41
48
  export interface ComponentProps {
@@ -58,6 +65,13 @@ const ChartGroup: FunctionComponent<ComponentProps> = (
58
65
  chart: Chart,
59
66
  index: number,
60
67
  ): ReactElement => {
68
+ /*
69
+ * When the chart has its own seriesControls panel, that panel doubles
70
+ * as a colored, interactive legend — so we suppress the built-in
71
+ * Recharts legend to avoid showing two legends for the same series.
72
+ */
73
+ const showLegend: boolean = !chart.seriesControls;
74
+
61
75
  switch (chart.type) {
62
76
  case ChartType.LINE:
63
77
  return (
@@ -68,6 +82,7 @@ const ChartGroup: FunctionComponent<ComponentProps> = (
68
82
  heightInPx={props.heightInPx}
69
83
  exemplarPoints={chart.exemplarPoints}
70
84
  onExemplarClick={chart.onExemplarClick}
85
+ showLegend={showLegend}
71
86
  />
72
87
  );
73
88
  case ChartType.BAR:
@@ -77,6 +92,7 @@ const ChartGroup: FunctionComponent<ComponentProps> = (
77
92
  {...(chart.props as BarChartProps)}
78
93
  syncid={syncId}
79
94
  heightInPx={props.heightInPx}
95
+ showLegend={showLegend}
80
96
  />
81
97
  );
82
98
  case ChartType.AREA:
@@ -88,6 +104,7 @@ const ChartGroup: FunctionComponent<ComponentProps> = (
88
104
  heightInPx={props.heightInPx}
89
105
  exemplarPoints={chart.exemplarPoints}
90
106
  onExemplarClick={chart.onExemplarClick}
107
+ showLegend={showLegend}
91
108
  />
92
109
  );
93
110
  default:
@@ -239,6 +256,9 @@ const ChartGroup: FunctionComponent<ComponentProps> = (
239
256
  </p>
240
257
  )}
241
258
  </div>
259
+ {chart.seriesControls ? (
260
+ <div className="mb-3">{chart.seriesControls}</div>
261
+ ) : null}
242
262
  {getChartContent(chart, index)}
243
263
  </div>
244
264
  </div>
@@ -283,6 +303,9 @@ const ChartGroup: FunctionComponent<ComponentProps> = (
283
303
  {chart.description}
284
304
  </p>
285
305
  )}
306
+ {chart.seriesControls ? (
307
+ <div className="mt-3">{chart.seriesControls}</div>
308
+ ) : null}
286
309
  {getChartContent(chart, index)}
287
310
  </div>
288
311
  );
@@ -1,4 +1,5 @@
1
1
  import { LineChart } from "../ChartLibrary/LineChart/LineChart";
2
+ import { AvailableChartColorsKeys } from "../ChartLibrary/Utils/ChartColors";
2
3
  import React, {
3
4
  FunctionComponent,
4
5
  ReactElement,
@@ -17,6 +18,18 @@ import ExemplarPoint from "../Types/ExemplarPoint";
17
18
  import XAxisUtil from "../Utils/XAxis";
18
19
  import NoDataMessage from "../ChartGroup/NoDataMessage";
19
20
 
21
+ export const LineChartPalette: Array<AvailableChartColorsKeys> = [
22
+ "indigo",
23
+ "rose",
24
+ "emerald",
25
+ "amber",
26
+ "cyan",
27
+ "gray",
28
+ "pink",
29
+ "lime",
30
+ "fuchsia",
31
+ ];
32
+
20
33
  export interface ComponentProps {
21
34
  data: Array<SeriesPoint>;
22
35
  xAxis: XAxis;
@@ -27,6 +40,7 @@ export interface ComponentProps {
27
40
  referenceLines?: Array<ChartReferenceLineProps> | undefined;
28
41
  exemplarPoints?: Array<ExemplarPoint> | undefined;
29
42
  onExemplarClick?: ((exemplar: ExemplarPoint) => void) | undefined;
43
+ showLegend?: boolean | undefined;
30
44
  }
31
45
 
32
46
  export interface LineInternalProps extends ComponentProps {
@@ -93,19 +107,10 @@ const LineChartElement: FunctionComponent<LineInternalProps> = (
93
107
  tickGap={1}
94
108
  index={"Time"}
95
109
  categories={categories}
96
- colors={[
97
- "indigo",
98
- "rose",
99
- "emerald",
100
- "amber",
101
- "cyan",
102
- "gray",
103
- "pink",
104
- "lime",
105
- "fuchsia",
106
- ]}
110
+ colors={LineChartPalette}
107
111
  valueFormatter={props.yAxis.options.formatter || undefined}
108
112
  showTooltip={true}
113
+ showLegend={props.showLegend !== false}
109
114
  connectNulls={true}
110
115
  curve={props.curve}
111
116
  syncid={props.sync ? props.syncid : undefined}
@@ -39,7 +39,9 @@ type DateState = {
39
39
  end: Date | null;
40
40
  };
41
41
 
42
- const toDate = (value: unknown): Date | null => {
42
+ type ToDateFunction = (value: unknown) => Date | null;
43
+
44
+ const toDate: ToDateFunction = (value: unknown): Date | null => {
43
45
  if (!value) {
44
46
  return null;
45
47
  }
@@ -53,7 +55,9 @@ const toDate = (value: unknown): Date | null => {
53
55
  }
54
56
  };
55
57
 
56
- const detectState = (rawValue: unknown): DateState => {
58
+ type DetectStateFunction = (rawValue: unknown) => DateState;
59
+
60
+ const detectState: DetectStateFunction = (rawValue: unknown): DateState => {
57
61
  if (rawValue instanceof InBetween) {
58
62
  const start: Date | null = toDate(rawValue.startValue as unknown);
59
63
  const end: Date | null = toDate(rawValue.endValue as unknown);
@@ -102,7 +106,12 @@ const detectState = (rawValue: unknown): DateState => {
102
106
  return { operator: FilterOperator.Is, start: null, end: null };
103
107
  };
104
108
 
105
- const buildValue = (state: DateState, isDateTime: boolean): unknown => {
109
+ type BuildValueFunction = (state: DateState, isDateTime: boolean) => unknown;
110
+
111
+ const buildValue: BuildValueFunction = (
112
+ state: DateState,
113
+ isDateTime: boolean,
114
+ ): unknown => {
106
115
  switch (state.operator) {
107
116
  case FilterOperator.Is: {
108
117
  if (!state.start) {
@@ -128,9 +137,7 @@ const buildValue = (state: DateState, isDateTime: boolean): unknown => {
128
137
  (isDateTime
129
138
  ? state.start
130
139
  : OneUptimeDate.getStartOfDay(state.start)) as any,
131
- (isDateTime
132
- ? state.end
133
- : OneUptimeDate.getEndOfDay(state.end)) as any,
140
+ (isDateTime ? state.end : OneUptimeDate.getEndOfDay(state.end)) as any,
134
141
  );
135
142
  }
136
143
  case FilterOperator.IsEmpty:
@@ -163,7 +170,6 @@ const DateFilter: DateFilterFunction = <T extends GenericObject>(
163
170
  if (raw !== undefined && raw !== null) {
164
171
  setLocalOperator(detected.operator);
165
172
  }
166
- // eslint-disable-next-line react-hooks/exhaustive-deps
167
173
  }, [props.filterData[filter.key]]);
168
174
 
169
175
  const state: DateState = { ...detected, operator: localOperator };
@@ -204,7 +210,9 @@ const DateFilter: DateFilterFunction = <T extends GenericObject>(
204
210
  }}
205
211
  />
206
212
  {!valuelessOperator && (
207
- <div className={isBetween ? "flex-1 flex gap-2 min-w-0" : "flex-1 min-w-0"}>
213
+ <div
214
+ className={isBetween ? "flex-1 flex gap-2 min-w-0" : "flex-1 min-w-0"}
215
+ >
208
216
  <div className="flex-1 min-w-0">
209
217
  <Input
210
218
  key={`${filter.key as string}-start-${state.operator}`}
@@ -42,7 +42,11 @@ type EntityState = {
42
42
  values: Array<string>;
43
43
  };
44
44
 
45
- const detectArrayState = (rawValue: unknown): EntityState => {
45
+ type DetectStateFunction = (rawValue: unknown) => EntityState;
46
+
47
+ const detectArrayState: DetectStateFunction = (
48
+ rawValue: unknown,
49
+ ): EntityState => {
46
50
  if (rawValue instanceof IncludesAll) {
47
51
  return {
48
52
  operator: FilterOperator.HasAllOf,
@@ -84,7 +88,9 @@ const detectArrayState = (rawValue: unknown): EntityState => {
84
88
  return { operator: FilterOperator.HasAnyOf, values: [] };
85
89
  };
86
90
 
87
- const detectSingleState = (rawValue: unknown): EntityState => {
91
+ const detectSingleState: DetectStateFunction = (
92
+ rawValue: unknown,
93
+ ): EntityState => {
88
94
  if (rawValue instanceof IsNull) {
89
95
  return { operator: FilterOperator.IsEmpty, values: [] };
90
96
  }
@@ -97,12 +103,18 @@ const detectSingleState = (rawValue: unknown): EntityState => {
97
103
  return { operator: FilterOperator.Is, values: [] };
98
104
  };
99
105
 
100
- const buildArrayValue = (state: EntityState): unknown => {
106
+ type BuildValueFunction = (state: EntityState) => unknown;
107
+
108
+ const buildArrayValue: BuildValueFunction = (state: EntityState): unknown => {
101
109
  switch (state.operator) {
102
110
  case FilterOperator.HasAllOf:
103
- return state.values.length > 0 ? new IncludesAll(state.values) : undefined;
111
+ return state.values.length > 0
112
+ ? new IncludesAll(state.values)
113
+ : undefined;
104
114
  case FilterOperator.HasNoneOf:
105
- return state.values.length > 0 ? new IncludesNone(state.values) : undefined;
115
+ return state.values.length > 0
116
+ ? new IncludesNone(state.values)
117
+ : undefined;
106
118
  case FilterOperator.HasAnyOf:
107
119
  return state.values.length > 0 ? state.values : undefined;
108
120
  case FilterOperator.IsEmpty:
@@ -114,15 +126,13 @@ const buildArrayValue = (state: EntityState): unknown => {
114
126
  }
115
127
  };
116
128
 
117
- const buildSingleValue = (state: EntityState): unknown => {
129
+ const buildSingleValue: BuildValueFunction = (state: EntityState): unknown => {
118
130
  switch (state.operator) {
119
131
  case FilterOperator.Is:
120
132
  return state.values[0] || undefined;
121
133
  case FilterOperator.IsNot:
122
134
  // Use IncludesNone with single-item array to represent "is not".
123
- return state.values[0]
124
- ? new IncludesNone([state.values[0]])
125
- : undefined;
135
+ return state.values[0] ? new IncludesNone([state.values[0]]) : undefined;
126
136
  case FilterOperator.IsEmpty:
127
137
  return new IsNull();
128
138
  case FilterOperator.IsNotEmpty:
@@ -153,24 +163,29 @@ const EntityFilter: EntityFilterFunction = <T extends GenericObject>(
153
163
  ? detectArrayState(props.filterData[filter.key])
154
164
  : detectSingleState(props.filterData[filter.key]);
155
165
 
156
- // Hold the operator locally so the user's choice persists even when no
157
- // values are selected yet (otherwise `buildArrayValue` would return
158
- // undefined, filterData wouldn't carry the operator, and the next render
159
- // would reset back to the default).
166
+ /*
167
+ * Hold the operator locally so the user's choice persists even when no
168
+ * values are selected yet (otherwise `buildArrayValue` would return
169
+ * undefined, filterData wouldn't carry the operator, and the next render
170
+ * would reset back to the default).
171
+ */
160
172
  const [localOperator, setLocalOperator] = useState<FilterOperator>(
161
173
  detectedState.operator,
162
174
  );
163
175
 
164
- // When the external filter data changes and can unambiguously tell us
165
- // which operator it represents, sync local state.
176
+ /*
177
+ * When the external filter data changes and can unambiguously tell us
178
+ * which operator it represents, sync local state.
179
+ */
166
180
  useEffect(() => {
167
181
  const raw: unknown = props.filterData[filter.key];
168
182
  if (raw !== undefined && raw !== null) {
169
183
  setLocalOperator(detectedState.operator);
170
184
  }
171
- // Intentionally only re-run when the underlying filter data reference
172
- // changes not on every detectedState re-computation.
173
- // eslint-disable-next-line react-hooks/exhaustive-deps
185
+ /*
186
+ * Intentionally only re-run when the underlying filter data reference
187
+ * changes — not on every detectedState re-computation.
188
+ */
174
189
  }, [props.filterData[filter.key]]);
175
190
 
176
191
  const operator: FilterOperator = localOperator;
@@ -1,4 +1,4 @@
1
- import Icon from "../Icon/Icon";
1
+ import Icon, { SizeProp } from "../Icon/Icon";
2
2
  import Includes from "../../../Types/BaseDatabase/Includes";
3
3
  import IncludesAll from "../../../Types/BaseDatabase/IncludesAll";
4
4
  import IncludesNone from "../../../Types/BaseDatabase/IncludesNone";
@@ -16,7 +16,6 @@ import NotNull from "../../../Types/BaseDatabase/NotNull";
16
16
  import Button, { ButtonStyleType } from "../Button/Button";
17
17
  import { DropdownOption } from "../Dropdown/Dropdown";
18
18
  import ErrorMessage from "../ErrorMessage/ErrorMessage";
19
- import { SizeProp } from "../Icon/Icon";
20
19
  import Modal, { ModalWidth } from "../Modal/Modal";
21
20
  import FieldType from "../Types/FieldType";
22
21
  import FilterViewerItem from "./FilterViewerItem";
@@ -213,7 +212,8 @@ const FilterComponent: FilterComponentFunction = <T extends GenericObject>(
213
212
  if (value instanceof NotNull) {
214
213
  return (
215
214
  <span>
216
- <span className="font-medium">{data.filter.title}</span> is not empty
215
+ <span className="font-medium">{data.filter.title}</span> is not
216
+ empty
217
217
  </span>
218
218
  );
219
219
  }
@@ -302,7 +302,8 @@ const FilterComponent: FilterComponentFunction = <T extends GenericObject>(
302
302
  if (value instanceof NotNull) {
303
303
  return (
304
304
  <span>
305
- <span className="font-medium">{data.filter.title}</span> is not empty
305
+ <span className="font-medium">{data.filter.title}</span> is not
306
+ empty
306
307
  </span>
307
308
  );
308
309
  }
@@ -369,7 +370,8 @@ const FilterComponent: FilterComponentFunction = <T extends GenericObject>(
369
370
  if (rawValue instanceof NotNull) {
370
371
  return (
371
372
  <span>
372
- <span className="font-medium">{data.filter.title}</span> is not empty
373
+ <span className="font-medium">{data.filter.title}</span> is not
374
+ empty
373
375
  </span>
374
376
  );
375
377
  }
@@ -28,6 +28,22 @@ export interface ComponentProps<T extends GenericObject> {
28
28
  | undefined
29
29
  | ((showAdvancedFilters: boolean) => void);
30
30
  showAdvancedFiltersByDefault?: boolean | undefined;
31
+ /**
32
+ * Suppress the built-in "Show/Hide Advanced Filters" toggle
33
+ * button. Useful when the parent renders its own toggle further
34
+ * down the page (e.g. below other non-filter controls) and needs
35
+ * full layout control. Parent must drive visibility through
36
+ * `showAdvancedFilters` instead.
37
+ */
38
+ hideAdvancedFilterToggle?: boolean | undefined;
39
+ /**
40
+ * Controlled replacement for the internal toggle state. When
41
+ * provided, the form renders advanced filters based on this flag
42
+ * and calls `onAdvancedFiltersToggle` for changes but does not
43
+ * manage its own state. Pair with `hideAdvancedFilterToggle` when
44
+ * the parent owns the UI toggle.
45
+ */
46
+ showAdvancedFilters?: boolean | undefined;
31
47
  }
32
48
 
33
49
  type FiltersFormFunction = <T extends GenericObject>(
@@ -51,16 +67,24 @@ const FiltersForm: FiltersFormFunction = <T extends GenericObject>(
51
67
  }
52
68
  };
53
69
 
54
- const showAdvancedFilterButton: boolean = Boolean(
70
+ const hasAdvancedFilter: boolean = Boolean(
55
71
  props.filters.find((filter: Filter<T>) => {
56
72
  return filter.isAdvancedFilter;
57
73
  }),
58
74
  );
59
75
 
60
- const [showMoreFilters, setShowMoreFilters] = React.useState<boolean>(
76
+ const showAdvancedFilterButton: boolean =
77
+ hasAdvancedFilter && !props.hideAdvancedFilterToggle;
78
+
79
+ const [internalShowMoreFilters, setShowMoreFilters] = React.useState<boolean>(
61
80
  props.showAdvancedFiltersByDefault ?? false,
62
81
  );
63
82
 
83
+ const showMoreFilters: boolean =
84
+ props.showAdvancedFilters !== undefined
85
+ ? props.showAdvancedFilters
86
+ : internalShowMoreFilters;
87
+
64
88
  type ClearFilterFunction = (key: keyof T) => void;
65
89
 
66
90
  const clearFilter: ClearFilterFunction = (key: keyof T): void => {
@@ -72,9 +96,7 @@ const FiltersForm: FiltersFormFunction = <T extends GenericObject>(
72
96
  const visibleFilters: Array<Filter<T>> = props.filters.filter(
73
97
  (filter: Filter<T>) => {
74
98
  if (filter.isAdvancedFilter) {
75
- return (
76
- showMoreFilters && !props.isFilterLoading && !props.filterError
77
- );
99
+ return showMoreFilters && !props.isFilterLoading && !props.filterError;
78
100
  }
79
101
  return true;
80
102
  },
@@ -168,7 +168,6 @@ const NumberFilter: NumberFilterFunction = <T extends GenericObject>(
168
168
  if (raw !== undefined && raw !== null) {
169
169
  setLocalOperator(detected.operator);
170
170
  }
171
- // eslint-disable-next-line react-hooks/exhaustive-deps
172
171
  }, [props.filterData[filter.key]]);
173
172
 
174
173
  const state: NumberState = { ...detected, operator: localOperator };
@@ -209,7 +208,9 @@ const NumberFilter: NumberFilterFunction = <T extends GenericObject>(
209
208
  }}
210
209
  />
211
210
  {!valuelessOperator && (
212
- <div className={isBetween ? "flex-1 flex gap-2 min-w-0" : "flex-1 min-w-0"}>
211
+ <div
212
+ className={isBetween ? "flex-1 flex gap-2 min-w-0" : "flex-1 min-w-0"}
213
+ >
213
214
  <div className="flex-1 min-w-0">
214
215
  <input
215
216
  type="number"
@@ -133,9 +133,11 @@ const TextFilter: TextFilterFunction = <T extends GenericObject>(
133
133
  const detected: { operator: FilterOperator; value: string } =
134
134
  detectCurrentState(props.filterData[filter.key]);
135
135
 
136
- // Keep the operator locally so the user's choice persists even when no
137
- // value has been typed yet (otherwise buildQueryValue returns undefined,
138
- // the filter is deleted, and the operator resets on re-render).
136
+ /*
137
+ * Keep the operator locally so the user's choice persists even when no
138
+ * value has been typed yet (otherwise buildQueryValue returns undefined,
139
+ * the filter is deleted, and the operator resets on re-render).
140
+ */
139
141
  const [localOperator, setLocalOperator] = useState<FilterOperator>(
140
142
  detected.operator,
141
143
  );
@@ -145,7 +147,6 @@ const TextFilter: TextFilterFunction = <T extends GenericObject>(
145
147
  if (raw !== undefined && raw !== null) {
146
148
  setLocalOperator(detected.operator);
147
149
  }
148
- // eslint-disable-next-line react-hooks/exhaustive-deps
149
150
  }, [props.filterData[filter.key]]);
150
151
 
151
152
  const operator: FilterOperator = localOperator;
@@ -1525,9 +1525,11 @@ const BaseModelTable: <TBaseModel extends BaseModel | AnalyticsBaseModel>(
1525
1525
  );
1526
1526
  }
1527
1527
 
1528
- // Pass any QueryOperator instance (IncludesAll, IncludesNone, StartsWith,
1529
- // EndsWith, NotContains, EqualTo, NotEqual, GreaterThan, LessThan,
1530
- // InBetween, IsNull, NotNull, ...) through to the query as-is.
1528
+ /*
1529
+ * Pass any QueryOperator instance (IncludesAll, IncludesNone, StartsWith,
1530
+ * EndsWith, NotContains, EqualTo, NotEqual, GreaterThan, LessThan,
1531
+ * InBetween, IsNull, NotNull, ...) through to the query as-is.
1532
+ */
1531
1533
  if (filterData[key] instanceof QueryOperator) {
1532
1534
  newQuery[key as keyof TBaseModel] = filterData[key];
1533
1535
  }