diff --git a/dashboard/src/components/Accordion/Accordion.tsx b/dashboard/src/components/Accordion/Accordion.tsx index 6114ecc..1e235d8 100644 --- a/dashboard/src/components/Accordion/Accordion.tsx +++ b/dashboard/src/components/Accordion/Accordion.tsx @@ -1,9 +1,11 @@ -import { ReactElement, ReactNode, useMemo } from 'react'; +import { ReactElement, useMemo } from 'react'; import { MdCheck, MdClose } from 'react-icons/md'; import { FormattedMessage } from 'react-intl'; +import { AccordionItemBuilds } from '@/types/tree/TreeDetails'; + import { TableBody, TableCell, TableRow } from '../ui/table'; import BaseTable from '../Table/BaseTable'; import { @@ -14,6 +16,8 @@ import { import ColoredCircle from '../ColoredCircle/ColoredCircle'; import { ItemType } from '../ListingItem/ListingItem'; +import AccordionBuildContent from './BuildAccordionContent'; + export interface IAccordion { headers?: ReactElement[]; items: IAccordionItems[]; @@ -21,8 +25,7 @@ export interface IAccordion { } export interface IAccordionItems { - trigger: AccordionItemBuildsTrigger | AccordionItemTestsTrigger; - content?: ReactNode; + accordionData: AccordionItemBuilds | AccordionItemTestsTrigger; } interface ICustomAccordionTableBody { @@ -30,15 +33,6 @@ interface ICustomAccordionTableBody { type: 'build' | 'test'; } -export type AccordionItemBuildsTrigger = { - config?: string; - compiler?: string; - date?: string; - buildErrors?: number; - buildTime?: string; - status?: 'valid' | 'invalid'; -}; - export type AccordionItemTestsTrigger = { testPlans?: string; testSuccessfull?: number; @@ -81,13 +75,21 @@ const AccordionTableBody = ({ {type === 'build' ? ( - + ) : ( - + )} - {item.content} + +
+ {type === 'build' ? ( + + ) : ( + <> + )} +
+
)), @@ -97,8 +99,10 @@ const AccordionTableBody = ({ return {accordionItems}; }; -const AccordionBuildsTrigger = ({ trigger }: IAccordionItems): JSX.Element => { - const triggerInfo = trigger as AccordionItemBuildsTrigger; +const AccordionBuildsTrigger = ({ + accordionData, +}: IAccordionItems): JSX.Element => { + const triggerInfo = accordionData as AccordionItemBuilds; return ( <> {triggerInfo.config} @@ -123,8 +127,10 @@ const AccordionBuildsTrigger = ({ trigger }: IAccordionItems): JSX.Element => { ); }; -const AccordionTestsTrigger = ({ trigger }: IAccordionItems): JSX.Element => { - const triggerInfo = trigger as AccordionItemTestsTrigger; +const AccordionTestsTrigger = ({ + accordionData, +}: IAccordionItems): JSX.Element => { + const triggerInfo = accordionData as AccordionItemTestsTrigger; return ( <> {triggerInfo.testPlans} diff --git a/dashboard/src/components/Accordion/BuildAccordionContent.tsx b/dashboard/src/components/Accordion/BuildAccordionContent.tsx new file mode 100644 index 0000000..510ae6c --- /dev/null +++ b/dashboard/src/components/Accordion/BuildAccordionContent.tsx @@ -0,0 +1,156 @@ +import { MdFolderOpen } from 'react-icons/md'; + +import { useMemo } from 'react'; + +import { FormattedMessage } from 'react-intl'; + +import { AccordionItemBuilds } from '@/types/tree/TreeDetails'; + +import LinkWithIcon from '../LinkWithIcon/LinkWithIcon'; +import StatusChartMemoized, { Colors } from '../StatusChart/StatusCharts'; + +import { IAccordionItems } from './Accordion'; + +export interface IBuildAccordionContent { + testStatus: { + failTests: number; + errorTests: number; + passTests: number; + skipTests: number; + }; + kernelImage?: string; + buildLogs?: string; + kernelConfig?: string; + dtb?: string; + systemMap?: string; + modules?: string; +} + +export interface ILinksGroup { + kernelImage?: string; + buildLogs?: string; + kernelConfig?: string; + dtb?: string; + systemMap?: string; + modules?: string; +} + +const AccordionBuildContent = ({ + accordionData, +}: IAccordionItems): JSX.Element => { + const contentData = accordionData as AccordionItemBuilds; + const chartElements = useMemo(() => { + return contentData.testStatus?.passTests || + contentData.testStatus?.skipTests || + contentData.testStatus?.errorTests || + contentData.testStatus?.failTests + ? [ + { + value: contentData.testStatus?.passTests ?? 0, + label: 'Test Success', + color: Colors.Green, + }, + { + value: + (contentData.testStatus?.failTests ?? 0) + + (contentData.testStatus?.errorTests ?? 0), + label: 'Test failed', + color: Colors.Red, + }, + { + value: contentData.testStatus?.skipTests ?? 0, + label: 'Test skiped', + color: Colors.Gray, + }, + ] + : [{ value: 1, label: 'None', color: Colors.Gray }]; + }, [ + contentData.testStatus?.errorTests, + contentData.testStatus?.failTests, + contentData.testStatus?.passTests, + contentData.testStatus?.skipTests, + ]); + + return ( +
+
+ } + elements={chartElements} + /> +
+ +
+ ); +}; + +const LinksGroup = ({ + kernelImage, + buildLogs, + kernelConfig, + dtb, + systemMap, + modules, +}: ILinksGroup): JSX.Element => { + return ( +
+ {kernelImage && ( + } + icon={} + linkText={`kernel/${kernelImage}`} + /> + )} + {kernelConfig && ( + } + icon={} + link={kernelConfig} + linkText={} + /> + )} + {dtb && ( + } + icon={} + link={dtb} + linkText={} + /> + )} + {buildLogs && ( + } + icon={} + link={buildLogs} + linkText={} + /> + )} + {systemMap && ( + } + icon={} + link={systemMap} + linkText={} + /> + )} + {modules && ( + } + icon={} + link={modules} + linkText={} + /> + )} +
+ ); +}; + +export default AccordionBuildContent; diff --git a/dashboard/src/components/CardsGroup/CardsGroup.tsx b/dashboard/src/components/CardsGroup/CardsGroup.tsx index d38b837..f77fd8e 100644 --- a/dashboard/src/components/CardsGroup/CardsGroup.tsx +++ b/dashboard/src/components/CardsGroup/CardsGroup.tsx @@ -40,7 +40,7 @@ const CardContent = ({ card }: ICardContent): JSX.Element => { /> ); } else if (card.type === 'chart') { - return ; + return } />; } else { return <>; } diff --git a/dashboard/src/components/LinkWithIcon/LinkWithIcon.tsx b/dashboard/src/components/LinkWithIcon/LinkWithIcon.tsx new file mode 100644 index 0000000..19e7d14 --- /dev/null +++ b/dashboard/src/components/LinkWithIcon/LinkWithIcon.tsx @@ -0,0 +1,27 @@ +import { ReactElement } from 'react'; + +interface ILinkWithIcon { + title?: string | ReactElement; + linkText?: string | ReactElement; + link?: string; + icon?: ReactElement; +} + +const LinkWithIcon = ({ + title, + linkText, + icon, + link, +}: ILinkWithIcon): JSX.Element => { + return ( +
+ {title} + + {linkText} + {icon} + +
+ ); +}; + +export default LinkWithIcon; diff --git a/dashboard/src/components/StatusChart/StatusCharts.tsx b/dashboard/src/components/StatusChart/StatusCharts.tsx index 961ead7..7015e12 100644 --- a/dashboard/src/components/StatusChart/StatusCharts.tsx +++ b/dashboard/src/components/StatusChart/StatusCharts.tsx @@ -52,6 +52,7 @@ const StatusChart = ({ decreaseElement, pieCentralLabel, pieCentralDescription, + title, }: IStatusChart): JSX.Element => { const showChart = elements.some(element => element.value > 0); @@ -69,28 +70,31 @@ const StatusChart = ({ return <>; } return ( -
- - } - /> - -
- - +
+ {title} +
+ + } + /> + +
+ + +
); diff --git a/dashboard/src/components/Tabs/TreeDetails/TreeDetailsBuildTab.tsx b/dashboard/src/components/Tabs/TreeDetails/TreeDetailsBuildTab.tsx index 6459b0c..c665c8b 100644 --- a/dashboard/src/components/Tabs/TreeDetails/TreeDetailsBuildTab.tsx +++ b/dashboard/src/components/Tabs/TreeDetails/TreeDetailsBuildTab.tsx @@ -22,7 +22,7 @@ const TreeDetailsBuildTab = ({ const [filterBy, setFilterBy] = useState<'error' | 'success' | 'all'>('all'); const accordionContent = useMemo(() => { return treeDetailsData?.builds.map(row => ({ - trigger: { + accordionData: { ...row, config: row.config ?? '-', compiler: row.compiler ?? '-', @@ -37,19 +37,26 @@ const TreeDetailsBuildTab = ({ '-' ), date: row.date?.split('T')[0], + testStatus: { + failTests: row.testStatus?.failTests, + errorTests: row.testStatus?.errorTests, + passTests: row.testStatus?.passTests, + skipTests: row.testStatus?.skipTests, + }, }, - content: <>, })); }, [treeDetailsData?.builds]); const filteredContent = filterBy === 'error' ? accordionContent?.filter( - row => row.trigger.buildErrors && row.trigger.buildErrors > 0, + row => + row.accordionData.buildErrors && row.accordionData.buildErrors > 0, ) : filterBy === 'success' ? accordionContent?.filter( - row => row.trigger.status && row.trigger.status === 'valid', + row => + row.accordionData.status && row.accordionData.status === 'valid', ) : accordionContent; diff --git a/dashboard/src/routes/TreeDetails/TreeDetails.tsx b/dashboard/src/routes/TreeDetails/TreeDetails.tsx index 81dddf4..af8882c 100644 --- a/dashboard/src/routes/TreeDetails/TreeDetails.tsx +++ b/dashboard/src/routes/TreeDetails/TreeDetails.tsx @@ -12,14 +12,13 @@ import { useTreeDetails } from '@/api/TreeDetails'; import { IListingItem } from '@/components/ListingItem/ListingItem'; import { ISummaryItem } from '@/components/Summary/Summary'; -import { Results } from '@/types/tree/TreeDetails'; -import { AccordionItemBuildsTrigger } from '@/components/Accordion/Accordion'; +import { AccordionItemBuilds, Results } from '@/types/tree/TreeDetails'; export interface ITreeDetails { archs: ISummaryItem[]; configs: IListingItem[]; buildsSummary: Results; - builds: AccordionItemBuildsTrigger[]; + builds: AccordionItemBuilds[]; } const TreeDetails = (): JSX.Element => { @@ -51,16 +50,28 @@ const TreeDetails = (): JSX.Element => { null: data.summary.builds.null, }; - const buildsData: AccordionItemBuildsTrigger[] = Object.entries( - data.builds, - ).map(([, value]) => ({ - config: value.config_name, - date: value.start_time, - buildTime: value.duration, - compiler: value.compiler, - buildErrors: value.test_status?.error ?? 0, - status: value.valid ? 'valid' : 'invalid', - })); + const buildsData: AccordionItemBuilds[] = Object.entries(data.builds).map( + ([, value]) => ({ + config: value.config_name, + date: value.start_time, + buildTime: value.duration, + compiler: value.compiler, + buildErrors: value.test_status?.error_tests ?? 0, + status: value.valid ? 'valid' : 'invalid', + testStatus: { + failTests: value.test_status?.fail_tests ?? 0, + passTests: value.test_status?.pass_tests ?? 0, + errorTests: value.test_status?.error_tests ?? 0, + skipTests: value.test_status?.skip_tests ?? 0, + }, + buildLogs: value.log_url, + kernelConfig: value.config_url, + kernelImage: value.misc ? value.misc['kernel_type'] : undefined, + dtb: value.misc ? value.misc['dtb'] : undefined, + systemMap: value.misc ? value.misc['system_map'] : undefined, + modules: value.misc ? value.misc['modules'] : undefined, + }), + ); setTreeDetailsData({ archs: archData, diff --git a/dashboard/src/types/tree/TreeDetails.tsx b/dashboard/src/types/tree/TreeDetails.tsx index 8457599..1fa75d4 100644 --- a/dashboard/src/types/tree/TreeDetails.tsx +++ b/dashboard/src/types/tree/TreeDetails.tsx @@ -9,16 +9,44 @@ type TreeDetailsBuild = { config_url: string; log_url: string; test_status: { - fail: number; - error: number; - miss: number; - pass: number; - done: number; - skip: number; - null: number; - total: number; + fail_tests: number; + error_tests: number; + miss_tests: number; + pass_tests: number; + done_tests: number; + skip_tests: number; + null_tests: number; + total_tests: number; }; - misc: JSON | null; + misc: ITreeDetailsMisc | null; +}; + +interface ITreeDetailsMisc { + kernel_type?: string; + dtb?: string; + modules?: string; + system_map?: string; +} + +export type AccordionItemBuilds = { + config?: string; + compiler?: string; + date?: string; + buildErrors?: number; + buildTime?: string; + status?: 'valid' | 'invalid'; + testStatus?: { + failTests: number; + errorTests: number; + passTests: number; + skipTests: number; + }; + kernelImage?: string; + buildLogs?: string; + kernelConfig?: string; + dtb?: string; + systemMap?: string; + modules?: string; }; export type TreeDetails = {