Finish code for updating email
This commit is contained in:
parent
438f1b06b9
commit
da24f66563
6 changed files with 134 additions and 20 deletions
|
@ -1,12 +1,17 @@
|
|||
@import url('//fonts.googleapis.com/css?family=Rubik:300,400,500&display=swap');
|
||||
@import url('https://fonts.googleapis.com/css?family=IBM+Plex+Sans:500&display=swap');
|
||||
|
||||
body {
|
||||
@apply .text-neutral-200;
|
||||
letter-spacing: 0.015em;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
@apply .font-medium;
|
||||
letter-spacing: 0;
|
||||
font-family: 'IBM Plex Sans', -apple-system, '"Roboto"', 'system-ui', 'sans-serif';
|
||||
}
|
||||
|
||||
p {
|
||||
@apply .text-neutral-200;
|
||||
letter-spacing: 0.015em;
|
||||
}
|
||||
|
|
9
resources/scripts/api/account/updateAccountEmail.ts
Normal file
9
resources/scripts/api/account/updateAccountEmail.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
import http from '@/api/http';
|
||||
|
||||
export default (email: string, password: string): Promise<void> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
http.put('/api/client/account/email', { email, password })
|
||||
.then(() => resolve())
|
||||
.catch(reject);
|
||||
});
|
||||
};
|
|
@ -1,6 +1,7 @@
|
|||
import * as React from 'react';
|
||||
import ContentBox from '@/components/elements/ContentBox';
|
||||
import UpdatePasswordForm from '@/components/account/forms/UpdatePasswordForm';
|
||||
import UpdateEmailAddressForm from '@/components/account/forms/UpdateEmailAddressForm';
|
||||
|
||||
export default () => {
|
||||
return (
|
||||
|
@ -9,7 +10,8 @@ export default () => {
|
|||
<UpdatePasswordForm/>
|
||||
</ContentBox>
|
||||
<div className={'flex-1 ml-4'}>
|
||||
<ContentBox title={'Update Email Address'}>
|
||||
<ContentBox title={'Update Email Address'} showFlashes={'account:email'}>
|
||||
<UpdateEmailAddressForm/>
|
||||
</ContentBox>
|
||||
<ContentBox title={'Update Identity'} className={'mt-8'}>
|
||||
</ContentBox>
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
import React from 'react';
|
||||
import { Actions, State, useStoreActions, useStoreState } from 'easy-peasy';
|
||||
import { ApplicationState } from '@/state/types';
|
||||
import { Form, Formik, FormikActions } from 'formik';
|
||||
import * as Yup from 'yup';
|
||||
import SpinnerOverlay from '@/components/elements/SpinnerOverlay';
|
||||
import Field from '@/components/elements/Field';
|
||||
import { httpErrorToHuman } from '@/api/http';
|
||||
|
||||
interface Values {
|
||||
email: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
email: Yup.string().email().required(),
|
||||
password: Yup.string().required('You must provide your current account password.'),
|
||||
});
|
||||
|
||||
export default () => {
|
||||
const user = useStoreState((state: State<ApplicationState>) => state.user.data);
|
||||
const updateEmail = useStoreActions((state: Actions<ApplicationState>) => state.user.updateUserEmail);
|
||||
|
||||
const { clearFlashes, addFlash } = useStoreActions((actions: Actions<ApplicationState>) => actions.flashes);
|
||||
|
||||
const submit = (values: Values, { resetForm, setSubmitting }: FormikActions<Values>) => {
|
||||
clearFlashes('account:email');
|
||||
|
||||
updateEmail({ ...values })
|
||||
.then(() => addFlash({
|
||||
type: 'success',
|
||||
key: 'account:email',
|
||||
message: 'Your primary email has been updated.',
|
||||
}))
|
||||
.catch(error => addFlash({
|
||||
type: 'error',
|
||||
key: 'account:email',
|
||||
title: 'Error',
|
||||
message: httpErrorToHuman(error),
|
||||
}))
|
||||
.then(() => {
|
||||
resetForm();
|
||||
setSubmitting(false);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Formik
|
||||
onSubmit={submit}
|
||||
validationSchema={schema}
|
||||
initialValues={{ email: user!.email, password: '' }}
|
||||
>
|
||||
{
|
||||
({ isSubmitting, isValid }) => (
|
||||
<React.Fragment>
|
||||
<SpinnerOverlay large={true} visible={isSubmitting}/>
|
||||
<Form className={'m-0'}>
|
||||
<Field
|
||||
id={'current_email'}
|
||||
type={'email'}
|
||||
name={'email'}
|
||||
label={'Email'}
|
||||
/>
|
||||
<div className={'mt-6'}>
|
||||
<Field
|
||||
id={'confirm_password'}
|
||||
type={'password'}
|
||||
name={'password'}
|
||||
label={'Confirm Password'}
|
||||
/>
|
||||
</div>
|
||||
<div className={'mt-6'}>
|
||||
<button className={'btn btn-sm btn-primary'} disabled={isSubmitting || !isValid}>
|
||||
Update Email
|
||||
</button>
|
||||
</div>
|
||||
</Form>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
</Formik>
|
||||
);
|
||||
};
|
|
@ -1,11 +1,41 @@
|
|||
import { UserState } from '@/state/types';
|
||||
import { action } from 'easy-peasy';
|
||||
import { Action, action, Thunk, thunk } from 'easy-peasy';
|
||||
import updateAccountEmail from '@/api/account/updateAccountEmail';
|
||||
|
||||
export interface UserData {
|
||||
uuid: string;
|
||||
username: string;
|
||||
email: string;
|
||||
language: string;
|
||||
rootAdmin: boolean;
|
||||
useTotp: boolean;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
export interface UserState {
|
||||
data?: UserData;
|
||||
setUserData: Action<UserState, UserData>;
|
||||
updateUserData: Action<UserState, Partial<UserData>>;
|
||||
updateUserEmail: Thunk<UserState, { email: string; password: string }, any, {}, Promise<void>>;
|
||||
}
|
||||
|
||||
const user: UserState = {
|
||||
data: undefined,
|
||||
setUserData: action((state, payload) => {
|
||||
state.data = payload;
|
||||
}),
|
||||
|
||||
updateUserData: action((state, payload) => {
|
||||
// Limitation of Typescript, can't do much about that currently unfortunately.
|
||||
// @ts-ignore
|
||||
state.data = { ...state.data, ...payload };
|
||||
}),
|
||||
|
||||
updateUserEmail: thunk(async (actions, payload) => {
|
||||
await updateAccountEmail(payload.email, payload.password);
|
||||
|
||||
actions.updateUserData({ email: payload.email });
|
||||
}),
|
||||
};
|
||||
|
||||
export default user;
|
||||
|
|
17
resources/scripts/state/types.d.ts
vendored
17
resources/scripts/state/types.d.ts
vendored
|
@ -1,5 +1,6 @@
|
|||
import { FlashMessageType } from '@/components/MessageBox';
|
||||
import { Action } from 'easy-peasy';
|
||||
import { UserState } from '@/state/models/user';
|
||||
|
||||
export interface ApplicationState {
|
||||
flashes: FlashState;
|
||||
|
@ -12,22 +13,6 @@ export interface FlashState {
|
|||
clearFlashes: Action<FlashState, string | void>;
|
||||
}
|
||||
|
||||
export interface UserState {
|
||||
data?: UserData;
|
||||
setUserData: Action<UserState, UserData>;
|
||||
}
|
||||
|
||||
export interface UserData {
|
||||
uuid: string;
|
||||
username: string;
|
||||
email: string;
|
||||
language: string;
|
||||
rootAdmin: boolean;
|
||||
useTotp: boolean;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
export interface FlashMessage {
|
||||
id?: string;
|
||||
key?: string;
|
||||
|
|
Loading…
Reference in a new issue