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

View File

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

View File

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

View File

@@ -1,51 +1,42 @@
// @ts-nocheck
import React from 'react';
import ContentLoader from 'react-content-loader';
import { If, Icon, FormattedMessage as T } from '@/components';
import { saveInvoke } from '@/utils';
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>
);
}
import styled from 'styled-components';
import { AuthInsiderCard } from './_components';
import { Skeleton } from '@/components';
/**
* Invite accept loading space.
*/
export function InviteAcceptLoading({ isLoading, children, ...props }) {
export function InviteAcceptLoading({ isLoading, children }) {
return isLoading ? (
<ContentLoader
speed={2}
width={400}
height={280}
viewBox="0 0 400 280"
backgroundColor="#f3f3f3"
foregroundColor="#e6e6e6"
{...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>
<AuthInsiderCard>
<Fields>
<SkeletonField />
<SkeletonField />
<SkeletonField />
</Fields>
</AuthInsiderCard>
) : (
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({
first_name: Yup.string().required().label(intl.get('first_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')),
});

View File

@@ -6,10 +6,6 @@ const Schema = Yup.object().shape({
email: Yup.string().email().required().label(intl.get('email')),
first_name: Yup.string().required().label(intl.get('first_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_')),
});

View File

@@ -13,7 +13,14 @@ import UserFormContent from './UserFormContent';
import { useUserFormContext } from './UserFormProvider';
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.
@@ -27,12 +34,9 @@ function UserForm({
const { dialogName, user, userId, isEditMode, EditUserMutate } =
useUserFormContext();
const initialValues = {
...(isEditMode &&
pick(
objectKeysTransform(user, snakeCase),
Object.keys(UserFormSchema.fields),
)),
const initialFormValues = {
...initialValues,
...(isEditMode && transformToForm(user, initialValues)),
};
const handleSubmit = (values, { setSubmitting, setErrors }) => {
@@ -68,7 +72,7 @@ function UserForm({
return (
<Formik
validationSchema={UserFormSchema}
initialValues={initialValues}
initialValues={initialFormValues}
onSubmit={handleSubmit}
>
<UserFormContent calloutCode={calloutCode} />

View File

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