Wizard: Show error with duplicated values

This show an error when user imports duplicate values to a `<LabelInput>` field.
This commit is contained in:
regexowl 2025-06-26 16:04:42 +02:00 committed by Klara Simickova
parent a92d087014
commit bb9c5620ee
3 changed files with 136 additions and 25 deletions

View file

@ -48,30 +48,36 @@ const LabelInput = ({
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const [inputValue, setInputValue] = useState(''); const [inputValue, setInputValue] = useState('');
const [errorText, setErrorText] = useState(stepValidation.errors[fieldName]); const [onStepInputErrorText, setOnStepInputErrorText] = useState('');
let [invalidImports, duplicateImports] = ['', ''];
if (stepValidation.errors[fieldName]) {
[invalidImports, duplicateImports] =
stepValidation.errors[fieldName].split('|');
}
const onTextInputChange = ( const onTextInputChange = (
_event: React.FormEvent<HTMLInputElement>, _event: React.FormEvent<HTMLInputElement>,
value: string value: string
) => { ) => {
setInputValue(value); setInputValue(value);
setErrorText(''); setOnStepInputErrorText('');
}; };
const addItem = (value: string) => { const addItem = (value: string) => {
if (list?.includes(value) || requiredList?.includes(value)) { if (list?.includes(value) || requiredList?.includes(value)) {
setErrorText(`${item} already exists.`); setOnStepInputErrorText(`${item} already exists.`);
return; return;
} }
if (!validator(value)) { if (!validator(value)) {
setErrorText('Invalid format.'); setOnStepInputErrorText('Invalid format.');
return; return;
} }
dispatch(addAction(value)); dispatch(addAction(value));
setInputValue(''); setInputValue('');
setErrorText(''); setOnStepInputErrorText('');
}; };
const handleKeyDown = (e: React.KeyboardEvent, value: string) => { const handleKeyDown = (e: React.KeyboardEvent, value: string) => {
@ -87,14 +93,18 @@ const LabelInput = ({
const handleRemoveItem = (e: React.MouseEvent, value: string) => { const handleRemoveItem = (e: React.MouseEvent, value: string) => {
dispatch(removeAction(value)); dispatch(removeAction(value));
setErrorText('');
}; };
const handleClear = () => { const handleClear = () => {
setInputValue(''); setInputValue('');
setErrorText(''); setOnStepInputErrorText('');
}; };
const errors = [];
if (onStepInputErrorText) errors.push(onStepInputErrorText);
if (invalidImports) errors.push(invalidImports);
if (duplicateImports) errors.push(duplicateImports);
return ( return (
<> <>
<TextInputGroup> <TextInputGroup>
@ -125,9 +135,13 @@ const LabelInput = ({
/> />
</TextInputGroupUtilities> </TextInputGroupUtilities>
</TextInputGroup> </TextInputGroup>
{errorText && ( {errors.length > 0 && (
<HelperText> <HelperText>
<HelperTextItem variant={'error'}>{errorText}</HelperTextItem> {errors.map((error, index) => (
<HelperTextItem key={index} variant={'error'}>
{error}
</HelperTextItem>
))}
</HelperText> </HelperText>
)} )}
{requiredList && requiredList.length > 0 && ( {requiredList && requiredList.length > 0 && (

View file

@ -0,0 +1,6 @@
export const getListOfDuplicates = (list: string[]) => {
const duplicates = list.filter((item, index) => list.indexOf(item) !== index);
const uniqueDuplicates = [...new Set(duplicates)];
return uniqueDuplicates;
};

View file

@ -3,6 +3,8 @@ import React, { useEffect, useState } from 'react';
import { CheckCircleIcon } from '@patternfly/react-icons'; import { CheckCircleIcon } from '@patternfly/react-icons';
import { jwtDecode, JwtPayload } from 'jwt-decode'; import { jwtDecode, JwtPayload } from 'jwt-decode';
import { getListOfDuplicates } from './getListOfDuplicates';
import { UNIQUE_VALIDATION_DELAY } from '../../../constants'; import { UNIQUE_VALIDATION_DELAY } from '../../../constants';
import { useLazyGetBlueprintsQuery } from '../../../store/backendApi'; import { useLazyGetBlueprintsQuery } from '../../../store/backendApi';
import { useAppSelector } from '../../../store/hooks'; import { useAppSelector } from '../../../store/hooks';
@ -281,14 +283,26 @@ export function useTimezoneValidation(): StepValidation {
} }
} }
const duplicateNtpServers = getListOfDuplicates(ntpServers || []);
const timezoneError = const timezoneError =
timezone && !timezones.includes(timezone) ? 'Unknown timezone' : ''; timezone && !timezones.includes(timezone) ? 'Unknown timezone' : '';
const ntpServersError = const ntpServersError =
invalidServers.length > 0 ? `Invalid NTP servers: ${invalidServers}` : ''; invalidServers.length > 0 ? `Invalid NTP servers: ${invalidServers}` : '';
const duplicateNtpServersError =
duplicateNtpServers.length > 0
? `Includes duplicate NTP servers: ${duplicateNtpServers.join(', ')}`
: '';
return { return {
errors: { timezone: timezoneError, ntpServers: ntpServersError }, errors: {
disabledNext: timezoneError !== '' || invalidServers.length > 0, timezone: timezoneError,
ntpServers: ntpServersError + '|' + duplicateNtpServersError,
},
disabledNext:
timezoneError !== '' ||
invalidServers.length > 0 ||
duplicateNtpServers.length > 0,
}; };
} }
@ -363,15 +377,28 @@ export function useKernelValidation(): StepValidation {
} }
} }
const duplicateKernelArgs = getListOfDuplicates(kernel.append);
const kernelNameError = const kernelNameError =
kernel.name && !isKernelNameValid(kernel.name) ? 'Invalid format.' : ''; kernel.name && !isKernelNameValid(kernel.name) ? 'Invalid format.' : '';
const kernelAppendError = const kernelAppendError =
invalidArgs.length > 0 ? `Invalid kernel arguments: ${invalidArgs}` : ''; invalidArgs.length > 0 ? `Invalid kernel arguments: ${invalidArgs}` : '';
const duplicateKernelArgsError =
duplicateKernelArgs.length > 0
? `Includes duplicate kernel arguments: ${duplicateKernelArgs.join(', ')}`
: '';
return { return {
errors: { kernel: kernelNameError, kernelAppend: kernelAppendError }, errors: {
disabledNext: kernelNameError !== '' || kernelAppendError !== '', kernel: kernelNameError,
kernelAppend: kernelAppendError + '|' + duplicateKernelArgsError,
},
disabledNext:
kernelNameError !== '' ||
kernelAppendError !== '' ||
duplicateKernelArgs.length > 0,
}; };
} }
@ -405,8 +432,32 @@ export function useFirewallValidation(): StepValidation {
} }
} }
const duplicatePorts = getListOfDuplicates(firewall.ports);
const duplicateDisabledServices = getListOfDuplicates(
firewall.services.disabled
);
const duplicateEnabledServices = getListOfDuplicates(
firewall.services.enabled
);
const portsError = const portsError =
invalidPorts.length > 0 ? `Invalid ports: ${invalidPorts}` : ''; invalidPorts.length > 0 ? `Invalid ports: ${invalidPorts}` : '';
const duplicatePortsError =
duplicatePorts.length > 0
? `Includes duplicate ports: ${duplicatePorts.join(', ')}`
: '';
const duplicateDisabledServicesError =
duplicateDisabledServices.length > 0
? `Includes duplicate disabled services: ${duplicateDisabledServices.join(
', '
)}`
: '';
const duplicateEnabledServicesError =
duplicateEnabledServices.length > 0
? `Includes duplicate enabled services: ${duplicateEnabledServices.join(
', '
)}`
: '';
const disabledServicesError = const disabledServicesError =
invalidDisabled.length > 0 invalidDisabled.length > 0
? `Invalid disabled services: ${invalidDisabled}` ? `Invalid disabled services: ${invalidDisabled}`
@ -418,14 +469,19 @@ export function useFirewallValidation(): StepValidation {
return { return {
errors: { errors: {
ports: portsError, ports: portsError + '|' + duplicatePortsError,
disabledServices: disabledServicesError, disabledServices:
enabledServices: enabledServicesError, disabledServicesError + '|' + duplicateDisabledServicesError,
enabledServices:
enabledServicesError + '|' + duplicateEnabledServicesError,
}, },
disabledNext: disabledNext:
invalidPorts.length > 0 || invalidPorts.length > 0 ||
invalidDisabled.length > 0 || invalidDisabled.length > 0 ||
invalidEnabled.length > 0, invalidEnabled.length > 0 ||
duplicatePorts.length > 0 ||
duplicateDisabledServices.length > 0 ||
duplicateEnabledServices.length > 0,
}; };
} }
@ -460,6 +516,10 @@ export function useServicesValidation(): StepValidation {
} }
} }
const duplicateDisabledServices = getListOfDuplicates(services.disabled);
const duplicateMaskedServices = getListOfDuplicates(services.masked);
const duplicateEnabledServices = getListOfDuplicates(services.enabled);
const disabledSystemdServicesError = const disabledSystemdServicesError =
invalidDisabled.length > 0 invalidDisabled.length > 0
? `Invalid disabled services: ${invalidDisabled}` ? `Invalid disabled services: ${invalidDisabled}`
@ -470,17 +530,41 @@ export function useServicesValidation(): StepValidation {
invalidEnabled.length > 0 invalidEnabled.length > 0
? `Invalid enabled services: ${invalidEnabled}` ? `Invalid enabled services: ${invalidEnabled}`
: ''; : '';
const duplicateDisabledServicesError =
duplicateDisabledServices.length > 0
? `Includes duplicate disabled services: ${duplicateDisabledServices.join(
', '
)}`
: '';
const duplicateMaskedServicesError =
duplicateMaskedServices.length > 0
? `Includes duplicate masked services: ${duplicateMaskedServices.join(
', '
)}`
: '';
const duplicateEnabledServicesError =
duplicateEnabledServices.length > 0
? `Includes duplicate enabled services: ${duplicateEnabledServices.join(
', '
)}`
: '';
return { return {
errors: { errors: {
disabledSystemdServices: disabledSystemdServicesError, disabledSystemdServices:
maskedSystemdServices: maskedSystemdServicesError, disabledSystemdServicesError + '|' + duplicateDisabledServicesError,
enabledSystemdServices: enabledSystemdServicesError, maskedSystemdServices:
maskedSystemdServicesError + '|' + duplicateMaskedServicesError,
enabledSystemdServices:
enabledSystemdServicesError + '|' + duplicateEnabledServicesError,
}, },
disabledNext: disabledNext:
invalidDisabled.length > 0 || invalidDisabled.length > 0 ||
invalidMasked.length > 0 || invalidMasked.length > 0 ||
invalidEnabled.length > 0, invalidEnabled.length > 0 ||
duplicateDisabledServices.length > 0 ||
duplicateMaskedServices.length > 0 ||
duplicateEnabledServices.length > 0,
}; };
} }
@ -544,20 +628,27 @@ export function useUsersValidation(): UsersStepValidation {
} }
} }
const groupsError = const duplicateGroups = getListOfDuplicates(users[index].groups);
const invalidError =
invalidGroups.length > 0 ? `Invalid user groups: ${invalidGroups}` : ''; invalidGroups.length > 0 ? `Invalid user groups: ${invalidGroups}` : '';
const duplicateError =
duplicateGroups.length > 0
? `Includes duplicate groups: ${duplicateGroups.join(', ')}`
: '';
if ( if (
userNameError || userNameError ||
sshKeyError || sshKeyError ||
(users[index].password && !isPasswordValid) || (users[index].password && !isPasswordValid) ||
groupsError invalidError ||
duplicateError
) { ) {
errors[`${index}`] = { errors[index] = {
userName: userNameError, userName: userNameError,
userSshKey: sshKeyError, userSshKey: sshKeyError,
userPassword: passwordError, userPassword: passwordError,
groups: groupsError, groups: invalidError + '|' + duplicateError,
}; };
} }
} }