Add support for changing account password

This commit is contained in:
Dane Everitt 2019-06-22 23:45:09 -07:00
parent d43b7ea5bc
commit 438f1b06b9
No known key found for this signature in database
GPG key ID: EEA66103B3D71F53
7 changed files with 38 additions and 13 deletions

View file

@ -8,7 +8,7 @@ interface Data {
export default ({ current, password, confirmPassword }: Data): Promise<void> => {
return new Promise((resolve, reject) => {
http.put('/account/password', {
http.put('/api/client/account/password', {
// eslint-disable-next-line @typescript-eslint/camelcase
current_password: current,
password: password,

View file

@ -4,14 +4,20 @@ import { State, useStoreState } from 'easy-peasy';
import { ApplicationState } from '@/state/types';
type Props = Readonly<{
byKey?: string;
spacerClass?: string;
withBottomSpace?: boolean;
}>;
export default ({ withBottomSpace, spacerClass }: Props) => {
export default ({ withBottomSpace, spacerClass, byKey }: Props) => {
const flashes = useStoreState((state: State<ApplicationState>) => state.flashes.items);
if (flashes.length === 0) {
let filtered = flashes;
if (byKey) {
filtered = flashes.filter(flash => flash.key === byKey);
}
if (filtered.length === 0) {
return null;
}
@ -19,7 +25,7 @@ export default ({ withBottomSpace, spacerClass }: Props) => {
return (
<div className={withBottomSpace === false ? undefined : 'mb-2'}>
{
flashes.map((flash, index) => (
filtered.map((flash, index) => (
<React.Fragment key={flash.id || flash.type + flash.message}>
{index > 0 && <div className={spacerClass || 'mt-2'}></div>}
<MessageBox type={flash.type} title={flash.title}>

View file

@ -5,7 +5,7 @@ import UpdatePasswordForm from '@/components/account/forms/UpdatePasswordForm';
export default () => {
return (
<div className={'flex my-10'}>
<ContentBox className={'flex-1 mr-4'} title={'Update Password'}>
<ContentBox className={'flex-1 mr-4'} title={'Update Password'} showFlashes={'account:password'}>
<UpdatePasswordForm/>
</ContentBox>
<div className={'flex-1 ml-4'}>

View file

@ -1,10 +1,12 @@
import React, { useState } from 'react';
import { State, useStoreState } from 'easy-peasy';
import React from 'react';
import { Actions, State, useStoreActions, useStoreState } from 'easy-peasy';
import { ApplicationState } from '@/state/types';
import { Form, Formik, FormikActions } from 'formik';
import Field from '@/components/elements/Field';
import * as Yup from 'yup';
import SpinnerOverlay from '@/components/elements/SpinnerOverlay';
import updateAccountPassword from '@/api/account/updateAccountPassword';
import { httpErrorToHuman } from '@/api/http';
interface Values {
current: string;
@ -22,13 +24,26 @@ const schema = Yup.object().shape({
export default () => {
const user = useStoreState((state: State<ApplicationState>) => state.user.data);
const { clearFlashes, addFlash } = useStoreActions((actions: Actions<ApplicationState>) => actions.flashes);
if (!user) {
return null;
}
const submit = (values: Values, { setSubmitting }: FormikActions<Values>) => {
setTimeout(() => setSubmitting(false), 1500);
const submit = (values: Values, { resetForm, setSubmitting }: FormikActions<Values>) => {
clearFlashes('account:password');
updateAccountPassword({ ...values })
.then(() => {
resetForm();
addFlash({ key: 'account:password', type: 'success', message: 'Your password has been updated.' });
})
.catch(error => addFlash({
key: 'account:password',
type: 'error',
title: 'Error',
message: httpErrorToHuman(error),
}))
.then(() => setSubmitting(false));
};
return (

View file

@ -1,14 +1,17 @@
import * as React from 'react';
import classNames from 'classnames';
import FlashMessageRender from '@/components/FlashMessageRender';
type Props = Readonly<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement> & {
title?: string;
borderColor?: string;
showFlashes?: string | boolean;
}>;
export default ({ title, borderColor, children, ...props }: Props) => (
export default ({ title, borderColor, showFlashes, children, ...props }: Props) => (
<div {...props}>
{title && <h2 className={'text-neutral-300 mb-4 px-4'}>{title}</h2>}
{showFlashes && <FlashMessageRender byKey={typeof showFlashes === 'string' ? showFlashes : undefined}/>}
<div className={classNames('bg-neutral-700 p-4 rounded shadow-lg relative', borderColor, {
'border-t-4': !!borderColor,
})}>

View file

@ -6,8 +6,8 @@ const flashes: FlashState = {
addFlash: action((state, payload) => {
state.items.push(payload);
}),
clearFlashes: action(state => {
state.items = [];
clearFlashes: action((state, payload) => {
state.items = payload ? state.items.filter(flashes => flashes.key !== payload) : [];
}),
};

View file

@ -9,7 +9,7 @@ export interface ApplicationState {
export interface FlashState {
items: FlashMessage[];
addFlash: Action<FlashState, FlashMessage>;
clearFlashes: Action<FlashState>;
clearFlashes: Action<FlashState, string | void>;
}
export interface UserState {
@ -30,6 +30,7 @@ export interface UserData {
export interface FlashMessage {
id?: string;
key?: string;
type: FlashMessageType;
title?: string;
message: string;