From aab3955847be11ead0a2672d19a8d63c6ab5410c Mon Sep 17 00:00:00 2001 From: Andrew Azores Date: Thu, 28 Sep 2023 10:13:28 -0400 Subject: [PATCH] feat(recordings): update Automated Analysis UI (#1117) * update recording options UI * update JSON report details for new API response format * replace ReportFrame with new-style automated analysis UI * remove View Report recording action * only display non-default recording options * move recording options into table column * add refresh button --- .../AutomatedAnalysisCard.tsx | 16 +- .../AutomatedAnalysisCardList.tsx | 14 +- .../AutomatedAnalysisFilters.tsx | 12 +- .../ClickableAutomatedAnalysisLabel.tsx | 46 +-- src/app/Dashboard/dashboard-utils.tsx | 68 +++- src/app/Recordings/ActiveRecordingsTable.tsx | 136 ++++++- .../Recordings/ArchivedRecordingsTable.tsx | 72 +++- src/app/Recordings/RecordingActions.tsx | 11 +- src/app/Recordings/ReportFrame.tsx | 67 ---- src/app/Shared/Services/Api.service.tsx | 2 +- src/app/Shared/Services/Report.service.tsx | 94 ++--- src/app/utils/fakeData.ts | 79 +++- .../AutomatedAnalysisCard.test.tsx | 74 +++- .../AutomatedAnalysisCardList.test.tsx | 56 ++- .../ClickableAutomatedAnalysisLabel.test.tsx | 177 +++++++-- .../AutomatedAnalysisNameFilter.test.tsx | 74 +++- .../AutomatedAnalysisTopicFilter.test.tsx | 74 +++- .../AutomatedAnalysisCardList.test.tsx.snap | 339 ++++++++++++++++-- .../Recordings/ActiveRecordingsTable.test.tsx | 16 - .../ArchivedRecordingsTable.test.tsx | 19 - 20 files changed, 1050 insertions(+), 396 deletions(-) delete mode 100644 src/app/Recordings/ReportFrame.tsx diff --git a/src/app/Dashboard/AutomatedAnalysis/AutomatedAnalysisCard.tsx b/src/app/Dashboard/AutomatedAnalysis/AutomatedAnalysisCard.tsx index 08a1e9cea..e270c8bf1 100644 --- a/src/app/Dashboard/AutomatedAnalysis/AutomatedAnalysisCard.tsx +++ b/src/app/Dashboard/AutomatedAnalysis/AutomatedAnalysisCard.tsx @@ -42,7 +42,7 @@ import { FAILED_REPORT_MESSAGE, NO_RECORDINGS_MESSAGE, RECORDING_FAILURE_MESSAGE, - RuleEvaluation, + AnalysisResult, TEMPLATE_UNSUPPORTED_MESSAGE, } from '@app/Shared/Services/Report.service'; import { ServiceContext } from '@app/Shared/Services/Services'; @@ -125,7 +125,7 @@ export const AutomatedAnalysisCard: DashboardCardFC const { t } = useTranslation(); const [targetConnectURL, setTargetConnectURL] = React.useState(''); - const [evaluations, setEvaluations] = React.useState([]); + const [results, setResults] = React.useState([]); const [categorizedEvaluation, setCategorizedEvaluation] = React.useState([]); const [filteredCategorizedEvaluation, setFilteredCategorizedEvaluation] = React.useState< @@ -155,9 +155,9 @@ export const AutomatedAnalysisCard: DashboardCardFC }) as AutomatedAnalysisGlobalFiltersCategories; const categorizeEvaluation = React.useCallback( - (arr: RuleEvaluation[]) => { - setEvaluations(arr); - const map = new Map(); + (arr: AnalysisResult[]) => { + setResults(arr); + const map = new Map(); arr.forEach((evaluation) => { const topicValue = map.get(evaluation.topic); if (topicValue === undefined) { @@ -170,7 +170,7 @@ export const AutomatedAnalysisCard: DashboardCardFC const sorted = (Array.from(map) as CategorizedRuleEvaluations[]).sort(); setCategorizedEvaluation(sorted); }, - [setCategorizedEvaluation, setEvaluations], + [setCategorizedEvaluation, setResults], ); // Will perform analysis on the first ActiveRecording which has @@ -794,7 +794,7 @@ export const AutomatedAnalysisCard: DashboardCardFC const headerLabels = React.useMemo(() => { if (isLoading || errorMessage) return undefined; - const filtered = evaluations.filter((e) => e.score >= AutomatedAnalysisScore.ORANGE_SCORE_THRESHOLD); + const filtered = results.filter((e) => e.score >= AutomatedAnalysisScore.ORANGE_SCORE_THRESHOLD); if (filtered.length === 0) return ; const [warnings, errors] = _.partition(filtered, (e) => e.score < AutomatedAnalysisScore.RED_SCORE_THRESHOLD); return ( @@ -804,7 +804,7 @@ export const AutomatedAnalysisCard: DashboardCardFC {warnings.length > 0 && } ); - }, [isLoading, errorMessage, evaluations, reportSource]); + }, [isLoading, errorMessage, results, reportSource]); const header = React.useMemo(() => { return ( diff --git a/src/app/Dashboard/AutomatedAnalysis/AutomatedAnalysisCardList.tsx b/src/app/Dashboard/AutomatedAnalysis/AutomatedAnalysisCardList.tsx index e1d8a74f4..01324081b 100644 --- a/src/app/Dashboard/AutomatedAnalysis/AutomatedAnalysisCardList.tsx +++ b/src/app/Dashboard/AutomatedAnalysis/AutomatedAnalysisCardList.tsx @@ -124,24 +124,24 @@ export const AutomatedAnalysisCardList: React.FC - {flatFiltered.map((evaluation) => { + {flatFiltered.map((result) => { return ( - + - {evaluation.name} + {result.name} - {evaluation.score == AutomatedAnalysisScore.NA_SCORE + {result.score == AutomatedAnalysisScore.NA_SCORE ? t('N/A', { ns: 'common' }) - : evaluation.score.toFixed(1)} + : result.score.toFixed(1)} - {icon(evaluation.score)} + {icon(result.score)} - {transformAADescription(evaluation.description)} + {transformAADescription(result)} ); diff --git a/src/app/Dashboard/AutomatedAnalysis/AutomatedAnalysisFilters.tsx b/src/app/Dashboard/AutomatedAnalysis/AutomatedAnalysisFilters.tsx index e31deec3f..3c3a6599f 100644 --- a/src/app/Dashboard/AutomatedAnalysis/AutomatedAnalysisFilters.tsx +++ b/src/app/Dashboard/AutomatedAnalysis/AutomatedAnalysisFilters.tsx @@ -16,7 +16,7 @@ import { allowedAutomatedAnalysisFilters } from '@app/Shared/Redux/Filters/AutomatedAnalysisFilterSlice'; import { UpdateFilterOptions } from '@app/Shared/Redux/Filters/Common'; import { automatedAnalysisUpdateCategoryIntent, RootState, StateDispatch } from '@app/Shared/Redux/ReduxStore'; -import { RuleEvaluation } from '@app/Shared/Services/Report.service'; +import { AnalysisResult } from '@app/Shared/Services/Report.service'; import { Dropdown, DropdownItem, @@ -46,7 +46,7 @@ export interface AutomatedAnalysisGlobalFiltersCategories { export interface AutomatedAnalysisFiltersProps { target: string; - evaluations: [string, RuleEvaluation[]][]; + evaluations: [string, AnalysisResult[]][]; filters: AutomatedAnalysisFiltersCategories; updateFilters: (target: string, updateFilterOptions: UpdateFilterOptions) => void; } @@ -187,7 +187,7 @@ export const AutomatedAnalysisFilters: React.FC = }; export const filterAutomatedAnalysis = ( - topicEvalTuple: [string, RuleEvaluation[]][], + topicEvalTuple: [string, AnalysisResult[]][], filters: AutomatedAnalysisFiltersCategories, globalFilters: AutomatedAnalysisGlobalFiltersCategories, showNAScores: boolean, @@ -202,7 +202,7 @@ export const filterAutomatedAnalysis = ( filtered = filtered.map(([topic, evaluations]) => { return [topic, evaluations.filter((evaluation) => filters.Name.includes(evaluation.name))] as [ string, - RuleEvaluation[], + AnalysisResult[], ]; }); } @@ -216,12 +216,12 @@ export const filterAutomatedAnalysis = ( } return globalFilters.Score <= evaluation.score; }), - ] as [string, RuleEvaluation[]]; + ] as [string, AnalysisResult[]]; }); } if (filters.Topic != null && !!filters.Topic.length) { filtered = filtered.map(([topic, evaluations]) => { - return [topic, evaluations.filter((_) => filters.Topic.includes(topic))] as [string, RuleEvaluation[]]; + return [topic, evaluations.filter((_) => filters.Topic.includes(topic))] as [string, AnalysisResult[]]; }); } diff --git a/src/app/Dashboard/AutomatedAnalysis/ClickableAutomatedAnalysisLabel.tsx b/src/app/Dashboard/AutomatedAnalysis/ClickableAutomatedAnalysisLabel.tsx index 5f39b6dc4..b4214945b 100644 --- a/src/app/Dashboard/AutomatedAnalysis/ClickableAutomatedAnalysisLabel.tsx +++ b/src/app/Dashboard/AutomatedAnalysis/ClickableAutomatedAnalysisLabel.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import { AutomatedAnalysisScore, RuleEvaluation } from '@app/Shared/Services/Report.service'; +import { AutomatedAnalysisScore, AnalysisResult } from '@app/Shared/Services/Report.service'; import { portalRoot } from '@app/utils/utils'; import { Label, LabelProps, Popover } from '@patternfly/react-core'; import { CheckCircleIcon, ExclamationCircleIcon, InfoCircleIcon, WarningTriangleIcon } from '@patternfly/react-icons'; @@ -25,12 +25,12 @@ import { useTranslation } from 'react-i18next'; import { transformAADescription } from '../dashboard-utils'; export interface ClickableAutomatedAnalysisLabelProps { - label: RuleEvaluation; + label: AnalysisResult; } export const clickableAutomatedAnalysisKey = 'clickable-automated-analysis-label'; -export const ClickableAutomatedAnalysisLabel: React.FC = ({ label }) => { +export const ClickableAutomatedAnalysisLabel: React.FC = ({ label: result }) => { const { t } = useTranslation(); const [isHoveredOrFocused, setIsHoveredOrFocused] = React.useState(false); @@ -50,72 +50,72 @@ export const ClickableAutomatedAnalysisLabel: React.FC { // TODO: use label color schemes based on settings for accessibility // context.settings.etc. - return label.score == AutomatedAnalysisScore.NA_SCORE + return result.score == AutomatedAnalysisScore.NA_SCORE ? 'grey' - : label.score < AutomatedAnalysisScore.ORANGE_SCORE_THRESHOLD + : result.score < AutomatedAnalysisScore.ORANGE_SCORE_THRESHOLD ? 'green' - : label.score < AutomatedAnalysisScore.RED_SCORE_THRESHOLD + : result.score < AutomatedAnalysisScore.RED_SCORE_THRESHOLD ? 'orange' : 'red'; - }, [label.score]); + }, [result.score]); const alertPopoverVariant = React.useMemo(() => { - return label.score == AutomatedAnalysisScore.NA_SCORE + return result.score == AutomatedAnalysisScore.NA_SCORE ? 'default' - : label.score < AutomatedAnalysisScore.ORANGE_SCORE_THRESHOLD + : result.score < AutomatedAnalysisScore.ORANGE_SCORE_THRESHOLD ? 'success' - : label.score < AutomatedAnalysisScore.RED_SCORE_THRESHOLD + : result.score < AutomatedAnalysisScore.RED_SCORE_THRESHOLD ? 'warning' : 'danger'; - }, [label.score]); + }, [result.score]); const icon = React.useMemo(() => { - return label.score == AutomatedAnalysisScore.NA_SCORE ? ( + return result.score == AutomatedAnalysisScore.NA_SCORE ? ( - ) : label.score < AutomatedAnalysisScore.ORANGE_SCORE_THRESHOLD ? ( + ) : result.score < AutomatedAnalysisScore.ORANGE_SCORE_THRESHOLD ? ( - ) : label.score < AutomatedAnalysisScore.RED_SCORE_THRESHOLD ? ( + ) : result.score < AutomatedAnalysisScore.RED_SCORE_THRESHOLD ? ( ) : ( ); - }, [label.score]); + }, [result.score]); return ( {label.name}} + headerContent={
{result.name}
} alertSeverityVariant={alertPopoverVariant} alertSeverityScreenReaderText={alertPopoverVariant} shouldOpen={() => setIsDescriptionVisible(true)} shouldClose={() => setIsDescriptionVisible(false)} - key={`${clickableAutomatedAnalysisKey}-popover-${label.name}`} + key={`${clickableAutomatedAnalysisKey}-popover-${result.name}`} bodyContent={

- {label.score == AutomatedAnalysisScore.NA_SCORE ? 'N/A' : label.score.toFixed(1)} + {result.score == AutomatedAnalysisScore.NA_SCORE ? 'N/A' : result.score.toFixed(1)}

- {transformAADescription(label.description)} + {transformAADescription(result)}
} appendTo={portalRoot} >