diff --git a/src/Routes/RouteConstant.tsx b/src/Routes/RouteConstant.tsx new file mode 100644 index 00000000..cd65fb01 --- /dev/null +++ b/src/Routes/RouteConstant.tsx @@ -0,0 +1,5 @@ +export const ROUTES = { + HOME: '/', + WIZARD: '/wizard', +}; + \ No newline at end of file diff --git a/src/renderer/components/Dialogs/WarningDialog.tsx b/src/renderer/components/Dialogs/WarningDialog.tsx new file mode 100644 index 00000000..49a6d7ea --- /dev/null +++ b/src/renderer/components/Dialogs/WarningDialog.tsx @@ -0,0 +1,43 @@ +/* + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright Contributors to the Zowe Project. + */ +import { Box, Button, Dialog, DialogActions, DialogContent, DialogTitle, FormControl, TextField } from '@mui/material'; +import { useState } from 'react'; + +const WarningDialog = ({onWarningDialogSubmit}: {onWarningDialogSubmit: any}) => { + + const [isDialogVisible, setIsDialogVisible] = useState(true); + + const handleClose = () => { + setIsDialogVisible(false); + onWarningDialogSubmit(false); + } + + const handleSubmit = () => { + setIsDialogVisible(false); + onWarningDialogSubmit(true); + } + + return ( +
+ + Warning! + Starting a new installation will erase the previously stored installation data from the wizard. Do you wish to proceed? + +
+ + +
+
+
+
+ ) +} + +export default WarningDialog; \ No newline at end of file diff --git a/src/renderer/components/Home.tsx b/src/renderer/components/Home.tsx index 1499e9ca..0fe4644f 100644 --- a/src/renderer/components/Home.tsx +++ b/src/renderer/components/Home.tsx @@ -10,7 +10,7 @@ import '../global.css'; import { useEffect, useState } from "react"; -import { Link } from 'react-router-dom'; +import { Link, useNavigate } from 'react-router-dom'; import { Box, Card, CardContent, CardMedia, Typography, Button } from '@mui/material'; import flatten, { unflatten } from 'flat'; import { IResponse, IIpcConnectionArgs } from '../../types/interfaces'; @@ -22,25 +22,22 @@ import { Tooltip } from '@mui/material'; import installationImg from '../assets/installation.png' import installationDryImg from '../assets/installation-dry-run.png' import eventDispatcher from "../../services/eventDispatcher"; -import { selectConnectionStatus} from './stages/progress/progressSlice'; +import { selectConnectionStatus, setConnectionStatus} from './stages/progress/progressSlice'; import HorizontalLinearStepper from './common/Stepper'; import Wizard from './configuration-wizard/Wizard' -import { ActiveState } from '../../types/stateInterfaces'; +import { ActiveState, InstallationArgs } from '../../types/stateInterfaces'; import { getInstallationArguments, getPreviousInstallation } from './stages/progress/StageProgressStatus'; import { DEF_NO_OUTPUT, FALLBACK_SCHEMA, FALLBACK_YAML } from './common/Utils'; -import { selectInstallationArgs, setInstallationArgs, installationSlice } from './stages/installation/installationSlice'; +import { selectInstallationArgs, setInstallationArgs, installationSlice, setIsNewInstallation, selectIsNewInstallation } from './stages/installation/installationSlice'; import PasswordDialog from './common/passwordDialog'; +import WarningDialog from './Dialogs/WarningDialog'; +import HomeCardComponent from './HomeCardComponent'; +import { ICard } from '../../types/interfaces'; +import { ROUTES } from '../../Routes/RouteConstant'; // REVIEW: Get rid of routing -interface ICard { - id: string, - name: string, - description: string, - link: string, - media: any, -} - +// Cards Data const cards: Array = [ { id: "install", @@ -58,79 +55,39 @@ const cards: Array = [ } ] +// Constants const prevInstallationKey = "prev_installation"; const lastActiveState: ActiveState = { activeStepIndex: 0, isSubStep: false, activeSubStepIndex: 0, }; +const defaultTooltip: string = "Resume"; + +// Helper Functions +const getNewInstallationArgs = (id: string, currentArgs: InstallationArgs) => { + return id === "install" + ? { ...currentArgs, dryRunMode: false } + : { ...currentArgs, dryRunMode: true }; +}; const Home = () => { const dispatch = useAppDispatch(); + const navigate = useNavigate(); const connectionStatus = useAppSelector(selectConnectionStatus); - const [showWizard, setShowWizard] = useState(false); - const [showLoginDialog, setShowLogin] = useState(false); - const [localYaml, setLocalYaml] = useState(useAppSelector(selectYaml)); - const [schema, setLocalSchema] = useState(useAppSelector(selectSchema)); - const installationArgs = useAppSelector(selectInstallationArgs); - - const { activeStepIndex, isSubStep, activeSubStepIndex, lastActiveDate } = getPreviousInstallation(); - - const [isNewInstallation, setIsNewInstallation] = useState(false); - + const schema = useAppSelector(selectSchema); const stages: any = []; - const defaultTooltip: string = "Resume"; const resumeTooltip = connectionStatus ? defaultTooltip : `Validate Credentials & ${defaultTooltip}`; + const isNewInstallation = useAppSelector(selectIsNewInstallation); + const { lastActiveDate } = getPreviousInstallation(); + + const [showWizard, setShowWizard] = useState(false); + const [localYaml, setLocalYaml] = useState(useAppSelector(selectYaml)); const [showPasswordDialog, setShowPasswordDialog] = useState(false); - const [updatedConnection, setUpdatedConnection] = useState(false); - const [isResume, setIsResume] = useState(useAppSelector(selectResumeProgress)); - - const makeCard = (card: ICard) => { - const {id, name, description, link, media} = card; - - const handleClick = () => { - let newInstallationArgs = installationSlice.getInitialState().installationArgs; - if (id === "install") { - newInstallationArgs = {...newInstallationArgs, dryRunMode: false}; - } else if (id === "dry run") { - newInstallationArgs = {...newInstallationArgs, dryRunMode: true}; - } - dispatch(setYaml(FALLBACK_YAML)); - dispatch(setInstallationArgs(newInstallationArgs)); - window.electron.ipcRenderer.setConfigByKeyNoValidate("installationArgs", newInstallationArgs); - setLocalYaml(FALLBACK_YAML); - window.electron.ipcRenderer.setConfig(FALLBACK_YAML); - // TODO: Ideally, reset connectionArgs too - // but this introduces bug with "self certificate chain" it's the checkbox, it looks checked but - // it acts like it's not unless you touch it - // dispatch(setConnectionArgs(connectionSlice.getInitialState().connectionArgs)); - setIsResume(false); - }; - - return ( - - - - - - - - {name} - - - {description} - - - - - - - ) - } + const [newInstallationClicked, setNewInstallationClick] = useState(false); + const [previousInstallation, setPreviousInstallation] = useState(false); + const [defaultYaml, setDefaultYaml] = useState(FALLBACK_YAML); useEffect(() => { eventDispatcher.on('saveAndCloseEvent', () => setShowWizard(false)); @@ -142,8 +99,9 @@ const Home = () => { window.electron.ipcRenderer.getConfig().then((res: IResponse) => { if (res.status) { dispatch(setYaml(res.details)); + setDefaultYaml(res.details); } else { - dispatch(setYaml(FALLBACK_YAML)); + dispatch(setYaml(defaultYaml)); } }) } @@ -176,14 +134,10 @@ const Home = () => { } }); - window.electron.ipcRenderer.setStandardOutput(DEF_NO_OUTPUT).then((res: any) => { - }) - window.electron.ipcRenderer.findPreviousInstallations().then((res: IResponse) => { const connectionStore = res.details; if (connectionStore["connection-type"] === 'ftp') { const jobStatement = connectionStore['ftp-details'].jobStatement.trim() || useAppSelector(selectInitJobStatement); - // console.log(JSON.stringify(connectionStore['ftp-details'],null,2)); const connectionArgs: IIpcConnectionArgs = { ...connectionStore["ftp-details"], password: "", @@ -200,13 +154,14 @@ const Home = () => { if (!lastInstallation) { const flattenedData = flatten(lastActiveState); localStorage.setItem(prevInstallationKey, JSON.stringify(flattenedData)); - setIsNewInstallation(true); + dispatch(setIsNewInstallation(true)); + setPreviousInstallation(false); } else { const data: ActiveState = unflatten(JSON.parse(lastInstallation)); - setIsNewInstallation(!(data && data.lastActiveDate)); + dispatch(setIsNewInstallation(!(data && data.lastActiveDate))); + setPreviousInstallation(!!(data && data.lastActiveDate)); } - }); return () => { eventDispatcher.off('saveAndCloseEvent', () => setShowWizard(true)); @@ -216,33 +171,93 @@ const Home = () => { const resumeProgress = () => { setShowWizard(true); dispatch(setResumeProgress(true)); - + dispatch(setIsNewInstallation(false)); + if(connectionStatus) { setShowPasswordDialog(true); - setUpdatedConnection(false); } } const confirmConnection = (status: boolean) => { - setUpdatedConnection(status); + setShowPasswordDialog(!status); setShowWizard(status); } + const handleNewInstallation = (newInstallationArgs: InstallationArgs) => { + dispatch(setYaml(defaultYaml)); + dispatch(setInstallationArgs(newInstallationArgs)); + + window.electron.ipcRenderer.setConfigByKeyNoValidate("installationArgs", newInstallationArgs); + window.electron.ipcRenderer.setConfig(defaultYaml); + + setLocalYaml(defaultYaml); + + // TODO: Ideally, reset connectionArgs too + // but this introduces bug with "self certificate chain" it's the checkbox, it looks checked but + // it acts like it's not unless you touch it + // dispatch(setConnectionArgs(connectionSlice.getInitialState().connectionArgs)); + } + + const handleCardClick = (id: string) => { + + const initialInstallationArgs = installationSlice.getInitialState().installationArgs; + const newInstallationArgs = getNewInstallationArgs(id, initialInstallationArgs); + + if (id === "install") { + setNewInstallationClick(true); + if(previousInstallation) { + return; + } + dispatch(setIsNewInstallation(true)); + dispatch(setConnectionStatus(false)); + dispatch(setResumeProgress(false)); + } + handleNewInstallation(newInstallationArgs); + }; + + const confirmNewInstallation = (status: boolean) => { + dispatch(setIsNewInstallation(status)) + setNewInstallationClick(false); + setPreviousInstallation(!status); + + if(status) { + dispatch(setConnectionStatus(false)); + dispatch(setResumeProgress(false)); + handleNewInstallation({ ...installationSlice.getInitialState().installationArgs, dryRunMode: false }); + navigate(ROUTES.WIZARD); + } + } + return ( <> + { previousInstallation && newInstallationClicked && + + } + {!showWizard &&
- + {stages.length > 0 && }
{!connectionStatus &&
}
- {cards.map(card => makeCard(card))} + {cards.map(card => ( + + ))}
- {!isNewInstallation &&
+ {previousInstallation &&
@@ -268,8 +283,7 @@ const Home = () => { {showWizard && <> {showPasswordDialog && } - {(showPasswordDialog && updatedConnection) && } - {!showPasswordDialog && } + {!showPasswordDialog && } } diff --git a/src/renderer/components/HomeCardComponent.tsx b/src/renderer/components/HomeCardComponent.tsx new file mode 100644 index 00000000..22355aff --- /dev/null +++ b/src/renderer/components/HomeCardComponent.tsx @@ -0,0 +1,27 @@ +import { Box, Card, CardContent, CardMedia, Typography, Tooltip, Button } from '@mui/material'; +import { Link } from 'react-router-dom'; + +const HomeCardComponent = ({ id, name, description, link, media, previousInstallation, handleCardClick }:{id: string, name: string, description: string, link: string, media: any, previousInstallation: boolean, handleCardClick: any}) => { + + return ( + + handleCardClick(id)}> + + + + + + {name} + + + {description} + + + + + + + ); +}; + +export default HomeCardComponent; diff --git a/src/renderer/components/common/Stepper.tsx b/src/renderer/components/common/Stepper.tsx index 0b345870..e938f03b 100644 --- a/src/renderer/components/common/Stepper.tsx +++ b/src/renderer/components/common/Stepper.tsx @@ -9,7 +9,6 @@ */ import React, { useState, useEffect } from 'react'; -import { useSelector } from 'react-redux'; import Box from '@mui/material/Box'; import Stepper from '@mui/material/Stepper'; import Step from '@mui/material/Step'; @@ -29,27 +28,25 @@ import Warning from '@mui/icons-material/Warning'; import CheckCircle from '@mui/icons-material/CheckCircle'; import { TYPE_YAML, TYPE_OUTPUT, TYPE_JCL, INIT_STAGE_LABEL, REVIEW_INSTALL_STAGE_LABEL, UNPAX_STAGE_LABEL } from '../common/Utils'; import { getProgress, getCompleteProgress, mapAndSetSkipStatus, mapAndGetSkipStatus } from '../stages/progress/StageProgressStatus'; - import '../../styles/Stepper.css'; import { StepIcon } from '@mui/material'; import { getStageDetails } from '../../../services/StageDetails'; import { IResponse } from '../../../types/interfaces'; import { selectConnectionArgs, setPassword } from '../stages/connection/connectionSlice'; -import { selectInstallationArgs } from '../stages/installation/installationSlice'; +import { selectInstallationArgs, selectIsNewInstallation } from '../stages/installation/installationSlice'; // TODO: define props, stages, stage interfaces // TODO: One rule in the store to enable/disable button export default function HorizontalLinearStepper({stages, initialization}:{stages: any, initialization?:boolean}) { - const connectionStatus = useSelector(selectConnectionStatus); + const connectionStatus = useAppSelector(selectConnectionStatus); - const INIT_STAGE_ID = getStageDetails(INIT_STAGE_LABEL).id; - const REVIEW_STAGE_ID = getStageDetails(REVIEW_INSTALL_STAGE_LABEL).id; + const isNewInstallation = useAppSelector(selectIsNewInstallation); const completeProgress = getCompleteProgress(); - + const stageProgressStatus = [ - useSelector(selectConnectionStatus), + useAppSelector(selectConnectionStatus), completeProgress.planningStatus, completeProgress.installationTypeStatus, completeProgress.downloadUnpaxStatus, @@ -66,15 +63,11 @@ export default function HorizontalLinearStepper({stages, initialization}:{stages completeProgress.certificateStatus, completeProgress.launchConfigStatus ]) - - - const [activeStep, setActiveStep] = initialization ? useState(0) : useState(useAppSelector(selectActiveStepIndex)); - const [activeSubStep, setActiveSubStep] = initialization ? useState(0) : useState(useAppSelector(selectActiveSubStepIndex)); - const [nextText, setNextText] = useState("Continue"); + const [activeStep, setActiveStep] = isNewInstallation ? useState(0) : useState(useAppSelector(selectActiveStepIndex)); + const [activeSubStep, setActiveSubStep] = isNewInstallation ? useState(0) : useState(useAppSelector(selectActiveSubStepIndex)); const [contentType, setContentType] = useState('output'); const [editorVisible, setEditorVisible] = useState(false); - const [editorContent, setEditorContent] = useState(''); const installationArgs = useAppSelector(selectInstallationArgs); const connectionArgs = useAppSelector(selectConnectionArgs); const dispatch = useAppDispatch(); @@ -108,14 +101,6 @@ export default function HorizontalLinearStepper({stages, initialization}:{stages setEditorVisible(!editorVisible); }; - const getContinueText = () => { - return 'Continue to next step';//'+stages[activeStep+1].label; - }; - - const getSkipText = () => { - return 'Skip step';//+stages[activeStep+1].label; - }; - const handleYAML = () => { toggleEditorVisibility(TYPE_YAML); } @@ -168,14 +153,12 @@ export default function HorizontalLinearStepper({stages, initialization}:{stages return; } setActiveStep((prevActiveStep) => prevActiveStep + 1); - setNextText(getContinueText()); } }; const handleBack = () => { alertEmitter.emit('hideAlert'); stages[activeStep].subStages && activeSubStep > 0 ? setActiveSubStep((prevActiveSubStep) => prevActiveSubStep - 1) : setActiveStep((prevActiveStep) => prevActiveStep - 1); - setNextText(getContinueText()); }; const handleReset = () => { @@ -183,11 +166,6 @@ export default function HorizontalLinearStepper({stages, initialization}:{stages setActiveStep(0); }; - const handlePreview = (test_jcl: any) => { - toggleEditorVisibility(TYPE_JCL); - setEditorContent(test_jcl); - }; - const handleStepperClick = (newActiveStep: number, isSubStep: boolean, subStepIndex?: number) => { if(!connectionStatus) { return; @@ -239,9 +217,6 @@ export default function HorizontalLinearStepper({stages, initialization}:{stages alertEmitter.emit('hideAlert'); eventDispatcher.emit('saveAndCloseEvent'); dispatch(setPassword('')); - // TODO: This is a workaround for same session + Save & Close + new install not resetting the Wizard properly. - // Fixed by reloading page. This is not ideal and should be investigated - window.location.reload(); } const isNextStepEnabled = useAppSelector(selectNextStepEnabled); diff --git a/src/renderer/components/stages/Planning.tsx b/src/renderer/components/stages/Planning.tsx index 6280545f..e9fceadf 100644 --- a/src/renderer/components/stages/Planning.tsx +++ b/src/renderer/components/stages/Planning.tsx @@ -21,7 +21,7 @@ import { setYaml, setNextStepEnabled, setLoading, selectYaml } from '../configur import { selectConnectionArgs, setConnectionArgs, setJobStatementVal } from './connection/connectionSlice'; import { setPlanningStatus, selectPlanningStatus } from './progress/progressSlice'; import { setZoweVersion, setInstallationArgs, selectInstallationArgs, selectZoweVersion } from './installation/installationSlice'; -import { setJobStatement, setJobStatementValid, setJobStatementValidMsg, setLocationValidationDetails, setIsLocationValid, selectJobStatementValidMsg, selectLocValidationDetails } from "./PlanningSlice"; +import { setJobStatement, setJobStatementValid, setJobStatementValidMsg, setLocationValidationDetails, setIsLocationValid, selectJobStatementValidMsg, selectLocValidationDetails, selectJobStatement } from "./PlanningSlice"; import { useAppDispatch, useAppSelector } from '../../hooks'; import { IResponse } from '../../../types/interfaces'; import { alertEmitter } from "../Header"; @@ -63,8 +63,7 @@ const Planning = () => { const [jobHeaderSaved, setJobHeaderSaved] = useState(false); const [isJobStatementUpdated, setIsJobStatementUpdated] = useState(false); - // const [jobStatementValue, setJobStatementValue] = useState(useAppSelector(selectJobStatement)); - const [jobStatementValue, setJobStatementValue] = useState(getPlanningStageStatus()?.jobStatement); + const [jobStatementValue, setJobStatementValue] = useState(useAppSelector(selectJobStatement)); const [locationsValidated, setLocationsValidated] = useState(getPlanningStageStatus()?.isLocationValid || false); const [isLocationsUpdated, setIsLocationsUpdated] = useState(false); diff --git a/src/renderer/components/stages/PlanningSlice.tsx b/src/renderer/components/stages/PlanningSlice.tsx index 82357bd5..765b5ebb 100644 --- a/src/renderer/components/stages/PlanningSlice.tsx +++ b/src/renderer/components/stages/PlanningSlice.tsx @@ -11,6 +11,7 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit'; import { RootState } from '../../store'; import { setPlanningStageStatus, getPlanningStageStatus } from './progress/StageProgressStatus'; +import { DEF_JOB_STATEMENT } from '../common/Utils'; export interface jobValidation { jobStatement: string; @@ -24,7 +25,7 @@ export interface locationValidation { } const initialState: jobValidation = { - jobStatement: getPlanningStageStatus()?.jobStatement || '', + jobStatement: getPlanningStageStatus()?.jobStatement || DEF_JOB_STATEMENT, isJobStatementValid: getPlanningStageStatus()?.isJobStatementValid || false, jobStatementValidMsg: '' } diff --git a/src/renderer/components/stages/ReviewInstallation.tsx b/src/renderer/components/stages/ReviewInstallation.tsx index 340ec70a..e2611626 100644 --- a/src/renderer/components/stages/ReviewInstallation.tsx +++ b/src/renderer/components/stages/ReviewInstallation.tsx @@ -9,7 +9,6 @@ */ import React, {useEffect, useState} from "react"; -import { useSelector } from 'react-redux'; import {Box, Button, Typography, Tooltip} from '@mui/material'; import CheckCircleIcon from '@mui/icons-material/CheckCircle'; import WarningIcon from '@mui/icons-material/Warning'; @@ -48,7 +47,7 @@ const ReviewInstallation = () => { const completeProgress = getCompleteProgress(); const stageProgressStatus = [ - useSelector(selectConnectionStatus), + useAppSelector(selectConnectionStatus), completeProgress.planningStatus, completeProgress.installationTypeStatus, completeProgress.initializationStatus, diff --git a/src/renderer/components/stages/connection/Connection.tsx b/src/renderer/components/stages/connection/Connection.tsx index f1d7dbd6..b4d875e5 100644 --- a/src/renderer/components/stages/connection/Connection.tsx +++ b/src/renderer/components/stages/connection/Connection.tsx @@ -33,10 +33,10 @@ import { setConnectionStatus, selectConnectionStatus} from '../progress/progres import { Container } from "@mui/material"; import { alertEmitter } from "../../Header"; import { getStageDetails, initStageSkipStatus } from "../../../../services/StageDetails"; -import { initializeProgress, getActiveStage, } from "../progress/StageProgressStatus"; +import { initializeProgress, getActiveStage, resetProgress } from "../progress/StageProgressStatus"; import eventDispatcher from "../../../../services/eventDispatcher"; import { setLocationValidationDetails } from "../PlanningSlice"; -import { selectInstallationArgs } from "../installation/installationSlice"; +import { selectInstallationArgs, selectIsNewInstallation } from "../installation/installationSlice"; const Connection = () => { @@ -125,6 +125,7 @@ const FTPConnectionForm = () => { const [isFtpConnection, setIsFtpConnection] = useState(useAppSelector(selectConnectionSecure)); const [isAllCertificatesAccepted, setIsAllCertificatesAccepted] = useState(useAppSelector(selectAcceptAllCertificates)); + const [isNewInstallation, setIsNewInstallation] = useState(useAppSelector(selectIsNewInstallation)); const [formProcessed, toggleFormProcessed] = React.useState(false); const [validationDetails, setValidationDetails] = React.useState(''); @@ -132,6 +133,12 @@ const FTPConnectionForm = () => { const installationArgs = useAppSelector(selectInstallationArgs); + useEffect(() => { + if(isNewInstallation) { + resetProgress(connectionArgs.host, connectionArgs.user); + } + }, []); + const handleFormChange = (ftpConnection?:boolean, acceptCerts?:boolean) => { dispatch(setConnectionStatus(false)); dispatch(setNextStepEnabled(false)); diff --git a/src/renderer/components/stages/installation/installationSlice.ts b/src/renderer/components/stages/installation/installationSlice.ts index 872c74b7..4bcd01e8 100644 --- a/src/renderer/components/stages/installation/installationSlice.ts +++ b/src/renderer/components/stages/installation/installationSlice.ts @@ -17,6 +17,7 @@ interface InstallationState { installationArgs: InstallationArgs; zoweVersion: string; licenseAgreement: boolean; + isNewInstallation?: boolean; } const initialState: InstallationState = { @@ -42,6 +43,7 @@ const initialState: InstallationState = { }, zoweVersion: '', licenseAgreement: getInstallationTypeStatus()?.licenseAgreement || false, + isNewInstallation: false, }; export const installationSlice = createSlice({ @@ -67,14 +69,18 @@ export const installationSlice = createSlice({ state.licenseAgreement = action.payload; setInstallationTypeStatus('licenseAgreement', action.payload) }, + setIsNewInstallation: (state, action: PayloadAction) => { + state.isNewInstallation = action.payload; + } } }); -export const { setInstallationArgs, setZoweVersion, setInstallationType, setLicenseAgreement, setUserUploadedPaxPath} = installationSlice.actions; +export const { setInstallationArgs, setZoweVersion, setInstallationType, setLicenseAgreement, setUserUploadedPaxPath, setIsNewInstallation} = installationSlice.actions; export const selectInstallationArgs = (state: RootState) => state.installation.installationArgs; export const selectZoweVersion = (state: RootState) => state.installation.zoweVersion; export const selectInstallationType = (state: RootState) => state.installation.installationArgs.installationType; export const selectLicenseAgreement = (state: RootState) => state.installation.licenseAgreement; +export const selectIsNewInstallation = (state: RootState) => state.installation.isNewInstallation; export default installationSlice.reducer; diff --git a/src/renderer/components/stages/progress/StageProgressStatus.ts b/src/renderer/components/stages/progress/StageProgressStatus.ts index 592460c4..bd8c4716 100644 --- a/src/renderer/components/stages/progress/StageProgressStatus.ts +++ b/src/renderer/components/stages/progress/StageProgressStatus.ts @@ -10,126 +10,23 @@ import { flatten, unflatten } from 'flat'; import { ProgressState, PlanningState, InstallationType, ActiveState, DatasetInstallationState, InitSubStepsState, CertInitSubStepsState, PlanningValidationDetails, SkipState, InstallationArgs, DownloadUnpaxState} from '../../../../types/stateInterfaces'; +import { initProgressStatus, initInstallationTypeStatus, initDownloadUnpaxStatus, initActiveStatus, initPlanningStageStatus, initDatasetInstallationStatus, initApfAuthStatus, initSecurityInitStatus, initStcsInitStatus, initCertificateInitStatus, initVsamInitStatus, initPlanningValidationDetailsStatus, initStepSkipStatus, initInstallationArgsStatus } from './progressConst'; import { stages } from '../../configuration-wizard/Wizard'; -const installationTypeStatus: InstallationType = { - installationType: 'download', - licenseAgreement: false, - userUploadedPaxPath: '', -} - -export const downloadUnpaxStatus: DownloadUnpaxState = { - uploadYaml: false, - download: false, - upload: false, - unpax: false, - getExampleYaml: false, - getSchemas: false, -} - -const progressStatus: ProgressState = { - connectionStatus: false, - planningStatus: false, - installationTypeStatus: false, - downloadUnpaxStatus: false, - initializationStatus: false, - datasetInstallationStatus: false, - networkingStatus: false, - apfAuthStatus: false, - securityStatus: false, - stcsStatus: false, - certificateStatus: false, - vsamStatus: false, - launchConfigStatus: false, - reviewStatus: false, -} - -const activeStatus: ActiveState = { - activeStepIndex: 0, - isSubStep: false, - activeSubStepIndex: 0, -}; - -const planningStageStatus: PlanningState = { - jobStatement: '', - isJobStatementValid: false, - isLocationValid: false, -} - -export const datasetInstallationStatus: DatasetInstallationState = { - uploadYaml: false, - install: false, - initMVS: false -} - -const apfAuthStatus: InitSubStepsState = { - writeYaml: false, - uploadYaml: false, - success: false -} - -const securityInitStatus: InitSubStepsState = { - writeYaml: false, - uploadYaml: false, - success: false -} - -const stcsInitStatus: InitSubStepsState = { - writeYaml: false, - uploadYaml: false, - success: false -} - -const certificateInitStatus: CertInitSubStepsState = { - writeYaml: false, - uploadYaml: false, - zweInitCertificate: false -} - -const vsamInitStatus: InitSubStepsState = { - writeYaml: false, - uploadYaml: false, - success: false -} - -const planningValidationDetailsStatus: PlanningValidationDetails = { - javaVersion: '', - nodeVersion: '', - spaceAvailableMb: '', - error: '' -} - -const stepSkipStatus: SkipState = { - downloadUnpax: false, - datasetInstallation: false, - networking: false, - apfAuth: false, - security: false, - certificate: false, - vsam: false, - launchConfig: false -} - -const installationArgsStatus: InstallationArgs = { - installationDir: '', - workspaceDir: '', - logDir:'', - extensionDir:'', - installationType: 'download', - userUploadedPaxPath: '', - downloadDir: '', - javaHome: '', - nodeHome: '', - setupConfig: {}, - jobName: 'ZWE1SV', - jobPrefix: 'ZWE1', - rbacProfile: '1', - cookieId: '1', - zosmfHost: '', - zosmfPort: '443', - zosmfApplId: 'IZUDFLT', - dryRunMode:false -} +let installationTypeStatus: InstallationType; +export let downloadUnpaxStatus: DownloadUnpaxState; +let progressStatus: ProgressState; +let activeStatus: ActiveState; +let planningStageStatus: PlanningState; +export let datasetInstallationStatus: DatasetInstallationState; +let apfAuthStatus: InitSubStepsState; +let securityInitStatus: InitSubStepsState; +let stcsInitStatus: InitSubStepsState; +let certificateInitStatus: CertInitSubStepsState; +let vsamInitStatus: InitSubStepsState; +let planningValidationDetailsStatus: PlanningValidationDetails; +let stepSkipStatus: SkipState; +let installationArgsStatus: InstallationArgs; let progressStateKey = 'stage_progress'; let activeStateKey = 'active_state'; @@ -147,9 +44,14 @@ let prevInstallationKey = `prev_installation`; let skipStateKey = `skip_state`; let installationArgsKey = `intallation_args`; -let skipKeysArray: (keyof SkipState)[] = Object.keys(stepSkipStatus) as (keyof SkipState)[]; +let skipKeysArray: (keyof SkipState)[]; const setKeys = (id: string) => { + + if (progressStateKey.endsWith(`_${id}`)) { + return; + } + progressStateKey = `${progressStateKey}_${id}`; activeStateKey = `${activeStateKey}_${id}`; planningStateKey = `${planningStateKey}_${id}`; @@ -166,10 +68,46 @@ const setKeys = (id: string) => { installationArgsKey = `${installationArgsKey}_${id}`; } +const setProgressObjects = () => { + installationTypeStatus = { ...initInstallationTypeStatus }; + downloadUnpaxStatus = { ...initDownloadUnpaxStatus }; + progressStatus = { ...initProgressStatus }; + activeStatus = { ...initActiveStatus }; + planningStageStatus = { ...initPlanningStageStatus }; + datasetInstallationStatus = { ...initDatasetInstallationStatus }; + apfAuthStatus = { ...initApfAuthStatus }; + securityInitStatus = { ...initSecurityInitStatus }; + stcsInitStatus = { ...initStcsInitStatus }; + certificateInitStatus = { ...initCertificateInitStatus }; + vsamInitStatus = { ...initVsamInitStatus }; + planningValidationDetailsStatus = { ...initPlanningValidationDetailsStatus }; + stepSkipStatus = { ...initStepSkipStatus }; + installationArgsStatus = { ...initInstallationArgsStatus }; + + skipKeysArray = Object.keys(stepSkipStatus) as (keyof SkipState)[]; + +} + +export const resetProgress = (host: string, user: string,) => { + if(host && user) { + const id = `${host}_${user}`; + setKeys(id); + } + + const keysArray = [progressStateKey, activeStateKey, planningStateKey, installationTypeKey, downloadUnpaxKey, datasetInstallationKey, apfAuthKey, securityKey, stcsKey, certificateKey, vsamKey, planningValidationDetailsKey, prevInstallationKey, skipStateKey, installationArgsKey]; + keysArray.forEach(key => { + if(localStorage.getItem(key) !== null) { + localStorage.removeItem(key); + } + }) +} + export const initializeProgress = (host: string, user: string, isResume: boolean) => { const id = `${host}_${user}`; setKeys(id); + setProgressObjects(); + const progress = localStorage.getItem(progressStateKey); if(!progress || !isResume) { const flattenedData = flatten(progressStatus); @@ -440,6 +378,9 @@ export const getDownloadUnpaxState = (): DownloadUnpaxState => { }; export const setPlanningStageStatus = (key: K, newValue: PlanningState[K]): void => { + if(!planningStageStatus) { + return; + } const planningData = localStorage.getItem(planningStateKey); if (planningData) { const flattenedData = JSON.parse(planningData); @@ -462,6 +403,9 @@ export const getPlanningStageStatus = (): PlanningState => { } export const setInstallationArguments = (newInstallationArgs: InstallationArgs): void => { + if(!installationArgsStatus) { + return; + } Object.assign(installationArgsStatus, newInstallationArgs); const flattenedData = flatten(installationArgsStatus); localStorage.setItem(installationArgsKey, JSON.stringify(flattenedData)); @@ -496,6 +440,9 @@ export const getProgress = (key: keyof ProgressState): boolean => { const unFlattenedData = unflatten(flattenedData) as ProgressState; return unFlattenedData[key]; } else { + if(!progressStatus) { + return false; + } return progressStatus[key]; } } @@ -506,6 +453,9 @@ export const getCompleteProgress = () : ProgressState => { const flattenedData = JSON.parse(progress); return unflatten(flattenedData); } else { + if(!progressStatus) { + return initProgressStatus; + } return progressStatus; } } @@ -552,7 +502,7 @@ export const getPreviousInstallation = () : ActiveState => { const flattenedData = JSON.parse(activeStage); return unflatten(flattenedData); } else { - return activeStatus; + return initActiveStatus; } } diff --git a/src/renderer/components/stages/progress/progressConst.ts b/src/renderer/components/stages/progress/progressConst.ts new file mode 100644 index 00000000..5c3de832 --- /dev/null +++ b/src/renderer/components/stages/progress/progressConst.ts @@ -0,0 +1,131 @@ +/* + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright Contributors to the Zowe Project. + */ + +import { ProgressState, PlanningState, InstallationType, ActiveState, DatasetInstallationState, InitSubStepsState, CertInitSubStepsState, PlanningValidationDetails, SkipState, InstallationArgs, DownloadUnpaxState } from "../../../../types/stateInterfaces"; + +export const initProgressStatus: ProgressState = { + connectionStatus: false, + planningStatus: false, + installationTypeStatus: false, + downloadUnpaxStatus: false, + initializationStatus: false, + datasetInstallationStatus: false, + networkingStatus: false, + apfAuthStatus: false, + securityStatus: false, + stcsStatus: false, + certificateStatus: false, + vsamStatus: false, + launchConfigStatus: false, + reviewStatus: false, +} + +export const initInstallationTypeStatus: InstallationType = { + installationType: 'download', + licenseAgreement: false, + userUploadedPaxPath: '', +} + +export const initDownloadUnpaxStatus: DownloadUnpaxState = { + uploadYaml: false, + download: false, + upload: false, + unpax: false, + getExampleYaml: false, + getSchemas: false, +} + +export const initActiveStatus: ActiveState = { + activeStepIndex: 0, + isSubStep: false, + activeSubStepIndex: 0, +} + +export const initPlanningStageStatus: PlanningState = { + jobStatement: '', + isJobStatementValid: false, + isLocationValid: false, +} + +export const initDatasetInstallationStatus: DatasetInstallationState = { + uploadYaml: false, + install: false, + initMVS: false +} + +export const initApfAuthStatus: InitSubStepsState = { + writeYaml: false, + uploadYaml: false, + success: false +} + +export const initSecurityInitStatus: InitSubStepsState = { + writeYaml: false, + uploadYaml: false, + success: false +} + +export const initStcsInitStatus: InitSubStepsState = { + writeYaml: false, + uploadYaml: false, + success: false +} + +export const initCertificateInitStatus: CertInitSubStepsState = { + writeYaml: false, + uploadYaml: false, + zweInitCertificate: false +} + +export const initVsamInitStatus: InitSubStepsState = { + writeYaml: false, + uploadYaml: false, + success: false +} + +export const initPlanningValidationDetailsStatus: PlanningValidationDetails = { + javaVersion: '', + nodeVersion: '', + spaceAvailableMb: '', + error: '' +} + +export const initStepSkipStatus: SkipState = { + downloadUnpax: false, + datasetInstallation: false, + networking: false, + apfAuth: false, + security: false, + certificate: false, + vsam: false, + launchConfig: false +} + +export const initInstallationArgsStatus: InstallationArgs = { + installationDir: '', + workspaceDir: '', + logDir:'', + extensionDir:'', + installationType: 'download', + userUploadedPaxPath: '', + downloadDir: '', + javaHome: '', + nodeHome: '', + setupConfig: {}, + jobName: 'ZWE1SV', + jobPrefix: 'ZWE1', + rbacProfile: '1', + cookieId: '1', + zosmfHost: '', + zosmfPort: '443', + zosmfApplId: 'IZUDFLT', + dryRunMode:false +} + \ No newline at end of file diff --git a/src/renderer/index.tsx b/src/renderer/index.tsx index b8584af3..8f680680 100644 --- a/src/renderer/index.tsx +++ b/src/renderer/index.tsx @@ -19,6 +19,7 @@ import Wizard from './components/configuration-wizard/Wizard'; import Header from './components/Header'; import theme from './theme'; import { ThemeProvider } from '@mui/material/styles'; +import { ROUTES } from '../Routes/RouteConstant'; // TODO: Support of Zowe Configuration and Saved Installation actions. // - Add and use state saving @@ -34,9 +35,9 @@ function App() { - }> + }> } /> - }/> + }/> } /> diff --git a/src/types/interfaces.ts b/src/types/interfaces.ts index e7eecbae..0c84163a 100644 --- a/src/types/interfaces.ts +++ b/src/types/interfaces.ts @@ -45,3 +45,11 @@ export interface IResponse { errorMsg?: string; } +export interface ICard { + id: string, + name: string, + description: string, + link: string, + media: any, +} +