Merge pull request #48 from Nez27/feat/add-edit-infor-account-page

Add edit information account page
This commit is contained in:
Loi Phan
2023-11-30 14:20:51 +07:00
committed by GitHub
9 changed files with 273 additions and 2 deletions
+2
View File
@@ -16,6 +16,7 @@ import Booking from './pages/Booking';
import Room from './pages/Room';
import NotFound from './pages/NotFound';
import Login from '@page/Login';
import Account from '@page/Account';
// Constants
import * as PATH from './constants/path';
@@ -93,6 +94,7 @@ function App() {
<Route path={PATH.BOOKING} element={<Booking />} />
<Route path={PATH.USER} element={<User />} />
<Route path={PATH.ROOM} element={<Room />} />
<Route path={PATH.ACCOUNT} element={<Account />} />
</Route>
<Route path={PATH.LOGIN} element={<Login />} />
<Route path={PATH.OTHER_PATH} element={<NotFound />} />
@@ -3,15 +3,18 @@ import Nav from '@component/Nav';
// Styled
import { Heading, StyledSidebar } from './styled';
import { useNavigate } from 'react-router-dom';
interface ISidebar {
heading: string;
}
const Sidebar = ({ heading }: ISidebar) => {
const navigate = useNavigate();
return (
<StyledSidebar>
<Heading>{heading}</Heading>
<Heading onClick={() => navigate('/')}>{heading}</Heading>
<Nav />
</StyledSidebar>
);
@@ -13,6 +13,8 @@ const Heading = styled.h1`
text-transform: uppercase;
text-align: center;
cursor: pointer;
color: var(--primary-color);
`;
@@ -0,0 +1,25 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { toast } from 'react-hot-toast';
// Services
import { updateAccount as updateAccountFn } from '@service/authenticationService';
// Constants
import { UPDATE_ACCOUNT_SUCCESSFUL } from '@constant/messages';
const useUpdateAccount = () => {
const queryClient = useQueryClient();
const { mutate: updateAccount, isPending: isUpdating } = useMutation({
mutationFn: updateAccountFn,
onSuccess: (account) => {
toast.success(UPDATE_ACCOUNT_SUCCESSFUL);
queryClient.setQueryData(['account'], account.user);
},
onError: (err) => toast.error(err.message),
});
return { updateAccount, isUpdating };
};
export { useUpdateAccount };
@@ -0,0 +1,59 @@
import { useForm } from 'react-hook-form';
// Styled
import Input from '@commonStyle/Input';
// Components
import Form from '@component/Form';
// Constants
import { REQUIRED_FIELD_ERROR } from '@constant/formValidateMessage';
// Hooks
import { useUpdateAccount } from '@hook/authentication/useUpdateAccount';
import { useAccount } from '@hook/authentication/useAccount';
interface IAccountForm {
fullName: string;
}
const AccountInforForm = () => {
const { account } = useAccount();
const { updateAccount, isUpdating } = useUpdateAccount();
const {
register,
handleSubmit,
formState: { errors },
} = useForm<IAccountForm>();
const onSubmit = (data: IAccountForm) => {
updateAccount(data);
};
return (
<Form onSubmit={handleSubmit(onSubmit)}>
<Form.Row label="Email:">
<Input id="email" readOnly value={account?.email} />
</Form.Row>
<Form.Row label="Full Name:" error={errors?.fullName?.message as string}>
<Input
id="fullName"
{...register('fullName', {
required: REQUIRED_FIELD_ERROR,
value: account?.user_metadata.fullName,
})}
/>
</Form.Row>
<Form.Action>
<Form.Button type="submit" name="submit" disabled={isUpdating}>
Update account
</Form.Button>
</Form.Action>
</Form>
);
};
export default AccountInforForm;
@@ -0,0 +1,93 @@
import { useEffect } from 'react';
import { useForm } from 'react-hook-form';
import toast from 'react-hot-toast';
// Styled
import Input from '@commonStyle/Input';
// Components
import Form from '@component/Form';
// Constants
import { REGEX } from '@constant/commons';
import {
PASSWORD_CONDITION,
PASSWORD_NOT_MATCH,
PASSWORD_NOT_MATCH_REQUIREMENT,
} from '@constant/formValidateMessage';
// Helpers
import { isValidRegex } from '@helper/validators';
// Hooks
import { useUpdateAccount } from '@hook/authentication/useUpdateAccount';
interface IAccountPasswordForm {
password: string;
confirmPassword: string;
}
const AccountPasswordForm = () => {
const { updateAccount, isUpdating } = useUpdateAccount();
const {
register,
handleSubmit,
getValues,
reset,
formState: { errors },
} = useForm<IAccountPasswordForm>();
useEffect(() => {
if (errors?.password?.message) {
toast.error(PASSWORD_CONDITION);
}
}, [errors?.password?.message]);
const onSubmit = (data: IAccountPasswordForm) => {
updateAccount(data, {
onSuccess: () => {
reset();
},
});
};
return (
<Form onSubmit={handleSubmit(onSubmit)}>
<Form.Row label="Password:" error={errors?.password?.message}>
<Input
id="password"
type="password"
{...register('password', {
validate: {
checkPassword: (v) =>
isValidRegex(REGEX.PASSWORD, v) ||
PASSWORD_NOT_MATCH_REQUIREMENT,
},
})}
/>
</Form.Row>
<Form.Row
label="Confirm Password:"
error={errors?.confirmPassword?.message}
>
<Input
id="confirmPassword"
type="password"
{...register('confirmPassword', {
validate: (value) =>
getValues().password === value || PASSWORD_NOT_MATCH,
})}
/>
</Form.Row>
<Form.Action>
<Form.Button type="submit" name="submit" disabled={isUpdating}>
Update password
</Form.Button>
</Form.Action>
</Form>
);
};
export default AccountPasswordForm;
@@ -0,0 +1,26 @@
// Styled
import { FormArea, StyledAccount, SubTitle, Title } from './styled';
// Components
import AccountInforForm from './AccountInforForm';
import AccountPasswordForm from './AccountPasswordForm';
const Account = () => {
return (
<StyledAccount>
<Title>Update account</Title>
<FormArea>
<SubTitle>Update information</SubTitle>
<AccountInforForm />
</FormArea>
<FormArea>
<SubTitle>Update password</SubTitle>
<AccountPasswordForm />
</FormArea>
</StyledAccount>
);
};
export default Account;
@@ -0,0 +1,36 @@
import styled from 'styled-components';
const Title = styled.h2`
font-size: var(--fs-md);
color: var(--dark-text);
text-transform: capitalize;
`;
const SubTitle = styled.h3`
font-size: var(--fs-sm);
text-transform: capitalize;
text-align: center;
margin-bottom: 20px;
`
const StyledAccount = styled.div`
padding: 20px;
display: flex;
flex-direction: column;
align-items: center;
`;
const FormArea = styled.div`
border-radius: var(--radius-sm);
border: 1px solid var(--border-color);
padding: 20px 40px;
width: 500px;
margin-top: 50px;
`
export { Title, StyledAccount, FormArea, SubTitle };
@@ -1,8 +1,12 @@
import { ILogin } from '@type/common';
import { UserAttributes } from '@supabase/supabase-js';
// Services
import supabase from './supabaseService';
// Types
import { IAccount } from '@type/account';
const login = async ({ email, password }: ILogin) => {
const { data, error } = await supabase.auth.signInWithPassword({
email,
@@ -40,4 +44,25 @@ const logout = async () => {
}
};
export { login, logout, getCurrentAccount };
const updateAccount = async ({ fullName, password }: IAccount) => {
let accountUpdate: UserAttributes | null = null;
if (password) {
accountUpdate = { password };
}
if (fullName) {
accountUpdate = { data: { fullName } };
}
const { data, error } = await supabase.auth.updateUser(accountUpdate!);
if (error) {
console.error(error.message);
throw new Error(error.message);
}
return data;
};
export { login, logout, getCurrentAccount, updateAccount };