@oneuptime/common 8.0.5414 → 8.0.5438

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 (81) hide show
  1. package/Models/AnalyticsModels/AnalyticsBaseModel/AnalyticsBaseModel.ts +22 -0
  2. package/Models/AnalyticsModels/ExceptionInstance.ts +341 -323
  3. package/Models/AnalyticsModels/Log.ts +278 -231
  4. package/Models/AnalyticsModels/Metric.ts +504 -446
  5. package/Models/AnalyticsModels/MonitorLog.ts +99 -93
  6. package/Models/AnalyticsModels/Span.ts +473 -417
  7. package/Server/Services/AlertService.ts +12 -0
  8. package/Server/Services/IncidentService.ts +12 -0
  9. package/Server/Services/OpenTelemetryIngestService.ts +4 -0
  10. package/Server/Services/TelemetryAttributeService.ts +21 -4
  11. package/Server/Utils/Monitor/MonitorResource.ts +24 -0
  12. package/Server/Utils/Telemetry/Telemetry.ts +13 -0
  13. package/Types/AnalyticsDatabase/MaterializedView.ts +4 -0
  14. package/Types/AnalyticsDatabase/Projection.ts +4 -0
  15. package/Types/Date.ts +108 -0
  16. package/UI/Components/AutocompleteTextInput/AutocompleteTextInput.tsx +250 -0
  17. package/UI/Components/Dictionary/Dictionary.tsx +53 -66
  18. package/UI/Components/Filters/JSONFilter.tsx +2 -2
  19. package/UI/Components/Forms/Fields/FormField.tsx +2 -2
  20. package/UI/Components/GanttChart/Bar/Index.tsx +13 -0
  21. package/UI/Components/GanttChart/Index.tsx +7 -1
  22. package/UI/Components/GanttChart/Row/Index.tsx +1 -0
  23. package/UI/Components/GanttChart/Row/Row.tsx +101 -10
  24. package/UI/Components/GanttChart/Row/RowLabel.tsx +7 -2
  25. package/UI/Components/GanttChart/Rows.tsx +7 -1
  26. package/UI/Components/LogsViewer/LogItem.tsx +149 -10
  27. package/UI/Components/LogsViewer/LogsViewer.tsx +25 -1
  28. package/build/dist/Models/AnalyticsModels/AnalyticsBaseModel/AnalyticsBaseModel.js +16 -0
  29. package/build/dist/Models/AnalyticsModels/AnalyticsBaseModel/AnalyticsBaseModel.js.map +1 -1
  30. package/build/dist/Models/AnalyticsModels/ExceptionInstance.js +325 -310
  31. package/build/dist/Models/AnalyticsModels/ExceptionInstance.js.map +1 -1
  32. package/build/dist/Models/AnalyticsModels/Log.js +263 -222
  33. package/build/dist/Models/AnalyticsModels/Log.js.map +1 -1
  34. package/build/dist/Models/AnalyticsModels/Metric.js +477 -427
  35. package/build/dist/Models/AnalyticsModels/Metric.js.map +1 -1
  36. package/build/dist/Models/AnalyticsModels/MonitorLog.js +95 -90
  37. package/build/dist/Models/AnalyticsModels/MonitorLog.js.map +1 -1
  38. package/build/dist/Models/AnalyticsModels/Span.js +449 -400
  39. package/build/dist/Models/AnalyticsModels/Span.js.map +1 -1
  40. package/build/dist/Server/Services/AlertService.js +4 -0
  41. package/build/dist/Server/Services/AlertService.js.map +1 -1
  42. package/build/dist/Server/Services/IncidentService.js +4 -0
  43. package/build/dist/Server/Services/IncidentService.js.map +1 -1
  44. package/build/dist/Server/Services/OpenTelemetryIngestService.js +1 -0
  45. package/build/dist/Server/Services/OpenTelemetryIngestService.js.map +1 -1
  46. package/build/dist/Server/Services/TelemetryAttributeService.js +19 -4
  47. package/build/dist/Server/Services/TelemetryAttributeService.js.map +1 -1
  48. package/build/dist/Server/Utils/Monitor/MonitorResource.js +12 -0
  49. package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
  50. package/build/dist/Server/Utils/Telemetry/Telemetry.js +6 -0
  51. package/build/dist/Server/Utils/Telemetry/Telemetry.js.map +1 -1
  52. package/build/dist/Types/AnalyticsDatabase/MaterializedView.js +2 -0
  53. package/build/dist/Types/AnalyticsDatabase/MaterializedView.js.map +1 -0
  54. package/build/dist/Types/AnalyticsDatabase/Projection.js +2 -0
  55. package/build/dist/Types/AnalyticsDatabase/Projection.js.map +1 -0
  56. package/build/dist/Types/Date.js +82 -0
  57. package/build/dist/Types/Date.js.map +1 -1
  58. package/build/dist/UI/Components/AutocompleteTextInput/AutocompleteTextInput.js +143 -0
  59. package/build/dist/UI/Components/AutocompleteTextInput/AutocompleteTextInput.js.map +1 -0
  60. package/build/dist/UI/Components/Dictionary/Dictionary.js +25 -36
  61. package/build/dist/UI/Components/Dictionary/Dictionary.js.map +1 -1
  62. package/build/dist/UI/Components/Filters/JSONFilter.js +2 -2
  63. package/build/dist/UI/Components/Filters/JSONFilter.js.map +1 -1
  64. package/build/dist/UI/Components/Forms/Fields/FormField.js +2 -2
  65. package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
  66. package/build/dist/UI/Components/GanttChart/Bar/Index.js +10 -0
  67. package/build/dist/UI/Components/GanttChart/Bar/Index.js.map +1 -1
  68. package/build/dist/UI/Components/GanttChart/Index.js +6 -2
  69. package/build/dist/UI/Components/GanttChart/Index.js.map +1 -1
  70. package/build/dist/UI/Components/GanttChart/Row/Index.js.map +1 -1
  71. package/build/dist/UI/Components/GanttChart/Row/Row.js +62 -9
  72. package/build/dist/UI/Components/GanttChart/Row/Row.js.map +1 -1
  73. package/build/dist/UI/Components/GanttChart/Row/RowLabel.js +2 -2
  74. package/build/dist/UI/Components/GanttChart/Row/RowLabel.js.map +1 -1
  75. package/build/dist/UI/Components/GanttChart/Rows.js +5 -1
  76. package/build/dist/UI/Components/GanttChart/Rows.js.map +1 -1
  77. package/build/dist/UI/Components/LogsViewer/LogItem.js +73 -5
  78. package/build/dist/UI/Components/LogsViewer/LogItem.js.map +1 -1
  79. package/build/dist/UI/Components/LogsViewer/LogsViewer.js +7 -1
  80. package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
  81. package/package.json +1 -1
@@ -1,6 +1,5 @@
1
1
  import Button, { ButtonSize, ButtonStyleType } from "../Button/Button";
2
2
  import Dropdown, { DropdownOption, DropdownValue } from "../Dropdown/Dropdown";
3
- import Icon, { SizeProp } from "../Icon/Icon";
4
3
  import Input, { InputType } from "../Input/Input";
5
4
  import Dictionary from "../../../Types/Dictionary";
6
5
  import IconProp from "../../../Types/Icon/IconProp";
@@ -10,8 +9,8 @@ import React, {
10
9
  useEffect,
11
10
  useState,
12
11
  } from "react";
13
- import NumberUtil from "../../../Utils/Number";
14
- import BooleanUtil from "../../../Utils/Boolean";
12
+ import AutocompleteTextInput from "../AutocompleteTextInput/AutocompleteTextInput";
13
+ import FieldLabelElement from "../Forms/Fields/FieldLabel";
15
14
 
16
15
  export enum ValueType {
17
16
  Text = "Text",
@@ -28,7 +27,6 @@ export interface ComponentProps {
28
27
  valuePlaceholder?: string;
29
28
  addButtonSuffix?: string | undefined;
30
29
  valueTypes?: Array<ValueType>; // by default it'll be Text
31
- autoConvertValueTypes?: boolean | undefined;
32
30
  keys?: Array<string> | undefined;
33
31
  }
34
32
 
@@ -83,9 +81,11 @@ const DictionaryForm: FunctionComponent<ComponentProps> = (
83
81
  }
84
82
  }
85
83
 
84
+ const value: string | number | boolean | undefined = json[key];
85
+
86
86
  return {
87
87
  key: key,
88
- value: json[key] || "",
88
+ value: value === undefined || value === null ? "" : value,
89
89
  type: valueType,
90
90
  };
91
91
  });
@@ -109,15 +109,6 @@ const DictionaryForm: FunctionComponent<ComponentProps> = (
109
109
  const onDataChange: OnDataChangeFunction = (data: Array<Item>): void => {
110
110
  const result: Dictionary<string | number | boolean> = {};
111
111
  data.forEach((item: Item) => {
112
- if (props.autoConvertValueTypes) {
113
- if (NumberUtil.canBeConvertedToNumber(item.value)) {
114
- item.value = Number(item.value);
115
- }
116
-
117
- if (BooleanUtil.canBeConvertedToBoolean(item.value)) {
118
- item.value = Boolean(item.value);
119
- }
120
- }
121
112
  result[item.key] = item.value;
122
113
  });
123
114
  if (props.onChange) {
@@ -135,68 +126,55 @@ const DictionaryForm: FunctionComponent<ComponentProps> = (
135
126
  value: "False",
136
127
  };
137
128
 
138
- const dropdownOptionsForKeys: Array<DropdownOption> = props.keys
139
- ? props.keys.map((key: string) => {
140
- return {
141
- label: key,
142
- value: key,
143
- };
144
- })
145
- : [];
129
+ const getDefaultValueForType: (
130
+ type: ValueType,
131
+ ) => string | number | boolean = (type: ValueType) => {
132
+ if (type === ValueType.Boolean) {
133
+ return true;
134
+ }
135
+
136
+ return "";
137
+ };
146
138
 
147
139
  return (
148
140
  <div>
149
141
  <div>
150
142
  {data.map((item: Item, index: number) => {
151
143
  return (
152
- <div key={index} className="flex">
144
+ <div key={index} className="flex items-start mb-4 last:mb-0">
153
145
  <div className="mr-1 w-1/2">
154
- {!props.keys && (
155
- <Input
156
- value={item.key}
157
- placeholder={props.keyPlaceholder}
158
- onChange={(value: string) => {
159
- const newData: Array<Item> = [...data];
160
- newData[index]!.key = value;
161
- setData(newData);
162
- onDataChange(newData);
163
- }}
146
+ <div className="mb-1">
147
+ <FieldLabelElement
148
+ title="Key"
149
+ required={true}
150
+ hideOptionalLabel={true}
164
151
  />
165
- )}
166
-
167
- {props.keys && (
168
- <Dropdown
169
- value={dropdownOptionsForKeys.find(
170
- (option: DropdownOption) => {
171
- return option.value === item.key;
172
- },
173
- )}
174
- options={dropdownOptionsForKeys}
175
- isMultiSelect={false}
176
- onChange={(
177
- selectedOption:
178
- | DropdownValue
179
- | Array<DropdownValue>
180
- | null,
181
- ) => {
182
- const newData: Array<Item> = [...data];
183
- newData[index]!.key = selectedOption as string;
184
- setData(newData);
185
- onDataChange(newData);
186
- }}
187
- />
188
- )}
152
+ </div>
153
+ <AutocompleteTextInput
154
+ value={item.key}
155
+ placeholder={props.keyPlaceholder}
156
+ suggestions={props.keys}
157
+ onChange={(value: string) => {
158
+ const newData: Array<Item> = [...data];
159
+ newData[index]!.key = value;
160
+ setData(newData);
161
+ onDataChange(newData);
162
+ }}
163
+ />
189
164
  </div>
190
165
 
191
- <div className="mr-1 ml-1 mt-auto mb-auto">
192
- <Icon
193
- className="h-3 w-3"
194
- icon={IconProp.Equals}
195
- size={SizeProp.Small}
196
- />
166
+ <div className="mr-1 ml-1 flex items-center justify-center pt-8">
167
+ <span className="text-slate-500 text-2xl leading-none">=</span>
197
168
  </div>
198
169
  {valueTypes.length > 1 && (
199
170
  <div className="ml-1 w-1/2">
171
+ <div className="mb-1">
172
+ <FieldLabelElement
173
+ title="Type"
174
+ hideOptionalLabel={true}
175
+ required={true}
176
+ />
177
+ </div>
200
178
  <Dropdown
201
179
  value={dropdownOptionsForValueTypes.find(
202
180
  (dropdownOption: DropdownOption) => {
@@ -212,8 +190,10 @@ const DictionaryForm: FunctionComponent<ComponentProps> = (
212
190
  | null,
213
191
  ) => {
214
192
  const newData: Array<Item> = [...data];
215
- newData[index]!.type =
193
+ const newType: ValueType =
216
194
  (selectedOption as ValueType) || valueTypes[0];
195
+ newData[index]!.type = newType;
196
+ newData[index]!.value = getDefaultValueForType(newType);
217
197
  setData(newData);
218
198
  onDataChange(newData);
219
199
  }}
@@ -221,6 +201,13 @@ const DictionaryForm: FunctionComponent<ComponentProps> = (
221
201
  </div>
222
202
  )}
223
203
  <div className="ml-1 w-1/2">
204
+ <div className="mb-1">
205
+ <FieldLabelElement
206
+ title="Value"
207
+ hideOptionalLabel={true}
208
+ required={true}
209
+ />
210
+ </div>
224
211
  {item.type === ValueType.Text && (
225
212
  <Input
226
213
  value={item.value.toString()}
@@ -284,7 +271,7 @@ const DictionaryForm: FunctionComponent<ComponentProps> = (
284
271
  />
285
272
  )}
286
273
  </div>
287
- <div className="ml-1 mt-1">
274
+ <div className="ml-1 flex flex-col justify-end pt-6">
288
275
  <Button
289
276
  dataTestId={`delete-${item.key}`}
290
277
  title="Delete"
@@ -311,7 +298,7 @@ const DictionaryForm: FunctionComponent<ComponentProps> = (
311
298
  ...data,
312
299
  {
313
300
  key: "",
314
- value: "",
301
+ value: getDefaultValueForType(valueTypes[0] as ValueType),
315
302
  type: valueTypes[0] as ValueType,
316
303
  },
317
304
  ]);
@@ -1,5 +1,5 @@
1
1
  import { FindWhereProperty } from "../../../Types/BaseDatabase/Query";
2
- import DictionaryForm from "../Dictionary/Dictionary";
2
+ import DictionaryForm, { ValueType } from "../Dictionary/Dictionary";
3
3
  import FieldType from "../Types/FieldType";
4
4
  import Filter from "./Types/Filter";
5
5
  import FilterData from "./Types/FilterData";
@@ -31,7 +31,7 @@ const JSONFilter: JSONFilterFunction = <T extends GenericObject>(
31
31
  addButtonSuffix={filter.title}
32
32
  keyPlaceholder={"Key"}
33
33
  valuePlaceholder={"Value"}
34
- autoConvertValueTypes={true}
34
+ valueTypes={[ValueType.Text, ValueType.Number, ValueType.Boolean]}
35
35
  initialValue={(filterData[filter.key] as Dictionary<string>) || {}}
36
36
  onChange={(value: Dictionary<string | number | boolean>) => {
37
37
  // if no keys in the dictionary, remove the filter
@@ -5,7 +5,7 @@ import CheckboxElement, {
5
5
  CategoryCheckboxValue,
6
6
  } from "../../Checkbox/Checkbox";
7
7
  import CodeEditor from "../../CodeEditor/CodeEditor";
8
- import DictionaryForm from "../../Dictionary/Dictionary";
8
+ import DictionaryForm, { ValueType } from "../../Dictionary/Dictionary";
9
9
  import Dropdown, { DropdownValue } from "../../Dropdown/Dropdown";
10
10
  import FilePicker from "../../FilePicker/FilePicker";
11
11
  import Input, { InputType } from "../../Input/Input";
@@ -386,7 +386,7 @@ const FormField: <T extends GenericObject>(
386
386
  addButtonSuffix={props.field.title}
387
387
  keyPlaceholder={"Key"}
388
388
  valuePlaceholder={"Value"}
389
- autoConvertValueTypes={true}
389
+ valueTypes={[ValueType.Text, ValueType.Number, ValueType.Boolean]}
390
390
  initialValue={
391
391
  props.currentValues &&
392
392
  (props.currentValues as any)[props.fieldName]
@@ -26,6 +26,7 @@ export interface ComponentProps {
26
26
  onSelect: (barId: string) => void;
27
27
  onDeselect: (barId: string) => void;
28
28
  isSelected?: boolean;
29
+ isHighlighted?: boolean;
29
30
  }
30
31
 
31
32
  const Bar: FunctionComponent<ComponentProps> = (
@@ -66,10 +67,18 @@ const Bar: FunctionComponent<ComponentProps> = (
66
67
  barOpacity = 0.5;
67
68
  }
68
69
 
70
+ if (props.isHighlighted) {
71
+ barOpacity = 1;
72
+ }
73
+
69
74
  if (isHovered) {
70
75
  barOpacity = 1;
71
76
  }
72
77
 
78
+ const highlightShadow: string | undefined = props.isHighlighted
79
+ ? "0 0 0 2px rgba(99, 102, 241, 0.8), 0 0 14px rgba(129, 140, 248, 0.55)"
80
+ : undefined;
81
+
73
82
  return (
74
83
  // rectangle div with curved corners and text inside in tailwindcss
75
84
  <div
@@ -84,6 +93,8 @@ const Bar: FunctionComponent<ComponentProps> = (
84
93
  width: `${barWidth}px`,
85
94
  backgroundColor: `${props.bar.barColor.toString()}`,
86
95
  opacity: barOpacity,
96
+ boxShadow: highlightShadow,
97
+ zIndex: props.isHighlighted ? 30 : undefined,
87
98
  }}
88
99
  onMouseEnter={handleMouseEnter}
89
100
  onMouseLeave={handleMouseLeave}
@@ -107,6 +118,8 @@ const Bar: FunctionComponent<ComponentProps> = (
107
118
  style={{
108
119
  marginLeft: `${barWidth + 15}px`,
109
120
  opacity: barOpacity,
121
+ fontWeight: props.isHighlighted ? 600 : undefined,
122
+ color: props.isHighlighted ? "#4338ca" : undefined,
110
123
  }}
111
124
  >
112
125
  <BarLabel label={props.bar.label} />
@@ -11,6 +11,7 @@ export interface GanttChartProps {
11
11
  onBarSelectChange: (barIds: string[]) => void;
12
12
  multiSelect?: boolean | undefined;
13
13
  selectedBarIds: string[];
14
+ highlightBarIds?: string[];
14
15
  }
15
16
 
16
17
  export interface ComponentProps {
@@ -71,7 +72,12 @@ const GanttChart: FunctionComponent<ComponentProps> = (
71
72
  chartTimelineStart={props.chart.timeline.start}
72
73
  rows={props.chart.rows}
73
74
  selectedBarIds={props.chart.selectedBarIds}
74
- multiSelect={props.chart.multiSelect}
75
+ {...(props.chart.multiSelect !== undefined
76
+ ? { multiSelect: props.chart.multiSelect }
77
+ : {})}
78
+ {...(props.chart.highlightBarIds
79
+ ? { highlightBarIds: props.chart.highlightBarIds }
80
+ : {})}
75
81
  onBarSelectChange={(barIds: string[]) => {
76
82
  props.chart.onBarSelectChange(barIds);
77
83
  }}
@@ -10,6 +10,7 @@ export interface ComponentProps {
10
10
  onBarSelectChange: (barIds: string[]) => void;
11
11
  children?: ReactElement;
12
12
  multiSelect?: boolean | undefined;
13
+ highlightBarIds?: string[];
13
14
  }
14
15
 
15
16
  const RowIndex: FunctionComponent<ComponentProps> = (
@@ -2,7 +2,13 @@ import Icon from "../../Icon/Icon";
2
2
  import Bar, { GanttChartBar } from "../Bar/Index";
3
3
  import RowLabel from "./RowLabel";
4
4
  import IconProp from "../../../../Types/Icon/IconProp";
5
- import React, { FunctionComponent, ReactElement, useState } from "react";
5
+ import React, {
6
+ FunctionComponent,
7
+ ReactElement,
8
+ useEffect,
9
+ useMemo,
10
+ useState,
11
+ } from "react";
6
12
 
7
13
  export interface GanttChartRow {
8
14
  rowInfo: {
@@ -23,8 +29,26 @@ export interface ComponentProps {
23
29
  onBarSelectChange: (barIds: string[]) => void;
24
30
  level: number;
25
31
  multiSelect?: boolean | undefined;
32
+ highlightBarIds?: string[];
26
33
  }
27
34
 
35
+ const doesRowContainHighlight: (
36
+ row: GanttChartRow,
37
+ highlightSet: Set<string>,
38
+ ) => boolean = (row: GanttChartRow, highlightSet: Set<string>): boolean => {
39
+ if (
40
+ row.bars.some((bar: GanttChartBar) => {
41
+ return highlightSet.has(bar.id);
42
+ })
43
+ ) {
44
+ return true;
45
+ }
46
+
47
+ return row.childRows.some((childRow: GanttChartRow) => {
48
+ return doesRowContainHighlight(childRow, highlightSet);
49
+ });
50
+ };
51
+
28
52
  const Row: FunctionComponent<ComponentProps> = (
29
53
  props: ComponentProps,
30
54
  ): ReactElement => {
@@ -38,21 +62,61 @@ const Row: FunctionComponent<ComponentProps> = (
38
62
 
39
63
  const hasChildRows: boolean = row.childRows.length > 0;
40
64
 
41
- const shouldShowChildRows: boolean = level < 2;
65
+ const highlightSet: Set<string> | null = useMemo(() => {
66
+ if (!props.highlightBarIds || props.highlightBarIds.length === 0) {
67
+ return null;
68
+ }
69
+
70
+ return new Set(props.highlightBarIds);
71
+ }, [props.highlightBarIds]);
72
+
73
+ const hasHighlightedDescendant: boolean = useMemo(() => {
74
+ if (!highlightSet) {
75
+ return false;
76
+ }
77
+
78
+ return row.childRows.some((childRow: GanttChartRow) => {
79
+ return doesRowContainHighlight(childRow, highlightSet);
80
+ });
81
+ }, [highlightSet, row]);
82
+
83
+ const shouldShowChildRows: boolean = level < 2 || hasHighlightedDescendant;
42
84
 
43
85
  const [showChildRows, setShowChildRows] = useState(shouldShowChildRows);
44
86
 
45
- const paddingCount: number = level * 4;
87
+ useEffect(() => {
88
+ if (hasHighlightedDescendant && !showChildRows) {
89
+ setShowChildRows(true);
90
+ }
91
+ }, [hasHighlightedDescendant, showChildRows]);
92
+
93
+ const rowIsHighlighted: boolean = useMemo(() => {
94
+ if (!highlightSet) {
95
+ return false;
96
+ }
97
+
98
+ return doesRowContainHighlight(row, highlightSet);
99
+ }, [highlightSet, row]);
100
+
101
+ const labelPaddingLeft: string = `${level}rem`;
46
102
 
47
103
  return (
48
104
  // rectangle div with curved corners and text inside in tailwindcss
49
105
  <div>
50
106
  <div
51
- className={`flex w-full border-b-2 border-gray-200 border-l-2 border-l-gray-400 border-r-2 border-r-gray-400`}
107
+ className={`flex w-full border-b border-gray-200 border-l-2 border-l-gray-300 border-r-2 border-r-gray-300 transition-colors duration-150 ${rowIsHighlighted ? "bg-indigo-50/40 ring-1 ring-inset ring-indigo-200/70 shadow-sm" : ""}`}
108
+ data-span-highlighted={rowIsHighlighted ? "true" : undefined}
52
109
  >
53
110
  <div className="flex w-1/4 border-r-2 border-gray-300 overflow-hidden">
54
111
  <div
55
- className={`pl-${paddingCount} pt-2 pb-2 pr-2 flex overflow-hidden`}
112
+ className="flex overflow-hidden items-center gap-3 pr-3 pt-2 pb-2 w-full"
113
+ style={{
114
+ paddingLeft: labelPaddingLeft,
115
+ backgroundColor: rowIsHighlighted
116
+ ? "rgba(99, 102, 241, 0.08)"
117
+ : undefined,
118
+ borderRadius: rowIsHighlighted ? "0.5rem" : undefined,
119
+ }}
56
120
  >
57
121
  <div className="w-5 h-5 ml-3 mt-1">
58
122
  {hasChildRows && (
@@ -67,13 +131,31 @@ const Row: FunctionComponent<ComponentProps> = (
67
131
  />
68
132
  )}
69
133
  </div>
70
- <RowLabel
71
- title={row.rowInfo.title}
72
- description={row.rowInfo.description}
73
- />
134
+ <div className="flex-1 min-w-0">
135
+ <RowLabel
136
+ title={row.rowInfo.title}
137
+ description={row.rowInfo.description}
138
+ isHighlighted={rowIsHighlighted}
139
+ />
140
+ </div>
141
+ {rowIsHighlighted ? (
142
+ <span className="hidden lg:inline-flex items-center gap-1 rounded-full bg-indigo-100 text-indigo-600 text-[10px] font-semibold uppercase tracking-wide px-2 py-0.5 shadow-sm">
143
+ <span className="inline-block h-1.5 w-1.5 rounded-full bg-indigo-500" />
144
+ Matched
145
+ </span>
146
+ ) : (
147
+ <></>
148
+ )}
74
149
  </div>
75
150
  </div>
76
- <div className="flex w-3/4">
151
+ <div
152
+ className={`flex w-3/4 relative overflow-hidden transition-all duration-150 ${rowIsHighlighted ? "bg-indigo-500/5" : ""}`}
153
+ style={{
154
+ boxShadow: rowIsHighlighted
155
+ ? "inset 0 0 0 1px rgba(129, 140, 248, 0.25)"
156
+ : undefined,
157
+ }}
158
+ >
77
159
  {row.bars.map((bar: GanttChartBar, i: number) => {
78
160
  return (
79
161
  <Bar
@@ -84,6 +166,9 @@ const Row: FunctionComponent<ComponentProps> = (
84
166
  timelineWidth={props.timelineWidth}
85
167
  areOtherBarsSelected={props.selectedBarIds.length > 0}
86
168
  isSelected={props.selectedBarIds.includes(bar.id)}
169
+ isHighlighted={Boolean(
170
+ highlightSet && highlightSet.has(bar.id),
171
+ )}
87
172
  onSelect={(barId: string) => {
88
173
  // check if the bar is already selected
89
174
  if (props.selectedBarIds.includes(barId)) {
@@ -127,6 +212,12 @@ const Row: FunctionComponent<ComponentProps> = (
127
212
  timelineWidth={props.timelineWidth}
128
213
  selectedBarIds={props.selectedBarIds}
129
214
  onBarSelectChange={props.onBarSelectChange}
215
+ {...(props.highlightBarIds
216
+ ? { highlightBarIds: props.highlightBarIds }
217
+ : {})}
218
+ {...(props.multiSelect !== undefined
219
+ ? { multiSelect: props.multiSelect }
220
+ : {})}
130
221
  />
131
222
  </div>
132
223
  );
@@ -3,6 +3,7 @@ import React, { FunctionComponent, ReactElement } from "react";
3
3
  export interface ComponentProps {
4
4
  title: string | ReactElement;
5
5
  description: string | ReactElement;
6
+ isHighlighted?: boolean;
6
7
  }
7
8
 
8
9
  const RowLabel: FunctionComponent<ComponentProps> = (
@@ -12,10 +13,14 @@ const RowLabel: FunctionComponent<ComponentProps> = (
12
13
  // rectangle div with curved corners and text inside in tailwindcss
13
14
 
14
15
  <div className="overflow-hidden">
15
- <div className="text-sm text-gray-600 truncate overflow-hidden">
16
+ <div
17
+ className={`truncate overflow-hidden text-sm transition-colors duration-150 ${props.isHighlighted ? "font-semibold text-indigo-700" : "text-gray-600"}`}
18
+ >
16
19
  {props.title}
17
20
  </div>
18
- <div className="text-xs text-gray-500 truncate overflow-hidden">
21
+ <div
22
+ className={`truncate overflow-hidden text-xs transition-colors duration-150 ${props.isHighlighted ? "text-indigo-500" : "text-gray-500"}`}
23
+ >
19
24
  {props.description}
20
25
  </div>
21
26
  </div>
@@ -10,6 +10,7 @@ export interface ComponentProps {
10
10
  selectedBarIds: string[];
11
11
  onBarSelectChange: (barIds: string[]) => void;
12
12
  multiSelect?: boolean | undefined;
13
+ highlightBarIds?: string[];
13
14
  }
14
15
 
15
16
  const Rows: FunctionComponent<ComponentProps> = (
@@ -28,9 +29,14 @@ const Rows: FunctionComponent<ComponentProps> = (
28
29
  chartTimelineEnd={props.chartTimelineEnd}
29
30
  timelineWidth={props.timelineWidth}
30
31
  row={row}
31
- multiSelect={props.multiSelect}
32
32
  selectedBarIds={props.selectedBarIds}
33
33
  onBarSelectChange={props.onBarSelectChange}
34
+ {...(props.multiSelect !== undefined
35
+ ? { multiSelect: props.multiSelect }
36
+ : {})}
37
+ {...(props.highlightBarIds
38
+ ? { highlightBarIds: props.highlightBarIds }
39
+ : {})}
34
40
  />
35
41
  );
36
42
  };