From da24f665635fc0a2236e9b769c5620f45de7946d Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Mon, 24 Jun 2019 21:43:46 -0700 Subject: [PATCH] Finish code for updating email --- .../assets/styles/components/typography.css | 7 +- .../scripts/api/account/updateAccountEmail.ts | 9 ++ .../account/AccountOverviewContainer.tsx | 4 +- .../account/forms/UpdateEmailAddressForm.tsx | 83 +++++++++++++++++++ resources/scripts/state/models/user.ts | 34 +++++++- resources/scripts/state/types.d.ts | 17 +--- 6 files changed, 134 insertions(+), 20 deletions(-) create mode 100644 resources/scripts/api/account/updateAccountEmail.ts create mode 100644 resources/scripts/components/account/forms/UpdateEmailAddressForm.tsx diff --git a/resources/assets/styles/components/typography.css b/resources/assets/styles/components/typography.css index 679688f1a..301eabb2a 100644 --- a/resources/assets/styles/components/typography.css +++ b/resources/assets/styles/components/typography.css @@ -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; } diff --git a/resources/scripts/api/account/updateAccountEmail.ts b/resources/scripts/api/account/updateAccountEmail.ts new file mode 100644 index 000000000..5ff230265 --- /dev/null +++ b/resources/scripts/api/account/updateAccountEmail.ts @@ -0,0 +1,9 @@ +import http from '@/api/http'; + +export default (email: string, password: string): Promise => { + return new Promise((resolve, reject) => { + http.put('/api/client/account/email', { email, password }) + .then(() => resolve()) + .catch(reject); + }); +}; diff --git a/resources/scripts/components/account/AccountOverviewContainer.tsx b/resources/scripts/components/account/AccountOverviewContainer.tsx index 61174a548..fcc208ac5 100644 --- a/resources/scripts/components/account/AccountOverviewContainer.tsx +++ b/resources/scripts/components/account/AccountOverviewContainer.tsx @@ -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 () => {
- + + diff --git a/resources/scripts/components/account/forms/UpdateEmailAddressForm.tsx b/resources/scripts/components/account/forms/UpdateEmailAddressForm.tsx new file mode 100644 index 000000000..4593908a5 --- /dev/null +++ b/resources/scripts/components/account/forms/UpdateEmailAddressForm.tsx @@ -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) => state.user.data); + const updateEmail = useStoreActions((state: Actions) => state.user.updateUserEmail); + + const { clearFlashes, addFlash } = useStoreActions((actions: Actions) => actions.flashes); + + const submit = (values: Values, { resetForm, setSubmitting }: FormikActions) => { + 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 ( + + { + ({ isSubmitting, isValid }) => ( + + +
+ +
+ +
+
+ +
+ +
+ ) + } +
+ ); +}; diff --git a/resources/scripts/state/models/user.ts b/resources/scripts/state/models/user.ts index 3997d009f..c7da19fbc 100644 --- a/resources/scripts/state/models/user.ts +++ b/resources/scripts/state/models/user.ts @@ -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; + updateUserData: Action>; + updateUserEmail: Thunk>; +} 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; diff --git a/resources/scripts/state/types.d.ts b/resources/scripts/state/types.d.ts index 644a4679f..9774f6f73 100644 --- a/resources/scripts/state/types.d.ts +++ b/resources/scripts/state/types.d.ts @@ -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; } -export interface UserState { - data?: UserData; - setUserData: Action; -} - -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;