Skip to content

Commit

Permalink
Merge branch 'feature/profile' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
hutamatr committed Aug 18, 2023
2 parents 804fc6c + fb996ca commit ae0eaf8
Show file tree
Hide file tree
Showing 25 changed files with 914 additions and 348 deletions.
2 changes: 1 addition & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<link rel="icon" type="image/svg+xml" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Urban Fashion</title>
</head>
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "urban-fashion-shop",
"private": true,
"version": "1.1.4",
"version": "1.2.3",
"type": "module",
"engines": {
"node": ">= 16",
Expand Down Expand Up @@ -35,6 +35,7 @@
"react-hook-form": "^7.45.2",
"react-hot-toast": "^2.4.1",
"react-icons": "^4.10.1",
"react-phone-number-input": "^3.3.2",
"react-redux": "^8.1.2",
"react-router-dom": "^6.14.2",
"react-toggle-dark-mode": "^1.1.1",
Expand Down
Binary file added public/favicon.ico
Binary file not shown.
4 changes: 2 additions & 2 deletions src/components/Footer/NavigationFooter.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import clsx from 'clsx';
import { Link } from 'react-router-dom';

import { logoutHandler } from '@store/authSlice';
import { logoutUser } from '@store/authSlice';
import { useAppDispatch, useAppSelector } from '@store/store';

export default function NavigationFooter() {
Expand All @@ -11,7 +11,7 @@ export default function NavigationFooter() {
const dispatch = useAppDispatch();

const logOutUserHandler = () => {
dispatch(logoutHandler());
dispatch(logoutUser());
};

return (
Expand Down
7 changes: 1 addition & 6 deletions src/components/Layout/Layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,7 @@ export default function Layout() {
>
<Navigation />
</header>
<main
className={clsx(
'layout flex min-h-screen flex-col gap-y-5',
'dark:bg-dark-brown'
)}
>
<main className={clsx('layout flex flex-col', 'dark:bg-dark-brown')}>
<Outlet />
</main>
<Footer />
Expand Down
202 changes: 145 additions & 57 deletions src/components/MyAccount/AccountDetails.tsx
Original file line number Diff line number Diff line change
@@ -1,70 +1,158 @@
import { zodResolver } from '@hookform/resolvers/zod';
import clsx from 'clsx';
import { useEffect, useState } from 'react';
import { SubmitHandler, useForm } from 'react-hook-form';
import { toast } from 'react-hot-toast';
import PhoneInput from 'react-phone-number-input';
import { z } from 'zod';

import Input from '@components/UI/Input';
import Loading from '@components/UI/Loading';

import { useAppSelector } from '@store/store';
import { useAppDispatch, useAppSelector } from '@store/store';
import { updateUserDetail } from '@store/userSlice';

import { IUser } from 'types/types';
import { userDetailSchema } from '@utils/formSchema';

export default function AccountDetails({
username,
email,
address,
phone_number,
}: IUser) {
const { status } = useAppSelector((state) => state.auth);
import 'react-phone-number-input/style.css';

import { IUserUpdate } from 'types/types';

type FormSchemaType = z.infer<typeof userDetailSchema>;

export default function AccountDetails() {
const dispatch = useAppDispatch();
// const { status } = useAppSelector((state) => state.auth);

const [phoneNumber, setPhoneNumber] = useState<string>('');
const {
register,
handleSubmit,
formState: { isSubmitting },
setValue,
} = useForm<FormSchemaType>({
resolver: zodResolver(userDetailSchema),
});

const { user, status } = useAppSelector((state) => state.user);

useEffect(() => {
if (user?.id) {
setValue('name', user.username);
setValue('email', user.email);
setValue('phone', user.phone_number);
setValue('address', user.address);
setPhoneNumber(user.phone_number);
}
}, [
setValue,
user?.id,
user?.username,
user?.email,
user?.phone_number,
user?.address,
]);

const updateUserHandler: SubmitHandler<FormSchemaType> = async (
data,
event
) => {
event?.preventDefault();

const updatedUser: IUserUpdate = {
username: data.name,
email: data.email,
phone_number: phoneNumber,
address: data.address,
};

const res = await dispatch(updateUserDetail(updatedUser));

if (res.meta.requestStatus === 'fulfilled') {
toast.success('Update user successfully!', { duration: 3000 });
}

if (res.meta.requestStatus === 'rejected') {
toast.error('Update user detail failed!', {
duration: 3000,
});
}
};

return (
<section className='mx-auto p-6'>
{status === 'pending' && <Loading />}
{status === 'rejected' && (
<p className='mx-auto py-6 text-center font-medium uppercase text-red-700'>
Failed to view user data!
</p>
)}
{status === 'fulfilled' && (
<>
<h2
className={clsx(
'mb-4 text-lg font-semibold',
'dark:text-white-bone'
)}
>
User Detail
</h2>
<div
className={clsx('mb-6 flex items-center', 'dark:text-white-bone')}
>
<span className='w-24'>Name :</span>
<span>{username}</span>
</div>
<div
className={clsx('mb-6 flex items-center', 'dark:text-white-bone')}
>
<span className='w-24'>Email :</span>
<span>{email}</span>
</div>
<div
className={clsx('mb-6 flex items-center', 'dark:text-white-bone')}
>
<span className='w-24'>Phone :</span>
<span>{phone_number}</span>
<section className={clsx('', 'md:px-6')}>
<div
className={clsx(
'mb-4 flex flex-col gap-y-2',
'md:flex-row md:items-center md:gap-x-8'
)}
>
<h2 className={clsx('text-2xl font-semibold', 'dark:text-white-bone')}>
Your Profile
</h2>
{status === 'rejected' && (
<p className='text-sm text-red-700'>Failed to view user data!</p>
)}
</div>
{status === 'pending' ? (
<Loading />
) : (
<form
onSubmit={handleSubmit(updateUserHandler)}
className={clsx('flex flex-col gap-y-4', 'md:gap-y-5')}
>
<div className={clsx('grid grid-cols-1 gap-4', 'md:grid-cols-3')}>
<Input
title='Name'
type='text'
{...register('name', { required: true })}
/>
<Input
title='Email'
type='email'
{...register('email', { required: true })}
/>
<div className='flex flex-col gap-y-1'>
<label
htmlFor='phone'
className={clsx(
'text-xs font-medium text-dark-brown',
'dark:text-white-bone'
)}
>
Phone Number
</label>
<PhoneInput
placeholder='Enter phone number'
value={phoneNumber}
onChange={(value) => setPhoneNumber(value as string)}
className={clsx(
'w-full rounded bg-white-bone font-medium',
'dark:bg-dark-brown dark:placeholder:text-white-bone',
'placeholder:text-sm focus:ring-0'
)}
/>
</div>
</div>
{/* <h2
className={clsx(
'mb-4 text-lg font-semibold',
'dark:text-white-bone'
)}
>
Address :
</h2> */}
<div
className={clsx('mb-6 flex items-center', 'dark:text-white-bone')}
>
<span className='w-24'>Address :</span>
<span>{address}</span>
<Input
title='Address'
isTextArea={true}
{...register('address', { required: true })}
/>
<div className='flex flex-col gap-y-2'>
<button
type='submit'
disabled={isSubmitting}
className={clsx(
'mx-auto block w-fit flex-row gap-x-2 rounded-sm bg-dark-brown px-4 py-3 font-light text-white',
'disabled:cursor-not-allowed disabled:bg-dark-brown/80',
'dark:bg-white-bone dark:font-semibold dark:text-dark-brown dark:disabled:bg-white-bone/80'
)}
>
{isSubmitting ? 'Updating...' : 'Update Profile'}
</button>
</div>
</>
</form>
)}
</section>
);
Expand Down
120 changes: 120 additions & 0 deletions src/components/MyAccount/ChangePassword.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import { zodResolver } from '@hookform/resolvers/zod';
import clsx from 'clsx';
import { useState } from 'react';
import { SubmitHandler, useForm } from 'react-hook-form';
import { toast } from 'react-hot-toast';
import { z } from 'zod';

import Input from '@components/UI/Input';

import { changePassword, logoutUser } from '@store/authSlice';
import { useAppDispatch } from '@store/store';

import { changePasswordSchema } from '@utils/formSchema';

import { IChangePassword } from 'types/types';

type FormSchemaType = z.infer<typeof changePasswordSchema>;

export default function ChangePassword() {
const [isPassView, setIsPassView] = useState(false);
const {
register,
handleSubmit,
reset,
formState: { isSubmitting },
} = useForm<FormSchemaType>({
resolver: zodResolver(changePasswordSchema),
});

const dispatch = useAppDispatch();

const viewPasswordHandler = () => {
setIsPassView((prevState) => !prevState);
};

const updatePasswordHandler: SubmitHandler<FormSchemaType> = async (
data,
event
) => {
event?.preventDefault();

const changedPassword: IChangePassword = {
currentPassword: data.currentPassword,
password: data.newPassword,
passwordConfirmation: data.confirmNewPassword,
};

const res = await dispatch(changePassword(changedPassword));

if (res.meta.requestStatus === 'fulfilled') {
dispatch(logoutUser())
.unwrap()
.then(() => {
setTimeout(() => {
toast.success('Change password successfully!', {
duration: 3000,
});
}, 1000);
});
}

if (res.meta.requestStatus === 'rejected') {
toast.error('Change password failed!', {
duration: 3000,
});
}

reset();
};

return (
<section className={clsx('flex flex-col gap-y-4', 'md:px-6')}>
<h2 className={clsx('text-2xl font-semibold', 'dark:text-white-bone')}>
Change Password*
</h2>
<form
onSubmit={handleSubmit(updatePasswordHandler)}
className={clsx(
'flex w-full flex-col gap-y-4',
'md:max-w-xl md:gap-y-5'
)}
>
<Input
title='Current Password'
type={isPassView ? 'text' : 'password'}
isPassword
isPassView={isPassView}
onViewPasswordHandler={viewPasswordHandler}
{...register('currentPassword', { required: true })}
/>
<Input
title='New Password'
type={isPassView ? 'text' : 'password'}
{...register('newPassword', { required: true })}
/>
<Input
title='Confirm New Password'
type={isPassView ? 'text' : 'password'}
{...register('confirmNewPassword', { required: true })}
/>
<div className='flex flex-col gap-y-2'>
<button
type='submit'
disabled={isSubmitting}
className={clsx(
'mx-auto block w-fit flex-row gap-x-2 rounded-sm bg-dark-brown px-4 py-3 font-light text-white',
'disabled:cursor-not-allowed disabled:bg-dark-brown/80',
'dark:bg-white-bone dark:font-semibold dark:text-dark-brown dark:disabled:bg-white-bone/80'
)}
>
{isSubmitting ? 'Updating...' : 'Change Password'}
</button>
</div>
</form>
<span className={clsx('text-xs text-dark-brown', 'dark:text-white-bone')}>
* you will be logged in again after changing your password
</span>
</section>
);
}
Loading

0 comments on commit ae0eaf8

Please sign in to comment.