Merge pull request #216 from stephb9959/main

[WIFI-13515] Supporting deviceTypes in lowercase
This commit is contained in:
Charles Bourque
2024-03-15 17:53:25 +01:00
committed by GitHub
7 changed files with 160 additions and 104 deletions

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "ucentral-client",
"version": "3.0.2(8)",
"version": "3.0.2(9)",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "ucentral-client",
"version": "3.0.2(8)",
"version": "3.0.2(9)",
"license": "ISC",
"dependencies": {
"@chakra-ui/anatomy": "^2.1.1",

View File

@@ -1,6 +1,6 @@
{
"name": "ucentral-client",
"version": "3.0.2(8)",
"version": "3.0.2(9)",
"description": "",
"private": true,
"main": "index.tsx",

View File

@@ -1,6 +1,6 @@
import React from 'react';
import { FormControl, FormErrorMessage, FormLabel } from '@chakra-ui/react';
import { Select } from 'chakra-react-select';
import { CreatableSelect, Select } from 'chakra-react-select';
import PropTypes from 'prop-types';
import isEqual from 'react-fast-compare';
import { useTranslation } from 'react-i18next';
@@ -25,6 +25,7 @@ const propTypes = {
isHidden: PropTypes.bool,
isPortal: PropTypes.bool.isRequired,
definitionKey: PropTypes.string,
isCreatable: PropTypes.bool,
};
const defaultProps = {
@@ -36,6 +37,7 @@ const defaultProps = {
isDisabled: false,
isHidden: false,
definitionKey: null,
isCreatable: false,
};
const FastMultiSelectInput = ({
@@ -50,6 +52,7 @@ const FastMultiSelectInput = ({
isRequired,
isDisabled,
isHidden,
isCreatable,
isPortal,
definitionKey,
}) => {
@@ -61,35 +64,62 @@ const FastMultiSelectInput = ({
{label}
<ConfigurationFieldExplanation definitionKey={definitionKey} />
</FormLabel>
<Select
chakraStyles={{
control: (provided, { isDisabled: isControlDisabled }) => ({
...provided,
borderRadius: '15px',
opacity: isControlDisabled ? '0.8 !important' : '1',
border: '2px solid',
}),
dropdownIndicator: (provided) => ({
...provided,
backgroundColor: 'unset',
border: 'unset',
}),
}}
classNamePrefix={isPortal ? 'chakra-react-select' : ''}
menuPortalTarget={isPortal ? document.body : undefined}
isMulti
closeMenuOnSelect={false}
options={canSelectAll ? [{ value: '*', label: t('common.all') }, ...options] : options}
value={
value?.map((val) => {
if (val === '*') return { value: val, label: t('common.all') };
return options.find((opt) => opt.value === val);
}) ?? []
}
onChange={onChange}
onBlur={onBlur}
isDisabled={isDisabled}
/>
{isCreatable ? (
<CreatableSelect
chakraStyles={{
control: (provided, { isDisabled: isControlDisabled }) => ({
...provided,
borderRadius: '15px',
opacity: isControlDisabled ? '0.8 !important' : '1',
border: '2px solid',
}),
dropdownIndicator: (provided) => ({
...provided,
backgroundColor: 'unset',
border: 'unset',
}),
}}
classNamePrefix={isPortal ? 'chakra-react-select' : ''}
menuPortalTarget={isPortal ? document.body : undefined}
isMulti
closeMenuOnSelect={false}
options={options}
value={value}
onChange={onChange}
onBlur={onBlur}
isDisabled={isDisabled}
/>
) : (
<Select
chakraStyles={{
control: (provided, { isDisabled: isControlDisabled }) => ({
...provided,
borderRadius: '15px',
opacity: isControlDisabled ? '0.8 !important' : '1',
border: '2px solid',
}),
dropdownIndicator: (provided) => ({
...provided,
backgroundColor: 'unset',
border: 'unset',
}),
}}
classNamePrefix={isPortal ? 'chakra-react-select' : ''}
menuPortalTarget={isPortal ? document.body : undefined}
isMulti
closeMenuOnSelect={false}
options={canSelectAll ? [{ value: '*', label: t('common.all') }, ...options] : options}
value={
value?.map((val) => {
if (val === '*') return { value: val, label: t('common.all') };
return options.find((opt) => opt.value === val);
}) ?? []
}
onChange={onChange}
onBlur={onBlur}
isDisabled={isDisabled}
/>
)}
<FormErrorMessage>{error}</FormErrorMessage>
</FormControl>
);

View File

@@ -20,6 +20,7 @@ const propTypes = {
canSelectAll: PropTypes.bool,
isPortal: PropTypes.bool,
definitionKey: PropTypes.string,
isCreatable: PropTypes.bool,
};
const defaultProps = {
@@ -31,6 +32,7 @@ const defaultProps = {
canSelectAll: false,
isPortal: false,
definitionKey: null,
isCreatable: false,
};
const MultiSelectField = ({
@@ -43,25 +45,39 @@ const MultiSelectField = ({
emptyIsUndefined,
canSelectAll,
hasVirtualAll,
isCreatable,
isPortal,
definitionKey,
}) => {
const [{ value }, { touched, error }, { setValue, setTouched }] = useField(name);
const onChange = useCallback((option) => {
const allIndex = option.findIndex((opt) => opt.value === '*');
if (option.length === 0 && emptyIsUndefined) {
setValue(undefined);
} else if (allIndex === 0 && option.length > 1) {
const newValues = option.slice(1);
setValue(newValues.map((val) => val.value));
} else if (allIndex >= 0) {
if (!hasVirtualAll) setValue(['*']);
else setValue(options.map(({ value: v }) => v));
} else if (option.length > 0) setValue(option.map((val) => val.value));
else setValue([]);
setTouched(true);
}, []);
const onChange = useCallback(
(option) => {
if (isCreatable) {
if (typeof option === 'string') {
setValue([...value, option]);
} else {
setValue(option);
}
// setValue([...value, option]);
} else {
const allIndex = option.findIndex((opt) => opt.value === '*');
if (option.length === 0 && emptyIsUndefined) {
setValue(undefined);
} else if (allIndex === 0 && option.length > 1) {
const newValues = option.slice(1);
setValue(newValues.map((val) => val.value));
} else if (allIndex >= 0) {
if (!hasVirtualAll) setValue(['*']);
else setValue(options.map(({ value: v }) => v));
} else if (option.length > 0) setValue(option.map((val) => val.value));
else setValue([]);
setTouched(true);
}
},
[value],
);
const onFieldBlur = useCallback(() => {
setTouched(true);
@@ -82,6 +98,7 @@ const MultiSelectField = ({
isHidden={isHidden}
isPortal={isPortal}
definitionKey={definitionKey}
isCreatable={isCreatable}
/>
);
};

View File

@@ -69,35 +69,38 @@ const CreateDefaultConfigurationModal = () => {
key={formKey}
validationSchema={DefaultConfigurationSchema(t)}
onSubmit={(data, { setSubmitting, resetForm }) => {
createConfig.mutateAsync(data, {
onSuccess: () => {
toast({
id: `config-create-success`,
title: t('common.success'),
description: t('controller.configurations.create_success'),
status: 'success',
duration: 5000,
isClosable: true,
position: 'top-right',
});
setSubmitting(false);
resetForm();
modalProps.onClose();
createConfig.mutateAsync(
{ ...data, modelIds: data.modelIds.map((v) => v.value) },
{
onSuccess: () => {
toast({
id: `config-create-success`,
title: t('common.success'),
description: t('controller.configurations.create_success'),
status: 'success',
duration: 5000,
isClosable: true,
position: 'top-right',
});
setSubmitting(false);
resetForm();
modalProps.onClose();
},
onError: (error) => {
const e = error as AxiosError;
toast({
id: `config-create-error`,
title: t('common.error'),
description: e?.response?.data?.ErrorDescription,
status: 'error',
duration: 5000,
isClosable: true,
position: 'top-right',
});
setSubmitting(false);
},
},
onError: (error) => {
const e = error as AxiosError;
toast({
id: `config-create-error`,
title: t('common.error'),
description: e?.response?.data?.ErrorDescription,
status: 'error',
duration: 5000,
isClosable: true,
position: 'top-right',
});
setSubmitting(false);
},
});
);
}}
>
<Box>
@@ -133,6 +136,7 @@ const CreateDefaultConfigurationModal = () => {
value: devType,
})) ?? []
}
isCreatable
isRequired
/>
<StringField name="configuration" label={t('configurations.one')} isArea isDisabled={isDisabled} mt={4} />

View File

@@ -70,40 +70,44 @@ const EditDefaultConfiguration = ({ modalProps, config }: Props) => {
innerRef={formRef as React.Ref<FormikProps<DefaultConfigurationResponse>>}
initialValues={{
...config,
modelIds: config.modelIds.map((v) => ({ label: v, value: v })),
configuration: JSON.stringify(config.configuration, null, 2),
}}
key={formKey}
validationSchema={DefaultConfigurationSchema(t)}
onSubmit={(data, { setSubmitting, resetForm }) => {
updateConfig.mutateAsync(data, {
onSuccess: () => {
toast({
id: `config-edit-success`,
title: t('common.success'),
description: t('controller.configurations.update_success'),
status: 'success',
duration: 5000,
isClosable: true,
position: 'top-right',
});
setSubmitting(false);
resetForm();
modalProps.onClose();
updateConfig.mutateAsync(
{ ...data, modelIds: data.modelIds.map((v) => v.value) },
{
onSuccess: () => {
toast({
id: `config-edit-success`,
title: t('common.success'),
description: t('controller.configurations.update_success'),
status: 'success',
duration: 5000,
isClosable: true,
position: 'top-right',
});
setSubmitting(false);
resetForm();
modalProps.onClose();
},
onError: (error) => {
const e = error as AxiosError;
toast({
id: `config-edit-error`,
title: t('common.error'),
description: e?.response?.data?.ErrorDescription,
status: 'error',
duration: 5000,
isClosable: true,
position: 'top-right',
});
setSubmitting(false);
},
},
onError: (error) => {
const e = error as AxiosError;
toast({
id: `config-edit-error`,
title: t('common.error'),
description: e?.response?.data?.ErrorDescription,
status: 'error',
duration: 5000,
isClosable: true,
position: 'top-right',
});
setSubmitting(false);
},
});
);
}}
>
<Box>
@@ -139,6 +143,7 @@ const EditDefaultConfiguration = ({ modalProps, config }: Props) => {
value: devType,
})) ?? []
}
isCreatable
isRequired
/>
<StringField

View File

@@ -6,7 +6,7 @@ export const DefaultConfigurationSchema = (t: (str: string) => string) =>
.shape({
name: Yup.string().required(t('form.required')),
description: Yup.string(),
modelIds: Yup.array().of(Yup.string()).required(t('form.required')).min(1, t('form.required')),
modelIds: Yup.array().of(Yup.object()).required(t('form.required')).min(1, t('form.required')),
platform: Yup.string().oneOf(['ap', 'switch']).required(t('form.required')),
configuration: Yup.string()
.required(t('form.required'))