From 9df0b9bfd8a9d3af57cae9081bf32f661495fe82 Mon Sep 17 00:00:00 2001 From: Elizaveta Selyukovich Date: Tue, 12 Mar 2024 12:56:00 +0300 Subject: [PATCH 1/2] Redesign dashboard --- package.json | 1 - src/__tests__/integration/validation.test.js | 5 +- .../layouts/pages/inspect/tree/Tree.js | 4 +- .../layouts/pages/results/summary/Summary.js | 97 +++++++++---------- .../pages/results/summary/Summary.scss | 60 ++++++++++-- src/common/store/job/result/selectors.js | 1 + yarn.lock | 22 +---- 7 files changed, 104 insertions(+), 86 deletions(-) diff --git a/package.json b/package.json index a751ae97..0c4f5c98 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,6 @@ "react-arborist": "^3.2.0", "react-dom": "^16.13.0", "react-dropzone": "^10.2.2", - "react-google-charts": "^3.0.15", "react-redux": "^7.2.0", "react-router-dom": "^5.1.2", "react-scripts": "^5.0.0", diff --git a/src/__tests__/integration/validation.test.js b/src/__tests__/integration/validation.test.js index 1443b508..51db76fc 100644 --- a/src/__tests__/integration/validation.test.js +++ b/src/__tests__/integration/validation.test.js @@ -1,7 +1,6 @@ import { integrationTest, moveNext, storeFile, waitFor, stepFinished } from './index'; import { createJob, executeJob, getJob, updateJob } from '../../common/services/jobService'; import { getFileContent, uploadFile } from '../../common/services/fileService'; -import { Chart } from 'react-google-charts'; import Progress from '../../common/components/shared/progress/Progress'; import Results from '../../common/components/layouts/pages/results/Results'; @@ -82,9 +81,7 @@ describe('Validation', () => { // Once job is complete app should be redirected to Results summary page expect(component.find(Results)).toHaveLength(1); - // Chart component loaded - expect(component.find(Chart)).toHaveLength(1); - expect(component.find('.summary__compliance').text()).toBe('50%compliant'); + expect(component.find('ul.list').text()).toMatch('Page:0 errors'); expect(component.find('li.legend-item_passed').text()).toBe('1 checks passed'); expect(component.find('li.legend-item_failed').text()).toBe('1 errors'); }) diff --git a/src/common/components/layouts/pages/inspect/tree/Tree.js b/src/common/components/layouts/pages/inspect/tree/Tree.js index 3b98f17f..66c80e45 100644 --- a/src/common/components/layouts/pages/inspect/tree/Tree.js +++ b/src/common/components/layouts/pages/inspect/tree/Tree.js @@ -610,11 +610,13 @@ export function sortChecksByPage(checks, errorsMap) { return newChecks; } -const SummaryInterface = PropTypes.shape({ +export const SummaryInterface = PropTypes.shape({ clause: PropTypes.string.isRequired, testNumber: PropTypes.number.isRequired, description: PropTypes.string.isRequired, checks: PropTypes.arrayOf(PropTypes.object).isRequired, + failedChecks: PropTypes.number.isRequired, + tags: PropTypes.arrayOf(PropTypes.string).isRequired, }); Tree.propTypes = { diff --git a/src/common/components/layouts/pages/results/summary/Summary.js b/src/common/components/layouts/pages/results/summary/Summary.js index 4b63a022..68b26805 100644 --- a/src/common/components/layouts/pages/results/summary/Summary.js +++ b/src/common/components/layouts/pages/results/summary/Summary.js @@ -1,12 +1,10 @@ -import React, { useMemo, useState } from 'react'; +import React, { useMemo } from 'react'; import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import classNames from 'classnames'; +import _ from 'lodash'; import Paper from '@material-ui/core/Paper'; -import CircularProgress from '@material-ui/core/CircularProgress'; -import { useTheme } from '@material-ui/core/styles'; -import { Chart } from 'react-google-charts'; import FiberManualRecordIcon from '@material-ui/icons/FiberManualRecord'; import HighlightOffIcon from '@material-ui/icons/HighlightOff'; import ErrorOutline from '@material-ui/icons/ErrorOutline'; @@ -18,13 +16,17 @@ import { getFileNameLink } from '../../../../../store/pdfLink/selectors'; import { getResultSummary, getJobEndStatus } from '../../../../../store/job/result/selectors'; import { getProfileOptions } from '../../../../../store/validationProfiles/selectors'; import { isFileUploadMode } from '../../../../../store/application/selectors'; +import { SummaryInterface as RuleSummariesInterface } from '../../inspect/tree/Tree'; import './Summary.scss'; +import errorTags from '../../inspect/validationErrorTags.json'; + const JOB_END_STATUS = { CANCELLED: 'cancelled', TIMEOUT: 'timeout', }; +const CATEGORY = 'Category'; function Summary({ fileName, profiles, selectedProfile, resultSummary, jobEndStatus }) { return ( @@ -41,26 +43,16 @@ function Summary({ fileName, profiles, selectedProfile, resultSummary, jobEndSta } function ProcessedSummary({ resultSummary }) { - const theme = useTheme(); - const [chartReady, setChartReady] = useState(false); - const chartData = useMemo(() => buildChartData(resultSummary), [resultSummary]); - const chartOptions = useMemo(() => getChartOptions(theme), [theme]); - const chartEvents = useMemo(() => [{ eventName: 'ready', callback: () => setChartReady(true) }], [setChartReady]); - const compliancePercent = useMemo(() => calculateCompliance(resultSummary), [resultSummary]); + const listOfErrors = useMemo(() => getListOfErrors(resultSummary.ruleSummaries), [resultSummary]); + return ( <> -
- } - data={chartData} - options={chartOptions} - chartEvents={chartEvents} - /> -
- {compliancePercent}% - compliant -
+
+
    + {Object.keys(listOfErrors)?.map(key => ( + 0} /> + ))} +
    @@ -88,35 +80,25 @@ function TimeoutSummary() { ); } -function buildChartData({ passedChecks, failedChecks }) { - return [ - ['Check', 'Number'], - ['Passed', passedChecks], - ['Error', failedChecks], - ]; -} - -function getChartOptions(theme) { - return { - pieHole: 0.7, - title: '', - slices: [{ color: theme.palette.success.main }, { color: theme.palette.error.main }], - legend: 'none', - pieSliceText: 'none', - height: '300px', - width: '300px', - chartArea: { - height: '80%', - }, - }; -} - -function calculateCompliance({ passedChecks, failedChecks }) { - const total = passedChecks + failedChecks; - if (total === 0) { - return 100; - } - return Math.floor((passedChecks * 100) / total); +function getListOfErrors(ruleSummaries) { + const listOfErrors = {}; + const otherIndexes = []; + const categoryTags = errorTags[CATEGORY].map(({ name }) => name); + categoryTags.forEach(key => { + listOfErrors[key] = ruleSummaries + ?.filter((rule, index) => { + if (_.isNil(rule)) return false; + // Avoid duplicated rules in different categories + const isRuleShow = rule.tags?.includes(key) && !otherIndexes.includes(index); + if (isRuleShow) { + otherIndexes.push(index); + } + return isRuleShow; + }) + .reduce((num, item) => num + item.failedChecks, 0); + }); + const sortedList = _.fromPairs(_.sortBy(_.toPairs(listOfErrors), 1).reverse()); + return sortedList; } function LegendItem({ label, value, type }) { @@ -128,9 +110,22 @@ function LegendItem({ label, value, type }) { ); } +function ListItem({ label, value, hasError }) { + return ( +
  • + + {_.startCase(label)}: + + {value} {value === 1 ? 'error' : 'errors'} + +
  • + ); +} + const SummaryInterface = PropTypes.shape({ passedChecks: PropTypes.number, failedChecks: PropTypes.number, + ruleSummaries: PropTypes.arrayOf(RuleSummariesInterface).isRequired, }); Summary.propTypes = { diff --git a/src/common/components/layouts/pages/results/summary/Summary.scss b/src/common/components/layouts/pages/results/summary/Summary.scss index a12d70b3..d5a13566 100644 --- a/src/common/components/layouts/pages/results/summary/Summary.scss +++ b/src/common/components/layouts/pages/results/summary/Summary.scss @@ -11,15 +11,10 @@ margin-bottom: 0; } - &__chart { + &__list { @include centered-column; - position: relative; - height: 300px; - - // Remove chart tooltip flickering - svg > g > g:last-child { - pointer-events: none - } + margin: 50px auto; + min-width: fit-content; } &__compliance { @@ -65,6 +60,55 @@ } } + .list { + list-style: none; + padding: 0; + columns: 2; + margin: 0 20px; + -webkit-columns: 2; + -moz-columns: 2; + + &-item { + margin: 0 10px; + display: flex; + align-items: center; + font-weight: 500; + margin-bottom: 9px; + + .list-item__icon { + font-size: 16px; + margin-right: 5px; + } + + &_passed { + .list-item__icon, span { + color: $color-success; + } + } + &_failed { + .list-item__icon, span { + color: $color-error; + } + } + + span { + padding-left: 5px; + font-weight: 400 + } + } + + @media screen and (max-width: 490px) { + columns: 1; + -webkit-columns: 1; + -moz-columns: 1; + } + + @media screen and (max-width: 290px) { + margin: 0; + } + } + + .error-section { @include centered-column; margin: 30px; diff --git a/src/common/store/job/result/selectors.js b/src/common/store/job/result/selectors.js index 179de18e..8b22f969 100644 --- a/src/common/store/job/result/selectors.js +++ b/src/common/store/job/result/selectors.js @@ -9,6 +9,7 @@ export const getResultDetails = createSelector(getResult, result => result?.deta export const getResultSummary = createSelector(getResultDetails, checks => ({ passedChecks: checks?.passedChecks || null, failedChecks: checks?.failedChecks || null, + ruleSummaries: checks?.ruleSummaries || null, })); export const isCompliant = createSelector(getResult, result => result?.compliant || false); diff --git a/yarn.lock b/yarn.lock index 3addaf05..4e442ae0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8496,15 +8496,7 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== -pdfjs-dist@2.16.105: - version "2.16.105" - resolved "https://registry.yarnpkg.com/pdfjs-dist/-/pdfjs-dist-2.16.105.tgz#937b9c4a918f03f3979c88209d84c1ce90122c2a" - integrity sha512-J4dn41spsAwUxCpEoVf6GVoz908IAA3mYiLmNxg8J9kfRXc2jxpbUepcP0ocp0alVNLFthTAM8DZ1RaHh8sU0A== - dependencies: - dommatrix "^1.0.3" - web-streams-polyfill "^3.2.1" - -"pdfjs-dist@github:veraPDF/pdfjs-dist#v2.16.105-taggedPdf-0.1.18": +pdfjs-dist@2.16.105, "pdfjs-dist@github:veraPDF/pdfjs-dist#v2.16.105-taggedPdf-0.1.18": version "2.16.105" resolved "https://codeload.github.com/veraPDF/pdfjs-dist/tar.gz/7e5be3ffc8d2c521550eeae07e828a3d4d652901" dependencies: @@ -9431,13 +9423,6 @@ react-error-overlay@^6.0.11: resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.11.tgz#92835de5841c5cf08ba00ddd2d677b6d17ff9adb" integrity sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg== -react-google-charts@^3.0.15: - version "3.0.15" - resolved "https://registry.yarnpkg.com/react-google-charts/-/react-google-charts-3.0.15.tgz#30759a470f48336e744fd383d054122b039a1ff2" - integrity sha512-78s5xOQOJvL+jIewrWQZEHtlVk+5Yh4zZy+ODA1on1o1FaRjKWXxoo4n4JQl1XuqkF/A9NWque3KqM6pMggjzQ== - dependencies: - react-load-script "^0.0.6" - react-is@^16.12.0, react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.4, react-is@^16.8.6: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" @@ -9453,11 +9438,6 @@ react-is@^18.0.0: resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== -react-load-script@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/react-load-script/-/react-load-script-0.0.6.tgz#db6851236aaa25bb622677a2eb51dad4f8d2c258" - integrity sha512-aRGxDGP9VoLxcsaYvKWIW+LRrMOzz2eEcubTS4NvQPPugjk2VvMhow0wWTkSl7RxookomD1MwcP4l5UStg5ShQ== - react-pdf@6.2.2: version "6.2.2" resolved "https://registry.yarnpkg.com/react-pdf/-/react-pdf-6.2.2.tgz#61dbf1cd32b49bb452c8dd26e7ee7b2f987e4904" From 8c0421cb23f49f95c2625990108e93426af17ebb Mon Sep 17 00:00:00 2001 From: Elizaveta Selyukovich Date: Thu, 14 Mar 2024 18:04:12 +0300 Subject: [PATCH 2/2] Improve sorting --- .../components/layouts/pages/results/summary/Summary.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/common/components/layouts/pages/results/summary/Summary.js b/src/common/components/layouts/pages/results/summary/Summary.js index 68b26805..b1e4fbab 100644 --- a/src/common/components/layouts/pages/results/summary/Summary.js +++ b/src/common/components/layouts/pages/results/summary/Summary.js @@ -49,8 +49,8 @@ function ProcessedSummary({ resultSummary }) { <>
      - {Object.keys(listOfErrors)?.map(key => ( - 0} /> + {listOfErrors?.map(([key, value]) => ( + 0} /> ))}
    @@ -97,7 +97,7 @@ function getListOfErrors(ruleSummaries) { }) .reduce((num, item) => num + item.failedChecks, 0); }); - const sortedList = _.fromPairs(_.sortBy(_.toPairs(listOfErrors), 1).reverse()); + const sortedList = _.sortBy(_.toPairs(listOfErrors), 1).reverse(); return sortedList; }