@orchestrator-ui/orchestrator-ui-components 6.7.8 → 7.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/.turbo/turbo-build.log +8 -8
  2. package/.turbo/turbo-lint.log +1 -1
  3. package/.turbo/turbo-test.log +10 -11
  4. package/CHANGELOG.md +16 -0
  5. package/dist/index.d.ts +180 -4325
  6. package/dist/index.js +1774 -5741
  7. package/dist/index.js.map +1 -1
  8. package/package.json +4 -8
  9. package/src/components/WfoPageTemplate/WfoPageTemplate/WfoPageTemplate.tsx +9 -1
  10. package/src/components/WfoPageTemplate/WfoSidebar/WfoSidebar.tsx +9 -2
  11. package/src/components/WfoPydanticForm/RenderFormErrors.tsx +2 -1
  12. package/src/components/WfoPydanticForm/Row.tsx +3 -1
  13. package/src/components/WfoPydanticForm/fields/WfoSummary.tsx +3 -5
  14. package/src/components/WfoPydanticForm/fields/styles.ts +72 -0
  15. package/src/components/WfoStartButton/WfoStartButtonComboBox.tsx +73 -3
  16. package/src/components/WfoStartButton/WfoStartWorkflowComboBox.tsx +15 -3
  17. package/src/components/WfoWorkflowSteps/WfoStep/WfoStep.tsx +10 -27
  18. package/src/components/WfoWorkflowSteps/WfoStep/WfoStepForm.tsx +1 -1
  19. package/src/components/index.ts +0 -1
  20. package/src/configuration/version.ts +1 -1
  21. package/src/contexts/OrchestratorConfigContext.tsx +0 -1
  22. package/src/pages/processes/WfoStartProcessPage.tsx +5 -107
  23. package/src/rtk/endpoints/index.ts +0 -2
  24. package/src/rtk/endpoints/startOptions.ts +23 -10
  25. package/src/types/index.ts +0 -1
  26. package/src/types/types.ts +0 -1
  27. package/src/components/WfoForms/AutoFieldLoader.tsx +0 -118
  28. package/src/components/WfoForms/AutoFields.tsx +0 -49
  29. package/src/components/WfoForms/CreateForm.tsx +0 -75
  30. package/src/components/WfoForms/UserInputForm.tsx +0 -697
  31. package/src/components/WfoForms/UserInputFormStyling.ts +0 -80
  32. package/src/components/WfoForms/UserInputFormWizard.tsx +0 -127
  33. package/src/components/WfoForms/formFields/AcceptField.tsx +0 -243
  34. package/src/components/WfoForms/formFields/AcceptFieldStyling.ts +0 -35
  35. package/src/components/WfoForms/formFields/BoolField.tsx +0 -77
  36. package/src/components/WfoForms/formFields/BoolFieldStyling.ts +0 -64
  37. package/src/components/WfoForms/formFields/ConnectedSelectField.tsx +0 -19
  38. package/src/components/WfoForms/formFields/CustomerField.tsx +0 -77
  39. package/src/components/WfoForms/formFields/DateField.tsx +0 -72
  40. package/src/components/WfoForms/formFields/DividerField.tsx +0 -29
  41. package/src/components/WfoForms/formFields/ErrorField.tsx +0 -40
  42. package/src/components/WfoForms/formFields/ErrorsField.tsx +0 -34
  43. package/src/components/WfoForms/formFields/LabelField.tsx +0 -43
  44. package/src/components/WfoForms/formFields/ListAddField.tsx +0 -95
  45. package/src/components/WfoForms/formFields/ListDelField.tsx +0 -95
  46. package/src/components/WfoForms/formFields/ListField.tsx +0 -117
  47. package/src/components/WfoForms/formFields/ListItemField.tsx +0 -40
  48. package/src/components/WfoForms/formFields/ListSelectField.tsx +0 -95
  49. package/src/components/WfoForms/formFields/LocationCodeField.tsx +0 -60
  50. package/src/components/WfoForms/formFields/LongTextField.tsx +0 -68
  51. package/src/components/WfoForms/formFields/NestField.tsx +0 -107
  52. package/src/components/WfoForms/formFields/NumField.tsx +0 -85
  53. package/src/components/WfoForms/formFields/OptGroupField.tsx +0 -74
  54. package/src/components/WfoForms/formFields/RadioField.tsx +0 -87
  55. package/src/components/WfoForms/formFields/SelectField/SelectField.tsx +0 -177
  56. package/src/components/WfoForms/formFields/SelectField/index.ts +0 -1
  57. package/src/components/WfoForms/formFields/SelectField/styles.ts +0 -52
  58. package/src/components/WfoForms/formFields/SubmitField.tsx +0 -50
  59. package/src/components/WfoForms/formFields/SubscriptionSummaryField.tsx +0 -74
  60. package/src/components/WfoForms/formFields/SummaryField.tsx +0 -104
  61. package/src/components/WfoForms/formFields/SummaryFieldStyling.ts +0 -44
  62. package/src/components/WfoForms/formFields/TextField.tsx +0 -81
  63. package/src/components/WfoForms/formFields/commonStyles.ts +0 -32
  64. package/src/components/WfoForms/formFields/deprecated/ContactPersonAutocomplete.tsx +0 -99
  65. package/src/components/WfoForms/formFields/deprecated/ContactPersonAutocompleteStyles.ts +0 -41
  66. package/src/components/WfoForms/formFields/deprecated/ContactPersonNameField.tsx +0 -263
  67. package/src/components/WfoForms/formFields/deprecated/FileUploadField.tsx +0 -151
  68. package/src/components/WfoForms/formFields/deprecated/ImsNodeIdField.tsx +0 -109
  69. package/src/components/WfoForms/formFields/deprecated/ImsPortIdField.tsx +0 -233
  70. package/src/components/WfoForms/formFields/deprecated/ImsPortIdFieldStyling.ts +0 -17
  71. package/src/components/WfoForms/formFields/deprecated/IpNetworkField.tsx +0 -105
  72. package/src/components/WfoForms/formFields/deprecated/IpPrefixTableField.tsx +0 -390
  73. package/src/components/WfoForms/formFields/deprecated/IpPrefixTableFieldStyling.ts +0 -117
  74. package/src/components/WfoForms/formFields/deprecated/SplitPrefix.tsx +0 -138
  75. package/src/components/WfoForms/formFields/deprecated/SplitPrefixStyling.ts +0 -11
  76. package/src/components/WfoForms/formFields/deprecated/SubscriptionField.tsx +0 -263
  77. package/src/components/WfoForms/formFields/deprecated/SubscriptionFieldStyling.ts +0 -33
  78. package/src/components/WfoForms/formFields/deprecated/TimestampField.tsx +0 -110
  79. package/src/components/WfoForms/formFields/deprecated/VlanField.tsx +0 -300
  80. package/src/components/WfoForms/formFields/deprecated/index.ts +0 -15
  81. package/src/components/WfoForms/formFields/deprecated/types.ts +0 -74
  82. package/src/components/WfoForms/formFields/deprecated/utils.ts +0 -1
  83. package/src/components/WfoForms/formFields/index.ts +0 -30
  84. package/src/components/WfoForms/formFields/listFieldStyling.ts +0 -86
  85. package/src/components/WfoForms/formFields/types.ts +0 -41
  86. package/src/components/WfoForms/formFields/utils.spec.ts +0 -296
  87. package/src/components/WfoForms/formFields/utils.ts +0 -69
  88. package/src/components/WfoForms/index.ts +0 -5
  89. package/src/components/WfoWorkflowSteps/WfoStep/WfoStepFormOld.tsx +0 -67
  90. package/src/hooks/deprecated/useGetSurfSubcriptionDropdownOptions.ts +0 -37
  91. package/src/hooks/deprecated/useIsTaggedPort.ts +0 -25
  92. package/src/rtk/endpoints/deprecated/index.ts +0 -1
  93. package/src/rtk/endpoints/deprecated/surfSubscriptionDropdownOptions.ts +0 -53
  94. package/src/rtk/endpoints/formFields.ts +0 -131
  95. package/src/rtk/endpoints/ipam.ts +0 -54
  96. package/src/types/deprecated/SurfSubscriptionDropdownOptionsFilterParams.ts +0 -10
  97. 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' });