Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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