diff --git a/src/AppContext.tsx b/src/AppContext.tsx index 7240e30..307a35c 100644 --- a/src/AppContext.tsx +++ b/src/AppContext.tsx @@ -1,34 +1,38 @@ -import { createContext } from 'react'; +import { ReactNode, createContext, useState } from 'react'; import { Domain } from './Api'; import { VerifyState } from './Routes/WizardPage/Components/VerifyRegistry/VerifyRegistry'; +import React from 'react'; /** * It represents the application context so common events and properties * are shared for many components, making their values accessible. * @public */ -export interface IAppContext { +export interface AppContextType { /** Represent the current list of domains to be displayed in the listDomains view. */ - getDomains: () => Domain[]; + domains: Domain[]; /** Callback to set the value of `domains`. */ setDomains: (domains: Domain[]) => void; /** The current editing domain */ - getEditing: () => Domain | undefined; + editing?: Domain; /** Set the current editing domain */ - setEditing: (value: Domain | undefined) => void; + setEditing: (value?: Domain) => void; + /** Encapsulates the context related with the wizard. */ wizard: { /** Retrieve the current token, required to register a domain. */ - getToken: () => string; + token: string; /** Set the value of the token. */ setToken: (value: string) => void; + /** Retrieve the value of the registered status which is updated once * the user has registered the domain by using ipa-hcc tool. */ - getRegisteredStatus: () => VerifyState; + registeredStatus: VerifyState; /** Setter for the registered status. */ setRegisteredStatus: (value: VerifyState) => void; + /** Get the ephemeral domain state that manage the wizard. */ - getDomain: () => Domain; + domain: Domain; /** Set the ephemeral domain information. */ setDomain: (value: Domain) => void; }; @@ -38,37 +42,47 @@ export interface IAppContext { * Represent the application context. * @public */ -export const AppContext = createContext({ - getDomains: (): Domain[] => { - return []; - }, - setDomains: (domains: Domain[]) => { - throw new Error('Function "setDomains" not implemented: domains=' + domains); - }, - getEditing: (): Domain | undefined => { - return undefined; - }, - setEditing: (value: Domain | undefined) => { - throw new Error('Function "setEditing" not implemented: value=' + value); - }, - wizard: { - getToken: (): string => { - return ''; - }, - setToken: (value: string) => { - throw new Error('Function "setToken" not implemented: value=' + value); - }, - getRegisteredStatus: (): VerifyState => { - return 'initial'; - }, - setRegisteredStatus: (value: VerifyState) => { - throw new Error('Function "setRegisteredStatus" not implemented: value=' + value); - }, - getDomain: (): Domain => { - return {} as Domain; - }, - setDomain: (value: Domain) => { - throw new Error('Function "setDomain" not implemented: value=' + value); - }, - }, -}); +export const AppContext = createContext(undefined); + +/** + * The properties accepted by the AppContextProvider. + */ +interface AppContextProviderProps { + /** The children components. */ + children: ReactNode; +} + +/** + * Define the provider for the application context. + * @param param0 The children components. + * @returns the application context. + */ +export const AppContextProvider: React.FC = ({ children }) => { + const [domains, _setDomains] = useState([]); + const [editing, _setEditing] = useState(); + + const [wizardToken, _setWizardSetToken] = useState(); + const [wizardRegisteredStatus, _setWizardRegisteredStatus] = useState('initial'); + const [wizardDomain, _setWizardDomain] = useState(); + + return ( + + {children} + + ); +}; diff --git a/src/AppEntry.tsx b/src/AppEntry.tsx index adbc4cd..dba22de 100644 --- a/src/AppEntry.tsx +++ b/src/AppEntry.tsx @@ -1,72 +1,19 @@ -import React, { useState } from 'react'; +import React from 'react'; import { BrowserRouter as Router } from 'react-router-dom'; import { Provider } from 'react-redux'; import { init } from './store'; import App from './App'; import { getBaseName } from '@redhat-cloud-services/frontend-components-utilities/helpers'; import logger from 'redux-logger'; -import { AppContext } from './AppContext'; -import { Domain } from './Api'; -import { VerifyState } from './Routes/WizardPage/Components/VerifyRegistry/VerifyRegistry'; +import { AppContextProvider } from './AppContext'; const AppEntry = () => { - const [domains, setDomains] = useState([]); - const [wizardToken, setWizardToken] = useState(''); - const [wizardDomain, setWizardDomain] = useState({} as Domain); - const [wizardRegisterStatus, setWizardRegisterStatus] = useState('initial'); - const [editing, setEditing] = useState(undefined); - - const cbGetDomains = (): Domain[] => { - return domains; - }; - const cbSetDomains = (domains: Domain[]) => { - setDomains(domains); - }; - const cbGetWizardToken = (): string => { - return wizardToken; - }; - const cbSetWizardToken = (value: string) => { - setWizardToken(value); - }; - const cbGetWizardDomain = (): Domain => { - return wizardDomain; - }; - const cbSetWizardDomain = (value: Domain) => { - setWizardDomain(value); - }; - const cbGetRegisterStatus = (): VerifyState => { - return wizardRegisterStatus; - }; - const cbSetRegisterStatus = (value: VerifyState) => { - setWizardRegisterStatus(value); - }; - const cbGetEditing = (): Domain | undefined => { - return editing; - }; - const cbSetEditing = (value: Domain | undefined) => { - setEditing(value); - }; return ( - + - + ); diff --git a/src/Components/DomainList/DomainList.tsx b/src/Components/DomainList/DomainList.tsx index be5a78e..5eecdbc 100644 --- a/src/Components/DomainList/DomainList.tsx +++ b/src/Components/DomainList/DomainList.tsx @@ -4,8 +4,8 @@ import { Fragment, useContext, useState } from 'react'; import React from 'react'; import { Domain, DomainType, ResourcesApiFactory } from '../../Api/api'; -import { Link, useNavigate } from 'react-router-dom'; -import { AppContext, IAppContext } from '../../AppContext'; +import { useNavigate } from 'react-router-dom'; +import { AppContext, AppContextType } from '../../AppContext'; import { Button } from '@patternfly/react-core'; export interface IColumnType { @@ -95,7 +95,7 @@ export const DomainList = () => { const base_url = '/api/idmsvc/v1'; const resources_api = ResourcesApiFactory(undefined, base_url, undefined); - const context = useContext(AppContext); + const context = useContext(AppContext); const navigate = useNavigate(); // Index of the currently sorted column @@ -106,7 +106,7 @@ export const DomainList = () => { // Sort direction of the currently sorted column const [activeSortDirection, setActiveSortDirection] = React.useState<'asc' | 'desc'>('asc'); - const [domains, setDomains] = useState(context.getDomains()); + const [domains, setDomains] = useState(context?.domains || ([] as Domain[])); const enabledText = 'Enabled'; const disabledText = 'Disabled'; @@ -204,7 +204,7 @@ export const DomainList = () => { const onShowDetails = (domain: Domain | undefined) => { if (domain !== undefined) { - context.setEditing(domain); + context?.setEditing(domain); navigate('/details/' + domain?.domain_id); } }; diff --git a/src/Routes/DefaultPage/DefaultPage.tsx b/src/Routes/DefaultPage/DefaultPage.tsx index c9fa415..9cd268b 100644 --- a/src/Routes/DefaultPage/DefaultPage.tsx +++ b/src/Routes/DefaultPage/DefaultPage.tsx @@ -28,7 +28,7 @@ import './DefaultPage.scss'; import Section from '@redhat-cloud-services/frontend-components/Section'; import { Domain, ResourcesApiFactory } from '../../Api/api'; import { DomainList } from '../../Components/DomainList/DomainList'; -import { AppContext, IAppContext } from '../../AppContext'; +import { AppContext, AppContextType } from '../../AppContext'; const Header = () => { const linkLearnMoreAbout = 'https://access.redhat.com/articles/1586893'; @@ -55,13 +55,15 @@ const EmptyContent = (props: EmptyContentProps) => { // FIXME Update this link in the future const linkLearnMoreAbout = 'https://access.redhat.com/articles/1586893'; const navigate = useNavigate(); - const appContext = useContext(AppContext); + const appContext = useContext(AppContext); const handleOpenWizard = () => { - appContext.wizard.setDomain({ domain_id: '', title: '', description: '' } as Domain); - appContext.wizard.setToken(''); - appContext.wizard.setRegisteredStatus('initial'); - navigate('/domains/wizard', { replace: true }); + if (appContext !== undefined) { + appContext.wizard.setDomain({ domain_id: '', title: '', description: '' } as Domain); + appContext.wizard.setToken(''); + appContext.wizard.setRegisteredStatus('initial'); + navigate('/domains/wizard', { replace: true }); + } }; return ( @@ -131,7 +133,7 @@ const ListContent = () => { .then((res_domain) => { local_domains[count++] = res_domain.data; if (res.data.data.length == local_domains.length) { - appContext.setDomains(local_domains); + appContext?.setDomains(local_domains); const newOffset = Math.floor((offset + perPage - 1) / perPage) * perPage; const newPage = newOffset / perPage; setItemCount(res.data.meta.count); @@ -154,10 +156,12 @@ const ListContent = () => { }, [page, perPage, offset]); const handleOpenWizard = () => { - appContext.wizard.setDomain({ domain_id: '', title: '', description: '' } as Domain); - appContext.wizard.setRegisteredStatus('initial'); - appContext.wizard.setToken(''); - navigate('/domains/wizard', { replace: true }); + if (appContext !== undefined) { + appContext.wizard.setDomain({ domain_id: '', title: '', description: '' } as Domain); + appContext.wizard.setRegisteredStatus('initial'); + appContext.wizard.setToken(''); + navigate('/domains/wizard', { replace: true }); + } }; const onSetPage = (_event: React.MouseEvent | React.KeyboardEvent | MouseEvent, newPage: number) => { @@ -222,7 +226,7 @@ const DefaultPage = () => { // States const [page, setPage] = useState(0); - const [itemCount, setItemCount] = useState(appContext.getDomains().length || -1); + const [itemCount, setItemCount] = useState(appContext?.domains.length || -1); const [perPage] = useState(10); const [offset, setOffset] = useState(0); @@ -240,14 +244,14 @@ const DefaultPage = () => { .readDomain(item.domain_id) .then((res_domain) => { local_domains[count++] = res_domain.data; - // if (res.data.data.length == local_domains.length) { - appContext.setDomains(local_domains); - const newOffset = Math.floor((offset + perPage - 1) / perPage) * perPage; - const newPage = newOffset / perPage; - setItemCount(res.data.meta.count); - setOffset(newOffset); - setPage(newPage); - // } + if (res.data.data.length == local_domains.length) { + appContext?.setDomains(local_domains); + const newOffset = Math.floor((offset + perPage - 1) / perPage) * perPage; + const newPage = newOffset / perPage; + setItemCount(res.data.meta.count); + setOffset(newOffset); + setPage(newPage); + } console.log('INFO:domain list updated'); }) .catch((reason) => { diff --git a/src/Routes/DetailPage/DetailPage.tsx b/src/Routes/DetailPage/DetailPage.tsx index 3e0d497..2e5b692 100644 --- a/src/Routes/DetailPage/DetailPage.tsx +++ b/src/Routes/DetailPage/DetailPage.tsx @@ -1,12 +1,25 @@ import { useNavigate, useParams } from 'react-router-dom'; import React, { useContext, useEffect, useState } from 'react'; -import { Card, CardBody, Dropdown, DropdownItem, Flex, FlexItem, KebabToggle, Page, PageSection, Tab, TabTitleText, Tabs, setTabIndex } from '@patternfly/react-core'; +import { + Card, + CardBody, + Dropdown, + DropdownItem, + Flex, + FlexItem, + KebabToggle, + Page, + PageSection, + Tab, + TabTitleText, + Tabs, +} from '@patternfly/react-core'; import { PageHeader, PageHeaderTitle } from '@redhat-cloud-services/frontend-components/PageHeader'; import './DetailPage.scss'; import { Domain, ResourcesApiFactory } from '../../Api/api'; -import { AppContext } from '../../AppContext'; +import { AppContext, AppContextType } from '../../AppContext'; import { DetailGeneral } from './Components/DetailGeneral/DetailGeneral'; import { DetailServers } from './Components/DetailServers/DetailServers'; @@ -21,7 +34,7 @@ interface DetailContentProps extends React.HTMLProps { * @see https://reactrouter.com/en/main/hooks/use-params */ const DetailPage = () => { - const appContext = useContext(AppContext); + const appContext = useContext(AppContext); const base_url = '/api/idmsvc/v1'; const resources_api = ResourcesApiFactory(undefined, base_url, undefined); const navigate = useNavigate(); @@ -31,7 +44,7 @@ const DetailPage = () => { // States const [domain, setDomain] = useState(); - const domains = appContext.getDomains(); + const domains = appContext?.domains; console.log('INFO:DetailPage render:domain_id=' + domain_id); diff --git a/src/Routes/WizardPage/Components/PagePreparation/PagePreparation.tsx b/src/Routes/WizardPage/Components/PagePreparation/PagePreparation.tsx index 16c2c80..345f37e 100644 --- a/src/Routes/WizardPage/Components/PagePreparation/PagePreparation.tsx +++ b/src/Routes/WizardPage/Components/PagePreparation/PagePreparation.tsx @@ -4,7 +4,7 @@ import { Alert, Button, ClipboardCopy, Form, FormGroup, TextContent, Title } fro import './PagePreparation.scss'; import { ResourcesApiFactory } from '../../../../Api'; -import { AppContext, IAppContext } from '../../../../AppContext'; +import { AppContext, AppContextType } from '../../../../AppContext'; /** Represent the properties for PagePreparation component. */ interface PagePreparationProps { @@ -30,14 +30,14 @@ const PagePreparation = (props: PagePreparationProps) => { const prerequisitesLink = 'https://www.google.com?q=rhel-idm+pre-requisites'; // States - const appContext = useContext(AppContext); + const appContext = useContext(AppContext); const base_url = '/api/idmsvc/v1'; const resources_api = ResourcesApiFactory(undefined, base_url, undefined); - const token = appContext.wizard.getToken(); - const domain = appContext.wizard.getDomain(); - const domain_id = domain.domain_id ? domain.domain_id : ''; + const token = appContext?.wizard.token; + const domain = appContext?.wizard.domain; + const domain_id = domain?.domain_id ? domain.domain_id : ''; /** * side effect to retrieve a token in the background. @@ -52,18 +52,20 @@ const PagePreparation = (props: PagePreparationProps) => { resources_api .createDomainToken({ domain_type: 'rhel-idm' }, undefined, undefined) .then((value) => { - appContext.wizard.setToken(value.data.domain_token); - const domain_id = value.data.domain_id; - const domain_name = 'My domain'; - const domain_type = value.data.domain_type; - const token = value.data.domain_token; - const expiration = value.data.expiration; - appContext.wizard.setDomain({ - domain_id: domain_id, - domain_name: domain_name, - domain_type: domain_type, - }); - props.onToken?.(token, domain_id, expiration); + if (appContext !== undefined) { + appContext.wizard.setToken(value.data.domain_token); + const domain_id = value.data.domain_id; + const domain_name = 'My domain'; + const domain_type = value.data.domain_type; + const token = value.data.domain_token; + const expiration = value.data.expiration; + appContext.wizard.setDomain({ + domain_id: domain_id, + domain_name: domain_name, + domain_type: domain_type, + }); + props.onToken?.(token, domain_id, expiration); + } }) .catch((reason) => { // FIXME handle the error here diff --git a/src/Routes/WizardPage/WizardPage.tsx b/src/Routes/WizardPage/WizardPage.tsx index 0e89d56..e64a63d 100644 --- a/src/Routes/WizardPage/WizardPage.tsx +++ b/src/Routes/WizardPage/WizardPage.tsx @@ -34,7 +34,7 @@ const WizardPage = () => { const base_url = '/api/idmsvc/v1'; const resources_api = ResourcesApiFactory(undefined, base_url, undefined); const appContext = useContext(AppContext); - const domain = appContext.wizard.getDomain(); + const domain = appContext?.wizard.domain; const navigate = useNavigate(); // FIXME Update the URL with the location for docs @@ -48,7 +48,7 @@ const WizardPage = () => { // - if not registered, dismiss wizard // - else => DELETE /domains/:domain_id // - Cancel or close model => Do not dismiss wizard - if (domain.domain_id) { + if (domain?.domain_id) { resources_api .updateDomainUser(domain.domain_id, { title: domain.title, @@ -102,9 +102,9 @@ const WizardPage = () => { }; const initCanJumpPage1 = true; - const initCanJumpPage2 = initCanJumpPage1 && domain.domain_id != '' && appContext.wizard.getToken() != ''; - const initCanJumpPage3 = initCanJumpPage2 && appContext.wizard.getRegisteredStatus() === 'completed'; - const initCanJumpPage4 = initCanJumpPage3 && domain.title !== undefined && domain.title.length > 0; + const initCanJumpPage2 = initCanJumpPage1 && domain?.domain_id != '' && appContext?.wizard.token != ''; + const initCanJumpPage3 = initCanJumpPage2 && appContext?.wizard.registeredStatus === 'completed'; + const initCanJumpPage4 = initCanJumpPage3 && domain?.title !== undefined && domain.title.length > 0; const [canJumpPage1] = useState(initCanJumpPage1); const [canJumpPage2, setCanJumpPage2] = useState(initCanJumpPage2); @@ -121,15 +121,15 @@ const WizardPage = () => { }; const onVerify = (value: VerifyState, data?: Domain) => { - if (appContext.wizard.getRegisteredStatus() === 'completed') { + if (appContext?.wizard.registeredStatus === 'completed') { // verify was previously completed (e.g. user stepped the wizard // back to the token page. Do not overwrite the domain value, // so that we do not discard any user-specified settings. return; } - appContext.wizard.setRegisteredStatus(value); + appContext?.wizard.setRegisteredStatus(value); if (value === 'completed' && data !== undefined) { - appContext.wizard.setDomain(data); + appContext?.wizard.setDomain(data); setCanJumpPage3(true); // Check whether initial values for user-configurable fields // are valid. They should be, which will enable the user to @@ -148,17 +148,23 @@ const WizardPage = () => { }; const onChangeTitle = (value: string) => { - const newDomain: Domain = { ...domain, title: value }; - appContext.wizard.setDomain(newDomain); - onUserInputChange(newDomain); + if (domain !== undefined) { + const newDomain: Domain = { ...domain, title: value }; + appContext?.wizard.setDomain(newDomain); + onUserInputChange(newDomain); + } }; const onChangeDescription = (value: string) => { - appContext.wizard.setDomain({ ...domain, description: value }); + if (domain !== undefined) { + appContext?.wizard.setDomain({ ...domain, description: value }); + } }; const onChangeAutoEnrollment = (value: boolean) => { - appContext.wizard.setDomain({ ...domain, auto_enrollment_enabled: value }); + if (domain !== undefined) { + appContext?.wizard.setDomain({ ...domain, auto_enrollment_enabled: value }); + } }; /** Configure the wizard pages. */ @@ -174,7 +180,9 @@ const WizardPage = () => { { id: 2, name: 'Domain registration', - component: , + component: ( + + ), canJumpTo: canJumpPage2, enableNext: canJumpPage3, }, @@ -183,9 +191,9 @@ const WizardPage = () => { name: 'Domain information', component: ( { { id: 4, name: 'Review', - component: , + component: , nextButtonText: 'Finish', canJumpTo: canJumpPage4, enableNext: true,