@patternfly/react-core#Alert JavaScript Examples
The following examples show how to use
@patternfly/react-core#Alert.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: customPackage.js From edge-frontend with Apache License 2.0 | 6 votes |
showAlert = (type) => {
return (
<Alert
className="pf-u-mt-lg"
variant={type}
isInline
title={
type === 'danger'
? 'No custom repositories linked. Clear custom packages or link a repository.'
: 'Linked custom repositories were removed when these packages were added. Ensure the package list is still correct.'
}
style={{ '--pf-c-content--h4--MarginTop': 0 }}
/>
);
}
Example #2
Source File: Notifications.js From sed-frontend with Apache License 2.0 | 6 votes |
Notifications = () => {
const { notifications, removeNotification } = useNotifications();
return (
<AlertGroup isToast>
{notifications.map((notification, i) => (
<Alert
isLiveRegion
timeout={notification.timeout}
title={notification.message}
variant={notification.variant}
key={notification.key}
actionClose={
<AlertActionCloseButton
data-testid={`notification-close-btn-${i}`}
title={notification.message}
variantLabel={`${notification.variant} alert`}
onClose={() => {
removeNotification(notification.key);
if (notification?.downloadHref) {
window.URL.revokeObjectURL(notification.downloadHref);
}
}}
/>
}
/>
))}
</AlertGroup>
);
}
Example #3
Source File: ibutsu-page.js From ibutsu-server with MIT License | 6 votes |
render() {
document.title = this.props.title || 'Ibutsu';
return (
<React.Fragment>
<AlertGroup isToast>
{this.state.notifications.map((notification) => (
<Alert key={notification.key} variant={notification.type} title={notification.title} actionLinks={notification.action} isLiveRegion>
{notification.message}
</Alert>
))}
</AlertGroup>
<Page header={<IbutsuHeader eventEmitter={this.props.eventEmitter} version={this.state.version} />} sidebar={<PageSidebar nav={this.props.navigation} theme="dark" />} isManagedSidebar={true} style={{position: "relative"}}>
{this.props.children}
</Page>
</React.Fragment>
);
}
Example #4
Source File: project-edit.js From ibutsu-server with MIT License | 5 votes |
render() {
const { project, users, owner } = this.state;
return (
<React.Fragment>
<PageSection variant={PageSectionVariants.light}>
<Title headingLevel="h1" size='2xl' className="pf-c-title">
Projects / {project && project.title}
</Title>
</PageSection>
<PageSection>
{!project && <Alert variant="info" title="Loading..." />}
{project &&
<Card>
<CardBody>
<Form>
<FormGroup label="Title" isRequired fieldId="projectTitle" helperText="The project's friendly name">
<TextInput
isRequired
type="text"
id="projectTitle"
name="projectTitle"
aria-describedby="The project's friendly name"
value={project.title}
onChange={this.onProjectTitleChanged}
/>
</FormGroup>
<FormGroup label="Name" isRequired fieldId="projectName" helperText="The project's machine name">
<TextInput
isRequired
type="text"
id="projectName"
name="projectName"
aria-describedby="The project's machine name"
value={project.name}
onChange={this.onProjectNameChanged}
/>
</FormGroup>
<FormGroup fieldId="owner" label="Owner" helperText="The user who owns the project">
<Select
variant={SelectVariant.typeahead}
typeAheadAriaLabel="Select user"
onToggle={this.onOwnerToggle}
onSelect={this.onOwnerSelect}
onClear={this.onOwnerClear}
onTypeaheadInputChanged={this.onOwnerChanged}
selections={owner}
isOpen={this.state.isOwnerOpen}
aria-labelledby="owner"
placeholderText="Select user"
>
{users.map(user => (
<SelectOption key={user.id} value={userToOption(user)} description={user.email} />
))}
</Select>
</FormGroup>
<ActionGroup>
<Button
variant="primary"
ouiaId="admin-project-edit-save"
onClick={this.onSubmitClick}
>
Submit
</Button>
<Button
variant="secondary"
ouiaId="admin-project-edit-cancel"
component={(props: any) => <Link {...props} to="/admin/projects" />}
>
Cancel
</Button>
</ActionGroup>
</Form>
</CardBody>
</Card>
}
</PageSection>
</React.Fragment>
);
}
Example #5
Source File: InterfaceDetails.js From cockpit-wicked with GNU General Public License v2.0 | 5 votes |
renderError = (error) => {
if (!error) return;
return <Alert variant="warning" isInline title={error} />;
}
Example #6
Source File: forgot-password.js From ibutsu-server with MIT License | 5 votes |
render() {
const signUpForAccountMessage = (
<LoginMainFooterBandItem>
Need an account? <NavLink to="/sign-up">Sign up.</NavLink>
</LoginMainFooterBandItem>
);
const forgotCredentials = (
<LoginMainFooterBandItem>
Already registered? <NavLink to="/login">Log in.</NavLink>
</LoginMainFooterBandItem>
);
const backgroundImages = {
lg: '/images/pfbg_1200.jpg',
sm: '/images/pfbg_768.jpg',
sm2x: '/images/[email protected]',
xs: '/images/pfbg_576.jpg',
xs2x: '/images/[email protected]'
};
return (
<LoginPage
footerListVariants="inline"
brandImgSrc="/images/ibutsu-wordart-164.png"
brandImgAlt="Ibutsu"
backgroundImgSrc={backgroundImages}
backgroundImgAlt="Background image"
textContent="Ibutsu is an open source test result aggregation. Collect and display your test results, view artifacts, and monitor tests."
loginTitle="Recover your account"
loginSubtitle="Please type in your e-mail address and a reset link will be sent to it."
signUpForAccountMessage={signUpForAccountMessage}
forgotCredentials={forgotCredentials}
>
<Form>
{this.state.showAlert &&
<FormAlert>
<Alert variant={this.state.alertType} title={this.state.alertText} aria-live="polite" isInline/>
</FormAlert>
}
<FormGroup
label="Email address"
isRequired
fieldId="email"
validated={this.state.isValidEmail ? 'default' : 'error'}
helperText="The e-mail address you signed up with"
>
<TextInput
isRequired
type="email"
id="email"
name="email"
validated={this.state.isValidEmail ? 'default' : 'error'}
aria-describedby="email-helper"
value={this.state.emailValue}
onChange={this.onEmailChange}
/>
</FormGroup>
<ActionGroup>
<Button variant="primary" isBlock onClick={this.onRecoverAccountClick}>Recover account</Button>
</ActionGroup>
</Form>
</LoginPage>
);
}
Example #7
Source File: app.jsx From cockpit-certificates with GNU Lesser General Public License v2.1 | 5 votes |
render() {
const { certmongerService, startErrorMessage, cas, certs, toExpireCerts, expiredCerts } = this.state;
if (expiredCerts > 0) {
page_status.set_own({
type: "error",
title: cockpit.format(cockpit.ngettext("$0 certificate has expired",
"$0 certificates have expired",
expiredCerts), expiredCerts),
details: []
});
} else if (toExpireCerts > 0) {
page_status.set_own({
type: "warning",
title: cockpit.format(cockpit.ngettext("$0 certificate expires soon",
"$0 certificates expire soon",
toExpireCerts), toExpireCerts),
details: []
});
}
const certificatesBody = (
<CertificateList cas={cas} certs={certs} addAlert={this.addAlert} appOnValueChanged={this.onValueChanged} />
);
const emptyStateBody = (
<EmptyState service={ certmongerService }
serviceName={ CERTMONGER_SERVICE_NAME }
errorMessage={ startErrorMessage }
updateService={ () => this.updateCertmongerService() } />
);
const body = () => {
if (!certmongerService || !certmongerService.exists || !certmongerService.state || certmongerService.state !== "running")
return emptyStateBody;
return certificatesBody;
};
return (
<Page>
<PageSection variant={PageSectionVariants.light}>
{ body() }
<AlertGroup isToast>
{this.state.alerts.map((danger, index) => (
<Alert isLiveRegion
variant={AlertVariant.danger}
title={danger.title}
actionClose={
<AlertActionCloseButton variantLabel="danger alert"
onClose={() => this.removeAlert(index)} />
}
key={index}>
{_("Error message: ") + danger.message}
</Alert>
))}
</AlertGroup>
</PageSection>
</Page>
);
}
Example #8
Source File: Services.js From sed-frontend with Apache License 2.0 | 4 votes |
Services = ({
defaults,
setConfirmChangesOpen,
onChange,
isEditing,
setIsEditing,
}) => {
const initState = {
enableCloudConnector: {
value: defaults.enableCloudConnector,
isDisabled: false,
},
useOpenSCAP: { value: defaults.useOpenSCAP, isDisabled: false },
};
const [formState, setFormState] = useState(initState);
const [madeChanges, setMadeChanges] = useState(false);
const { hasAccess, isLoading } = usePermissions(
'',
[
'config-manager:activation_keys:*',
'config-manager:state:read',
'config-manager:state:write',
'config-manager:state-changes:read',
'inventory:*:read',
'playbook-dispatcher:run:read',
],
false,
true
);
const cancelEditing = () => {
setFormState(initState);
setIsEditing(false);
};
useEffect(() => {
setMadeChanges(
formState.useOpenSCAP.value !== defaults.useOpenSCAP ||
formState.enableCloudConnector.value != defaults.enableCloudConnector
);
onChange({
useOpenSCAP: formState.useOpenSCAP.value,
enableCloudConnector: formState.enableCloudConnector.value,
});
}, [formState]);
const getStatusIcon = (row) => {
if (formState[row.id].value) {
return (
<Flex style={{ color: 'var(--pf-global--success-color--200)' }}>
<FlexItem spacer={{ default: 'spacerXs' }}>
<CheckCircleIcon />
</FlexItem>
<FlexItem className="status">
<b>Enabled</b>
</FlexItem>
</Flex>
);
}
return (
<Flex style={{ color: 'var(--pf-global--default-color--300)' }}>
<FlexItem spacer={{ default: 'spacerXs' }}>
<BanIcon />
</FlexItem>
<FlexItem className="status">
<b>Disabled</b>
</FlexItem>
</Flex>
);
};
return (
<Stack hasGutter className="pf-u-p-md">
<StackItem>
<Toolbar id="toolbar-items">
<ToolbarContent>
{!isEditing && (
<ToolbarItem>
{!hasAccess ? (
<Tooltip
content={
<div>
To perform this action, you must be granted the
"System Administrator" role by your
Organization Administrator in your Setting's User
Access area.
</div>
}
>
{changeSettingsButton(isLoading, hasAccess, setIsEditing)}
</Tooltip>
) : (
changeSettingsButton(isLoading, hasAccess, setIsEditing)
)}
</ToolbarItem>
)}
{isEditing && (
<>
<ToolbarItem>
<Button
ouiaId="primary-save-button"
onClick={() => setConfirmChangesOpen(true)}
isDisabled={!madeChanges}
>
Save changes
</Button>
</ToolbarItem>
<ToolbarItem>
<Button
ouiaId="secondary-cancel-button"
onClick={() => cancelEditing()}
variant="secondary"
>
Cancel
</Button>
</ToolbarItem>
<ToolbarItem>
<Alert
variant="info"
isInline
isPlain
title="Changes will affect all systems connected with Red Hat connector"
/>
</ToolbarItem>
</>
)}
</ToolbarContent>
</Toolbar>
</StackItem>
<StackItem>
<TableComposable aria-label="Settings table">
<Thead>
<Tr>
<Th>Permission</Th>
<Th>Status</Th>
</Tr>
</Thead>
<Tbody>
{permissions.map((row) => (
<Tr key={row.name}>
<Td
dataLabel="Permission"
width={80}
style={row.secondary && { paddingLeft: 70, fontSize: 14 }}
>
<Stack>
<StackItem>
<Flex>
<FlexItem>
<b>{row.name}</b>
</FlexItem>
{row.additionalInfo && (
<FlexItem
style={{ color: 'var(--pf-global--Color--100)' }}
>
<i>{row.additionalInfo}</i>
</FlexItem>
)}
</Flex>
</StackItem>
<StackItem style={{ fontSize: 14 }}>
{row.description}
</StackItem>
{row.links && (
<StackItem className="stack-item">
<Flex>
{row.links.map((link) => (
<FlexItem key={link.name}>
<a
href={link.link}
target="_blank"
rel="noopener noreferrer"
>
{link.name}
<ExternalLinkAltIcon className="pf-u-ml-sm" />
</a>
</FlexItem>
))}
</Flex>
</StackItem>
)}
</Stack>
</Td>
{!isEditing && <Td dataLabel="Status">{getStatusIcon(row)}</Td>}
{isEditing && (
<Td dataLabel="Status">
<ToggleGroup aria-label="Default with single selectable">
<ToggleGroupItem
text="Enabled"
isSelected={formState[row.id].value}
onChange={() =>
setFormState({
...formState,
[row.id]: { ...formState[row.id], value: true },
})
}
isDisabled={formState[row.id].isDisabled}
/>
<ToggleGroupItem
text="Disabled"
isSelected={!formState[row.id].value}
onChange={() =>
setFormState({
...formState,
[row.id]: { ...formState[row.id], value: false },
})
}
isDisabled={formState[row.id].isDisabled}
/>
</ToggleGroup>
</Td>
)}
</Tr>
))}
</Tbody>
</TableComposable>
</StackItem>
</Stack>
);
}
Example #9
Source File: login.js From ibutsu-server with MIT License | 4 votes |
render() {
const socialMediaLoginContent = (
<React.Fragment>
{this.state.externalLogins.keycloak &&
<LoginMainFooterLinksItem onClick={this.onKeycloakLogin} href="#" linkComponentProps={{ 'aria-label': `Login with ${this.getKeycloakName()}`, 'title': `Login with ${this.getKeycloakName()}` }}>
{this.getKeycloakIcon()}
</LoginMainFooterLinksItem>
}
{this.state.externalLogins.google &&
<GoogleLogin
clientId={this.state.externalLogins.google.client_id}
scope={this.state.externalLogins.google.scope}
redirectUri={this.state.externalLogins.google.redirect_uri}
onSuccess={this.onGoogleLogin}
onFailure={(response) => console.error(response)}
render={renderProps => (
<LoginMainFooterLinksItem onClick={renderProps.onClick} href="#" linkComponentProps={{ 'aria-label': 'Login with Google', 'title': 'Login with Google' }}>
<GoogleIcon size="lg" />
</LoginMainFooterLinksItem>
)}
/>
}
{this.state.externalLogins.github &&
<OAuth2Login
isCrossOrigin={true}
authorizationUrl={this.state.externalLogins.github.authorization_url}
responseType="code"
clientId={this.state.externalLogins.github.client_id}
redirectUri={this.state.externalLogins.github.redirect_uri}
scope={this.state.externalLogins.github.scope}
onSuccess={this.onOAuth2Success}
onFailure={(response) => console.error(response)}
render={renderProps => (
<LoginMainFooterLinksItem onClick={renderProps.onClick} href="#" linkComponentProps={{ 'aria-label': 'Login with GitHub', 'title': 'Login with GitHub' }}>
<GithubIcon size="lg" />
</LoginMainFooterLinksItem>
)}
/>
}
{this.state.externalLogins.facebook &&
<FacebookLogin
appId={this.state.externalLogins.facebook.app_id}
onSuccess={this.onFacebookLogin}
onFail={(response) => console.error(response)}
// useRedirect={true}
dialogParams={{redirect_uri: this.state.externalLogins.facebook.redirect_uri, response_type: 'code'}}
render={(renderProps) => (
<LoginMainFooterLinksItem onClick={renderProps.onClick} href="#" linkComponentProps={{ 'aria-label': 'Login with Facebook' }}>
<FacebookIcon size="lg" />
</LoginMainFooterLinksItem>
)}
/>
}
{this.state.externalLogins.gitlab &&
<OAuth2Login
isCrossOrigin={true}
authorizationUrl={this.state.externalLogins.gitlab.authorization_url}
responseType="code"
clientId={this.state.externalLogins.gitlab.client_id}
redirectUri={this.state.externalLogins.gitlab.redirect_uri}
scope={this.state.externalLogins.gitlab.scope}
onSuccess={this.onOAuth2Success}
onFailure={(response) => console.error(response)}
render={renderProps => (
<LoginMainFooterLinksItem onClick={renderProps.onClick} href="#" linkComponentProps={{ 'aria-label': 'Login with GitLab', 'title': 'Login with GitLab' }}>
<GitlabIcon size="lg" />
</LoginMainFooterLinksItem>
)}
/>
}
</React.Fragment>
);
const signUpForAccountMessage = (
<LoginMainFooterBandItem>
Need an account? <NavLink to="/sign-up">Sign up.</NavLink>
</LoginMainFooterBandItem>
);
const forgotCredentials = (
<LoginMainFooterBandItem>
<NavLink to="/forgot-password">Forgot username or password?</NavLink>
</LoginMainFooterBandItem>
);
const loginWithUserDescription = 'Please use your e-mail address and password, or login via one of the icons below the Log In button.';
const loginWithoutUserDescription = 'Log in via one of the icons below.';
const backgroundImages = {
lg: '/images/pfbg_1200.jpg',
sm: '/images/pfbg_768.jpg',
sm2x: '/images/[email protected]',
xs: '/images/pfbg_576.jpg',
xs2x: '/images/[email protected]'
};
return (
<LoginPage
footerListVariants="inline"
brandImgSrc="/images/ibutsu-wordart-164.png"
brandImgAlt="Ibutsu"
backgroundImgSrc={backgroundImages}
backgroundImgAlt="Background image"
textContent="Ibutsu is an open source test result aggregation tool. Collect and display your test results, view artifacts, and monitor tests."
loginTitle="Log in to your account"
loginSubtitle={this.state.loginSupport.user ? loginWithUserDescription : loginWithoutUserDescription}
socialMediaLoginContent={socialMediaLoginContent}
signUpForAccountMessage={this.state.loginSupport.user ? signUpForAccountMessage : ''}
forgotCredentials={this.state.loginSupport.user ? forgotCredentials : ''}
>
{this.state.loginSupport.user &&
<Form>
<FormAlert>
{this.state.alert && this.state.alert.message &&
<Alert
variant={this.state.alert.status || 'info'}
title={this.state.alert.message}
aria-live="polite"
isInline
/>
}
</FormAlert>
<FormGroup
label="Email address"
isRequired
fieldId="email"
validated={this.state.isValidEmail ? 'default' : 'error'}
>
<TextInput
isRequired
type="email"
id="email"
name="email"
validated={this.state.isValidEmail ? 'default' : 'error'}
aria-describedby="email-helper"
value={this.state.emailValue}
onChange={this.onEmailChange}
onKeyPress={this.onEnterKeyPress}
/>
</FormGroup>
<FormGroup
label="Password"
isRequired
fieldId="password"
validated={this.state.isValidPassword ? 'default' : 'error'}
>
<InputGroup>
{!this.state.isPasswordVisible &&
<TextInput
isRequired
type="password"
id="password"
name="password"
validated={this.state.isValidPassword ? 'default' : 'error'}
aria-describedby="password-helper"
value={this.state.passwordValue}
onChange={this.onPasswordChange}
onKeyPress={this.onEnterKeyPress} />
}
{this.state.isPasswordVisible &&
<TextInput
isRequired
type="text"
id="password"
name="password"
validated={this.state.isValidPassword ? 'default' : 'error'}
aria-describedby="password-helper"
value={this.state.passwordValue}
onChange={this.onPasswordChange}
onKeyPress={this.onEnterKeyPress} />
}
<Button variant="control" aria-label="Show password" onClick={this.onPasswordVisibleClick}>
{!this.state.isPasswordVisible && <EyeIcon/>}
{this.state.isPasswordVisible && <EyeSlashIcon/>}
</Button>
</InputGroup>
</FormGroup>
<ActionGroup>
<Button
variant="primary"
isBlock
isLoading={this.state.isLoggingIn}
isDisabled={this.state.isLoggingIn}
onClick={this.onLoginButtonClick}
>
{this.state.isLoggingIn ? 'Logging in...' : 'Log In'}
</Button>
</ActionGroup>
</Form>
}
</LoginPage>
);
}
Example #10
Source File: Vulnerability.js From edge-frontend with Apache License 2.0 | 4 votes |
VulnerabilityTab = ({
deviceData,
setUpdateModal,
imageId,
setReload,
}) => {
const { params } = useRouteMatch('/inventory/:deviceId');
const { getRegistry } = useContext(RegistryContext);
const [updateCveModal, setUpdateCveModal] = useState({
isOpen: false,
imageId: null,
cveCount: 0,
});
const [CVEs, setCVEs] = useState(null);
const [newImageStatus, setNewImageStatus] = useState(null);
const [activeAlert, setActiveAlert] = useState('noAlert');
useEffect(() => {
setUpdateCveModal((prevState) => ({ ...prevState, imageId: imageId }));
}, [imageId]);
useEffect(() => {
(async () => {
if (!deviceData) {
return;
}
const id = {
id: deviceData?.ImageInfo?.Image?.ImageSetID,
};
const newImageData = await getImageSet(id);
setNewImageStatus(newImageData?.Data?.images?.[0]?.image?.Status);
})();
}, [deviceData]);
useEffect(() => {
!CVEs?.isLoading &&
!CVEs?.meta?.filter &&
setUpdateCveModal((prevState) => ({
...prevState,
cveCount: CVEs?.data?.length,
}));
setActiveAlert((prevState) =>
getActiveAlert(CVEs, deviceData, newImageStatus, imageId, prevState)
);
}, [CVEs, deviceData, newImageStatus, imageId]);
const handleUpdateImageButton = () => {
setUpdateCveModal((preState) => ({
...preState,
isOpen: true,
}));
};
const handleUpdateDeviceButton = () => {
setUpdateModal((preState) => ({
...preState,
isOpen: true,
}));
};
const alerts = {
updateImage: (
<Alert
className="pf-u-mb-md"
variant="info"
isInline
title="To remediate CVEs, update the image."
actionLinks={
<Button
className="pf-u-mt-sm"
isSmall
onClick={handleUpdateImageButton}
>
Update Image
</Button>
}
/>
),
imageBuilding: (
<Alert
className="pf-u-mb-md"
customIcon={<InProgressIcon />}
variant="info"
isInline
title="Image build in progress. Once completed, you'll need to update your device."
/>
),
updateDevice: (
<Alert
className="pf-u-mb-md"
variant="warning"
isInline
title=" Image build completed. Update device to the newest image version to remediate CVEs."
actionLinks={
<Button
className="pf-u-mt-sm"
isSmall
onClick={handleUpdateDeviceButton}
>
Update Device
</Button>
}
/>
),
systemUpdating: (
<Alert
className="pf-u-mb-md"
customIcon={<InProgressIcon />}
variant="info"
isInline
title="Device updating. No additional actions required."
/>
),
noAlert: <></>,
};
return (
<>
<Main className="add-100vh">
{alerts[activeAlert]}
<AsyncComponent
appName="vulnerability"
module="./SystemDetail"
getRegistry={getRegistry}
customIntlProvider
entity={{ id: params.deviceId }}
canSelect={false}
canEditPairStatus={false}
canManageColumns={false}
linkToCustomerPortal
defaultColumns={[
'synopsis',
'public_date',
'impact',
'cvss_score',
'advisory',
]}
filters={[
'filter',
'security_rule',
'known_exploit',
'impact',
'cvss_score',
'advisory',
]}
customAction={(cve) => {
setCVEs(cve);
}}
/>
</Main>
{updateCveModal.isOpen && (
<UpdateImageModal
updateCveModal={updateCveModal}
setUpdateCveModal={setUpdateCveModal}
setReload={setReload}
/>
)}
</>
);
}
Example #11
Source File: user-edit.js From ibutsu-server with MIT License | 4 votes |
render() {
const { user, projects, userProjects } = this.state;
return (
<React.Fragment>
<PageSection variant={PageSectionVariants.light}>
<Title headingLevel="h1" size='2xl' className="pf-c-title">
Users / {user && user.name} {' '}
{user && user.is_superadmin &&
<Label className="super-admin-label" variant="outline" color="blue">Administrator</Label>
}
</Title>
</PageSection>
<PageSection>
{!user && <Alert variant="info" title="Loading..." />}
{user &&
<Card>
<CardBody>
<Form>
<FormGroup label="Name" isRequired fieldId="userName" helperText="The user's name">
<TextInput
isRequired
type="text"
id="userName"
name="userName"
aria-describedby="The user's name"
value={user.name}
onChange={this.onUserNameChanged}
/>
</FormGroup>
<FormGroup label="E-mail" isRequired fieldId="userEmail" helperText="The user's e-mail address">
<TextInput
isRequired
type="email"
id="userEmail"
name="userEmail"
aria-describedby="The user's e-mail address"
value={user.email}
onChange={this.onUserEmailChanged}
/>
</FormGroup>
<FormGroup fieldId="userStatus" label="User status">
<Checkbox
label="Is active"
id="userIsActive"
name="userIsActive"
aria-label="User is active"
isChecked={user.is_active}
onChange={this.onIsActiveToggle}
/>
<Checkbox
label="Is administrator"
id="userIsAdmin"
name="userIsAdmin"
aria-label="User is administrator"
isChecked={user.is_superadmin}
onChange={this.onIsAdminToggle}
/>
</FormGroup>
<FormGroup fieldId="userProjects" label="Projects" helperText="The projects to which a user has access">
<Select
variant={SelectVariant.typeaheadMulti}
typeAheadAriaLabel="Select one or more projects"
onToggle={this.onProjectsToggle}
onSelect={this.onProjectsSelect}
onClear={this.onProjectsClear}
selections={userProjects}
isOpen={this.state.isProjectsOpen}
aria-labelledby="userProjects"
placeholderText="Select one or more projects"
>
{projects.map(project => (
<SelectOption key={project.id} value={projectToOption(project)} description={project.name} />
))}
</Select>
</FormGroup>
<ActionGroup>
<Button variant="primary" onClick={this.onSubmitClick}>Submit</Button>
<Button variant="secondary" onClick={this.props.history.goBack}>Cancel</Button>
</ActionGroup>
</Form>
</CardBody>
</Card>
}
</PageSection>
</React.Fragment>
);
}
Example #12
Source File: user.js From ibutsu-server with MIT License | 4 votes |
render() {
const { user, projects } = this.state;
let projectInfo = [];
// create the project rows
if (projects && user) {
projectInfo.push(projects.map((project) => (
<DataListCell key={project.name} className="pf-u-p-sm">
<span> {project.title} </span>
{project.owner_id === user.id &&
<Label className="project-owner-label" variant="filled" color="green" isCompact>Owner</Label>
}
</DataListCell>
)))
}
return (
<React.Fragment>
<PageSection variant={PageSectionVariants.light}>
<Title headingLevel="h1" size='2xl' className="pf-c-title">
<React.Fragment>
<span> Profile </span>
{user && user.is_superadmin &&
<Label className="super-admin-label" variant="filled" color="blue">Administrator</Label>
}
</React.Fragment>
</Title>
</PageSection>
<PageSection>
{!user && <Alert variant="danger" title="Error fetching user details" />}
{user &&
<DataList selectedDataListItemId={null} aria-label="User profile">
<DataListItem aria-labelledby="Name">
<DataListItemRow>
{!this.state.isEditing &&
<DataListItemCells
dataListCells={[
<DataListCell key={1} width={2}><strong>Name:</strong></DataListCell>,
<DataListCell key={2} width={4}>{user.name} <Button variant="link" icon={<PencilAltIcon />} onClick={this.onEditButtonClicked} isInline isSmall ouiaId="edit-profile-button">Edit</Button></DataListCell>
]}
/>
}
{this.state.isEditing &&
<DataListItemCells
dataListCells={[
<DataListCell key={1} width={2}><strong>Name:</strong></DataListCell>,
<DataListCell key={2} width={4}>
<InputGroup>
<TextInput value={this.state.tempName} type="text" onChange={value => this.setState({tempName: value})} aria-label="User name" />
<Button variant="control" icon={<CheckIcon />} onClick={this.onSaveButtonClicked} ouiaId="edit-save-button">Save</Button>
<Button variant="control" icon={<TimesIcon />} onClick={this.onCancelButtonClicked} ouiaId="edit-cancel-button">Cancel</Button>
</InputGroup>
</DataListCell>
]}
/>
}
</DataListItemRow>
</DataListItem>
<DataListItem aria-labelledby="E-mail">
<DataListItemRow>
<DataListItemCells
dataListCells={[
<DataListCell key={1} width={2}><strong>E-mail:</strong></DataListCell>,
<DataListCell key={2} width={4}>{user.email}</DataListCell>
]}
/>
</DataListItemRow>
</DataListItem>
<DataListItem aria-labelledby="Projects">
<DataListItemRow>
<DataListItemCells
dataListCells={[
<DataListCell key={1} width={2}><strong>My Projects:</strong></DataListCell>,
<DataListCell key={2} width={4} style={{paddingTop: 0, paddingBottom: 0}}>
<DataList aria-lable="projects" style={{borderTop: "none"}}>
{projects &&
projectInfo
}
</DataList>
</DataListCell>
]}
/>
</DataListItemRow>
</DataListItem>
</DataList>
}
</PageSection>
</React.Fragment>
);
}
Example #13
Source File: reset-password.js From ibutsu-server with MIT License | 4 votes |
render() {
const loginMessage = (
<LoginMainFooterBandItem>
Already registered? <NavLink to="/login">Log in.</NavLink>
</LoginMainFooterBandItem>
);
const forgotCredentials = (
<LoginMainFooterBandItem>
<NavLink to="/forgot-password">Forgot username or password?</NavLink>
</LoginMainFooterBandItem>
);
const backgroundImages = {
lg: '/images/pfbg_1200.jpg',
sm: '/images/pfbg_768.jpg',
sm2x: '/images/[email protected]',
xs: '/images/pfbg_576.jpg',
xs2x: '/images/[email protected]'
};
return (
<LoginPage
footerListVariants="inline"
brandImgSrc="/images/ibutsu-wordart-164.png"
brandImgAlt="Ibutsu"
backgroundImgSrc={backgroundImages}
backgroundImgAlt="Background image"
textContent="Ibutsu is an open source test result aggregation. Collect and display your test results, view artifacts, and monitor tests."
loginTitle="Reset your password"
loginSubtitle="Please type in a secure password"
signUpForAccountMessage={loginMessage}
forgotCredentials={forgotCredentials}
>
<Form>
{this.state.showAlert &&
<FormAlert>
<Alert variant={this.state.alertType} title={this.state.alertText} aria-live="polite" isInline/>
</FormAlert>
}
<FormGroup
label="Password"
isRequired
fieldId="password"
validated={this.state.isValidPassword ? 'default' : 'error'}
>
<InputGroup>
{!this.state.isPasswordVisible &&
<TextInput
isRequired
type="password"
id="password"
name="password"
validated={this.state.isValidPassword ? 'default' : 'error'}
aria-describedby="password-helper"
value={this.state.passwordValue}
onChange={this.onPasswordChange} />
}
{this.state.isPasswordVisible &&
<TextInput
isRequired
type="text"
id="password"
name="password"
validated={this.state.isValidPassword ? 'default' : 'error'}
aria-describedby="password-helper"
value={this.state.passwordValue}
onChange={this.onPasswordChange} />}
<Button variant="control" aria-label="Show password" onClick={this.onPasswordVisibleClick}>
{!this.state.isPasswordVisible && <EyeIcon/>}
{this.state.isPasswordVisible && <EyeSlashIcon/>}
</Button>
</InputGroup>
<PasswordErrorBoundary>
<Suspense fallback={""}>
<PasswordStrengthBar password={this.state.passwordValue}/>
</Suspense>
</PasswordErrorBoundary>
</FormGroup>
<FormGroup
label="Confirm password"
isRequired
fieldId="confirm-password"
helperText={this.state.confirmPasswordHelpText}
helperTextInvalid="Passwords do not match"
validated={this.state.confirmPasswordValidation}
>
<InputGroup>
{!this.state.isConfirmPasswordVisible && <TextInput isRequired type="password" id="confirm-password" name="confirm-password" aria-describedby="confirm-password-helper" value={this.state.confirmPasswordValue} onChange={this.onConfirmPasswordChange} validated={this.state.confirmPasswordValidation} />}
{this.state.isConfirmPasswordVisible && <TextInput isRequired type="text" id="confirm-password" name="confirm-password" aria-describedby="confirm-password-helper" value={this.state.confirmPasswordValue} onChange={this.onConfirmPasswordChange} validated={this.state.confirmPasswordValidation} />}
<Button variant="control" aria-label="Show password" onClick={this.onConfirmPasswordVisibleClick}>
{!this.state.isConfirmPasswordVisible && <EyeIcon/>}
{this.state.isConfirmPasswordVisible && <EyeSlashIcon/>}
</Button>
</InputGroup>
</FormGroup>
<ActionGroup>
<Button variant="primary" isBlock onClick={this.onResetButtonClick}>Reset Password</Button>
</ActionGroup>
</Form>
</LoginPage>
);
}
Example #14
Source File: sign-up.js From ibutsu-server with MIT License | 4 votes |
render() {
const loginMessage = (
<LoginMainFooterBandItem>
Already registered? <NavLink to="/login">Log in.</NavLink>
</LoginMainFooterBandItem>
);
const forgotCredentials = (
<LoginMainFooterBandItem>
<NavLink to="/forgot-password">Forgot username or password?</NavLink>
</LoginMainFooterBandItem>
);
const backgroundImages = {
lg: '/images/pfbg_1200.jpg',
sm: '/images/pfbg_768.jpg',
sm2x: '/images/[email protected]',
xs: '/images/pfbg_576.jpg',
xs2x: '/images/[email protected]'
};
return (
<LoginPage
footerListVariants="inline"
brandImgSrc="/images/ibutsu-wordart-164.png"
brandImgAlt="Ibutsu"
backgroundImgSrc={backgroundImages}
backgroundImgAlt="Background image"
textContent="Ibutsu is an open source test result aggregation. Collect and display your test results, view artifacts, and monitor tests."
loginTitle="Register a new account"
loginSubtitle="Please type in your e-mail address and a secure password"
signUpForAccountMessage={loginMessage}
forgotCredentials={forgotCredentials}
>
<Form>
{this.state.showAlert &&
<FormAlert>
<Alert variant={this.state.alertType} title={this.state.alertText} aria-live="polite" isInline/>
</FormAlert>
}
<FormGroup
label="Email address"
isRequired
fieldId="email"
validated={this.state.isValidEmail ? 'default' : 'error'}
helperText="The e-mail address you want to use to log in"
>
<TextInput
isRequired
type="email"
id="email"
name="email"
validated={this.state.isValidEmail ? 'default' : 'error'}
aria-describedby="email-helper"
value={this.state.emailValue}
onChange={this.onEmailChange}
/>
</FormGroup>
<FormGroup
label="Password"
isRequired
fieldId="password"
validated={this.state.isValidPassword ? 'default' : 'error'}
>
<InputGroup>
{!this.state.isPasswordVisible &&
<TextInput
isRequired
type="password"
id="password"
name="password"
validated={this.state.isValidPassword ? 'default' : 'error'}
aria-describedby="password-helper"
value={this.state.passwordValue}
onChange={this.onPasswordChange} />
}
{this.state.isPasswordVisible &&
<TextInput
isRequired
type="text"
id="password"
name="password"
validated={this.state.isValidPassword ? 'default' : 'error'}
aria-describedby="password-helper"
value={this.state.passwordValue}
onChange={this.onPasswordChange} />}
<Button variant="control" aria-label="Show password" onClick={this.onPasswordVisibleClick}>
{!this.state.isPasswordVisible && <EyeIcon/>}
{this.state.isPasswordVisible && <EyeSlashIcon/>}
</Button>
</InputGroup>
<PasswordErrorBoundary>
<Suspense fallback={""}>
<PasswordStrengthBar password={this.state.passwordValue}/>
</Suspense>
</PasswordErrorBoundary>
</FormGroup>
<FormGroup
label="Confirm password"
isRequired
fieldId="confirm-password"
helperText={this.state.confirmPasswordHelpText}
helperTextInvalid="Passwords do not match"
validated={this.state.confirmPasswordValidation}
>
<InputGroup>
{!this.state.isConfirmPasswordVisible && <TextInput isRequired type="password" id="confirm-password" name="confirm-password" aria-describedby="confirm-password-helper" value={this.state.confirmPasswordValue} onChange={this.onConfirmPasswordChange} validated={this.state.confirmPasswordValidation} />}
{this.state.isConfirmPasswordVisible && <TextInput isRequired type="text" id="confirm-password" name="confirm-password" aria-describedby="confirm-password-helper" value={this.state.confirmPasswordValue} onChange={this.onConfirmPasswordChange} validated={this.state.confirmPasswordValidation} />}
<Button variant="control" aria-label="Show password" onClick={this.onConfirmPasswordVisibleClick}>
{!this.state.isConfirmPasswordVisible && <EyeIcon/>}
{this.state.isConfirmPasswordVisible && <EyeSlashIcon/>}
</Button>
</InputGroup>
</FormGroup>
<ActionGroup>
<Button variant="primary" isBlock onClick={this.onRegisterButtonClick}>Register</Button>
</ActionGroup>
</Form>
</LoginPage>
);
}
Example #15
Source File: DnsSettingsForm.js From cockpit-wicked with GNU General Public License v2.0 | 4 votes |
DnsSettingsForm = ({ isOpen, onClose, dns }) => {
const dispatch = useNetworkDispatch();
const [nameserver1, setNameserver1] = useState(dns.nameServers[0]);
const [nameserver2, setNameserver2] = useState(dns.nameServers[1]);
const [nameserver3, setNameserver3] = useState(dns.nameServers[2]);
const [policy, setPolicy] = useState(dns.policy);
const [searchListInput, setSearchListInput] = useState(dns.searchList.join(" "));
const [errorMessages, setErrorMessages] = useState([]);
const validate = () => {
const errors = [];
// Clean previous error messages
setErrorMessages([]);
if (searchList().some((d) => !isValidDomain(d))) {
errors.push({
key: 'invalid-searchlist',
message: _("There are invalid domains in the search list.")
});
setSearchListInput(searchList().join(" "));
}
if (nameServers().some((s) => !isValidIP(s))) {
errors.push({
key: 'invalid-nameservers',
message: _("There are invalid name servers.")
});
}
setErrorMessages(errors);
return (errors.length == 0);
};
const searchList = () => searchListInput.split(" ").filter(Boolean);
const nameServers = () => [nameserver1, nameserver2, nameserver3].filter(Boolean);
const configChanged = () => {
return !deep_equal(dns, createDnsSettings({ policy, searchList: searchList(), nameServers: nameServers() }));
};
const handleSubmit = () => {
if (!validate()) return false;
if (configChanged()) {
updateDnsSettings(dispatch, { nameServers: nameServers(), policy, searchList: searchList() });
}
onClose();
};
/**
* Renders error messages in an Patternfly/Alert component, if any
*/
const renderErrors = () => {
if (errorMessages.length === 0) return null;
return (
<Alert
isInline
variant="danger"
aria-live="polite"
title={_("Data is not valid, please check it")}
>
{errorMessages.map(({ key, message }) => <p key={key}>{message}</p>)}
</Alert>
);
};
const handleError = (value) => console.log("Invalid value", value, "for nameserver 1");
return (
<ModalForm
title={_("DNS Settings")}
isOpen={isOpen}
onCancel={onClose}
onSubmit={handleSubmit}
>
{renderErrors()}
<FormGroup
label={_("Policy")}
fieldId="dns_policy"
helperText={_("Defines the DNS merge policy as documented in netconfig(8) manual page.")}
>
<TextInput
isRequired
id="dns_policy"
value={policy}
onChange={setPolicy}
/>
</FormGroup>
<FormGroup
label={_("Search List")}
isRequired
fieldId="dns_search_list"
helperText={_("Space separated list of DNS domain names used for host-name lookup")}
>
<TextInput
id="dns_search_list"
placeholder={_("example.com another.com")}
value={searchListInput}
onChange={setSearchListInput}
/>
</FormGroup>
<FormGroup
label={_("Static Name Servers")}
helperText={_("Name Server IP address used for host-name lookup.")}
>
<IPInput
id="dns_nameserver_one"
onChange={setNameserver1}
placeholder={_("Nameserver IP")}
defaultValue={nameserver1}
onError={handleError}
/>
</FormGroup>
<FormGroup>
<IPInput
id="dns_nameserver_two"
placeholder={_("Name Server IP")}
defaultValue={nameserver2}
onChange={setNameserver2}
onError={handleError}
/>
</FormGroup>
<FormGroup>
<IPInput
id="dns_nameserver_three"
placeholder={_("Name Server IP")}
defaultValue={nameserver3}
onChange={setNameserver3}
onError={handleError}
/>
</FormGroup>
</ModalForm>
);
}
Example #16
Source File: IPSettingsForm.js From cockpit-wicked with GNU General Public License v2.0 | 4 votes |
IPSettingsForm = ({ connection, ipVersion = 'ipv4', isOpen, onClose }) => {
const dispatch = useNetworkDispatch();
const settings = connection[ipVersion];
const [bootProto, setBootProto] = useState(settings.bootProto);
const [addresses, setAddresses] = useState(settings.addresses);
const [addressRequired, setAddressRequired] = useState(settings.bootProto === bootProtocol.STATIC);
const [errorMessages, setErrorMessages] = useState([]);
/**
* Performs an update of the internal addresses state
*
* When the "Static" boot protocol is selected, it ensures that there is at least one {@link
* module:/model~AddressConfig} in the collection, which helps displaying needed fields in the
* UI.
*
* @param {Array<module:model~AddressConfig>} [nextAddresses] - Addresses to be used for the
* update. When not given, current addresses will be used.
*/
const forceAddressesUpdate = useCallback((nextAddresses) => {
nextAddresses ||= addresses;
if (bootProto === bootProtocol.STATIC && nextAddresses.length === 0) {
nextAddresses = [createAddressConfig()];
}
setAddresses(nextAddresses);
}, [addresses, bootProto]);
/**
* Performs validations using given addresses
*
* @param {Array<module:model~AddressConfig>} sanitizedAddresses - a collection of sanitize
* addresses. See {@link sanitize}
* @return {boolean} true when all validations success; false otherwise
*/
const validate = (sanitizedAddresses) => {
/**
* TODO: improve validations
* TODO: highlight addresses with errors?
*/
let result = true;
const errors = [];
// Clean previous error messages
setErrorMessages([]);
if (bootProto === bootProtocol.STATIC && sanitizedAddresses.length === 0) {
result = false;
errors.push({
key: 'static-address-required',
message: format(
_('At least one address must be provided when using the "$bootProto" boot protocol'),
{ bootProto: bootProtocol.label(bootProtocol.STATIC) }
)
});
}
if (findInvalidIP(sanitizedAddresses)) {
result = false;
errors.push({
key: 'invalid-ips',
message: _("There are invalid IPs")
});
}
if (findRepeatedLabel(sanitizedAddresses)) {
result = false;
errors.push({
key: 'repeated-labels',
message: _("There are repeated labels")
});
}
setErrorMessages(errors);
return result;
};
/**
* Handles the form submit, performing a connection update when proceed
*
* @see {@link validate}
* @see {@link module/context/network~updateConnection}
*/
const handleSubmit = () => {
const sanitizedAddresses = sanitize(addresses);
// Do not proceed if errors were found
if (!validate(sanitizedAddresses)) {
forceAddressesUpdate(sanitizedAddresses);
return;
}
// If everything looks good, send requested changes and close
updateConnection(
dispatch,
connection,
{ [ipVersion]: { bootProto, addresses: sanitizedAddresses } }
);
onClose();
};
/**
* Updates the UI according to the bootProtocol selected
*
* Basically, setting the internal form state in order to ensure that the AddressDataList
* component displays the fields for at least one {@link module/model~AddressConfig} item.
*/
useEffect(() => {
forceAddressesUpdate();
setAddressRequired(bootProto === bootProtocol.STATIC);
}, [forceAddressesUpdate, bootProto]);
/**
* Renders error messages in an Patternfly/Alert component, if any
*/
const renderErrors = () => {
if (errorMessages.length === 0) return null;
return (
<Alert
isInline
variant="danger"
aria-live="polite"
title={_("Data is not valid, please check it")}
>
{errorMessages.map(({ key, message }) => <p key={key}>{message}</p>)}
</Alert>
);
};
return (
<ModalForm
caption={connection.name}
title={_(`${ipVersion.toUpperCase()} Settings`)}
isOpen={isOpen}
onSubmit={handleSubmit}
onCancel={onClose}
variant={ModalVariant.medium}
>
{renderErrors()}
<FormGroup label={_("Boot Protocol")} isRequired>
<BootProtoSelector value={bootProto} onChange={setBootProto} />
</FormGroup>
<FormGroup label={_("Addresses")}>
<AddressesDataList
addresses={addresses}
updateAddresses={setAddresses}
allowEmpty={!addressRequired}
/>
</FormGroup>
</ModalForm>
);
}
Example #17
Source File: ReviewStep.js From edge-frontend with Apache License 2.0 | 4 votes |
ReviewStep = () => {
const { getState } = useFormApi();
const isUpdate = getState().initialValues.isUpdate;
const { getRegistry } = useContext(RegistryContext);
const { isLoading, hasError } = useSelector(
({ createImageReducer }) => ({
isLoading:
createImageReducer?.isLoading !== undefined
? createImageReducer?.isLoading
: false,
hasError: createImageReducer?.hasError || false,
error: createImageReducer?.error || null,
}),
shallowEqual
);
useEffect(() => {
const registered = getRegistry().register({ createImageReducer });
return () => registered();
}, []);
if (isLoading) {
return (
<Bullseye>
<Spinner />
</Bullseye>
);
}
const details = [
{ name: 'Name', value: getState().values.name },
{ name: 'Version', value: String(getState().initialValues.version + 1) },
{ name: 'Description', value: getState().values.description },
];
const output = () => {
let outputs = [
{ name: 'Release', value: releaseMapper[getState().values.release] },
{
name: 'Output Type',
value: imageTypeMapper['rhel-edge-commit'],
},
];
if (getState().values.imageType.includes('rhel-edge-installer')) {
outputs.push({ name: '', value: imageTypeMapper['rhel-edge-installer'] });
}
return outputs;
};
const registration = [
{ name: 'Username', value: getState().values['username'] },
{ name: 'ssh-key', value: getState().values.credentials },
];
const RHELPackageBefore = getState().initialValues['selected-packages'] || [];
const RHELPackageAfter = getState().values['selected-packages'] || [];
const customPackageBefore = getState().initialValues['custom-packages'] || [];
const customPackageAfter = getState().values['custom-packages'] || [];
const calcPkgDiff = (arr1, arr2) =>
arr1.reduce(
(acc, { name }) => acc + (!arr2.some((pkg) => pkg.name === name) ? 1 : 0),
0
);
const packages = () => {
const pkgs = [
getState().values?.includesCustomRepos && {
name: isUpdate ? 'Custom Updated' : 'Custom Added',
value: String(calcPkgDiff(customPackageAfter, customPackageBefore)),
},
{
name: isUpdate ? 'RHEL Updated' : 'RHEL Added',
value: String(calcPkgDiff(RHELPackageAfter, RHELPackageBefore)),
},
];
return pkgs;
};
return (
<Fragment>
{hasError && (
<Alert
variant="danger"
title="Failed sending the request: Edge API is not available"
/>
)}
<TextContent>
<Text>
Review the information and click{' '}
<Text component={'b'}>Create image</Text> to start the build process.
</Text>
<ReviewSection
title={'Details'}
data={details}
testid={'review-image-details'}
/>
<ReviewSection
title={'Output'}
data={output()}
testid={'review-image-output'}
/>
{getState().values.imageType.includes('rhel-edge-installer') ? (
<ReviewSection
title={'Registration'}
data={registration}
testid={'review-image-registration'}
/>
) : null}
<ReviewSection
title={'Packages'}
data={packages()}
testid={'review-image-packages'}
/>
</TextContent>
</Fragment>
);
}
Example #18
Source File: RouteForm.js From cockpit-wicked with GNU General Public License v2.0 | 4 votes |
RouteForm = ({ isOpen, onClose, route }) => {
const isEditing = !!route;
const [isDefault, setIsDefault] = useState(route?.isDefault || false);
const [gateway, setGateway] = useState(route?.gateway || "");
const [destination, setDestination] = useState(route?.destination || "");
const [device, setDevice] = useState(route?.device || "");
const [options, setOptions] = useState(route?.options || "");
const [errors, setErrors] = useState([]);
const { interfaces, routes } = useNetworkState();
const [candidateInterfaces, setCandidateInterfaces] = useState([]);
const dispatch = useNetworkDispatch();
useEffect(() => {
setCandidateInterfaces([{ name: "" }, ...Object.values(interfaces)]);
}, [interfaces]);
/**
* Performs the form validations
*
* To be considered a valid form both, destination and gateway must be valid IPs values. There
* is only an exception for destination, which can be "default" too.
*
* @return {boolean} true when route is valid; false otherwise
*/
const validate = () => {
const errors = [];
if (!isDefault && !isValidIP(destination)) {
errors.push({
key: 'invalid-destination',
message: _("Destination is not valid.")
});
}
if (!isValidIP(gateway)) {
errors.push({
key: 'invalid-gateway',
message: _("Gateway is not valid.")
});
}
setErrors(errors);
return errors.length === 0;
};
const addOrUpdateRoute = () => {
if (!validate()) return;
if (isEditing) {
updateRoute(dispatch, routes, route.id, buildRouteData());
} else {
addRoute(dispatch, routes, buildRouteData());
}
onClose();
};
const buildRouteData = () => {
return {
isDefault,
destination: isDefault ? "default" : destination,
gateway,
device,
options
};
};
const isIncomplete = () => {
if (!isDefault && destination.length == 0) return true;
if (gateway.length == 0) return true;
return false;
};
/**
* Renders error messages in an Patternfly/Alert component, if any
*/
const renderErrors = () => {
if (errors.length === 0) return null;
return (
<Alert
isInline
variant="danger"
aria-live="polite"
title={_("Route is not valid, please check it.")}
>
{errors.map(({ key, message }) => <p key={key}>{message}</p>)}
</Alert>
);
};
/**
* Renders the destination input only when needed (i.e., route is not marked as a default)
*/
const renderDestination = () => {
if (isDefault) return null;
return (
<FormGroup
isRequired
label={_("Destination")}
fieldId="destination"
helperText={_("Destination")}
>
<TextInput
isRequired
id="destination"
value={destination}
onChange={setDestination}
/>
</FormGroup>
);
};
return (
<ModalForm
title={isEditing ? _("Edit Route") : _("Add Route")}
isOpen={isOpen}
onCancel={onClose}
onSubmit={addOrUpdateRoute}
onSubmitLabel={isEditing ? _("Change") : _("Add")}
onSubmitDisable={isIncomplete()}
>
{renderErrors()}
<FormGroup
label={_("Default route")}
fieldId="isDefault"
>
<Checkbox
id="isDefault"
isChecked={isDefault}
onChange={setIsDefault}
/>
</FormGroup>
{renderDestination()}
<FormGroup
isRequired
label={_("Gateway")}
fieldId="gateway"
>
<TextInput
isRequired
id="gateway"
value={gateway}
onChange={setGateway}
/>
</FormGroup>
<FormGroup
label={_("Device")}
fieldId="device"
>
<FormSelect value={device} onChange={setDevice} id="device">
{candidateInterfaces.map(({ name }, index) => (
<FormSelectOption key={index} value={name} label={name} />
))}
</FormSelect>
</FormGroup>
<FormGroup
label={_("Options")}
fieldId="options"
>
<TextInput
id="options"
value={options}
onChange={setOptions}
/>
</FormGroup>
</ModalForm>
);
}