diff --git a/hotel-management/src/App.tsx b/hotel-management/src/App.tsx
index e6edaa6..534dbb3 100644
--- a/hotel-management/src/App.tsx
+++ b/hotel-management/src/App.tsx
@@ -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() {
} />
} />
} />
+ } />
} />
} />
diff --git a/hotel-management/src/components/Sidebar/index.tsx b/hotel-management/src/components/Sidebar/index.tsx
index b393109..7d97768 100644
--- a/hotel-management/src/components/Sidebar/index.tsx
+++ b/hotel-management/src/components/Sidebar/index.tsx
@@ -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 (
- {heading}
+ navigate('/')}>{heading}
);
diff --git a/hotel-management/src/components/Sidebar/styled.ts b/hotel-management/src/components/Sidebar/styled.ts
index 2973bef..7fa1822 100644
--- a/hotel-management/src/components/Sidebar/styled.ts
+++ b/hotel-management/src/components/Sidebar/styled.ts
@@ -13,6 +13,8 @@ const Heading = styled.h1`
text-transform: uppercase;
text-align: center;
+ cursor: pointer;
+
color: var(--primary-color);
`;
diff --git a/hotel-management/src/hooks/authentication/useUpdateAccount.ts b/hotel-management/src/hooks/authentication/useUpdateAccount.ts
new file mode 100644
index 0000000..f8e9e06
--- /dev/null
+++ b/hotel-management/src/hooks/authentication/useUpdateAccount.ts
@@ -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 };
diff --git a/hotel-management/src/pages/Account/AccountInforForm.tsx b/hotel-management/src/pages/Account/AccountInforForm.tsx
new file mode 100644
index 0000000..1494b7f
--- /dev/null
+++ b/hotel-management/src/pages/Account/AccountInforForm.tsx
@@ -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();
+
+ const onSubmit = (data: IAccountForm) => {
+ updateAccount(data);
+ };
+
+ return (
+
+
+
+
+
+
+
+
+
+
+ Update account
+
+
+
+ );
+};
+
+export default AccountInforForm;
diff --git a/hotel-management/src/pages/Account/AccountPasswordForm.tsx b/hotel-management/src/pages/Account/AccountPasswordForm.tsx
new file mode 100644
index 0000000..8f97e99
--- /dev/null
+++ b/hotel-management/src/pages/Account/AccountPasswordForm.tsx
@@ -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();
+
+ useEffect(() => {
+ if (errors?.password?.message) {
+ toast.error(PASSWORD_CONDITION);
+ }
+ }, [errors?.password?.message]);
+
+ const onSubmit = (data: IAccountPasswordForm) => {
+ updateAccount(data, {
+ onSuccess: () => {
+ reset();
+ },
+ });
+ };
+
+ return (
+
+
+ isValidRegex(REGEX.PASSWORD, v) ||
+ PASSWORD_NOT_MATCH_REQUIREMENT,
+ },
+ })}
+ />
+
+
+
+
+ getValues().password === value || PASSWORD_NOT_MATCH,
+ })}
+ />
+
+
+
+
+ Update password
+
+
+
+ );
+};
+
+export default AccountPasswordForm;
diff --git a/hotel-management/src/pages/Account/index.tsx b/hotel-management/src/pages/Account/index.tsx
new file mode 100644
index 0000000..5597e61
--- /dev/null
+++ b/hotel-management/src/pages/Account/index.tsx
@@ -0,0 +1,26 @@
+// Styled
+import { FormArea, StyledAccount, SubTitle, Title } from './styled';
+
+// Components
+import AccountInforForm from './AccountInforForm';
+import AccountPasswordForm from './AccountPasswordForm';
+
+const Account = () => {
+ return (
+
+ Update account
+
+
+ Update information
+
+
+
+
+ Update password
+
+
+
+ );
+};
+
+export default Account;
diff --git a/hotel-management/src/pages/Account/styled.ts b/hotel-management/src/pages/Account/styled.ts
new file mode 100644
index 0000000..80d01a1
--- /dev/null
+++ b/hotel-management/src/pages/Account/styled.ts
@@ -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 };
diff --git a/hotel-management/src/services/authenticationService.ts b/hotel-management/src/services/authenticationService.ts
index 5a0e5f1..151730e 100644
--- a/hotel-management/src/services/authenticationService.ts
+++ b/hotel-management/src/services/authenticationService.ts
@@ -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 };