Files
AdGuardHome/client/src/components/Settings/StatsConfig/Form.tsx
Ildar Kamalov 8b2ab8ea87 Pull request 2322: ADG-9415
Merge in DNS/adguard-home from ADG-9415 to master

Squashed commit of the following:

commit 76bf99499a
Merge: 29529970a 0389515ee
Author: Ildar Kamalov <ik@adguard.com>
Date:   Wed Feb 26 18:31:41 2025 +0300

    Merge branch 'master' into ADG-9415

commit 29529970a3
Merge: b49790daf 782a1a982
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Mon Feb 24 15:44:38 2025 +0300

    Merge branch 'master' into ADG-9415

commit b49790daf8
Author: Ildar Kamalov <ik@adguard.com>
Date:   Mon Feb 24 15:30:18 2025 +0300

    fix default lease duration value

commit cb307472ec
Author: Ildar Kamalov <ik@adguard.com>
Date:   Mon Feb 24 10:35:26 2025 +0300

    fix default response status

commit 115e743e1a
Author: Ildar Kamalov <ik@adguard.com>
Date:   Mon Feb 24 10:32:46 2025 +0300

    fix upstream description

commit 26b0eddaca
Author: Ildar Kamalov <ik@adguard.com>
Date:   Tue Feb 18 17:40:41 2025 +0300

    use const for test config file

commit 58faa7c537
Author: Ildar Kamalov <ik@adguard.com>
Date:   Tue Feb 18 17:31:04 2025 +0300

    fix install config

commit 0a3346d911
Author: Ildar Kamalov <ik@adguard.com>
Date:   Mon Feb 17 15:25:23 2025 +0300

    fix install check config

commit 17c4c26ea8
Author: Ildar Kamalov <ik@adguard.com>
Date:   Fri Feb 14 17:18:20 2025 +0300

    fix query log

commit 14a2685ae3
Author: Ildar Kamalov <ik@adguard.com>
Date:   Fri Feb 14 15:52:36 2025 +0300

    fix dhcp initial values

commit e7a8db7afd
Author: Ildar Kamalov <ik@adguard.com>
Date:   Fri Feb 14 14:37:24 2025 +0300

    fix encryption form values

commit 1c8917f7ac
Author: Ildar Kamalov <ik@adguard.com>
Date:   Fri Feb 14 14:07:29 2025 +0300

    fix blocked services submit

commit 4dfa536cea
Author: Ildar Kamalov <ik@adguard.com>
Date:   Fri Feb 14 13:50:47 2025 +0300

    dns config ip validation

commit 4fee83fe13
Author: Ildar Kamalov <ik@adguard.com>
Date:   Wed Feb 12 17:49:54 2025 +0300

    add playwright warning

commit 8c2f36e7a6
Author: Ildar Kamalov <ik@adguard.com>
Date:   Tue Feb 11 18:36:18 2025 +0300

    fix config file name

commit 83db5f33dc
Author: Ildar Kamalov <ik@adguard.com>
Date:   Tue Feb 11 16:16:43 2025 +0300

    temp config file

commit 9080c1620f
Author: Ildar Kamalov <ik@adguard.com>
Date:   Tue Feb 11 15:01:46 2025 +0300

    update readme

commit ee1520307f
Merge: fd12e33c0 2fe2d254b
Author: Ildar Kamalov <ik@adguard.com>
Date:   Tue Feb 11 14:44:06 2025 +0300

    Merge branch 'master' into ADG-9415

commit fd12e33c06
Author: Igor Lobanov <bniwredyc@gmail.com>
Date:   Mon Feb 10 10:29:43 2025 +0100

    added typecheck on build, fixed eslint

commit b3849eebc4
Merge: 225167a8b 9bf3ee128
Author: Igor Lobanov <bniwredyc@gmail.com>
Date:   Mon Feb 10 09:43:32 2025 +0100

    Merge branch 'ADG-9415' of https://bit.int.agrd.dev/scm/dns/adguard-home into ADG-9415

... and 94 more commits
2025-02-26 19:37:52 +03:00

212 lines
7.7 KiB
TypeScript

import React, { useEffect } from 'react';
import { Trans, useTranslation } from 'react-i18next';
import i18next from 'i18next';
import { Controller, useForm } from 'react-hook-form';
import { STATS_INTERVALS_DAYS, 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: any) => {
switch (interval) {
case RETENTION_CUSTOM:
return i18next.t('settings_custom');
case DAY:
return i18next.t('interval_24_hour');
default:
return i18next.t('interval_days', { count: interval / DAY });
}
};
export type FormValues = {
enabled: boolean;
interval: number;
customInterval?: number | null;
ignored: string;
};
const defaultFormValues = {
enabled: false,
interval: DAY,
customInterval: null,
ignored: '',
};
type Props = {
initialValues: FormValues;
processing: boolean;
processingReset: boolean;
onSubmit: (values: FormValues) => void;
onReset: () => void;
};
export const Form = ({ initialValues, processing, processingReset, onSubmit, onReset }: Props) => {
const { t } = useTranslation();
const {
handleSubmit,
watch,
setValue,
control,
formState: { isSubmitting },
} = useForm<FormValues>({
mode: 'onBlur',
defaultValues: {
...defaultFormValues,
...initialValues,
},
});
const intervalValue = watch('interval');
const customIntervalValue = watch('customInterval');
useEffect(() => {
if (STATS_INTERVALS_DAYS.includes(intervalValue)) {
setValue('customInterval', null);
}
}, [intervalValue]);
const onSubmitForm = (data: FormValues) => {
onSubmit(data);
};
const disableSubmit = isSubmitting || processing || (intervalValue === RETENTION_CUSTOM && !customIntervalValue);
return (
<form onSubmit={handleSubmit(onSubmitForm)}>
<div className="form__group form__group--settings">
<Controller
name="enabled"
control={control}
render={({ field }) => (
<Checkbox
{...field}
data-testid="stats_config_enabled"
title={t('statistics_enable')}
disabled={processing}
/>
)}
/>
</div>
<div className="form__label form__label--with-desc">
<Trans>statistics_retention</Trans>
</div>
<div className="form__desc form__desc--top">
<Trans>statistics_retention_desc</Trans>
</div>
<div className="form__group form__group--settings mt-2">
<div className="custom-controls-stacked">
<label className="custom-control custom-radio">
<input
type="radio"
data-testid="stats_config_interval"
className="custom-control-input"
disabled={processing}
checked={!STATS_INTERVALS_DAYS.includes(intervalValue)}
value={RETENTION_CUSTOM}
onChange={(e) => {
setValue('interval', parseInt(e.target.value, 10));
}}
/>
<span className="custom-control-label">{getIntervalTitle(RETENTION_CUSTOM)}</span>
</label>
{!STATS_INTERVALS_DAYS.includes(intervalValue) && (
<div className="form__group--input">
<div className="form__desc form__desc--top">{i18next.t('custom_retention_input')}</div>
<Controller
name="customInterval"
control={control}
render={({ field, fieldState }) => (
<Input
{...field}
data-testid="stats_config_custom_interval"
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) => (
<label key={interval} className="custom-control custom-radio">
<input
type="radio"
className="custom-control-input"
disabled={processing}
value={interval}
checked={intervalValue === interval}
onChange={(e) => {
setValue('interval', parseInt(e.target.value, 10));
}}
/>
<span className="custom-control-label">{getIntervalTitle(interval)}</span>
</label>
))}
</div>
</div>
<div className="form__label form__label--with-desc">
<Trans>ignore_domains_title</Trans>
</div>
<div className="form__desc form__desc--top">
<Trans>ignore_domains_desc_stats</Trans>
</div>
<div className="form__group form__group--settings">
<Controller
name="ignored"
control={control}
render={({ field, fieldState }) => (
<Textarea
{...field}
data-testid="stats_config_ignored"
placeholder={t('ignore_domains')}
className="text-input"
disabled={processing}
error={fieldState.error?.message}
trimOnBlur
/>
)}
/>
</div>
<div className="mt-5">
<button
type="submit"
data-testid="stats_config_save"
className="btn btn-success btn-standard btn-large"
disabled={disableSubmit}>
<Trans>save_btn</Trans>
</button>
<button
type="button"
data-testid="stats_config_clear"
className="btn btn-outline-secondary btn-standard form__button"
onClick={onReset}
disabled={processingReset}>
<Trans>statistics_clear</Trans>
</button>
</div>
</form>
);
};