@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.
- package/Models/AnalyticsModels/AnalyticsBaseModel/AnalyticsBaseModel.ts +22 -0
- package/Models/AnalyticsModels/ExceptionInstance.ts +341 -323
- package/Models/AnalyticsModels/Log.ts +278 -231
- package/Models/AnalyticsModels/Metric.ts +504 -446
- package/Models/AnalyticsModels/MonitorLog.ts +99 -93
- package/Models/AnalyticsModels/Span.ts +473 -417
- package/Server/Services/AlertService.ts +12 -0
- package/Server/Services/IncidentService.ts +12 -0
- package/Server/Services/OpenTelemetryIngestService.ts +4 -0
- package/Server/Services/TelemetryAttributeService.ts +21 -4
- package/Server/Utils/Monitor/MonitorResource.ts +24 -0
- package/Server/Utils/Telemetry/Telemetry.ts +13 -0
- package/Types/AnalyticsDatabase/MaterializedView.ts +4 -0
- package/Types/AnalyticsDatabase/Projection.ts +4 -0
- package/Types/Date.ts +108 -0
- package/UI/Components/AutocompleteTextInput/AutocompleteTextInput.tsx +250 -0
- package/UI/Components/Dictionary/Dictionary.tsx +53 -66
- package/UI/Components/Filters/JSONFilter.tsx +2 -2
- package/UI/Components/Forms/Fields/FormField.tsx +2 -2
- package/UI/Components/GanttChart/Bar/Index.tsx +13 -0
- package/UI/Components/GanttChart/Index.tsx +7 -1
- package/UI/Components/GanttChart/Row/Index.tsx +1 -0
- package/UI/Components/GanttChart/Row/Row.tsx +101 -10
- package/UI/Components/GanttChart/Row/RowLabel.tsx +7 -2
- package/UI/Components/GanttChart/Rows.tsx +7 -1
- package/UI/Components/LogsViewer/LogItem.tsx +149 -10
- package/UI/Components/LogsViewer/LogsViewer.tsx +25 -1
- package/build/dist/Models/AnalyticsModels/AnalyticsBaseModel/AnalyticsBaseModel.js +16 -0
- package/build/dist/Models/AnalyticsModels/AnalyticsBaseModel/AnalyticsBaseModel.js.map +1 -1
- package/build/dist/Models/AnalyticsModels/ExceptionInstance.js +325 -310
- package/build/dist/Models/AnalyticsModels/ExceptionInstance.js.map +1 -1
- package/build/dist/Models/AnalyticsModels/Log.js +263 -222
- package/build/dist/Models/AnalyticsModels/Log.js.map +1 -1
- package/build/dist/Models/AnalyticsModels/Metric.js +477 -427
- package/build/dist/Models/AnalyticsModels/Metric.js.map +1 -1
- package/build/dist/Models/AnalyticsModels/MonitorLog.js +95 -90
- package/build/dist/Models/AnalyticsModels/MonitorLog.js.map +1 -1
- package/build/dist/Models/AnalyticsModels/Span.js +449 -400
- package/build/dist/Models/AnalyticsModels/Span.js.map +1 -1
- package/build/dist/Server/Services/AlertService.js +4 -0
- package/build/dist/Server/Services/AlertService.js.map +1 -1
- package/build/dist/Server/Services/IncidentService.js +4 -0
- package/build/dist/Server/Services/IncidentService.js.map +1 -1
- package/build/dist/Server/Services/OpenTelemetryIngestService.js +1 -0
- package/build/dist/Server/Services/OpenTelemetryIngestService.js.map +1 -1
- package/build/dist/Server/Services/TelemetryAttributeService.js +19 -4
- package/build/dist/Server/Services/TelemetryAttributeService.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorResource.js +12 -0
- package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/Telemetry.js +6 -0
- package/build/dist/Server/Utils/Telemetry/Telemetry.js.map +1 -1
- package/build/dist/Types/AnalyticsDatabase/MaterializedView.js +2 -0
- package/build/dist/Types/AnalyticsDatabase/MaterializedView.js.map +1 -0
- package/build/dist/Types/AnalyticsDatabase/Projection.js +2 -0
- package/build/dist/Types/AnalyticsDatabase/Projection.js.map +1 -0
- package/build/dist/Types/Date.js +82 -0
- package/build/dist/Types/Date.js.map +1 -1
- package/build/dist/UI/Components/AutocompleteTextInput/AutocompleteTextInput.js +143 -0
- package/build/dist/UI/Components/AutocompleteTextInput/AutocompleteTextInput.js.map +1 -0
- package/build/dist/UI/Components/Dictionary/Dictionary.js +25 -36
- package/build/dist/UI/Components/Dictionary/Dictionary.js.map +1 -1
- package/build/dist/UI/Components/Filters/JSONFilter.js +2 -2
- package/build/dist/UI/Components/Filters/JSONFilter.js.map +1 -1
- package/build/dist/UI/Components/Forms/Fields/FormField.js +2 -2
- package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
- package/build/dist/UI/Components/GanttChart/Bar/Index.js +10 -0
- package/build/dist/UI/Components/GanttChart/Bar/Index.js.map +1 -1
- package/build/dist/UI/Components/GanttChart/Index.js +6 -2
- package/build/dist/UI/Components/GanttChart/Index.js.map +1 -1
- package/build/dist/UI/Components/GanttChart/Row/Index.js.map +1 -1
- package/build/dist/UI/Components/GanttChart/Row/Row.js +62 -9
- package/build/dist/UI/Components/GanttChart/Row/Row.js.map +1 -1
- package/build/dist/UI/Components/GanttChart/Row/RowLabel.js +2 -2
- package/build/dist/UI/Components/GanttChart/Row/RowLabel.js.map +1 -1
- package/build/dist/UI/Components/GanttChart/Rows.js +5 -1
- package/build/dist/UI/Components/GanttChart/Rows.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/LogItem.js +73 -5
- package/build/dist/UI/Components/LogsViewer/LogItem.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js +7 -1
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
- 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
|
|
14
|
-
import
|
|
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:
|
|
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
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
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
|
-
|
|
155
|
-
<
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
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
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
)
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
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
|
|
192
|
-
<
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
}}
|
|
@@ -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, {
|
|
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
|
|
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
|
-
|
|
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
|
|
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=
|
|
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
|
-
<
|
|
71
|
-
|
|
72
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
};
|