@oneuptime/common 10.0.37 → 10.0.39

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 (60) hide show
  1. package/Server/API/TelemetryAPI.ts +10 -0
  2. package/Server/Services/LogAggregationService.ts +24 -1
  3. package/Server/Types/Workflow/Components/Conditions/IfElse.ts +68 -21
  4. package/Server/Utils/Telemetry/Telemetry.ts +38 -19
  5. package/Types/Workflow/Component.ts +1 -0
  6. package/Types/Workflow/Components/Condition.ts +24 -0
  7. package/UI/Components/Charts/Area/AreaChart.tsx +81 -0
  8. package/UI/Components/Charts/ChartGroup/ChartGroup.tsx +106 -63
  9. package/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.tsx +986 -0
  10. package/UI/Components/Charts/ChartLibrary/LineChart/LineChart.tsx +1 -1
  11. package/UI/Components/Charts/ChartLibrary/Utils/ChartColors.ts +18 -1
  12. package/UI/Components/Charts/Utils/XAxis.ts +26 -21
  13. package/UI/Components/ConditionsTable/ConditionsTable.tsx +86 -67
  14. package/UI/Components/Dictionary/DictionaryOfStingsViewer.tsx +48 -28
  15. package/UI/Components/Filters/FiltersForm.tsx +19 -13
  16. package/UI/Components/InfoCard/InfoCard.tsx +3 -1
  17. package/UI/Components/LogsViewer/LogsViewer.tsx +9 -4
  18. package/UI/Components/LogsViewer/components/ActiveFilterChips.tsx +29 -2
  19. package/UI/Components/LogsViewer/types.ts +1 -0
  20. package/UI/Components/Workflow/Utils.ts +32 -1
  21. package/build/dist/Server/API/TelemetryAPI.js +8 -0
  22. package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
  23. package/build/dist/Server/Services/LogAggregationService.js +12 -0
  24. package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
  25. package/build/dist/Server/Types/Workflow/Components/Conditions/IfElse.js +49 -19
  26. package/build/dist/Server/Types/Workflow/Components/Conditions/IfElse.js.map +1 -1
  27. package/build/dist/Server/Utils/Telemetry/Telemetry.js +29 -15
  28. package/build/dist/Server/Utils/Telemetry/Telemetry.js.map +1 -1
  29. package/build/dist/Types/Workflow/Component.js +1 -0
  30. package/build/dist/Types/Workflow/Component.js.map +1 -1
  31. package/build/dist/Types/Workflow/Components/Condition.js +24 -0
  32. package/build/dist/Types/Workflow/Components/Condition.js.map +1 -1
  33. package/build/dist/UI/Components/Charts/Area/AreaChart.js +39 -0
  34. package/build/dist/UI/Components/Charts/Area/AreaChart.js.map +1 -0
  35. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js +28 -9
  36. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js.map +1 -1
  37. package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js +376 -0
  38. package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js.map +1 -0
  39. package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js +1 -1
  40. package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js.map +1 -1
  41. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/ChartColors.js +15 -0
  42. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/ChartColors.js.map +1 -1
  43. package/build/dist/UI/Components/Charts/Utils/XAxis.js +25 -21
  44. package/build/dist/UI/Components/Charts/Utils/XAxis.js.map +1 -1
  45. package/build/dist/UI/Components/ConditionsTable/ConditionsTable.js +51 -30
  46. package/build/dist/UI/Components/ConditionsTable/ConditionsTable.js.map +1 -1
  47. package/build/dist/UI/Components/Dictionary/DictionaryOfStingsViewer.js +23 -11
  48. package/build/dist/UI/Components/Dictionary/DictionaryOfStingsViewer.js.map +1 -1
  49. package/build/dist/UI/Components/Filters/FiltersForm.js +10 -6
  50. package/build/dist/UI/Components/Filters/FiltersForm.js.map +1 -1
  51. package/build/dist/UI/Components/InfoCard/InfoCard.js +1 -1
  52. package/build/dist/UI/Components/InfoCard/InfoCard.js.map +1 -1
  53. package/build/dist/UI/Components/LogsViewer/LogsViewer.js +5 -1
  54. package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
  55. package/build/dist/UI/Components/LogsViewer/components/ActiveFilterChips.js +17 -2
  56. package/build/dist/UI/Components/LogsViewer/components/ActiveFilterChips.js.map +1 -1
  57. package/build/dist/UI/Components/LogsViewer/types.js.map +1 -1
  58. package/build/dist/UI/Components/Workflow/Utils.js +28 -1
  59. package/build/dist/UI/Components/Workflow/Utils.js.map +1 -1
  60. package/package.json +1 -1
@@ -955,7 +955,7 @@ const LineChart: React.ForwardRefExoticComponent<
955
955
  }}
956
956
  key={category}
957
957
  name={category}
958
- type="linear"
958
+ type={props.curve || ChartCurve.LINEAR}
959
959
  dataKey={category}
960
960
  stroke=""
961
961
  strokeWidth={2}
@@ -5,73 +5,84 @@ export type ColorUtility = "bg" | "stroke" | "fill" | "text";
5
5
  export const chartColors: {
6
6
  [color: string]: {
7
7
  [key in ColorUtility]: string;
8
- };
8
+ } & { hex: string };
9
9
  } = {
10
10
  blue: {
11
11
  bg: "bg-blue-500",
12
12
  stroke: "stroke-blue-500",
13
13
  fill: "fill-blue-500",
14
14
  text: "text-blue-500",
15
+ hex: "#3b82f6",
15
16
  },
16
17
  emerald: {
17
18
  bg: "bg-emerald-500",
18
19
  stroke: "stroke-emerald-500",
19
20
  fill: "fill-emerald-500",
20
21
  text: "text-emerald-500",
22
+ hex: "#10b981",
21
23
  },
22
24
  violet: {
23
25
  bg: "bg-violet-500",
24
26
  stroke: "stroke-violet-500",
25
27
  fill: "fill-violet-500",
26
28
  text: "text-violet-500",
29
+ hex: "#8b5cf6",
27
30
  },
28
31
  amber: {
29
32
  bg: "bg-amber-500",
30
33
  stroke: "stroke-amber-500",
31
34
  fill: "fill-amber-500",
32
35
  text: "text-amber-500",
36
+ hex: "#f59e0b",
33
37
  },
34
38
  gray: {
35
39
  bg: "bg-gray-500",
36
40
  stroke: "stroke-gray-500",
37
41
  fill: "fill-gray-500",
38
42
  text: "text-gray-500",
43
+ hex: "#6b7280",
39
44
  },
40
45
  cyan: {
41
46
  bg: "bg-cyan-500",
42
47
  stroke: "stroke-cyan-500",
43
48
  fill: "fill-cyan-500",
44
49
  text: "text-cyan-500",
50
+ hex: "#06b6d4",
45
51
  },
46
52
  pink: {
47
53
  bg: "bg-pink-500",
48
54
  stroke: "stroke-pink-500",
49
55
  fill: "fill-pink-500",
50
56
  text: "text-pink-500",
57
+ hex: "#ec4899",
51
58
  },
52
59
  lime: {
53
60
  bg: "bg-lime-500",
54
61
  stroke: "stroke-lime-500",
55
62
  fill: "fill-lime-500",
56
63
  text: "text-lime-500",
64
+ hex: "#84cc16",
57
65
  },
58
66
  fuchsia: {
59
67
  bg: "bg-fuchsia-500",
60
68
  stroke: "stroke-fuchsia-500",
61
69
  fill: "fill-fuchsia-500",
62
70
  text: "text-fuchsia-500",
71
+ hex: "#d946ef",
63
72
  },
64
73
  indigo: {
65
74
  bg: "bg-indigo-500",
66
75
  stroke: "stroke-indigo-500",
67
76
  fill: "fill-indigo-500",
68
77
  text: "text-indigo-500",
78
+ hex: "#6366f1",
69
79
  },
70
80
  rose: {
71
81
  bg: "bg-rose-500",
72
82
  stroke: "stroke-rose-500",
73
83
  fill: "fill-rose-500",
74
84
  text: "text-rose-500",
85
+ hex: "#f43f5e",
75
86
  },
76
87
  };
77
88
 
@@ -115,3 +126,9 @@ export const getColorClassName: (
115
126
  };
116
127
  return chartColors[color]?.[type] ?? fallbackColor[type];
117
128
  };
129
+
130
+ export const getColorHex: (color: AvailableChartColorsKeys) => string = (
131
+ color: AvailableChartColorsKeys,
132
+ ): string => {
133
+ return chartColors[color]?.hex ?? "#6b7280";
134
+ };
@@ -27,67 +27,72 @@ export default class XAxisUtil {
27
27
  const totalWeeks: number = totalDays / 7;
28
28
  const totalMonths: number = totalDays / 30;
29
29
 
30
- if (totalSeconds <= 100) {
30
+ /*
31
+ * Thresholds are chosen so that the number of tick labels
32
+ * stays between ~6 and ~15 for any range, keeping the axis readable.
33
+ */
34
+
35
+ if (totalSeconds <= 15) {
31
36
  return XAxisPrecision.EVERY_SECOND;
32
37
  }
33
- if (totalSeconds <= 500) {
38
+ if (totalSeconds <= 75) {
34
39
  return XAxisPrecision.EVERY_FIVE_SECONDS;
35
40
  }
36
- if (totalSeconds <= 1000) {
41
+ if (totalSeconds <= 150) {
37
42
  return XAxisPrecision.EVERY_TEN_SECONDS;
38
43
  }
39
- if (totalSeconds <= 3000) {
44
+ if (totalSeconds <= 450) {
40
45
  return XAxisPrecision.EVERY_THIRTY_SECONDS;
41
46
  }
42
- if (totalMinutes <= 100) {
47
+ if (totalMinutes <= 15) {
43
48
  return XAxisPrecision.EVERY_MINUTE;
44
49
  }
45
- if (totalMinutes <= 500) {
50
+ if (totalMinutes <= 75) {
46
51
  return XAxisPrecision.EVERY_FIVE_MINUTES;
47
52
  }
48
- if (totalMinutes <= 1000) {
53
+ if (totalMinutes <= 150) {
49
54
  return XAxisPrecision.EVERY_TEN_MINUTES;
50
55
  }
51
- if (totalMinutes <= 3000) {
56
+ if (totalMinutes <= 450) {
52
57
  return XAxisPrecision.EVERY_THIRTY_MINUTES;
53
58
  }
54
- if (totalHours <= 80) {
59
+ if (totalHours <= 15) {
55
60
  return XAxisPrecision.EVERY_HOUR;
56
61
  }
57
- if (totalHours <= 100) {
62
+ if (totalHours <= 30) {
58
63
  return XAxisPrecision.EVERY_TWO_HOURS;
59
64
  }
60
- if (totalHours <= 150) {
65
+ if (totalHours <= 45) {
61
66
  return XAxisPrecision.EVERY_THREE_HOURS;
62
67
  }
63
- if (totalHours <= 300) {
68
+ if (totalHours <= 90) {
64
69
  return XAxisPrecision.EVERY_SIX_HOURS;
65
70
  }
66
- if (totalHours <= 1200) {
71
+ if (totalHours <= 180) {
67
72
  return XAxisPrecision.EVERY_TWELVE_HOURS;
68
73
  }
69
- if (totalDays <= 100) {
74
+ if (totalDays <= 15) {
70
75
  return XAxisPrecision.EVERY_DAY;
71
76
  }
72
- if (totalDays <= 200) {
77
+ if (totalDays <= 30) {
73
78
  return XAxisPrecision.EVERY_TWO_DAYS;
74
79
  }
75
- if (totalWeeks <= 100) {
80
+ if (totalWeeks <= 15) {
76
81
  return XAxisPrecision.EVERY_WEEK;
77
82
  }
78
- if (totalWeeks <= 200) {
83
+ if (totalWeeks <= 30) {
79
84
  return XAxisPrecision.EVERY_TWO_WEEKS;
80
85
  }
81
- if (totalMonths <= 100) {
86
+ if (totalMonths <= 15) {
82
87
  return XAxisPrecision.EVERY_MONTH;
83
88
  }
84
- if (totalMonths <= 200) {
89
+ if (totalMonths <= 30) {
85
90
  return XAxisPrecision.EVERY_TWO_MONTHS;
86
91
  }
87
- if (totalMonths <= 300) {
92
+ if (totalMonths <= 45) {
88
93
  return XAxisPrecision.EVERY_THREE_MONTHS;
89
94
  }
90
- if (totalMonths <= 600) {
95
+ if (totalMonths <= 90) {
91
96
  return XAxisPrecision.EVERY_SIX_MONTHS;
92
97
  }
93
98
  return XAxisPrecision.EVERY_YEAR;
@@ -1,5 +1,8 @@
1
1
  import React, { FunctionComponent, ReactElement } from "react";
2
2
  import ExpandableText from "../ExpandableText/ExpandableText";
3
+ import Table from "../Table/Table";
4
+ import FieldType from "../Types/FieldType";
5
+ import SortOrder from "../../../Types/BaseDatabase/SortOrder";
3
6
 
4
7
  export interface Condition {
5
8
  type: string;
@@ -23,16 +26,6 @@ const defaultNegativeTypes: Array<string> = [
23
26
  "NetworkUnavailable",
24
27
  ];
25
28
 
26
- function isConditionBad(
27
- condition: Condition,
28
- negativeTypes: Array<string>,
29
- ): boolean {
30
- if (negativeTypes.includes(condition.type)) {
31
- return condition.status === "True";
32
- }
33
- return condition.status === "False";
34
- }
35
-
36
29
  function getStatusStyle(
37
30
  condition: Condition,
38
31
  negativeTypes: Array<string>,
@@ -83,65 +76,91 @@ const ConditionsTable: FunctionComponent<ComponentProps> = (
83
76
  const negativeTypes: Array<string> =
84
77
  props.negativeTypes || defaultNegativeTypes;
85
78
 
86
- if (props.conditions.length === 0) {
87
- return (
88
- <div className="text-gray-500 text-sm p-4">No conditions available.</div>
89
- );
90
- }
91
-
92
79
  return (
93
- <div className={`overflow-x-auto ${props.className || ""}`}>
94
- <table className="min-w-full divide-y divide-gray-200">
95
- <thead className="bg-gray-50">
96
- <tr>
97
- <th className="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase">
98
- Type
99
- </th>
100
- <th className="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase">
101
- Status
102
- </th>
103
- <th className="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase">
104
- Reason
105
- </th>
106
- <th className="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase">
107
- Message
108
- </th>
109
- <th className="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase">
110
- Last Transition
111
- </th>
112
- </tr>
113
- </thead>
114
- <tbody className="bg-white divide-y divide-gray-200">
115
- {props.conditions.map((condition: Condition, index: number) => {
116
- const isBad: boolean = isConditionBad(condition, negativeTypes);
117
- return (
118
- <tr key={index} className={isBad ? "bg-red-50/50" : ""}>
119
- <td className="px-4 py-3 whitespace-nowrap text-sm font-medium text-gray-900">
80
+ <div className={props.className || ""}>
81
+ <Table<Condition>
82
+ id="conditions-table"
83
+ data={props.conditions}
84
+ singularLabel="Condition"
85
+ pluralLabel="Conditions"
86
+ isLoading={false}
87
+ error=""
88
+ currentPageNumber={1}
89
+ totalItemsCount={props.conditions.length}
90
+ itemsOnPage={props.conditions.length}
91
+ disablePagination={true}
92
+ noItemsMessage="No conditions available."
93
+ onNavigateToPage={() => {}}
94
+ sortBy={null}
95
+ sortOrder={SortOrder.Ascending}
96
+ onSortChanged={() => {}}
97
+ columns={[
98
+ {
99
+ title: "Type",
100
+ type: FieldType.Element,
101
+ key: "type",
102
+ disableSort: true,
103
+ getElement: (condition: Condition): ReactElement => {
104
+ return (
105
+ <span className="font-medium text-gray-900">
120
106
  {condition.type}
121
- </td>
122
- <td className="px-4 py-3 whitespace-nowrap text-sm">
123
- <span
124
- className={`inline-flex px-2 py-0.5 text-xs font-semibold rounded-full ${getStatusStyle(condition, negativeTypes)}`}
125
- >
126
- {condition.status}
127
- </span>
128
- </td>
129
- <td className="px-4 py-3 whitespace-nowrap text-sm text-gray-600">
130
- {condition.reason || "-"}
131
- </td>
132
- <td className="px-4 py-3 text-sm max-w-md">
133
- <ExpandableText text={condition.message || "-"} />
134
- </td>
135
- <td className="px-4 py-3 whitespace-nowrap text-sm text-gray-500">
136
- <span title={condition.lastTransitionTime || ""}>
137
- {formatRelativeTime(condition.lastTransitionTime || "")}
138
- </span>
139
- </td>
140
- </tr>
141
- );
142
- })}
143
- </tbody>
144
- </table>
107
+ </span>
108
+ );
109
+ },
110
+ },
111
+ {
112
+ title: "Status",
113
+ type: FieldType.Element,
114
+ key: "status",
115
+ disableSort: true,
116
+ getElement: (condition: Condition): ReactElement => {
117
+ return (
118
+ <span
119
+ className={`inline-flex px-2 py-0.5 text-xs font-semibold rounded-full ${getStatusStyle(condition, negativeTypes)}`}
120
+ >
121
+ {condition.status}
122
+ </span>
123
+ );
124
+ },
125
+ },
126
+ {
127
+ title: "Reason",
128
+ type: FieldType.Element,
129
+ key: "reason",
130
+ disableSort: true,
131
+ getElement: (condition: Condition): ReactElement => {
132
+ return (
133
+ <span className="text-gray-600">{condition.reason || "-"}</span>
134
+ );
135
+ },
136
+ },
137
+ {
138
+ title: "Message",
139
+ type: FieldType.Element,
140
+ key: "message",
141
+ disableSort: true,
142
+ getElement: (condition: Condition): ReactElement => {
143
+ return <ExpandableText text={condition.message || "-"} />;
144
+ },
145
+ },
146
+ {
147
+ title: "Last Transition",
148
+ type: FieldType.Element,
149
+ key: "lastTransitionTime",
150
+ disableSort: true,
151
+ getElement: (condition: Condition): ReactElement => {
152
+ return (
153
+ <span
154
+ className="text-gray-500"
155
+ title={condition.lastTransitionTime || ""}
156
+ >
157
+ {formatRelativeTime(condition.lastTransitionTime || "")}
158
+ </span>
159
+ );
160
+ },
161
+ },
162
+ ]}
163
+ />
145
164
  </div>
146
165
  );
147
166
  };
@@ -1,4 +1,7 @@
1
1
  import Dictionary from "../../../Types/Dictionary";
2
+ import Table from "../Table/Table";
3
+ import FieldType from "../Types/FieldType";
4
+ import SortOrder from "../../../Types/BaseDatabase/SortOrder";
2
5
  import React, {
3
6
  FunctionComponent,
4
7
  ReactElement,
@@ -47,36 +50,53 @@ const DictionaryOfStringsViewer: FunctionComponent<ComponentProps> = (
47
50
  }
48
51
 
49
52
  return (
50
- <div className="overflow-x-auto">
51
- <table className="min-w-full divide-y divide-gray-200">
52
- <thead className="bg-gray-50">
53
- <tr>
54
- <th className="px-4 py-2.5 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
55
- Key
56
- </th>
57
- <th className="px-4 py-2.5 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
58
- Value
59
- </th>
60
- </tr>
61
- </thead>
62
- <tbody className="bg-white divide-y divide-gray-100">
63
- {data.map((item: Item, index: number) => {
53
+ <Table<Item>
54
+ id="dictionary-viewer-table"
55
+ data={data}
56
+ singularLabel="Item"
57
+ pluralLabel="Items"
58
+ isLoading={false}
59
+ error=""
60
+ currentPageNumber={1}
61
+ totalItemsCount={data.length}
62
+ itemsOnPage={data.length}
63
+ disablePagination={true}
64
+ noItemsMessage="No items to display."
65
+ onNavigateToPage={() => {}}
66
+ sortBy={null}
67
+ sortOrder={SortOrder.Ascending}
68
+ onSortChanged={() => {}}
69
+ columns={[
70
+ {
71
+ title: "Key",
72
+ type: FieldType.Element,
73
+ key: "key",
74
+ disableSort: true,
75
+ getElement: (item: Item): ReactElement => {
76
+ return (
77
+ <span className="font-mono font-medium text-gray-900">
78
+ {item.key}
79
+ </span>
80
+ );
81
+ },
82
+ },
83
+ {
84
+ title: "Value",
85
+ type: FieldType.Element,
86
+ key: "value",
87
+ disableSort: true,
88
+ getElement: (item: Item): ReactElement => {
64
89
  return (
65
- <tr key={index} className="hover:bg-gray-50/50">
66
- <td className="px-4 py-2 text-sm font-mono font-medium text-indigo-700 whitespace-nowrap">
67
- {item.key}
68
- </td>
69
- <td className="px-4 py-2 text-sm font-mono text-gray-600 break-all">
70
- {item.value || (
71
- <span className="text-gray-400 italic">empty</span>
72
- )}
73
- </td>
74
- </tr>
90
+ <span className="font-mono text-gray-600">
91
+ {item.value || (
92
+ <span className="text-gray-400 italic">empty</span>
93
+ )}
94
+ </span>
75
95
  );
76
- })}
77
- </tbody>
78
- </table>
79
- </div>
96
+ },
97
+ },
98
+ ]}
99
+ />
80
100
  );
81
101
  };
82
102
 
@@ -66,12 +66,18 @@ const FiltersForm: FiltersFormFunction = <T extends GenericObject>(
66
66
  <div className="pt-3 pb-5">
67
67
  <div className="space-y-5">
68
68
  {props.showFilter &&
69
- !props.isFilterLoading &&
70
- !props.filterError &&
71
69
  props.filters &&
72
70
  props.filters
73
71
  .filter((filter: Filter<T>) => {
74
- return !filter.isAdvancedFilter || showMoreFilters;
72
+ if (filter.isAdvancedFilter) {
73
+ // Hide advanced filters if not toggled on, or if they are still loading/errored
74
+ return (
75
+ showMoreFilters &&
76
+ !props.isFilterLoading &&
77
+ !props.filterError
78
+ );
79
+ }
80
+ return true;
75
81
  })
76
82
  .map((filter: Filter<T>, i: number) => {
77
83
  return (
@@ -127,6 +133,16 @@ const FiltersForm: FiltersFormFunction = <T extends GenericObject>(
127
133
  );
128
134
  })}
129
135
  </div>
136
+ {props.showFilter && props.isFilterLoading && !props.filterError && (
137
+ <ComponentLoader />
138
+ )}
139
+
140
+ {props.showFilter && props.filterError && (
141
+ <ErrorMessage
142
+ message={props.filterError}
143
+ onRefreshClick={props.onFilterRefreshClick}
144
+ />
145
+ )}
130
146
  {showAdvancedFilterButton && (
131
147
  <Button
132
148
  className="-ml-3 mt-1"
@@ -146,16 +162,6 @@ const FiltersForm: FiltersFormFunction = <T extends GenericObject>(
146
162
  />
147
163
  )}
148
164
  </div>
149
- {props.showFilter && props.isFilterLoading && !props.filterError && (
150
- <ComponentLoader />
151
- )}
152
-
153
- {props.showFilter && props.filterError && (
154
- <ErrorMessage
155
- message={props.filterError}
156
- onRefreshClick={props.onFilterRefreshClick}
157
- />
158
- )}
159
165
  </div>
160
166
  );
161
167
  };
@@ -6,6 +6,7 @@ export interface ComponentProps {
6
6
  value: string | ReactElement;
7
7
  className?: string;
8
8
  textClassName?: string;
9
+ onClick?: (() => void) | undefined;
9
10
  }
10
11
 
11
12
  const InfoCard: FunctionComponent<ComponentProps> = (
@@ -13,7 +14,8 @@ const InfoCard: FunctionComponent<ComponentProps> = (
13
14
  ): ReactElement => {
14
15
  return (
15
16
  <div
16
- className={`rounded-xl bg-white border border-gray-200 shadow-sm hover:shadow-md transition-shadow duration-200 p-5 ${props.className || ""}`}
17
+ onClick={props.onClick}
18
+ className={`rounded-xl bg-white border border-gray-200 shadow-sm hover:shadow-md transition-shadow duration-200 p-5 ${props.onClick ? "cursor-pointer" : ""} ${props.className || ""}`}
17
19
  >
18
20
  <div className="mb-2">
19
21
  <FieldLabelElement title={props.title} />
@@ -87,6 +87,7 @@ export interface ComponentProps {
87
87
  onFacetExclude?: (facetKey: string, value: string) => void;
88
88
  showFacetSidebar?: boolean;
89
89
  activeFilters?: Array<ActiveFilter> | undefined;
90
+ baseActiveFilters?: Array<ActiveFilter> | undefined;
90
91
  onRemoveFilter?: ((facetKey: string, value: string) => void) | undefined;
91
92
  onClearAllFilters?: (() => void) | undefined;
92
93
  valueSuggestions?: Record<string, Array<string>> | undefined;
@@ -829,11 +830,15 @@ const LogsViewer: FunctionComponent<ComponentProps> = (
829
830
  </div>
830
831
  )}
831
832
 
832
- {/* Active filter chips */}
833
- {enrichedActiveFilters.length > 0 && props.onRemoveFilter && (
833
+ {/* Active filter chips (read-only base filters + user-applied filters) */}
834
+ {((props.baseActiveFilters && props.baseActiveFilters.length > 0) ||
835
+ (enrichedActiveFilters.length > 0 && props.onRemoveFilter)) && (
834
836
  <ActiveFilterChips
835
- filters={enrichedActiveFilters}
836
- onRemove={props.onRemoveFilter}
837
+ filters={[
838
+ ...(props.baseActiveFilters || []),
839
+ ...enrichedActiveFilters,
840
+ ]}
841
+ onRemove={props.onRemoveFilter || (() => {})}
837
842
  onClearAll={props.onClearAllFilters || (() => {})}
838
843
  />
839
844
  )}
@@ -16,9 +16,36 @@ const ActiveFilterChips: FunctionComponent<ActiveFilterChipsProps> = (
16
16
  return null;
17
17
  }
18
18
 
19
+ const readOnlyFilters: Array<ActiveFilter> = props.filters.filter(
20
+ (f: ActiveFilter) => {
21
+ return f.readOnly;
22
+ },
23
+ );
24
+ const removableFilters: Array<ActiveFilter> = props.filters.filter(
25
+ (f: ActiveFilter) => {
26
+ return !f.readOnly;
27
+ },
28
+ );
29
+
19
30
  return (
20
31
  <div className="flex flex-wrap items-center gap-1.5 px-0.5">
21
- {props.filters.map((filter: ActiveFilter) => {
32
+ {readOnlyFilters.map((filter: ActiveFilter) => {
33
+ const chipKey: string = `readonly:${filter.facetKey}:${filter.value}`;
34
+ return (
35
+ <span
36
+ key={chipKey}
37
+ className="inline-flex items-center gap-1 rounded-md border border-gray-300 bg-gray-100 py-0.5 pl-2 pr-2 text-xs text-gray-700"
38
+ title={`${filter.displayKey}: ${filter.displayValue} (applied filter)`}
39
+ >
40
+ <Icon icon={IconProp.Lock} className="h-2.5 w-2.5 text-gray-400" />
41
+ <span className="font-medium text-gray-500">
42
+ {filter.displayKey}:
43
+ </span>
44
+ <span>{filter.displayValue}</span>
45
+ </span>
46
+ );
47
+ })}
48
+ {removableFilters.map((filter: ActiveFilter) => {
22
49
  const chipKey: string = `${filter.facetKey}:${filter.value}`;
23
50
  return (
24
51
  <span
@@ -42,7 +69,7 @@ const ActiveFilterChips: FunctionComponent<ActiveFilterChipsProps> = (
42
69
  </span>
43
70
  );
44
71
  })}
45
- {props.filters.length > 1 && (
72
+ {removableFilters.length > 1 && (
46
73
  <button
47
74
  type="button"
48
75
  className="rounded px-1.5 py-0.5 text-[11px] font-medium text-gray-400 transition-colors hover:bg-gray-100 hover:text-gray-600"
@@ -22,6 +22,7 @@ export interface ActiveFilter {
22
22
  value: string;
23
23
  displayKey: string;
24
24
  displayValue: string;
25
+ readOnly?: boolean | undefined;
25
26
  }
26
27
 
27
28
  export type LogsViewMode = "list" | "analytics";
@@ -8,7 +8,10 @@ import ComponentMetadata, {
8
8
  import Components, { Categories } from "../../../Types/Workflow/Components";
9
9
  import BaseModelComponentFactory from "../../../Types/Workflow/Components/BaseModel";
10
10
  import Entities from "../../../Models/DatabaseModels/Index";
11
- import { ConditionOperator } from "../../../Types/Workflow/Components/Condition";
11
+ import {
12
+ ConditionOperator,
13
+ ConditionValueType,
14
+ } from "../../../Types/Workflow/Components/Condition";
12
15
 
13
16
  type LoadComponentsAndCategoriesFunction = () => {
14
17
  components: Array<ComponentMetadata>;
@@ -230,6 +233,34 @@ export const componentInputTypeToFormFieldType: ComponentInputTypeToFormFieldTyp
230
233
  };
231
234
  }
232
235
 
236
+ if (componentInputType === ComponentInputType.ValueType) {
237
+ return {
238
+ fieldType: FormFieldSchemaType.Dropdown,
239
+ dropdownOptions: [
240
+ {
241
+ label: "Text",
242
+ value: ConditionValueType.Text,
243
+ },
244
+ {
245
+ label: "Boolean",
246
+ value: ConditionValueType.Boolean,
247
+ },
248
+ {
249
+ label: "Number",
250
+ value: ConditionValueType.Number,
251
+ },
252
+ {
253
+ label: "Null",
254
+ value: ConditionValueType.Null,
255
+ },
256
+ {
257
+ label: "Undefined",
258
+ value: ConditionValueType.Undefined,
259
+ },
260
+ ],
261
+ };
262
+ }
263
+
233
264
  if (componentInputType === ComponentInputType.Date) {
234
265
  return {
235
266
  fieldType: FormFieldSchemaType.Date,