Skip to content

Commit

Permalink
Feat: 마이페이지 수정 (#111)
Browse files Browse the repository at this point in the history
* feat: 프로필 수정 api 연결

* fix: 계좌번호 type 수정

* fix: 계좌번호 input 추가

* fix: rounded 수정

* fix: profileEditVerificationTypes 설정

* fix: 프로필 수정 로직 추가

* feat: 닉네임 수정 스키마 추가

* fix: profileImageUpload FieldValues 추가

* feat: 프로필 수정 api 연결

* remove: 불필요한 주석 삭제

* fix: 채팅방 계좌 설정

* remove: 불필요한 주석 제거
  • Loading branch information
woneeeee authored Feb 4, 2025
1 parent c29268b commit af65179
Show file tree
Hide file tree
Showing 9 changed files with 185 additions and 32 deletions.
13 changes: 10 additions & 3 deletions src/components/chat/bottomMenu/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import useTimerStore from '@/store/useTimerStore';
import { getCloseMatching } from '@/libs/apis/getCloseMatching.api';
import getExitChatRoom from '@/libs/apis/getExitChatRoom';
import useSSEStore from '@/store/useSSEStore';
import useUserStore from '@/store/useUserStore';

const BottomMenu = ({
onSendAccount,
Expand All @@ -29,6 +30,8 @@ const BottomMenu = ({
const nav = useNavigate();
const { messages } = useSSEStore();
const [isOwner, setIsOwner] = useState(false);
const { user } = useUserStore();
const accountNumber = user?.accountNumber || '계좌번호 없음';

messages.forEach((message) => {
if (message.topic === 'match_room_created') {
Expand All @@ -38,6 +41,10 @@ const BottomMenu = ({
});

const handleSendClick = () => {
if (!user?.accountNumber) {
openToast('등록된 계좌번호가 없습니다.', 'error');
return;
}
setShowAccountModal(true);
};

Expand Down Expand Up @@ -87,7 +94,7 @@ const BottomMenu = ({
};

const clickHandlers: Record<string, () => void> = {
'계좌 전송': isOwner ? handleSendClick : () => {},
'계좌 전송': handleSendClick,
'택시 호출': isOwner ? handleTaxiClick : () => {},
'매칭 마감': isOwner ? handleCloseMatching : () => {},
'매칭 취소': handleExitModal,
Expand All @@ -100,7 +107,7 @@ const BottomMenu = ({
key={index}
onClick={clickHandlers[item.label] || undefined}
className={`${
!isOwner && item.label !== '매칭 취소'
item.label !== '계좌 전송' && !isOwner && item.label !== '매칭 취소'
? 'cursor-not-allowed opacity-50'
: 'cursor-pointer'
}`}
Expand All @@ -112,7 +119,7 @@ const BottomMenu = ({
{showAccountModal && (
<SendAccountModal
onClose={() => setShowAccountModal(false)}
account="농협 302 XXXX XXXX XX"
account={accountNumber}
onSend={(accountInfo) => {
onSendAccount(accountInfo);
setShowAccountModal(false);
Expand Down
4 changes: 2 additions & 2 deletions src/components/chat/modal/sendAccountModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ const SendAccountModal: React.FC<SendAccountModalProps> = ({
}) => {
return (
<Modal onClose={onClose}>
<div className="flex items-center gap-2 mb-7 gap-3">
<p className="text-white text-captionHeader">{account}</p>
<div className="flex items-center gap-2 justify-center mb-7 gap-3">
<p className="text-white text-captionHeader text-center">{account}</p>
</div>
<Button
onClick={() => onSend(account)}
Expand Down
2 changes: 1 addition & 1 deletion src/components/commons/Input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ function Input<T extends FieldValues>({
id={name.toString()}
type={type}
placeholder={placeholder}
className={`border outline-none border-textDarkGray bg-transparent rounded-common p-3 pl-4 text-textDarkGray placeholder:text-body ${className ? className : ''}`}
className={`border outline-none border-textDarkGray bg-transparent rounded-modal p-3 pl-4 text-textDarkGray placeholder:text-body ${className ? className : ''}`}
{...field}
/>
{fieldState.error && (
Expand Down
14 changes: 6 additions & 8 deletions src/components/sign/userInfoVerification/ProfileImageUpload.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,24 @@
import { Control, Controller } from 'react-hook-form';
import { userInfoVerificationSchema } from '@/libs/schemas/auth';
import { z } from 'zod';
import { Control, Controller, FieldValues, Path } from 'react-hook-form';
import BasicProfileIcon from '@/assets/icon/basicProfileIcon.svg?react';
import CameraIcon from '@/assets/icon/cameraIcon.svg?react';
import ImageCancelIcon from '@/assets/icon/imageCancelIcon.svg?react';
import Button from '@/components/commons/Button';

interface ProfileImageUploadProps {
control: Control<z.infer<typeof userInfoVerificationSchema>>;
interface ProfileImageUploadProps<T extends FieldValues> {
control: Control<T>;
imagePreview: string | undefined;
setImagePreview: (value: string | undefined) => void;
}

const ProfileImageUpload = ({
const ProfileImageUpload = <T extends FieldValues>({
control,
imagePreview,
setImagePreview,
}: ProfileImageUploadProps) => {
}: ProfileImageUploadProps<T>) => {
return (
<Controller
control={control}
name="profilePicture"
name={'profilePicture' as Path<T>}
render={({ field: { onChange, ...field } }) => (
<>
<div className="flex items-center justify-center my-6">
Expand Down
11 changes: 11 additions & 0 deletions src/libs/apis/updateUserProfile.api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import client from './clients';
import { ProfileEditVerificationTypes } from 'gachTaxi-types';

export const updateUserProfile = async (data: ProfileEditVerificationTypes) => {
try {
const res = await client.patch('/api/members/info', data);
return res.data;
} catch (error) {
console.log('프로필 업데이트 실패', error);
}
};
19 changes: 19 additions & 0 deletions src/libs/schemas/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,19 @@ const realNameSchema = z.string().min(1, '본명을 입력해주세요!');

const genderSchema = z.enum(['MALE', 'FEMALE']).default('MALE');

const accountNumberSchema = z
.string()
.min(10, '올바른 계좌번호를 입력해주세요!')
.refine((value) => !isNaN(Number(value)), {
message: '계좌번호는 숫자로만 입력해야 합니다!',
})
.optional();

const editNickNameSchema = z
.string()
.min(1, '닉네임을 입력해주세요!')
.optional();

const profileImageSchema = z.optional(
z
.union([
Expand Down Expand Up @@ -100,6 +113,12 @@ export const userInfoVerificationSchema = z.object({
gender: genderSchema,
});

export const profileEditVerificationSchema = z.object({
profilePicture: profileImageSchema,
nickName: editNickNameSchema,
accountNumber: accountNumberSchema,
});

export const agreementsSchema = z.object({
termsAgreement: requiredAgreementSchema,
privacyAgreement: requiredAgreementSchema,
Expand Down
78 changes: 78 additions & 0 deletions src/pages/mathcing/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import Button from '@/components/commons/Button';
import Timer from '@/components/matchingInfo/TImer';
import useSSEStore from '@/store/useSSEStore';
import useTimerStore from '@/store/useTimerStore';
import { useEffect, useState } from 'react';

const MatchingInfoPage = () => {
const { reset } = useTimerStore();
const { initializeSSE, messages } = useSSEStore();

const [roomCapacity, setRoomCapacity] = useState<number>(0);
const [roomStatus, setRoomStatus] = useState<'searching' | 'matching'>(
'searching',
);

useEffect(() => {
initializeSSE();
}, [initializeSSE]);

useEffect(() => {
messages.forEach((message) => {
switch (message.topic) {
case 'match_member_joined':
setRoomCapacity((prev) => Math.max(prev + 1, 4));
break;

case 'match_member_cancelled':
setRoomCapacity((prev) => Math.max(prev - 1, 0));
break;

case 'match_room_created':
setRoomCapacity((prev) => Math.max(prev + 1, 4));
setRoomStatus('matching');
break;

default:
break;
}
});
}, [messages, reset]);

return (
<section className="flex-1 flex flex-col justify-between p-4">
<div className="w-full flex flex-col items-center mt-20">
{roomStatus === 'searching' ? (
<p className="font-bold text-header text-center">
매칭 방을 탐색중이에요! <br /> 조금만 기다려주세요!
</p>
) : (
<div className="w-full flex flex-col gap-2">
<p className="font-bold text-header text-center">
가치 탈 사람 <br />
찾는중...
</p>
<span className="font-medium text-captionHeader">
{roomCapacity}/4
</span>
</div>
)}
</div>
<div
className={` w-full flex justify-center flex-col gap-2 items-center ${roomStatus === 'searching' ? 'flex-grow' : ''}`}
>
<Timer />
<>택시아이콘자리</>
</div>
{roomStatus === 'matching' && (
<div className=" w-full mb-4">
<Button className="w-full" onClick={() => reset()}>
채팅방
</Button>
</div>
)}
</section>
);
};

export default MatchingInfoPage;
69 changes: 51 additions & 18 deletions src/pages/my-page/EditProfilePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,64 @@ import BackButton from '@/components/commons/BackButton';
import Button from '@/components/commons/Button';
import Input from '@/components/commons/Input';
import { z } from 'zod';
import { userInfoVerificationSchema } from '@/libs/schemas/auth';
import { UserInfoVerificationTypes } from 'gachTaxi-types';
import { profileEditVerificationSchema } from '@/libs/schemas/auth';
import { ProfileEditVerificationTypes } from 'gachTaxi-types';
import { zodResolver } from '@hookform/resolvers/zod';
import { useForm, SubmitHandler } from 'react-hook-form';
import ProfileImageUpload from '@/components/sign/userInfoVerification/ProfileImageUpload';
import useUploadImage from '@/hooks/useUploadImage';
import { updateUserProfile } from '@/libs/apis/updateUserProfile.api';
import { useToast } from '@/contexts/ToastContext';
import useUserStore from '@/store/useUserStore';
import handleAxiosError from '@/libs/apis/axiosError.api';

const EditProfilePage = () => {
const profileForm = useForm<z.infer<typeof userInfoVerificationSchema>>({
resolver: zodResolver(userInfoVerificationSchema),
const profileForm = useForm<z.infer<typeof profileEditVerificationSchema>>({
resolver: zodResolver(profileEditVerificationSchema),
defaultValues: {
nickname: '',
realName: '',
studentNumber: '',
gender: 'MALE',
nickName: '',
profilePicture: undefined,
accountNumber: '',
},
mode: 'onSubmit',
});

const currentImage = profileForm.watch('profilePicture');
const { imagePreview, uploadedImage, setImagePreview } =
useUploadImage(currentImage);
const { setUser } = useUserStore();
const { openToast } = useToast();

const handleSubmitChange: SubmitHandler<UserInfoVerificationTypes> = (
data,
) => {
const handleSubmitChange: SubmitHandler<
ProfileEditVerificationTypes
> = async (data) => {
try {
console.log(data);
console.log(uploadedImage);
profileForm.setValue('nickname', '');
} catch (e) {
console.error(e);
const updateData = profileForm.getValues();
if (
data.profilePicture !== uploadedImage &&
typeof data.profilePicture !== 'string'
) {
updateData.profilePicture = uploadedImage;
const res = await updateUserProfile(updateData);
if (res?.code === 200) {
const userData = res?.data;
setUser(userData);
openToast(res.message, 'success');
}
} else {
const res = await updateUserProfile(data);

if (res?.code === 200) {
if (res?.data) {
const userData = res.data;
setUser(userData);
}
openToast(res.message, 'success');
}
}
} catch (error) {
const errorMessage = handleAxiosError(error);
openToast(errorMessage, 'error');
}
};

Expand All @@ -48,7 +73,7 @@ const EditProfilePage = () => {
className="flex flex-col gap-2 w-full"
onSubmit={profileForm.handleSubmit(
handleSubmitChange as SubmitHandler<
z.infer<typeof userInfoVerificationSchema>
z.infer<typeof profileEditVerificationSchema>
>,
)}
>
Expand All @@ -60,12 +85,20 @@ const EditProfilePage = () => {

<Input
control={profileForm.control}
name="nickname"
name="nickName"
label="닉네임"
placeholder="닉네임을 입력해주세요"
type="text"
/>

<Input
control={profileForm.control}
name="accountNumber"
label="계좌번호"
placeholder="계좌번호를 입력해주세요"
type="text"
/>

<Button type="submit" className="w-full mt-8">
변경하기
</Button>
Expand Down
7 changes: 7 additions & 0 deletions src/types/auth.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ declare module 'gachTaxi-types' {
gender: 'MALE' | 'FEMALE';
}

interface ProfileEditVerificationTypes {
profilePicture?: file | string | undefined;
nickName?: string;
accountNumber?: string;
}

interface AgreementsTypes {
termsAgreement: boolean;
privacyAgreement: boolean;
Expand All @@ -46,6 +52,7 @@ declare module 'gachTaxi-types' {
email: string;
role: string;
gender: string;
accountNumber?: string;
}

// 회원가입 또는 로그인 시 반환되는 유저 정보
Expand Down

0 comments on commit af65179

Please sign in to comment.