- client: Fix query logs UI issues
Close #1828 Squashed commit of the following: commit a3955c989a939866c6772b147547344b3f8769c4 Merge: c91c41cb2759d81a
Author: ArtemBaskal <a.baskal@adguard.com> Date: Mon Jul 13 15:14:47 2020 +0300 Merge branch 'master' into fix/1828 commit c91c41cbc5f616e0af1092424e42b909d2f43f7c Author: ArtemBaskal <a.baskal@adguard.com> Date: Mon Jul 13 13:48:54 2020 +0300 Fix cell overflow commit 19e1d31a40f2e1bb1189a85b72507bcc364d4e0c Merge: af31f48ca33164bf
Author: ArtemBaskal <a.baskal@adguard.com> Date: Mon Jul 13 12:36:44 2020 +0300 Merge branch 'master' into fix/1828 commit af31f48c4d2699ebfbd2034711c51499b42e40f5 Author: ArtemBaskal <a.baskal@adguard.com> Date: Mon Jul 13 10:45:57 2020 +0300 minor commit d9507c5f3f5758e587766ae0fa45f1b9ad703ccf Author: ArtemBaskal <a.baskal@adguard.com> Date: Fri Jul 10 18:34:22 2020 +0300 - client: Fix query logs UI issues
This commit is contained in:
parent
2759d81afe
commit
8a417604a9
|
@ -480,6 +480,7 @@
|
||||||
"whois": "Whois",
|
"whois": "Whois",
|
||||||
"filtering_rules_learn_more": "<0>Learn more</0> about creating your own hosts lists.",
|
"filtering_rules_learn_more": "<0>Learn more</0> about creating your own hosts lists.",
|
||||||
"blocked_by_response": "Blocked by CNAME or IP in response",
|
"blocked_by_response": "Blocked by CNAME or IP in response",
|
||||||
|
"blocked_by_cname_or_ip": "Blocked by CNAME or IP",
|
||||||
"try_again": "Try again",
|
"try_again": "Try again",
|
||||||
"domain_desc": "Enter the domain name or wildcard you want to be rewritten.",
|
"domain_desc": "Enter the domain name or wildcard you want to be rewritten.",
|
||||||
"example_rewrite_domain": "rewrite responses for this domain name only.",
|
"example_rewrite_domain": "rewrite responses for this domain name only.",
|
||||||
|
@ -560,5 +561,6 @@
|
||||||
"filter_category_general_desc": "Lists that block tracking and advertising on most of the devices",
|
"filter_category_general_desc": "Lists that block tracking and advertising on most of the devices",
|
||||||
"filter_category_security_desc": "Lists that specialize on blocking malware, phishing or scam domains",
|
"filter_category_security_desc": "Lists that specialize on blocking malware, phishing or scam domains",
|
||||||
"filter_category_regional_desc": "Lists that focus on regional ads and tracking servers",
|
"filter_category_regional_desc": "Lists that focus on regional ads and tracking servers",
|
||||||
"filter_category_other_desc": "Other blocklists"
|
"filter_category_other_desc": "Other blocklists",
|
||||||
|
"original_response": "Original response"
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,7 +68,7 @@ const getClientCell = ({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="logs__row o-hidden h-100">
|
<div className="logs__row o-hidden h-100">
|
||||||
{processedData && getHintElement({
|
{getHintElement({
|
||||||
className: hintClass,
|
className: hintClass,
|
||||||
columnClass: 'grid grid--limited',
|
columnClass: 'grid grid--limited',
|
||||||
tooltipClass: 'px-5 pb-5 pt-4 mw-75',
|
tooltipClass: 'px-5 pb-5 pt-4 mw-75',
|
||||||
|
|
|
@ -33,16 +33,6 @@ const getDomainCell = (props) => {
|
||||||
'my-3': isDetailed,
|
'my-3': isDetailed,
|
||||||
});
|
});
|
||||||
|
|
||||||
const dnssecHint = getHintElement({
|
|
||||||
className: lockIconClass,
|
|
||||||
tooltipClass: 'py-4 px-5 pb-45',
|
|
||||||
canShowTooltip: answer_dnssec,
|
|
||||||
xlinkHref: 'lock',
|
|
||||||
columnClass: 'w-100',
|
|
||||||
content: 'validated_with_dnssec',
|
|
||||||
placement: 'bottom',
|
|
||||||
});
|
|
||||||
|
|
||||||
const protocol = t(SCHEME_TO_PROTOCOL_MAP[client_proto]) || '';
|
const protocol = t(SCHEME_TO_PROTOCOL_MAP[client_proto]) || '';
|
||||||
const ip = type ? `${t('type_table_header')}: ${type}` : '';
|
const ip = type ? `${t('type_table_header')}: ${type}` : '';
|
||||||
|
|
||||||
|
@ -100,7 +90,15 @@ const getDomainCell = (props) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="logs__row o-hidden">
|
<div className="logs__row o-hidden">
|
||||||
{dnssec_enabled && dnssecHint}
|
{dnssec_enabled && getHintElement({
|
||||||
|
className: lockIconClass,
|
||||||
|
tooltipClass: 'py-4 px-5 pb-45',
|
||||||
|
canShowTooltip: answer_dnssec,
|
||||||
|
xlinkHref: 'lock',
|
||||||
|
columnClass: 'w-100',
|
||||||
|
content: 'validated_with_dnssec',
|
||||||
|
placement: 'bottom',
|
||||||
|
})}
|
||||||
{trackerHint}
|
{trackerHint}
|
||||||
<div className={valueClass}>
|
<div className={valueClass}>
|
||||||
<div className="text-truncate" title={domain}>{domain}</div>
|
<div className="text-truncate" title={domain}>{domain}</div>
|
||||||
|
|
|
@ -6,6 +6,7 @@ import classNames from 'classnames';
|
||||||
import './Tooltip.css';
|
import './Tooltip.css';
|
||||||
import 'react-popper-tooltip/dist/styles.css';
|
import 'react-popper-tooltip/dist/styles.css';
|
||||||
import { HIDE_TOOLTIP_DELAY } from '../../../helpers/constants';
|
import { HIDE_TOOLTIP_DELAY } from '../../../helpers/constants';
|
||||||
|
import { processContent } from '../../../helpers/helpers';
|
||||||
|
|
||||||
const getHintElement = ({
|
const getHintElement = ({
|
||||||
className,
|
className,
|
||||||
|
@ -17,34 +18,34 @@ const getHintElement = ({
|
||||||
placement,
|
placement,
|
||||||
tooltipClass,
|
tooltipClass,
|
||||||
content,
|
content,
|
||||||
renderContent = React.Children.map(
|
renderContent = content ? React.Children.map(
|
||||||
content,
|
processContent(content),
|
||||||
(item, idx) => <div key={idx} className={contentItemClass}>
|
(item, idx) => <div key={idx} className={contentItemClass}>
|
||||||
<Trans>{item || '—'}</Trans>
|
<Trans>{item || '—'}</Trans>
|
||||||
</div>,
|
</div>,
|
||||||
),
|
) : null,
|
||||||
}) => <TooltipTrigger placement={placement} trigger="hover" delayHide={HIDE_TOOLTIP_DELAY} tooltip={
|
}) => <TooltipTrigger placement={placement} trigger="hover" delayHide={HIDE_TOOLTIP_DELAY} tooltip={
|
||||||
({
|
({
|
||||||
tooltipRef,
|
tooltipRef,
|
||||||
getTooltipProps,
|
getTooltipProps,
|
||||||
}) => <div {...getTooltipProps({
|
}) => <div {...getTooltipProps({
|
||||||
ref: tooltipRef,
|
ref: tooltipRef,
|
||||||
className: classNames('tooltip__container', tooltipClass, { 'd-none': !canShowTooltip }),
|
className: classNames('tooltip__container', tooltipClass, { 'd-none': !canShowTooltip }),
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
{title && <div className="pb-4 h-25 grid-content font-weight-bold">
|
{title && <div className="pb-4 h-25 grid-content font-weight-bold">
|
||||||
<Trans>{title}</Trans>
|
<Trans>{title}</Trans>
|
||||||
</div>}
|
</div>}
|
||||||
<div className={classNames(columnClass)}>{renderContent}</div>
|
<div className={classNames(columnClass)}>{renderContent}</div>
|
||||||
</div>
|
</div>
|
||||||
}>{({
|
}>{({
|
||||||
getTriggerProps, triggerRef,
|
getTriggerProps, triggerRef,
|
||||||
}) => <span {...getTriggerProps({ ref: triggerRef })}>
|
}) => <span {...getTriggerProps({ ref: triggerRef })}>
|
||||||
{xlinkHref && <svg className={className}>
|
{xlinkHref && <svg className={className}>
|
||||||
<use xlinkHref={`#${xlinkHref}`} />
|
<use xlinkHref={`#${xlinkHref}`} />
|
||||||
</svg>}
|
</svg>}
|
||||||
</span>}
|
</span>}
|
||||||
</TooltipTrigger>;
|
</TooltipTrigger>;
|
||||||
|
|
||||||
getHintElement.propTypes = {
|
getHintElement.propTypes = {
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
|
|
|
@ -9,18 +9,24 @@ import getHintElement from './getHintElement';
|
||||||
|
|
||||||
const getResponseCell = (row, filtering, t, isDetailed, getFilterName) => {
|
const getResponseCell = (row, filtering, t, isDetailed, getFilterName) => {
|
||||||
const {
|
const {
|
||||||
reason, filterId, rule, status, upstream, elapsedMs, domain, response,
|
reason, filterId, rule, status, upstream, elapsedMs,
|
||||||
|
domain, response, originalResponse,
|
||||||
} = row.original;
|
} = row.original;
|
||||||
|
|
||||||
const { filters, whitelistFilters } = filtering;
|
const { filters, whitelistFilters } = filtering;
|
||||||
const formattedElapsedMs = formatElapsedMs(elapsedMs, t);
|
const formattedElapsedMs = formatElapsedMs(elapsedMs, t);
|
||||||
|
|
||||||
const statusLabel = t(FILTERED_STATUS_TO_META_MAP[reason]?.label || reason);
|
const isBlocked = reason === FILTERED_STATUS.FILTERED_BLACK_LIST
|
||||||
|
|| reason === FILTERED_STATUS.FILTERED_BLOCKED_SERVICE;
|
||||||
|
|
||||||
|
const isBlockedByResponse = originalResponse.length > 0 && isBlocked;
|
||||||
|
|
||||||
|
const statusLabel = t(isBlockedByResponse ? 'blocked_by_cname_or_ip' : FILTERED_STATUS_TO_META_MAP[reason]?.label || reason);
|
||||||
const boldStatusLabel = <span className="font-weight-bold">{statusLabel}</span>;
|
const boldStatusLabel = <span className="font-weight-bold">{statusLabel}</span>;
|
||||||
const filter = getFilterName(filters, whitelistFilters, filterId, t);
|
const filter = getFilterName(filters, whitelistFilters, filterId, t);
|
||||||
|
|
||||||
const renderResponses = (responseArr) => {
|
const renderResponses = (responseArr) => {
|
||||||
if (responseArr.length === 0) {
|
if (responseArr?.length === 0) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,6 +56,7 @@ const getResponseCell = (row, filtering, t, isDetailed, getFilterName) => {
|
||||||
filter,
|
filter,
|
||||||
rule_label: rule,
|
rule_label: rule,
|
||||||
response_code: status,
|
response_code: status,
|
||||||
|
original_response: renderResponses(originalResponse),
|
||||||
},
|
},
|
||||||
[FILTERED_STATUS.NOT_FILTERED_WHITE_LIST]: {
|
[FILTERED_STATUS.NOT_FILTERED_WHITE_LIST]: {
|
||||||
domain,
|
domain,
|
||||||
|
@ -78,9 +85,11 @@ const getResponseCell = (row, filtering, t, isDetailed, getFilterName) => {
|
||||||
domain,
|
domain,
|
||||||
encryption_status: boldStatusLabel,
|
encryption_status: boldStatusLabel,
|
||||||
filter,
|
filter,
|
||||||
|
rule_label: rule,
|
||||||
install_settings_dns: upstream,
|
install_settings_dns: upstream,
|
||||||
elapsed: formattedElapsedMs,
|
elapsed: formattedElapsedMs,
|
||||||
response_code: status,
|
response_code: status,
|
||||||
|
original_response: renderResponses(originalResponse),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -88,13 +97,11 @@ const getResponseCell = (row, filtering, t, isDetailed, getFilterName) => {
|
||||||
? Object.entries(FILTERED_STATUS_TO_FIELDS_MAP[reason])
|
? Object.entries(FILTERED_STATUS_TO_FIELDS_MAP[reason])
|
||||||
: Object.entries(FILTERED_STATUS_TO_FIELDS_MAP.NotFilteredNotFound);
|
: Object.entries(FILTERED_STATUS_TO_FIELDS_MAP.NotFilteredNotFound);
|
||||||
|
|
||||||
const detailedInfo = reason === FILTERED_STATUS.FILTERED_BLOCKED_SERVICE
|
const detailedInfo = isBlocked ? filter : formattedElapsedMs;
|
||||||
|| reason === FILTERED_STATUS.FILTERED_BLACK_LIST
|
|
||||||
? filter : formattedElapsedMs;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="logs__row">
|
<div className="logs__row">
|
||||||
{fields && getHintElement({
|
{getHintElement({
|
||||||
className: classNames('icons mr-4 icon--small cursor--pointer icon--light-gray', { 'my-3': isDetailed }),
|
className: classNames('icons mr-4 icon--small cursor--pointer icon--light-gray', { 'my-3': isDetailed }),
|
||||||
columnClass: 'grid grid--limited',
|
columnClass: 'grid grid--limited',
|
||||||
tooltipClass: 'px-5 pb-5 pt-4 mw-75 custom-tooltip__response-details',
|
tooltipClass: 'px-5 pb-5 pt-4 mw-75 custom-tooltip__response-details',
|
||||||
|
@ -107,7 +114,8 @@ const getResponseCell = (row, filtering, t, isDetailed, getFilterName) => {
|
||||||
<div className="text-truncate">
|
<div className="text-truncate">
|
||||||
<div className="text-truncate" title={statusLabel}>{statusLabel}</div>
|
<div className="text-truncate" title={statusLabel}>{statusLabel}</div>
|
||||||
{isDetailed && <div
|
{isDetailed && <div
|
||||||
className="detailed-info d-none d-sm-block pt-1 text-truncate" title={detailedInfo}>{detailedInfo}</div>}
|
className="detailed-info d-none d-sm-block pt-1 text-truncate"
|
||||||
|
title={detailedInfo}>{detailedInfo}</div>}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -12,7 +12,7 @@ import {
|
||||||
FILTERED_STATUS_TO_META_MAP,
|
FILTERED_STATUS_TO_META_MAP,
|
||||||
TABLE_DEFAULT_PAGE_SIZE,
|
TABLE_DEFAULT_PAGE_SIZE,
|
||||||
SCHEME_TO_PROTOCOL_MAP,
|
SCHEME_TO_PROTOCOL_MAP,
|
||||||
CUSTOM_FILTERING_RULES_ID,
|
CUSTOM_FILTERING_RULES_ID, FILTERED_STATUS,
|
||||||
} from '../../helpers/constants';
|
} from '../../helpers/constants';
|
||||||
import getDateCell from './Cells/getDateCell';
|
import getDateCell from './Cells/getDateCell';
|
||||||
import getDomainCell from './Cells/getDomainCell';
|
import getDomainCell from './Cells/getDomainCell';
|
||||||
|
@ -300,6 +300,8 @@ const Table = (props) => {
|
||||||
type,
|
type,
|
||||||
client_proto,
|
client_proto,
|
||||||
filterId,
|
filterId,
|
||||||
|
rule,
|
||||||
|
originalResponse,
|
||||||
} = rowInfo.original;
|
} = rowInfo.original;
|
||||||
|
|
||||||
const hasTracker = !!tracker;
|
const hasTracker = !!tracker;
|
||||||
|
@ -317,12 +319,16 @@ const Table = (props) => {
|
||||||
const formattedElapsedMs = formatElapsedMs(elapsedMs, t);
|
const formattedElapsedMs = formatElapsedMs(elapsedMs, t);
|
||||||
const isFiltered = checkFiltered(reason);
|
const isFiltered = checkFiltered(reason);
|
||||||
|
|
||||||
|
const isBlocked = reason === FILTERED_STATUS.FILTERED_BLACK_LIST
|
||||||
|
|| reason === FILTERED_STATUS.FILTERED_BLOCKED_SERVICE;
|
||||||
|
|
||||||
const buttonType = isFiltered ? BLOCK_ACTIONS.UNBLOCK : BLOCK_ACTIONS.BLOCK;
|
const buttonType = isFiltered ? BLOCK_ACTIONS.UNBLOCK : BLOCK_ACTIONS.BLOCK;
|
||||||
const onToggleBlock = () => {
|
const onToggleBlock = () => {
|
||||||
toggleBlocking(buttonType, domain);
|
toggleBlocking(buttonType, domain);
|
||||||
};
|
};
|
||||||
|
|
||||||
const status = t(FILTERED_STATUS_TO_META_MAP[reason]?.label || reason);
|
const isBlockedByResponse = originalResponse.length > 0 && isBlocked;
|
||||||
|
const status = t(isBlockedByResponse ? 'blocked_by_cname_or_ip' : FILTERED_STATUS_TO_META_MAP[reason]?.label || reason);
|
||||||
const statusBlocked = <div className="bg--danger">{status}</div>;
|
const statusBlocked = <div className="bg--danger">{status}</div>;
|
||||||
|
|
||||||
const protocol = t(SCHEME_TO_PROTOCOL_MAP[client_proto]) || '';
|
const protocol = t(SCHEME_TO_PROTOCOL_MAP[client_proto]) || '';
|
||||||
|
@ -379,12 +385,14 @@ const Table = (props) => {
|
||||||
install_settings_dns: upstream,
|
install_settings_dns: upstream,
|
||||||
elapsed: formattedElapsedMs,
|
elapsed: formattedElapsedMs,
|
||||||
filter,
|
filter,
|
||||||
|
rule_label: rule,
|
||||||
response_table_header: response?.join('\n'),
|
response_table_header: response?.join('\n'),
|
||||||
|
original_response: originalResponse?.join('\n'),
|
||||||
[buttonType]: <div onClick={onToggleBlock}
|
[buttonType]: <div onClick={onToggleBlock}
|
||||||
className="title--border text-center">{t(buttonType)}</div>,
|
className="title--border text-center">{t(buttonType)}</div>,
|
||||||
};
|
};
|
||||||
|
|
||||||
const detailedDataCurrent = isFiltered ? detailedDataBlocked : detailedData;
|
const detailedDataCurrent = isBlocked ? detailedDataBlocked : detailedData;
|
||||||
|
|
||||||
setDetailedDataCurrent(detailedDataCurrent);
|
setDetailedDataCurrent(detailedDataCurrent);
|
||||||
setButtonType(buttonType);
|
setButtonType(buttonType);
|
||||||
|
|
|
@ -24,6 +24,10 @@ const INITIAL_REQUEST_DATA = ['', TABLE_FIRST_PAGE, INITIAL_REQUEST];
|
||||||
|
|
||||||
export const processContent = (data, buttonType) => Object.entries(data)
|
export const processContent = (data, buttonType) => Object.entries(data)
|
||||||
.map(([key, value]) => {
|
.map(([key, value]) => {
|
||||||
|
if (!value) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
const isTitle = value === 'title';
|
const isTitle = value === 'title';
|
||||||
const isButton = key === buttonType;
|
const isButton = key === buttonType;
|
||||||
const isBoolean = typeof value === 'boolean';
|
const isBoolean = typeof value === 'boolean';
|
||||||
|
|
|
@ -52,7 +52,7 @@ export const formatClientCell = (row, t, isDetailed = false) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="logs__text" title={value}>
|
<div className="logs__text mw-100" title={value}>
|
||||||
<>
|
<>
|
||||||
{nameContainer}
|
{nameContainer}
|
||||||
{whoisContainer}
|
{whoisContainer}
|
||||||
|
|
|
@ -86,18 +86,16 @@ export const normalizeLogs = (logs) => logs.map((log) => {
|
||||||
|
|
||||||
const { host: domain, type } = question;
|
const { host: domain, type } = question;
|
||||||
|
|
||||||
const response = answer ? answer.map((response) => {
|
const processResponse = (data) => (data ? data.map((response) => {
|
||||||
const { value, type, ttl } = response;
|
const { value, type, ttl } = response;
|
||||||
return `${type}: ${value} (ttl=${ttl})`;
|
return `${type}: ${value} (ttl=${ttl})`;
|
||||||
}) : [];
|
}) : []);
|
||||||
|
|
||||||
const tracker = getTrackerData(domain);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
time,
|
time,
|
||||||
domain,
|
domain,
|
||||||
type,
|
type,
|
||||||
response,
|
response: processResponse(answer),
|
||||||
reason,
|
reason,
|
||||||
client,
|
client,
|
||||||
client_proto,
|
client_proto,
|
||||||
|
@ -106,7 +104,8 @@ export const normalizeLogs = (logs) => logs.map((log) => {
|
||||||
status,
|
status,
|
||||||
serviceName: service_name,
|
serviceName: service_name,
|
||||||
originalAnswer: original_answer,
|
originalAnswer: original_answer,
|
||||||
tracker,
|
originalResponse: processResponse(original_answer),
|
||||||
|
tracker: getTrackerData(domain),
|
||||||
answer_dnssec,
|
answer_dnssec,
|
||||||
elapsedMs,
|
elapsedMs,
|
||||||
upstream,
|
upstream,
|
||||||
|
@ -618,3 +617,8 @@ export const selectCompletedFields = (values) => Object.entries(values)
|
||||||
}
|
}
|
||||||
return acc;
|
return acc;
|
||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
|
|
||||||
|
export const processContent = (content) => (Array.isArray(content)
|
||||||
|
? content.filter(([, value]) => value)
|
||||||
|
.flat() : content);
|
||||||
|
|
Loading…
Reference in New Issue