diff --git a/api b/api
index ac0c464..69afcd3 160000
--- a/api
+++ b/api
@@ -1 +1 @@
-Subproject commit ac0c464a6e650084f0d0f7bb73db6f47474f9b65
+Subproject commit 69afcd340496e6a47a08b2e45b5811bc5bcf62c8
diff --git a/docs/98-tips.md b/docs/98-tips.md
index d9c7583..d49ee55 100644
--- a/docs/98-tips.md
+++ b/docs/98-tips.md
@@ -28,22 +28,29 @@ For instance:
import './sample-component.scss';
import React from 'react';
-interface SampleComponentProp {
+/* SampleComponent component */
+
+interface SampleComponentProps {
name: string;
description: string;
}
+const defaultSampleComponentProps: SampleComponentProps = {
+ name: '',
+ description: '',
+};
+
/**
* This is a dumb component that only recieves properties from a smart component.
* Dumb components are usually functions and not classes.
*
* @param props the props given by the smart component.
*/
-const SampleComponent: React.FC = (props) => {
+const SampleComponent: React.FC = (props) => {
return {props.children} ;
};
-SampleComponent.displayName = 'SampleComponent';
+SampleComponent.defaultProps = defaultSampleComponentProps;
export default SampleComponent;
```
@@ -133,6 +140,43 @@ class MyComponent extends React.Component {
}
```
+## Forward properties when no levels or not many levels
+
+The initial approach would be to define for a new component the interface
+with the values to be passed as properties; this include values used and `events`
+that will receive the parent component. This is the first immediate mechanism
+to communicate with the parent component.
+
+```typescript
+interface MyComponentProps {
+ value: string;
+ onChange: (value: string) => void;
+}
+
+const defaultMyComponentProps: MyComponentProps = {
+ value: '',
+ onChange: (value: string) => { return; },
+};
+
+const MyComponent: React.FC = (props) => {
+ const [state, setState] = useState(props.value);
+ const onChange: (value: string) => {
+ setState(value);
+ props.onChange(state);
+ };
+ return (
+
+ );
+};
+
+MyComponent.defaultProps = defaultMyComponentProps;
+```
+
+In the above case, MyComponent receive the value to be displayed into
+the `input` component, and when it is changed, the new value is send
+to the parent component by calling the onChange callback provided by
+the parent component.
+
## Use context when necessary
The most immediate way to pass information between the components
@@ -156,6 +200,16 @@ Said this, we can quickly identify some specific cases for our repository:
- The register domain in process, to show and update the info
as we advance in the wizard.
+## Document your components
+
+TODO
+
+Document, document, document. Today we are confident about what we are
+coding, tomorrow maybe we have forgotten everything about that new,
+fancy and awesome component. It is for you, for your team and the
+community. A component that is well documented, can be enhanced by
+other team mates or the community.
+
## References
Helpful articles:
diff --git a/src/Api/api.ts b/src/Api/api.ts
index e7f1441..fd04316 100644
--- a/src/Api/api.ts
+++ b/src/Api/api.ts
@@ -970,56 +970,6 @@ export const ActionsApiAxiosParamCreator = function (configuration?: Configurati
localVarRequestOptions.headers = { ...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers };
localVarRequestOptions.data = serializeDataIfNeeded(updateDomainAgentRequest, localVarRequestOptions, configuration);
- return {
- url: toPathString(localVarUrlObj),
- options: localVarRequestOptions,
- };
- },
- /**
- * Update the rhel-idm domain information.
- * @summary Update domain information by user.
- * @param {string} uuid The uuid that identify the domain.
- * @param {UpdateDomainUserRequest} updateDomainUserRequest Information for an IPA domain so it is updated from the ipa-hcc agent.
- * @param {string} [xRhInsightsRequestId] Request id for distributed tracing.
- * @param {*} [options] Override http request option.
- * @throws {RequiredError}
- */
- updateDomainUser: async (
- uuid: string,
- updateDomainUserRequest: UpdateDomainUserRequest,
- xRhInsightsRequestId?: string,
- options: AxiosRequestConfig = {}
- ): Promise => {
- // verify required parameter 'uuid' is not null or undefined
- assertParamExists('updateDomainUser', 'uuid', uuid);
- // verify required parameter 'updateDomainUserRequest' is not null or undefined
- assertParamExists('updateDomainUser', 'updateDomainUserRequest', updateDomainUserRequest);
- const localVarPath = `/domains/{uuid}`.replace(`{${'uuid'}}`, encodeURIComponent(String(uuid)));
- // use dummy base URL string because the URL constructor only accepts absolute URLs.
- const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
- let baseOptions;
- if (configuration) {
- baseOptions = configuration.baseOptions;
- }
-
- const localVarRequestOptions = { method: 'PATCH', ...baseOptions, ...options };
- const localVarHeaderParameter = {} as any;
- const localVarQueryParameter = {} as any;
-
- // authentication x-rh-identity required
- await setApiKeyToObject(localVarHeaderParameter, 'X-Rh-Identity', configuration);
-
- if (xRhInsightsRequestId != null) {
- localVarHeaderParameter['X-Rh-Insights-Request-Id'] = String(xRhInsightsRequestId);
- }
-
- localVarHeaderParameter['Content-Type'] = 'application/json';
-
- setSearchParams(localVarUrlObj, localVarQueryParameter);
- let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
- localVarRequestOptions.headers = { ...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers };
- localVarRequestOptions.data = serializeDataIfNeeded(updateDomainUserRequest, localVarRequestOptions, configuration);
-
return {
url: toPathString(localVarUrlObj),
options: localVarRequestOptions,
@@ -1081,24 +1031,6 @@ export const ActionsApiFp = function (configuration?: Configuration) {
);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
},
- /**
- * Update the rhel-idm domain information.
- * @summary Update domain information by user.
- * @param {string} uuid The uuid that identify the domain.
- * @param {UpdateDomainUserRequest} updateDomainUserRequest Information for an IPA domain so it is updated from the ipa-hcc agent.
- * @param {string} [xRhInsightsRequestId] Request id for distributed tracing.
- * @param {*} [options] Override http request option.
- * @throws {RequiredError}
- */
- async updateDomainUser(
- uuid: string,
- updateDomainUserRequest: UpdateDomainUserRequest,
- xRhInsightsRequestId?: string,
- options?: AxiosRequestConfig
- ): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> {
- const localVarAxiosArgs = await localVarAxiosParamCreator.updateDomainUser(uuid, updateDomainUserRequest, xRhInsightsRequestId, options);
- return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
- },
};
};
@@ -1149,23 +1081,6 @@ export const ActionsApiFactory = function (configuration?: Configuration, basePa
.updateDomainAgent(uuid, xRhIdmVersion, updateDomainAgentRequest, xRhInsightsRequestId, options)
.then((request) => request(axios, basePath));
},
- /**
- * Update the rhel-idm domain information.
- * @summary Update domain information by user.
- * @param {string} uuid The uuid that identify the domain.
- * @param {UpdateDomainUserRequest} updateDomainUserRequest Information for an IPA domain so it is updated from the ipa-hcc agent.
- * @param {string} [xRhInsightsRequestId] Request id for distributed tracing.
- * @param {*} [options] Override http request option.
- * @throws {RequiredError}
- */
- updateDomainUser(
- uuid: string,
- updateDomainUserRequest: UpdateDomainUserRequest,
- xRhInsightsRequestId?: string,
- options?: any
- ): AxiosPromise {
- return localVarFp.updateDomainUser(uuid, updateDomainUserRequest, xRhInsightsRequestId, options).then((request) => request(axios, basePath));
- },
};
};
@@ -1215,27 +1130,6 @@ export class ActionsApi extends BaseAPI {
.updateDomainAgent(uuid, xRhIdmVersion, updateDomainAgentRequest, xRhInsightsRequestId, options)
.then((request) => request(this.axios, this.basePath));
}
-
- /**
- * Update the rhel-idm domain information.
- * @summary Update domain information by user.
- * @param {string} uuid The uuid that identify the domain.
- * @param {UpdateDomainUserRequest} updateDomainUserRequest Information for an IPA domain so it is updated from the ipa-hcc agent.
- * @param {string} [xRhInsightsRequestId] Request id for distributed tracing.
- * @param {*} [options] Override http request option.
- * @throws {RequiredError}
- * @memberof ActionsApi
- */
- public updateDomainUser(
- uuid: string,
- updateDomainUserRequest: UpdateDomainUserRequest,
- xRhInsightsRequestId?: string,
- options?: AxiosRequestConfig
- ) {
- return ActionsApiFp(this.configuration)
- .updateDomainUser(uuid, updateDomainUserRequest, xRhInsightsRequestId, options)
- .then((request) => request(this.axios, this.basePath));
- }
}
/**
@@ -1510,6 +1404,56 @@ export const ResourcesApiAxiosParamCreator = function (configuration?: Configura
localVarRequestOptions.headers = { ...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers };
localVarRequestOptions.data = serializeDataIfNeeded(registerDomainRequest, localVarRequestOptions, configuration);
+ return {
+ url: toPathString(localVarUrlObj),
+ options: localVarRequestOptions,
+ };
+ },
+ /**
+ * Update the rhel-idm domain information.
+ * @summary Update domain information by user.
+ * @param {string} uuid The uuid that identify the domain.
+ * @param {UpdateDomainUserRequest} updateDomainUserRequest Information for an IPA domain so it is updated from the ipa-hcc agent.
+ * @param {string} [xRhInsightsRequestId] Request id for distributed tracing.
+ * @param {*} [options] Override http request option.
+ * @throws {RequiredError}
+ */
+ updateDomainUser: async (
+ uuid: string,
+ updateDomainUserRequest: UpdateDomainUserRequest,
+ xRhInsightsRequestId?: string,
+ options: AxiosRequestConfig = {}
+ ): Promise => {
+ // verify required parameter 'uuid' is not null or undefined
+ assertParamExists('updateDomainUser', 'uuid', uuid);
+ // verify required parameter 'updateDomainUserRequest' is not null or undefined
+ assertParamExists('updateDomainUser', 'updateDomainUserRequest', updateDomainUserRequest);
+ const localVarPath = `/domains/{uuid}`.replace(`{${'uuid'}}`, encodeURIComponent(String(uuid)));
+ // use dummy base URL string because the URL constructor only accepts absolute URLs.
+ const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
+ let baseOptions;
+ if (configuration) {
+ baseOptions = configuration.baseOptions;
+ }
+
+ const localVarRequestOptions = { method: 'PATCH', ...baseOptions, ...options };
+ const localVarHeaderParameter = {} as any;
+ const localVarQueryParameter = {} as any;
+
+ // authentication x-rh-identity required
+ await setApiKeyToObject(localVarHeaderParameter, 'X-Rh-Identity', configuration);
+
+ if (xRhInsightsRequestId != null) {
+ localVarHeaderParameter['X-Rh-Insights-Request-Id'] = String(xRhInsightsRequestId);
+ }
+
+ localVarHeaderParameter['Content-Type'] = 'application/json';
+
+ setSearchParams(localVarUrlObj, localVarQueryParameter);
+ let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
+ localVarRequestOptions.headers = { ...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers };
+ localVarRequestOptions.data = serializeDataIfNeeded(updateDomainUserRequest, localVarRequestOptions, configuration);
+
return {
url: toPathString(localVarUrlObj),
options: localVarRequestOptions,
@@ -1631,6 +1575,24 @@ export const ResourcesApiFp = function (configuration?: Configuration) {
);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
},
+ /**
+ * Update the rhel-idm domain information.
+ * @summary Update domain information by user.
+ * @param {string} uuid The uuid that identify the domain.
+ * @param {UpdateDomainUserRequest} updateDomainUserRequest Information for an IPA domain so it is updated from the ipa-hcc agent.
+ * @param {string} [xRhInsightsRequestId] Request id for distributed tracing.
+ * @param {*} [options] Override http request option.
+ * @throws {RequiredError}
+ */
+ async updateDomainUser(
+ uuid: string,
+ updateDomainUserRequest: UpdateDomainUserRequest,
+ xRhInsightsRequestId?: string,
+ options?: AxiosRequestConfig
+ ): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> {
+ const localVarAxiosArgs = await localVarAxiosParamCreator.updateDomainUser(uuid, updateDomainUserRequest, xRhInsightsRequestId, options);
+ return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
+ },
};
};
@@ -1717,6 +1679,23 @@ export const ResourcesApiFactory = function (configuration?: Configuration, base
.registerDomain(xRhIdmRegistrationToken, xRhIdmVersion, registerDomainRequest, xRhInsightsRequestId, options)
.then((request) => request(axios, basePath));
},
+ /**
+ * Update the rhel-idm domain information.
+ * @summary Update domain information by user.
+ * @param {string} uuid The uuid that identify the domain.
+ * @param {UpdateDomainUserRequest} updateDomainUserRequest Information for an IPA domain so it is updated from the ipa-hcc agent.
+ * @param {string} [xRhInsightsRequestId] Request id for distributed tracing.
+ * @param {*} [options] Override http request option.
+ * @throws {RequiredError}
+ */
+ updateDomainUser(
+ uuid: string,
+ updateDomainUserRequest: UpdateDomainUserRequest,
+ xRhInsightsRequestId?: string,
+ options?: any
+ ): AxiosPromise {
+ return localVarFp.updateDomainUser(uuid, updateDomainUserRequest, xRhInsightsRequestId, options).then((request) => request(axios, basePath));
+ },
};
};
@@ -1824,4 +1803,25 @@ export class ResourcesApi extends BaseAPI {
.registerDomain(xRhIdmRegistrationToken, xRhIdmVersion, registerDomainRequest, xRhInsightsRequestId, options)
.then((request) => request(this.axios, this.basePath));
}
+
+ /**
+ * Update the rhel-idm domain information.
+ * @summary Update domain information by user.
+ * @param {string} uuid The uuid that identify the domain.
+ * @param {UpdateDomainUserRequest} updateDomainUserRequest Information for an IPA domain so it is updated from the ipa-hcc agent.
+ * @param {string} [xRhInsightsRequestId] Request id for distributed tracing.
+ * @param {*} [options] Override http request option.
+ * @throws {RequiredError}
+ * @memberof ResourcesApi
+ */
+ public updateDomainUser(
+ uuid: string,
+ updateDomainUserRequest: UpdateDomainUserRequest,
+ xRhInsightsRequestId?: string,
+ options?: AxiosRequestConfig
+ ) {
+ return ResourcesApiFp(this.configuration)
+ .updateDomainUser(uuid, updateDomainUserRequest, xRhInsightsRequestId, options)
+ .then((request) => request(this.axios, this.basePath));
+ }
}
diff --git a/src/AppContext.tsx b/src/AppContext.tsx
index ed5c41b..fe1457c 100644
--- a/src/AppContext.tsx
+++ b/src/AppContext.tsx
@@ -1,9 +1,22 @@
import { createContext } from 'react';
import { Domain } from './Api';
+import { VerifyState } from './Routes/WizardPage/Components/VerifyRegistry/VerifyRegistry';
export interface IAppContext {
domains: Domain[];
setDomains: (domains: Domain[]) => void;
+ wizard: {
+ getToken: () => string;
+ setToken: (value: string) => void;
+ getUUID: () => string;
+ setUUID: (value: string) => void;
+ title: string;
+ setTitle: (value: string) => void;
+ description: string;
+ setDescription: (value: string) => void;
+ getRegisteredStatus: () => VerifyState;
+ setRegisteredStatus: (value: VerifyState) => void;
+ };
// wizardDomain?: Domain;
// setWizardDomain: (domain?: Domain) => void;
}
@@ -13,6 +26,34 @@ export const AppContext = createContext({
setDomains: (domains: Domain[]) => {
throw new Error('Function "setDomains" not implemented: domains=' + domains);
},
+ wizard: {
+ getToken: (): string => {
+ return '';
+ },
+ setToken: (value: string) => {
+ throw new Error('Function "setToken" not implemented: value=' + value);
+ },
+ getUUID: (): string => {
+ return '';
+ },
+ setUUID: (value: string) => {
+ throw new Error('Function "setUUID" not implemented: value=' + value);
+ },
+ title: '',
+ setTitle: (value: string) => {
+ throw new Error('Function "setTitle" not implemented: value=' + value);
+ },
+ description: '',
+ setDescription: (value: string) => {
+ throw new Error('Function "setDescription" not implemented: value=' + value);
+ },
+ getRegisteredStatus: (): VerifyState => {
+ return 'initial';
+ },
+ setRegisteredStatus: (value: VerifyState) => {
+ throw new Error('Function "setRegisteredStatus" not implemented: value=' + value);
+ },
+ },
// wizardDomain: undefined,
// setWizardDomain: (domain?: Domain) => {
// throw new Error('Function "setWizardDomain" not implemented: domain=' + domain);
diff --git a/src/AppEntry.tsx b/src/AppEntry.tsx
index 8bf2bf4..15635bb 100644
--- a/src/AppEntry.tsx
+++ b/src/AppEntry.tsx
@@ -1,4 +1,4 @@
-import React, { useState } from 'react';
+import React, { useContext, useState } from 'react';
import { BrowserRouter as Router } from 'react-router-dom';
import { Provider } from 'react-redux';
import { init } from './store';
@@ -7,16 +7,67 @@ import { getBaseName } from '@redhat-cloud-services/frontend-components-utilitie
import logger from 'redux-logger';
import { AppContext } from './AppContext';
import { Domain } from './Api';
+import { VerifyState } from './Routes/WizardPage/Components/VerifyRegistry/VerifyRegistry';
const AppEntry = () => {
+ const appContext = useContext(AppContext);
const [domains, setDomains] = useState([]);
+ const [wizardToken, setWizardToken] = useState('');
+ const [wizardTitle, setWizardTitle] = useState('');
+ const [wizardDescription, setWizardDescription] = useState('');
+ const [wizardUUID, setWizardUUID] = useState('');
+ const [wizardRegisterStatus, setWizardRegisterStatus] = useState('initial');
const cbSetDomains = (domains: Domain[]) => {
+ appContext.domains = domains;
setDomains(domains);
};
+ const cbGetWizardToken = (): string => {
+ return wizardToken;
+ };
+ const cbSetWizardToken = (value: string) => {
+ setWizardToken(value);
+ };
+ const cbGetWizardUUID = (): string => {
+ return wizardUUID;
+ };
+ const cbSetWizardUUID = (value: string) => {
+ setWizardUUID(value);
+ };
+ const cbSetWizardTitle = (title: string) => {
+ appContext.wizard.title = title;
+ setWizardTitle(title);
+ };
+ const cbSetWizardDescription = (description: string) => {
+ appContext.wizard.description = description;
+ setWizardDescription(description);
+ };
+ const cbGetRegisterStatus = (): VerifyState => {
+ return wizardRegisterStatus;
+ };
+ const cbSetRegisterStatus = (value: VerifyState) => {
+ setWizardRegisterStatus(value);
+ };
return (
-
+
diff --git a/src/Routes/DefaultPage/DefaultPage.scss b/src/Routes/DefaultPage/DefaultPage.scss
index 5624387..acd9576 100644
--- a/src/Routes/DefaultPage/DefaultPage.scss
+++ b/src/Routes/DefaultPage/DefaultPage.scss
@@ -1,5 +1 @@
@import '~@redhat-cloud-services/frontend-components-utilities/styles/variables';
-
-.empty-state-body-content {
- margin-bottom: $ins-margin;
-}
diff --git a/src/Routes/DefaultPage/DefaultPage.tsx b/src/Routes/DefaultPage/DefaultPage.tsx
index bb41404..06c3515 100644
--- a/src/Routes/DefaultPage/DefaultPage.tsx
+++ b/src/Routes/DefaultPage/DefaultPage.tsx
@@ -27,7 +27,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 } from '../../AppContext';
+import { AppContext, IAppContext } from '../../AppContext';
// const SampleComponent = lazy(() => import('../../Components/SampleComponent/sample-component'));
@@ -51,9 +51,16 @@ const Header = () => {
};
const EmptyContent = () => {
+ const linkLearnMoreAbout = 'https://access.redhat.com/articles/1586893';
const navigate = useNavigate();
+ const appContext = useContext(AppContext);
const handleOpenWizard = () => {
+ appContext.wizard.setToken('');
+ appContext.wizard.setUUID('');
+
+ appContext.wizard.setTitle('');
+ appContext.wizard.setDescription('');
navigate('/domains/wizard', { replace: true });
};
@@ -62,21 +69,34 @@ const EmptyContent = () => {
-
-
- No domains
+
+
+ No direcotry and domain services registered
-
- To specify which existing access controls can be
-
leveraged for hosts launched through the console, you
-
must first register your domain(s). As part of that
-
process you will be required to SSO into your server(s)
-
and install packages via CLI.
+
+ Use access controls from your existing IdM host in your cloud
+
environment*. To get started, register a service
+
+
+
-
-
+
+ }
+ iconPosition="right"
+ href={linkLearnMoreAbout}
+ >
+ Learn more about the domain registry.
+
+
+
+ *Directory and domain services are currently available only for Red Hat IdM and IPA deployments.
@@ -205,9 +225,9 @@ const DefaultPage = () => {
const [perPage] = useState(10);
const [offset, setOffset] = useState(0);
- useEffect(() => {
- insights?.chrome?.appAction?.('default-page');
- }, []);
+ // useEffect(() => {
+ // insights?.chrome?.appAction?.('default-page');
+ // }, []);
console.log('INFO:DefaultPage render');
diff --git a/src/Routes/WizardPage/Components/PagePreparation/PagePreparation.scss b/src/Routes/WizardPage/Components/PagePreparation/PagePreparation.scss
index 219c002..acd9576 100644
--- a/src/Routes/WizardPage/Components/PagePreparation/PagePreparation.scss
+++ b/src/Routes/WizardPage/Components/PagePreparation/PagePreparation.scss
@@ -1,9 +1 @@
@import '~@redhat-cloud-services/frontend-components-utilities/styles/variables';
-
-.domain-type-select {
- width: 50%;
-}
-
-.domain-item-margin-left {
- margin-left: 16px;
-}
diff --git a/src/Routes/WizardPage/Components/PagePreparation/PagePreparation.tsx b/src/Routes/WizardPage/Components/PagePreparation/PagePreparation.tsx
index 511024a..ac109df 100644
--- a/src/Routes/WizardPage/Components/PagePreparation/PagePreparation.tsx
+++ b/src/Routes/WizardPage/Components/PagePreparation/PagePreparation.tsx
@@ -1,26 +1,64 @@
-import React from 'react';
+import React, { useContext, useEffect, useState } from 'react';
import ExternalLinkAltIcon from '@patternfly/react-icons/dist/esm/icons/external-link-alt-icon';
import InfoCircleIcon from '@patternfly/react-icons/dist/esm/icons/info-circle-icon';
-import { Button, ClipboardCopy, Form, FormGroup, Icon, Select, SelectOption, Stack, TextContent } from '@patternfly/react-core';
+import { Button, ClipboardCopy, Form, FormGroup, Icon, Select, SelectOption, TextContent, Title } from '@patternfly/react-core';
import './PagePreparation.scss';
+import { DomainType, ResourcesApiFactory } from '../../../../Api';
+import { AppContext, IAppContext } from '../../../../AppContext';
-const PagePreparation: React.FC = () => {
- // TODO Update links
- const firewallConfigurationLink =
- 'https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html/configuring_and_managing_networking/using-and-configuring-firewalld_configuring-and-managing-networking';
- const cloudProviderConfigurationLink =
- 'https://access.redhat.com/documentation/es-es/red_hat_subscription_management/2023/html-single/red_hat_cloud_access_reference_guide/index';
- const networkConfigurationLink = 'https://www.redhat.com/sysadmin/network-interface-linux';
- const installServerPackagesLink = 'https://freeipa.org/page/Quick_Start_Guide';
+interface PagePreparationProps {
+ token?: string;
+ onToken?: (token: string, domain_id: string, expiration: number) => void;
+}
+
+const defaultPagePreparationProps: PagePreparationProps = {
+ token: undefined,
+ onToken: undefined,
+};
+
+const PagePreparation: React.FC = (props) => {
+ // FIXME Update the target link when it is known
+ const installServerPackagesLink = 'https://duckduckgo.com/?q=freeipa+prerequisites';
+ // FIXME Update the target link when it is known
+ const prerequisitesLink = 'https://www.google.com?q=rhel-idm+pre-requisites';
// States
- const [isOpen, setIsOpen] = React.useState(false);
+ const [isOpen, setIsOpen] = useState(false);
+ 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_id = appContext.wizard.getUUID();
+
+ /**
+ * side effect to retrieve a token in the background.
+ * TODO When more than one type of domain, this callback will
+ * invoke from 'onRegisterDomainTypeSelect' event.
+ */
+ useEffect(() => {
+ if (token != '' && domain_id != '') {
+ return;
+ }
+ // NOTE await and async cannot be used directly because EffectCallback cannot be a Promise
+ resources_api
+ .createDomainToken({ domain_type: 'rhel-idm' }, undefined, undefined)
+ .then((value) => {
+ appContext.wizard.setToken(value.data.domain_token);
+ appContext.wizard.setUUID(value.data.domain_id);
+ props.onToken?.(value.data.domain_token, value.data.domain_id, value.data.expiration);
+ })
+ .catch((reason) => {
+ // FIXME handle the error here
+ console.log('error creating the token by createDomainToken: ' + reason);
+ });
+ }, [token, domain_id]);
- // hooks
const onRegisterDomainTypeSelect = () => {
- // TODO Not implemented
- console.debug('onRegisterDomainTypeSelect in WizardPage');
+ // TODO Not implemented, currently only support rhel-idm
+ console.debug('PagePreparation.onRegisterDomainTypeSelect in WizardPage');
return;
};
@@ -30,29 +68,29 @@ const PagePreparation: React.FC = () => {
const domainOptions = [
{
- value: 'rhel-idm',
- title: 'Red Hat Enterprise Linux IdM/IPA',
+ value: 'rhel-idm' as DomainType,
+ title: 'RHEL IdM (IPA)',
},
];
return (
+ Preparation for your directory and domain service
);
};
+PagePreparation.defaultProps = defaultPagePreparationProps;
+
export default PagePreparation;
diff --git a/src/Routes/WizardPage/Components/PageServiceDetails/PageServiceDetails.tsx b/src/Routes/WizardPage/Components/PageServiceDetails/PageServiceDetails.tsx
index 3b9e8cf..74d81be 100644
--- a/src/Routes/WizardPage/Components/PageServiceDetails/PageServiceDetails.tsx
+++ b/src/Routes/WizardPage/Components/PageServiceDetails/PageServiceDetails.tsx
@@ -1,5 +1,5 @@
import React, { useState } from 'react';
-import { Form, FormGroup, TextArea, Title, Tooltip } from '@patternfly/react-core';
+import { Form, FormGroup, Icon, TextArea, Title, Tooltip } from '@patternfly/react-core';
import { TextInput } from '@patternfly/react-core';
import { Domain } from '../../../../Api/api';
import OutlinedQuestionCircleIcon from '@patternfly/react-icons/dist/esm/icons/question-circle-icon';
@@ -28,7 +28,7 @@ const PageServiceDetails: React.FC<{ data: Domain }> = (props) => {
>
Service Details
-
+
@@ -44,7 +44,10 @@ const PageServiceDetails: React.FC<{ data: Domain }> = (props) => {
- Domain join on launch
+ Domain join on launch{' '}
+
+
+
>
}
>
diff --git a/src/Routes/WizardPage/Components/PageServiceRegistration/PageServiceRegistration.tsx b/src/Routes/WizardPage/Components/PageServiceRegistration/PageServiceRegistration.tsx
index 34e640a..5524f77 100644
--- a/src/Routes/WizardPage/Components/PageServiceRegistration/PageServiceRegistration.tsx
+++ b/src/Routes/WizardPage/Components/PageServiceRegistration/PageServiceRegistration.tsx
@@ -1,58 +1,77 @@
-import React from 'react';
-// import { useDispatch } from 'react-redux';
-import { ExternalLinkAltIcon } from '@patternfly/react-icons/dist/esm/icons/external-link-alt-icon';
-import { Alert, Button, ClipboardCopy, Form, FormGroup, TextContent } from '@patternfly/react-core';
+import React, { useState } from 'react';
+import { Alert, Button, ClipboardCopy, Flex, FlexItem, Form, TextContent, Title } from '@patternfly/react-core';
import './PageServiceRegistration.scss';
-import { Domain } from '../../../../Api/api';
+import VerifyRegistry, { VerifyState } from '../VerifyRegistry/VerifyRegistry';
+import { Domain } from '../../../../Api';
interface PageServiceRegistrationProp {
- data?: Domain;
- token?: string;
+ uuid: string;
+ token: string;
+ onVerify: (value: VerifyState, data?: Domain) => void;
}
const PageServiceRegistration: React.FC = (props) => {
- // FIXME Delete this
- const demoToken = 'F4ZWgmhUxcw.d2iqKLHa8281CM_1aknGLsBRFpwfoy3YkrTbLBIuEkM';
- const ipa_hcc_register_cmd = 'ipa-hcc register ' + demoToken;
+ // FIXME Update the URL with the location for docs
+ const installServerPackagesLink = 'https://freeipa.org/page/Quick_Start_Guide';
+ const [state, setState] = useState('initial');
+
+ const openInNewWindow = (url: string) => {
+ window.open(url, '_blank');
+ };
+
+ const onInstallServerPackagesClick = () => {
+ openInNewWindow(installServerPackagesLink);
+ };
+
+ const ipa_hcc_register_cmd = 'ipa-hcc register ' + props.token;
const alertTitle = 'Register your directory and domain service';
// FIXME Update the URL with the location for docs
- const linkLearnMoreAbout = 'https://access.redhat.com/articles/1586893';
+ const linkLearnMoreAbout = 'https://www.google.es/search?q=freeipa+registering+a+domain+service';
+
+ const onChangeVerifyRegistry = (newState: VerifyState) => {
+ setState(newState);
+ props.onVerify(newState);
+ };
+
+ console.log('PageServiceRegistration: uuid=' + props.uuid + '; token=' + props.token);
return (
-
- Completing this step registers your directory and domain service, and cannot be undone from the wizard.{' '}
- {/* FIXME Q What is the better way to fix the top padding between the link and the text? */}
-
- } iconPosition="right" href={linkLearnMoreAbout}>
- Learn more about the directory and domain services.
-
-
-
+ Register your directory and domain service
- {/* TODO Add here the new VerifyRegistration component */}
+
+
+
+
+
);
};
diff --git a/src/Routes/WizardPage/Components/VerifyRegistry/VerifyRegistry.scss b/src/Routes/WizardPage/Components/VerifyRegistry/VerifyRegistry.scss
new file mode 100644
index 0000000..861f8be
--- /dev/null
+++ b/src/Routes/WizardPage/Components/VerifyRegistry/VerifyRegistry.scss
@@ -0,0 +1,2 @@
+@import '~@redhat-cloud-services/frontend-components-utilities/styles/variables';
+
diff --git a/src/Routes/WizardPage/Components/VerifyRegistry/VerifyRegistry.test.tsx b/src/Routes/WizardPage/Components/VerifyRegistry/VerifyRegistry.test.tsx
new file mode 100644
index 0000000..ad7d07b
--- /dev/null
+++ b/src/Routes/WizardPage/Components/VerifyRegistry/VerifyRegistry.test.tsx
@@ -0,0 +1,10 @@
+// import React from 'react';
+// import { render } from '@testing-library/react';
+// import Page1 from './Page1';
+import '@testing-library/jest-dom';
+
+test('expect VerifyRegistry to render children', () => {
+ // render();
+ // expect(screen.getByRole('heading')).toHaveTextContent('Hello');
+ return;
+});
diff --git a/src/Routes/WizardPage/Components/VerifyRegistry/VerifyRegistry.tsx b/src/Routes/WizardPage/Components/VerifyRegistry/VerifyRegistry.tsx
new file mode 100644
index 0000000..ee33d27
--- /dev/null
+++ b/src/Routes/WizardPage/Components/VerifyRegistry/VerifyRegistry.tsx
@@ -0,0 +1,301 @@
+import React, { useEffect, useState } from 'react';
+
+import { AxiosError } from 'axios';
+import { Button, Icon, Label, Stack, StackItem, TextContent } from '@patternfly/react-core';
+import { CheckCircleIcon } from '@patternfly/react-icons/dist/esm/icons/check-circle-icon';
+import { ExclamationCircleIcon } from '@patternfly/react-icons/dist/esm/icons/exclamation-circle-icon';
+import { PendingIcon } from '@patternfly/react-icons/dist/esm/icons/pending-icon';
+import { ExternalLinkAltIcon } from '@patternfly/react-icons/dist/esm/icons/external-link-alt-icon';
+
+import './VerifyRegistry.scss';
+import { ResourcesApiFactory } from '../../../../Api';
+
+/* Common definitions */
+
+export type VerifyState = 'initial' | 'waiting' | 'timed-out' | 'not-found' | 'completed';
+
+/* VerifyRegistryIcon component */
+
+interface VerifyRegistryIconProps {
+ state: VerifyState;
+}
+
+// const defaultVerifyRegistryIconProps: VerifyRegistryIconProps = {
+// state: 'initial',
+// };
+
+const VerifyRegistryIcon = (props: VerifyRegistryIconProps) => {
+ return (
+ <>
+ {props.state == 'initial' && (
+
+ )}
+ {props.state == 'waiting' && (
+
+ )}
+ {props.state == 'timed-out' && (
+
+ )}
+ {props.state == 'not-found' && (
+
+ )}
+ {props.state == 'completed' && (
+
+ )}
+ >
+ );
+};
+
+// VerifyRegistryIcon.defaultProps = defaultVerifyRegistryIconProps;
+
+/* VerifyRegistryLabel component */
+
+interface VerifyRegistryLabelProps {
+ state: VerifyState;
+}
+
+// const defaultVerifyRegistryLabelProps: VerifyRegistryLabelProps = {
+// state: 'initial',
+// };
+
+const VerifyRegistryLabel = (props: VerifyRegistryLabelProps) => {
+ return (
+ <>
+ {props.state == 'initial' && Verify registration}
+ {props.state == 'waiting' && Verify registration}
+ {props.state == 'timed-out' && Verify registration}
+ {props.state == 'not-found' && Verify registration}
+ {props.state == 'completed' && Verify registration}
+ >
+ );
+};
+
+// VerifyRegistryLabel.defaultProps = defaultVerifyRegistryLabelProps;
+
+/* VerifyRegistryLabel component */
+
+interface VerifyRegistryDescriptionProps {
+ state: VerifyState;
+}
+
+// const defaultVerifyRegistryDescriptionProps: VerifyRegistryDescriptionProps = {
+// state: 'initial',
+// };
+
+const VerifyRegistryDescription = (props: VerifyRegistryDescriptionProps) => {
+ return (
+ <>
+ {props.state == 'initial' && Running verification test}
+ {props.state == 'waiting' && Waiting for registration data}
+ {props.state == 'timed-out' && Test timed out}
+ {props.state == 'not-found' && Registration data not found}
+ {props.state == 'completed' && Test completed}
+ >
+ );
+};
+
+// VerifyRegistryDescription.defaultProps = defaultVerifyRegistryDescriptionProps;
+
+/* VerifyRegistryyFooter */
+
+interface VerifyRegistryFooterProps {
+ state: VerifyState;
+ onTest?: () => void;
+}
+
+// const defaultVerifyRegistryFooterProps: VerifyRegistryFooterProps = {
+// state: 'initial',
+// onTest: () => {
+// console.log('VerifyRegistryFooter.onTest event fired');
+// },
+// };
+
+const VerifyRegistryFooter = (props: VerifyRegistryFooterProps) => {
+ const linkTroubleshootRegistration = 'https://www.google.com/search?q=freeipa+troubleshooting';
+ return (
+ <>
+ {props.state == 'initial' && (
+ <>
+
+ >
+ )}
+ {props.state == 'waiting' && <>>}
+ {props.state == 'timed-out' && (
+ <>
+
+ >
+ )}
+ {props.state == 'not-found' && (
+ <>
+ } iconPosition="right">
+ Troubleshoot registration
+
+ >
+ )}
+ {props.state == 'completed' && (
+ <>
+
+ >
+ )}
+ >
+ );
+};
+
+// VerifyRegistryFooter.defaultProps = defaultVerifyRegistryFooterProps;
+
+/* VerifyRegistry component */
+
+interface VerifyRegistryProps {
+ state: VerifyState;
+ uuid: string;
+ onChange: (newState: VerifyState) => void;
+}
+
+// const defaultVerifyRegistryProps: VerifyRegistryProps = {
+// state: 'initial',
+// uuid: '',
+// onChange: (value: VerifyState) => {
+// console.log('VerifyRegistry:onChange fired: value=' + value);
+// },
+// };
+
+const VerifyRegistry = (props: VerifyRegistryProps) => {
+ // const [state, setState] = useState(props.state);
+ const [isPolling, setIsPolling] = useState(true);
+
+ const base_url = '/api/idmsvc/v1';
+ const resources_api = ResourcesApiFactory(undefined, base_url, undefined);
+ const timeout = 30 * 1000; // Seconds
+
+ /** TODO Extract this effect in a hook to simplify this code */
+ useEffect(() => {
+ let intervalId: NodeJS.Timeout | null = null;
+ let elapsedTime = 0;
+ let newState: VerifyState = props.state;
+ const stopPolling = (state: VerifyState) => {
+ if (intervalId) {
+ clearInterval(intervalId);
+ }
+ intervalId = null;
+ setIsPolling(false);
+ // setState(state);
+ props.onChange(state);
+ };
+ if (!isPolling) {
+ return;
+ }
+ const fetchData = async () => {
+ try {
+ // setState('waiting');
+ const response = await resources_api.readDomain(props.uuid, undefined, undefined);
+ const newData = response.data;
+ newState = 'completed';
+ } catch (error) {
+ const axiosError = error as AxiosError;
+ switch (axiosError.code) {
+ case AxiosError.ECONNABORTED:
+ case AxiosError.ERR_BAD_OPTION:
+ case AxiosError.ERR_BAD_OPTION_VALUE:
+ case AxiosError.ERR_CANCELED:
+ newState = 'waiting';
+ break;
+ case AxiosError.ERR_DEPRECATED:
+ case AxiosError.ERR_FR_TOO_MANY_REDIRECTS:
+ case AxiosError.ERR_NETWORK:
+ case AxiosError.ETIMEDOUT:
+ newState = 'timed-out';
+ break;
+ case AxiosError.ERR_BAD_REQUEST:
+ case AxiosError.ERR_BAD_RESPONSE:
+ default:
+ newState = 'waiting';
+ break;
+ }
+ }
+
+ if (newState !== undefined && newState !== props.state) {
+ switch (newState) {
+ case 'timed-out':
+ case 'waiting':
+ props.onChange(newState);
+ // setState(newState);
+ break;
+ default:
+ if (elapsedTime >= timeout) {
+ newState = 'timed-out';
+ stopPolling(newState);
+ }
+ break;
+ case 'completed':
+ stopPolling(newState);
+ break;
+ }
+ }
+ elapsedTime += 1000; // Increase elapsed time by 1 second
+ };
+
+ fetchData();
+ intervalId = setInterval(fetchData, 1000);
+
+ return () => {
+ if (intervalId) {
+ clearInterval(intervalId);
+ }
+ };
+ }, [isPolling]);
+
+ const onRetry = () => {
+ props.onChange('initial');
+ // setState('initial');
+ setIsPolling(true);
+ };
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+// VerifyRegistry.defaultProps = defaultVerifyRegistryProps;
+
+export default VerifyRegistry;
diff --git a/src/Routes/WizardPage/WizardPage.tsx b/src/Routes/WizardPage/WizardPage.tsx
index a8edb05..26ca1a4 100644
--- a/src/Routes/WizardPage/WizardPage.tsx
+++ b/src/Routes/WizardPage/WizardPage.tsx
@@ -1,13 +1,15 @@
-import React, { useEffect, useState } from 'react';
+import React, { useContext, useState } from 'react';
import { ExternalLinkAltIcon } from '@patternfly/react-icons/dist/esm/icons/external-link-alt-icon';
-import { Button, Page, PageSection, PageSectionTypes, PageSectionVariants, Wizard } from '@patternfly/react-core';
+import { Button, Page, PageSection, PageSectionTypes, PageSectionVariants, Wizard, WizardStep } from '@patternfly/react-core';
import { PageHeader, PageHeaderTitle } from '@redhat-cloud-services/frontend-components/PageHeader';
import './WizardPage.scss';
import { useNavigate } from 'react-router-dom';
-import { Domain } from '../../Api/api';
+import { Domain, ResourcesApiFactory } from '../../Api/api';
+import { AppContext } from '../../AppContext';
+import { VerifyState } from './Components/VerifyRegistry/VerifyRegistry';
// Lazy load for the wizard pages
const PagePreparation = React.lazy(() => import('./Components/PagePreparation/PagePreparation'));
@@ -35,48 +37,149 @@ const initialDomain: Domain = {
* Wizard page to register a new domain into the service.
*/
const WizardPage: React.FC = () => {
+ const base_url = '/api/idmsvc/v1';
+ const resources_api = ResourcesApiFactory(undefined, base_url, undefined);
+ const appContext = useContext(AppContext);
const navigate = useNavigate();
// TODO Update the initial state into the context so that
// state can be read/write from the different pages
// into the wizard process.
- const [data] = useState(initialDomain);
+ const [data, setData] = useState(initialDomain);
+ const [verifyState, setVerifyState] = useState('initial');
- useEffect(() => {
- insights?.chrome?.appAction?.('default-page');
- }, []);
+ // FIXME Clean-up if this is not really necessary
+ // useEffect(() => {
+ // insights?.chrome?.appAction?.('default-page');
+ // }, []);
// FIXME Update the URL with the location for docs
const linkLearnMoreAbout = 'https://access.redhat.com/articles/1586893';
- // Event when Close button is clicked
+ /** Event triggered when Close button is clicked. */
const onCloseClick = () => {
// TODO Not implemented
// What happens on Cancel? (see documentation)
navigate('/domains');
};
+ /** Event triggered when Back button is clicked. */
+ const onPreviousPage = (
+ _newStep: { id?: string | number; name: React.ReactNode },
+ _prevStep: { prevId?: string | number; prevName: React.ReactNode }
+ ) => {
+ console.log('onPreviousPage fired');
+ return;
+ };
+
+ /** Event triggered when a specific page is clicked. */
+ const onGoToStep = (
+ _newStep: { id?: string | number; name: React.ReactNode },
+ _prevStep: { prevId?: string | number; prevName: React.ReactNode }
+ ) => {
+ console.log('onGoToStep fired');
+ return;
+ };
+
+ /** Event triggered when the Next button is clicked. */
+ const onNextPage = async ({ id }: WizardStep) => {
+ // FIXME Delete log
+ console.log('onNextPage fired for id=' + id);
+ if (id) {
+ if (typeof id === 'string') {
+ const [, orderIndex] = id.split('-');
+ id = parseInt(orderIndex);
+ }
+ if (id === 2) {
+ // FIXME Clean-up when the token is created into the page 1
+ // try {
+ // const response = await resources_api.createDomainToken({ domain_type: 'rhel-idm' }, undefined, undefined);
+ // const newData = response.data;
+ // appContext.wizard.setToken(newData.domain_token);
+ // appContext.wizard.setUUID(newData.domain_id);
+ // } catch (error) {
+ // // TODO Add error hanlder
+ // console.log('error noNextPage: ' + error);
+ // appContext.wizard.setToken('');
+ // appContext.wizard.setUUID('');
+ // }
+ }
+ if (id === 4) {
+ try {
+ // appContext.wizard.setTitle();
+ // appContext.wizard.setDescription(newData.domain_id);
+ const response = await resources_api.updateDomainUser(appContext.wizard.getUUID(), {
+ title: appContext.wizard.title,
+ description: appContext.wizard.description,
+ // FIXME Read value from the context
+ auto_enrollment_enabled: false,
+ });
+ const newData = response.data;
+ } catch (error) {
+ // TODO Add error hanlder
+ console.log('error noNextPage: ' + error);
+ }
+ }
+ }
+ };
+
+ const [canJumpPage1, setCanJumpPage1] = useState(true);
+ const [canJumpPage2, setCanJumpPage2] = useState(appContext.wizard.getUUID() != '' && appContext.wizard.getToken() != '');
+ const [canJumpPage3, setCanJumpPage3] = useState(appContext.wizard.getRegisteredStatus() === 'completed');
+ const [canJumpPage4, setCanJumpPage4] = useState(false);
+
+ const onToken = (token: string, domain_id: string, expiration: number) => {
+ console.log('WizardPage.OnToken fired');
+ if (token != '') {
+ setCanJumpPage2(true);
+ } else {
+ setCanJumpPage2(false);
+ }
+ };
+
+ const onVerify = (value: VerifyState, data?: Domain) => {
+ setVerifyState(value);
+ if (value === 'completed') {
+ if (data) {
+ setData(data);
+ }
+ setCanJumpPage3(true);
+ setCanJumpPage4(true);
+ } else {
+ setCanJumpPage3(false);
+ setCanJumpPage4(false);
+ }
+ };
+
+ /** Configure the wizard pages. */
const steps = [
{
// This page only display the pre-requisites
+ id: 1,
name: 'Preparation',
- component: ,
+ component: ,
+ canJumpTo: canJumpPage1,
},
{
+ id: 2,
name: 'Service registration',
- // FIXME Pass here the 'registering.domain' field from the context
- // FIXME Pass here the 'registering.token' field from the context
- component: ,
+ component: ,
+ canJumpTo: canJumpPage2,
},
{
+ id: 3,
name: 'Service details',
// FIXME Pass here the 'registering.domain' field from the context
component: ,
+ canJumpTo: canJumpPage3,
},
{
+ id: 4,
name: 'Review',
// FIXME Pass here the 'registering.domain' field from the context
component: ,
+ nextButtonText: 'Finish',
+ canJumpTo: canJumpPage4,
},
];
@@ -103,7 +206,15 @@ const WizardPage: React.FC = () => {
-
+