From 28cd8a41d27a0892372683738fe1b11d05c30287 Mon Sep 17 00:00:00 2001 From: Ildar Kamalov Date: Tue, 21 Jan 2025 13:47:40 +0300 Subject: [PATCH] add clients forms --- .../src/components/Filters/Services/Form.tsx | 19 +- .../src/components/Settings/Clients/Form.tsx | 514 ------------------ .../Form/components/BlockedServices.tsx | 81 +++ .../Clients/Form/components/ClientIds.tsx | 71 +++ .../Clients/Form/components/MainSettings.tsx | 113 ++++ .../Form/components/ScheduleServices.tsx | 25 + .../Clients/Form/components/UpstreamDns.tsx | 82 +++ .../Settings/Clients/Form/components/index.ts | 5 + .../Settings/Clients/Form/index.tsx | 212 ++++++++ .../components/Settings/Clients/Form/types.ts | 23 + .../src/components/Settings/Clients/Modal.tsx | 19 +- client/src/components/ui/Controls/Input.tsx | 2 +- client/src/helpers/validators.ts | 2 +- client/src/initialState.ts | 42 +- client/src/reducers/index.ts | 2 - client/src/reducers/install.ts | 3 - client/src/reducers/login.ts | 3 - 17 files changed, 660 insertions(+), 558 deletions(-) delete mode 100644 client/src/components/Settings/Clients/Form.tsx create mode 100644 client/src/components/Settings/Clients/Form/components/BlockedServices.tsx create mode 100644 client/src/components/Settings/Clients/Form/components/ClientIds.tsx create mode 100644 client/src/components/Settings/Clients/Form/components/MainSettings.tsx create mode 100644 client/src/components/Settings/Clients/Form/components/ScheduleServices.tsx create mode 100644 client/src/components/Settings/Clients/Form/components/UpstreamDns.tsx create mode 100644 client/src/components/Settings/Clients/Form/components/index.ts create mode 100644 client/src/components/Settings/Clients/Form/index.tsx create mode 100644 client/src/components/Settings/Clients/Form/types.ts diff --git a/client/src/components/Filters/Services/Form.tsx b/client/src/components/Filters/Services/Form.tsx index 7df0cb18..e37b8953 100644 --- a/client/src/components/Filters/Services/Form.tsx +++ b/client/src/components/Filters/Services/Form.tsx @@ -6,15 +6,15 @@ import { Controller, useForm } from 'react-hook-form'; import { ServiceField } from './ServiceField'; -type BlockedService = { +export type BlockedService = { id: string; name: string; icon_svg: string; -} +}; type FormValues = { blocked_services: Record; -} +}; interface FormProps { initialValues: Record; @@ -25,7 +25,12 @@ interface FormProps { } export const Form = ({ initialValues, blockedServices, processing, processingSet, onSubmit }: FormProps) => { - const { handleSubmit, control, setValue, formState: { isSubmitting, isDirty } } = useForm({ + const { + handleSubmit, + control, + setValue, + formState: { isSubmitting, isDirty }, + } = useForm({ mode: 'onChange', defaultValues: initialValues, }); @@ -43,8 +48,7 @@ export const Form = ({ initialValues, blockedServices, processing, processingSet type="button" className="btn btn-secondary btn-block" disabled={processing || processingSet} - onClick={() => handleToggleAllServices(true)} - > + onClick={() => handleToggleAllServices(true)}> block_all @@ -54,8 +58,7 @@ export const Form = ({ initialValues, blockedServices, processing, processingSet type="button" className="btn btn-secondary btn-block" disabled={processing || processingSet} - onClick={() => handleToggleAllServices(false)} - > + onClick={() => handleToggleAllServices(false)}> unblock_all diff --git a/client/src/components/Settings/Clients/Form.tsx b/client/src/components/Settings/Clients/Form.tsx deleted file mode 100644 index 1252bf16..00000000 --- a/client/src/components/Settings/Clients/Form.tsx +++ /dev/null @@ -1,514 +0,0 @@ -import React, { useState } from 'react'; -import { connect, useSelector } from 'react-redux'; -import { Field, FieldArray, reduxForm, formValueSelector, FormErrors } from 'redux-form'; -import { Trans, withTranslation } from 'react-i18next'; -import flow from 'lodash/flow'; - -import Select from 'react-select'; - -import i18n from '../../../i18n'; - -import Tabs from '../../ui/Tabs'; - -import Examples from '../Dns/Upstream/Examples'; - -import { ScheduleForm } from '../../Filters/Services/ScheduleForm'; -import { toggleAllServices, trimLinesAndRemoveEmpty, captitalizeWords } from '../../../helpers/helpers'; -import { - toNumber, - renderInputField, - renderGroupField, - CheckboxField, - renderServiceField, - renderTextareaField, -} from '../../../helpers/form'; -import { validateClientId, validateRequiredValue } from '../../../helpers/validators'; -import { CLIENT_ID_LINK, FORM_NAME, UINT32_RANGE } from '../../../helpers/constants'; -import './Service.css'; -import { RootState } from '../../../initialState'; - -const settingsCheckboxes = [ - { - name: 'use_global_settings', - placeholder: 'client_global_settings', - }, - { - name: 'filtering_enabled', - placeholder: 'block_domain_use_filters_and_hosts', - }, - { - name: 'safebrowsing_enabled', - placeholder: 'use_adguard_browsing_sec', - }, - { - name: 'parental_enabled', - placeholder: 'use_adguard_parental', - }, -]; - -const logAndStatsCheckboxes = [ - { - name: 'ignore_querylog', - placeholder: 'ignore_query_log', - }, - { - name: 'ignore_statistics', - placeholder: 'ignore_statistics', - }, -]; -const validate = (values: any): FormErrors => { - const errors: { - name?: string; - ids?: string[]; - } = {}; - const { name, ids } = values; - - errors.name = validateRequiredValue(name); - - if (ids && ids.length) { - const idArrayErrors: any = []; - ids.forEach((id: any, idx: any) => { - idArrayErrors[idx] = validateRequiredValue(id) || validateClientId(id); - }); - - if (idArrayErrors.length) { - errors.ids = idArrayErrors; - } - } - // @ts-expect-error FIXME: ts migration - return errors; -}; - -const renderFieldsWrapper = (placeholder: any, buttonTitle: any) => - function cell(row: any) { - const { fields } = row; - return ( -
- {fields.map((ip: any, index: any) => ( -
- fields.remove(index)} - normalizeOnBlur={(data: any) => data.trim()} - /> -
- ))} - - -
- ); - }; - -// Should create function outside of component to prevent component re-renders -const renderFields = renderFieldsWrapper(i18n.t('form_enter_id'), i18n.t('form_add_id')); - -interface renderMultiselectProps { - input: { - name: string; - value: string; - checked: boolean; - onChange: (...args: unknown[]) => unknown; - onBlur: (...args: unknown[]) => unknown; - }; - placeholder?: string; - options?: unknown[]; -} - -const renderMultiselect = (props: renderMultiselectProps) => { - const { input, placeholder, options } = props; - - return ( - { + const trimmedValue = event.target.value.trim(); + field.onBlur(); + field.onChange(trimmedValue); + }} + rightAddon={ + index !== 0 && ( + + + + ) + } + /> + )} + /> + + ))} + + + ); +}; diff --git a/client/src/components/Settings/Clients/Form/components/MainSettings.tsx b/client/src/components/Settings/Clients/Form/components/MainSettings.tsx new file mode 100644 index 00000000..c8c03cf4 --- /dev/null +++ b/client/src/components/Settings/Clients/Form/components/MainSettings.tsx @@ -0,0 +1,113 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import { Controller, useFormContext } from 'react-hook-form'; +import i18next from 'i18next'; +import { captitalizeWords } from '../../../../../helpers/helpers'; +import { ClientForm } from '../types'; +import { Checkbox } from '../../../../ui/Controls/Checkbox'; + +type ProtectionSettings = 'use_global_settings' | 'filtering_enabled' | 'safebrowsing_enabled' | 'parental_enabled'; + +const settingsCheckboxes: { + name: ProtectionSettings; + placeholder: string; +}[] = [ + { + name: 'use_global_settings', + placeholder: i18next.t('client_global_settings'), + }, + { + name: 'filtering_enabled', + placeholder: i18next.t('block_domain_use_filters_and_hosts'), + }, + { + name: 'safebrowsing_enabled', + placeholder: i18next.t('use_adguard_browsing_sec'), + }, + { + name: 'parental_enabled', + placeholder: i18next.t('use_adguard_parental'), + }, +]; + +type LogsStatsSettings = 'ignore_querylog' | 'ignore_statistics'; + +const logAndStatsCheckboxes: { name: LogsStatsSettings; placeholder: string }[] = [ + { + name: 'ignore_querylog', + placeholder: i18next.t('ignore_query_log'), + }, + { + name: 'ignore_statistics', + placeholder: i18next.t('ignore_statistics'), + }, +]; + +type Props = { + safeSearchServices: Record; +}; + +export const MainSettings = ({ safeSearchServices }: Props) => { + const { t } = useTranslation(); + const { watch, control } = useFormContext(); + + const useGlobalSettings = watch('use_global_settings'); + + return ( +
+
{t('protection_section_label')}
+ {settingsCheckboxes.map((setting) => ( +
+ ( + + )} + /> +
+ ))} + +
+ ( + + )} + /> +
+ +
+ {Object.keys(safeSearchServices).map((searchKey) => ( +
+ ( + + )} + /> +
+ ))} +
+ +
+ {t('log_and_stats_section_label')} +
+ {logAndStatsCheckboxes.map((setting) => ( +
+ } + /> +
+ ))} +
+ ); +}; diff --git a/client/src/components/Settings/Clients/Form/components/ScheduleServices.tsx b/client/src/components/Settings/Clients/Form/components/ScheduleServices.tsx new file mode 100644 index 00000000..a00787bb --- /dev/null +++ b/client/src/components/Settings/Clients/Form/components/ScheduleServices.tsx @@ -0,0 +1,25 @@ +import React from 'react'; +import { Trans } from 'react-i18next'; +import { useFormContext } from 'react-hook-form'; +import { ScheduleForm } from '../../../../Filters/Services/ScheduleForm'; +import { ClientForm } from '../types'; + +export const ScheduleServices = () => { + const { watch, setValue } = useFormContext(); + + const blockedServicesSchedule = watch('blocked_services_schedule'); + + const handleScheduleSubmit = (values: any) => { + setValue('blocked_services_schedule', values); + }; + + return ( + <> +
+ schedule_services_desc_client +
+ + + + ); +}; diff --git a/client/src/components/Settings/Clients/Form/components/UpstreamDns.tsx b/client/src/components/Settings/Clients/Form/components/UpstreamDns.tsx new file mode 100644 index 00000000..feb26bfc --- /dev/null +++ b/client/src/components/Settings/Clients/Form/components/UpstreamDns.tsx @@ -0,0 +1,82 @@ +import React from 'react'; +import { Trans, useTranslation } from 'react-i18next'; + +import { Controller, useFormContext } from 'react-hook-form'; +import Examples from '../../../Dns/Upstream/Examples'; +import { UINT32_RANGE } from '../../../../../helpers/constants'; +import { Textarea } from '../../../../ui/Controls/Textarea'; +import { ClientForm } from '../types'; +import { Checkbox } from '../../../../ui/Controls/Checkbox'; +import { Input } from '../../../../ui/Controls/Input'; +import { trimLinesAndRemoveEmpty } from '../../../../../helpers/helpers'; + +export const UpstreamDns = () => { + const { t } = useTranslation(); + + const { control } = useFormContext(); + + return ( +
+
+ + link + , + ]}> + upstream_dns_client_desc + +
+ + ( +