From e262d0fbb84d46387d2088fa22a0ae5fe84625ad Mon Sep 17 00:00:00 2001 From: Murilo Geraldini Date: Mon, 20 Jan 2025 16:29:49 -0300 Subject: [PATCH] feat(details): Add json viewer - Changed `LogSheetContent` to be able to work with both log content or JSON content - Added state variables needed to control if the sheet content is log or json in {Builds,Tests,Issues}Details. Part of #782 Closes #787 --- dashboard/package.json | 1 + dashboard/pnpm-lock.yaml | 141 ++++++++++++++++++ .../components/BuildDetails/BuildDetails.tsx | 19 ++- .../components/IssueDetails/IssueDetails.tsx | 51 ++++--- .../src/components/Section/MiscSection.tsx | 24 +++ dashboard/src/components/Section/Section.tsx | 6 +- .../LogOrJsonSheetContent.tsx} | 38 +++-- .../components/TestDetails/TestDetails.tsx | 40 ++++- dashboard/src/locales/messages/index.ts | 1 + .../Tabs/WrapperTableWithLogSheet.tsx | 4 +- 10 files changed, 280 insertions(+), 45 deletions(-) rename dashboard/src/components/{Log/LogSheetContent.tsx => Sheet/LogOrJsonSheetContent.tsx} (52%) diff --git a/dashboard/package.json b/dashboard/package.json index 6d5045b5..7ddbdc74 100644 --- a/dashboard/package.json +++ b/dashboard/package.json @@ -22,6 +22,7 @@ "@emotion/react": "^11.13.3", "@emotion/styled": "^11.13.0", "@hookform/resolvers": "^3.9.0", + "@microlink/react-json-view": "^1.24.0", "@mui/material": "^6.1.5", "@mui/x-charts": "^7.22.0", "@radix-ui/react-alert-dialog": "^1.1.1", diff --git a/dashboard/pnpm-lock.yaml b/dashboard/pnpm-lock.yaml index 472e7df6..8e8701cc 100644 --- a/dashboard/pnpm-lock.yaml +++ b/dashboard/pnpm-lock.yaml @@ -20,6 +20,9 @@ importers: '@hookform/resolvers': specifier: ^3.9.0 version: 3.9.0(react-hook-form@7.53.0(react@18.3.1)) + '@microlink/react-json-view': + specifier: ^1.24.0 + version: 1.24.0(@types/react@18.3.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@mui/material': specifier: ^6.1.5 version: 6.1.5(@emotion/react@11.13.3(@types/react@18.3.6)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.6)(react@18.3.1))(@types/react@18.3.6)(react@18.3.1))(@types/react@18.3.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -916,6 +919,12 @@ packages: '@types/react': '>=16' react: '>=16' + '@microlink/react-json-view@1.24.0': + resolution: {integrity: sha512-xrOBvWUR05Pe1x/H7op/yrLFpmf+VjMc5acu7pQpfS70Pux6AmoI+sO7jf2e76OYv27TOvt0TnYSXc3OtjH3Bg==} + peerDependencies: + react: '>= 15' + react-dom: '>= 15' + '@mui/core-downloads-tracker@6.1.5': resolution: {integrity: sha512-3J96098GrC95XsLw/TpGNMxhUOnoG9NZ/17Pfk1CrJj+4rcuolsF2RdF3XAFTu/3a/A+5ouxlSIykzYz6Ee87g==} @@ -1988,6 +1997,9 @@ packages: '@types/babel__traverse@7.20.6': resolution: {integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==} + '@types/base16@1.0.5': + resolution: {integrity: sha512-OzOWrTluG9cwqidEzC/Q6FAmIPcnZfm8BFRlIx0+UIUqnuAmi5OS88O0RpT3Yz6qdmqObvUhasrbNsCofE4W9A==} + '@types/body-parser@1.19.5': resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} @@ -2499,6 +2511,9 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + base16@1.0.0: + resolution: {integrity: sha512-pNdYkNPiJUnEhnfXV56+sQy8+AaPcG3POZAUnwr4EeqCUZFz4u2PePbo3e5Gj4ziYPCWGUZT9RHisvJKnwFuBQ==} + binary-extensions@2.3.0: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} @@ -2630,6 +2645,12 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + color-string@1.9.1: + resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} + + color@3.2.1: + resolution: {integrity: sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==} + combined-stream@1.0.8: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} @@ -3457,6 +3478,9 @@ packages: is-arrayish@0.2.1: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + is-arrayish@0.3.2: + resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} + is-async-function@2.0.0: resolution: {integrity: sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==} engines: {node: '>= 0.4'} @@ -3669,6 +3693,9 @@ packages: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} + lodash.curry@4.1.1: + resolution: {integrity: sha512-/u14pXGviLaweY5JI0IUzgzF2J6Ne8INyzAZjImcryjgkZ+ebruBxy2/JaOOkTqScddcYtakjhSaeemV8lR0tA==} + lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} @@ -4126,6 +4153,9 @@ packages: resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} engines: {node: '>= 0.8'} + react-base16-styling@0.9.1: + resolution: {integrity: sha512-1s0CY1zRBOQ5M3T61wetEpvQmsYSNtWEcdYzyZNxKa8t7oDvaOn9d21xrGezGAHFWLM7SHcktPuPTrvoqxSfKw==} + react-colorful@5.6.1: resolution: {integrity: sha512-1exovf0uGTGyq5mXQT0zgQ80uvj2PCwvF8zY1RN9/vbJVSjSo3fsB/4L3ObbF7u70NduSiK4xu4Y6q1MHoUGEw==} peerDependencies: @@ -4195,6 +4225,9 @@ packages: react-is@18.3.1: resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + react-lifecycles-compat@3.0.4: + resolution: {integrity: sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==} + react-refresh@0.14.2: resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==} engines: {node: '>=0.10.0'} @@ -4229,6 +4262,12 @@ packages: '@types/react': optional: true + react-textarea-autosize@8.3.4: + resolution: {integrity: sha512-CdtmP8Dc19xL8/R6sWvtknD/eCXkQr30dtvC4VmGInhRsfF8X/ihXCq6+9l9qbxmKRiq407/7z5fxE7cVWQNgQ==} + engines: {node: '>=10'} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-transition-group@4.4.5: resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==} peerDependencies: @@ -4390,6 +4429,9 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} + simple-swizzle@0.2.2: + resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} + slash@3.0.0: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} @@ -4739,6 +4781,33 @@ packages: '@types/react': optional: true + use-composed-ref@1.4.0: + resolution: {integrity: sha512-djviaxuOOh7wkj0paeO1Q/4wMZ8Zrnag5H6yBvzN7AKKe8beOaED9SF5/ByLqsku8NP4zQqsvM2u3ew/tJK8/w==} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + use-isomorphic-layout-effect@1.2.0: + resolution: {integrity: sha512-q6ayo8DWoPZT0VdG4u3D3uxcgONP3Mevx2i2b0434cwWBoL+aelL1DzkXI6w3PhTZzUeR2kaVlZn70iCiseP6w==} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + use-latest@1.3.0: + resolution: {integrity: sha512-mhg3xdm9NaM8q+gLT8KryJPnRFOz1/5XPBhmDEVZK1webPzDjrPk7f/mbpeLqTgB9msytYWANxgALOCJKnLvcQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + use-sidecar@1.1.2: resolution: {integrity: sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==} engines: {node: '>=10'} @@ -5602,6 +5671,16 @@ snapshots: '@types/react': 18.3.6 react: 18.3.1 + '@microlink/react-json-view@1.24.0(@types/react@18.3.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + react: 18.3.1 + react-base16-styling: 0.9.1 + react-dom: 18.3.1(react@18.3.1) + react-lifecycles-compat: 3.0.4 + react-textarea-autosize: 8.3.4(@types/react@18.3.6)(react@18.3.1) + transitivePeerDependencies: + - '@types/react' + '@mui/core-downloads-tracker@6.1.5': {} '@mui/material@6.1.5(@emotion/react@11.13.3(@types/react@18.3.6)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.6)(react@18.3.1))(@types/react@18.3.6)(react@18.3.1))(@types/react@18.3.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': @@ -6791,6 +6870,8 @@ snapshots: dependencies: '@babel/types': 7.24.7 + '@types/base16@1.0.5': {} + '@types/body-parser@1.19.5': dependencies: '@types/connect': 3.4.38 @@ -7446,6 +7527,8 @@ snapshots: balanced-match@1.0.2: {} + base16@1.0.0: {} + binary-extensions@2.3.0: {} body-parser@1.20.3: @@ -7588,6 +7671,16 @@ snapshots: color-name@1.1.4: {} + color-string@1.9.1: + dependencies: + color-name: 1.1.4 + simple-swizzle: 0.2.2 + + color@3.2.1: + dependencies: + color-convert: 1.9.3 + color-string: 1.9.1 + combined-stream@1.0.8: dependencies: delayed-stream: 1.0.0 @@ -8563,6 +8656,8 @@ snapshots: is-arrayish@0.2.1: {} + is-arrayish@0.3.2: {} + is-async-function@2.0.0: dependencies: has-tostringtag: 1.0.2 @@ -8754,6 +8849,8 @@ snapshots: dependencies: p-locate: 5.0.0 + lodash.curry@4.1.1: {} + lodash.merge@4.6.2: {} lodash@4.17.21: {} @@ -9103,6 +9200,16 @@ snapshots: iconv-lite: 0.4.24 unpipe: 1.0.0 + react-base16-styling@0.9.1: + dependencies: + '@babel/runtime': 7.26.0 + '@types/base16': 1.0.5 + '@types/lodash': 4.17.6 + base16: 1.0.0 + color: 3.2.1 + csstype: 3.1.3 + lodash.curry: 4.1.1 + react-colorful@5.6.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: react: 18.3.1 @@ -9183,6 +9290,8 @@ snapshots: react-is@18.3.1: {} + react-lifecycles-compat@3.0.4: {} + react-refresh@0.14.2: {} react-remove-scroll-bar@2.3.6(@types/react@18.3.6)(react@18.3.1): @@ -9213,6 +9322,15 @@ snapshots: optionalDependencies: '@types/react': 18.3.6 + react-textarea-autosize@8.3.4(@types/react@18.3.6)(react@18.3.1): + dependencies: + '@babel/runtime': 7.26.0 + react: 18.3.1 + use-composed-ref: 1.4.0(@types/react@18.3.6)(react@18.3.1) + use-latest: 1.3.0(@types/react@18.3.6)(react@18.3.1) + transitivePeerDependencies: + - '@types/react' + react-transition-group@4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@babel/runtime': 7.26.0 @@ -9439,6 +9557,10 @@ snapshots: signal-exit@4.1.0: {} + simple-swizzle@0.2.2: + dependencies: + is-arrayish: 0.3.2 + slash@3.0.0: {} source-map-js@1.2.1: {} @@ -9813,6 +9935,25 @@ snapshots: optionalDependencies: '@types/react': 18.3.6 + use-composed-ref@1.4.0(@types/react@18.3.6)(react@18.3.1): + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.6 + + use-isomorphic-layout-effect@1.2.0(@types/react@18.3.6)(react@18.3.1): + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.6 + + use-latest@1.3.0(@types/react@18.3.6)(react@18.3.1): + dependencies: + react: 18.3.1 + use-isomorphic-layout-effect: 1.2.0(@types/react@18.3.6)(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.6 + use-sidecar@1.1.2(@types/react@18.3.6)(react@18.3.1): dependencies: detect-node-es: 1.1.0 diff --git a/dashboard/src/components/BuildDetails/BuildDetails.tsx b/dashboard/src/components/BuildDetails/BuildDetails.tsx index de91c1e2..796feb38 100644 --- a/dashboard/src/components/BuildDetails/BuildDetails.tsx +++ b/dashboard/src/components/BuildDetails/BuildDetails.tsx @@ -4,7 +4,7 @@ import { MdClose, MdCheck, MdFolderOpen } from 'react-icons/md'; import { useIntl } from 'react-intl'; import { ErrorBoundary } from 'react-error-boundary'; -import { useMemo } from 'react'; +import { useMemo, useState } from 'react'; import { useSearch, @@ -27,7 +27,10 @@ import { Sheet, SheetTrigger } from '@/components/Sheet'; import type { TableFilter, TestsTableFilter } from '@/types/tree/TreeDetails'; -import { LogSheetContent } from '@/components/Log/LogSheetContent'; +import { + LogOrJsonSheetContent, + type SheetType, +} from '@/components/Sheet/LogOrJsonSheetContent'; import { TruncatedValueTooltip } from '@/components/Tooltip/TruncatedValueTooltip'; import { getMiscSection } from '@/components/Section/MiscSection'; @@ -70,12 +73,17 @@ const BuildDetails = ({ const hasUsefulLogInfo = data?.log_url || data?.log_excerpt; + const [sheetType, setSheetType] = useState('log'); + const [jsonContent, setJsonContent] = useState(); + const miscSection: ISection | undefined = useMemo((): | ISection | undefined => { return getMiscSection({ misc: data?.misc, title: formatMessage({ id: 'globalDetails.miscData' }), + setSheetType: setSheetType, + setJsonContent: setJsonContent, }); }, [data?.misc, formatMessage]); @@ -188,6 +196,9 @@ const BuildDetails = ({ ), icon: hasUsefulLogInfo ? : undefined, wrapperComponent: hasUsefulLogInfo ? SheetTrigger : undefined, + wrapperOnClick: hasUsefulLogInfo + ? (): void => setSheetType('log') + : undefined, }, { title: 'buildDetails.buildId', @@ -242,7 +253,9 @@ const BuildDetails = ({ historyState={historyState} previousSearch={searchParams} /> - diff --git a/dashboard/src/components/IssueDetails/IssueDetails.tsx b/dashboard/src/components/IssueDetails/IssueDetails.tsx index 0415ebbf..fa582845 100644 --- a/dashboard/src/components/IssueDetails/IssueDetails.tsx +++ b/dashboard/src/components/IssueDetails/IssueDetails.tsx @@ -1,6 +1,6 @@ import { useIntl } from 'react-intl'; import { ErrorBoundary } from 'react-error-boundary'; -import { useMemo } from 'react'; +import { useMemo, useState } from 'react'; import type { LinkProps } from '@tanstack/react-router'; @@ -26,6 +26,9 @@ import QuerySwitcher from '@/components/QuerySwitcher/QuerySwitcher'; import { MemoizedSectionError } from '@/components/DetailsPages/SectionError'; +import { Sheet } from '@/components/Sheet'; +import { LogOrJsonSheetContent } from '@/components/Sheet/LogOrJsonSheetContent'; + import { IssueDetailsTestSection } from './IssueDetailsTestSection'; import { IssueDetailsBuildSection } from './IssueDetailsBuildSection'; @@ -58,12 +61,15 @@ export const IssueDetails = ({ const { formatMessage } = useIntl(); + const [jsonContent, setJsonContent] = useState(); + const miscSection: ISection | undefined = useMemo((): | ISection | undefined => { return getMiscSection({ misc: data?.misc, title: formatMessage({ id: 'globalDetails.miscData' }), + setJsonContent: setJsonContent, }); }, [data?.misc, formatMessage]); @@ -158,26 +164,29 @@ export const IssueDetails = ({ } > - {breadcrumb} - - {(hasTest || hasNothingIdentified) && ( - - )} - {(hasBuild || hasNothingIdentified) && ( - - )} + + {breadcrumb} + + {(hasTest || hasNothingIdentified) && ( + + )} + {(hasBuild || hasNothingIdentified) && ( + + )} + + ); diff --git a/dashboard/src/components/Section/MiscSection.tsx b/dashboard/src/components/Section/MiscSection.tsx index 03d31f17..9bbcb6b9 100644 --- a/dashboard/src/components/Section/MiscSection.tsx +++ b/dashboard/src/components/Section/MiscSection.tsx @@ -1,16 +1,32 @@ +import { type Dispatch, type SetStateAction } from 'react'; + +import { MdFolderOpen } from 'react-icons/md'; + +import type { SheetType } from '@/components/Sheet/LogOrJsonSheetContent'; + import { isUrl, shouldTruncate } from '@/lib/string'; import { TruncatedValueTooltip } from '@/components/Tooltip/TruncatedValueTooltip'; import CopyButton from '@/components/Button/CopyButton'; +import { SheetTrigger } from '@/components/Sheet'; + import type { ISection, SubsectionLink } from './Section'; +const BlueFolderIcon = (): JSX.Element => ( + +); + export const getMiscSection = ({ misc, title, + setSheetType, + setJsonContent, }: { misc?: object; title: string; + setSheetType?: Dispatch>; + setJsonContent?: Dispatch>; }): ISection | undefined => { if (!misc || Object.keys(misc).length === 0) { return; @@ -44,6 +60,14 @@ export const getMiscSection = ({ {isObjectField && } ), + icon: isObjectField ? : undefined, + wrapperComponent: isObjectField ? SheetTrigger : undefined, + wrapperOnClick: isObjectField + ? (): void => { + if (setSheetType !== undefined) setSheetType('json'); + if (setJsonContent !== undefined) setJsonContent(fieldValue); + } + : undefined, link: isUrlResult ? stringField : undefined, }; }, diff --git a/dashboard/src/components/Section/Section.tsx b/dashboard/src/components/Section/Section.tsx index 957dc29d..4f6f9899 100644 --- a/dashboard/src/components/Section/Section.tsx +++ b/dashboard/src/components/Section/Section.tsx @@ -16,6 +16,7 @@ export interface ISection { export interface SubsectionLink extends ILinkWithIcon { wrapperComponent?: ElementType<{ children: ReactNode }>; + wrapperOnClick?: () => void; copyValue?: string; } export interface ISubsection { @@ -28,7 +29,10 @@ export const Subsection = ({ infos }: ISubsection): JSX.Element => { infos.map(info => { const WrapperComponent = info.wrapperComponent ?? Fragment; return ( - +
{ return ( - - + {type === 'log' ? ( + <> + + + + ) : ( + + )} ); }; diff --git a/dashboard/src/components/TestDetails/TestDetails.tsx b/dashboard/src/components/TestDetails/TestDetails.tsx index 0beac8de..cd4dc6cb 100644 --- a/dashboard/src/components/TestDetails/TestDetails.tsx +++ b/dashboard/src/components/TestDetails/TestDetails.tsx @@ -2,7 +2,7 @@ import { useIntl } from 'react-intl'; import { GiFlatPlatform } from 'react-icons/gi'; -import { useMemo } from 'react'; +import { useMemo, useState, type Dispatch, type SetStateAction } from 'react'; import { PiComputerTowerThin } from 'react-icons/pi'; import { MdFolderOpen } from 'react-icons/md'; @@ -18,7 +18,10 @@ import { useTestDetails, useTestIssues } from '@/api/testDetails'; import { RedirectFrom } from '@/types/general'; -import { LogSheetContent } from '@/components/Log/LogSheetContent'; +import { + LogOrJsonSheetContent, + type SheetType, +} from '@/components/Sheet/LogOrJsonSheetContent'; import type { ISection } from '@/components/Section/Section'; import { TooltipDateTime } from '@/components/TooltipDateTime'; import IssueSection from '@/components/Issue/IssueSection'; @@ -30,7 +33,15 @@ import { TruncatedValueTooltip } from '@/components/Tooltip/TruncatedValueToolti import QuerySwitcher from '@/components/QuerySwitcher/QuerySwitcher'; import { MemoizedSectionError } from '@/components/DetailsPages/SectionError'; -const TestDetailsSections = ({ test }: { test: TTestDetails }): JSX.Element => { +const TestDetailsSections = ({ + test, + setSheetType, + setJsonContent, +}: { + test: TTestDetails; + setSheetType: Dispatch>; + setJsonContent: Dispatch>; +}): JSX.Element => { const { formatMessage } = useIntl(); const historyState = useRouterState({ select: s => s.location.state }); const searchParams = useSearch({ from: '/test/$testId' }); @@ -106,6 +117,9 @@ const TestDetailsSections = ({ test }: { test: TTestDetails }): JSX.Element => { valueOrEmpty(test.log_url) ), wrapperComponent: hasUsefulLogInfo ? SheetTrigger : undefined, + wrapperOnClick: hasUsefulLogInfo + ? (): void => setSheetType('log') + : undefined, }, { title: 'testDetails.gitCommitHash', @@ -180,6 +194,7 @@ const TestDetailsSections = ({ test }: { test: TTestDetails }): JSX.Element => { hasUsefulLogInfo, buildDetailsLink, hardware, + setSheetType, ]); const miscSection: ISection | undefined = useMemo((): @@ -188,8 +203,10 @@ const TestDetailsSections = ({ test }: { test: TTestDetails }): JSX.Element => { return getMiscSection({ misc: test.misc, title: formatMessage({ id: 'globalDetails.miscData' }), + setSheetType: setSheetType, + setJsonContent: setJsonContent, }); - }, [formatMessage, test.misc]); + }, [formatMessage, test.misc, setSheetType, setJsonContent]); const environmentMiscSection: ISection | undefined = useMemo((): | ISection @@ -237,6 +254,9 @@ const TestDetails = ({ const { formatMessage } = useIntl(); + const [sheetType, setSheetType] = useState('log'); + const [jsonContent, setJsonContent] = useState(); + return ( {breadcrumb} - {data && } + {data && ( + + )}
- diff --git a/dashboard/src/locales/messages/index.ts b/dashboard/src/locales/messages/index.ts index 5259bce9..1fb2d992 100644 --- a/dashboard/src/locales/messages/index.ts +++ b/dashboard/src/locales/messages/index.ts @@ -198,6 +198,7 @@ export const messages = { 'issueDetails.reportSubject': 'Report Subject', 'issueDetails.reportUrl': 'Report URL', 'issueDetails.version': 'Version', + 'jsonSheet.title': 'JSON Viewer', 'logSheet.downloadLog': 'You can download the full log here: {link}', 'logSheet.fileName': 'File Name', 'logSheet.fileSize': 'File Size', diff --git a/dashboard/src/pages/TreeDetails/Tabs/WrapperTableWithLogSheet.tsx b/dashboard/src/pages/TreeDetails/Tabs/WrapperTableWithLogSheet.tsx index 8b04a6e5..4963a1c1 100644 --- a/dashboard/src/pages/TreeDetails/Tabs/WrapperTableWithLogSheet.tsx +++ b/dashboard/src/pages/TreeDetails/Tabs/WrapperTableWithLogSheet.tsx @@ -4,7 +4,7 @@ import type { LinkProps } from '@tanstack/react-router'; import { Sheet } from '@/components/Sheet'; -import { LogSheetContent } from '@/components/Log/LogSheetContent'; +import { LogOrJsonSheetContent } from '@/components/Sheet/LogOrJsonSheetContent'; import type { TNavigationLogActions } from '@/components/Sheet/WrapperSheetContent'; interface TableWithLogSheetProps { @@ -30,7 +30,7 @@ const WrapperTableWithLogSheet = ({ {children} -