Skip to content
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
5f8dec2
setting(DEVING-72): ๋กœ์ปฌ https ์‹คํ–‰ ํ™˜๊ฒฝ ์„ธํŒ…
lee1nna Mar 10, 2025
a6c9cfa
feat(DEVING-72): ๋ชจ์ž„ ๋ชฉ๋ก ํ™”๋ฉด ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ถ”๊ฐ€
lee1nna Mar 10, 2025
79b2805
feat(DEVING-72): ๋ชจ์ž„ ๋ชฉ๋ก ํ™”๋ฉด ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ถ”๊ฐ€
lee1nna Mar 10, 2025
4719a31
Merge branch 'dev' into feat/refactor/meeting-list-mvp-feedback/DEVINโ€ฆ
lee1nna Mar 10, 2025
e54c553
Merge branch 'dev' into feat/refactor/meeting-list-mvp-feedback/DEVINโ€ฆ
lee1nna Mar 11, 2025
0528676
feat(DEVING-72): ์ฐœํ•˜๊ธฐ ์—๋Ÿฌ์ƒํƒœ์— ๋”ฐ๋ฅธ ์˜ˆ์™ธ์ฒ˜๋ฆฌ ์ถ”๊ฐ€
lee1nna Mar 12, 2025
499c1fa
Merge branch 'dev' into feat/refactor/meeting-list-mvp-feedback/DEVINโ€ฆ
lee1nna Mar 12, 2025
8bddfb3
feat(DEVING-72): ์ฐœํ•˜๊ธฐ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ถ”๊ฐ€
lee1nna Mar 12, 2025
5379dd1
Merge branch 'dev' into feat/refactor/meeting-list-mvp-feedback/DEVINโ€ฆ
lee1nna Mar 13, 2025
002b5e9
Merge branch 'dev' into feat/refactor/meeting-list-mvp-feedback/DEVINโ€ฆ
lee1nna Mar 13, 2025
406fb87
feat(DEVING-72): ๊ณตํ†ต ์—๋Ÿฌ ์ปดํฌ๋„ŒํŠธ ์ถ”๊ฐ€
lee1nna Mar 14, 2025
53730ac
Merge branch 'dev' into feat/refactor/meeting-list-mvp-feedback/DEVINโ€ฆ
lee1nna Mar 14, 2025
540879a
feat(DEVING-72): ๊ณตํ†ต ์—๋Ÿฌ ์ฒ˜๋ฆฌ ์ œ์™ธ
lee1nna Mar 15, 2025
a643292
feat(DEVING-72): fetchQuery -> prefetchQuery๋กœ ์ˆ˜์ •
lee1nna Mar 15, 2025
1e50d75
Merge branch 'dev' into feat/refactor/meeting-list-mvp-feedback/DEVINโ€ฆ
lee1nna Mar 15, 2025
2dea374
feat(DEVING-72): ์ฐœํ•œ ๋ชจ์ž„ ์ฐœํ•ด์ œ ๊ธฐ๋Šฅ ์ถ”๊ฐ€
lee1nna Mar 15, 2025
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
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,7 @@ yarn-error.log*
# typescript
*.tsbuildinfo
next-env.d.ts

# key
/localhost-key.pem
/localhost.pem
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"dev:https": "node server.js",
"build": "next build",
"start": "next start",
"https:dev": "node server.js",
"lint": "next lint",
"lint:fix": "eslint --fix \"src/**/*.{js,jsx,ts,tsx}\"",
"format": "prettier --write \"**/*.{js,jsx,ts,tsx}\"",
Expand Down
13 changes: 7 additions & 6 deletions src/app/meeting/_features/MeetingList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ const MeetingList = () => {
}

return (
<div className="mt-[126px]">
<div className="mt-12">
<div className="typo-head1 mb-10 px-4 text-Cgray800">
{translateCategoryNameToKor(categoryStr)} ๋ชจ์ž„ ๋ชฉ๋ก
</div>
Expand Down Expand Up @@ -143,8 +143,9 @@ const MeetingList = () => {
{/* ๋ชจ์ž„ ๋ฆฌ์ŠคํŠธ ์›น๋ทฐ */}
{breakpoint === 'desktop' && (
<div className="flex-col sm:hidden lg:flex">
{allMeetings.map((meeting) => (
{allMeetings.map((meeting, index) => (
<HorizonCard
className={`animate-fadeInUp ${index % 2 === 0 ? 'delay-100' : 'delay-200'}`}
onClick={handleMoveDetailPage}
key={meeting.meetingId}
meetingId={meeting.meetingId}
Expand Down Expand Up @@ -179,10 +180,10 @@ const MeetingList = () => {
{/* ๋ชจ์ž„ ๋ฆฌ์ŠคํŠธ ํ…Œ๋ธ”๋ฆฟ๋ทฐ */}
{breakpoint === 'tablet' && (
<div className="hidden flex-col md:flex lg:hidden">
{allMeetings.map((meeting) => (
{allMeetings.map((meeting, index) => (
<HorizonCard
onClick={handleMoveDetailPage}
className="items-center"
className={`animate-fadeInUp items-center ${index % 2 === 0 ? 'delay-100' : 'delay-200'}`}
key={meeting.meetingId}
meetingId={meeting.meetingId}
category={translateCategoryNameToKor(categoryStr)}
Expand Down Expand Up @@ -218,10 +219,10 @@ const MeetingList = () => {
{/* ๋ชจ์ž„ ๋ฆฌ์ŠคํŠธ ๋ชจ๋ฐ”์ผ๋ทฐ */}
{breakpoint === 'mobile' && (
<div className="flex w-full flex-wrap items-center justify-center md:hidden lg:hidden">
{allMeetings.map((meeting) => (
{allMeetings.map((meeting, index) => (
<VerticalCard
onClick={handleMoveDetailPage}
className="h-[380px]"
className={`h-[380px] animate-fadeInUp items-center ${index % 2 === 0 ? 'delay-100' : 'delay-200'}`}
thumbnailHeight={160}
thumbnailWidth={311}
category={translateCategoryNameToKor(categoryStr)}
Expand Down
3 changes: 2 additions & 1 deletion src/app/meeting/_features/RecommendMeeting.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,12 @@ const RecommendMeeting = () => {
scrollbarWidth: 'thin',
scrollbarColor: '#a0aec0 transparent',
}}
className="hidden overflow-hidden overflow-x-auto pb-4 md:flex lg:flex"
className={`hidden overflow-hidden overflow-x-auto pb-4 md:flex lg:flex`}
>
{meetings?.map((meeting: TopMeeting) => (
<VerticalCard
onClick={handleMoveDetailPage}
className="animate-slideDown hover:animate-slideUp"
category={translateCategoryNameToKor(categoryStr)}
key={meeting.meetingId}
meetingId={meeting.meetingId}
Expand Down
30 changes: 30 additions & 0 deletions src/components/common/ErrorComponent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { useRouter } from 'next/navigation';

import { Button } from '../ui/Button';

interface ErrorComponentProps {
className?: string;
}

const ErrorComponent = ({ className }: ErrorComponentProps) => {
const router = useRouter();
const retryHandler = () => {
router.refresh();
};
return (
<div
className={`flex flex-col justify-center text-white ${className} h-screen w-full`}
>
<div className="text-center">
๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.
<br />
๋‹ค์‹œ ์‹œ๋„ํ•ด์ฃผ์„ธ์š”.
</div>
<div className="mt-10 flex w-full justify-center">
<Button onClick={retryHandler}>๋‹ค์‹œ ์‹œ๋„ํ•˜๊ธฐ</Button>
</div>
</div>
);
};

export default ErrorComponent;
39 changes: 39 additions & 0 deletions src/components/ui/CardImage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// components/CardImage.js
import Image from 'next/image';
import { useState } from 'react';
import React from 'react';

interface CardImageProps {
src: string;
width: number;
height: number;
}

const CardImage = React.memo(({ src, width, height }: CardImageProps) => {
const [thumbnail, setThumbnail] = useState(src);
const [thumbnailLoaded, setThumbnailLoaded] = useState(false);
return (
<div
className="relative flex-shrink-0"
style={{ height: `${height}px`, width: `${width}px` }}
>
{!thumbnailLoaded && (
<div className="animate-pulse h-full w-full rounded-[20px] bg-Cgray200"></div>
)}
<Image
className="transform-gpu rounded-[20px] object-cover"
src={thumbnail}
alt="card_image"
fill
onError={() => {
setThumbnail('/thumbnail.jpg');
}}
onLoad={() => setThumbnailLoaded(true)}
/>
</div>
);
});

CardImage.displayName = 'CardImage';

export default CardImage;
27 changes: 12 additions & 15 deletions src/components/ui/HorizonCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import useLikeHandler from '@/hooks/common/useLikeHandler';
import { getIconComponent } from '@/util/getIconDetail';
import { translateCategoryNameToEng } from '@/util/searchFilter';
import { Heart, Map } from 'lucide-react';
import Image from 'next/image';
import { useParams, useRouter } from 'next/navigation';
import { useState } from 'react';
import { IMeetingSearchCondition } from 'types/meeting';

import { Button } from './Button';
import CardImage from './CardImage';
import { Progress } from './Progress';
import Modal from './modal/Modal';
import TechButton from './tech-stack/tech-stack-components/TechButton';
Expand Down Expand Up @@ -61,12 +61,17 @@ const HorizonCard = ({
),
});

const [like, setLike] = useState(false);

const handleLikeButton = (e: React.MouseEvent<HTMLButtonElement>) => {
e.stopPropagation();
toggleLike(isLike);
setLike(true);
setTimeout(() => {
setLike(false);
}, 500);
};

// TODO: ๋ฆฌํŒฉํ† ๋ง ์˜ˆ์ •
const { id } = useParams();

const handleClickCard = () => {
Expand All @@ -81,9 +86,6 @@ const HorizonCard = ({
router.push('/login');
};

const [thumbnail, setThumbnail] = useState(thumbnailUrl);
const [thumbnailLoaded, setThumbnailLoaded] = useState(false);

return (
<div
className={`relative flex h-auto w-full flex-shrink-0 ${!id && 'cursor-pointer'} bg-BG p-4 ${className}`}
Expand All @@ -102,7 +104,7 @@ const HorizonCard = ({
</Modal>
{showLikeButton && (
<Button
className="absolute right-4 top-4 h-auto w-auto"
className={`absolute right-4 top-4 h-auto w-auto ${like ? 'animate-heartbeat' : ''}`}
variant="text"
size="sm"
onClick={(e) => handleLikeButton(e)}
Expand All @@ -122,15 +124,10 @@ const HorizonCard = ({
className="relative flex-shrink-0"
style={{ height: `${thumbnailHeight}px`, width: `${thumbnailWidth}px` }}
>
{!thumbnailLoaded && (
<div className="h-full w-full animate-pulse rounded-[20px] bg-Cgray200"></div>
)}
<Image
className="rounded-[20px] object-cover"
src={thumbnail ? thumbnail : '/thumbnail.jpg'}
alt="card_thumbnail"
fill
onError={() => setThumbnail('/thumbnail.jpg')}
<CardImage
src={thumbnailUrl ? thumbnailUrl : '/thumbnail.jpg'}
width={thumbnailWidth}
height={thumbnailHeight}
/>
</div>
<div className="flex min-w-0 flex-1 flex-col justify-center px-[10px] md:px-[30px] lg:px-[40px]">
Expand Down
28 changes: 13 additions & 15 deletions src/components/ui/VerticalCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ import { getIconComponent } from '@/util/getIconDetail';
import { translateCategoryNameToEng } from '@/util/searchFilter';
import { useQueryClient } from '@tanstack/react-query';
import { Heart, Map } from 'lucide-react';
import Image from 'next/image';
import { useRouter } from 'next/navigation';
import { useState } from 'react';
import { IMeetingSearchCondition } from 'types/meeting';

import { Button } from './Button';
import CardImage from './CardImage';
import { Progress } from './Progress';
import Modal from './modal/Modal';
import TechButton from './tech-stack/tech-stack-components/TechButton';
Expand Down Expand Up @@ -66,9 +66,15 @@ const VerticalCard = ({
),
});

const [like, setLike] = useState(false);

const handleLikeButton = (e: React.MouseEvent<HTMLButtonElement>) => {
e.stopPropagation();
toggleLike(isLike);
setLike(true);
setTimeout(() => {
setLike(false);
}, 500);
};

const [isLoginModalOpen, setIsLoginModalOpen] = useState(false);
Expand All @@ -78,12 +84,10 @@ const VerticalCard = ({
router.push('/login');
};

const [thumbnail, setThumbnail] = useState(thumbnailUrl);
const [thumbnailLoaded, setThumbnailLoaded] = useState(false);

return (
<div
className={`flex h-[490px] w-[335px] cursor-pointer flex-col justify-between bg-BG p-4 ${className}`}
style={{ transformStyle: 'preserve-3d' }}
role="presentation"
onClick={handleClickCard}
>
Expand All @@ -101,16 +105,10 @@ const VerticalCard = ({
className={'relative'}
style={{ height: `${thumbnailHeight}px`, width: `${thumbnailWidth}px` }}
>
{!thumbnailLoaded && (
<div className="h-full w-full animate-pulse rounded-[20px] bg-Cgray200"></div>
)}
<Image
className="flex-shrink-0 rounded-[20px] object-cover"
src={thumbnail ? thumbnail : '/thumbnail.jpg'}
alt="card_thumbnail"
fill
onError={() => setThumbnail('/thumbnail.jpg')}
onLoad={() => setThumbnailLoaded(true)}
<CardImage
src={thumbnailUrl ? thumbnailUrl : '/thumbnail.jpg'}
width={thumbnailWidth}
height={thumbnailHeight}
/>
</div>
<div className="mt-4">
Expand All @@ -120,7 +118,7 @@ const VerticalCard = ({
</span>
{showLikeButton && (
<Button
className="relative h-auto w-auto"
className={`relative h-auto w-auto ${like ? 'animate-heartbeat' : ''}`}
variant="text"
size="sm"
onClick={(e) => handleLikeButton(e)}
Expand Down
46 changes: 30 additions & 16 deletions src/hooks/common/useLikeHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ import {
MEETING_QUERY_KEYS,
meetingKeys,
} from '@/hooks/queries/useMeetingQueries';
import { getAccessToken } from '@/lib/serverActions';
import { InfiniteData, QueryKey, useQueryClient } from '@tanstack/react-query';
import { AxiosError } from 'axios';
import { useParams } from 'next/navigation';
import {
ErrorData,
IMeetingSearchCondition,
Paginated,
SearchMeeting,
Expand Down Expand Up @@ -123,9 +124,20 @@ const useLikeHandler = ({
onSuccess: () => {
showToast('์ฐœํ•œ ๋ชจ์ž„์— ์ถ”๊ฐ€๋˜์—ˆ์Šต๋‹ˆ๋‹ค!', 'success', { duration: 2000 });
},
onError: () => {
// TODO: ๋‚ด๊ฐ€ ๋งŒ๋“  ๋ชจ์ž„ ์ข‹์•„์š” ํ•  ๊ฒฝ์šฐ toast ๋ฌธ๊ตฌ ๋ณ€๊ฒฝ
showToast('์ž ์‹œ ํ›„ ๋‹ค์‹œ ์‹œ๋„ํ•ด์ฃผ์„ธ์š”', 'error', { duration: 3000 });
onError: (err: AxiosError<ErrorData>) => {
// ์—๋Ÿฌ ์ƒํƒœ์— ๋”ฐ๋ฅธ ์˜ˆ์™ธ์ฒ˜๋ฆฌ
if (
err.status === 403 &&
err?.response?.data?.data?.entityType === 'Meeting'
) {
showToast('๋‚ด๊ฐ€ ๋งŒ๋“  ๋ชจ์ž„์€ ์ฐœํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.', 'error', {
duration: 3000,
});
} else if ((err.status === 401 || err.status === 403) && onAuthRequired) {
onAuthRequired();
} else {
showToast('์ž ์‹œ ํ›„ ๋‹ค์‹œ ์‹œ๋„ํ•ด์ฃผ์„ธ์š”', 'error', { duration: 3000 });
}

// ๋ชจ์ž„ ๋ชฉ๋ก
if (category && searchQuery) {
Expand Down Expand Up @@ -190,9 +202,20 @@ const useLikeHandler = ({
onSuccess: () => {
showToast('์ฐœํ•œ ๋ชจ์ž„์—์„œ ์‚ญ์ œ๋˜์—ˆ์Šต๋‹ˆ๋‹ค!', 'success', { duration: 2000 });
},
onError: () => {
// ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ
showToast('์ž ์‹œ ํ›„ ๋‹ค์‹œ ์‹œ๋„ํ•ด์ฃผ์„ธ์š”', 'error', { duration: 3000 });
onError: (err: AxiosError<ErrorData>) => {
// ์—๋Ÿฌ ์ƒํƒœ์— ๋”ฐ๋ฅธ ์˜ˆ์™ธ์ฒ˜๋ฆฌ
if (
err.status === 403 &&
err?.response?.data?.data?.entityType === 'Meeting'
) {
showToast('๋‚ด๊ฐ€ ๋งŒ๋“  ๋ชจ์ž„์€ ์ฐœํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.', 'error', {
duration: 3000,
});
} else if ((err.status === 401 || err.status === 403) && onAuthRequired) {
onAuthRequired();
} else {
showToast('์ž ์‹œ ํ›„ ๋‹ค์‹œ ์‹œ๋„ํ•ด์ฃผ์„ธ์š”', 'error', { duration: 3000 });
}

// ๋ชจ์ž„ ๋ชฉ๋ก
if (category && searchQuery) {
Expand Down Expand Up @@ -226,15 +249,6 @@ const useLikeHandler = ({

// ์ข‹์•„์š” ํ† ๊ธ€ ํ•จ์ˆ˜
const toggleLike = async (isLiked: boolean) => {
const token = await getAccessToken();
if (!token) {
// ๋กœ๊ทธ์ธ ์ƒํƒœ๊ฐ€ ์•„๋‹ˆ๋ผ๋ฉด, onAuthRequired ์ฝœ๋ฐฑ ์‹คํ–‰
if (onAuthRequired) {
onAuthRequired();
}
return;
}

if (!isLiked) {
likeMutation();
} else {
Expand Down
5 changes: 5 additions & 0 deletions src/types/meeting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ interface Paginated<T> {
empty: boolean;
}

interface ErrorData {
data: { entityType: string; errorMessage: string; request: string };
}

export type {
CategoryTitle,
TopMeeting,
Expand All @@ -69,4 +73,5 @@ export type {
SearchMeeting,
IMeetingSearchCondition,
Paginated,
ErrorData,
};
Loading
Loading