cleanup forms

This commit is contained in:
Ildar Kamalov
2025-01-21 16:14:20 +03:00
parent edd9bf7b59
commit 70aeee3037
8 changed files with 125 additions and 139 deletions

View File

@@ -186,9 +186,7 @@ export const Form = ({
<div className="form__desc mt-0">
<Trans
components={[
<a href={CLIENT_ID_LINK} target="_blank" rel="noopener noreferrer" key="0">
text
</a>,
<a href={CLIENT_ID_LINK} target="_blank" rel="noopener noreferrer" key="0" />,
]}>
client_identifier_desc
</Trans>

View File

@@ -1,7 +1,8 @@
import React from 'react';
import React, { useMemo } from 'react';
import { useFormContext } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { isValid } from 'date-fns';
import { UINT32_RANGE } from '../../../helpers/constants';
import {
validateGatewaySubnetMask,
@@ -32,7 +33,7 @@ const FormDHCPv4 = ({ processingConfig, ipv4placeholders, interfaces, onSubmit }
const {
register,
handleSubmit,
formState: { errors, isSubmitting },
formState: { errors, isSubmitting, isValid },
watch,
} = useFormContext<DhcpFormValues>();
@@ -49,6 +50,10 @@ const FormDHCPv4 = ({ processingConfig, ipv4placeholders, interfaces, onSubmit }
}
};
const isDisabled = useMemo(() => {
return isSubmitting || !isValid || processingConfig || !isInterfaceIncludesIpv4 || isEmptyConfig;
}, [isSubmitting, isValid, processingConfig, isInterfaceIncludesIpv4, isEmptyConfig]);
return (
<form onSubmit={handleSubmit(handleFormSubmit)}>
<div className="row">
@@ -169,16 +174,7 @@ const FormDHCPv4 = ({ processingConfig, ipv4placeholders, interfaces, onSubmit }
</div>
<div className="btn-list">
<button
type="submit"
className="btn btn-success btn-standard"
disabled={
isSubmitting ||
processingConfig ||
!isInterfaceIncludesIpv4 ||
isEmptyConfig ||
Object.keys(errors).length > 0
}>
<button type="submit" className="btn btn-success btn-standard" disabled={isDisabled}>
{t('save_config')}
</button>
</div>

View File

@@ -1,4 +1,4 @@
import React from 'react';
import React, { useMemo } from 'react';
import { useFormContext } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
@@ -30,7 +30,7 @@ const FormDHCPv6 = ({ processingConfig, ipv6placeholders, interfaces, onSubmit }
const {
register,
handleSubmit,
formState: { errors, isSubmitting },
formState: { errors, isSubmitting, isValid },
watch,
} = useFormContext<DhcpFormValues>();
@@ -46,6 +46,10 @@ const FormDHCPv6 = ({ processingConfig, ipv6placeholders, interfaces, onSubmit }
}
};
const isDisabled = useMemo(() => {
return isSubmitting || !isValid || processingConfig || !isInterfaceIncludesIpv6 || isEmptyConfig;
}, [isSubmitting, isValid, processingConfig, isInterfaceIncludesIpv6, isEmptyConfig]);
return (
<form onSubmit={handleSubmit(handleFormSubmit)}>
<div className="row">
@@ -126,16 +130,7 @@ const FormDHCPv6 = ({ processingConfig, ipv6placeholders, interfaces, onSubmit }
</div>
<div className="btn-list">
<button
type="submit"
className="btn btn-success btn-standard"
disabled={
isSubmitting ||
processingConfig ||
!isInterfaceIncludesIpv6 ||
isEmptyConfig ||
Object.keys(errors).length > 0
}>
<button type="submit" className="btn btn-success btn-standard" disabled={isDisabled}>
{t('save_config')}
</button>
</div>

View File

@@ -4,6 +4,7 @@ import { Trans, useTranslation } from 'react-i18next';
import { CLIENT_ID_LINK } from '../../../../helpers/constants';
import { removeEmptyLines, trimMultilineString } from '../../../../helpers/helpers';
import { Textarea } from '../../../ui/Controls/Textarea';
const fields = [
{
@@ -44,7 +45,7 @@ interface FormData {
const Form = ({ initialValues, onSubmit, processingSet }: FormProps) => {
const { t } = useTranslation();
const {
control,
handleSubmit,
@@ -88,11 +89,7 @@ const Form = ({ initialValues, onSubmit, processingSet }: FormProps) => {
<div className="form__desc form__desc--top">
<Trans
components={{
a: (
<a href={CLIENT_ID_LINK} target="_blank" rel="noopener noreferrer">
{t('text')}
</a>
),
a: <a href={CLIENT_ID_LINK} target="_blank" rel="noopener noreferrer" />,
}}>
{subtitle}
</Trans>
@@ -102,14 +99,12 @@ const Form = ({ initialValues, onSubmit, processingSet }: FormProps) => {
name={id}
control={control}
render={({ field }) => (
<textarea
<Textarea
{...field}
id={id}
className="form-control form-control--textarea font-monospace"
disabled={disabled || processingSet}
onBlur={(e) => {
const normalized = normalizeOnBlur(e.target.value);
field.onChange(normalized);
field.onChange(normalizeOnBlur(e.target.value));
}}
/>
)}
@@ -120,8 +115,17 @@ const Form = ({ initialValues, onSubmit, processingSet }: FormProps) => {
return (
<form onSubmit={handleSubmit(onSubmit)}>
{fields.map((f) => renderField(f as { id: keyof FormData; title: string; subtitle: string; normalizeOnBlur: (value: string) => string; }))}
{fields.map((f) =>
renderField(
f as {
id: keyof FormData;
title: string;
subtitle: string;
normalizeOnBlur: (value: string) => string;
},
),
)}
<div className="card-actions">
<div className="btn-list">
<button

View File

@@ -2,19 +2,24 @@ import React, { useEffect, useRef } from 'react';
import { Controller, useForm } from 'react-hook-form';
import { Trans, useTranslation } from 'react-i18next';
import i18next from 'i18next';
import { toNumber } from '../../../helpers/form';
import { FILTERS_INTERVALS_HOURS, FILTERS_RELATIVE_LINK } from '../../../helpers/constants';
import { DAY, FILTERS_INTERVALS_HOURS, FILTERS_RELATIVE_LINK } from '../../../helpers/constants';
import { Checkbox } from '../../ui/Controls/Checkbox';
const getTitleForInterval = (interval: any, t: any) => {
const THREE_DAYS_INTERVAL = DAY * 3;
const SEVEN_DAYS_INTERVAL = DAY * 7;
const getTitleForInterval = (interval: number) => {
if (interval === 0) {
return t('disabled');
}
if (interval === 72 || interval === 168) {
return t('interval_days', { count: interval / 24 });
return i18next.t('disabled');
}
return t('interval_hours', { count: interval });
if (interval === THREE_DAYS_INTERVAL || interval === SEVEN_DAYS_INTERVAL) {
return i18next.t('interval_days', { count: interval / DAY });
}
return i18next.t('interval_hours', { count: interval });
};
export type FormValues = {

View File

@@ -4,16 +4,12 @@ import i18next from 'i18next';
import { Controller, useForm } from 'react-hook-form';
import { trimLinesAndRemoveEmpty } from '../../../helpers/helpers';
import {
QUERY_LOG_INTERVALS_DAYS,
HOUR,
DAY,
RETENTION_CUSTOM,
RETENTION_RANGE,
CUSTOM_INTERVAL,
} from '../../../helpers/constants';
import { QUERY_LOG_INTERVALS_DAYS, HOUR, DAY, RETENTION_CUSTOM, RETENTION_RANGE } from '../../../helpers/constants';
import '../FormButton.css';
import { Checkbox } from '../../ui/Controls/Checkbox';
import { Input } from '../../ui/Controls/Input';
import { toNumber } from '../../../helpers/form';
import { Textarea } from '../../ui/Controls/Textarea';
const getIntervalTitle = (interval: number) => {
switch (interval) {
@@ -48,7 +44,6 @@ export const Form = ({ initialValues, processing, processingReset, onSubmit, onR
const { t } = useTranslation();
const {
register,
handleSubmit,
watch,
setValue,
@@ -135,17 +130,23 @@ export const Form = ({ initialValues, processing, processingReset, onSubmit, onR
<div className="form__group--input">
<div className="form__desc form__desc--top">{i18next.t('custom_rotation_input')}</div>
<input
type="number"
className="form-control"
name={CUSTOM_INTERVAL}
min={RETENTION_RANGE.MIN}
max={RETENTION_RANGE.MAX}
disabled={processing}
{...register('customInterval')}
onChange={(e) => {
setValue('customInterval', parseInt(e.target.value, 10));
}}
<Controller
name="customInterval"
control={control}
render={({ field, fieldState }) => (
<Input
{...field}
placeholder={t('encryption_certificates_input')}
disabled={processing}
error={fieldState.error?.message}
min={RETENTION_RANGE.MIN}
max={RETENTION_RANGE.MAX}
onChange={(e) => {
const { value } = e.target;
field.onChange(toNumber(value));
}}
/>
)}
/>
</div>
)}
@@ -178,12 +179,19 @@ export const Form = ({ initialValues, processing, processingReset, onSubmit, onR
</div>
<div className="form__group form__group--settings">
<textarea
className="form-control form-control--textarea font-monospace text-input"
placeholder={i18next.t('ignore_domains')}
disabled={processing}
{...register('ignored')}
onBlur={handleIgnoredBlur}
<Controller
name="ignored"
control={control}
render={({ field, fieldState }) => (
<Textarea
{...field}
placeholder={t('ignore_domains')}
className="text-input"
disabled={processing}
error={fieldState.error?.message}
onBlur={handleIgnoredBlur}
/>
)}
/>
</div>

View File

@@ -3,17 +3,14 @@ import { Trans, useTranslation } from 'react-i18next';
import i18next from 'i18next';
import { Controller, useForm } from 'react-hook-form';
import {
STATS_INTERVALS_DAYS,
DAY,
RETENTION_CUSTOM,
CUSTOM_INTERVAL,
RETENTION_RANGE,
} from '../../../helpers/constants';
import { STATS_INTERVALS_DAYS, DAY, RETENTION_CUSTOM, RETENTION_RANGE } from '../../../helpers/constants';
import { trimLinesAndRemoveEmpty } from '../../../helpers/helpers';
import '../FormButton.css';
import { Checkbox } from '../../ui/Controls/Checkbox';
import { Input } from '../../ui/Controls/Input';
import { toNumber } from '../../../helpers/form';
import { Textarea } from '../../ui/Controls/Textarea';
const getIntervalTitle = (interval: any) => {
switch (interval) {
@@ -33,8 +30,15 @@ export type FormValues = {
ignored: string;
};
const defaultFormValues = {
enabled: false,
interval: DAY,
customInterval: null,
ignored: '',
};
type Props = {
initialValues: Partial<FormValues>;
initialValues: FormValues;
processing: boolean;
processingReset: boolean;
onSubmit: (values: FormValues) => void;
@@ -45,7 +49,6 @@ export const Form = ({ initialValues, processing, processingReset, onSubmit, onR
const { t } = useTranslation();
const {
register,
handleSubmit,
watch,
setValue,
@@ -54,10 +57,8 @@ export const Form = ({ initialValues, processing, processingReset, onSubmit, onR
} = useForm<FormValues>({
mode: 'onChange',
defaultValues: {
enabled: initialValues.enabled || false,
interval: initialValues.interval || DAY,
customInterval: initialValues.customInterval || null,
ignored: initialValues.ignored || '',
...defaultFormValues,
...initialValues,
},
});
@@ -120,43 +121,27 @@ export const Form = ({ initialValues, processing, processingReset, onSubmit, onR
<div className="form__group--input">
<div className="form__desc form__desc--top">{i18next.t('custom_retention_input')}</div>
{/* <Field
key={RETENTION_CUSTOM_INPUT}
name={CUSTOM_INTERVAL}
type="number"
className="form-control"
component={renderInputField}
disabled={processing}
normalize={toFloatNumber}
min={RETENTION_RANGE.MIN}
max={RETENTION_RANGE.MAX}
/> */}
<input
name={CUSTOM_INTERVAL}
type="number"
className="form-control"
min={RETENTION_RANGE.MIN}
max={RETENTION_RANGE.MAX}
disabled={processing}
{...register('customInterval')}
onChange={(e) => {
setValue('customInterval', parseInt(e.target.value, 10));
}}
<Controller
name="customInterval"
control={control}
render={({ field, fieldState }) => (
<Input
{...field}
placeholder={t('encryption_certificates_input')}
disabled={processing}
error={fieldState.error?.message}
min={RETENTION_RANGE.MIN}
max={RETENTION_RANGE.MAX}
onChange={(e) => {
const { value } = e.target;
field.onChange(toNumber(value));
}}
/>
)}
/>
</div>
)}
{STATS_INTERVALS_DAYS.map((interval) => (
// <Field
// key={interval}
// name="interval"
// type="radio"
// component={renderRadioField}
// value={interval}
// placeholder={getIntervalTitle(interval, t)}
// normalize={toNumber}
// disabled={processing}
// />
<label key={interval} className="custom-control custom-radio">
<input
type="radio"
@@ -184,21 +169,19 @@ export const Form = ({ initialValues, processing, processingReset, onSubmit, onR
</div>
<div className="form__group form__group--settings">
{/* <Field
<Controller
name="ignored"
type="textarea"
className="form-control form-control--textarea font-monospace text-input"
component={renderTextareaField}
placeholder={t('ignore_domains')}
disabled={processing}
normalizeOnBlur={trimLinesAndRemoveEmpty}
/> */}
<textarea
className="form-control form-control--textarea font-monospace text-input"
placeholder={i18next.t('ignore_domains')}
disabled={processing}
{...register('ignored')}
onBlur={handleIgnoredBlur}
control={control}
render={({ field, fieldState }) => (
<Textarea
{...field}
placeholder={t('ignore_domains')}
className="text-input"
disabled={processing}
error={fieldState.error?.message}
onBlur={handleIgnoredBlur}
/>
)}
/>
</div>

View File

@@ -40,12 +40,6 @@ const getDownloadLink = (host: string, clientId: string, protocol: string, inval
);
};
const githubLink = (
<a href={CLIENT_ID_LINK} target="_blank" rel="noopener noreferrer">
text
</a>
);
type FormValues = {
host: string;
clientId: string;
@@ -150,7 +144,10 @@ export const MobileConfigForm = ({ initialValues }: Props) => {
</label>
<div className="form__desc form__desc--top">
<Trans components={{ a: githubLink }}>client_id_desc</Trans>
<Trans
components={{ a: <a href={CLIENT_ID_LINK} target="_blank" rel="noopener noreferrer" /> }}>
client_id_desc
</Trans>
</div>
<Controller