@oneuptime/common 10.0.68 → 10.0.70
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/DatabaseModels/KubernetesCluster.ts +5 -0
- package/Models/DatabaseModels/KubernetesResource.ts +19 -0
- package/Server/API/KubernetesResourceAPI.ts +2 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1776865086264-MigrationName.ts +17 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1776881254913-DedupeKubernetesClustersAndAddUniqueIndex.ts +134 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
- package/Server/Services/DatabaseService.ts +19 -4
- package/Server/Services/KubernetesResourceService.ts +323 -8
- package/Server/Types/Database/QueryHelper.ts +127 -0
- package/Server/Types/Database/QueryUtil.ts +244 -0
- package/Server/Utils/VM/VMRunner.ts +39 -22
- package/Types/BaseDatabase/EndsWith.ts +41 -0
- package/Types/BaseDatabase/IncludesAll.ts +45 -0
- package/Types/BaseDatabase/IncludesNone.ts +48 -0
- package/Types/BaseDatabase/NotContains.ts +41 -0
- package/Types/BaseDatabase/StartsWith.ts +41 -0
- package/Types/IsolatedVM/ReturnResult.ts +6 -0
- package/Types/JSON.ts +20 -0
- package/Types/Kubernetes/KubernetesInventoryExtractor.ts +15 -1
- package/Types/SerializableObjectDictionary.ts +10 -0
- package/UI/Components/Filters/BooleanFilter.tsx +1 -0
- package/UI/Components/Filters/DateFilter.tsx +212 -25
- package/UI/Components/Filters/DropdownFilter.tsx +1 -0
- package/UI/Components/Filters/EntityFilter.tsx +214 -41
- package/UI/Components/Filters/FilterViewer.tsx +228 -146
- package/UI/Components/Filters/FilterViewerItem.tsx +1 -11
- package/UI/Components/Filters/FiltersForm.tsx +148 -97
- package/UI/Components/Filters/NumberFilter.tsx +219 -34
- package/UI/Components/Filters/OperatorSelector.tsx +91 -0
- package/UI/Components/Filters/TextFilter.tsx +182 -71
- package/UI/Components/Filters/Types/FilterOperator.ts +73 -0
- package/UI/Components/ModelTable/BaseModelTable.tsx +8 -0
- package/build/dist/Models/DatabaseModels/KubernetesCluster.js +7 -1
- package/build/dist/Models/DatabaseModels/KubernetesCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/KubernetesResource.js +20 -0
- package/build/dist/Models/DatabaseModels/KubernetesResource.js.map +1 -1
- package/build/dist/Server/API/KubernetesResourceAPI.js +2 -0
- package/build/dist/Server/API/KubernetesResourceAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776865086264-MigrationName.js +12 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776865086264-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776881254913-DedupeKubernetesClustersAndAddUniqueIndex.js +123 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776881254913-DedupeKubernetesClustersAndAddUniqueIndex.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +4 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +18 -4
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/KubernetesResourceService.js +204 -8
- package/build/dist/Server/Services/KubernetesResourceService.js.map +1 -1
- package/build/dist/Server/Types/Database/QueryHelper.js +110 -0
- package/build/dist/Server/Types/Database/QueryHelper.js.map +1 -1
- package/build/dist/Server/Types/Database/QueryUtil.js +180 -0
- package/build/dist/Server/Types/Database/QueryUtil.js.map +1 -1
- package/build/dist/Server/Utils/VM/VMRunner.js +33 -19
- package/build/dist/Server/Utils/VM/VMRunner.js.map +1 -1
- package/build/dist/Types/BaseDatabase/EndsWith.js +31 -0
- package/build/dist/Types/BaseDatabase/EndsWith.js.map +1 -0
- package/build/dist/Types/BaseDatabase/IncludesAll.js +34 -0
- package/build/dist/Types/BaseDatabase/IncludesAll.js.map +1 -0
- package/build/dist/Types/BaseDatabase/IncludesNone.js +34 -0
- package/build/dist/Types/BaseDatabase/IncludesNone.js.map +1 -0
- package/build/dist/Types/BaseDatabase/NotContains.js +31 -0
- package/build/dist/Types/BaseDatabase/NotContains.js.map +1 -0
- package/build/dist/Types/BaseDatabase/StartsWith.js +31 -0
- package/build/dist/Types/BaseDatabase/StartsWith.js.map +1 -0
- package/build/dist/Types/JSON.js +5 -0
- package/build/dist/Types/JSON.js.map +1 -1
- package/build/dist/Types/Kubernetes/KubernetesInventoryExtractor.js +7 -1
- package/build/dist/Types/Kubernetes/KubernetesInventoryExtractor.js.map +1 -1
- package/build/dist/Types/SerializableObjectDictionary.js +10 -0
- package/build/dist/Types/SerializableObjectDictionary.js.map +1 -1
- package/build/dist/UI/Components/Filters/BooleanFilter.js +1 -1
- package/build/dist/UI/Components/Filters/BooleanFilter.js.map +1 -1
- package/build/dist/UI/Components/Filters/DateFilter.js +158 -14
- package/build/dist/UI/Components/Filters/DateFilter.js.map +1 -1
- package/build/dist/UI/Components/Filters/DropdownFilter.js +1 -1
- package/build/dist/UI/Components/Filters/DropdownFilter.js.map +1 -1
- package/build/dist/UI/Components/Filters/EntityFilter.js +174 -30
- package/build/dist/UI/Components/Filters/EntityFilter.js.map +1 -1
- package/build/dist/UI/Components/Filters/FilterViewer.js +188 -97
- package/build/dist/UI/Components/Filters/FilterViewer.js.map +1 -1
- package/build/dist/UI/Components/Filters/FilterViewerItem.js +1 -6
- package/build/dist/UI/Components/Filters/FilterViewerItem.js.map +1 -1
- package/build/dist/UI/Components/Filters/FiltersForm.js +46 -38
- package/build/dist/UI/Components/Filters/FiltersForm.js.map +1 -1
- package/build/dist/UI/Components/Filters/NumberFilter.js +165 -23
- package/build/dist/UI/Components/Filters/NumberFilter.js.map +1 -1
- package/build/dist/UI/Components/Filters/OperatorSelector.js +41 -0
- package/build/dist/UI/Components/Filters/OperatorSelector.js.map +1 -0
- package/build/dist/UI/Components/Filters/TextFilter.js +130 -53
- package/build/dist/UI/Components/Filters/TextFilter.js.map +1 -1
- package/build/dist/UI/Components/Filters/Types/FilterOperator.js +63 -0
- package/build/dist/UI/Components/Filters/Types/FilterOperator.js.map +1 -0
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js +7 -0
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import BadDataException from "../Exception/BadDataException";
|
|
2
|
+
import { JSONObject, ObjectType } from "../JSON";
|
|
3
|
+
import QueryOperator from "./QueryOperator";
|
|
4
|
+
|
|
5
|
+
export default class StartsWith<T extends string> extends QueryOperator<T> {
|
|
6
|
+
private _value!: T;
|
|
7
|
+
|
|
8
|
+
public get value(): T {
|
|
9
|
+
return this._value;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
public set value(v: T) {
|
|
13
|
+
this._value = v;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
public constructor(value: T) {
|
|
17
|
+
super();
|
|
18
|
+
this.value = value;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
public override toString(): T {
|
|
22
|
+
return this.value;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
public override toJSON(): JSONObject {
|
|
26
|
+
return {
|
|
27
|
+
_type: ObjectType.StartsWith,
|
|
28
|
+
value: (this as StartsWith<T>).toString(),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
public static override fromJSON<T extends string>(
|
|
33
|
+
json: JSONObject,
|
|
34
|
+
): StartsWith<T> {
|
|
35
|
+
if (json["_type"] === ObjectType.StartsWith) {
|
|
36
|
+
return new StartsWith<T>((json["value"] as T) || ("" as T));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
throw new BadDataException("Invalid JSON: " + JSON.stringify(json));
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -4,4 +4,10 @@ export default interface ReturnResult {
|
|
|
4
4
|
returnValue: any;
|
|
5
5
|
logMessages: string[];
|
|
6
6
|
capturedMetrics: CapturedMetric[];
|
|
7
|
+
/**
|
|
8
|
+
* Populated when user-supplied code threw (or timed out). The runner still
|
|
9
|
+
* returns collected side-channel data (logs, metrics, and any host-realm
|
|
10
|
+
* context objects the caller passed in) so partial state survives the throw.
|
|
11
|
+
*/
|
|
12
|
+
scriptError?: Error | undefined;
|
|
7
13
|
}
|
package/Types/JSON.ts
CHANGED
|
@@ -7,6 +7,11 @@ import GreaterThan from "./BaseDatabase/GreaterThan";
|
|
|
7
7
|
import GreaterThanOrEqual from "./BaseDatabase/GreaterThanOrEqual";
|
|
8
8
|
import InBetween from "./BaseDatabase/InBetween";
|
|
9
9
|
import Includes from "./BaseDatabase/Includes";
|
|
10
|
+
import IncludesAll from "./BaseDatabase/IncludesAll";
|
|
11
|
+
import IncludesNone from "./BaseDatabase/IncludesNone";
|
|
12
|
+
import StartsWith from "./BaseDatabase/StartsWith";
|
|
13
|
+
import EndsWith from "./BaseDatabase/EndsWith";
|
|
14
|
+
import NotContains from "./BaseDatabase/NotContains";
|
|
10
15
|
import LessThan from "./BaseDatabase/LessThan";
|
|
11
16
|
import LessThanOrEqual from "./BaseDatabase/LessThanOrEqual";
|
|
12
17
|
import NotEqual from "./BaseDatabase/NotEqual";
|
|
@@ -71,6 +76,11 @@ export enum ObjectType {
|
|
|
71
76
|
NotNull = "NotNull",
|
|
72
77
|
IsNull = "IsNull",
|
|
73
78
|
Includes = "Includes",
|
|
79
|
+
IncludesAll = "IncludesAll",
|
|
80
|
+
IncludesNone = "IncludesNone",
|
|
81
|
+
StartsWith = "StartsWith",
|
|
82
|
+
EndsWith = "EndsWith",
|
|
83
|
+
NotContains = "NotContains",
|
|
74
84
|
DashboardComponent = "DashboardComponent",
|
|
75
85
|
DashboardViewConfig = "DashboardViewConfig",
|
|
76
86
|
}
|
|
@@ -157,6 +167,16 @@ export type JSONValue =
|
|
|
157
167
|
| Array<StartAndEndTime>
|
|
158
168
|
| Includes
|
|
159
169
|
| Array<Includes>
|
|
170
|
+
| IncludesAll
|
|
171
|
+
| Array<IncludesAll>
|
|
172
|
+
| IncludesNone
|
|
173
|
+
| Array<IncludesNone>
|
|
174
|
+
| StartsWith<string>
|
|
175
|
+
| Array<StartsWith<string>>
|
|
176
|
+
| EndsWith<string>
|
|
177
|
+
| Array<EndsWith<string>>
|
|
178
|
+
| NotContains<string>
|
|
179
|
+
| Array<NotContains<string>>
|
|
160
180
|
| DashboardViewConfig;
|
|
161
181
|
|
|
162
182
|
export interface JSONObject {
|
|
@@ -79,6 +79,12 @@ export interface ParsedKubernetesResource {
|
|
|
79
79
|
annotations: JSONObject | null;
|
|
80
80
|
ownerReferences: JSONObject | null;
|
|
81
81
|
spec: JSONObject | null;
|
|
82
|
+
/*
|
|
83
|
+
* For Pod kinds: length of spec.containers at parse time. Lets the
|
|
84
|
+
* overview summary SUM() a plain int column instead of scanning
|
|
85
|
+
* every pod's JSONB spec on every page load.
|
|
86
|
+
*/
|
|
87
|
+
containerCount: number | null;
|
|
82
88
|
status: JSONObject | null;
|
|
83
89
|
lastSeenAt: Date;
|
|
84
90
|
resourceCreationTimestamp: Date | null;
|
|
@@ -255,13 +261,20 @@ export function extractInventoryResource(data: {
|
|
|
255
261
|
return null;
|
|
256
262
|
}
|
|
257
263
|
|
|
258
|
-
// Pod-specific hot
|
|
264
|
+
// Pod-specific hot columns
|
|
259
265
|
let phase: string | null = null;
|
|
266
|
+
let containerCount: number | null = null;
|
|
260
267
|
if (kind === "Pod") {
|
|
261
268
|
const podStatus: KubernetesPodObject["status"] = (
|
|
262
269
|
parsed as KubernetesPodObject
|
|
263
270
|
).status;
|
|
264
271
|
phase = podStatus?.phase || null;
|
|
272
|
+
const podSpec: KubernetesPodObject["spec"] | undefined = (
|
|
273
|
+
parsed as KubernetesPodObject
|
|
274
|
+
).spec;
|
|
275
|
+
containerCount = Array.isArray(podSpec?.containers)
|
|
276
|
+
? podSpec.containers.length
|
|
277
|
+
: 0;
|
|
265
278
|
}
|
|
266
279
|
|
|
267
280
|
// Node-specific hot columns
|
|
@@ -318,6 +331,7 @@ export function extractInventoryResource(data: {
|
|
|
318
331
|
} as unknown as JSONObject)
|
|
319
332
|
: null,
|
|
320
333
|
spec: (anyParsed.spec as JSONObject | undefined) || null,
|
|
334
|
+
containerCount,
|
|
321
335
|
status: (anyParsed.status as JSONObject | undefined) || null,
|
|
322
336
|
lastSeenAt: data.lastSeenAt,
|
|
323
337
|
resourceCreationTimestamp: parseCreationTimestamp(
|
|
@@ -7,6 +7,11 @@ import GreaterThan from "./BaseDatabase/GreaterThan";
|
|
|
7
7
|
import GreaterThanOrEqual from "./BaseDatabase/GreaterThanOrEqual";
|
|
8
8
|
import InBetween from "./BaseDatabase/InBetween";
|
|
9
9
|
import Includes from "./BaseDatabase/Includes";
|
|
10
|
+
import IncludesAll from "./BaseDatabase/IncludesAll";
|
|
11
|
+
import IncludesNone from "./BaseDatabase/IncludesNone";
|
|
12
|
+
import StartsWith from "./BaseDatabase/StartsWith";
|
|
13
|
+
import EndsWith from "./BaseDatabase/EndsWith";
|
|
14
|
+
import NotContains from "./BaseDatabase/NotContains";
|
|
10
15
|
import IsNull from "./BaseDatabase/IsNull";
|
|
11
16
|
import LessThan from "./BaseDatabase/LessThan";
|
|
12
17
|
import LessThanOrEqual from "./BaseDatabase/LessThanOrEqual";
|
|
@@ -65,6 +70,11 @@ const SerializableObjectDictionary: Dictionary<any> = {
|
|
|
65
70
|
[ObjectType.HashedString]: HashedString,
|
|
66
71
|
[ObjectType.InBetween]: InBetween,
|
|
67
72
|
[ObjectType.Includes]: Includes,
|
|
73
|
+
[ObjectType.IncludesAll]: IncludesAll,
|
|
74
|
+
[ObjectType.IncludesNone]: IncludesNone,
|
|
75
|
+
[ObjectType.StartsWith]: StartsWith,
|
|
76
|
+
[ObjectType.EndsWith]: EndsWith,
|
|
77
|
+
[ObjectType.NotContains]: NotContains,
|
|
68
78
|
[ObjectType.NotNull]: NotNull,
|
|
69
79
|
[ObjectType.IsNull]: IsNull,
|
|
70
80
|
[ObjectType.Recurring]: Recurring,
|
|
@@ -1,11 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import StartAndEndDate, { StartAndEndDateType } from "../Date/StartAndEndDate";
|
|
1
|
+
import Input, { InputType } from "../Input/Input";
|
|
3
2
|
import FieldType from "../Types/FieldType";
|
|
4
3
|
import Filter from "./Types/Filter";
|
|
5
4
|
import FilterData from "./Types/FilterData";
|
|
5
|
+
import FilterOperator from "./Types/FilterOperator";
|
|
6
|
+
import OperatorSelector from "./OperatorSelector";
|
|
6
7
|
import InBetween from "../../../Types/BaseDatabase/InBetween";
|
|
8
|
+
import GreaterThan from "../../../Types/BaseDatabase/GreaterThan";
|
|
9
|
+
import LessThan from "../../../Types/BaseDatabase/LessThan";
|
|
10
|
+
import EqualTo from "../../../Types/BaseDatabase/EqualTo";
|
|
11
|
+
import IsNull from "../../../Types/BaseDatabase/IsNull";
|
|
12
|
+
import NotNull from "../../../Types/BaseDatabase/NotNull";
|
|
13
|
+
import OneUptimeDate from "../../../Types/Date";
|
|
7
14
|
import GenericObject from "../../../Types/GenericObject";
|
|
8
|
-
import React, { ReactElement } from "react";
|
|
15
|
+
import React, { ReactElement, useEffect, useState } from "react";
|
|
9
16
|
|
|
10
17
|
export interface ComponentProps<T extends GenericObject> {
|
|
11
18
|
filter: Filter<T>;
|
|
@@ -17,38 +24,218 @@ type DateFilterFunction = <T extends GenericObject>(
|
|
|
17
24
|
props: ComponentProps<T>,
|
|
18
25
|
) => ReactElement;
|
|
19
26
|
|
|
27
|
+
const DATE_OPERATORS: Array<FilterOperator> = [
|
|
28
|
+
FilterOperator.Is,
|
|
29
|
+
FilterOperator.Before,
|
|
30
|
+
FilterOperator.After,
|
|
31
|
+
FilterOperator.Between,
|
|
32
|
+
FilterOperator.IsEmpty,
|
|
33
|
+
FilterOperator.IsNotEmpty,
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
type DateState = {
|
|
37
|
+
operator: FilterOperator;
|
|
38
|
+
start: Date | null;
|
|
39
|
+
end: Date | null;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const toDate = (value: unknown): Date | null => {
|
|
43
|
+
if (!value) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
if (value instanceof Date) {
|
|
47
|
+
return value;
|
|
48
|
+
}
|
|
49
|
+
try {
|
|
50
|
+
return OneUptimeDate.fromString(value as string);
|
|
51
|
+
} catch {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const detectState = (rawValue: unknown): DateState => {
|
|
57
|
+
if (rawValue instanceof InBetween) {
|
|
58
|
+
const start: Date | null = toDate(rawValue.startValue as unknown);
|
|
59
|
+
const end: Date | null = toDate(rawValue.endValue as unknown);
|
|
60
|
+
|
|
61
|
+
// If start/end match the bounds of a single day, treat as "Is".
|
|
62
|
+
if (start && end) {
|
|
63
|
+
const startOfDay: Date = OneUptimeDate.getStartOfDay(start);
|
|
64
|
+
const endOfDay: Date = OneUptimeDate.getEndOfDay(start);
|
|
65
|
+
if (
|
|
66
|
+
start.getTime() === startOfDay.getTime() &&
|
|
67
|
+
end.getTime() === endOfDay.getTime()
|
|
68
|
+
) {
|
|
69
|
+
return { operator: FilterOperator.Is, start, end: null };
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return { operator: FilterOperator.Between, start, end };
|
|
74
|
+
}
|
|
75
|
+
if (rawValue instanceof GreaterThan) {
|
|
76
|
+
return {
|
|
77
|
+
operator: FilterOperator.After,
|
|
78
|
+
start: toDate(rawValue.value as unknown),
|
|
79
|
+
end: null,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
if (rawValue instanceof LessThan) {
|
|
83
|
+
return {
|
|
84
|
+
operator: FilterOperator.Before,
|
|
85
|
+
start: toDate(rawValue.value as unknown),
|
|
86
|
+
end: null,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
if (rawValue instanceof EqualTo) {
|
|
90
|
+
return {
|
|
91
|
+
operator: FilterOperator.Is,
|
|
92
|
+
start: toDate(rawValue.value as unknown),
|
|
93
|
+
end: null,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
if (rawValue instanceof IsNull) {
|
|
97
|
+
return { operator: FilterOperator.IsEmpty, start: null, end: null };
|
|
98
|
+
}
|
|
99
|
+
if (rawValue instanceof NotNull) {
|
|
100
|
+
return { operator: FilterOperator.IsNotEmpty, start: null, end: null };
|
|
101
|
+
}
|
|
102
|
+
return { operator: FilterOperator.Is, start: null, end: null };
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const buildValue = (state: DateState, isDateTime: boolean): unknown => {
|
|
106
|
+
switch (state.operator) {
|
|
107
|
+
case FilterOperator.Is: {
|
|
108
|
+
if (!state.start) {
|
|
109
|
+
return undefined;
|
|
110
|
+
}
|
|
111
|
+
if (isDateTime) {
|
|
112
|
+
return new EqualTo(state.start as any);
|
|
113
|
+
}
|
|
114
|
+
return new InBetween(
|
|
115
|
+
OneUptimeDate.getStartOfDay(state.start) as any,
|
|
116
|
+
OneUptimeDate.getEndOfDay(state.start) as any,
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
case FilterOperator.Before:
|
|
120
|
+
return state.start ? new LessThan(state.start) : undefined;
|
|
121
|
+
case FilterOperator.After:
|
|
122
|
+
return state.start ? new GreaterThan(state.start) : undefined;
|
|
123
|
+
case FilterOperator.Between: {
|
|
124
|
+
if (!state.start || !state.end) {
|
|
125
|
+
return undefined;
|
|
126
|
+
}
|
|
127
|
+
return new InBetween(
|
|
128
|
+
(isDateTime
|
|
129
|
+
? state.start
|
|
130
|
+
: OneUptimeDate.getStartOfDay(state.start)) as any,
|
|
131
|
+
(isDateTime
|
|
132
|
+
? state.end
|
|
133
|
+
: OneUptimeDate.getEndOfDay(state.end)) as any,
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
case FilterOperator.IsEmpty:
|
|
137
|
+
return new IsNull();
|
|
138
|
+
case FilterOperator.IsNotEmpty:
|
|
139
|
+
return new NotNull();
|
|
140
|
+
default:
|
|
141
|
+
return undefined;
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
|
|
20
145
|
const DateFilter: DateFilterFunction = <T extends GenericObject>(
|
|
21
146
|
props: ComponentProps<T>,
|
|
22
147
|
): ReactElement => {
|
|
23
148
|
const filter: Filter<T> = props.filter;
|
|
24
|
-
const filterData: FilterData<T> = { ...props.filterData };
|
|
25
149
|
|
|
26
150
|
if (filter.type !== FieldType.Date && filter.type !== FieldType.DateTime) {
|
|
27
151
|
return <></>;
|
|
28
152
|
}
|
|
29
153
|
|
|
154
|
+
const isDateTime: boolean = filter.type === FieldType.DateTime;
|
|
155
|
+
const detected: DateState = detectState(props.filterData[filter.key]);
|
|
156
|
+
|
|
157
|
+
const [localOperator, setLocalOperator] = useState<FilterOperator>(
|
|
158
|
+
detected.operator,
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
useEffect(() => {
|
|
162
|
+
const raw: unknown = props.filterData[filter.key];
|
|
163
|
+
if (raw !== undefined && raw !== null) {
|
|
164
|
+
setLocalOperator(detected.operator);
|
|
165
|
+
}
|
|
166
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
167
|
+
}, [props.filterData[filter.key]]);
|
|
168
|
+
|
|
169
|
+
const state: DateState = { ...detected, operator: localOperator };
|
|
170
|
+
|
|
171
|
+
const valuelessOperator: boolean =
|
|
172
|
+
state.operator === FilterOperator.IsEmpty ||
|
|
173
|
+
state.operator === FilterOperator.IsNotEmpty;
|
|
174
|
+
const isBetween: boolean = state.operator === FilterOperator.Between;
|
|
175
|
+
|
|
176
|
+
type ApplyFunction = (nextState: DateState) => void;
|
|
177
|
+
|
|
178
|
+
const apply: ApplyFunction = (nextState: DateState): void => {
|
|
179
|
+
if (!filter.key) {
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
setLocalOperator(nextState.operator);
|
|
183
|
+
const next: FilterData<T> = { ...props.filterData };
|
|
184
|
+
const built: unknown = buildValue(nextState, isDateTime);
|
|
185
|
+
if (built === undefined) {
|
|
186
|
+
delete next[filter.key];
|
|
187
|
+
} else {
|
|
188
|
+
next[filter.key] = built as any;
|
|
189
|
+
}
|
|
190
|
+
props.onFilterChanged?.(next);
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
const inputType: InputType = isDateTime
|
|
194
|
+
? InputType.DATETIME_LOCAL
|
|
195
|
+
: InputType.DATE;
|
|
196
|
+
|
|
30
197
|
return (
|
|
31
|
-
<
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
198
|
+
<div className="flex gap-2 items-start">
|
|
199
|
+
<OperatorSelector
|
|
200
|
+
value={state.operator}
|
|
201
|
+
options={DATE_OPERATORS}
|
|
202
|
+
onChange={(nextOperator: FilterOperator) => {
|
|
203
|
+
apply({ ...state, operator: nextOperator });
|
|
204
|
+
}}
|
|
205
|
+
/>
|
|
206
|
+
{!valuelessOperator && (
|
|
207
|
+
<div className={isBetween ? "flex-1 flex gap-2 min-w-0" : "flex-1 min-w-0"}>
|
|
208
|
+
<div className="flex-1 min-w-0">
|
|
209
|
+
<Input
|
|
210
|
+
key={`${filter.key as string}-start-${state.operator}`}
|
|
211
|
+
onChange={(changed: string | Date) => {
|
|
212
|
+
const parsed: Date | null = toDate(changed);
|
|
213
|
+
apply({ ...state, start: parsed });
|
|
214
|
+
}}
|
|
215
|
+
value={state.start || ""}
|
|
216
|
+
placeholder={isBetween ? "From" : `Filter by ${filter.title}`}
|
|
217
|
+
type={inputType}
|
|
218
|
+
outerDivClassName="relative rounded-md w-full"
|
|
219
|
+
/>
|
|
220
|
+
</div>
|
|
221
|
+
{isBetween && (
|
|
222
|
+
<div className="flex-1 min-w-0">
|
|
223
|
+
<Input
|
|
224
|
+
key={`${filter.key as string}-end-${state.operator}`}
|
|
225
|
+
onChange={(changed: string | Date) => {
|
|
226
|
+
const parsed: Date | null = toDate(changed);
|
|
227
|
+
apply({ ...state, end: parsed });
|
|
228
|
+
}}
|
|
229
|
+
value={state.end || ""}
|
|
230
|
+
placeholder="To"
|
|
231
|
+
type={inputType}
|
|
232
|
+
outerDivClassName="relative rounded-md w-full"
|
|
233
|
+
/>
|
|
234
|
+
</div>
|
|
235
|
+
)}
|
|
236
|
+
</div>
|
|
237
|
+
)}
|
|
238
|
+
</div>
|
|
52
239
|
);
|
|
53
240
|
};
|
|
54
241
|
|
|
@@ -61,6 +61,7 @@ const DropdownFilter: DropdownFilterFunction = <T extends GenericObject>(
|
|
|
61
61
|
value={dropdownValues}
|
|
62
62
|
isMultiSelect={props.isMultiSelect || false}
|
|
63
63
|
placeholder={`Filter by ${filter.title}`}
|
|
64
|
+
className="relative rounded-md w-full overflow-visible"
|
|
64
65
|
/>
|
|
65
66
|
);
|
|
66
67
|
}
|