diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b060ada..a8f44577 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,18 +25,44 @@ NOTE: Add new changes BELOW THIS COMMENT. ### Added +- Two new HTTP APIs, `PUT /control/stats/config/update` and `GET + control/stats/config`, which can be used to set and receive the query log + configuration. See openapi/openapi.yaml for the full description. +- Two new HTTP APIs, `PUT /control/querylog/config/update` and `GET + control/querylog/config`, which can be used to set and receive the statistics + configuration. See openapi/openapi.yaml for the full description. +- The ability to set custom IP for EDNS Client Subnet by using the DNS-server + configuration section on the DNS settings page in the UI ([#1472]). - The ability to manage safesearch for each service by using the new `safe_search` field ([#1163]). -### Changed +### Changed - ARPA domain names containing a subnet within private networks now also considered private, behaving closer to [RFC 6761][rfc6761] ([#5567]). #### Configuration Changes -In this release, the schema version has changed from 17 to 19. +In this release, the schema version has changed from 17 to 20. +- Property `statistics.interval`, which in schema versions 19 and earlier used + to be an integer number of days, is now a string with a human-readable + duration: + + ```yaml + # BEFORE: + 'statistics': + # … + 'interval': 1 + + # AFTER: + 'statistics': + # … + 'interval': '24h' + ``` + + To rollback this change, convert the property back into days and change the + `schema_version` back to `19`. - The `dns.safesearch_enabled` field has been replaced with `safe_search` object containing per-service settings. - The `clients.persistent.safesearch_enabled` field has been replaced with @@ -62,12 +88,41 @@ In this release, the schema version has changed from 17 to 19. client's specific `clients.persistent.safesearch` and then change the `schema_version` back to `17`. +### Deprecated + +- The `POST /control/safesearch/enable` HTTP API is deprecated. Use the new + `PUT /control/safesearch/settings` API. +- The `POST /control/safesearch/disable` HTTP API is deprecated. Use the new + `PUT /control/safesearch/settings` API +- The `safesearch_enabled` field is deprecated in the following HTTP APIs: + - `GET /control/clients` + - `POST /control/clients/add` + - `POST /control/clients/update` + - `GET /control/clients/find?ip0=...&ip1=...&ip2=...` + + Check `openapi/openapi.yaml` for more details. +- The `GET /control/stats_info` HTTP API; use the new `GET + /control/stats/config` API instead. + + **NOTE:** If interval is custom then it will be equal to `90` days for + compatibility reasons. See openapi/openapi.yaml and `openapi/CHANGELOG.md`. +- The `POST /control/stats_config` HTTP API; use the new `PUT + /control/stats/config/update` API instead. +- The `GET /control/querylog_info` HTTP API; use the new `GET + /control/querylog/config` API instead. + + **NOTE:** If interval is custom then it will be equal to `90` days for + compatibility reasons. See openapi/openapi.yaml and `openapi/CHANGELOG.md`. +- The `POST /control/querylog_config` HTTP API; use the new `PUT + /control/querylog/config/update` API instead. + ### Fixed - Panic caused by empty top-level domain name label in `/etc/hosts` files ([#5584]). [#1163]: https://github.com/AdguardTeam/AdGuardHome/issues/1163 +[#1472]: https://github.com/AdguardTeam/AdGuardHome/issues/1472 [#5567]: https://github.com/AdguardTeam/AdGuardHome/issues/5567 [#5584]: https://github.com/AdguardTeam/AdGuardHome/issues/5584 @@ -100,8 +155,6 @@ See also the [v0.107.26 GitHub milestone][ms-v0.107.26]. #### Configuration Changes -In this release, the schema version has changed from 16 to 17. - - Property `edns_client_subnet`, which in schema versions 16 and earlier used to be a part of the `dns` object, is now part of the `dns.edns_client_subnet` object: diff --git a/client/src/__locales/en.json b/client/src/__locales/en.json index 2afa38bd..08f3c08f 100644 --- a/client/src/__locales/en.json +++ b/client/src/__locales/en.json @@ -167,6 +167,7 @@ "enabled_parental_toast": "Enabled Parental Control", "disabled_safe_search_toast": "Disabled Safe Search", "enabled_save_search_toast": "Enabled Safe Search", + "updated_save_search_toast": "Safe Search settings updated", "enabled_table_header": "Enabled", "name_table_header": "Name", "list_url_table_header": "List URL", @@ -290,6 +291,8 @@ "rate_limit": "Rate limit", "edns_enable": "Enable EDNS client subnet", "edns_cs_desc": "Add the EDNS Client Subnet option (ECS) to upstream requests and log the values sent by the clients in the query log.", + "edns_use_custom_ip": "Use custom IP for EDNS", + "edns_use_custom_ip_desc": "Allow to use custom IP for EDNS", "rate_limit_desc": "The number of requests per second allowed per client. Setting it to 0 means no limit.", "blocking_ipv4_desc": "IP address to be returned for a blocked A request", "blocking_ipv6_desc": "IP address to be returned for a blocked AAAA request", @@ -523,6 +526,10 @@ "statistics_retention_confirm": "Are you sure you want to change statistics retention? If you decrease the interval value, some data will be lost", "statistics_cleared": "Statistics successfully cleared", "statistics_enable": "Enable statistics", + "ignore_domains": "Ignored domains (separated by newline)", + "ignore_domains_title": "Ignored domains", + "ignore_domains_desc_stats": "Queries for these domains are not written to the statistics", + "ignore_domains_desc_query": "Queries for these domains are not written to the query log", "interval_hours": "{{count}} hour", "interval_hours_plural": "{{count}} hours", "filters_configuration": "Filters configuration", @@ -642,5 +649,6 @@ "anonymizer_notification": "<0>Note: IP anonymization is enabled. You can disable it in <1>General settings.", "confirm_dns_cache_clear": "Are you sure you want to clear DNS cache?", "cache_cleared": "DNS cache successfully cleared", - "clear_cache": "Clear cache" + "clear_cache": "Clear cache", + "make_static": "Make static" } diff --git a/client/src/actions/index.js b/client/src/actions/index.js index 2780c94d..a164f51a 100644 --- a/client/src/actions/index.js +++ b/client/src/actions/index.js @@ -24,6 +24,12 @@ import { getFilteringStatus, setRules } from './filtering'; export const toggleSettingStatus = createAction('SETTING_STATUS_TOGGLE'); export const showSettingsFailure = createAction('SETTINGS_FAILURE_SHOW'); +/** + * + * @param {*} settingKey = SETTINGS_NAMES + * @param {*} status: boolean | SafeSearchConfig + * @returns + */ export const toggleSetting = (settingKey, status) => async (dispatch) => { let successMessage = ''; try { @@ -49,14 +55,9 @@ export const toggleSetting = (settingKey, status) => async (dispatch) => { dispatch(toggleSettingStatus({ settingKey })); break; case SETTINGS_NAMES.safesearch: - if (status) { - successMessage = 'disabled_safe_search_toast'; - await apiClient.disableSafesearch(); - } else { - successMessage = 'enabled_save_search_toast'; - await apiClient.enableSafesearch(); - } - dispatch(toggleSettingStatus({ settingKey })); + successMessage = 'updated_save_search_toast'; + await apiClient.updateSafesearch(status); + dispatch(toggleSettingStatus({ settingKey, value: status })); break; default: break; @@ -71,7 +72,9 @@ export const initSettingsRequest = createAction('SETTINGS_INIT_REQUEST'); export const initSettingsFailure = createAction('SETTINGS_INIT_FAILURE'); export const initSettingsSuccess = createAction('SETTINGS_INIT_SUCCESS'); -export const initSettings = (settingsList) => async (dispatch) => { +export const initSettings = (settingsList = { + safebrowsing: {}, parental: {}, +}) => async (dispatch) => { dispatch(initSettingsRequest()); try { const safebrowsingStatus = await apiClient.getSafebrowsingStatus(); @@ -80,7 +83,6 @@ export const initSettings = (settingsList) => async (dispatch) => { const { safebrowsing, parental, - safesearch, } = settingsList; const newSettingsList = { safebrowsing: { @@ -92,8 +94,7 @@ export const initSettings = (settingsList) => async (dispatch) => { enabled: parentalStatus.enabled, }, safesearch: { - ...safesearch, - enabled: safesearchStatus.enabled, + ...safesearchStatus, }, }; dispatch(initSettingsSuccess({ settingsList: newSettingsList })); diff --git a/client/src/actions/queryLogs.js b/client/src/actions/queryLogs.js index 99da2cb0..e07c6fae 100644 --- a/client/src/actions/queryLogs.js +++ b/client/src/actions/queryLogs.js @@ -177,7 +177,7 @@ export const getLogsConfigSuccess = createAction('GET_LOGS_CONFIG_SUCCESS'); export const getLogsConfig = () => async (dispatch) => { dispatch(getLogsConfigRequest()); try { - const data = await apiClient.getQueryLogInfo(); + const data = await apiClient.getQueryLogConfig(); dispatch(getLogsConfigSuccess(data)); } catch (error) { dispatch(addErrorToast({ error })); diff --git a/client/src/actions/stats.js b/client/src/actions/stats.js index d3948efa..0e5b416e 100644 --- a/client/src/actions/stats.js +++ b/client/src/actions/stats.js @@ -13,7 +13,7 @@ export const getStatsConfigSuccess = createAction('GET_STATS_CONFIG_SUCCESS'); export const getStatsConfig = () => async (dispatch) => { dispatch(getStatsConfigRequest()); try { - const data = await apiClient.getStatsInfo(); + const data = await apiClient.getStatsConfig(); dispatch(getStatsConfigSuccess(data)); } catch (error) { dispatch(addErrorToast({ error })); diff --git a/client/src/api/Api.js b/client/src/api/Api.js index d984bbb8..caf836b8 100644 --- a/client/src/api/Api.js +++ b/client/src/api/Api.js @@ -208,24 +208,40 @@ class Api { // Safesearch SAFESEARCH_STATUS = { path: 'safesearch/status', method: 'GET' }; - SAFESEARCH_ENABLE = { path: 'safesearch/enable', method: 'POST' }; - - SAFESEARCH_DISABLE = { path: 'safesearch/disable', method: 'POST' }; + SAFESEARCH_UPDATE = { path: 'safesearch/settings', method: 'PUT' }; getSafesearchStatus() { const { path, method } = this.SAFESEARCH_STATUS; return this.makeRequest(path, method); } - enableSafesearch() { - const { path, method } = this.SAFESEARCH_ENABLE; - return this.makeRequest(path, method); + /** + * interface SafeSearchConfig { + "enabled": boolean, + "bing": boolean, + "duckduckgo": boolean, + "google": boolean, + "pixabay": boolean, + "yandex": boolean, + "youtube": boolean + * } + * @param {*} data - SafeSearchConfig + * @returns 200 ok + */ + updateSafesearch(data) { + const { path, method } = this.SAFESEARCH_UPDATE; + return this.makeRequest(path, method, { data }); } - disableSafesearch() { - const { path, method } = this.SAFESEARCH_DISABLE; - return this.makeRequest(path, method); - } + // enableSafesearch() { + // const { path, method } = this.SAFESEARCH_ENABLE; + // return this.makeRequest(path, method); + // } + + // disableSafesearch() { + // const { path, method } = this.SAFESEARCH_DISABLE; + // return this.makeRequest(path, method); + // } // Language @@ -497,9 +513,9 @@ class Api { // Settings for statistics GET_STATS = { path: 'stats', method: 'GET' }; - STATS_INFO = { path: 'stats_info', method: 'GET' }; + GET_STATS_CONFIG = { path: 'stats/config', method: 'GET' }; - STATS_CONFIG = { path: 'stats_config', method: 'POST' }; + UPDATE_STATS_CONFIG = { path: 'stats/config/update', method: 'PUT' }; STATS_RESET = { path: 'stats_reset', method: 'POST' }; @@ -508,13 +524,13 @@ class Api { return this.makeRequest(path, method); } - getStatsInfo() { - const { path, method } = this.STATS_INFO; + getStatsConfig() { + const { path, method } = this.GET_STATS_CONFIG; return this.makeRequest(path, method); } setStatsConfig(data) { - const { path, method } = this.STATS_CONFIG; + const { path, method } = this.UPDATE_STATS_CONFIG; const config = { data, }; @@ -529,9 +545,9 @@ class Api { // Query log GET_QUERY_LOG = { path: 'querylog', method: 'GET' }; - QUERY_LOG_CONFIG = { path: 'querylog_config', method: 'POST' }; + UPDATE_QUERY_LOG_CONFIG = { path: 'querylog/config/update', method: 'PUT' }; - QUERY_LOG_INFO = { path: 'querylog_info', method: 'GET' }; + GET_QUERY_LOG_CONFIG = { path: 'querylog/config', method: 'GET' }; QUERY_LOG_CLEAR = { path: 'querylog_clear', method: 'POST' }; @@ -543,13 +559,13 @@ class Api { return this.makeRequest(url, method); } - getQueryLogInfo() { - const { path, method } = this.QUERY_LOG_INFO; + getQueryLogConfig() { + const { path, method } = this.GET_QUERY_LOG_CONFIG; return this.makeRequest(path, method); } setQueryLogConfig(data) { - const { path, method } = this.QUERY_LOG_CONFIG; + const { path, method } = this.UPDATE_QUERY_LOG_CONFIG; const config = { data, }; diff --git a/client/src/components/Dashboard/BlockedDomains.js b/client/src/components/Dashboard/BlockedDomains.js index 144f5bee..73829a1b 100644 --- a/client/src/components/Dashboard/BlockedDomains.js +++ b/client/src/components/Dashboard/BlockedDomains.js @@ -29,8 +29,11 @@ const BlockedDomains = ({ blockedFiltering, replacedSafebrowsing, replacedParental, + replacedSafesearch, }) => { - const totalBlocked = blockedFiltering + replacedSafebrowsing + replacedParental; + const totalBlocked = ( + blockedFiltering + replacedSafebrowsing + replacedParental + replacedSafesearch + ); return ( diff --git a/client/src/components/Settings/Clients/ClientsTable/ClientsTable.js b/client/src/components/Settings/Clients/ClientsTable/ClientsTable.js index 2d2b893b..41ed4e3b 100644 --- a/client/src/components/Settings/Clients/ClientsTable/ClientsTable.js +++ b/client/src/components/Settings/Clients/ClientsTable/ClientsTable.js @@ -7,6 +7,7 @@ import { useDispatch, useSelector } from 'react-redux'; import ReactTable from 'react-table'; import { getAllBlockedServices } from '../../../../actions/services'; +import { initSettings } from '../../../../actions'; import { splitByNewLine, countClientsStatistics, @@ -38,9 +39,13 @@ const ClientsTable = ({ const [t] = useTranslation(); const dispatch = useDispatch(); const services = useSelector((store) => store?.services); + const globalSettings = useSelector((store) => store?.settings.settingsList) || {}; + + const { safesearch } = globalSettings; useEffect(() => { dispatch(getAllBlockedServices()); + dispatch(initSettings()); }, []); const handleFormAdd = (values) => { @@ -107,6 +112,7 @@ const ClientsTable = ({ tags: [], use_global_settings: true, use_global_blocked_services: true, + safe_search: { ...(safesearch || {}) }, }; }; diff --git a/client/src/components/Settings/Clients/Form.js b/client/src/components/Settings/Clients/Form.js index f6b12d1c..6e5763e6 100644 --- a/client/src/components/Settings/Clients/Form.js +++ b/client/src/components/Settings/Clients/Form.js @@ -11,7 +11,7 @@ import Select from 'react-select'; import i18n from '../../../i18n'; import Tabs from '../../ui/Tabs'; import Examples from '../Dns/Upstream/Examples'; -import { toggleAllServices, trimLinesAndRemoveEmpty } from '../../../helpers/helpers'; +import { toggleAllServices, trimLinesAndRemoveEmpty, captitalizeWords } from '../../../helpers/helpers'; import { renderInputField, renderGroupField, @@ -40,10 +40,6 @@ const settingsCheckboxes = [ name: 'parental_enabled', placeholder: 'use_adguard_parental', }, - { - name: 'safesearch_enabled', - placeholder: 'enforce_safe_search', - }, ]; const validate = (values) => { const errors = {}; @@ -139,8 +135,12 @@ let Form = (props) => { processingUpdating, invalid, tagsOptions, + initialValues, } = props; const services = useSelector((store) => store?.services); + const { safe_search } = initialValues; + const safeSearchServices = { ...safe_search }; + delete safeSearchServices.enabled; const [activeTabLabel, setActiveTabLabel] = useState('settings'); @@ -163,6 +163,28 @@ let Form = (props) => { /> ))} +
+ +
+
+ {Object.keys(safeSearchServices).map((searchKey) => ( +
+ +
+ ))} +
, }, block_services: { @@ -358,6 +380,7 @@ Form.propTypes = { processingUpdating: PropTypes.bool.isRequired, invalid: PropTypes.bool.isRequired, tagsOptions: PropTypes.array.isRequired, + initialValues: PropTypes.object, }; const selector = formValueSelector(FORM_NAME.CLIENT); diff --git a/client/src/components/Settings/Dhcp/Leases.js b/client/src/components/Settings/Dhcp/Leases.js index 70400538..96ca8852 100644 --- a/client/src/components/Settings/Dhcp/Leases.js +++ b/client/src/components/Settings/Dhcp/Leases.js @@ -1,9 +1,11 @@ import React, { Component } from 'react'; +import { connect } from 'react-redux'; import PropTypes from 'prop-types'; import ReactTable from 'react-table'; import { Trans, withTranslation } from 'react-i18next'; import { LEASES_TABLE_DEFAULT_PAGE_SIZE } from '../../../helpers/constants'; import { sortIp } from '../../../helpers/helpers'; +import { toggleLeaseModal } from '../../../actions'; class Leases extends Component { cellWrap = ({ value }) => ( @@ -14,6 +16,30 @@ class Leases extends Component { ); + convertToStatic = (data) => () => { + const { dispatch } = this.props; + dispatch(toggleLeaseModal(data)); + } + + makeStatic = ({ row }) => { + const { t, disabledLeasesButton } = this.props; + return ( +
+ +
+ ); + } + render() { const { leases, t } = this.props; return ( @@ -23,20 +49,27 @@ class Leases extends Component { { Header: 'MAC', accessor: 'mac', + minWidth: 180, Cell: this.cellWrap, }, { Header: 'IP', accessor: 'ip', + minWidth: 230, Cell: this.cellWrap, sortMethod: sortIp, }, { Header: dhcp_table_hostname, accessor: 'hostname', + minWidth: 230, Cell: this.cellWrap, }, { Header: dhcp_table_expires, accessor: 'expires', + minWidth: 220, Cell: this.cellWrap, + }, { + Header: actions_table_header, + Cell: this.makeStatic, }, ]} pageSize={LEASES_TABLE_DEFAULT_PAGE_SIZE} @@ -53,6 +86,8 @@ class Leases extends Component { Leases.propTypes = { leases: PropTypes.array, t: PropTypes.func, + dispatch: PropTypes.func, + disabledLeasesButton: PropTypes.bool, }; -export default withTranslation()(Leases); +export default withTranslation()(connect(() => ({}), (dispatch) => ({ dispatch }))(Leases)); diff --git a/client/src/components/Settings/Dhcp/StaticLeases/Form.js b/client/src/components/Settings/Dhcp/StaticLeases/Form.js index 0525f6a3..e26b4da5 100644 --- a/client/src/components/Settings/Dhcp/StaticLeases/Form.js +++ b/client/src/components/Settings/Dhcp/StaticLeases/Form.js @@ -2,7 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { Field, reduxForm } from 'redux-form'; import { Trans, useTranslation } from 'react-i18next'; -import { useDispatch } from 'react-redux'; +import { useDispatch, useSelector, shallowEqual } from 'react-redux'; import { renderInputField, normalizeMac } from '../../../../helpers/form'; import { @@ -25,6 +25,7 @@ const Form = ({ }) => { const { t } = useTranslation(); const dispatch = useDispatch(); + const dynamicLease = useSelector((store) => store.dhcp.leaseModalConfig, shallowEqual); const onClick = () => { reset(); @@ -87,7 +88,7 @@ const Form = ({ diff --git a/client/src/components/Settings/Dhcp/StaticLeases/Modal.js b/client/src/components/Settings/Dhcp/StaticLeases/Modal.js index 0baf487e..7a11cfce 100644 --- a/client/src/components/Settings/Dhcp/StaticLeases/Modal.js +++ b/client/src/components/Settings/Dhcp/StaticLeases/Modal.js @@ -2,7 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { Trans, withTranslation } from 'react-i18next'; import ReactModal from 'react-modal'; -import { useDispatch } from 'react-redux'; +import { shallowEqual, useDispatch, useSelector } from 'react-redux'; import Form from './Form'; import { toggleLeaseModal } from '../../../../actions'; @@ -18,6 +18,9 @@ const Modal = ({ const dispatch = useDispatch(); const toggleModal = () => dispatch(toggleLeaseModal()); + const leaseInitialData = useSelector( + (state) => state.dhcp.leaseModalConfig, shallowEqual, + ) || {}; return (
dhcp_table_hostname, accessor: 'hostname', + minWidth: 230, Cell: cellWrap, }, { diff --git a/client/src/components/Settings/Dhcp/index.js b/client/src/components/Settings/Dhcp/index.js index bd3a45e3..a509ac49 100644 --- a/client/src/components/Settings/Dhcp/index.js +++ b/client/src/components/Settings/Dhcp/index.js @@ -188,8 +188,8 @@ const Dhcp = () => { const inputtedIPv4values = dhcp?.values?.v4?.gateway_ip && dhcp?.values?.v4?.subnet_mask; const isEmptyConfig = !Object.values(dhcp?.values?.v4 ?? {}).some(Boolean); - const disabledLeasesButton = dhcp?.syncErrors || interfaces?.syncErrors - || !isInterfaceIncludesIpv4 || isEmptyConfig || processingConfig || !inputtedIPv4values; + const disabledLeasesButton = Boolean(dhcp?.syncErrors || interfaces?.syncErrors + || !isInterfaceIncludesIpv4 || isEmptyConfig || processingConfig || !inputtedIPv4values); const cidr = inputtedIPv4values ? `${dhcp?.values?.v4?.gateway_ip}/${subnetMaskToBitMask(dhcp?.values?.v4?.subnet_mask)}` : ''; return <> @@ -260,7 +260,7 @@ const Dhcp = () => { >
- +
} diff --git a/client/src/components/Settings/Dns/Config/Form.js b/client/src/components/Settings/Dns/Config/Form.js index a2dd2bf9..52d94741 100644 --- a/client/src/components/Settings/Dns/Config/Form.js +++ b/client/src/components/Settings/Dns/Config/Form.js @@ -13,15 +13,11 @@ import { validateIpv4, validateIpv6, validateRequiredValue, + validateIp, } from '../../../../helpers/validators'; import { BLOCKING_MODES, FORM_NAME, UINT32_RANGE } from '../../../../helpers/constants'; const checkboxes = [ - { - name: 'edns_cs_enabled', - placeholder: 'edns_enable', - subtitle: 'edns_cs_desc', - }, { name: 'dnssec_enabled', placeholder: 'dnssec_enable', @@ -66,6 +62,8 @@ const Form = ({ const { t } = useTranslation(); const { blocking_mode, + edns_cs_enabled, + edns_cs_use_custom, } = useSelector((state) => state.form[FORM_NAME.BLOCKING_MODE].values ?? {}, shallowEqual); return @@ -92,6 +90,39 @@ const Form = ({ /> +
+
+ +
+
+
+
+ +
+ + {edns_cs_use_custom && ()} + +
{checkboxes.map(({ name, placeholder, subtitle }) =>
{ blocking_ipv4, blocking_ipv6, edns_cs_enabled, + edns_cs_use_custom, + edns_cs_custom_ip, dnssec_enabled, disable_ipv6, processingSetConfig, @@ -39,6 +41,8 @@ const Config = () => { edns_cs_enabled, disable_ipv6, dnssec_enabled, + edns_cs_use_custom, + edns_cs_custom_ip, }} onSubmit={handleFormSubmit} processing={processingSetConfig} diff --git a/client/src/components/Settings/LogsConfig/Form.js b/client/src/components/Settings/LogsConfig/Form.js index 8db3f18d..b29b974e 100644 --- a/client/src/components/Settings/LogsConfig/Form.js +++ b/client/src/components/Settings/LogsConfig/Form.js @@ -4,18 +4,28 @@ import { Field, reduxForm } from 'redux-form'; import { Trans, withTranslation } from 'react-i18next'; import flow from 'lodash/flow'; -import { CheckboxField, renderRadioField, toFloatNumber } from '../../../helpers/form'; -import { FORM_NAME, QUERY_LOG_INTERVALS_DAYS } from '../../../helpers/constants'; +import { + CheckboxField, + renderRadioField, + toFloatNumber, + renderTextareaField, +} from '../../../helpers/form'; +import { + FORM_NAME, + QUERY_LOG_INTERVALS_DAYS, + HOUR, + DAY, +} from '../../../helpers/constants'; import '../FormButton.css'; const getIntervalTitle = (interval, t) => { switch (interval) { - case 0.25: + case 6 * HOUR: return t('interval_6_hour'); - case 1: + case DAY: return t('interval_24_hour'); default: - return t('interval_days', { count: interval }); + return t('interval_days', { count: interval / DAY }); } }; @@ -66,6 +76,22 @@ const Form = (props) => { {getIntervalFields(processing, t, toFloatNumber)}
+ +
+ ignore_domains_desc_query +
+
+ +