Skip to content

Commit

Permalink
ZKUI-389: Init the veeam configuration form UI
Browse files Browse the repository at this point in the history
  • Loading branch information
ChengYanJin committed Oct 31, 2023
1 parent 758147e commit a4fc239
Show file tree
Hide file tree
Showing 4 changed files with 251 additions and 1 deletion.
3 changes: 3 additions & 0 deletions src/react/Routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import { Locations } from './locations/Locations';
import ReauthDialog from './ui-elements/ReauthDialog';
import { useAuthGroups } from './utils/hooks';
import { useTheme } from 'styled-components';
import Configuration from './ui-elements/Veeam/VeeamConfiguration';

export const RemoveTrailingSlash = ({ ...rest }) => {
const location = useLocation();
Expand Down Expand Up @@ -181,6 +182,7 @@ function PrivateRoutes() {
<Route exact path="/create-dataservice" component={EndpointCreate} />
<Route exact path="/dataservices" component={Endpoints} />
<Route exact path="/locations" component={Locations} />
<Route path="/veeam/configuration" component={Configuration} />
<Route path="*" component={NoMatch} />
</Switch>
);
Expand Down Expand Up @@ -222,6 +224,7 @@ function Routes() {
{ path: '/accounts/:accountName/create-bucket' },
{ path: '/accounts/:accountName/workflows/create-workflow' },
{ path: '/accounts/:accountName/create-policy' },
{ path: '/veeam/configuration' },
];

const hideSideBar = doesRouteMatch(routeWithoutSideBars);
Expand Down
38 changes: 38 additions & 0 deletions src/react/ui-elements/Veeam/VeeamConfiguration.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import Configuration from './VeeamConfiguration';
import { QueryClient, QueryClientProvider } from 'react-query';

describe('Veeam Configuration UI', () => {
const selectors = {
repositoryInput: () => screen.getByLabelText(/Repository name/i),
continueButton: () => screen.getByRole('button', { name: /Continue/i }),
skipButton: () =>
screen.getByRole('button', { name: /Skip Use case configuration/i }),
title: () => screen.getByText(/Prepare ARTESCA for Veeam/i),
};
it('should be able to set the Veeam configuration', async () => {
//S
render(
<QueryClientProvider client={new QueryClient()}>
<Configuration />
</QueryClientProvider>,
);
//V
expect(selectors.title()).toBeInTheDocument();
expect(selectors.continueButton()).toBeDisabled();
expect(selectors.skipButton()).toBeEnabled();
//E
userEvent.type(selectors.repositoryInput(), 'veeam-bucket');
//V
expect(selectors.repositoryInput()).toHaveValue('veeam-bucket');
// expect Veeam version is selected
expect(screen.getByText(/Veeam 12/i)).toBeInTheDocument();
//expect the immutable backup toogle to be active
expect(screen.getByText('Active')).toBeEnabled();

await waitFor(() => {
expect(selectors.continueButton()).toBeEnabled();
});
});
});
206 changes: 206 additions & 0 deletions src/react/ui-elements/Veeam/VeeamConfiguration.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
import Joi from '@hapi/joi';
import { joiResolver } from '@hookform/resolvers/joi';
import { Form, FormGroup, FormSection, Stack, Toggle } from '@scality/core-ui';
import { Button, Input, Select } from '@scality/core-ui/dist/next';
import { useRef } from 'react';
import { Controller, useForm } from 'react-hook-form';

const schema = Joi.object({
name: Joi.string().required(),
version: Joi.string().required(),
capacity: Joi.number().required().min(1).max(1023),
capacityUnit: Joi.string().required(),
enableImmutableBackup: Joi.boolean().required(),
});

type VeeamConfiguration = {
name: string;
version: string;
capacity: string;
capacityUnit: string;
enableImmutableBackup: boolean;
};

const Configuration = () => {
const {
handleSubmit,
control,
register,
formState: { errors, isValid },
} = useForm<VeeamConfiguration>({
mode: 'all',
resolver: joiResolver(schema),
defaultValues: {
name: '',
version: 'Veeam12',
capacity: '5', //TODO: The default value will be net capacity.
capacityUnit: 'TiB',
enableImmutableBackup: true,
},
});

const onSubmit = () => {
//TODO: Create account
};
const formRef = useRef(null);
return (
<Form
onSubmit={handleSubmit(onSubmit)}
ref={formRef}
layout={{
title: 'Configure ARTESCA for your Use case',
kind: 'page',
}}
rightActions={
<Stack gap="r16">
<Button
type="button"
variant="outline"
onClick={() => {
//TODO: Go back to the landing page base on the user profile
}}
label="Skip Use case configuration"
/>
<Button
type="submit"
variant="primary"
label="Continue"
disabled={!isValid}
/>
</Stack>
}
>
<FormSection title={{ name: 'Prepare ARTESCA for Veeam' }}>
<FormGroup
id="version"
label="Veeam version"
direction="vertical"
labelHelpTooltip="TODO"
content={
<Controller
name="version"
control={control}
render={({ field: { onChange, value } }) => {
return (
<Select
id="version"
onChange={(value) => {
onChange(value);
}}
value={value}
>
<Select.Option key="Veeam11" value={'Veeam11'}>
Veeam 11
</Select.Option>
<Select.Option key="Veeam12" value={'Veeam12'}>
Veeam 12
</Select.Option>
</Select>
);
}}
></Controller>
}
></FormGroup>
<FormGroup
id="name"
label="Repository name"
direction="vertical"
required={true}
labelHelpTooltip="TODO"
error={errors.name?.message ?? ''}
content={
<Input
id="name"
type="text"
autoComplete="off"
placeholder="Veeam-repository name"
{...register('name')}
/>
}
></FormGroup>
<FormGroup
id="enableImmutableBackup"
label="Immutable backup"
direction="vertical"
help="Enables permanent, unchangeable backups of objects in bucket."
helpErrorPosition="bottom"
labelHelpTooltip="TODO"
content={
<Controller
name="enableImmutableBackup"
control={control}
render={({ field: { value, onChange } }) => {
return (
<Toggle
id="enableImmutableBackup"
name="enableImmutableBackup"
toggle={value}
label={value ? 'Active' : 'Inactive'}
onChange={(value) => {
onChange(value);
}}
/>
);
}}
></Controller>
}
></FormGroup>
<FormGroup
id="capacity"
label="Repository capacity"
direction="vertical"
error={errors.capacity?.message ?? ''}
helpErrorPosition="bottom"
labelHelpTooltip="TODO"
content={
<Stack direction="horizontal">
<Controller
name="capacity"
control={control}
render={({ field: { value, onChange } }) => {
return (
<Input
id="capacity"
type="number"
//@ts-ignore
size="1/3"
min={1}
max={1023}
value={value}
onChange={(value) => {
onChange(value);
}}
/>
);
}}
></Controller>
<Controller
name="capacityUnit"
control={control}
render={({ field: { value, onChange } }) => {
return (
<Select
id="capacityUnit"
onChange={(value) => {
onChange(value);
}}
value={value}
size="2/3"
>
<Select.Option value={'MiB'}>MiB</Select.Option>
<Select.Option value={'GiB'}>GiB</Select.Option>
<Select.Option value={'TiB'}>TiB</Select.Option>
<Select.Option value={'PiB'}>PiB</Select.Option>
</Select>
);
}}
></Controller>
</Stack>
}
></FormGroup>
</FormSection>
</Form>
);
};

export default Configuration;
5 changes: 4 additions & 1 deletion src/react/ui-elements/Veeam/VeeamWelcomeModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,16 @@ import {
import { VeeamLogo } from './VeeamLogo';
import { ArtescaLogo } from './ArtescaLogo';
import styled from 'styled-components';
import { useHistory } from 'react-router-dom';

const CustomModal = styled(Modal)`
background-color: ${(props) => props.theme.backgroundLevel1};
`;

export const VeeamWelcomeModalInternal = () => {
const [isOpen, setIsOpen] = useState<boolean>(true);
const { features } = useConfig();
const { features, basePath } = useConfig();
const history = useHistory();

if (!features.includes('Veeam')) {
return <></>;
Expand Down Expand Up @@ -46,6 +48,7 @@ export const VeeamWelcomeModalInternal = () => {
label={'Continue'}
onClick={() => {
setIsOpen(false);
history.push(`${basePath}/veeam/configuration`);
}}
/>
</Stack>
Expand Down

0 comments on commit a4fc239

Please sign in to comment.