Finish code for updating email

This commit is contained in:
Dane Everitt 2019-06-24 21:43:46 -07:00
parent 438f1b06b9
commit da24f66563
No known key found for this signature in database
GPG key ID: EEA66103B3D71F53
6 changed files with 134 additions and 20 deletions

View file

@ -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;
}

View 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);
});
};

View file

@ -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>

View file

@ -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>
);
};

View file

@ -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;

View file

@ -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;