mirror of
https://git.vectorsigma.ru/public/AdGuardHome.git
synced 2026-08-04 21:09:15 +00:00
remove unused
This commit is contained in:
@@ -1,115 +0,0 @@
|
||||
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 { DAY, FILTERS_INTERVALS_HOURS, FILTERS_RELATIVE_LINK } from '../../../helpers/constants';
|
||||
import { Checkbox } from '../../ui/Controls/Checkbox';
|
||||
import { Select } from '../../ui/Controls/Select';
|
||||
|
||||
const THREE_DAYS_INTERVAL = DAY * 3;
|
||||
const SEVEN_DAYS_INTERVAL = DAY * 7;
|
||||
|
||||
const getTitleForInterval = (interval: number) => {
|
||||
if (interval === 0) {
|
||||
return i18next.t('disabled');
|
||||
}
|
||||
|
||||
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 = {
|
||||
enabled: boolean;
|
||||
interval: number;
|
||||
};
|
||||
|
||||
type Props = {
|
||||
initialValues: FormValues;
|
||||
setFiltersConfig: (values: FormValues) => void;
|
||||
processing: boolean;
|
||||
};
|
||||
|
||||
export const FiltersConfig = ({ initialValues, setFiltersConfig, processing }: Props) => {
|
||||
const { t } = useTranslation();
|
||||
const prevFormValuesRef = useRef<FormValues>(initialValues);
|
||||
|
||||
const { watch, control } = useForm({
|
||||
mode: 'onBlur',
|
||||
defaultValues: initialValues,
|
||||
});
|
||||
|
||||
const formValues = watch();
|
||||
|
||||
useEffect(() => {
|
||||
const prevFormValues = prevFormValuesRef.current;
|
||||
|
||||
if (JSON.stringify(prevFormValues) !== JSON.stringify(formValues)) {
|
||||
setFiltersConfig(formValues);
|
||||
prevFormValuesRef.current = formValues;
|
||||
}
|
||||
}, [formValues]);
|
||||
|
||||
const components = {
|
||||
a: <a href={FILTERS_RELATIVE_LINK} rel="noopener noreferrer" />,
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="row">
|
||||
<div className="col-12">
|
||||
<div className="form__group form__group--settings">
|
||||
<Controller
|
||||
name="enabled"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Checkbox
|
||||
{...field}
|
||||
data-testid="filters_enabled"
|
||||
title={t('block_domain_use_filters_and_hosts')}
|
||||
disabled={processing}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
||||
<p>
|
||||
<Trans components={components}>filters_block_toggle_hint</Trans>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="col-12 col-md-5">
|
||||
<div className="form__group form__group--inner mb-5">
|
||||
<label className="form__label">
|
||||
<Trans>filters_interval</Trans>
|
||||
</label>
|
||||
<Controller
|
||||
name="interval"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Select
|
||||
{...field}
|
||||
data-testid="filters_interval"
|
||||
disabled={processing}
|
||||
onChange={(e) => {
|
||||
const { value } = e.target;
|
||||
field.onChange(toNumber(value));
|
||||
}}>
|
||||
{FILTERS_INTERVALS_HOURS.map((interval) => (
|
||||
<option value={interval} key={interval}>
|
||||
{getTitleForInterval(interval)}
|
||||
</option>
|
||||
))}
|
||||
</Select>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -1,11 +0,0 @@
|
||||
.form__button {
|
||||
margin-left: 1.5rem;
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
.form__button {
|
||||
margin-left: 0;
|
||||
margin-top: 1rem;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
@@ -1,229 +0,0 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { Trans, useTranslation } from 'react-i18next';
|
||||
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 } 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) {
|
||||
case RETENTION_CUSTOM:
|
||||
return i18next.t('settings_custom');
|
||||
case 6 * HOUR:
|
||||
return i18next.t('interval_6_hour');
|
||||
case DAY:
|
||||
return i18next.t('interval_24_hour');
|
||||
default:
|
||||
return i18next.t('interval_days', { count: interval / DAY });
|
||||
}
|
||||
};
|
||||
|
||||
export type FormValues = {
|
||||
enabled: boolean;
|
||||
anonymize_client_ip: boolean;
|
||||
interval: number;
|
||||
customInterval?: number | null;
|
||||
ignored: string;
|
||||
};
|
||||
|
||||
type Props = {
|
||||
initialValues: Partial<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: {
|
||||
enabled: initialValues.enabled || false,
|
||||
anonymize_client_ip: initialValues.anonymize_client_ip || false,
|
||||
interval: initialValues.interval || DAY,
|
||||
customInterval: initialValues.customInterval || null,
|
||||
ignored: initialValues.ignored || '',
|
||||
},
|
||||
});
|
||||
|
||||
const intervalValue = watch('interval');
|
||||
const customIntervalValue = watch('customInterval');
|
||||
|
||||
useEffect(() => {
|
||||
if (QUERY_LOG_INTERVALS_DAYS.includes(intervalValue)) {
|
||||
setValue('customInterval', null);
|
||||
}
|
||||
}, [intervalValue]);
|
||||
|
||||
const onSubmitForm = (data: FormValues) => {
|
||||
onSubmit(data);
|
||||
};
|
||||
|
||||
const handleIgnoredBlur = (e: React.FocusEvent<HTMLTextAreaElement>) => {
|
||||
const trimmed = trimLinesAndRemoveEmpty(e.target.value);
|
||||
setValue('ignored', trimmed);
|
||||
};
|
||||
|
||||
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="logs_enabled"
|
||||
title={t('query_log_enable')}
|
||||
disabled={processing}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="form__group form__group--settings">
|
||||
<Controller
|
||||
name="anonymize_client_ip"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Checkbox
|
||||
{...field}
|
||||
data-testid="logs_anonymize_client_ip"
|
||||
title={t('anonymize_client_ip')}
|
||||
subtitle={t('anonymize_client_ip_desc')}
|
||||
disabled={processing}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="form__label">
|
||||
<Trans>query_log_retention</Trans>
|
||||
</div>
|
||||
|
||||
<div className="form__group form__group--settings">
|
||||
<div className="custom-controls-stacked">
|
||||
<label className="custom-control custom-radio">
|
||||
<input
|
||||
type="radio"
|
||||
data-testid="logs_config_interval"
|
||||
className="custom-control-input"
|
||||
disabled={processing}
|
||||
checked={!QUERY_LOG_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>
|
||||
|
||||
{!QUERY_LOG_INTERVALS_DAYS.includes(intervalValue) && (
|
||||
<div className="form__group--input">
|
||||
<div className="form__desc form__desc--top">{t('custom_rotation_input')}</div>
|
||||
|
||||
<Controller
|
||||
name="customInterval"
|
||||
control={control}
|
||||
render={({ field, fieldState }) => (
|
||||
<Input
|
||||
{...field}
|
||||
data-testid="logs_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>
|
||||
)}
|
||||
|
||||
{QUERY_LOG_INTERVALS_DAYS.map((interval) => (
|
||||
<label key={interval} className="custom-control custom-radio">
|
||||
<input
|
||||
type="radio"
|
||||
className="custom-control-input"
|
||||
data-testid={`logs_config_${interval}`}
|
||||
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>
|
||||
|
||||
<label className="form__label form__label--with-desc">
|
||||
<Trans>ignore_domains_title</Trans>
|
||||
</label>
|
||||
|
||||
<div className="form__desc form__desc--top">
|
||||
<Trans>ignore_domains_desc_query</Trans>
|
||||
</div>
|
||||
|
||||
<div className="form__group form__group--settings">
|
||||
<Controller
|
||||
name="ignored"
|
||||
control={control}
|
||||
render={({ field, fieldState }) => (
|
||||
<Textarea
|
||||
{...field}
|
||||
data-testid="logs_config_ingored"
|
||||
placeholder={t('ignore_domains')}
|
||||
className="text-input"
|
||||
disabled={processing}
|
||||
error={fieldState.error?.message}
|
||||
onBlur={handleIgnoredBlur}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mt-5">
|
||||
<button
|
||||
type="submit"
|
||||
data-testid="logs_config_save"
|
||||
className="btn btn-success btn-standard btn-large"
|
||||
disabled={disableSubmit}>
|
||||
<Trans>save_btn</Trans>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
data-testid="logs_config_clear"
|
||||
className="btn btn-outline-secondary btn-standard form__button"
|
||||
onClick={onReset}
|
||||
disabled={processingReset}>
|
||||
<Trans>query_log_clear</Trans>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
@@ -1,87 +0,0 @@
|
||||
import React, { Component } from 'react';
|
||||
import { withTranslation } from 'react-i18next';
|
||||
|
||||
import Card from '../../ui/Card';
|
||||
|
||||
import { Form, FormValues } from './Form';
|
||||
import { HOUR } from '../../../helpers/constants';
|
||||
|
||||
interface LogsConfigProps {
|
||||
interval: number;
|
||||
customInterval?: number;
|
||||
enabled: boolean;
|
||||
anonymize_client_ip: boolean;
|
||||
processing: boolean;
|
||||
ignored: unknown[];
|
||||
processingClear: boolean;
|
||||
setLogsConfig: (...args: unknown[]) => unknown;
|
||||
clearLogs: (...args: unknown[]) => unknown;
|
||||
t: (...args: unknown[]) => string;
|
||||
}
|
||||
|
||||
class LogsConfig extends Component<LogsConfigProps> {
|
||||
handleFormSubmit = (values: FormValues) => {
|
||||
const { t, interval: prevInterval } = this.props;
|
||||
const { interval, customInterval, ...rest } = values;
|
||||
|
||||
const newInterval = customInterval ? customInterval * HOUR : interval;
|
||||
|
||||
const data = {
|
||||
...rest,
|
||||
ignored: values.ignored ? values.ignored.split('\n') : [],
|
||||
interval: newInterval,
|
||||
};
|
||||
|
||||
if (newInterval < prevInterval) {
|
||||
// eslint-disable-next-line no-alert
|
||||
if (window.confirm(t('query_log_retention_confirm'))) {
|
||||
this.props.setLogsConfig(data);
|
||||
}
|
||||
} else {
|
||||
this.props.setLogsConfig(data);
|
||||
}
|
||||
};
|
||||
|
||||
handleClear = () => {
|
||||
const { t, clearLogs } = this.props;
|
||||
// eslint-disable-next-line no-alert
|
||||
if (window.confirm(t('query_log_confirm_clear'))) {
|
||||
clearLogs();
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
t,
|
||||
enabled,
|
||||
interval,
|
||||
processing,
|
||||
processingClear,
|
||||
anonymize_client_ip,
|
||||
ignored,
|
||||
customInterval,
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<Card title={t('query_log_configuration')} bodyType="card-body box-body--settings" id="logs-config">
|
||||
<div className="form">
|
||||
<Form
|
||||
initialValues={{
|
||||
enabled,
|
||||
interval,
|
||||
customInterval,
|
||||
anonymize_client_ip,
|
||||
ignored: ignored?.join('\n'),
|
||||
}}
|
||||
processing={processing}
|
||||
processingReset={processingClear}
|
||||
onSubmit={this.handleFormSubmit}
|
||||
onReset={this.handleClear}
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withTranslation()(LogsConfig);
|
||||
@@ -1,171 +0,0 @@
|
||||
.form__group {
|
||||
position: relative;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.form__group:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.form__group--settings:last-child {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.form__group--inner {
|
||||
max-width: 300px;
|
||||
margin-top: -10px;
|
||||
margin-left: 40px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.form__group--input {
|
||||
max-width: 300px;
|
||||
margin: 0 1.5rem 10px;
|
||||
}
|
||||
|
||||
.form__group--checkbox {
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.form__group--inner .form__group--checkbox {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.form__group--inner .form__group--checkbox:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.form__inline {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.btn-standard {
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
.btn-large {
|
||||
min-width: 150px;
|
||||
}
|
||||
|
||||
.form-control--textarea {
|
||||
min-height: 110px;
|
||||
transition: 0.3s ease-in-out background-color;
|
||||
}
|
||||
|
||||
.form-control--textarea-small {
|
||||
min-height: 90px;
|
||||
}
|
||||
|
||||
.form-control--textarea-large {
|
||||
min-height: 240px;
|
||||
}
|
||||
|
||||
.form__message {
|
||||
margin-top: 4px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.form__message--error {
|
||||
color: #cd201f;
|
||||
}
|
||||
|
||||
.form__message--left-pad {
|
||||
padding-left: 0.85rem;
|
||||
}
|
||||
|
||||
.interface__title {
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.interface__ip:after {
|
||||
content: ', ';
|
||||
}
|
||||
|
||||
.interface__ip:last-child:after {
|
||||
content: '';
|
||||
}
|
||||
|
||||
.form__desc {
|
||||
margin-top: 10px;
|
||||
font-size: 13px;
|
||||
color: var(--scolor);
|
||||
}
|
||||
|
||||
.form__desc--top {
|
||||
margin: 0 0 8px;
|
||||
}
|
||||
|
||||
.form__label {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.form__label--bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.form__label--with-desc {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.form__label--bot {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.form__label--top {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.form__status {
|
||||
margin-top: 10px;
|
||||
font-size: 14px;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.encryption__list {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.encryption__list li {
|
||||
list-style: inside;
|
||||
}
|
||||
|
||||
.btn-icon {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
background-color: transparent;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.btn-icon--green {
|
||||
color: var(--green);
|
||||
}
|
||||
|
||||
.btn-icon-sm {
|
||||
width: 23px;
|
||||
height: 23px;
|
||||
min-width: 23px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.custom-control-label,
|
||||
.custom-control-label:before {
|
||||
transition:
|
||||
0.3s ease-in-out background-color,
|
||||
0.3s ease-in-out color;
|
||||
}
|
||||
|
||||
.custom-select:disabled {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
.custom-select {
|
||||
transition:
|
||||
0.3s ease-in-out background-color,
|
||||
0.3s ease-in-out color;
|
||||
}
|
||||
@@ -1,211 +0,0 @@
|
||||
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>
|
||||
);
|
||||
};
|
||||
@@ -1,81 +0,0 @@
|
||||
import React, { Component } from 'react';
|
||||
import { withTranslation } from 'react-i18next';
|
||||
|
||||
import Card from '../../ui/Card';
|
||||
|
||||
import { Form, FormValues } from './Form';
|
||||
import { HOUR } from '../../../helpers/constants';
|
||||
|
||||
interface StatsConfigProps {
|
||||
interval: number;
|
||||
customInterval?: number;
|
||||
ignored: unknown[];
|
||||
enabled: boolean;
|
||||
processing: boolean;
|
||||
processingReset: boolean;
|
||||
setStatsConfig: (...args: unknown[]) => unknown;
|
||||
resetStats: (...args: unknown[]) => unknown;
|
||||
t: (...args: unknown[]) => string;
|
||||
}
|
||||
|
||||
class StatsConfig extends Component<StatsConfigProps> {
|
||||
handleFormSubmit = ({ enabled, interval, ignored, customInterval }: FormValues) => {
|
||||
const { t, interval: prevInterval } = this.props;
|
||||
const newInterval = customInterval ? customInterval * HOUR : interval;
|
||||
|
||||
const config = {
|
||||
enabled,
|
||||
interval: newInterval,
|
||||
ignored: ignored ? ignored.split('\n') : [],
|
||||
};
|
||||
|
||||
if (config.interval < prevInterval) {
|
||||
if (window.confirm(t('statistics_retention_confirm'))) {
|
||||
this.props.setStatsConfig(config);
|
||||
}
|
||||
} else {
|
||||
this.props.setStatsConfig(config);
|
||||
}
|
||||
};
|
||||
|
||||
handleReset = () => {
|
||||
const { t, resetStats } = this.props;
|
||||
// eslint-disable-next-line no-alert
|
||||
if (window.confirm(t('statistics_clear_confirm'))) {
|
||||
resetStats();
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
t,
|
||||
interval,
|
||||
customInterval,
|
||||
processing,
|
||||
processingReset,
|
||||
ignored,
|
||||
enabled,
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<Card title={t('statistics_configuration')} bodyType="card-body box-body--settings" id="stats-config">
|
||||
<div className="form">
|
||||
<Form
|
||||
initialValues={{
|
||||
interval,
|
||||
customInterval,
|
||||
enabled,
|
||||
ignored: ignored.join('\n'),
|
||||
}}
|
||||
processing={processing}
|
||||
processingReset={processingReset}
|
||||
onSubmit={this.handleFormSubmit}
|
||||
onReset={this.handleReset}
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withTranslation()(StatsConfig);
|
||||
@@ -1,228 +0,0 @@
|
||||
import React, { Component, Fragment } from 'react';
|
||||
import { withTranslation } from 'react-i18next';
|
||||
|
||||
import i18next from 'i18next';
|
||||
import StatsConfig from './StatsConfig';
|
||||
|
||||
import LogsConfig from './LogsConfig';
|
||||
|
||||
import { FiltersConfig } from './FiltersConfig';
|
||||
|
||||
import { Checkbox } from '../ui/Controls/Checkbox';
|
||||
|
||||
import Loading from '../ui/Loading';
|
||||
|
||||
import PageTitle from '../ui/PageTitle';
|
||||
|
||||
import Card from '../ui/Card';
|
||||
|
||||
import { getObjectKeysSorted, captitalizeWords } from '../../helpers/helpers';
|
||||
import './Settings.css';
|
||||
import { SettingsData } from '../../initialState';
|
||||
|
||||
const ORDER_KEY = 'order';
|
||||
|
||||
const SETTINGS = {
|
||||
safebrowsing: {
|
||||
enabled: false,
|
||||
title: i18next.t('use_adguard_browsing_sec'),
|
||||
subtitle: i18next.t('use_adguard_browsing_sec_hint'),
|
||||
testId: 'safebrowsing',
|
||||
[ORDER_KEY]: 0,
|
||||
},
|
||||
parental: {
|
||||
enabled: false,
|
||||
title: i18next.t('use_adguard_parental'),
|
||||
subtitle: i18next.t('use_adguard_parental_hint'),
|
||||
testId: 'parental',
|
||||
[ORDER_KEY]: 1,
|
||||
},
|
||||
};
|
||||
|
||||
interface SettingsProps {
|
||||
initSettings: (...args: unknown[]) => unknown;
|
||||
settings: SettingsData;
|
||||
toggleSetting: (...args: unknown[]) => unknown;
|
||||
getStatsConfig: (...args: unknown[]) => unknown;
|
||||
setStatsConfig: (...args: unknown[]) => unknown;
|
||||
resetStats: (...args: unknown[]) => unknown;
|
||||
setFiltersConfig: (...args: unknown[]) => unknown;
|
||||
getFilteringStatus: (...args: unknown[]) => unknown;
|
||||
t: (...args: unknown[]) => string;
|
||||
getLogsConfig?: (...args: unknown[]) => unknown;
|
||||
setLogsConfig?: (...args: unknown[]) => unknown;
|
||||
clearLogs?: (...args: unknown[]) => unknown;
|
||||
stats?: {
|
||||
processingGetConfig?: boolean;
|
||||
interval?: number;
|
||||
customInterval?: number;
|
||||
enabled?: boolean;
|
||||
ignored?: unknown[];
|
||||
processingSetConfig?: boolean;
|
||||
processingReset?: boolean;
|
||||
};
|
||||
queryLogs?: {
|
||||
enabled?: boolean;
|
||||
interval?: number;
|
||||
customInterval?: number;
|
||||
anonymize_client_ip?: boolean;
|
||||
processingSetConfig?: boolean;
|
||||
processingClear?: boolean;
|
||||
processingGetConfig?: boolean;
|
||||
ignored?: unknown[];
|
||||
};
|
||||
filtering?: {
|
||||
interval?: number;
|
||||
enabled?: boolean;
|
||||
processingSetConfig?: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
class Settings extends Component<SettingsProps> {
|
||||
componentDidMount() {
|
||||
this.props.initSettings(SETTINGS);
|
||||
|
||||
this.props.getStatsConfig();
|
||||
|
||||
this.props.getLogsConfig();
|
||||
|
||||
this.props.getFilteringStatus();
|
||||
}
|
||||
|
||||
renderSettings = (settings: any) =>
|
||||
getObjectKeysSorted(SETTINGS, ORDER_KEY).map((key: any) => {
|
||||
const setting = settings[key];
|
||||
const { enabled, title, subtitle, testId } = setting;
|
||||
|
||||
return (
|
||||
<div key={key} className="form__group form__group--checkbox">
|
||||
<Checkbox
|
||||
data-testid={testId}
|
||||
value={enabled}
|
||||
title={title}
|
||||
subtitle={subtitle}
|
||||
onChange={(checked) => this.props.toggleSetting(key, !checked)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
renderSafeSearch = () => {
|
||||
const {
|
||||
settings: {
|
||||
settingsList: { safesearch },
|
||||
},
|
||||
} = this.props;
|
||||
const { enabled } = safesearch || {};
|
||||
const searches = { ...(safesearch || {}) };
|
||||
delete searches.enabled;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="form__group form__group--checkbox">
|
||||
<Checkbox
|
||||
data-testid="safesearch"
|
||||
value={enabled}
|
||||
title={i18next.t('enforce_safe_search')}
|
||||
subtitle={i18next.t('enforce_save_search_hint')}
|
||||
onChange={(checked) =>
|
||||
this.props.toggleSetting('safesearch', { ...safesearch, enabled: checked })
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="form__group--inner">
|
||||
{Object.keys(searches).map((searchKey) => (
|
||||
<div key={searchKey} className="form__group form__group--checkbox">
|
||||
<Checkbox
|
||||
value={searches[searchKey]}
|
||||
title={captitalizeWords(searchKey)}
|
||||
disabled={!safesearch.enabled}
|
||||
onChange={(checked) =>
|
||||
this.props.toggleSetting('safesearch', { ...safesearch, [searchKey]: checked })
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
settings,
|
||||
setStatsConfig,
|
||||
resetStats,
|
||||
stats,
|
||||
queryLogs,
|
||||
setLogsConfig,
|
||||
clearLogs,
|
||||
filtering,
|
||||
setFiltersConfig,
|
||||
t,
|
||||
} = this.props;
|
||||
|
||||
const isDataReady = !settings.processing && !stats.processingGetConfig && !queryLogs.processingGetConfig;
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<PageTitle title={t('general_settings')} />
|
||||
|
||||
{!isDataReady && <Loading />}
|
||||
|
||||
{isDataReady && (
|
||||
<div className="content">
|
||||
<div className="row">
|
||||
<div className="col-md-12">
|
||||
<Card bodyType="card-body box-body--settings">
|
||||
<div className="form">
|
||||
<FiltersConfig
|
||||
initialValues={{
|
||||
interval: filtering.interval,
|
||||
enabled: filtering.enabled,
|
||||
}}
|
||||
processing={filtering.processingSetConfig}
|
||||
setFiltersConfig={setFiltersConfig}
|
||||
/>
|
||||
{this.renderSettings(settings.settingsList)}
|
||||
{this.renderSafeSearch()}
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div className="col-md-12">
|
||||
<LogsConfig
|
||||
enabled={queryLogs.enabled}
|
||||
ignored={queryLogs.ignored}
|
||||
interval={queryLogs.interval}
|
||||
customInterval={queryLogs.customInterval}
|
||||
anonymize_client_ip={queryLogs.anonymize_client_ip}
|
||||
processing={queryLogs.processingSetConfig}
|
||||
processingClear={queryLogs.processingClear}
|
||||
setLogsConfig={setLogsConfig}
|
||||
clearLogs={clearLogs}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="col-md-12">
|
||||
<StatsConfig
|
||||
interval={stats.interval}
|
||||
customInterval={stats.customInterval}
|
||||
ignored={stats.ignored}
|
||||
enabled={stats.enabled}
|
||||
processing={stats.processingSetConfig}
|
||||
processingReset={stats.processingReset}
|
||||
setStatsConfig={setStatsConfig}
|
||||
resetStats={resetStats}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withTranslation()(Settings);
|
||||
Reference in New Issue
Block a user