@orchestrator-ui/orchestrator-ui-components 6.8.0 → 7.0.1
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/.turbo/turbo-build.log +8 -8
- package/.turbo/turbo-lint.log +5 -2
- package/.turbo/turbo-test.log +9 -10
- package/CHANGELOG.md +19 -0
- package/dist/index.d.ts +164 -4306
- package/dist/index.js +1739 -5747
- package/dist/index.js.map +1 -1
- package/package.json +4 -8
- package/src/components/WfoPydanticForm/Footer.tsx +33 -6
- package/src/components/WfoPydanticForm/RenderFormErrors.tsx +2 -1
- package/src/components/WfoPydanticForm/Row.tsx +3 -1
- package/src/components/WfoPydanticForm/fields/WfoInteger.tsx +5 -1
- package/src/components/WfoPydanticForm/fields/WfoSummary.tsx +3 -5
- package/src/components/WfoPydanticForm/fields/styles.ts +72 -0
- package/src/components/WfoSearchPage/WfoSearch/WfoSearch.tsx +42 -54
- package/src/components/WfoSearchPage/utils.ts +9 -1
- package/src/components/WfoWorkflowSteps/WfoStep/WfoStep.tsx +10 -27
- package/src/components/WfoWorkflowSteps/WfoStep/WfoStepForm.tsx +1 -1
- package/src/components/index.ts +0 -1
- package/src/configuration/version.ts +1 -1
- package/src/contexts/OrchestratorConfigContext.tsx +0 -1
- package/src/hooks/useSearch.ts +12 -2
- package/src/hooks/useSearchPagination.ts +4 -0
- package/src/messages/en-GB.json +5 -3
- package/src/pages/processes/WfoStartProcessPage.tsx +5 -107
- package/src/rtk/endpoints/index.ts +0 -2
- package/src/rtk/endpoints/search.ts +13 -4
- package/src/types/index.ts +0 -1
- package/src/types/search.ts +7 -0
- package/src/types/types.ts +0 -1
- package/src/components/WfoForms/AutoFieldLoader.tsx +0 -118
- package/src/components/WfoForms/AutoFields.tsx +0 -49
- package/src/components/WfoForms/CreateForm.tsx +0 -75
- package/src/components/WfoForms/UserInputForm.tsx +0 -697
- package/src/components/WfoForms/UserInputFormStyling.ts +0 -80
- package/src/components/WfoForms/UserInputFormWizard.tsx +0 -127
- package/src/components/WfoForms/formFields/AcceptField.tsx +0 -243
- package/src/components/WfoForms/formFields/AcceptFieldStyling.ts +0 -35
- package/src/components/WfoForms/formFields/BoolField.tsx +0 -77
- package/src/components/WfoForms/formFields/BoolFieldStyling.ts +0 -64
- package/src/components/WfoForms/formFields/ConnectedSelectField.tsx +0 -19
- package/src/components/WfoForms/formFields/CustomerField.tsx +0 -77
- package/src/components/WfoForms/formFields/DateField.tsx +0 -72
- package/src/components/WfoForms/formFields/DividerField.tsx +0 -29
- package/src/components/WfoForms/formFields/ErrorField.tsx +0 -40
- package/src/components/WfoForms/formFields/ErrorsField.tsx +0 -34
- package/src/components/WfoForms/formFields/LabelField.tsx +0 -43
- package/src/components/WfoForms/formFields/ListAddField.tsx +0 -95
- package/src/components/WfoForms/formFields/ListDelField.tsx +0 -95
- package/src/components/WfoForms/formFields/ListField.tsx +0 -117
- package/src/components/WfoForms/formFields/ListItemField.tsx +0 -40
- package/src/components/WfoForms/formFields/ListSelectField.tsx +0 -95
- package/src/components/WfoForms/formFields/LocationCodeField.tsx +0 -60
- package/src/components/WfoForms/formFields/LongTextField.tsx +0 -68
- package/src/components/WfoForms/formFields/NestField.tsx +0 -107
- package/src/components/WfoForms/formFields/NumField.tsx +0 -85
- package/src/components/WfoForms/formFields/OptGroupField.tsx +0 -74
- package/src/components/WfoForms/formFields/RadioField.tsx +0 -87
- package/src/components/WfoForms/formFields/SelectField/SelectField.tsx +0 -177
- package/src/components/WfoForms/formFields/SelectField/index.ts +0 -1
- package/src/components/WfoForms/formFields/SelectField/styles.ts +0 -52
- package/src/components/WfoForms/formFields/SubmitField.tsx +0 -50
- package/src/components/WfoForms/formFields/SubscriptionSummaryField.tsx +0 -74
- package/src/components/WfoForms/formFields/SummaryField.tsx +0 -104
- package/src/components/WfoForms/formFields/SummaryFieldStyling.ts +0 -44
- package/src/components/WfoForms/formFields/TextField.tsx +0 -81
- package/src/components/WfoForms/formFields/commonStyles.ts +0 -32
- package/src/components/WfoForms/formFields/deprecated/ContactPersonAutocomplete.tsx +0 -99
- package/src/components/WfoForms/formFields/deprecated/ContactPersonAutocompleteStyles.ts +0 -41
- package/src/components/WfoForms/formFields/deprecated/ContactPersonNameField.tsx +0 -263
- package/src/components/WfoForms/formFields/deprecated/FileUploadField.tsx +0 -151
- package/src/components/WfoForms/formFields/deprecated/ImsNodeIdField.tsx +0 -109
- package/src/components/WfoForms/formFields/deprecated/ImsPortIdField.tsx +0 -233
- package/src/components/WfoForms/formFields/deprecated/ImsPortIdFieldStyling.ts +0 -17
- package/src/components/WfoForms/formFields/deprecated/IpNetworkField.tsx +0 -105
- package/src/components/WfoForms/formFields/deprecated/IpPrefixTableField.tsx +0 -390
- package/src/components/WfoForms/formFields/deprecated/IpPrefixTableFieldStyling.ts +0 -117
- package/src/components/WfoForms/formFields/deprecated/SplitPrefix.tsx +0 -138
- package/src/components/WfoForms/formFields/deprecated/SplitPrefixStyling.ts +0 -11
- package/src/components/WfoForms/formFields/deprecated/SubscriptionField.tsx +0 -263
- package/src/components/WfoForms/formFields/deprecated/SubscriptionFieldStyling.ts +0 -33
- package/src/components/WfoForms/formFields/deprecated/TimestampField.tsx +0 -110
- package/src/components/WfoForms/formFields/deprecated/VlanField.tsx +0 -300
- package/src/components/WfoForms/formFields/deprecated/index.ts +0 -15
- package/src/components/WfoForms/formFields/deprecated/types.ts +0 -74
- package/src/components/WfoForms/formFields/deprecated/utils.ts +0 -1
- package/src/components/WfoForms/formFields/index.ts +0 -30
- package/src/components/WfoForms/formFields/listFieldStyling.ts +0 -86
- package/src/components/WfoForms/formFields/types.ts +0 -41
- package/src/components/WfoForms/formFields/utils.spec.ts +0 -296
- package/src/components/WfoForms/formFields/utils.ts +0 -69
- package/src/components/WfoForms/index.ts +0 -5
- package/src/components/WfoWorkflowSteps/WfoStep/WfoStepFormOld.tsx +0 -67
- package/src/hooks/deprecated/useGetSurfSubcriptionDropdownOptions.ts +0 -37
- package/src/hooks/deprecated/useIsTaggedPort.ts +0 -25
- package/src/rtk/endpoints/deprecated/index.ts +0 -1
- package/src/rtk/endpoints/deprecated/surfSubscriptionDropdownOptions.ts +0 -53
- package/src/rtk/endpoints/formFields.ts +0 -131
- package/src/rtk/endpoints/ipam.ts +0 -54
- package/src/types/deprecated/SurfSubscriptionDropdownOptionsFilterParams.ts +0 -10
- package/src/types/deprecated/index.ts +0 -1
|
@@ -1,263 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Copyright 2019-2023 SURF.
|
|
3
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
-
* you may not use this file except in compliance with the License.
|
|
5
|
-
* You may obtain a copy of the License at
|
|
6
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
7
|
-
*
|
|
8
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
9
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
10
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
-
* See the License for the specific language governing permissions and
|
|
12
|
-
* limitations under the License.
|
|
13
|
-
*
|
|
14
|
-
*/
|
|
15
|
-
import React from 'react';
|
|
16
|
-
import ReactSelect from 'react-select';
|
|
17
|
-
|
|
18
|
-
import get from 'lodash/get';
|
|
19
|
-
import { useTranslations } from 'next-intl';
|
|
20
|
-
import {
|
|
21
|
-
connectField,
|
|
22
|
-
filterDOMProps,
|
|
23
|
-
joinName,
|
|
24
|
-
useField,
|
|
25
|
-
useForm,
|
|
26
|
-
} from 'uniforms';
|
|
27
|
-
|
|
28
|
-
import {
|
|
29
|
-
EuiButtonIcon,
|
|
30
|
-
EuiFlexGroup,
|
|
31
|
-
EuiFlexItem,
|
|
32
|
-
EuiFormRow,
|
|
33
|
-
EuiText,
|
|
34
|
-
} from '@elastic/eui';
|
|
35
|
-
|
|
36
|
-
import { useWithOrchestratorTheme } from '@/hooks';
|
|
37
|
-
import { useGetSurfSubscriptionDropdownOptions } from '@/hooks/deprecated/useGetSurfSubcriptionDropdownOptions';
|
|
38
|
-
import type { Option } from '@/types';
|
|
39
|
-
|
|
40
|
-
import { getSelectFieldStyles } from '../SelectField/styles';
|
|
41
|
-
import { FieldProps } from '../types';
|
|
42
|
-
import { subscriptionFieldStyling } from './SubscriptionFieldStyling';
|
|
43
|
-
|
|
44
|
-
declare module 'uniforms' {
|
|
45
|
-
interface FilterDOMProps {
|
|
46
|
-
excludedSubscriptionIds: never;
|
|
47
|
-
customerId: never;
|
|
48
|
-
customerKey: never;
|
|
49
|
-
visiblePortMode: never;
|
|
50
|
-
bandwidth: never;
|
|
51
|
-
bandwidthKey: never;
|
|
52
|
-
tags: never;
|
|
53
|
-
statuses: never;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
filterDOMProps.register(
|
|
57
|
-
'excludedSubscriptionIds',
|
|
58
|
-
'customerId',
|
|
59
|
-
'customerKey',
|
|
60
|
-
'visiblePortMode',
|
|
61
|
-
'bandwidth',
|
|
62
|
-
'bandwidthKey',
|
|
63
|
-
'tags',
|
|
64
|
-
'statuses',
|
|
65
|
-
);
|
|
66
|
-
|
|
67
|
-
export type SubscriptionFieldProps = FieldProps<
|
|
68
|
-
string,
|
|
69
|
-
{
|
|
70
|
-
productIds?: string[];
|
|
71
|
-
excludedSubscriptionIds?: string[];
|
|
72
|
-
customerId?: string;
|
|
73
|
-
customerKey?: string;
|
|
74
|
-
visiblePortMode?: string;
|
|
75
|
-
bandwidth?: number;
|
|
76
|
-
bandwidthKey?: string;
|
|
77
|
-
tags?: string[]; // There is an assumption that using tags means you want port subscriptions
|
|
78
|
-
statuses?: string[];
|
|
79
|
-
}
|
|
80
|
-
>;
|
|
81
|
-
|
|
82
|
-
function toPortModes(visiblePortMode: string): string[] {
|
|
83
|
-
if (visiblePortMode === 'all') {
|
|
84
|
-
return [];
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
if (visiblePortMode === 'normal') {
|
|
88
|
-
return ['tagged', 'untagged'];
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
return [visiblePortMode];
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
function SubscriptionFieldDefinition({
|
|
95
|
-
disabled,
|
|
96
|
-
id,
|
|
97
|
-
label,
|
|
98
|
-
description,
|
|
99
|
-
name,
|
|
100
|
-
onChange,
|
|
101
|
-
readOnly,
|
|
102
|
-
value,
|
|
103
|
-
error,
|
|
104
|
-
showInlineError,
|
|
105
|
-
errorMessage,
|
|
106
|
-
className = '',
|
|
107
|
-
productIds = [],
|
|
108
|
-
excludedSubscriptionIds = [],
|
|
109
|
-
customerId,
|
|
110
|
-
customerKey,
|
|
111
|
-
visiblePortMode = 'all',
|
|
112
|
-
bandwidth,
|
|
113
|
-
bandwidthKey,
|
|
114
|
-
tags,
|
|
115
|
-
statuses,
|
|
116
|
-
...props
|
|
117
|
-
}: SubscriptionFieldProps) {
|
|
118
|
-
const t = useTranslations('pydanticForms');
|
|
119
|
-
// React select allows callbacks to supply style for innercomponents: https://react-select.com/styles#inner-components
|
|
120
|
-
const { reactSelectInnerComponentStyles } =
|
|
121
|
-
useWithOrchestratorTheme(getSelectFieldStyles);
|
|
122
|
-
|
|
123
|
-
const nameArray = joinName(null, name);
|
|
124
|
-
let parentName = joinName(nameArray.slice(0, -1));
|
|
125
|
-
|
|
126
|
-
// We cant call useField conditionally so we call it for ourself if there is no parent
|
|
127
|
-
if (parentName === '') {
|
|
128
|
-
parentName = name;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
const parent = useField(parentName, {}, { absoluteName: true })[0];
|
|
132
|
-
|
|
133
|
-
const { model, schema } = useForm();
|
|
134
|
-
|
|
135
|
-
const bandWithFromField = bandwidthKey
|
|
136
|
-
? get(model, bandwidthKey!) || schema.getInitialValue(bandwidthKey, {})
|
|
137
|
-
: undefined;
|
|
138
|
-
|
|
139
|
-
const usedBandwidth = bandwidth || bandWithFromField;
|
|
140
|
-
|
|
141
|
-
// Get value from org field if customerKey is set.
|
|
142
|
-
const usedCustomerId = customerKey
|
|
143
|
-
? get(model, customerKey, 'nonExistingOrgToFilterEverything')
|
|
144
|
-
: customerId;
|
|
145
|
-
|
|
146
|
-
const getFilteredOptions = (optionsInput: Option[]): Option[] => {
|
|
147
|
-
// Remnant of the old logic in which much more filtering happened clientside, which is now done
|
|
148
|
-
// server-side by setting the required URL parameters.
|
|
149
|
-
|
|
150
|
-
// The 'uniqueItems' filter below should exclude options already chosen in other SubscriptionFields in the same parent Array.
|
|
151
|
-
// Although this partly relies on uniforms magic which will be reworked/replaced with pydantic-forms.
|
|
152
|
-
if (
|
|
153
|
-
parentName !== name &&
|
|
154
|
-
parent.fieldType === Array &&
|
|
155
|
-
// @ts-expect-error Parent field can have the uniqueItems boolean property but this is not part of JSONSchema6 type
|
|
156
|
-
// TODO: Figure out why this is so
|
|
157
|
-
parent.uniqueItems
|
|
158
|
-
) {
|
|
159
|
-
const allValues: string[] = get(model, parentName, []);
|
|
160
|
-
const chosenValues = allValues.filter(
|
|
161
|
-
(_item, index) =>
|
|
162
|
-
index.toString() !== nameArray[nameArray.length - 1],
|
|
163
|
-
);
|
|
164
|
-
|
|
165
|
-
return optionsInput.filter((option) =>
|
|
166
|
-
chosenValues.includes(option.value),
|
|
167
|
-
);
|
|
168
|
-
} else {
|
|
169
|
-
return optionsInput;
|
|
170
|
-
}
|
|
171
|
-
};
|
|
172
|
-
|
|
173
|
-
const excludeSubscriptionIds = excludedSubscriptionIds;
|
|
174
|
-
const portModes = toPortModes(visiblePortMode);
|
|
175
|
-
const {
|
|
176
|
-
refetch,
|
|
177
|
-
options: unfilteredOptions,
|
|
178
|
-
isFetching,
|
|
179
|
-
} = useGetSurfSubscriptionDropdownOptions(
|
|
180
|
-
tags,
|
|
181
|
-
statuses,
|
|
182
|
-
productIds,
|
|
183
|
-
excludeSubscriptionIds,
|
|
184
|
-
usedCustomerId,
|
|
185
|
-
portModes,
|
|
186
|
-
usedBandwidth,
|
|
187
|
-
);
|
|
188
|
-
|
|
189
|
-
const options = getFilteredOptions(unfilteredOptions);
|
|
190
|
-
|
|
191
|
-
const selectedValue = options.find(
|
|
192
|
-
(option: Option) => option.value === value,
|
|
193
|
-
);
|
|
194
|
-
|
|
195
|
-
const isDisabled = disabled || readOnly || isFetching;
|
|
196
|
-
|
|
197
|
-
return (
|
|
198
|
-
<EuiFlexItem css={subscriptionFieldStyling} grow={1}>
|
|
199
|
-
<section
|
|
200
|
-
{...filterDOMProps(props)}
|
|
201
|
-
className={`${className} subscription-field${
|
|
202
|
-
disabled ? '-disabled' : ''
|
|
203
|
-
}`}
|
|
204
|
-
>
|
|
205
|
-
<EuiFormRow
|
|
206
|
-
label={label}
|
|
207
|
-
labelAppend={<EuiText size="m">{description}</EuiText>}
|
|
208
|
-
error={showInlineError ? errorMessage : false}
|
|
209
|
-
isInvalid={error}
|
|
210
|
-
id={id}
|
|
211
|
-
fullWidth
|
|
212
|
-
>
|
|
213
|
-
<div>
|
|
214
|
-
{!disabled && (
|
|
215
|
-
<EuiFlexGroup
|
|
216
|
-
alignItems={'center'}
|
|
217
|
-
gutterSize={'none'}
|
|
218
|
-
responsive={false}
|
|
219
|
-
>
|
|
220
|
-
<EuiButtonIcon
|
|
221
|
-
className="reload-subscriptions-icon-button"
|
|
222
|
-
id={`refresh-icon-${id}`}
|
|
223
|
-
iconType="refresh"
|
|
224
|
-
iconSize="l"
|
|
225
|
-
disabled={isDisabled}
|
|
226
|
-
onClick={() => {
|
|
227
|
-
if (isDisabled) {
|
|
228
|
-
refetch();
|
|
229
|
-
}
|
|
230
|
-
}}
|
|
231
|
-
/>
|
|
232
|
-
</EuiFlexGroup>
|
|
233
|
-
)}
|
|
234
|
-
<ReactSelect<Option, false>
|
|
235
|
-
id={id}
|
|
236
|
-
inputId={`${id}.search`}
|
|
237
|
-
name={name}
|
|
238
|
-
onChange={(option) => {
|
|
239
|
-
onChange(option?.value);
|
|
240
|
-
}}
|
|
241
|
-
options={options}
|
|
242
|
-
value={selectedValue}
|
|
243
|
-
isSearchable={true}
|
|
244
|
-
isClearable={false}
|
|
245
|
-
placeholder={
|
|
246
|
-
isFetching
|
|
247
|
-
? t('widgets.subscription.loading')
|
|
248
|
-
: t('widgets.subscription.placeholder')
|
|
249
|
-
}
|
|
250
|
-
isDisabled={isDisabled}
|
|
251
|
-
styles={reactSelectInnerComponentStyles}
|
|
252
|
-
className="subscription-field-select"
|
|
253
|
-
/>
|
|
254
|
-
</div>
|
|
255
|
-
</EuiFormRow>
|
|
256
|
-
</section>
|
|
257
|
-
</EuiFlexItem>
|
|
258
|
-
);
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
export const SubscriptionField = connectField(SubscriptionFieldDefinition, {
|
|
262
|
-
kind: 'leaf',
|
|
263
|
-
});
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { css } from '@emotion/react';
|
|
2
|
-
|
|
3
|
-
export const subscriptionFieldStyling = css`
|
|
4
|
-
.subscription-field {
|
|
5
|
-
> div {
|
|
6
|
-
display: flex;
|
|
7
|
-
|
|
8
|
-
.subscription-field-select {
|
|
9
|
-
width: 100%;
|
|
10
|
-
margin-left: 5px;
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
.euiFormRow > .euiFormRow__fieldWrapper > div {
|
|
15
|
-
display: flex;
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
// Setup sensible margins for port selectors
|
|
20
|
-
.subscription-field-disabled {
|
|
21
|
-
> div {
|
|
22
|
-
display: flex;
|
|
23
|
-
.subscription-field-select {
|
|
24
|
-
margin-left: 0px;
|
|
25
|
-
margin-top: 5px;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
.reload-subscriptions-icon-button {
|
|
31
|
-
margin-left: -7px;
|
|
32
|
-
}
|
|
33
|
-
`;
|
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Copyright 2019-2023 SURF.
|
|
3
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
-
* you may not use this file except in compliance with the License.
|
|
5
|
-
* You may obtain a copy of the License at
|
|
6
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
7
|
-
*
|
|
8
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
9
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
10
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
-
* See the License for the specific language governing permissions and
|
|
12
|
-
* limitations under the License.
|
|
13
|
-
*
|
|
14
|
-
*/
|
|
15
|
-
import React from 'react';
|
|
16
|
-
|
|
17
|
-
import moment, { Moment } from 'moment-timezone';
|
|
18
|
-
import { connectField, filterDOMProps } from 'uniforms';
|
|
19
|
-
|
|
20
|
-
import { EuiDatePicker, EuiFormRow, EuiText } from '@elastic/eui';
|
|
21
|
-
|
|
22
|
-
import { getCommonFormFieldStyles } from '@/components/WfoForms/formFields/commonStyles';
|
|
23
|
-
import { useWithOrchestratorTheme } from '@/hooks';
|
|
24
|
-
|
|
25
|
-
import { FieldProps } from '../types';
|
|
26
|
-
|
|
27
|
-
export function utcTimestampToLocalMoment(utc_timestamp: number) {
|
|
28
|
-
// Convert UTC timestamp to localized Moment object
|
|
29
|
-
return moment
|
|
30
|
-
.unix(utc_timestamp)
|
|
31
|
-
.tz(moment.tz.guess() ?? 'Europe/Amsterdam');
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export function localMomentToUtcTimestamp(local_moment: Moment) {
|
|
35
|
-
// Convert localized Moment object to UTC timestamp
|
|
36
|
-
return local_moment.unix();
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export type TimestampFieldProps = FieldProps<
|
|
40
|
-
number,
|
|
41
|
-
{
|
|
42
|
-
max?: number;
|
|
43
|
-
min?: number;
|
|
44
|
-
showTimeSelect: boolean;
|
|
45
|
-
locale?: string;
|
|
46
|
-
dateFormat?: string;
|
|
47
|
-
timeFormat?: string;
|
|
48
|
-
}
|
|
49
|
-
>;
|
|
50
|
-
|
|
51
|
-
function Timestamp({
|
|
52
|
-
disabled,
|
|
53
|
-
id,
|
|
54
|
-
label,
|
|
55
|
-
description,
|
|
56
|
-
max,
|
|
57
|
-
min,
|
|
58
|
-
showTimeSelect,
|
|
59
|
-
locale,
|
|
60
|
-
dateFormat,
|
|
61
|
-
timeFormat,
|
|
62
|
-
onChange,
|
|
63
|
-
value,
|
|
64
|
-
error,
|
|
65
|
-
showInlineError,
|
|
66
|
-
errorMessage,
|
|
67
|
-
...props
|
|
68
|
-
}: TimestampFieldProps) {
|
|
69
|
-
const { formRowStyle } = useWithOrchestratorTheme(getCommonFormFieldStyles);
|
|
70
|
-
|
|
71
|
-
return (
|
|
72
|
-
<div {...filterDOMProps(props)}>
|
|
73
|
-
<EuiFormRow
|
|
74
|
-
css={formRowStyle}
|
|
75
|
-
label={label}
|
|
76
|
-
labelAppend={<EuiText size="m">{description}</EuiText>}
|
|
77
|
-
error={showInlineError ? errorMessage : false}
|
|
78
|
-
isInvalid={error}
|
|
79
|
-
id={id}
|
|
80
|
-
fullWidth
|
|
81
|
-
>
|
|
82
|
-
<EuiDatePicker
|
|
83
|
-
disabled={disabled}
|
|
84
|
-
timeIntervals={15}
|
|
85
|
-
selected={value ? utcTimestampToLocalMoment(value) : null}
|
|
86
|
-
value={
|
|
87
|
-
value
|
|
88
|
-
? utcTimestampToLocalMoment(value).toLocaleString()
|
|
89
|
-
: undefined
|
|
90
|
-
}
|
|
91
|
-
onChange={(event) => {
|
|
92
|
-
onChange(
|
|
93
|
-
event
|
|
94
|
-
? localMomentToUtcTimestamp(event)
|
|
95
|
-
: undefined,
|
|
96
|
-
);
|
|
97
|
-
}}
|
|
98
|
-
showTimeSelect={showTimeSelect}
|
|
99
|
-
dateFormat={dateFormat ? dateFormat : undefined}
|
|
100
|
-
timeFormat={timeFormat ? timeFormat : undefined}
|
|
101
|
-
locale={locale ? locale : 'en-en'}
|
|
102
|
-
maxDate={max ? moment.unix(max) : undefined}
|
|
103
|
-
minDate={min ? moment.unix(min) : undefined}
|
|
104
|
-
/>
|
|
105
|
-
</EuiFormRow>
|
|
106
|
-
</div>
|
|
107
|
-
);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
export const TimestampField = connectField(Timestamp, { kind: 'leaf' });
|
|
@@ -1,300 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Copyright 2019-2023 SURF.
|
|
3
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
-
* you may not use this file except in compliance with the License.
|
|
5
|
-
* You may obtain a copy of the License at
|
|
6
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
7
|
-
*
|
|
8
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
9
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
10
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
-
* See the License for the specific language governing permissions and
|
|
12
|
-
* limitations under the License.
|
|
13
|
-
*
|
|
14
|
-
*/
|
|
15
|
-
import React, { useEffect, useState } from 'react';
|
|
16
|
-
|
|
17
|
-
import get from 'lodash/get';
|
|
18
|
-
import { useTranslations } from 'next-intl';
|
|
19
|
-
import { connectField, filterDOMProps, joinName, useForm } from 'uniforms';
|
|
20
|
-
|
|
21
|
-
import { EuiFieldText, EuiFormRow, EuiText } from '@elastic/eui';
|
|
22
|
-
|
|
23
|
-
import { useIsTaggedPort } from '@/hooks/deprecated/useIsTaggedPort';
|
|
24
|
-
import { useVlansByServicePortQuery } from '@/rtk/endpoints/formFields';
|
|
25
|
-
|
|
26
|
-
import { FieldProps } from '../types';
|
|
27
|
-
import { ServicePort } from './types';
|
|
28
|
-
|
|
29
|
-
function inValidVlan(vlan: string) {
|
|
30
|
-
const value = vlan || '0';
|
|
31
|
-
|
|
32
|
-
const stripped = value.toString().replace(/ /g, '');
|
|
33
|
-
return (
|
|
34
|
-
!/^\d{1,4}(?:-\d{1,4})?(?:,\d{1,4}(?:-\d{1,4})?)*$/.test(stripped) ||
|
|
35
|
-
stripped.split(',').some(inValidRange)
|
|
36
|
-
);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function inValidRange(range: string) {
|
|
40
|
-
if (range.indexOf('-') > -1) {
|
|
41
|
-
const ranges = range.split('-');
|
|
42
|
-
return (
|
|
43
|
-
ranges.some(inValidRange) ||
|
|
44
|
-
parseInt(ranges[0], 10) >= parseInt(ranges[1], 10)
|
|
45
|
-
);
|
|
46
|
-
}
|
|
47
|
-
return parseInt(range, 10) < 2 || parseInt(range, 10) > 4094;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function getAllNumbersForVlanRange(vlanRange?: string) {
|
|
51
|
-
if (!vlanRange) {
|
|
52
|
-
return [];
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
if (vlanRange !== '0' && inValidVlan(vlanRange)) {
|
|
56
|
-
//semantically invalid so we don't validate against the already used ports
|
|
57
|
-
return [];
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return numbersFromGroupedArray(
|
|
61
|
-
vlanRange
|
|
62
|
-
.replace(/ /g, '')
|
|
63
|
-
.split(',')
|
|
64
|
-
.map((sl) => sl.split('-').map(Number)),
|
|
65
|
-
);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function numbersFromGroupedArray(list: number[][]) {
|
|
69
|
-
return list.reduce((acc, boundaries) => {
|
|
70
|
-
const max = boundaries[boundaries.length - 1];
|
|
71
|
-
const min = boundaries[0];
|
|
72
|
-
return acc.concat(
|
|
73
|
-
Array.from(new Array(max - min + 1), (x, i) => min + i),
|
|
74
|
-
);
|
|
75
|
-
}, []);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function groupedArrayFromNumbers(numbers: number[]) {
|
|
79
|
-
numbers = [...numbers].sort((a, b) => a - b);
|
|
80
|
-
|
|
81
|
-
// Group by properly incrementing numbers
|
|
82
|
-
const groupedNumbers = numbers.reduce((r: number[][], n) => {
|
|
83
|
-
let lastSubArray: number[] = r[r.length - 1];
|
|
84
|
-
|
|
85
|
-
// Skip duplicates
|
|
86
|
-
if (lastSubArray && lastSubArray[lastSubArray.length - 1] === n) {
|
|
87
|
-
return r;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// If this is the first one or number is more than 1 difference make a new sub array
|
|
91
|
-
if (!lastSubArray || lastSubArray[lastSubArray.length - 1] !== n - 1) {
|
|
92
|
-
lastSubArray = [];
|
|
93
|
-
r.push(lastSubArray);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
lastSubArray.push(n);
|
|
97
|
-
|
|
98
|
-
return r;
|
|
99
|
-
}, []);
|
|
100
|
-
|
|
101
|
-
return groupedNumbers.map((l) =>
|
|
102
|
-
l[0] !== l[l.length - 1] ? [l[0], l[l.length - 1]] : [l[0]],
|
|
103
|
-
);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
function vlanRangeFromNumbers(list: number[]) {
|
|
107
|
-
return groupedArrayFromNumbers(list)
|
|
108
|
-
.map((sl) => sl.join('-'))
|
|
109
|
-
.join(',');
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
export type VlanFieldProps = FieldProps<
|
|
113
|
-
string,
|
|
114
|
-
{ subscriptionFieldName?: string; nsiVlansOnly?: boolean }
|
|
115
|
-
>;
|
|
116
|
-
|
|
117
|
-
export type VlanRange = [number, number];
|
|
118
|
-
|
|
119
|
-
function Vlan({
|
|
120
|
-
disabled,
|
|
121
|
-
id,
|
|
122
|
-
label,
|
|
123
|
-
description,
|
|
124
|
-
name,
|
|
125
|
-
onChange,
|
|
126
|
-
readOnly,
|
|
127
|
-
value,
|
|
128
|
-
error,
|
|
129
|
-
showInlineError,
|
|
130
|
-
errorMessage,
|
|
131
|
-
subscriptionFieldName = 'subscription_id',
|
|
132
|
-
nsiVlansOnly = false,
|
|
133
|
-
...props
|
|
134
|
-
}: VlanFieldProps) {
|
|
135
|
-
const t = useTranslations('pydanticForms');
|
|
136
|
-
|
|
137
|
-
const { model, schema } = useForm();
|
|
138
|
-
const initialValue = schema.getInitialValue(name, {});
|
|
139
|
-
const nameArray = joinName(null, name);
|
|
140
|
-
const selfName = nameArray.slice(-1);
|
|
141
|
-
const subscriptionIdFieldName = joinName(
|
|
142
|
-
nameArray.slice(0, -1),
|
|
143
|
-
subscriptionFieldName,
|
|
144
|
-
);
|
|
145
|
-
const completeListFieldName = joinName(nameArray.slice(0, -2));
|
|
146
|
-
const subscriptionId = get(model, subscriptionIdFieldName);
|
|
147
|
-
const [isFetched, portIsTagged] = useIsTaggedPort(subscriptionId);
|
|
148
|
-
|
|
149
|
-
const completeList: ServicePort[] = get(model, completeListFieldName) || [];
|
|
150
|
-
const extraUsedVlans = completeList
|
|
151
|
-
.filter(
|
|
152
|
-
(_item, index) => index.toString() !== nameArray.slice(-2, -1)[0],
|
|
153
|
-
)
|
|
154
|
-
.filter((item) => get(item, subscriptionFieldName) === subscriptionId)
|
|
155
|
-
.map((item) => get(item, selfName))
|
|
156
|
-
.join(',');
|
|
157
|
-
|
|
158
|
-
useEffect(() => {
|
|
159
|
-
if (subscriptionId && isFetched && !portIsTagged && value !== '0') {
|
|
160
|
-
onChange('0');
|
|
161
|
-
} else if (
|
|
162
|
-
!disabled &&
|
|
163
|
-
((!subscriptionId && value !== '') ||
|
|
164
|
-
(subscriptionId && portIsTagged && value === '0'))
|
|
165
|
-
) {
|
|
166
|
-
onChange('');
|
|
167
|
-
}
|
|
168
|
-
// Adding the missing dependencies to the dependency array leads to an infinite loop
|
|
169
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
170
|
-
}, [onChange, subscriptionId, isFetched, portIsTagged]);
|
|
171
|
-
|
|
172
|
-
const [usedVlansInIms, setUsedVlansInIms] = useState<VlanRange[]>([]);
|
|
173
|
-
const [missingInIms, setMissingInIms] = useState(false);
|
|
174
|
-
const {
|
|
175
|
-
data,
|
|
176
|
-
isFetching: isLoading,
|
|
177
|
-
error: fetchError,
|
|
178
|
-
} = useVlansByServicePortQuery(
|
|
179
|
-
{ subscriptionId, nsiVlansOnly },
|
|
180
|
-
{
|
|
181
|
-
skip: !subscriptionId,
|
|
182
|
-
},
|
|
183
|
-
);
|
|
184
|
-
|
|
185
|
-
useEffect(() => {
|
|
186
|
-
if (data) {
|
|
187
|
-
setUsedVlansInIms(data);
|
|
188
|
-
setMissingInIms(false);
|
|
189
|
-
}
|
|
190
|
-
if (fetchError) {
|
|
191
|
-
console.error(fetchError);
|
|
192
|
-
setMissingInIms(true);
|
|
193
|
-
setUsedVlansInIms([]);
|
|
194
|
-
}
|
|
195
|
-
}, [data, fetchError]);
|
|
196
|
-
|
|
197
|
-
// Filter currently used vlans because they are probably from the current subscription
|
|
198
|
-
const currentVlans = getAllNumbersForVlanRange(initialValue);
|
|
199
|
-
const usedVlans = numbersFromGroupedArray(usedVlansInIms).filter(
|
|
200
|
-
(n) => !currentVlans.includes(n),
|
|
201
|
-
);
|
|
202
|
-
const allUsedVlans = usedVlans
|
|
203
|
-
.concat(getAllNumbersForVlanRange(extraUsedVlans))
|
|
204
|
-
.sort();
|
|
205
|
-
const validFormat = !value || value === '0' || !inValidVlan(value);
|
|
206
|
-
// Don't validate if disabled (untagged shows as disable but needs validation)
|
|
207
|
-
const vlansInUse =
|
|
208
|
-
validFormat && !disabled
|
|
209
|
-
? getAllNumbersForVlanRange(value).filter((num) =>
|
|
210
|
-
allUsedVlans.includes(num),
|
|
211
|
-
)
|
|
212
|
-
: [];
|
|
213
|
-
|
|
214
|
-
const placeholder = isLoading
|
|
215
|
-
? t('widgets.vlan.loadingIms')
|
|
216
|
-
: subscriptionId
|
|
217
|
-
? t('widgets.vlan.placeholder')
|
|
218
|
-
: t('widgets.vlan.placeholderNoServicePort');
|
|
219
|
-
|
|
220
|
-
const errorMessageExtra = missingInIms
|
|
221
|
-
? t('widgets.vlan.missingInIms')
|
|
222
|
-
: !validFormat
|
|
223
|
-
? t('widgets.vlan.invalidVlan')
|
|
224
|
-
: vlansInUse.length
|
|
225
|
-
? vlansInUse.length >= 1 && vlansInUse[0] === 0
|
|
226
|
-
? t('widgets.vlan.untaggedPortInUse')
|
|
227
|
-
: t('widgets.vlan.vlansInUseError', {
|
|
228
|
-
vlans: vlanRangeFromNumbers(vlansInUse),
|
|
229
|
-
})
|
|
230
|
-
: undefined;
|
|
231
|
-
|
|
232
|
-
let message = '';
|
|
233
|
-
if (!isLoading && subscriptionId) {
|
|
234
|
-
if (portIsTagged && nsiVlansOnly) {
|
|
235
|
-
const initialUsedVlans = schema
|
|
236
|
-
.getInitialValue('service_ports', {})
|
|
237
|
-
.map((sp: { vlan: string }) => sp.vlan);
|
|
238
|
-
const currentUsedVlans = initialUsedVlans.filter(
|
|
239
|
-
(vlan: string) => vlan !== value && vlan !== extraUsedVlans,
|
|
240
|
-
);
|
|
241
|
-
|
|
242
|
-
const allAvailableVlans = [
|
|
243
|
-
...usedVlans.filter(
|
|
244
|
-
(number) =>
|
|
245
|
-
!getAllNumbersForVlanRange(extraUsedVlans).includes(
|
|
246
|
-
number,
|
|
247
|
-
),
|
|
248
|
-
),
|
|
249
|
-
...currentUsedVlans,
|
|
250
|
-
].sort();
|
|
251
|
-
|
|
252
|
-
message = !allAvailableVlans.length
|
|
253
|
-
? t('widgets.vlan.nsiNoPortsAvailable')
|
|
254
|
-
: t('widgets.vlan.nsiVlansAvailable', {
|
|
255
|
-
vlans: vlanRangeFromNumbers(allAvailableVlans),
|
|
256
|
-
});
|
|
257
|
-
} else if (portIsTagged) {
|
|
258
|
-
message = !allUsedVlans.length
|
|
259
|
-
? t('widgets.vlan.allPortsAvailable')
|
|
260
|
-
: t('widgets.vlan.vlansInUse', {
|
|
261
|
-
vlans: vlanRangeFromNumbers(allUsedVlans),
|
|
262
|
-
});
|
|
263
|
-
} else {
|
|
264
|
-
message = t('widgets.vlan.taggedOnly');
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
return (
|
|
269
|
-
<section {...filterDOMProps(props)}>
|
|
270
|
-
<EuiFormRow
|
|
271
|
-
label={label}
|
|
272
|
-
labelAppend={<EuiText size="m">{description}</EuiText>}
|
|
273
|
-
error={
|
|
274
|
-
(error || errorMessageExtra) && showInlineError
|
|
275
|
-
? errorMessage || errorMessageExtra
|
|
276
|
-
: false
|
|
277
|
-
}
|
|
278
|
-
isInvalid={error}
|
|
279
|
-
helpText={message}
|
|
280
|
-
id={id}
|
|
281
|
-
fullWidth
|
|
282
|
-
>
|
|
283
|
-
<EuiFieldText
|
|
284
|
-
isLoading={isLoading}
|
|
285
|
-
fullWidth
|
|
286
|
-
disabled={!subscriptionId || disabled || !portIsTagged}
|
|
287
|
-
name={name}
|
|
288
|
-
isInvalid={error}
|
|
289
|
-
onChange={(event) => onChange(event.target.value)}
|
|
290
|
-
placeholder={placeholder}
|
|
291
|
-
readOnly={readOnly}
|
|
292
|
-
type="text"
|
|
293
|
-
value={value ?? ''}
|
|
294
|
-
/>
|
|
295
|
-
</EuiFormRow>
|
|
296
|
-
</section>
|
|
297
|
-
);
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
export const VlanField = connectField(Vlan, { kind: 'leaf' });
|