fix dhcp form

This commit is contained in:
Ildar Kamalov
2025-01-24 17:09:54 +03:00
parent f78dc10c2a
commit eb9b9b6c2b
5 changed files with 165 additions and 135 deletions

View File

@@ -1,5 +1,5 @@
import React, { useMemo } from 'react'; import React, { useMemo } from 'react';
import { useFormContext } from 'react-hook-form'; import { Controller, useFormContext } from 'react-hook-form';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { UINT32_RANGE } from '../../../helpers/constants'; import { UINT32_RANGE } from '../../../helpers/constants';
@@ -12,6 +12,8 @@ import {
validateRequiredValue, validateRequiredValue,
} from '../../../helpers/validators'; } from '../../../helpers/validators';
import { DhcpFormValues } from '.'; import { DhcpFormValues } from '.';
import { Input } from '../../ui/Controls/Input';
import { toNumber } from '../../../helpers/form';
type FormDHCPv4Props = { type FormDHCPv4Props = {
processingConfig?: boolean; processingConfig?: boolean;
@@ -30,9 +32,9 @@ const FormDHCPv4 = ({ processingConfig, ipv4placeholders, interfaces, onSubmit }
const { t } = useTranslation(); const { t } = useTranslation();
const { const {
register,
handleSubmit, handleSubmit,
formState: { errors, isSubmitting }, formState: { errors, isSubmitting },
control,
watch, watch,
} = useFormContext<DhcpFormValues>(); } = useFormContext<DhcpFormValues>();
@@ -52,121 +54,138 @@ const FormDHCPv4 = ({ processingConfig, ipv4placeholders, interfaces, onSubmit }
<div className="row"> <div className="row">
<div className="col-lg-6"> <div className="col-lg-6">
<div className="form__group form__group--settings"> <div className="form__group form__group--settings">
<label>{t('dhcp_form_gateway_input')}</label> <Controller
<input name="v4.gateway_ip"
data-testid="v4_gateway_ip" control={control}
type="text" rules={{
className="form-control"
placeholder={t(ipv4placeholders?.gateway_ip || '')}
disabled={!isInterfaceIncludesIpv4}
{...register('v4.gateway_ip', {
validate: { validate: {
ipv4: validateIpv4, ipv4: validateIpv4,
required: (value) => (isEmptyConfig ? undefined : validateRequiredValue(value)), required: (value) => (isEmptyConfig ? undefined : validateRequiredValue(value)),
notInRange: validateNotInRange, notInRange: validateNotInRange,
}, },
})} }}
render={({ field, fieldState }) => (
<Input
{...field}
type="text"
data-testid="v4_gateway_ip"
label={t('dhcp_form_gateway_input')}
placeholder={t(ipv4placeholders.gateway_ip)}
error={fieldState.error?.message}
disabled={!isInterfaceIncludesIpv4}
/> />
{errors.v4?.gateway_ip && (
<div className="form__message form__message--error">{t(errors.v4.gateway_ip.message)}</div>
)} )}
/>
</div> </div>
<div className="form__group form__group--settings"> <div className="form__group form__group--settings">
<label>{t('dhcp_form_subnet_input')}</label> <Controller
<input name="v4.subnet_mask"
data-testid="v4_subnet_mask" control={control}
type="text" rules={{
className="form-control"
placeholder={t(ipv4placeholders?.subnet_mask || '')}
disabled={!isInterfaceIncludesIpv4}
{...register('v4.subnet_mask', {
validate: { validate: {
required: (value) => (isEmptyConfig ? undefined : validateRequiredValue(value)), required: (value) => (isEmptyConfig ? undefined : validateRequiredValue(value)),
subnet: validateGatewaySubnetMask, subnet: validateGatewaySubnetMask,
}, },
})} }}
render={({ field, fieldState }) => (
<Input
{...field}
type="text"
data-testid="v4_subnet_mask"
label={t('dhcp_form_subnet_input')}
placeholder={t(ipv4placeholders.subnet_mask)}
error={fieldState.error?.message}
disabled={!isInterfaceIncludesIpv4}
/> />
{errors.v4?.subnet_mask && (
<div className="form__message form__message--error">{t(errors.v4.subnet_mask.message)}</div>
)} )}
/>
</div> </div>
</div> </div>
<div className="col-lg-6"> <div className="col-lg-6">
<div className="form__group form__group--settings"> <div className="form__group mb-0">
<div className="row"> <div className="row">
<div className="col-12"> <div className="col-12">
<label>{t('dhcp_form_range_title')}</label> <label>{t('dhcp_form_range_title')}</label>
</div> </div>
<div className="col"> <div className="col">
<input <Controller
data-testid="v4_range_start" name="v4.range_start"
type="text" control={control}
className="form-control" rules={{
placeholder={t(ipv4placeholders?.range_start || '')}
disabled={!isInterfaceIncludesIpv4}
{...register('v4.range_start', {
validate: { validate: {
ipv4: validateIpv4, ipv4: validateIpv4,
gateway: validateIpForGatewaySubnetMask, gateway: validateIpForGatewaySubnetMask,
}, },
})} }}
render={({ field, fieldState }) => (
<Input
{...field}
type="text"
data-testid="v4_range_start"
placeholder={t(ipv4placeholders.range_start)}
error={fieldState.error?.message}
disabled={!isInterfaceIncludesIpv4}
/> />
{errors.v4?.range_start && (
<div className="form__message form__message--error">
{t(errors.v4.range_start.message)}
</div>
)} )}
/>
</div> </div>
<div className="col"> <div className="col">
<input <Controller
data-testid="v4_range_end" name="v4.range_end"
type="text" control={control}
className="form-control" rules={{
placeholder={t(ipv4placeholders?.range_end || '')}
disabled={!isInterfaceIncludesIpv4}
{...register('v4.range_end', {
validate: { validate: {
ipv4: validateIpv4, ipv4: validateIpv4,
rangeEnd: validateIpv4RangeEnd, rangeEnd: validateIpv4RangeEnd,
gateway: validateIpForGatewaySubnetMask, gateway: validateIpForGatewaySubnetMask,
}, },
})} }}
render={({ field, fieldState }) => (
<Input
{...field}
type="text"
data-testid="v4_range_end"
placeholder={t(ipv4placeholders.range_end)}
error={fieldState.error?.message}
disabled={!isInterfaceIncludesIpv4}
/> />
{errors.v4?.range_end && (
<div className="form__message form__message--error">
{errors.v4.range_end.message}
</div>
)} )}
/>
</div> </div>
</div> </div>
</div> </div>
<div className="form__group form__group--settings"> <div className="form__group form__group--settings">
<label>{t('dhcp_form_lease_title')}</label> <Controller
<input name="v4.lease_duration"
data-testid="v4_lease_duration" control={control}
type="number" rules={{
className="form-control"
placeholder={t(ipv4placeholders?.lease_duration || '')}
disabled={!isInterfaceIncludesIpv4}
min={1}
max={UINT32_RANGE.MAX}
{...register('v4.lease_duration', {
valueAsNumber: true,
validate: { validate: {
required: (value) => (isEmptyConfig ? undefined : validateRequiredValue(value)), required: (value) => (isEmptyConfig ? undefined : validateRequiredValue(value)),
}, },
})} }}
render={({ field, fieldState }) => (
<Input
{...field}
type="number"
data-testid="v4_lease_duration"
label={t('dhcp_form_lease_title')}
placeholder={t(ipv4placeholders.lease_duration)}
error={fieldState.error?.message}
disabled={!isInterfaceIncludesIpv4}
min={1}
max={UINT32_RANGE.MAX}
onChange={(e) => {
const { value } = e.target;
field.onChange(toNumber(value));
}}
/> />
{errors.v4?.lease_duration && (
<div className="form__message form__message--error">
{t(errors.v4.lease_duration.message)}
</div>
)} )}
/>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -1,10 +1,12 @@
import React, { useMemo } from 'react'; import React, { useMemo } from 'react';
import { useFormContext } from 'react-hook-form'; import { Controller, useFormContext } from 'react-hook-form';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { UINT32_RANGE } from '../../../helpers/constants'; import { UINT32_RANGE } from '../../../helpers/constants';
import { validateIpv6, validateRequiredValue } from '../../../helpers/validators'; import { validateIpv6, validateRequiredValue } from '../../../helpers/validators';
import { DhcpFormValues } from '.'; import { DhcpFormValues } from '.';
import { Input } from '../../ui/Controls/Input';
import { toNumber } from '../../../helpers/form';
type FormDHCPv6Props = { type FormDHCPv6Props = {
processingConfig?: boolean; processingConfig?: boolean;
@@ -20,9 +22,9 @@ type FormDHCPv6Props = {
const FormDHCPv6 = ({ processingConfig, ipv6placeholders, interfaces, onSubmit }: FormDHCPv6Props) => { const FormDHCPv6 = ({ processingConfig, ipv6placeholders, interfaces, onSubmit }: FormDHCPv6Props) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { const {
register,
handleSubmit, handleSubmit,
formState: { errors, isSubmitting, isValid }, formState: { isSubmitting, isValid },
control,
watch, watch,
} = useFormContext<DhcpFormValues>(); } = useFormContext<DhcpFormValues>();
@@ -40,54 +42,60 @@ const FormDHCPv6 = ({ processingConfig, ipv6placeholders, interfaces, onSubmit }
<form onSubmit={handleSubmit(onSubmit)}> <form onSubmit={handleSubmit(onSubmit)}>
<div className="row"> <div className="row">
<div className="col-lg-6"> <div className="col-lg-6">
<div className="form__group form__group--settings"> <div className="form__group mb-0">
<div className="row"> <div className="row">
<div className="col-12"> <div className="col-12">
<label>{t('dhcp_form_range_title')}</label> <label>{t('dhcp_form_range_title')}</label>
</div> </div>
<div className="col"> <div className="col">
<input <Controller
data-testid="v6_range_start" name="v6.range_start"
type="text" control={control}
className="form-control" rules={{
placeholder={t(ipv6placeholders?.range_start || '')} validate: isInterfaceIncludesIpv6
disabled={!isInterfaceIncludesIpv6} ? {
{...register('v6.range_start', {
validate: {
ipv6: validateIpv6, ipv6: validateIpv6,
required: (value) => required: validateRequiredValue,
isInterfaceIncludesIpv6 ? undefined : validateRequiredValue(value), }
}, : undefined,
})} }}
render={({ field, fieldState }) => (
<Input
{...field}
type="text"
data-testid="v6_range_start"
placeholder={t(ipv6placeholders.range_start)}
error={fieldState.error?.message}
disabled={!isInterfaceIncludesIpv6}
/> />
{errors.v6?.range_start && (
<div className="form__message form__message--error">
{t(errors.v6.range_start.message)}
</div>
)} )}
/>
</div> </div>
<div className="col"> <div className="col">
<input <Controller
data-testid="v6_range_end" name="v6.range_end"
type="text" control={control}
className="form-control" rules={{
placeholder={t(ipv6placeholders?.range_end || '')} validate: isInterfaceIncludesIpv6
disabled={!isInterfaceIncludesIpv6} ? {
{...register('v6.range_end', {
validate: {
ipv6: validateIpv6, ipv6: validateIpv6,
required: (value) => required: validateRequiredValue,
isInterfaceIncludesIpv6 ? undefined : validateRequiredValue(value), }
}, : undefined,
})} }}
render={({ field, fieldState }) => (
<Input
{...field}
type="text"
data-testid="v6_range_end"
placeholder={t(ipv6placeholders.range_end)}
error={fieldState.error?.message}
disabled={!isInterfaceIncludesIpv6}
/> />
{errors.v6?.range_end && (
<div className="form__message form__message--error">
{t(errors.v6.range_end.message)}
</div>
)} )}
/>
</div> </div>
</div> </div>
</div> </div>
@@ -96,26 +104,34 @@ const FormDHCPv6 = ({ processingConfig, ipv6placeholders, interfaces, onSubmit }
<div className="row"> <div className="row">
<div className="col-lg-6 form__group form__group--settings"> <div className="col-lg-6 form__group form__group--settings">
<label>{t('dhcp_form_lease_title')}</label> <Controller
<input name="v6.lease_duration"
data-testid="v6_lease_duration" control={control}
rules={{
validate: isInterfaceIncludesIpv6
? {
required: validateRequiredValue,
}
: undefined,
}}
render={({ field, fieldState }) => (
<Input
{...field}
type="number" type="number"
className="form-control" data-testid="v6_lease_duration"
placeholder={t(ipv6placeholders?.lease_duration || '')} label={t('dhcp_form_lease_title')}
placeholder={t(ipv6placeholders.lease_duration)}
error={fieldState.error?.message}
disabled={!isInterfaceIncludesIpv6} disabled={!isInterfaceIncludesIpv6}
min={1} min={1}
max={UINT32_RANGE.MAX} max={UINT32_RANGE.MAX}
{...register('v6.lease_duration', { onChange={(e) => {
valueAsNumber: true, const { value } = e.target;
validate: { field.onChange(toNumber(value));
required: (value) => }}
isInterfaceIncludesIpv6 ? undefined : validateRequiredValue(value),
},
})}
/> />
{errors.v6?.lease_duration && (
<div className="form__message form__message--error">{t(errors.v6.lease_duration.message)}</div>
)} )}
/>
</div> </div>
</div> </div>

View File

@@ -168,7 +168,7 @@ const Dhcp = () => {
} }
}; };
const handleSubmit = (values: any) => { const handleSubmit = (values: DhcpFormValues) => {
dispatch( dispatch(
setDhcpConfig({ setDhcpConfig({
interface_name, interface_name,
@@ -293,7 +293,6 @@ const Dhcp = () => {
<FormProvider {...methods}> <FormProvider {...methods}>
<Interfaces /> <Interfaces />
<Card title={t('dhcp_ipv4_settings')} bodyType="card-body box-body--settings"> <Card title={t('dhcp_ipv4_settings')} bodyType="card-body box-body--settings">
<div> <div>
<FormDHCPv4 <FormDHCPv4
@@ -304,7 +303,6 @@ const Dhcp = () => {
/> />
</div> </div>
</Card> </Card>
<Card title={t('dhcp_ipv6_settings')} bodyType="card-body box-body--settings"> <Card title={t('dhcp_ipv6_settings')} bodyType="card-body box-body--settings">
<div> <div>
<FormDHCPv6 <FormDHCPv6

View File

@@ -143,7 +143,6 @@ export const Form = ({
subject, subject,
warning_validation, warning_validation,
onSubmit, onSubmit,
debouncedConfigValidation,
setTlsConfig, setTlsConfig,
validateTlsConfig, validateTlsConfig,
}: Props) => { }: Props) => {
@@ -187,7 +186,6 @@ export const Form = ({
if (JSON.stringify(previousValues) !== JSON.stringify(watchedValues)) { if (JSON.stringify(previousValues) !== JSON.stringify(watchedValues)) {
// TODO(ik) onChange TLS config validation // TODO(ik) onChange TLS config validation
console.log('debouncedConfigValidation'); console.log('debouncedConfigValidation');
debouncedConfigValidation(watchedValues);
previousValuesRef.current = watchedValues; previousValuesRef.current = watchedValues;
} }
}, [watchedValues]); }, [watchedValues]);

View File

@@ -72,7 +72,6 @@ export const Encryption = ({ encryption, setTlsConfig, validateTlsConfig }: Prop
const submitValues = getSubmitValues(values); const submitValues = getSubmitValues(values);
if (submitValues.enabled) { if (submitValues.enabled) {
console.log('validateTlsConfig');
validateTlsConfig(submitValues); validateTlsConfig(submitValues);
} }
}, DEBOUNCE_TIMEOUT), }, DEBOUNCE_TIMEOUT),