Add support for changing account password
This commit is contained in:
parent
d43b7ea5bc
commit
438f1b06b9
7 changed files with 38 additions and 13 deletions
|
@ -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,
|
||||
|
|
|
@ -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}>
|
||||
|
|
|
@ -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'}>
|
||||
|
|
|
@ -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 (
|
||||
|
|
|
@ -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,
|
||||
})}>
|
||||
|
|
|
@ -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) : [];
|
||||
}),
|
||||
};
|
||||
|
||||
|
|
3
resources/scripts/state/types.d.ts
vendored
3
resources/scripts/state/types.d.ts
vendored
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue