cleanup forms
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user