feat(server): remove the phone number from users management

This commit is contained in:
a.bouhuolia
2023-04-06 03:08:51 +02:00
parent e4a647376c
commit 950b5407c3
8 changed files with 127 additions and 149 deletions

View File

@@ -4,7 +4,6 @@ import intl from 'react-intl-universal';
import { Formik } from 'formik'; import { Formik } from 'formik';
import { useHistory } from 'react-router-dom'; import { useHistory } from 'react-router-dom';
import { Intent, Position } from '@blueprintjs/core'; import { Intent, Position } from '@blueprintjs/core';
import { FormattedMessage as T } from '@/components';
import { isEmpty } from 'lodash'; import { isEmpty } from 'lodash';
import { useInviteAcceptContext } from './InviteAcceptProvider'; import { useInviteAcceptContext } from './InviteAcceptProvider';
@@ -13,6 +12,14 @@ import { InviteAcceptSchema } from './utils';
import InviteAcceptFormContent from './InviteAcceptFormContent'; import InviteAcceptFormContent from './InviteAcceptFormContent';
import { AuthInsiderCard } from './_components'; import { AuthInsiderCard } from './_components';
const initialValues = {
organization_name: '',
invited_email: '',
first_name: '',
last_name: '',
password: '',
};
export default function InviteAcceptForm() { export default function InviteAcceptForm() {
const history = useHistory(); const history = useHistory();
@@ -20,9 +27,8 @@ export default function InviteAcceptForm() {
const { inviteAcceptMutate, inviteMeta, token } = useInviteAcceptContext(); const { inviteAcceptMutate, inviteMeta, token } = useInviteAcceptContext();
// Invite value. // Invite value.
const inviteValue = { const inviteFormValue = {
organization_name: '', ...initialValues,
invited_email: '',
...(!isEmpty(inviteMeta) ...(!isEmpty(inviteMeta)
? { ? {
invited_email: inviteMeta.email, invited_email: inviteMeta.email,
@@ -34,19 +40,17 @@ export default function InviteAcceptForm() {
// Handle form submitting. // Handle form submitting.
const handleSubmit = (values, { setSubmitting, setErrors }) => { const handleSubmit = (values, { setSubmitting, setErrors }) => {
inviteAcceptMutate([values, token]) inviteAcceptMutate([values, token])
.then((response) => { .then(() => {
AppToaster.show({ AppToaster.show({
message: intl.getHTML( message: intl.getHTML(
'congrats_your_account_has_been_created_and_invited', 'congrats_your_account_has_been_created_and_invited',
{ {
organization_name: inviteValue.organization_name, organization_name: inviteMeta.organizationName,
}, },
), ),
intent: Intent.SUCCESS, intent: Intent.SUCCESS,
}); });
history.push('/auth/login'); history.push('/auth/login');
setSubmitting(false);
}) })
.catch( .catch(
({ ({
@@ -84,7 +88,7 @@ export default function InviteAcceptForm() {
<AuthInsiderCard> <AuthInsiderCard>
<Formik <Formik
validationSchema={InviteAcceptSchema} validationSchema={InviteAcceptSchema}
initialValues={inviteValue} initialValues={inviteFormValue}
onSubmit={handleSubmit} onSubmit={handleSubmit}
component={InviteAcceptFormContent} component={InviteAcceptFormContent}
/> />

View File

@@ -1,34 +1,45 @@
// @ts-nocheck // @ts-nocheck
import React from 'react'; import React, { useState } from 'react';
import intl from 'react-intl-universal'; import intl from 'react-intl-universal';
import { InputGroup, Intent } from '@blueprintjs/core'; import { Button, InputGroup, Intent } from '@blueprintjs/core';
import { Form, useFormikContext } from 'formik'; import { Form, useFormikContext } from 'formik';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { Tooltip2 } from '@blueprintjs/popover2';
import styled from 'styled-components';
import { Col, FFormGroup, Row, FormattedMessage as T } from '@/components'; import {
Col,
FFormGroup,
FInputGroup,
Row,
FormattedMessage as T,
} from '@/components';
import { useInviteAcceptContext } from './InviteAcceptProvider'; import { useInviteAcceptContext } from './InviteAcceptProvider';
import { PasswordRevealer } from './components';
import { AuthSubmitButton } from './_components'; import { AuthSubmitButton } from './_components';
/** /**
* Invite user form. * Invite user form.
*/ */
export default function InviteUserFormContent() { export default function InviteUserFormContent() {
// Invite accept context. const [showPassword, setShowPassword] = useState<boolean>(false);
const { inviteMeta } = useInviteAcceptContext();
// Formik context. const { inviteMeta } = useInviteAcceptContext();
const { isSubmitting } = useFormikContext(); const { isSubmitting } = useFormikContext();
const [passwordType, setPasswordType] = React.useState('password');
// Handle password revealer changing. // Handle password revealer changing.
const handlePasswordRevealerChange = React.useCallback( const handleLockClick = () => {
(shown) => { setShowPassword(!showPassword);
const type = shown ? 'text' : 'password'; };
setPasswordType(type); const lockButton = (
}, <Tooltip2 content={`${showPassword ? 'Hide' : 'Show'} Password`}>
[setPasswordType], <Button
icon={showPassword ? 'unlock' : 'lock'}
intent={Intent.WARNING}
minimal={true}
onClick={handleLockClick}
small={true}
/>
</Tooltip2>
); );
return ( return (
@@ -36,26 +47,27 @@ export default function InviteUserFormContent() {
<Row> <Row>
<Col md={6}> <Col md={6}>
<FFormGroup name={'first_name'} label={<T id={'first_name'} />}> <FFormGroup name={'first_name'} label={<T id={'first_name'} />}>
<InputGroup name={'first_name'} /> <FInputGroup name={'first_name'} large={true} />
</FFormGroup> </FFormGroup>
</Col> </Col>
<Col md={6}> <Col md={6}>
<FFormGroup name={'last_name'} label={<T id={'last_name'} />}> <FFormGroup name={'last_name'} label={<T id={'last_name'} />}>
<InputGroup name={'last_name'} /> <FInputGroup name={'last_name'} large={true} />
</FFormGroup> </FFormGroup>
</Col> </Col>
</Row> </Row>
<FFormGroup <FFormGroup name={'password'} label={<T id={'password'} />}>
name={'password'} <FInputGroup
label={<T id={'password'} />} name={'password'}
labelInfo={<PasswordRevealer onChange={handlePasswordRevealerChange} />} large={true}
> rightElement={lockButton}
<InputGroup name={'password'} /> type={showPassword ? 'text' : 'password'}
/>
</FFormGroup> </FFormGroup>
<div className={'invite-form__statement-section'}> <InviteAcceptFooterParagraphs>
<p> <p>
<T id={'you_email_address_is'} /> <b>{inviteMeta.email},</b> <br /> <T id={'you_email_address_is'} /> <b>{inviteMeta.email},</b> <br />
<T id={'you_will_use_this_address_to_sign_in_to_bigcapital'} /> <T id={'you_will_use_this_address_to_sign_in_to_bigcapital'} />
@@ -66,16 +78,25 @@ export default function InviteUserFormContent() {
privacy: (msg) => <Link>{msg}</Link>, privacy: (msg) => <Link>{msg}</Link>,
})} })}
</p> </p>
</div> </InviteAcceptFooterParagraphs>
<AuthSubmitButton <InviteAuthSubmitButton
intent={Intent.PRIMARY} intent={Intent.PRIMARY}
type="submit" type="submit"
fill={true} fill={true}
large={true}
loading={isSubmitting} loading={isSubmitting}
> >
<T id={'create_account'} /> <T id={'create_account'} />
</AuthSubmitButton> </InviteAuthSubmitButton>
</Form> </Form>
); );
} }
const InviteAcceptFooterParagraphs = styled.div`
opacity: 0.8;
`;
const InviteAuthSubmitButton = styled(AuthSubmitButton)`
margin-top: 1.6rem;
`;

View File

@@ -1,8 +1,8 @@
// @ts-nocheck // @ts-nocheck
import React, { createContext, useContext } from 'react'; import React, { createContext, useContext, useEffect } from 'react';
import { useHistory } from 'react-router-dom';
import { useInviteMetaByToken, useAuthInviteAccept } from '@/hooks/query'; import { useInviteMetaByToken, useAuthInviteAccept } from '@/hooks/query';
import { InviteAcceptLoading } from './components'; import { InviteAcceptLoading } from './components';
import { useHistory } from 'react-router-dom';
const InviteAcceptContext = createContext(); const InviteAcceptContext = createContext();
@@ -22,11 +22,10 @@ function InviteAcceptProvider({ token, ...props }) {
const { mutateAsync: inviteAcceptMutate } = useAuthInviteAccept({ const { mutateAsync: inviteAcceptMutate } = useAuthInviteAccept({
retry: false, retry: false,
}); });
// History context. // History context.
const history = useHistory(); const history = useHistory();
React.useEffect(() => { useEffect(() => {
if (inviteMetaError) { history.push('/auth/login'); } if (inviteMetaError) { history.push('/auth/login'); }
}, [history, inviteMetaError]); }, [history, inviteMetaError]);

View File

@@ -1,51 +1,42 @@
// @ts-nocheck // @ts-nocheck
import React from 'react'; import React from 'react';
import ContentLoader from 'react-content-loader'; import styled from 'styled-components';
import { If, Icon, FormattedMessage as T } from '@/components'; import { AuthInsiderCard } from './_components';
import { saveInvoke } from '@/utils'; import { Skeleton } from '@/components';
export function PasswordRevealer({ defaultShown = false, onChange }) {
const [shown, setShown] = React.useState(defaultShown);
const handleClick = () => {
setShown(!shown);
saveInvoke(onChange, !shown);
};
return (
<span class="password-revealer" onClick={handleClick}>
<If condition={shown}>
<Icon icon="eye-slash" />{' '}
</If>
<If condition={!shown}>
<Icon icon="eye" />{' '}
</If>
</span>
);
}
/** /**
* Invite accept loading space. * Invite accept loading space.
*/ */
export function InviteAcceptLoading({ isLoading, children, ...props }) { export function InviteAcceptLoading({ isLoading, children }) {
return isLoading ? ( return isLoading ? (
<ContentLoader <AuthInsiderCard>
speed={2} <Fields>
width={400} <SkeletonField />
height={280} <SkeletonField />
viewBox="0 0 400 280" <SkeletonField />
backgroundColor="#f3f3f3" </Fields>
foregroundColor="#e6e6e6" </AuthInsiderCard>
{...props}
>
<rect x="0" y="80" rx="2" ry="2" width="200" height="20" />
<rect x="0" y="0" rx="2" ry="2" width="250" height="30" />
<rect x="0" y="38" rx="2" ry="2" width="300" height="15" />
<rect x="0" y="175" rx="2" ry="2" width="200" height="20" />
<rect x="1" y="205" rx="2" ry="2" width="385" height="38" />
<rect x="0" y="110" rx="2" ry="2" width="385" height="38" />
</ContentLoader>
) : ( ) : (
children children
); );
} }
function SkeletonField() {
return (
<SkeletonFieldRoot>
<Skeleton>XXXX XXXX</Skeleton>
<Skeleton minWidth={100}>XXXX XXXX XXXX XXXX</Skeleton>
</SkeletonFieldRoot>
);
}
const Fields = styled.div`
display: flex;
flex-direction: column;
gap: 20px;
`;
const SkeletonFieldRoot = styled.div`
display: flex;
flex-direction: column;
gap: 8px;
`;

View File

@@ -42,10 +42,6 @@ export const SendResetPasswordSchema = Yup.object().shape({
export const InviteAcceptSchema = Yup.object().shape({ export const InviteAcceptSchema = Yup.object().shape({
first_name: Yup.string().required().label(intl.get('first_name_')), first_name: Yup.string().required().label(intl.get('first_name_')),
last_name: Yup.string().required().label(intl.get('last_name_')), last_name: Yup.string().required().label(intl.get('last_name_')),
phone_number: Yup.string()
.matches()
.required()
.label(intl.get('phone_number')),
password: Yup.string().min(4).required().label(intl.get('password')), password: Yup.string().min(4).required().label(intl.get('password')),
}); });

View File

@@ -6,10 +6,6 @@ const Schema = Yup.object().shape({
email: Yup.string().email().required().label(intl.get('email')), email: Yup.string().email().required().label(intl.get('email')),
first_name: Yup.string().required().label(intl.get('first_name_')), first_name: Yup.string().required().label(intl.get('first_name_')),
last_name: Yup.string().required().label(intl.get('last_name_')), last_name: Yup.string().required().label(intl.get('last_name_')),
phone_number: Yup.string()
.matches()
.required()
.label(intl.get('phone_number_')),
role_id: Yup.string().required().label(intl.get('roles.label.role_name_')), role_id: Yup.string().required().label(intl.get('roles.label.role_name_')),
}); });

View File

@@ -13,7 +13,14 @@ import UserFormContent from './UserFormContent';
import { useUserFormContext } from './UserFormProvider'; import { useUserFormContext } from './UserFormProvider';
import { transformErrors } from './utils'; import { transformErrors } from './utils';
import { compose, objectKeysTransform } from '@/utils'; import { compose, objectKeysTransform, transformToForm } from '@/utils';
const initialValues = {
first_name: '',
last_name: '',
email: '',
role_id: '',
};
/** /**
* User form. * User form.
@@ -27,12 +34,9 @@ function UserForm({
const { dialogName, user, userId, isEditMode, EditUserMutate } = const { dialogName, user, userId, isEditMode, EditUserMutate } =
useUserFormContext(); useUserFormContext();
const initialValues = { const initialFormValues = {
...(isEditMode && ...initialValues,
pick( ...(isEditMode && transformToForm(user, initialValues)),
objectKeysTransform(user, snakeCase),
Object.keys(UserFormSchema.fields),
)),
}; };
const handleSubmit = (values, { setSubmitting, setErrors }) => { const handleSubmit = (values, { setSubmitting, setErrors }) => {
@@ -68,7 +72,7 @@ function UserForm({
return ( return (
<Formik <Formik
validationSchema={UserFormSchema} validationSchema={UserFormSchema}
initialValues={initialValues} initialValues={initialFormValues}
onSubmit={handleSubmit} onSubmit={handleSubmit}
> >
<UserFormContent calloutCode={calloutCode} /> <UserFormContent calloutCode={calloutCode} />

View File

@@ -8,9 +8,10 @@ import {
Button, Button,
} from '@blueprintjs/core'; } from '@blueprintjs/core';
import { FastField, Form, useFormikContext, ErrorMessage } from 'formik'; import { FastField, Form, useFormikContext, ErrorMessage } from 'formik';
import { FormattedMessage as T } from '@/components';
import { CLASSES } from '@/constants/classes';
import classNames from 'classnames'; import classNames from 'classnames';
import { FFormGroup, FInputGroup, FormattedMessage as T } from '@/components';
import { CLASSES } from '@/constants/classes';
import { inputIntent } from '@/utils'; import { inputIntent } from '@/utils';
import { ListSelect, FieldRequiredHint } from '@/components'; import { ListSelect, FieldRequiredHint } from '@/components';
import { useUserFormContext } from './UserFormProvider'; import { useUserFormContext } from './UserFormProvider';
@@ -23,6 +24,7 @@ import { UserFormCalloutAlerts } from './components';
*/ */
function UserFormContent({ function UserFormContent({
calloutCode, calloutCode,
// #withDialogActions // #withDialogActions
closeDialog, closeDialog,
}) { }) {
@@ -39,60 +41,20 @@ function UserFormContent({
<UserFormCalloutAlerts calloutCodes={calloutCode} /> <UserFormCalloutAlerts calloutCodes={calloutCode} />
{/* ----------- Email ----------- */} {/* ----------- Email ----------- */}
<FastField name={'email'}> <FFormGroup name={'email'} label={<T id={'email'} />}>
{({ field, meta: { error, touched } }) => ( <FInputGroup name={'email'} />
<FormGroup </FFormGroup>
label={<T id={'email'} />}
labelInfo={<FieldRequiredHint />}
className={classNames('form-group--email', CLASSES.FILL)}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name="email" />}
>
<InputGroup medium={true} {...field} />
</FormGroup>
)}
</FastField>
{/* ----------- First name ----------- */} {/* ----------- First name ----------- */}
<FastField name={'first_name'}> <FFormGroup name={'first_name'} label={<T id={'first_name'} />}>
{({ form, field, meta: { error, touched } }) => ( <FInputGroup name={'first_name'} />
<FormGroup </FFormGroup>
label={<T id={'first_name'} />}
labelInfo={<FieldRequiredHint />}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name={'first_name'} />}
>
<InputGroup intent={inputIntent({ error, touched })} {...field} />
</FormGroup>
)}
</FastField>
{/* ----------- Last name ----------- */} {/* ----------- Last name ----------- */}
<FastField name={'last_name'}> <FFormGroup name={'last_name'} label={<T id={'last_name'} />}>
{({ form, field, meta: { error, touched } }) => ( <FInputGroup name={'last_name'} />
<FormGroup </FFormGroup>
label={<T id={'last_name'} />}
labelInfo={<FieldRequiredHint />}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name={'last_name'} />}
>
<InputGroup intent={inputIntent({ error, touched })} {...field} />
</FormGroup>
)}
</FastField>
{/* ----------- Phone name ----------- */}
<FastField name={'phone_number'}>
{({ form, field, meta: { error, touched } }) => (
<FormGroup
label={<T id={'phone_number'} />}
labelInfo={<FieldRequiredHint />}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name={'phone_number'} />}
>
<InputGroup intent={inputIntent({ error, touched })} {...field} />
</FormGroup>
)}
</FastField>
{/* ----------- Role name ----------- */} {/* ----------- Role name ----------- */}
<FastField name={'role_id'}> <FastField name={'role_id'}>
{({ form, field: { value }, meta: { error, touched } }) => ( {({ form, field: { value }, meta: { error, touched } }) => (
@@ -127,7 +89,12 @@ function UserFormContent({
<T id={'cancel'} /> <T id={'cancel'} />
</Button> </Button>
<Button intent={Intent.PRIMARY} type="submit" disabled={isSubmitting}> <Button
intent={Intent.PRIMARY}
type="submit"
disabled={isSubmitting}
loading={isSubmitting}
>
<T id={'edit'} /> <T id={'edit'} />
</Button> </Button>
</div> </div>