@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.
Files changed (101) hide show
  1. package/.turbo/turbo-build.log +8 -8
  2. package/.turbo/turbo-lint.log +5 -2
  3. package/.turbo/turbo-test.log +9 -10
  4. package/CHANGELOG.md +19 -0
  5. package/dist/index.d.ts +164 -4306
  6. package/dist/index.js +1739 -5747
  7. package/dist/index.js.map +1 -1
  8. package/package.json +4 -8
  9. package/src/components/WfoPydanticForm/Footer.tsx +33 -6
  10. package/src/components/WfoPydanticForm/RenderFormErrors.tsx +2 -1
  11. package/src/components/WfoPydanticForm/Row.tsx +3 -1
  12. package/src/components/WfoPydanticForm/fields/WfoInteger.tsx +5 -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/WfoSearchPage/WfoSearch/WfoSearch.tsx +42 -54
  16. package/src/components/WfoSearchPage/utils.ts +9 -1
  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/hooks/useSearch.ts +12 -2
  23. package/src/hooks/useSearchPagination.ts +4 -0
  24. package/src/messages/en-GB.json +5 -3
  25. package/src/pages/processes/WfoStartProcessPage.tsx +5 -107
  26. package/src/rtk/endpoints/index.ts +0 -2
  27. package/src/rtk/endpoints/search.ts +13 -4
  28. package/src/types/index.ts +0 -1
  29. package/src/types/search.ts +7 -0
  30. package/src/types/types.ts +0 -1
  31. package/src/components/WfoForms/AutoFieldLoader.tsx +0 -118
  32. package/src/components/WfoForms/AutoFields.tsx +0 -49
  33. package/src/components/WfoForms/CreateForm.tsx +0 -75
  34. package/src/components/WfoForms/UserInputForm.tsx +0 -697
  35. package/src/components/WfoForms/UserInputFormStyling.ts +0 -80
  36. package/src/components/WfoForms/UserInputFormWizard.tsx +0 -127
  37. package/src/components/WfoForms/formFields/AcceptField.tsx +0 -243
  38. package/src/components/WfoForms/formFields/AcceptFieldStyling.ts +0 -35
  39. package/src/components/WfoForms/formFields/BoolField.tsx +0 -77
  40. package/src/components/WfoForms/formFields/BoolFieldStyling.ts +0 -64
  41. package/src/components/WfoForms/formFields/ConnectedSelectField.tsx +0 -19
  42. package/src/components/WfoForms/formFields/CustomerField.tsx +0 -77
  43. package/src/components/WfoForms/formFields/DateField.tsx +0 -72
  44. package/src/components/WfoForms/formFields/DividerField.tsx +0 -29
  45. package/src/components/WfoForms/formFields/ErrorField.tsx +0 -40
  46. package/src/components/WfoForms/formFields/ErrorsField.tsx +0 -34
  47. package/src/components/WfoForms/formFields/LabelField.tsx +0 -43
  48. package/src/components/WfoForms/formFields/ListAddField.tsx +0 -95
  49. package/src/components/WfoForms/formFields/ListDelField.tsx +0 -95
  50. package/src/components/WfoForms/formFields/ListField.tsx +0 -117
  51. package/src/components/WfoForms/formFields/ListItemField.tsx +0 -40
  52. package/src/components/WfoForms/formFields/ListSelectField.tsx +0 -95
  53. package/src/components/WfoForms/formFields/LocationCodeField.tsx +0 -60
  54. package/src/components/WfoForms/formFields/LongTextField.tsx +0 -68
  55. package/src/components/WfoForms/formFields/NestField.tsx +0 -107
  56. package/src/components/WfoForms/formFields/NumField.tsx +0 -85
  57. package/src/components/WfoForms/formFields/OptGroupField.tsx +0 -74
  58. package/src/components/WfoForms/formFields/RadioField.tsx +0 -87
  59. package/src/components/WfoForms/formFields/SelectField/SelectField.tsx +0 -177
  60. package/src/components/WfoForms/formFields/SelectField/index.ts +0 -1
  61. package/src/components/WfoForms/formFields/SelectField/styles.ts +0 -52
  62. package/src/components/WfoForms/formFields/SubmitField.tsx +0 -50
  63. package/src/components/WfoForms/formFields/SubscriptionSummaryField.tsx +0 -74
  64. package/src/components/WfoForms/formFields/SummaryField.tsx +0 -104
  65. package/src/components/WfoForms/formFields/SummaryFieldStyling.ts +0 -44
  66. package/src/components/WfoForms/formFields/TextField.tsx +0 -81
  67. package/src/components/WfoForms/formFields/commonStyles.ts +0 -32
  68. package/src/components/WfoForms/formFields/deprecated/ContactPersonAutocomplete.tsx +0 -99
  69. package/src/components/WfoForms/formFields/deprecated/ContactPersonAutocompleteStyles.ts +0 -41
  70. package/src/components/WfoForms/formFields/deprecated/ContactPersonNameField.tsx +0 -263
  71. package/src/components/WfoForms/formFields/deprecated/FileUploadField.tsx +0 -151
  72. package/src/components/WfoForms/formFields/deprecated/ImsNodeIdField.tsx +0 -109
  73. package/src/components/WfoForms/formFields/deprecated/ImsPortIdField.tsx +0 -233
  74. package/src/components/WfoForms/formFields/deprecated/ImsPortIdFieldStyling.ts +0 -17
  75. package/src/components/WfoForms/formFields/deprecated/IpNetworkField.tsx +0 -105
  76. package/src/components/WfoForms/formFields/deprecated/IpPrefixTableField.tsx +0 -390
  77. package/src/components/WfoForms/formFields/deprecated/IpPrefixTableFieldStyling.ts +0 -117
  78. package/src/components/WfoForms/formFields/deprecated/SplitPrefix.tsx +0 -138
  79. package/src/components/WfoForms/formFields/deprecated/SplitPrefixStyling.ts +0 -11
  80. package/src/components/WfoForms/formFields/deprecated/SubscriptionField.tsx +0 -263
  81. package/src/components/WfoForms/formFields/deprecated/SubscriptionFieldStyling.ts +0 -33
  82. package/src/components/WfoForms/formFields/deprecated/TimestampField.tsx +0 -110
  83. package/src/components/WfoForms/formFields/deprecated/VlanField.tsx +0 -300
  84. package/src/components/WfoForms/formFields/deprecated/index.ts +0 -15
  85. package/src/components/WfoForms/formFields/deprecated/types.ts +0 -74
  86. package/src/components/WfoForms/formFields/deprecated/utils.ts +0 -1
  87. package/src/components/WfoForms/formFields/index.ts +0 -30
  88. package/src/components/WfoForms/formFields/listFieldStyling.ts +0 -86
  89. package/src/components/WfoForms/formFields/types.ts +0 -41
  90. package/src/components/WfoForms/formFields/utils.spec.ts +0 -296
  91. package/src/components/WfoForms/formFields/utils.ts +0 -69
  92. package/src/components/WfoForms/index.ts +0 -5
  93. package/src/components/WfoWorkflowSteps/WfoStep/WfoStepFormOld.tsx +0 -67
  94. package/src/hooks/deprecated/useGetSurfSubcriptionDropdownOptions.ts +0 -37
  95. package/src/hooks/deprecated/useIsTaggedPort.ts +0 -25
  96. package/src/rtk/endpoints/deprecated/index.ts +0 -1
  97. package/src/rtk/endpoints/deprecated/surfSubscriptionDropdownOptions.ts +0 -53
  98. package/src/rtk/endpoints/formFields.ts +0 -131
  99. package/src/rtk/endpoints/ipam.ts +0 -54
  100. package/src/types/deprecated/SurfSubscriptionDropdownOptionsFilterParams.ts +0 -10
  101. 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' });