Add support for changing account password
This commit is contained in:
parent
d43b7ea5bc
commit
438f1b06b9
|
@ -8,7 +8,7 @@ interface Data {
|
||||||
|
|
||||||
export default ({ current, password, confirmPassword }: Data): Promise<void> => {
|
export default ({ current, password, confirmPassword }: Data): Promise<void> => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
http.put('/account/password', {
|
http.put('/api/client/account/password', {
|
||||||
// eslint-disable-next-line @typescript-eslint/camelcase
|
// eslint-disable-next-line @typescript-eslint/camelcase
|
||||||
current_password: current,
|
current_password: current,
|
||||||
password: password,
|
password: password,
|
||||||
|
|
|
@ -4,14 +4,20 @@ import { State, useStoreState } from 'easy-peasy';
|
||||||
import { ApplicationState } from '@/state/types';
|
import { ApplicationState } from '@/state/types';
|
||||||
|
|
||||||
type Props = Readonly<{
|
type Props = Readonly<{
|
||||||
|
byKey?: string;
|
||||||
spacerClass?: string;
|
spacerClass?: string;
|
||||||
withBottomSpace?: boolean;
|
withBottomSpace?: boolean;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
export default ({ withBottomSpace, spacerClass }: Props) => {
|
export default ({ withBottomSpace, spacerClass, byKey }: Props) => {
|
||||||
const flashes = useStoreState((state: State<ApplicationState>) => state.flashes.items);
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,7 +25,7 @@ export default ({ withBottomSpace, spacerClass }: Props) => {
|
||||||
return (
|
return (
|
||||||
<div className={withBottomSpace === false ? undefined : 'mb-2'}>
|
<div className={withBottomSpace === false ? undefined : 'mb-2'}>
|
||||||
{
|
{
|
||||||
flashes.map((flash, index) => (
|
filtered.map((flash, index) => (
|
||||||
<React.Fragment key={flash.id || flash.type + flash.message}>
|
<React.Fragment key={flash.id || flash.type + flash.message}>
|
||||||
{index > 0 && <div className={spacerClass || 'mt-2'}></div>}
|
{index > 0 && <div className={spacerClass || 'mt-2'}></div>}
|
||||||
<MessageBox type={flash.type} title={flash.title}>
|
<MessageBox type={flash.type} title={flash.title}>
|
||||||
|
|
|
@ -5,7 +5,7 @@ import UpdatePasswordForm from '@/components/account/forms/UpdatePasswordForm';
|
||||||
export default () => {
|
export default () => {
|
||||||
return (
|
return (
|
||||||
<div className={'flex my-10'}>
|
<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/>
|
<UpdatePasswordForm/>
|
||||||
</ContentBox>
|
</ContentBox>
|
||||||
<div className={'flex-1 ml-4'}>
|
<div className={'flex-1 ml-4'}>
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
import React, { useState } from 'react';
|
import React from 'react';
|
||||||
import { State, useStoreState } from 'easy-peasy';
|
import { Actions, State, useStoreActions, useStoreState } from 'easy-peasy';
|
||||||
import { ApplicationState } from '@/state/types';
|
import { ApplicationState } from '@/state/types';
|
||||||
import { Form, Formik, FormikActions } from 'formik';
|
import { Form, Formik, FormikActions } from 'formik';
|
||||||
import Field from '@/components/elements/Field';
|
import Field from '@/components/elements/Field';
|
||||||
import * as Yup from 'yup';
|
import * as Yup from 'yup';
|
||||||
import SpinnerOverlay from '@/components/elements/SpinnerOverlay';
|
import SpinnerOverlay from '@/components/elements/SpinnerOverlay';
|
||||||
|
import updateAccountPassword from '@/api/account/updateAccountPassword';
|
||||||
|
import { httpErrorToHuman } from '@/api/http';
|
||||||
|
|
||||||
interface Values {
|
interface Values {
|
||||||
current: string;
|
current: string;
|
||||||
|
@ -22,13 +24,26 @@ const schema = Yup.object().shape({
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
const user = useStoreState((state: State<ApplicationState>) => state.user.data);
|
const user = useStoreState((state: State<ApplicationState>) => state.user.data);
|
||||||
|
const { clearFlashes, addFlash } = useStoreActions((actions: Actions<ApplicationState>) => actions.flashes);
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const submit = (values: Values, { setSubmitting }: FormikActions<Values>) => {
|
const submit = (values: Values, { resetForm, setSubmitting }: FormikActions<Values>) => {
|
||||||
setTimeout(() => setSubmitting(false), 1500);
|
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 (
|
return (
|
||||||
|
|
|
@ -1,14 +1,17 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
import FlashMessageRender from '@/components/FlashMessageRender';
|
||||||
|
|
||||||
type Props = Readonly<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement> & {
|
type Props = Readonly<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement> & {
|
||||||
title?: string;
|
title?: string;
|
||||||
borderColor?: string;
|
borderColor?: string;
|
||||||
|
showFlashes?: string | boolean;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
export default ({ title, borderColor, children, ...props }: Props) => (
|
export default ({ title, borderColor, showFlashes, children, ...props }: Props) => (
|
||||||
<div {...props}>
|
<div {...props}>
|
||||||
{title && <h2 className={'text-neutral-300 mb-4 px-4'}>{title}</h2>}
|
{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, {
|
<div className={classNames('bg-neutral-700 p-4 rounded shadow-lg relative', borderColor, {
|
||||||
'border-t-4': !!borderColor,
|
'border-t-4': !!borderColor,
|
||||||
})}>
|
})}>
|
||||||
|
|
|
@ -6,8 +6,8 @@ const flashes: FlashState = {
|
||||||
addFlash: action((state, payload) => {
|
addFlash: action((state, payload) => {
|
||||||
state.items.push(payload);
|
state.items.push(payload);
|
||||||
}),
|
}),
|
||||||
clearFlashes: action(state => {
|
clearFlashes: action((state, payload) => {
|
||||||
state.items = [];
|
state.items = payload ? state.items.filter(flashes => flashes.key !== payload) : [];
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ export interface ApplicationState {
|
||||||
export interface FlashState {
|
export interface FlashState {
|
||||||
items: FlashMessage[];
|
items: FlashMessage[];
|
||||||
addFlash: Action<FlashState, FlashMessage>;
|
addFlash: Action<FlashState, FlashMessage>;
|
||||||
clearFlashes: Action<FlashState>;
|
clearFlashes: Action<FlashState, string | void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UserState {
|
export interface UserState {
|
||||||
|
@ -30,6 +30,7 @@ export interface UserData {
|
||||||
|
|
||||||
export interface FlashMessage {
|
export interface FlashMessage {
|
||||||
id?: string;
|
id?: string;
|
||||||
|
key?: string;
|
||||||
type: FlashMessageType;
|
type: FlashMessageType;
|
||||||
title?: string;
|
title?: string;
|
||||||
message: string;
|
message: string;
|
||||||
|
|
Loading…
Reference in New Issue