From 592d30988bebdb046656d444135e37767c8f3e10 Mon Sep 17 00:00:00 2001 From: Radoslaw Szwajkowski Date: Sun, 9 Jun 2024 20:05:48 +0200 Subject: [PATCH] :sparkles: Move Analysis Details to full page view (#1930) Related changes: 1. extend BreadCrumbPath component to support non-links 2. refactor CSS styles for SimpleDocumentViewer to support both modal and in-page mode 3. create new route for the analysis details page: /applications/:applicationId/analysis-details/:taskId Part-of: https://github.com/konveyor/tackle2-ui/issues/1929 Signed-off-by: Radoslaw Szwajkowski --- client/src/app/Paths.ts | 7 +++ client/src/app/Routes.tsx | 9 ++- client/src/app/components/BreadCrumbPath.tsx | 7 ++- client/src/app/components/PageHeader.tsx | 2 +- .../app/components/SimpleDocumentViewer.css | 36 +++++------- .../analysis-details/AnalysisDetails.tsx | 55 +++++++++++++++++++ .../applications/analysis-details/index.ts | 1 + .../applications-table/applications-table.tsx | 30 +++++----- .../application-detail-drawer.tsx | 30 +++++----- 9 files changed, 122 insertions(+), 55 deletions(-) create mode 100644 client/src/app/pages/applications/analysis-details/AnalysisDetails.tsx create mode 100644 client/src/app/pages/applications/analysis-details/index.ts diff --git a/client/src/app/Paths.ts b/client/src/app/Paths.ts index caf486843..57708283a 100644 --- a/client/src/app/Paths.ts +++ b/client/src/app/Paths.ts @@ -1,6 +1,8 @@ export const DevPaths = { // Developer perspective applications: "/applications", + applicationsAnalysisDetails: + "/applications/:applicationId/analysis-details/:taskId", applicationsAnalysisTab: "/applications/analysis-tab", applicationsAssessmentTab: "/applications/assessment-tab", applicationsImports: "/applications/application-imports", @@ -86,3 +88,8 @@ export interface ReviewRoute { export interface ImportSummaryRoute { importId: string; } + +export interface AnalysisDetailsRoute { + applicationId: string; + taskId: string; +} diff --git a/client/src/app/Routes.tsx b/client/src/app/Routes.tsx index 65437e9df..46cf40bd0 100644 --- a/client/src/app/Routes.tsx +++ b/client/src/app/Routes.tsx @@ -24,7 +24,9 @@ const ManageImports = lazy(() => import("./pages/applications/manage-imports")); const ImportDetails = lazy( () => import("./pages/applications/manage-imports-details") ); - +const AnalysisDetails = lazy( + () => import("./pages/applications/analysis-details") +); const Reports = lazy(() => import("./pages/reports")); const Controls = lazy(() => import("./pages/controls")); const Identities = lazy(() => import("./pages/identities")); @@ -74,6 +76,11 @@ export const devRoutes: IRoute[] = [ comp: ImportDetails, exact: false, }, + { + path: Paths.applicationsAnalysisDetails, + comp: AnalysisDetails, + exact: false, + }, { path: Paths.applicationsImports, comp: ManageImports, diff --git a/client/src/app/components/BreadCrumbPath.tsx b/client/src/app/components/BreadCrumbPath.tsx index b4d6f4f3b..ea494f378 100644 --- a/client/src/app/components/BreadCrumbPath.tsx +++ b/client/src/app/components/BreadCrumbPath.tsx @@ -3,7 +3,7 @@ import { Link } from "react-router-dom"; import { Breadcrumb, BreadcrumbItem, Button } from "@patternfly/react-core"; export interface BreadCrumbPathProps { - breadcrumbs: { title: string; path: string | (() => void) }[]; + breadcrumbs: { title: string; path?: string | (() => void) }[]; } export const BreadCrumbPath: React.FC = ({ @@ -12,6 +12,11 @@ export const BreadCrumbPath: React.FC = ({ return ( {breadcrumbs.map((crumb, i, { length }) => { + if (!crumb.path) { + // the item is not a link + return {crumb.title}; + } + const isLast = i === length - 1; const link = diff --git a/client/src/app/components/PageHeader.tsx b/client/src/app/components/PageHeader.tsx index 25a12c768..7382ea54e 100644 --- a/client/src/app/components/PageHeader.tsx +++ b/client/src/app/components/PageHeader.tsx @@ -13,7 +13,7 @@ import { HorizontalNav } from "./HorizontalNav"; export interface PageHeaderProps { title: string; description?: React.ReactNode; - breadcrumbs: { title: string; path: string | (() => void) }[]; + breadcrumbs: { title: string; path?: string | (() => void) }[]; btnActions?: React.ReactNode; navItems?: { title: string; path: string }[]; } diff --git a/client/src/app/components/SimpleDocumentViewer.css b/client/src/app/components/SimpleDocumentViewer.css index a566ed5be..e1b00de13 100644 --- a/client/src/app/components/SimpleDocumentViewer.css +++ b/client/src/app/components/SimpleDocumentViewer.css @@ -8,7 +8,7 @@ } /* Match empty state layout to the editor layout so they take up exactly the same space */ -.simple-task-viewer .simple-task-viewer-empty-state { +.simple-task-viewer-code .simple-task-viewer-empty-state { padding: var(--pf-v5-c-code-editor__code--PaddingTop) var(--pf-v5-c-code-editor__code--PaddingRight) var(--pf-v5-c-code-editor__code--PaddingBottom) @@ -17,37 +17,28 @@ /* Make all of the containers of the MonacoEditor component take the maximum height so the - editor can always be as tall as possible in the Modal. + editor can always be as tall as possible. */ -.simple-task-viewer[class*="full-height"] .pf-v5-c-modal-box__body { +.simple-task-viewer[class*="full-height"] .pf-v5-c-modal-box__body, +.simple-task-viewer-container { display: flex; flex-direction: column; } -.simple-task-viewer[class*="full-height"] - .pf-v5-c-modal-box__body - .pf-v5-c-code-editor { +.simple-task-viewer-code { flex-grow: 1; + height: 100%; } -.simple-task-viewer[class*="full-height"] - .pf-v5-c-modal-box__body - .pf-v5-c-code-editor - .pf-v5-c-file-upload { +.simple-task-viewer-code .pf-v5-c-file-upload { height: 100%; } -.simple-task-viewer[class*="full-height"] - .pf-v5-c-modal-box__body - .pf-v5-c-code-editor - .pf-v5-c-file-upload - .pf-v5-c-code-editor__main { +.simple-task-viewer-code .pf-v5-c-file-upload .pf-v5-c-code-editor__main { flex-grow: 1; } -.simple-task-viewer[class*="full-height"] - .pf-v5-c-modal-box__body - .pf-v5-c-code-editor +.simple-task-viewer-code .pf-v5-c-file-upload .pf-v5-c-code-editor__main .pf-v5-c-code-editor__code { @@ -58,20 +49,21 @@ Tweak the code editor so we can put the language selection toggle group in the "Language Label" location */ -.simple-task-viewer .pf-v5-c-code-editor__controls { +.simple-task-viewer-code .pf-v5-c-code-editor__controls { flex-grow: 1; } -.simple-task-viewer .pf-v5-c-code-editor__header-main { +.simple-task-viewer-code .pf-v5-c-code-editor__header-main { display: none; } -.simple-task-viewer .pf-v5-c-code-editor__tab.language-toggle-group-container { +.simple-task-viewer-code + .pf-v5-c-code-editor__tab.language-toggle-group-container { padding: 0; border: none; } -.simple-task-viewer .language-toggle-group { +.simple-task-viewer-code .language-toggle-group { --pf-v5-c-toggle-group__button--FontSize: var(--pf-v5-global--FontSize--md); } diff --git a/client/src/app/pages/applications/analysis-details/AnalysisDetails.tsx b/client/src/app/pages/applications/analysis-details/AnalysisDetails.tsx new file mode 100644 index 000000000..15d65ca70 --- /dev/null +++ b/client/src/app/pages/applications/analysis-details/AnalysisDetails.tsx @@ -0,0 +1,55 @@ +import React from "react"; +import { useParams } from "react-router-dom"; +import { useTranslation } from "react-i18next"; + +import { PageSection } from "@patternfly/react-core"; + +import { AnalysisDetailsRoute, Paths } from "@app/Paths"; +import { PageHeader } from "@app/components/PageHeader"; +import { formatPath } from "@app/utils/utils"; +import { SimpleDocumentViewer } from "@app/components/SimpleDocumentViewer"; +import { useFetchApplicationById } from "@app/queries/applications"; +import { useFetchTaskByID } from "@app/queries/tasks"; + +export const AnalysisDetails: React.FC = () => { + // i18 + const { t } = useTranslation(); + + // Router + const { applicationId, taskId } = useParams(); + + const { application } = useFetchApplicationById(applicationId); + const { task } = useFetchTaskByID(Number(taskId)); + const taskName = + (typeof task != "string" ? task?.name : taskId) ?? t("terms.unknown"); + const appName = application?.name ?? t("terms.unknown") ?? ""; + + return ( + <> + + + + + + + + ); +}; diff --git a/client/src/app/pages/applications/analysis-details/index.ts b/client/src/app/pages/applications/analysis-details/index.ts new file mode 100644 index 000000000..bbd98b98f --- /dev/null +++ b/client/src/app/pages/applications/analysis-details/index.ts @@ -0,0 +1 @@ +export { AnalysisDetails as default } from "./AnalysisDetails"; diff --git a/client/src/app/pages/applications/applications-table/applications-table.tsx b/client/src/app/pages/applications/applications-table/applications-table.tsx index 59eb559a2..83cc84b0f 100644 --- a/client/src/app/pages/applications/applications-table/applications-table.tsx +++ b/client/src/app/pages/applications/applications-table/applications-table.tsx @@ -104,7 +104,6 @@ import { ApplicationDependenciesForm } from "@app/components/ApplicationDependen import { useState } from "react"; import { ApplicationAnalysisStatus } from "../components/application-analysis-status"; import { ApplicationDetailDrawer } from "../components/application-detail-drawer/application-detail-drawer"; -import { SimpleDocumentViewerModal } from "@app/components/SimpleDocumentViewer"; import { AnalysisWizard } from "../analysis-wizard/analysis-wizard"; import { TaskGroupProvider } from "../analysis-wizard/components/TaskGroupContext"; import { ApplicationIdentityForm } from "../components/application-identity-form/application-identity-form"; @@ -553,11 +552,6 @@ export const ApplicationsTable: React.FC = () => { const [isApplicationImportModalOpen, setIsApplicationImportModalOpen] = useState(false); - const [taskToView, setTaskToView] = useState<{ - name: string; - task: number | undefined; - }>(); - const userScopes: string[] = token?.scope.split(" ") || [], importWriteAccess = checkAccess(userScopes, importsWriteScopes), applicationWriteAccess = checkAccess(userScopes, applicationsWriteScopes), @@ -1072,11 +1066,20 @@ export const ApplicationsTable: React.FC = () => { ? [ { title: t("actions.analysisDetails"), - onClick: () => - setTaskToView({ - name: application.name, - task: getTask(application)?.id, - }), + onClick: () => { + const taskId = getTask(application)?.id; + if (taskId && application.id) { + history.push( + formatPath( + Paths.applicationsAnalysisDetails, + { + applicationId: application.id, + taskId, + } + ) + ); + } + }, }, ] : []), @@ -1153,11 +1156,6 @@ export const ApplicationsTable: React.FC = () => { onClose={() => setSaveApplicationModalState(null)} /> )} - setTaskToView(undefined)} - /> { @@ -101,8 +102,6 @@ export const ApplicationDetailDrawer: React.FC< const { identities } = useFetchIdentities(); const { facts, isFetching } = useFetchFacts(application?.id); - const [taskIdToView, setTaskIdToView] = React.useState(); - let matchingSourceCredsRef: Identity | undefined; let matchingMavenCredsRef: Identity | undefined; if (application && identities) { @@ -113,6 +112,16 @@ export const ApplicationDetailDrawer: React.FC< const notAvailable = ; const enableDownloadSetting = useSetting("download.html.enabled"); + const history = useHistory(); + const navigateToAnalysisDetails = () => + application?.id && + task?.id && + history.push( + formatPath(Paths.applicationsAnalysisDetails, { + applicationId: application?.id, + taskId: task?.id, + }) + ); const reviewedArchetypes = application?.archetypes @@ -340,7 +349,7 @@ export const ApplicationDetailDrawer: React.FC< } type="button" variant="link" - onClick={() => setTaskIdToView(task.id)} + onClick={navigateToAnalysisDetails} className={spacing.ml_0} style={{ margin: "0", padding: "0" }} > @@ -401,7 +410,7 @@ export const ApplicationDetailDrawer: React.FC< } type="button" variant="link" - onClick={() => setTaskIdToView(task.id)} + onClick={navigateToAnalysisDetails} className={spacing.ml_0} style={{ margin: "0", padding: "0" }} > @@ -429,7 +438,7 @@ export const ApplicationDetailDrawer: React.FC< } type="button" variant="link" - onClick={() => setTaskIdToView(task?.id)} + onClick={navigateToAnalysisDetails} className={spacing.ml_0} style={{ margin: "0", padding: "0" }} > @@ -440,13 +449,6 @@ export const ApplicationDetailDrawer: React.FC< )} )} - { - setTaskIdToView(undefined); - }} - /> {!isFetching && !!facts.length && (