diff --git a/client/src/__locales/en.json b/client/src/__locales/en.json index 570d28e1..9c59c654 100644 --- a/client/src/__locales/en.json +++ b/client/src/__locales/en.json @@ -378,5 +378,11 @@ "statistics_retention_desc": "If you decrease the interval value, some data will be lost", "statistics_clear": " Clear statistics", "statistics_clear_confirm": "Are you sure you want to clear statistics?", - "statistics_cleared": "Statistics successfully cleared" + "statistics_cleared": "Statistics successfully cleared", + "interval_hours": "{{count}} hour", + "interval_hours_plural": "{{count}} hours", + "filters_configuration": "Filters configuration", + "filters_enable": "Enable filters", + "filters_interval": "Filters update interval", + "disabled": "Disabled" } diff --git a/client/src/__locales/ru.json b/client/src/__locales/ru.json index 4bca94d4..88799f70 100644 --- a/client/src/__locales/ru.json +++ b/client/src/__locales/ru.json @@ -1,10 +1,9 @@ { "client_settings": "Настройки клиентов", "example_upstream_reserved": "вы можете указать DNS-сервер <0>для конкретного домена(ов)", - "upstream_parallel": "Использовать одновременные запроссы ко всем серверам для ускорения обработки запроса", + "upstream_parallel": "Использовать одновременные запросы ко всем серверам для ускорения обработки запроса", "bootstrap_dns": "Bootstrap DNS-серверы", "bootstrap_dns_desc": "Bootstrap DNS-серверы используются для поиска IP-адресов DoH/DoT серверов, которые вы указали.", - "url_added_successfully": "URL успешно добавлен", "check_dhcp_servers": "Проверить DHCP-серверы", "save_config": "Сохранить конфигурацию", "enabled_dhcp": "DHCP-сервер включен", @@ -67,7 +66,6 @@ "disabled_protection": "Защита выкл.", "refresh_statics": "Обновить статистику", "dns_query": "DNS-запросы", - "blocked_by": "Заблокировано фильтрами", "stats_malware_phishing": "Заблокированные вредоносные и фишинговые сайты", "stats_adult": "Заблокированные \"взрослые\" сайты", "stats_query_domain": "Часто запрашиваемые домены", @@ -78,7 +76,6 @@ "top_clients": "Частые клиенты", "no_clients_found": "Клиентов не найдено", "general_statistics": "Общая статистика", - "number_of_dns_query_24_hours": "Количество DNS-запросов за 24 часа", "number_of_dns_query_blocked_24_hours": "Количество DNS-запросов, заблокированных фильтрами и блок-списками", "number_of_dns_query_blocked_24_hours_by_sec": "Количество DNS-запросов, заблокированных модулем Антифишинга AdGuard", "number_of_dns_query_blocked_24_hours_adult": "Количество заблокированных \"сайтов для взрослых\"", @@ -211,7 +208,7 @@ "install_devices_router_list_2": "Найдите настройки DHCP или DNS. Найдите буквы \"DNS\" рядом с текстовым полем, в которое можно ввести два или три ряда цифр, разделенных на 4 группы от одной до трёх цифр.", "install_devices_router_list_3": "Введите туда адрес вашего AdGuard Home.", "install_devices_windows_list_1": "Откройте Панель управления через меню \"Пуск\" или через поиск Windows.", - "install_devices_windows_list_2": "Откройте Панель управления через меню \"Пуск\" или через поиск Windows.", + "install_devices_windows_list_2": "Перейдите в \"Сеть и интернет\", а затем в \"Центр управления сетями и общим доступом\"", "install_devices_windows_list_3": "В левой стороне экрана найдите \"Изменение параметров адаптера\" и кликните по нему.", "install_devices_windows_list_4": "Выделите ваше активное подключение, затем кликните по нему правой клавишей мыши и выберите \"Свойства\".", "install_devices_windows_list_5": "Найдите в списке пункт \"IP версии 4 (TCP/IP)\", выделите его и затем снова нажмите \"Свойства\".", @@ -298,7 +295,6 @@ "client_deleted": "Клиент \"{{key}}\" успешно удален", "client_added": "Клиент \"{{key}}\" успешно добавлен", "client_updated": "Клиент \"{{key}}\" успешно обновлен", - "table_statistics": "Количество запросов (последние 24 часа)", "clients_not_found": "Клиентов не найдено", "client_confirm_delete": "Вы уверены, что хотите удалить клиента \"{{key}}\"?", "filter_confirm_delete": "Вы уверены, что хотите удалить фильтр?", @@ -309,7 +305,7 @@ "access_allowed_title": "Разрешенные клиенты", "access_allowed_desc": "Список CIDR- или IP-адресов. Если он настроен, AdGuard Home будет принимать запросы только с этих IP-адресов.", "access_disallowed_title": "Запрещенные клиенты", - "access_disallowed_desc": "Список CIDR- или IP-адресов. Если он настроек, AdGuard Home будет игнорировать запросы с этих IP-адресов.", + "access_disallowed_desc": "Список CIDR- или IP-адресов. Если он настроен, AdGuard Home будет игнорировать запросы с этих IP-адресов.", "access_blocked_title": "Заблокированные домены", "access_blocked_desc": "Не путайте это с фильтрами. AdGuard Home будет игнорировать DNS-запросы с этими доменами.", "access_settings_saved": "Настройки доступа успешно сохранены", @@ -353,5 +349,13 @@ "blocked_services_global": "Использовать глобальные заблокированные сервисы", "blocked_service": "Заблокированный сервис", "block_all": "Заблокировать все", - "unblock_all": "Разблокировать все" + "unblock_all": "Разблокировать все", + "domain": "Домен", + "answer": "Ответ", + "interval_hours_0": "{{count}} час", + "interval_hours_1": "{{count}} часа", + "interval_hours_2": "{{count}} часов", + "interval_days_0": "{{count}} день", + "interval_days_1": "{{count}} дня", + "interval_days_2": "{{count}} дней" } \ No newline at end of file diff --git a/client/src/actions/filtering.js b/client/src/actions/filtering.js new file mode 100644 index 00000000..3f0c59d9 --- /dev/null +++ b/client/src/actions/filtering.js @@ -0,0 +1,145 @@ +import { createAction } from 'redux-actions'; +import { showLoading, hideLoading } from 'react-redux-loading-bar'; + +import { normalizeFilteringStatus, normalizeRulesTextarea } from '../helpers/helpers'; +import { addErrorToast, addSuccessToast } from './index'; +import apiClient from '../api/Api'; + +export const toggleFilteringModal = createAction('FILTERING_MODAL_TOGGLE'); +export const handleRulesChange = createAction('HANDLE_RULES_CHANGE'); + +export const getFilteringStatusRequest = createAction('GET_FILTERING_STATUS_REQUEST'); +export const getFilteringStatusFailure = createAction('GET_FILTERING_STATUS_FAILURE'); +export const getFilteringStatusSuccess = createAction('GET_FILTERING_STATUS_SUCCESS'); + +export const getFilteringStatus = () => async (dispatch) => { + dispatch(getFilteringStatusRequest()); + try { + const status = await apiClient.getFilteringStatus(); + dispatch(getFilteringStatusSuccess({ ...normalizeFilteringStatus(status) })); + } catch (error) { + dispatch(addErrorToast({ error })); + dispatch(getFilteringStatusFailure()); + } +}; + +export const setRulesRequest = createAction('SET_RULES_REQUEST'); +export const setRulesFailure = createAction('SET_RULES_FAILURE'); +export const setRulesSuccess = createAction('SET_RULES_SUCCESS'); + +export const setRules = rules => async (dispatch) => { + dispatch(setRulesRequest()); + try { + const normalizedRules = normalizeRulesTextarea(rules); + await apiClient.setRules(normalizedRules); + dispatch(addSuccessToast('updated_custom_filtering_toast')); + dispatch(setRulesSuccess()); + } catch (error) { + dispatch(addErrorToast({ error })); + dispatch(setRulesFailure()); + } +}; + +export const addFilterRequest = createAction('ADD_FILTER_REQUEST'); +export const addFilterFailure = createAction('ADD_FILTER_FAILURE'); +export const addFilterSuccess = createAction('ADD_FILTER_SUCCESS'); + +export const addFilter = (url, name) => async (dispatch) => { + dispatch(addFilterRequest()); + try { + await apiClient.addFilter(url, name); + dispatch(addFilterSuccess(url)); + dispatch(toggleFilteringModal()); + dispatch(addSuccessToast('filter_added_successfully')); + dispatch(getFilteringStatus()); + } catch (error) { + dispatch(addErrorToast({ error })); + dispatch(addFilterFailure()); + } +}; + +export const removeFilterRequest = createAction('REMOVE_FILTER_REQUEST'); +export const removeFilterFailure = createAction('REMOVE_FILTER_FAILURE'); +export const removeFilterSuccess = createAction('REMOVE_FILTER_SUCCESS'); + +export const removeFilter = url => async (dispatch) => { + dispatch(removeFilterRequest()); + try { + await apiClient.removeFilter(url); + dispatch(removeFilterSuccess(url)); + dispatch(getFilteringStatus()); + } catch (error) { + dispatch(addErrorToast({ error })); + dispatch(removeFilterFailure()); + } +}; + +export const toggleFilterRequest = createAction('FILTER_TOGGLE_REQUEST'); +export const toggleFilterFailure = createAction('FILTER_TOGGLE_FAILURE'); +export const toggleFilterSuccess = createAction('FILTER_TOGGLE_SUCCESS'); + +export const toggleFilterStatus = (url, enabled) => async (dispatch) => { + dispatch(toggleFilterRequest()); + try { + await apiClient.setFilterUrl({ url, enabled: !enabled }); + dispatch(toggleFilterSuccess(url)); + dispatch(getFilteringStatus()); + } catch (error) { + dispatch(addErrorToast({ error })); + dispatch(toggleFilterFailure()); + } +}; + +export const refreshFiltersRequest = createAction('FILTERING_REFRESH_REQUEST'); +export const refreshFiltersFailure = createAction('FILTERING_REFRESH_FAILURE'); +export const refreshFiltersSuccess = createAction('FILTERING_REFRESH_SUCCESS'); + +export const refreshFilters = () => async (dispatch) => { + dispatch(refreshFiltersRequest()); + dispatch(showLoading()); + try { + const refreshText = await apiClient.refreshFilters(); + dispatch(refreshFiltersSuccess()); + + if (refreshText.includes('OK')) { + if (refreshText.includes('OK 0')) { + dispatch(addSuccessToast('all_filters_up_to_date_toast')); + } else { + dispatch(addSuccessToast(refreshText.replace(/OK /g, ''))); + } + } else { + dispatch(addErrorToast({ error: refreshText })); + } + + dispatch(getFilteringStatus()); + dispatch(hideLoading()); + } catch (error) { + dispatch(addErrorToast({ error })); + dispatch(refreshFiltersFailure()); + dispatch(hideLoading()); + } +}; + +export const setFiltersConfigRequest = createAction('SET_FILTERS_CONFIG_REQUEST'); +export const setFiltersConfigFailure = createAction('SET_FILTERS_CONFIG_FAILURE'); +export const setFiltersConfigSuccess = createAction('SET_FILTERS_CONFIG_SUCCESS'); + +export const setFiltersConfig = config => async (dispatch, getState) => { + dispatch(setFiltersConfigRequest()); + try { + const { enabled } = config; + const prevEnabled = getState().filtering.enabled; + let successToastMessage = 'config_successfully_saved'; + + if (prevEnabled !== enabled) { + successToastMessage = enabled ? 'enabled_filtering_toast' : 'disabled_filtering_toast'; + } + + await apiClient.setFiltersConfig(config); + dispatch(addSuccessToast(successToastMessage)); + dispatch(setFiltersConfigSuccess(config)); + } catch (error) { + dispatch(addErrorToast({ error })); + dispatch(setFiltersConfigFailure()); + } +}; diff --git a/client/src/actions/index.js b/client/src/actions/index.js index 8a132060..88e14ef9 100644 --- a/client/src/actions/index.js +++ b/client/src/actions/index.js @@ -1,10 +1,9 @@ import { createAction } from 'redux-actions'; import { t } from 'i18next'; -import { showLoading, hideLoading } from 'react-redux-loading-bar'; import axios from 'axios'; import versionCompare from '../helpers/versionCompare'; -import { normalizeFilteringStatus, normalizeTextarea, sortClients } from '../helpers/helpers'; +import { normalizeTextarea, sortClients } from '../helpers/helpers'; import { SETTINGS_NAMES, CHECK_TIMEOUT } from '../helpers/constants'; import { getTlsStatus } from './encryption'; import apiClient from '../api/Api'; @@ -21,16 +20,6 @@ export const toggleSetting = (settingKey, status) => async (dispatch) => { let successMessage = ''; try { switch (settingKey) { - case SETTINGS_NAMES.filtering: - if (status) { - successMessage = 'disabled_filtering_toast'; - await apiClient.disableFiltering(); - } else { - successMessage = 'enabled_filtering_toast'; - await apiClient.enableFiltering(); - } - dispatch(toggleSettingStatus({ settingKey })); - break; case SETTINGS_NAMES.safebrowsing: if (status) { successMessage = 'disabled_safe_browsing_toast'; @@ -77,18 +66,15 @@ export const initSettingsSuccess = createAction('SETTINGS_INIT_SUCCESS'); export const initSettings = settingsList => async (dispatch) => { dispatch(initSettingsRequest()); try { - const filteringStatus = await apiClient.getFilteringStatus(); const safebrowsingStatus = await apiClient.getSafebrowsingStatus(); const parentalStatus = await apiClient.getParentalStatus(); const safesearchStatus = await apiClient.getSafesearchStatus(); const { - filtering, safebrowsing, parental, safesearch, } = settingsList; const newSettingsList = { - filtering: { ...filtering, enabled: filteringStatus.enabled }, safebrowsing: { ...safebrowsing, enabled: safebrowsingStatus.enabled }, parental: { ...parental, enabled: parentalStatus.enabled }, safesearch: { ...safesearch, enabled: safesearchStatus.enabled }, @@ -100,21 +86,6 @@ export const initSettings = settingsList => async (dispatch) => { } }; -export const getFilteringRequest = createAction('GET_FILTERING_REQUEST'); -export const getFilteringFailure = createAction('GET_FILTERING_FAILURE'); -export const getFilteringSuccess = createAction('GET_FILTERING_SUCCESS'); - -export const getFiltering = () => async (dispatch) => { - dispatch(getFilteringRequest()); - try { - const filteringStatus = await apiClient.getFilteringStatus(); - dispatch(getFilteringSuccess(filteringStatus.enabled)); - } catch (error) { - dispatch(addErrorToast({ error })); - dispatch(getFilteringFailure()); - } -}; - export const toggleProtectionRequest = createAction('TOGGLE_PROTECTION_REQUEST'); export const toggleProtectionFailure = createAction('TOGGLE_PROTECTION_FAILURE'); export const toggleProtectionSuccess = createAction('TOGGLE_PROTECTION_SUCCESS'); @@ -290,133 +261,6 @@ export const disableDns = () => async (dispatch) => { } }; -export const setRulesRequest = createAction('SET_RULES_REQUEST'); -export const setRulesFailure = createAction('SET_RULES_FAILURE'); -export const setRulesSuccess = createAction('SET_RULES_SUCCESS'); - -export const setRules = rules => async (dispatch) => { - dispatch(setRulesRequest()); - try { - const replacedLineEndings = rules - .replace(/^\n/g, '') - .replace(/\n\s*\n/g, '\n'); - await apiClient.setRules(replacedLineEndings); - dispatch(addSuccessToast('updated_custom_filtering_toast')); - dispatch(setRulesSuccess()); - } catch (error) { - dispatch(addErrorToast({ error })); - dispatch(setRulesFailure()); - } -}; - -export const getFilteringStatusRequest = createAction('GET_FILTERING_STATUS_REQUEST'); -export const getFilteringStatusFailure = createAction('GET_FILTERING_STATUS_FAILURE'); -export const getFilteringStatusSuccess = createAction('GET_FILTERING_STATUS_SUCCESS'); - -export const getFilteringStatus = () => async (dispatch) => { - dispatch(getFilteringStatusRequest()); - try { - const status = await apiClient.getFilteringStatus(); - dispatch(getFilteringStatusSuccess({ status: normalizeFilteringStatus(status) })); - } catch (error) { - dispatch(addErrorToast({ error })); - dispatch(getFilteringStatusFailure()); - } -}; - -export const toggleFilterRequest = createAction('FILTER_ENABLE_REQUEST'); -export const toggleFilterFailure = createAction('FILTER_ENABLE_FAILURE'); -export const toggleFilterSuccess = createAction('FILTER_ENABLE_SUCCESS'); - -export const toggleFilterStatus = url => async (dispatch, getState) => { - dispatch(toggleFilterRequest()); - const state = getState(); - const { filters } = state.filtering; - const filter = filters.filter(filter => filter.url === url)[0]; - const { enabled } = filter; - let toggleStatusMethod; - if (enabled) { - toggleStatusMethod = apiClient.disableFilter.bind(apiClient); - } else { - toggleStatusMethod = apiClient.enableFilter.bind(apiClient); - } - try { - await toggleStatusMethod(url); - dispatch(toggleFilterSuccess(url)); - dispatch(getFilteringStatus()); - } catch (error) { - dispatch(addErrorToast({ error })); - dispatch(toggleFilterFailure()); - } -}; - -export const refreshFiltersRequest = createAction('FILTERING_REFRESH_REQUEST'); -export const refreshFiltersFailure = createAction('FILTERING_REFRESH_FAILURE'); -export const refreshFiltersSuccess = createAction('FILTERING_REFRESH_SUCCESS'); - -export const refreshFilters = () => async (dispatch) => { - dispatch(refreshFiltersRequest()); - dispatch(showLoading()); - try { - const refreshText = await apiClient.refreshFilters(); - dispatch(refreshFiltersSuccess()); - - if (refreshText.includes('OK')) { - if (refreshText.includes('OK 0')) { - dispatch(addSuccessToast('all_filters_up_to_date_toast')); - } else { - dispatch(addSuccessToast(refreshText.replace(/OK /g, ''))); - } - } else { - dispatch(addErrorToast({ error: refreshText })); - } - - dispatch(getFilteringStatus()); - dispatch(hideLoading()); - } catch (error) { - dispatch(addErrorToast({ error })); - dispatch(refreshFiltersFailure()); - dispatch(hideLoading()); - } -}; - -export const handleRulesChange = createAction('HANDLE_RULES_CHANGE'); - -export const addFilterRequest = createAction('ADD_FILTER_REQUEST'); -export const addFilterFailure = createAction('ADD_FILTER_FAILURE'); -export const addFilterSuccess = createAction('ADD_FILTER_SUCCESS'); - -export const addFilter = (url, name) => async (dispatch) => { - dispatch(addFilterRequest()); - try { - await apiClient.addFilter(url, name); - dispatch(addFilterSuccess(url)); - dispatch(getFilteringStatus()); - } catch (error) { - dispatch(addErrorToast({ error })); - dispatch(addFilterFailure()); - } -}; - - -export const removeFilterRequest = createAction('ADD_FILTER_REQUEST'); -export const removeFilterFailure = createAction('ADD_FILTER_FAILURE'); -export const removeFilterSuccess = createAction('ADD_FILTER_SUCCESS'); - -export const removeFilter = url => async (dispatch) => { - dispatch(removeFilterRequest()); - try { - await apiClient.removeFilter(url); - dispatch(removeFilterSuccess(url)); - dispatch(getFilteringStatus()); - } catch (error) { - dispatch(addErrorToast({ error })); - dispatch(removeFilterFailure()); - } -}; - -export const toggleFilteringModal = createAction('FILTERING_MODAL_TOGGLE'); - export const handleUpstreamChange = createAction('HANDLE_UPSTREAM_CHANGE'); export const setUpstreamRequest = createAction('SET_UPSTREAM_REQUEST'); export const setUpstreamFailure = createAction('SET_UPSTREAM_FAILURE'); diff --git a/client/src/api/Api.js b/client/src/api/Api.js index f39b28dc..187b7312 100644 --- a/client/src/api/Api.js +++ b/client/src/api/Api.js @@ -90,32 +90,19 @@ class Api { } // Filtering - FILTERING_STATUS = { path: 'filtering/status', method: 'GET' }; - FILTERING_ENABLE = { path: 'filtering/enable', method: 'POST' }; - FILTERING_DISABLE = { path: 'filtering/disable', method: 'POST' }; + FILTERING_INFO = { path: 'filtering_info', method: 'GET' }; FILTERING_ADD_FILTER = { path: 'filtering/add_url', method: 'POST' }; FILTERING_REMOVE_FILTER = { path: 'filtering/remove_url', method: 'POST' }; FILTERING_SET_RULES = { path: 'filtering/set_rules', method: 'POST' }; - FILTERING_ENABLE_FILTER = { path: 'filtering/enable_url', method: 'POST' }; - FILTERING_DISABLE_FILTER = { path: 'filtering/disable_url', method: 'POST' }; FILTERING_REFRESH = { path: 'filtering/refresh', method: 'POST' }; + FILTERING_SET_URL = { path: 'filtering/set_url', method: 'POST' }; + FILTERING_CONFIG = { path: 'filtering_config', method: 'POST' }; getFilteringStatus() { - const { path, method } = this.FILTERING_STATUS; + const { path, method } = this.FILTERING_INFO; return this.makeRequest(path, method); } - enableFiltering() { - const { path, method } = this.FILTERING_ENABLE; - return this.makeRequest(path, method); - } - - disableFiltering() { - const { path, method } = this.FILTERING_DISABLE; - return this.makeRequest(path, method); - } - - // TODO find out when to use force parameter refreshFilters() { const { path, method } = this.FILTERING_REFRESH; return this.makeRequest(path, method); @@ -151,26 +138,22 @@ class Api { return this.makeRequest(path, method, parameters); } - enableFilter(url) { - const { path, method } = this.FILTERING_ENABLE_FILTER; - const parameter = 'url'; - const requestBody = `${parameter}=${url}`; - const config = { - data: requestBody, - header: { 'Content-Type': 'text/plain' }, + setFiltersConfig(config) { + const { path, method } = this.FILTERING_CONFIG; + const parameters = { + data: config, + headers: { 'Content-Type': 'application/json' }, }; - return this.makeRequest(path, method, config); + return this.makeRequest(path, method, parameters); } - disableFilter(url) { - const { path, method } = this.FILTERING_DISABLE_FILTER; - const parameter = 'url'; - const requestBody = `${parameter}=${url}`; - const config = { - data: requestBody, - header: { 'Content-Type': 'text/plain' }, + setFilterUrl(config) { + const { path, method } = this.FILTERING_SET_URL; + const parameters = { + data: config, + headers: { 'Content-Type': 'application/json' }, }; - return this.makeRequest(path, method, config); + return this.makeRequest(path, method, parameters); } // Parental diff --git a/client/src/components/Filters/Modal.js b/client/src/components/Filters/Modal.js index 884117b7..c4b89437 100644 --- a/client/src/components/Filters/Modal.js +++ b/client/src/components/Filters/Modal.js @@ -33,27 +33,13 @@ class Modal extends Component { this.setState({ ...this.state, name }); }; - handleNext = () => { - this.props.addFilter(this.state.url, this.state.name); - setTimeout(() => { - if (this.props.isFilterAdded) { - this.closeModal(); - } - }, 2000); - }; - closeModal = () => { this.props.toggleModal(); this.setState({ ...this.state, ...initialState }); - } + }; render() { - const { - isOpen, - title, - inputDescription, - processingAddFilter, - } = this.props; + const { isOpen, processingAddFilter } = this.props; const { isUrlValid, url, name } = this.state; const inputUrlClass = classnames({ 'form-control mb-2': true, @@ -64,28 +50,7 @@ class Modal extends Component { 'form-control mb-2': true, 'is-valid': name.length > 0, }); - - const renderBody = () => { - if (!this.props.isFilterAdded) { - return ( - - - - {inputDescription && -
- {inputDescription} -
} -
- ); - } - return ( -
- filter_added_successfully -
- ); - }; - - const isValidForSubmit = !(url.length > 0 && isUrlValid && name.length > 0); + const isValidForSubmit = url.length > 0 && isUrlValid && name.length > 0; return (
-

- {title} -

- +

+ new_filter_btn +

+
- {renderBody()} -
- {!this.props.isFilterAdded && -
- - + + +
+ enter_valid_filter_url
- } +
+
+ + +
); @@ -134,12 +111,10 @@ class Modal extends Component { Modal.propTypes = { toggleModal: PropTypes.func.isRequired, isOpen: PropTypes.bool.isRequired, - title: PropTypes.string.isRequired, - inputDescription: PropTypes.string, addFilter: PropTypes.func.isRequired, - isFilterAdded: PropTypes.bool, - processingAddFilter: PropTypes.bool, - t: PropTypes.func, + isFilterAdded: PropTypes.bool.isRequired, + processingAddFilter: PropTypes.bool.isRequired, + t: PropTypes.func.isRequired, }; export default withNamespaces()(Modal); diff --git a/client/src/components/Filters/UserRules.js b/client/src/components/Filters/UserRules.js index 39d39351..d2d78e91 100644 --- a/client/src/components/Filters/UserRules.js +++ b/client/src/components/Filters/UserRules.js @@ -15,13 +15,13 @@ class UserRules extends Component { }; render() { - const { t } = this.props; + const { t, userRules } = this.props; return (