From e893ec86262bdbda0b05a5bef834772ba4b8fa24 Mon Sep 17 00:00:00 2001 From: Dallas Date: Mon, 25 Sep 2023 14:29:12 -0500 Subject: [PATCH 1/2] :seedling: replace deprecated wizard analysis wizard (#1394) effort towards #1244 (one of a few wizards) --------- Signed-off-by: gitdallas --- .../analysis-wizard/analysis-wizard.tsx | 189 ++++++++++-------- 1 file changed, 107 insertions(+), 82 deletions(-) diff --git a/client/src/app/pages/applications/analysis-wizard/analysis-wizard.tsx b/client/src/app/pages/applications/analysis-wizard/analysis-wizard.tsx index 8f3294da6f..dd85595c0a 100644 --- a/client/src/app/pages/applications/analysis-wizard/analysis-wizard.tsx +++ b/client/src/app/pages/applications/analysis-wizard/analysis-wizard.tsx @@ -1,11 +1,15 @@ import * as React from "react"; import { useIsMutating } from "@tanstack/react-query"; import { FormProvider, useForm } from "react-hook-form"; -import { Truncate } from "@patternfly/react-core"; import { + Modal, + ModalVariant, Wizard, - WizardStepFunctionType, -} from "@patternfly/react-core/deprecated"; + WizardStep, + WizardStepType, + WizardHeader, + Truncate, +} from "@patternfly/react-core"; import { useTranslation } from "react-i18next"; import { @@ -297,10 +301,8 @@ export const AnalysisWizard: React.FC = ({ onClose(); }; - const onMove: WizardStepFunctionType = ( - { id, name }, - { prevId, prevName } - ) => { + const onMove = (current: WizardStepType) => { + const id = current.id; if (id && stepIdReached < (id as number)) setStepIdReached(id as number); if (id === StepId.SetTargets) { if (!taskGroup) { @@ -311,93 +313,116 @@ export const AnalysisWizard: React.FC = ({ const analyzableApplications = useAnalyzableApplications(applications, mode); - const getStepNavProps = (stepId: StepId, forceBlock = false) => ({ - enableNext: - !forceBlock && - stepIdReached >= stepId && - (firstInvalidStep === null || firstInvalidStep >= stepId + 1), - canJumpTo: - !forceBlock && - stepIdReached >= stepId && - (firstInvalidStep === null || firstInvalidStep >= stepId), - }); + const isStepEnabled = (stepId: StepId) => { + return ( + stepIdReached + 1 >= stepId && + (firstInvalidStep === null || firstInvalidStep >= stepId) + ); + }; const steps = [ - { - name: t("wizard.terms.configureAnalysis"), - steps: [ - { - id: StepId.AnalysisMode, - name: t("wizard.terms.analysisMode"), - component: ( + + <> - ), - ...getStepNavProps(StepId.AnalysisMode, !!isMutating), - }, - { - id: StepId.SetTargets, - name: t("wizard.terms.setTargets"), - component: , - ...getStepNavProps(StepId.SetTargets), - }, - { - id: StepId.Scope, - name: t("wizard.terms.scope"), - component: , - ...getStepNavProps(StepId.Scope), - }, - ], - }, - { - name: t("wizard.terms.advanced"), - steps: [ - { - id: StepId.CustomRules, - name: t("wizard.terms.customRules"), - component: , - ...getStepNavProps(StepId.CustomRules), - }, - { - id: StepId.Options, - name: t("wizard.terms.options"), - component: , - ...getStepNavProps(StepId.Options), - }, - ], - }, - { - id: StepId.Review, - name: t("wizard.terms.review"), - component: , - nextButtonText: "Run", - ...getStepNavProps(StepId.Review), - }, + + , + + + , + + + , + ]} + >, + + + , + + + , + ]} + >, + + + , ]; + return ( <> {isOpen && ( - app.name).join(", ")} - /> - } - navAriaLabel={`${title} steps`} - mainAriaLabel={`${title} content`} - steps={steps} - onNext={onMove} - onBack={onMove} - onSave={handleSubmit(onSubmit)} - onClose={() => { - handleCancel(); - }} - /> + showClose={false} + aria-label="Application analysis wizard modal" + hasNoBodyWrapper + onEscapePress={handleCancel} + variant={ModalVariant.large} + > + + onMove(currentStep) + } + header={ + app.name).join(", ")} + /> + } + /> + } + > + {steps} + + )} From fbd5e3d7fd667ac485d7a1e7d6d4398358cacc6c Mon Sep 17 00:00:00 2001 From: Ian Bolton Date: Mon, 25 Sep 2023 17:11:00 -0400 Subject: [PATCH 2/2] :bug: Assessment settings table columns empty (#1391) Closes https://issues.redhat.com/browse/MTA-1317 Screenshot 2023-09-22 at 1 25 39 PM This pull request addresses the issue of empty columns in the Assessment settings table by making the following changes: Updated Models: Modified the Questionnaire model in client/src/app/api/models.ts to include the createTime field, which is now used for the "Date imported" column in the Assessment settings table. Date Formatting: Utilized the dayjs library to format the date from the createTime field into a human-readable format ("YYYY-MM-DD HH:mm:ss") for display in the table. Thresholds Display: Modified the rendering of thresholds in the "Rating" column to display each color and its percentage. These changes should resolve the issue of empty columns and ensure that the table displays the relevant data correctly. Signed-off-by: ibolton336 --- client/src/app/api/models.ts | 10 +++--- .../assessment-settings-page.tsx | 32 +++++++++++++------ .../export-questionnaire-dropdown-item.tsx} | 0 .../questionnaire-questions-column.tsx | 15 +++++++++ .../questionnaire-thresholds-column.tsx | 27 ++++++++++++++++ .../components/migration-wave-form.tsx | 4 --- .../pages/migration-waves/migration-waves.tsx | 5 --- client/src/index.tsx | 8 +++++ .../mocks/stub-new-work/questionnaireData.ts | 6 ++-- 9 files changed, 82 insertions(+), 25 deletions(-) rename client/src/app/pages/assessment-management/assessment-settings/{ExportQuestionnaireDropdownItem.tsx => components/export-questionnaire-dropdown-item.tsx} (100%) create mode 100644 client/src/app/pages/assessment-management/assessment-settings/components/questionnaire-questions-column.tsx create mode 100644 client/src/app/pages/assessment-management/assessment-settings/components/questionnaire-thresholds-column.tsx diff --git a/client/src/app/api/models.ts b/client/src/app/api/models.ts index 968e9b678a..1fd51790c0 100644 --- a/client/src/app/api/models.ts +++ b/client/src/app/api/models.ts @@ -646,7 +646,7 @@ export interface Questionnaire { revision: number; questions: number; rating: string; - dateImported: string; + createTime: string; required: boolean; system: boolean; sections: Section[]; @@ -687,10 +687,12 @@ export interface Answer { selected?: boolean; } export interface Thresholds { - red: number; - unknown: number; - yellow: number; + red?: number; + unknown?: number; + yellow?: number; + green?: number; } + export type AssessmentStatus = "empty" | "started" | "complete"; export type Risk = "green" | "yellow" | "red" | "unknown"; diff --git a/client/src/app/pages/assessment-management/assessment-settings/assessment-settings-page.tsx b/client/src/app/pages/assessment-management/assessment-settings/assessment-settings-page.tsx index dbc8a1fa36..3462a66b72 100644 --- a/client/src/app/pages/assessment-management/assessment-settings/assessment-settings-page.tsx +++ b/client/src/app/pages/assessment-management/assessment-settings/assessment-settings-page.tsx @@ -8,6 +8,7 @@ import { EmptyState, EmptyStateBody, EmptyStateIcon, + List, MenuToggle, MenuToggleElement, Modal, @@ -51,7 +52,10 @@ import { useHistory } from "react-router-dom"; import { Paths } from "@app/Paths"; import { ImportQuestionnaireForm } from "@app/pages/assessment-management/import-questionnaire-form/import-questionnaire-form"; import ConfirmDeleteDialog from "@app/components/ConfirmDeleteDialog/ConfirmDeleteDialog"; -import { ExportQuestionnaireDropdownItem } from "./ExportQuestionnaireDropdownItem"; +import { ExportQuestionnaireDropdownItem } from "./components/export-questionnaire-dropdown-item"; +import dayjs from "dayjs"; +import { QuestionnaireQuestionsColumn } from "./components/questionnaire-questions-column"; +import { QuestionnaireThresholdsColumn } from "./components/questionnaire-thresholds-column"; const AssessmentSettings: React.FC = () => { const { t } = useTranslation(); @@ -108,7 +112,7 @@ const AssessmentSettings: React.FC = () => { name: "Name", questions: "Questions", rating: "Rating", - dateImported: "Date imported", + createTime: "Date imported", }, isSelectable: false, expandableVariant: null, @@ -127,10 +131,10 @@ const AssessmentSettings: React.FC = () => { }, }, ], - sortableColumns: ["name", "dateImported"], + sortableColumns: ["name", "createTime"], getSortValues: (questionnaire) => ({ name: questionnaire.name || "", - dateImported: questionnaire.dateImported || "", + createTime: questionnaire.createTime || "", }), initialSort: { columnKey: "name", direction: "asc" }, hasPagination: true, @@ -225,7 +229,7 @@ const AssessmentSettings: React.FC = () => { - + @@ -247,6 +251,10 @@ const AssessmentSettings: React.FC = () => { numRenderedColumns={numRenderedColumns} > {currentPageItems?.map((questionnaire, rowIndex) => { + const formattedDate = dayjs(questionnaire.createTime) + .utc() + .format("YYYY-MM-DD HH:mm:ss"); + return ( @@ -283,19 +291,25 @@ const AssessmentSettings: React.FC = () => { width={10} {...getTdProps({ columnKey: "questions" })} > - {questionnaire.questions} + - {questionnaire.rating} + + + - {questionnaire.dateImported} + {formattedDate} = ({ questionnaire }) => { + const totalQuestions = (questionnaire.sections || []).reduce( + (total, section) => { + return total + (section.questions ? section.questions.length : 0); + }, + 0 + ); + return {totalQuestions}; +}; diff --git a/client/src/app/pages/assessment-management/assessment-settings/components/questionnaire-thresholds-column.tsx b/client/src/app/pages/assessment-management/assessment-settings/components/questionnaire-thresholds-column.tsx new file mode 100644 index 0000000000..b639a948ad --- /dev/null +++ b/client/src/app/pages/assessment-management/assessment-settings/components/questionnaire-thresholds-column.tsx @@ -0,0 +1,27 @@ +import React from "react"; +import { ListItem } from "@patternfly/react-core"; +import { Questionnaire, Thresholds } from "@app/api/models"; + +export const QuestionnaireThresholdsColumn: React.FC<{ + questionnaire: Questionnaire; +}> = ({ questionnaire }) => { + const thresholdsToListItems = (thresholds: Thresholds) => { + const thresholdKeys: (keyof Thresholds)[] = Object.keys( + thresholds + ) as (keyof Thresholds)[]; + + return ( + <> + {thresholdKeys.map((color) => { + const percentage: number = thresholds[color] || 0; + return ( + + {color} {percentage}% + + ); + })} + + ); + }; + return thresholdsToListItems(questionnaire.thresholds || {}); +}; diff --git a/client/src/app/pages/migration-waves/components/migration-wave-form.tsx b/client/src/app/pages/migration-waves/components/migration-wave-form.tsx index 51aec5403a..d9d8a50293 100644 --- a/client/src/app/pages/migration-waves/components/migration-wave-form.tsx +++ b/client/src/app/pages/migration-waves/components/migration-wave-form.tsx @@ -22,8 +22,6 @@ import { useUpdateMigrationWaveMutation, } from "@app/queries/migration-waves"; import dayjs from "dayjs"; -import utc from "dayjs/plugin/utc"; -import customParseFormat from "dayjs/plugin/customParseFormat"; import { Stakeholder, StakeholderGroup, @@ -37,8 +35,6 @@ import { import { OptionWithValue, SimpleSelect } from "@app/components/SimpleSelect"; import { NotificationsContext } from "@app/components/NotificationsContext"; import { DEFAULT_SELECT_MAX_HEIGHT } from "@app/Constants"; -dayjs.extend(utc); -dayjs.extend(customParseFormat); const stakeholderGroupToOption = ( value: StakeholderGroup diff --git a/client/src/app/pages/migration-waves/migration-waves.tsx b/client/src/app/pages/migration-waves/migration-waves.tsx index d2ebd9e260..e71c81e4e7 100644 --- a/client/src/app/pages/migration-waves/migration-waves.tsx +++ b/client/src/app/pages/migration-waves/migration-waves.tsx @@ -33,8 +33,6 @@ import { import { useTranslation } from "react-i18next"; import { AxiosError, AxiosResponse } from "axios"; import dayjs from "dayjs"; -import utc from "dayjs/plugin/utc"; -import timezone from "dayjs/plugin/timezone"; import CubesIcon from "@patternfly/react-icons/dist/esm/icons/cubes-icon"; import EllipsisVIcon from "@patternfly/react-icons/dist/esm/icons/ellipsis-v-icon"; @@ -70,9 +68,6 @@ import { AppPlaceholder } from "@app/components/AppPlaceholder"; import { ToolbarBulkSelector } from "@app/components/ToolbarBulkSelector"; import { ConfirmDialog } from "@app/components/ConfirmDialog"; -dayjs.extend(utc); -dayjs.extend(timezone); - export const MigrationWaves: React.FC = () => { const { t } = useTranslation(); const { pushNotification } = React.useContext(NotificationsContext); diff --git a/client/src/index.tsx b/client/src/index.tsx index 0cc1915b8b..a645470ac5 100644 --- a/client/src/index.tsx +++ b/client/src/index.tsx @@ -6,6 +6,14 @@ import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; import App from "@app/App"; import reportWebVitals from "@app/reportWebVitals"; import { KeycloakProvider } from "@app/components/KeycloakProvider"; +import dayjs from "dayjs"; +import utc from "dayjs/plugin/utc"; +import timezone from "dayjs/plugin/timezone"; +import customParseFormat from "dayjs/plugin/customParseFormat"; + +dayjs.extend(utc); +dayjs.extend(timezone); +dayjs.extend(customParseFormat); if (process.env.NODE_ENV === "development") { import("./mocks/browser").then((browserMocks) => { diff --git a/client/src/mocks/stub-new-work/questionnaireData.ts b/client/src/mocks/stub-new-work/questionnaireData.ts index 0986f99e46..36560b9147 100644 --- a/client/src/mocks/stub-new-work/questionnaireData.ts +++ b/client/src/mocks/stub-new-work/questionnaireData.ts @@ -10,7 +10,7 @@ const questionnaireData: Record = { revision: 1, questions: 42, rating: "5% Red, 25% Yellow", - dateImported: "8 Aug. 2023, 10:20 AM EST", + createTime: "8 Aug. 2023, 10:20 AM EST", required: false, system: true, sections: [ @@ -189,7 +189,7 @@ const questionnaireData: Record = { revision: 1, questions: 24, rating: "15% Red, 35% Yellow", - dateImported: "9 Aug. 2023, 03:32 PM EST", + createTime: "9 Aug. 2023, 03:32 PM EST", required: true, system: false, sections: [ @@ -369,7 +369,7 @@ const questionnaireData: Record = { revision: 1, questions: 34, rating: "7% Red, 25% Yellow", - dateImported: "10 Aug. 2023, 11:23 PM EST", + createTime: "10 Aug. 2023, 11:23 PM EST", required: true, system: false, sections: [