Skip to content

Commit

Permalink
style: add user modal (#313)
Browse files Browse the repository at this point in the history
  • Loading branch information
filipslezaklab committed Aug 21, 2023
1 parent bbbbfaf commit 9be556d
Show file tree
Hide file tree
Showing 20 changed files with 481 additions and 354 deletions.
5 changes: 4 additions & 1 deletion web/src/i18n/en/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,9 @@ const en: BaseTranslation = {
},
addUser: {
title: 'Add new user',
messages: {
userAdded: 'User added',
},
form: {
submit: 'Add user',
fields: {
Expand Down Expand Up @@ -346,7 +349,7 @@ const en: BaseTranslation = {
label: 'Phone',
},
enableEnrollment: {
label: 'Use remote enrollment',
label: 'Use enrollment process',
},
},
},
Expand Down
16 changes: 14 additions & 2 deletions web/src/i18n/i18n-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -657,6 +657,12 @@ type RootTranslation = {
* A​d​d​ ​n​e​w​ ​u​s​e​r
*/
title: string
messages: {
/**
* U​s​e​r​ ​a​d​d​e​d
*/
userAdded: string
}
form: {
/**
* A​d​d​ ​u​s​e​r
Expand Down Expand Up @@ -725,7 +731,7 @@ type RootTranslation = {
}
enableEnrollment: {
/**
* U​s​e​ ​r​e​m​o​t​e​ ​e​n​r​o​l​l​m​e​n​t
* U​s​e​ ​e​n​r​o​l​l​m​e​n​t​ ​p​r​o​c​e​s​s
*/
label: string
}
Expand Down Expand Up @@ -3926,6 +3932,12 @@ export type TranslationFunctions = {
* Add new user
*/
title: () => LocalizedString
messages: {
/**
* User added
*/
userAdded: () => LocalizedString
}
form: {
/**
* Add user
Expand Down Expand Up @@ -3994,7 +4006,7 @@ export type TranslationFunctions = {
}
enableEnrollment: {
/**
* Use remote enrollment
* Use enrollment process
*/
label: () => LocalizedString
}
Expand Down
3 changes: 3 additions & 0 deletions web/src/i18n/pl/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,9 @@ const pl: Translation = {
},
},
addUser: {
messages: {
userAdded: 'Stworzono użytkownika',
},
title: 'Dodaj nowego użytkownika',
form: {
submit: 'Dodaj użytkownika',
Expand Down
10 changes: 4 additions & 6 deletions web/src/pages/users/UsersOverview/UsersOverview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,12 @@ import {
SelectSelectedValue,
SelectSizeVariant,
} from '../../../shared/defguard-ui/components/Layout/Select/types';
import { useModalStore } from '../../../shared/hooks/store/useModalStore';
import useApi from '../../../shared/hooks/useApi';
import { QueryKeys } from '../../../shared/queries';
import { User } from '../../../shared/types';
import { UsersList } from './components/UsersList/UsersList';
import AddUserModal from './modals/AddUserModal/AddUserModal';
import { StartEnrollmentModal } from './modals/StartEnrollmentModal/StartEnrollmentModal';
import { AddUserModal } from './modals/AddUserModal/AddUserModal';
import { useAddUserModal } from './modals/AddUserModal/hooks/useAddUserModal';

enum FilterOptions {
ALL = 'all',
Expand Down Expand Up @@ -83,7 +82,7 @@ export const UsersOverview = () => {

const [usersSearchValue, setUsersSearchValue] = useState('');

const setUserAddModalState = useModalStore((state) => state.setAddUserModal);
const openAddUserModal = useAddUserModal((state) => state.open);

const filteredUsers = useMemo(() => {
if (!users || (users && !users.length)) {
Expand Down Expand Up @@ -161,7 +160,7 @@ export const UsersOverview = () => {
)}
<Button
className="add-item"
onClick={() => setUserAddModalState({ visible: true })}
onClick={openAddUserModal}
size={ButtonSize.SMALL}
styleVariant={ButtonStyleVariant.PRIMARY}
icon={<SvgIconUserAddNew />}
Expand All @@ -188,7 +187,6 @@ export const UsersOverview = () => {
</div>
)}
<AddUserModal />
<StartEnrollmentModal />
</section>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { useAuthStore } from '../../../../../shared/hooks/store/useAuthStore';
import { useModalStore } from '../../../../../shared/hooks/store/useModalStore';
import { useUserProfileStore } from '../../../../../shared/hooks/store/useUserProfileStore';
import { User } from '../../../../../shared/types';
import { useEnrollmentModalStore } from '../../modals/StartEnrollmentModal/hooks/useEnrollmentModalStore';
import { useAddUserModal } from '../../modals/AddUserModal/hooks/useAddUserModal';

type Props = {
user: User;
Expand All @@ -20,16 +20,22 @@ export const UserEditButton = ({ user }: Props) => {
const setProvisionKeyModal = useModalStore((state) => state.setProvisionKeyModal);
const setDeleteUserModal = useModalStore((state) => state.setDeleteUserModal);
const setChangePasswordModal = useModalStore((state) => state.setChangePasswordModal);
const openEnrollmentModal = useEnrollmentModalStore((state) => state.open);
const setUserProfile = useUserProfileStore((state) => state.setState);
const setAddUserModal = useAddUserModal((state) => state.setState);
const currentUser = useAuthStore((state) => state.user);
return (
<EditButton>
{!user.is_active && (
<EditButtonOption
key="start-enrollment"
text={LL.usersOverview.list.editButton.startEnrollment()}
onClick={() => openEnrollmentModal(user)}
onClick={() =>
setAddUserModal({
visible: true,
step: 1,
user: user,
})
}
/>
)}
<EditButtonOption
Expand Down
Original file line number Diff line number Diff line change
@@ -1,32 +1,43 @@
import './style.scss';

import { ReactNode } from 'react';
import { shallow } from 'zustand/shallow';

import { useI18nContext } from '../../../../../i18n/i18n-react';
import { ModalWithTitle } from '../../../../../shared/defguard-ui/components/Layout/modals/ModalWithTitle/ModalWithTitle';
import { useModalStore } from '../../../../../shared/hooks/store/useModalStore';
import { AddUserForm } from './AddUserForm';
import { AddUserForm } from './components/AddUserForm/AddUserForm';
import { EnrollmentTokenCard } from './components/EnrollmentTokenCard/EnrollmentTokenCard';
import { StartEnrollmentForm } from './components/StartEnrollmentForm/StartEnrollmentForm';
import { useAddUserModal } from './hooks/useAddUserModal';

const AddUserModal = () => {
const [{ visible: isOpen }, setModalState] = useModalStore(
(state) => [state.addUserModal, state.setAddUserModal],
const steps: ReactNode[] = [
<AddUserForm key={0} />,
<StartEnrollmentForm key={1} />,
<EnrollmentTokenCard key={2} />,
];

export const AddUserModal = () => {
const { LL } = useI18nContext();

const [currentStep, visible] = useAddUserModal(
(state) => [state.step, state.visible],
shallow,
);

const setIsOpen = (v: boolean) => setModalState({ visible: v });
const { LL } = useI18nContext();
const [reset, close] = useAddUserModal((state) => [state.reset, state.close], shallow);

return (
<ModalWithTitle
backdrop
title={LL.modals.addUser.title()}
isOpen={isOpen}
setIsOpen={setIsOpen}
id="add-user-modal"
>
<AddUserForm />
</ModalWithTitle>
backdrop
title={
currentStep === 0 ? LL.modals.addUser.title() : LL.modals.startEnrollment.title()
}
onClose={close}
afterClose={reset}
steps={steps}
currentStep={currentStep}
isOpen={visible}
/>
);
};

export default AddUserModal;
Original file line number Diff line number Diff line change
@@ -1,40 +1,43 @@
import './style.scss';

import { yupResolver } from '@hookform/resolvers/yup';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { omit } from 'lodash-es';
import { useMemo, useRef, useState } from 'react';
import { SubmitHandler, useController, useForm } from 'react-hook-form';
import * as yup from 'yup';
import { shallow } from 'zustand/shallow';

import { useI18nContext } from '../../../../../i18n/i18n-react';
import { FormCheckBox } from '../../../../../shared/defguard-ui/components/Form/FormCheckBox/FormCheckBox';
import { FormInput } from '../../../../../shared/defguard-ui/components/Form/FormInput/FormInput';
import { Button } from '../../../../../shared/defguard-ui/components/Layout/Button/Button';
import { useI18nContext } from '../../../../../../../i18n/i18n-react';
import { FormCheckBox } from '../../../../../../../shared/defguard-ui/components/Form/FormCheckBox/FormCheckBox';
import { FormInput } from '../../../../../../../shared/defguard-ui/components/Form/FormInput/FormInput';
import { Button } from '../../../../../../../shared/defguard-ui/components/Layout/Button/Button';
import {
ButtonSize,
ButtonStyleVariant,
} from '../../../../../shared/defguard-ui/components/Layout/Button/types';
import { useModalStore } from '../../../../../shared/hooks/store/useModalStore';
import useApi from '../../../../../shared/hooks/useApi';
import { useToaster } from '../../../../../shared/hooks/useToaster';
} from '../../../../../../../shared/defguard-ui/components/Layout/Button/types';
import useApi from '../../../../../../../shared/hooks/useApi';
import { useToaster } from '../../../../../../../shared/hooks/useToaster';
import {
patternDigitOrLowercase,
patternNoSpecialChars,
patternStartsWithDigit,
patternValidEmail,
patternValidPhoneNumber,
} from '../../../../../shared/patterns';
import { QueryKeys } from '../../../../../shared/queries';
import { passwordValidator } from '../../../../../shared/validators/password';
import { useEnrollmentModalStore } from '../StartEnrollmentModal/hooks/useEnrollmentModalStore';
} from '../../../../../../../shared/patterns';
import { QueryKeys } from '../../../../../../../shared/queries';
import { passwordValidator } from '../../../../../../../shared/validators/password';
import { useAddUserModal } from '../../hooks/useAddUserModal';

interface Inputs {
username: string;
password?: string;
email: string;
last_name: string;
first_name: string;
phone?: string;
// had to add field for conditional form validation to work
enable_enrollment: boolean;
// disabled when enableEnrollment is true
password?: string;
phone?: string;
}

export const AddUserForm = () => {
Expand Down Expand Up @@ -120,24 +123,30 @@ export const AddUserForm = () => {

const queryClient = useQueryClient();

const setModalState = useModalStore((state) => state.setAddUserModal);
const openEnrollmentModal = useEnrollmentModalStore((state) => state.open);

const toaster = useToaster();

const [setModalState, nextStep, close] = useAddUserModal(
(state) => [state.setState, state.nextStep, state.close],
shallow,
);

const addUserMutation = useMutation(addUser, {
onSuccess: (user) => {
queryClient.invalidateQueries([QueryKeys.FETCH_USERS_LIST]);
toaster.success('User added.');
if (enableEnrollment) {
openEnrollmentModal(user);
toaster.success(LL.modals.addUser.messages.userAdded());
setModalState({
user: user,
});
nextStep();
} else {
close();
}
setModalState({ visible: false });
},
onError: (err) => {
close();
toaster.error(LL.messages.error());
console.error(err);
setModalState({ visible: false });
toaster.error('Error occurred.');
},
});

Expand All @@ -148,14 +157,16 @@ export const AddUserForm = () => {
usernameAvailable(data.username)
.then(() => {
setCheckingUsername(false);
let userData = data;
if (enableEnrollment) {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { password, ...rest } = data;
userData = rest;
if (data.enable_enrollment) {
const userData = omit(data, ['password', 'enable_enrollment']);
addUserMutation.mutate(userData);
} else {
if (data.password) {
addUserMutation.mutate(omit(data, ['enable_enrollment']));
} else {
trigger('password', { shouldFocus: true });
}
}
// TODO: add notification toggle to form
addUserMutation.mutate(userData);
})
.catch(() => {
setCheckingUsername(false);
Expand All @@ -166,7 +177,16 @@ export const AddUserForm = () => {
};

return (
<form data-testid="add-user-form" onSubmit={handleSubmit(onSubmit)}>
<form
id="add-user-form"
data-testid="add-user-form"
onSubmit={handleSubmit(onSubmit)}
>
<FormCheckBox
labelPlacement="right"
label={LL.modals.addUser.form.fields.enableEnrollment.label()}
controller={{ control, name: 'enable_enrollment' }}
/>
<div className="row">
<div className="item">
<FormInput
Expand All @@ -188,10 +208,6 @@ export const AddUserForm = () => {
required={!enableEnrollment}
disabled={enableEnrollment}
/>
<FormCheckBox
label={LL.modals.addUser.form.fields.enableEnrollment.label()}
controller={{ control, name: 'enable_enrollment' }}
/>
<FormInput
label={LL.modals.addUser.form.fields.email.label()}
placeholder={LL.modals.addUser.form.fields.email.placeholder()}
Expand Down
Loading

0 comments on commit 9be556d

Please sign in to comment.