@oneuptime/common 8.0.5416 → 8.0.5440
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 +56 -66
- package/UI/Components/Filters/JSONFilter.tsx +2 -2
- package/UI/Components/Forms/Fields/FieldLabel.tsx +7 -3
- 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/UI/Components/ModelFilter/Filter.ts +1 -0
- package/UI/Components/ModelTable/BaseModelTable.tsx +2 -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/FieldLabel.js +2 -1
- package/build/dist/UI/Components/Forms/Fields/FieldLabel.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/build/dist/UI/Components/ModelTable/BaseModelTable.js +2 -1
- package/build/dist/UI/Components/ModelTable/BaseModelTable.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,57 @@ 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
|
-
|
|
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}
|
|
151
|
+
className="block text-xs text-gray-500 font-normal flex justify-between"
|
|
164
152
|
/>
|
|
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
|
-
)}
|
|
153
|
+
</div>
|
|
154
|
+
<AutocompleteTextInput
|
|
155
|
+
value={item.key}
|
|
156
|
+
placeholder={props.keyPlaceholder}
|
|
157
|
+
suggestions={props.keys}
|
|
158
|
+
onChange={(value: string) => {
|
|
159
|
+
const newData: Array<Item> = [...data];
|
|
160
|
+
newData[index]!.key = value;
|
|
161
|
+
setData(newData);
|
|
162
|
+
onDataChange(newData);
|
|
163
|
+
}}
|
|
164
|
+
/>
|
|
189
165
|
</div>
|
|
190
166
|
|
|
191
|
-
<div className="mr-1 ml-1
|
|
192
|
-
<
|
|
193
|
-
className="h-3 w-3"
|
|
194
|
-
icon={IconProp.Equals}
|
|
195
|
-
size={SizeProp.Small}
|
|
196
|
-
/>
|
|
167
|
+
<div className="mr-1 ml-1 flex items-center justify-center pt-8">
|
|
168
|
+
<span className="text-slate-500 text-2xl leading-none">=</span>
|
|
197
169
|
</div>
|
|
198
170
|
{valueTypes.length > 1 && (
|
|
199
171
|
<div className="ml-1 w-1/2">
|
|
172
|
+
<div className="mb-1">
|
|
173
|
+
<FieldLabelElement
|
|
174
|
+
title="Type"
|
|
175
|
+
hideOptionalLabel={true}
|
|
176
|
+
required={true}
|
|
177
|
+
className="block text-xs text-gray-500 font-normal flex justify-between"
|
|
178
|
+
/>
|
|
179
|
+
</div>
|
|
200
180
|
<Dropdown
|
|
201
181
|
value={dropdownOptionsForValueTypes.find(
|
|
202
182
|
(dropdownOption: DropdownOption) => {
|
|
@@ -212,8 +192,10 @@ const DictionaryForm: FunctionComponent<ComponentProps> = (
|
|
|
212
192
|
| null,
|
|
213
193
|
) => {
|
|
214
194
|
const newData: Array<Item> = [...data];
|
|
215
|
-
|
|
195
|
+
const newType: ValueType =
|
|
216
196
|
(selectedOption as ValueType) || valueTypes[0];
|
|
197
|
+
newData[index]!.type = newType;
|
|
198
|
+
newData[index]!.value = getDefaultValueForType(newType);
|
|
217
199
|
setData(newData);
|
|
218
200
|
onDataChange(newData);
|
|
219
201
|
}}
|
|
@@ -221,6 +203,14 @@ const DictionaryForm: FunctionComponent<ComponentProps> = (
|
|
|
221
203
|
</div>
|
|
222
204
|
)}
|
|
223
205
|
<div className="ml-1 w-1/2">
|
|
206
|
+
<div className="mb-1">
|
|
207
|
+
<FieldLabelElement
|
|
208
|
+
title="Value"
|
|
209
|
+
hideOptionalLabel={true}
|
|
210
|
+
required={true}
|
|
211
|
+
className="block text-xs text-gray-500 font-normal flex justify-between"
|
|
212
|
+
/>
|
|
213
|
+
</div>
|
|
224
214
|
{item.type === ValueType.Text && (
|
|
225
215
|
<Input
|
|
226
216
|
value={item.value.toString()}
|
|
@@ -284,7 +274,7 @@ const DictionaryForm: FunctionComponent<ComponentProps> = (
|
|
|
284
274
|
/>
|
|
285
275
|
)}
|
|
286
276
|
</div>
|
|
287
|
-
<div className="ml-1
|
|
277
|
+
<div className="ml-1 flex flex-col justify-end pt-6">
|
|
288
278
|
<Button
|
|
289
279
|
dataTestId={`delete-${item.key}`}
|
|
290
280
|
title="Delete"
|
|
@@ -311,7 +301,7 @@ const DictionaryForm: FunctionComponent<ComponentProps> = (
|
|
|
311
301
|
...data,
|
|
312
302
|
{
|
|
313
303
|
key: "",
|
|
314
|
-
value:
|
|
304
|
+
value: getDefaultValueForType(valueTypes[0] as ValueType),
|
|
315
305
|
type: valueTypes[0] as ValueType,
|
|
316
306
|
},
|
|
317
307
|
]);
|
|
@@ -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
|
|
@@ -9,6 +9,7 @@ export interface ComponentProps {
|
|
|
9
9
|
description?: string | ReactElement | undefined;
|
|
10
10
|
isHeading?: boolean | undefined;
|
|
11
11
|
hideOptionalLabel?: boolean | undefined;
|
|
12
|
+
className?: string | undefined;
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
const FieldLabelElement: FunctionComponent<ComponentProps> = (
|
|
@@ -17,9 +18,12 @@ const FieldLabelElement: FunctionComponent<ComponentProps> = (
|
|
|
17
18
|
return (
|
|
18
19
|
<>
|
|
19
20
|
<label
|
|
20
|
-
className={
|
|
21
|
-
props.
|
|
22
|
-
|
|
21
|
+
className={
|
|
22
|
+
props.className ||
|
|
23
|
+
`block ${
|
|
24
|
+
props.isHeading ? "text-lg" : "text-sm"
|
|
25
|
+
} font-medium text-gray-700 flex justify-between`
|
|
26
|
+
}
|
|
23
27
|
>
|
|
24
28
|
<span>
|
|
25
29
|
{props.title}{" "}
|
|
@@ -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
|
};
|