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
39 changes: 33 additions & 6 deletions src/app/(with-header)/myprofile/_components/MyProfileContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,49 +4,63 @@ import { useEffect, useState } from 'react';
import { fetchWithAuth } from '@/lib/auth';
import { MyProfile, MyProfileData } from './MyProfile';
import LoadingSpinner from '@/components/LoadingSpinner';
import Refresh from '@/components/Refresh';

export default function MyProfileContainer() {
const [profileData, setProfileData] = useState<MyProfileData>();
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState('');

const getUserData = async () => {
setError('');
try {
setIsLoading(true);
const response = await fetchWithAuth(`${process.env.NEXT_PUBLIC_BASE_URL}/users/me`);

if (!response?.ok || response === null) {
setError('유저 데이터를 불러오는데 실패했습니다.');
return;
}

const data: MyProfileData = await response.json();
setProfileData(data);
} catch (error) {
console.error('유저 정보를 불러오는 중 문제가 발생했습니다:', error);
if (error instanceof Error) {
setError(error.message);
} else {
setError('알 수 없는 오류가 발생했습니다.');
}
} finally {
setIsLoading(false);
}
};

const upLoadImgFile = async (formData: FormData): Promise<string | undefined> => {
setError('');
try {
const response = await fetchWithAuth(`${process.env.NEXT_PUBLIC_BASE_URL}/images/upload`, {
method: 'POST',
body: formData,
});

if (!response?.ok || response === null) {
setError('이미지를 업로드하는데 실패했습니다.');
return;
}

const data = await response.json();
return data.url as string;
} catch (error) {
console.error('이미지 업로드 중 문제가 발생했습니다:', error);
return;
if (error instanceof Error) {
setError(error.message);
} else {
setError('알 수 없는 오류가 발생했습니다.');
}
}
};

const upLoadUserData = async (image: string, nickname: string): Promise<string | undefined> => {
setError('');
try {
const response = await fetchWithAuth(`${process.env.NEXT_PUBLIC_BASE_URL}/users/me`, {
method: 'PATCH',
Expand All @@ -61,14 +75,18 @@ export default function MyProfileContainer() {
}

if (!response?.ok || response === null) {
setError('데이터를 업데이트 하는데 실패했습니다.');
return;
}

const result = await response.json();
return result;
} catch (error) {
console.error('프로필 업로드 중 문제가 발생했습니다:', error);
return;
if (error instanceof Error) {
setError(error.message);
} else {
setError('알 수 없는 오류가 발생했습니다.');
}
}
};

Expand All @@ -78,7 +96,16 @@ export default function MyProfileContainer() {

if (isLoading) return <LoadingSpinner className='h-[530px] w-[280px] rounded-[16px] border border-gray-300 shadow-drop tablet:h-[247px] tablet:w-full mobile:h-[241px]' />;

if (!profileData) return <div></div>;
if (!profileData || error)
return (
<Refresh
handleLoad={getUserData}
boxStyle='h-[530px] w-[280px] rounded-[16px] border border-gray-300 shadow-drop tablet:h-[247px] tablet:w-full mobile:h-[241px] gap-[10px]'
buttonStyle='px-[20px] py-[8px]'
iconSize='w-[50px] h-[50px] mobile:w-[40px] mobile:h-[40px]'
iconTextGap='gap-[10px]'
/>
);

return <MyProfile profileData={profileData} upLoadImgFile={upLoadImgFile} upLoadUserData={upLoadUserData} />;
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import { useCallback, useEffect, useState } from 'react';
import { fetchWithAuth } from '@/lib/auth';
import emptyData from '@/assets/icons/empty_review.svg';
import { MyReview, MyReviewResponse } from '@/types/review-data';
import { MyReviewItem } from './MyReviewItem';
import { MyReviewItem } from '@/app/(with-header)/myprofile/_components/MyReviewItem';
import LoadingSpinner from '@/components/LoadingSpinner';
import Refresh from '@/components/Refresh';

export interface EditReviewData {
rating: number;
Expand All @@ -22,21 +23,28 @@ export interface EditReviewData {
export default function MyReviewListContainer({ setDataCount }: { setDataCount: (value: number) => void }) {
const [myReviewData, setMyReviewData] = useState<MyReview[]>([]);
const [isLoading, setIsloading] = useState(true);
const [error, setError] = useState('');

const getMyReview = useCallback(async () => {
setError('');
try {
setIsloading(true);
const response = await fetchWithAuth(`${process.env.NEXT_PUBLIC_BASE_URL}/users/me/reviews?limit=30`);

if (!response?.ok || response === null) {
setError('리뷰 데이터를 불러오는데 실패했습니다.');
return;
}

const data: MyReviewResponse = await response.json();
setMyReviewData(data.list);
setDataCount(data.totalCount);
} catch (error) {
console.error('리뷰를 불러오는 중 문제가 발생했습니다:', error);
if (error instanceof Error) {
setError(error.message);
} else {
setError('알 수 없는 오류가 발생했습니다.');
}
} finally {
setIsloading(false);
}
Expand All @@ -63,9 +71,20 @@ export default function MyReviewListContainer({ setDataCount }: { setDataCount:

if (isLoading) return <LoadingSpinner className='flex h-[228px] w-[800px] rounded-[16px] border border-gray-300 tablet:w-full mobile:w-full' />;

if (error)
return (
<Refresh
handleLoad={getMyReview}
boxStyle='flex h-[228px] w-[800px] rounded-[16px] border border-gray-300 tablet:w-full mobile:w-full gap-[10px]'
buttonStyle='px-[20px] py-[8px]'
iconSize='w-[50px] h-[50px] mobile:w-[40px] mobile:h-[40px]'
iconTextGap='gap-[10px]'
/>
);

if (myReviewData.length === 0)
return (
<div className='flex h-[80vh] w-full flex-col items-center justify-center gap-[24px] pc:w-[800px] mobile:h-[40vh] mobile:gap-[12px]'>
<div className='h-[80vh] w-full flex-col items-center justify-center gap-[24px] pc:w-[800px] mobile:h-[40vh] mobile:gap-[12px]'>
<Image className='h-[120px] w-[120px] mobile:h-[50px] mobile:w-[50px]' alt='데이터 없음' src={emptyData} priority />
<p className='text-lg font-normal text-gray-500 mobile:text-md'>내가 등록한 후기가 없어요</p>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,33 @@ import emptyData from '@/assets/icons/empty_review.svg';
import WineCard from '@/components/WineCard';
import { WineDataProps } from './MyWIneKebabDropDown ';
import LoadingSpinner from '@/components/LoadingSpinner';
import Refresh from '@/components/Refresh';

export default function MyWineListContainer({ setDataCount }: { setDataCount: (value: number) => void }) {
const [myWineData, setMyWineData] = useState<WineDetails[]>([]);
const [isLoading, setIsloading] = useState(true);
const [error, setError] = useState('');

const getMyWine = useCallback(async () => {
setError('');
try {
setIsloading(true);
const response = await fetchWithAuth(`${process.env.NEXT_PUBLIC_BASE_URL}/users/me/wines?limit=30`);

if (!response?.ok || response === null) {
setError('와인 데이터를 불러오는데 실패했습니다.');
return;
}

const data: MyWineListResponse = await response.json();
setMyWineData(data.list);
setDataCount(data.totalCount);
} catch (error) {
console.error('와인을 불러오는 중 문제가 발생했습니다:', error);
if (error instanceof Error) {
setError(error.message);
} else {
setError('알 수 없는 오류가 발생했습니다.');
}
} finally {
setIsloading(false);
}
Expand All @@ -53,6 +61,17 @@ export default function MyWineListContainer({ setDataCount }: { setDataCount: (v

if (isLoading) return <LoadingSpinner className='flex h-[228px] w-[800px] rounded-[16px] border border-gray-300 tablet:w-full mobile:w-full' />;

if (error)
return (
<Refresh
handleLoad={getMyWine}
boxStyle='flex h-[228px] w-[800px] rounded-[16px] border border-gray-300 tablet:w-full mobile:w-full gap-[10px]'
buttonStyle='px-[20px] py-[8px]'
iconSize='w-[50px] h-[50px] mobile:w-[40px] mobile:h-[40px]'
iconTextGap='gap-[10px]'
/>
);

if (myWineData.length === 0)
return (
<div className='flex h-[80vh] w-full flex-col items-center justify-center gap-[24px] pc:w-[800px] mobile:h-[40vh] mobile:gap-[12px]'>
Expand Down
30 changes: 30 additions & 0 deletions src/components/Refresh.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import Image from 'next/image';
import emptyData from '@/assets/icons/empty_review.svg';
import Button from '@/components/Button';

interface RefreshProps {
handleLoad: () => void;
boxStyle?: string;
iconSize?: string;
iconTextGap?: string;
buttonStyle?: string;
}

export default function Refresh({ handleLoad, buttonStyle = 'px-[30px] py-[10px]', iconSize = 'h-[136px] w-[136px]', iconTextGap = 'gap-[10px]', boxStyle = 'gap-[10px]' }: RefreshProps) {
const onClickRefreshBtn = () => {
handleLoad();
};

return (
<div className={`flex flex-col items-center justify-center ${boxStyle}`}>
<div className={`flex flex-col items-center justify-center ${iconTextGap}`}>
<Image className={` ${iconSize}`} alt='데이터 없음' src={emptyData} priority />
<div className='flex flex-col items-center justify-center'>
<p className='text-center text-lg text-gray-500 mobile:text-md'>서버에서 데이터를 불러오는 데</p>
<p className='text-center text-lg text-gray-500 mobile:text-md'>실패했습니다.</p>
</div>
</div>
<Button onClick={onClickRefreshBtn} text='새로고침' className={`rounded-[100px] ${buttonStyle}`} />
</div>
);
}