Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 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
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,5 +74,6 @@
},
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode" // TypeScript์—์„œ๋„ Prettier ์‚ฌ์šฉ
}
},
"git.ignoreLimitWarning": true
}
816 changes: 419 additions & 397 deletions package-lock.json

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,15 @@
"@mantine/notifications": "^7.13.3",
"@storybook/addon-actions": "^8.3.5",
"@storybook/test": "^8.3.5",
"@tanstack/react-query": "^5.59.16",
"@tanstack/react-query": "^5.60.5",
"@tanstack/react-query-next-experimental": "^5.59.19",
"@vercel/speed-insights": "^1.1.0",
"dayjs": "^1.11.13",
"embla-carousel-react": "^7.1.0",
"framer-motion": "^11.11.9",
"next": "14.2.15",
"react": "^18",
"react-dom": "^18",
"next": "^14.2.15",
"react": "^18.3.1",
"react-dom": "^18.3.1",
Comment on lines +34 to +36
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

๐Ÿ’ก Codebase verification

์˜์กด์„ฑ ๋ฒ„์ „ ๋ถˆ์ผ์น˜ ๋ฐ ํ˜ธํ™˜์„ฑ ๋ฌธ์ œ ๋ฐœ๊ฒฌ

Next.js์™€ React ๋ฒ„์ „ ๊ฐ„์˜ ํ˜ธํ™˜์„ฑ ๋ฌธ์ œ๊ฐ€ ํ™•์ธ๋˜์—ˆ์Šต๋‹ˆ๋‹ค:

  • Next.js 14.2.18์€ React ^18.2.0์„ peer dependency๋กœ ์š”๊ตฌํ•˜์ง€๋งŒ, ํ˜„์žฌ React ^18.3.1๋กœ ์„ค์ •๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค
  • package-lock.json์—์„œ Next.js๋Š” 14.2.18 ๋ฒ„์ „์œผ๋กœ ๊ณ ์ •๋˜์–ด ์žˆ์œผ๋‚˜, package.json์—์„œ๋Š” ^14.2.15๋กœ ์„ค์ •๋˜์–ด ์žˆ์–ด ์ผ๊ด€์„ฑ์ด ์—†์Šต๋‹ˆ๋‹ค

๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ˆ˜์ •์„ ๊ถŒ์žฅ๋“œ๋ฆฝ๋‹ˆ๋‹ค:

  • React์™€ React DOM ๋ฒ„์ „์„ ^18.2.0์œผ๋กœ ๋‹ค์šด๊ทธ๋ ˆ์ด๋“œ
  • Next.js ๋ฒ„์ „์„ 14.2.18๋กœ ๊ณ ์ •
๐Ÿ”— Analysis chain

์˜์กด์„ฑ ๋ฒ„์ „ ๊ด€๋ฆฌ ์ „๋žต ๊ฒ€ํ†  ํ•„์š”

๋‹ค์Œ ๋ณ€๊ฒฝ ์‚ฌํ•ญ๋“ค์— ๋Œ€ํ•œ ๊ฒ€ํ† ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค:

  • next.js: ๊ณ ์ • ๋ฒ„์ „์—์„œ ์œ ๋™์  ๋ฒ„์ „์œผ๋กœ ๋ณ€๊ฒฝ (14.2.15 โ†’ ^14.2.15)
  • react์™€ react-dom: ๋” ๊ตฌ์ฒด์ ์ธ ๋ฒ„์ „์œผ๋กœ ์—…๋ฐ์ดํŠธ (^18 โ†’ ^18.3.1)

์ด๋Ÿฌํ•œ ๋ณ€๊ฒฝ์€ ์˜ˆ๊ธฐ์น˜ ์•Š์€ ํ˜ธํ™˜์„ฑ ๋ฌธ์ œ๋ฅผ ์ผ์œผํ‚ฌ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ, ํŒ€์˜ ๋ฒ„์ „ ๊ด€๋ฆฌ ์ •์ฑ…๊ณผ ์ผ์น˜ํ•˜๋Š”์ง€ ํ™•์ธํ•ด์ฃผ์„ธ์š”.

๐Ÿ Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for peer dependency requirements

# Check next.js peer dependencies
curl -s https://registry.npmjs.org/next/14.2.15 | jq '.peerDependencies'

# Check if react versions are aligned in other package files
fd -e json -e yaml -e yml | xargs grep -l "react.*18.3.1"

Length of output: 301


Script:

#!/bin/bash
# Check package-lock.json for react dependencies and their resolved versions
cat package-lock.json | jq '.packages | .["node_modules/react"], .["node_modules/react-dom"], .["node_modules/next"]' 2>/dev/null

# Check for any direct usage of React 18.3 specific features
rg -l "experimental|concurrent|use client" --type js --type jsx --type ts --type tsx

# Check the current next.js version in package-lock.json
cat package-lock.json | jq '.packages["node_modules/next"].version' 2>/dev/null

Length of output: 2272

"react-hook-form": "^7.53.1",
"react-hook-form-persist": "^3.0.0",
"react-intersection-observer": "^9.13.1",
Expand Down
14 changes: 14 additions & 0 deletions src/_apis/gathering/reviewable-gathering.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { fetchApi } from '@/src/utils/api';
import { PageableTypes } from '@/src/types/crew-card';
import { ReviewableGatheringCardInformResponse } from '@/src/types/reviewable-gathering-card';

export async function getReviewableGatherings(pageable: PageableTypes) {
const { page, size, sort = ['string'] } = pageable;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

๐Ÿ› ๏ธ Refactor suggestion

sort ๋งค๊ฐœ๋ณ€์ˆ˜์˜ ๊ธฐ๋ณธ๊ฐ’ ์„ค์ • ๊ฐœ์„  ํ•„์š”

sort ๋งค๊ฐœ๋ณ€์ˆ˜์˜ ๊ธฐ๋ณธ๊ฐ’์ด ['string']์œผ๋กœ ํ•˜๋“œ์ฝ”๋”ฉ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ์‹ค์ œ ์ •๋ ฌ ๋กœ์ง๊ณผ ๋งž์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ˆ˜์ •ํ•˜๋Š” ๊ฒƒ์„ ์ œ์•ˆ๋“œ๋ฆฝ๋‹ˆ๋‹ค:

- const { page, size, sort = ['string'] } = pageable;
+ const { page, size, sort = ['createdAt,desc'] } = pageable;
๐Ÿ“ Committable suggestion

โ€ผ๏ธ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const { page, size, sort = ['string'] } = pageable;
const { page, size, sort = ['createdAt,desc'] } = pageable;

const response: { data: ReviewableGatheringCardInformResponse } = await fetchApi(
`/api/gatherings/reviewable?page=${page}&size=${size}&sort=${sort}`,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

โš ๏ธ Potential issue

URL ๋งค๊ฐœ๋ณ€์ˆ˜ ์ธ์ฝ”๋”ฉ ์ฒ˜๋ฆฌ ํ•„์š”

URL ๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ ์ธ์ฝ”๋”ฉ๋˜์ง€ ์•Š์•„ ํŠน์ˆ˜ ๋ฌธ์ž๊ฐ€ ํฌํ•จ๋œ ๊ฒฝ์šฐ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ๊ณผ ๊ฐ™์ด encodeURIComponent๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ˆ˜์ •ํ•˜๋Š” ๊ฒƒ์„ ์ œ์•ˆ๋“œ๋ฆฝ๋‹ˆ๋‹ค:

- `/api/gatherings/reviewable?page=${page}&size=${size}&sort=${sort}`,
+ `/api/gatherings/reviewable?page=${encodeURIComponent(page)}&size=${encodeURIComponent(size)}&sort=${encodeURIComponent(sort)}`,
๐Ÿ“ Committable suggestion

โ€ผ๏ธ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
`/api/gatherings/reviewable?page=${page}&size=${size}&sort=${sort}`,
`/api/gatherings/reviewable?page=${encodeURIComponent(page)}&size=${encodeURIComponent(size)}&sort=${encodeURIComponent(sort)}`,

);
if (!response.data) {
throw new Error('Failed to get reviewable gatherings');
}
return response.data;
}
23 changes: 23 additions & 0 deletions src/_queries/my-gathering/reviewable-gathering-list.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
'use client';

import { getReviewableGatherings } from '@/src/_apis/gathering/reviewable-gathering';
import { PageableTypes } from '@/src/types/crew-card';
import { ReviewableGatheringCardInformResponse } from '@/src/types/reviewable-gathering-card';

export function useGetReviewableQuery({ pageable }: { pageable: PageableTypes }) {
const { size, sort = ['string'] } = pageable;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

โš ๏ธ Potential issue

๊ธฐ๋ณธ ์ •๋ ฌ ๊ฐ’์„ ์ˆ˜์ •ํ•ด ์ฃผ์„ธ์š”

sort ํŒŒ๋ผ๋ฏธํ„ฐ์˜ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ['string']์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์€ ์ ์ ˆํ•˜์ง€ ์•Š์•„ ๋ณด์ž…๋‹ˆ๋‹ค. ์‹ค์ œ ์ •๋ ฌ ๊ธฐ์ค€์„ ๋ฐ˜์˜ํ•˜๋Š” ์˜๋ฏธ ์žˆ๋Š” ๊ฐ’์œผ๋กœ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒƒ์ด ์ข‹๊ฒ ์Šต๋‹ˆ๋‹ค.

-  const { size, sort = ['string'] } = pageable;
+  const { size, sort = ['createdAt', 'desc'] } = pageable;
๐Ÿ“ Committable suggestion

โ€ผ๏ธ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const { size, sort = ['string'] } = pageable;
const { size, sort = ['createdAt', 'desc'] } = pageable;

return {
queryKey: ['reviewableGathering'],
queryFn: ({ pageParam = 0 }) =>
getReviewableGatherings({ page: pageParam, size, sort }).then((response) => {
if (response === undefined || response === null) {
throw new Error('์•ฝ์† ๋ชฉ๋ก์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ๋ฐ ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค.');
}
return response;
}),
getNextPageParam: (
lastPage: ReviewableGatheringCardInformResponse,
allPages: ReviewableGatheringCardInformResponse[],
) => (lastPage.hasNext ? allPages.length : undefined),
};
Comment on lines +18 to +22
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

๐Ÿ› ๏ธ Refactor suggestion

ํŽ˜์ด์ง€๋„ค์ด์…˜ ๋กœ์ง์„ ๋ณด์™„ํ•ด ์ฃผ์„ธ์š”

ํ˜„์žฌ ๊ตฌํ˜„๋œ getNextPageParam ๋กœ์ง์ด ํŽ˜์ด์ง€ ๋ฒˆํ˜ธ๋ฅผ ์ง์ ‘ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ๋ฐฐ์—ด์˜ ๊ธธ์ด๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ํŽ˜์ด์ง€ ๊ฑด๋„ˆ๋›ฐ๊ธฐ๋‚˜ ํŽ˜์ด์ง€ ํฌ๊ธฐ ๋ณ€๊ฒฝ ์‹œ ๋ฌธ์ œ๊ฐ€ ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

     getNextPageParam: (
       lastPage: ReviewableGatheringCardInformResponse,
-      allPages: ReviewableGatheringCardInformResponse[],
+      _pages: ReviewableGatheringCardInformResponse[],
-    ) => (lastPage.hasNext ? allPages.length : undefined),
+    ) => (lastPage.hasNext ? lastPage.currentPage + 1 : undefined),
๐Ÿ“ Committable suggestion

โ€ผ๏ธ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
getNextPageParam: (
lastPage: ReviewableGatheringCardInformResponse,
allPages: ReviewableGatheringCardInformResponse[],
) => (lastPage.hasNext ? allPages.length : undefined),
};
getNextPageParam: (
lastPage: ReviewableGatheringCardInformResponse,
_pages: ReviewableGatheringCardInformResponse[],
) => (lastPage.hasNext ? lastPage.currentPage + 1 : undefined),
};

}
17 changes: 0 additions & 17 deletions src/app/(crew)/api/mock-api/writable-gathering.ts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export default function ProfileCardPresenter({ data, onEdit, onDelete }: Profile
>
<Menu.Target>
<div className="h-full w-full">
<Profile editable imageUrl={data?.profileImageUrl ?? ''} />
<Profile editable imageUrl={data?.profileImageUrl ?? ''} priority />
</div>
</Menu.Target>
<Menu.Dropdown className="translate-x-16 translate-y-2 transform md:translate-x-24 md:translate-y-0 lg:translate-x-24 lg:translate-y-0">
Expand Down
13 changes: 8 additions & 5 deletions src/app/(crew)/my-page/_components/review-tabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,14 @@
import { useState } from 'react';
import { useRouter } from 'next/navigation';
import { Divider } from '@mantine/core';
import { getReviewableGatherings } from '@/src/_apis/gathering/reviewable-gathering';
import { useInfiniteScroll } from '@/src/hooks/use-infinite-scroll';
import { fetchMyReviewData } from '@/src/app/api/mock-api/review';
import ReviewCardList from '@/src/components/common/review-list/review-card-list';
import Tabs from '@/src/components/common/tab';
import ReviewableGatheringCardList from '@/src/components/my-page/reviewable-gatherings/reviewable-gathering-card-list';
import { ReviewInformResponse } from '@/src/types/review';
import { ReviewableGatheringCardInformResponse } from '@/src/types/reviewable-gathering-card';

export default function ReviewTabs() {
const myPageTabs = [
Expand All @@ -17,11 +24,7 @@ export default function ReviewTabs() {

const selectedTab = myPageTabs.find((tab) => tab.id === id);
if (selectedTab?.route) {
try {
router.push(selectedTab.route);
} catch (error) {
throw error;
}
router.push(selectedTab.route);
}
};

Expand Down
8 changes: 6 additions & 2 deletions src/app/(crew)/my-page/reviewable/page.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
export default function ReviewableGatheringsPage() {
return <div>reviewable gathering list component</div>;
'use client';

import ReviewableGatheringCardList from '@/src/components/my-page/reviewable-gatherings/reviewable-gathering-card-list';

export default function ReviewableTab() {
return <ReviewableGatheringCardList />;
}
Comment on lines +5 to 7
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

๐Ÿ› ๏ธ Refactor suggestion

์—๋Ÿฌ ์ฒ˜๋ฆฌ์™€ ๋กœ๋”ฉ ์ƒํƒœ ๊ด€๋ฆฌ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

ํ˜„์žฌ ๊ตฌํ˜„์€ ๋งค์šฐ ๋‹จ์ˆœํ•˜์—ฌ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ฐœ์„ ์‚ฌํ•ญ์„ ๊ณ ๋ คํ•ด๋ณด์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค:

  1. ์—๋Ÿฌ ๋ฐ”์šด๋”๋ฆฌ ์ถ”๊ฐ€
  2. ๋กœ๋”ฉ ์ƒํƒœ ์ฒ˜๋ฆฌ
  3. ํด๋ฐฑ UI ๊ตฌํ˜„

๋‹ค์Œ๊ณผ ๊ฐ™์ด ๊ฐœ์„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

+import { Suspense, ErrorBoundary } from 'react';
+import LoadingSpinner from '@/src/components/common/loading-spinner';
+import ErrorFallback from '@/src/components/common/error-fallback';

 export default function ReviewableTab() {
-  return <ReviewableGatheringCardList />;
+  return (
+    <ErrorBoundary fallback={<ErrorFallback />}>
+      <Suspense fallback={<LoadingSpinner />}>
+        <ReviewableGatheringCardList />
+      </Suspense>
+    </ErrorBoundary>
+  );
 }

Committable suggestion skipped: line range outside the PR's diff.

2 changes: 1 addition & 1 deletion src/components/common/crew-list/crew-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export default function CrewCard({
>
{/* ์ธ๋„ค์ผ */}
<div className="relative h-[203px] w-full flex-shrink-0 md:w-[230px]">
<Image fill objectFit="cover" alt={title} src={imageUrl} />
<Image fill style={{ objectFit: 'cover' }} alt={title} src={imageUrl} />
</div>

<div className="flex min-h-[203px] w-full flex-col justify-between p-6 sm:px-4 sm:pt-4">
Expand Down
172 changes: 172 additions & 0 deletions src/components/common/crew-list/detail-crew-card.tsx
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

์š” ํŒŒ์ผ์€ ์ง€๊ธˆ ์—†๋Š” ํŒŒ์ผ์ธ๋ฐ ๋‹ค์‹œ ์ƒ๊ฒผ๋„ค์š”...! ์ตœ์‹ ํ™”๊ฐ€ ๋œ๋์„๊นŒ์š”...! ์•„๋‹ˆ๋ฉด ๋จธ์ง€ํ•˜๋ฉด์„œ ์ด์ „ ํŒŒ์ผ์ด ์‚ด์•„๋‚œ๊ฑฐ๊ฐ™์•„์š”๐Ÿฅฒ

Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
'use client';

import Image from 'next/image';
import { Menu } from '@mantine/core';
import { useDisclosure } from '@mantine/hooks';
import Profiles from '@/src/components/common/crew-list/profiles';
import ConfirmCancelModal from '@/src/components/common/modal/confirm-cancel-modal';
import ProgressBar from '@/src/components/common/progress-bar/index';
import { CrewMember } from '@/src/types/crew-card';
import Check from '@/public/assets/icons/ic-check.svg';
import KebabIcon from '@/public/assets/icons/kebab-btn.svg';

interface DetailCrewCardProps {
data: {
id: number;
title: string;
mainLocation: string;
subLocation: string;
participantCount: number;
totalCount: number;
confirmed: boolean;
imageUrl: string;
totalGatheringCount: number;
crewMembers: CrewMember[];
};
}

export default function DetailCrewCard({ data }: DetailCrewCardProps) {
const {
id,
title,
mainLocation,
subLocation,
participantCount,
totalCount,
confirmed,
imageUrl,
totalGatheringCount,
crewMembers,
} = data;
const [confirmCancelOpened, { open: openConfirmCancel, close: closeConfirmCancel }] =
useDisclosure();
const [leaveCrewModalOpened, { open: openLeaveCrewModal, close: closeLeaveCrewModal }] =
useDisclosure();

const handleDelete = () => {
openConfirmCancel();
};

const handleLeaveCrew = () => {
openLeaveCrewModal();
};

const handleConfirmDelete = () => {
// TODO : ์‚ญ์ œ ๋กœ์ง
closeConfirmCancel();
};

const handleConfirmLeaveCrew = () => {
// TODO : ํƒˆํ‡ด ๋กœ์ง
closeLeaveCrewModal();
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

โš ๏ธ Potential issue

๋ฏธ๊ตฌํ˜„๋œ ์‚ญ์ œ/ํƒˆํ‡ด ๋กœ์ง ๊ตฌํ˜„ ํ•„์š”

์‚ญ์ œ์™€ ํƒˆํ‡ด ๊ธฐ๋Šฅ์ด ์•„์ง ๊ตฌํ˜„๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ์—๋Ÿฌ ์ฒ˜๋ฆฌ์™€ ์‚ฌ์šฉ์ž ํ”ผ๋“œ๋ฐฑ๋„ ๊ณ ๋ คํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์‚ญ์ œ/ํƒˆํ‡ด ๋กœ์ง ๊ตฌํ˜„์— ๋„์›€์ด ํ•„์š”ํ•˜์‹ ๊ฐ€์š”? GitHub ์ด์Šˆ๋ฅผ ์ƒ์„ฑํ•ด๋“œ๋ฆด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


return (
<div className="relative flex h-[508px] max-w-full flex-col overflow-hidden rounded-[14px] bg-white shadow-bg md:h-[270px] md:flex-row lg:h-[270px] lg:w-[1180px]">
{/* eslint-disable-next-line no-nested-ternary */}
{/* //TODO: captin, crew์ธ ๊ฒฝ์šฐ ๋กœ์ง ์ˆ˜์ • */}
{/* {isCaptain ? (
<Menu trigger="click" position="bottom-end" openDelay={100} closeDelay={400}>
<Menu.Target>
<div className="top-68 absolute right-6 top-[286px] cursor-pointer md:top-6 lg:top-6">
<Image src={KebabIcon} alt="๋”๋ณด๊ธฐ" width={20} height={20} />
</div>
</Menu.Target>
<Menu.Dropdown>
<Menu.Item component="a" href={`/crew/detail/${id}/edit`} className="font-pretendard">
ํฌ๋ฃจ ์ˆ˜์ •ํ•˜๊ธฐ
</Menu.Item>
<Menu.Item
type="button"
onClick={handleDelete}
className="font-pretendard text-red-600"
>
ํฌ๋ฃจ ์‚ญ์ œํ•˜๊ธฐ
</Menu.Item>
</Menu.Dropdown>
</Menu>
) : isCrew ? (
<Menu trigger="click" position="bottom-end" openDelay={100} closeDelay={400}>
<Menu.Target>
<div className="top-68 absolute right-6 top-[286px] md:top-6 lg:top-6">
<Image src={KebabIcon} alt="๋”๋ณด๊ธฐ" width={20} height={20} />
</div>
</Menu.Target>
<Menu.Dropdown>
<Menu.Item type="button" onClick={handleLeaveCrew} className="font-pretendard">
ํฌ๋ฃจ ํƒˆํ‡ดํ•˜๊ธฐ
</Menu.Item>
</Menu.Dropdown>
</Menu>
) : null} */}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

โš ๏ธ Potential issue

์ฃผ์„ ์ฒ˜๋ฆฌ๋œ ์ฝ”๋“œ ์ •๋ฆฌ ํ•„์š”

์ฃผ์„ ์ฒ˜๋ฆฌ๋œ captain/crew ๊ด€๋ จ ์ฝ”๋“œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ์ฝ”๋“œ ๊ฐ€๋…์„ฑ์„ ์ €ํ•˜์‹œํ‚ค๊ณ  ์œ ์ง€๋ณด์ˆ˜๋ฅผ ์–ด๋ ต๊ฒŒ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

๊ตฌํ˜„์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ TODO ์ฃผ์„๋งŒ ๋‚จ๊ธฐ๊ณ  ๋‚˜๋จธ์ง€๋Š” ์ œ๊ฑฐํ•˜๊ฑฐ๋‚˜, ํ•„์š”์—†๋Š” ๊ฒฝ์šฐ ์ „์ฒด ์ฃผ์„์„ ์ œ๊ฑฐํ•ด์ฃผ์„ธ์š”.


{/* ์ธ๋„ค์ผ */}
<div className="relative h-[270px] w-full md:w-[385px] lg:w-[540px]">
<Image fill style={{ objectFit: 'cover' }} alt={title} src={imageUrl} />
</div>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

๐Ÿ› ๏ธ Refactor suggestion

์ ‘๊ทผ์„ฑ ๊ฐœ์„  ํ•„์š”

์ด๋ฏธ์ง€์— ๋Œ€ํ•œ ์˜๋ฏธ ์žˆ๋Š” ๋Œ€์ฒด ํ…์ŠคํŠธ๊ฐ€ ๋ถ€์กฑํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ ์ด๋ฏธ์ง€๊ฐ€ ์žฅ์‹์šฉ์ธ ๊ฒฝ์šฐ์—๋Š” ๋ช…์‹œ์ ์œผ๋กœ ํ‘œ์‹œํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ˆ˜์ •ํ•ด์ฃผ์„ธ์š”:

-<Image fill style={{ objectFit: 'cover' }} alt={title} src={imageUrl} />
+<Image 
+  fill 
+  style={{ objectFit: 'cover' }} 
+  alt={`${title} ํฌ๋ฃจ์˜ ๋Œ€ํ‘œ ์ด๋ฏธ์ง€`} 
+  src={imageUrl} 
+/>
๐Ÿ“ Committable suggestion

โ€ผ๏ธ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<div className="relative h-[270px] w-full md:w-[385px] lg:w-[540px]">
<Image fill style={{ objectFit: 'cover' }} alt={title} src={imageUrl} />
</div>
<div className="relative h-[270px] w-full md:w-[385px] lg:w-[540px]">
<Image
fill
style={{ objectFit: 'cover' }}
alt={`${title} ํฌ๋ฃจ์˜ ๋Œ€ํ‘œ ์ด๋ฏธ์ง€`}
src={imageUrl}
/>
</div>


<div className="flex w-full flex-col justify-between p-6 sm:h-[238px] sm:px-4 sm:pt-4 md:h-[270px] md:flex-1 lg:h-[270px]">
{/* ๋ฉ”์ธ ๋‚ด์šฉ */}
<div>
<div className="flex flex-col gap-1 lg:flex-row lg:items-center">
<span className="max-w-full overflow-hidden text-ellipsis whitespace-nowrap pr-6 text-lg font-semibold lg:pr-0">
{title}
</span>
<span className="hidden lg:ml-2 lg:inline-block">|</span>
<span className="text-base font-medium">
{mainLocation} {subLocation}
</span>
</div>

<span className="text-sm font-semibold text-blue-600">
{`ํ˜„์žฌ ${totalGatheringCount}๊ฐœ์˜ ์•ฝ์†์ด ๊ฐœ์„ค๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.`}
</span>
</div>
{/* ์„ธ๋ถ€๋‚ด์šฉ */}
<div className="flex w-full gap-8 border-t-[2px] border-t-gray-200 pt-[31px]">
<div className="flex flex-grow flex-col items-start gap-2">
<div className="flex w-full items-center justify-between">
<div className="flex items-center space-x-2">
<span className="text-sm font-medium">๋ชจ์ง‘ ์ •์›</span>
<span className="text-sm font-semibold">{participantCount}๋ช…</span>
<div className="pl-2">
<Profiles profiles={crewMembers} />
</div>
</div>

{confirmed && (
<span className="flex items-center gap-[1px] text-blue-600">
<Image src={Check} alt="ํ™•์ธ" width={24} height={24} />
<span className="text-sm font-medium"> ๊ฐœ์„ค ํ™•์ •</span>
</span>
)}
</div>
<ProgressBar total={totalCount} current={participantCount} />
<div className="flex w-full justify-between">
<span className="text-sm font-medium text-gray-700">์ตœ์†Œ์ธ์› 2๋ช…</span>
<span className="text-sm font-medium text-gray-700">์ตœ๋Œ€์ธ์› {totalCount}๋ช…</span>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

๐Ÿ› ๏ธ Refactor suggestion

ํ•˜๋“œ์ฝ”๋”ฉ๋œ ๊ฐ’๋“ค์˜ ์ƒ์ˆ˜ํ™” ํ•„์š”

์ตœ์†Œ ์ธ์›์ˆ˜๊ฐ€ ํ•˜๋“œ์ฝ”๋”ฉ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฐ ๊ฐ’๋“ค์€ ์ƒ์ˆ˜๋กœ ๋ถ„๋ฆฌํ•˜์—ฌ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ƒ์ˆ˜๋ฅผ ์ถ”๊ฐ€ํ•ด์ฃผ์„ธ์š”:

const CREW_CONSTANTS = {
  MIN_MEMBERS: 2,
  // ๋‹ค๋ฅธ ์ƒ์ˆ˜๋“ค๋„ ์ถ”๊ฐ€
} as const;

</div>
</div>
</div>
</div>

{/* ์‚ญ์ œ ํ™•์ธ ๋ชจ๋‹ฌ */}
<ConfirmCancelModal
opened={confirmCancelOpened}
onClose={closeConfirmCancel}
onConfirm={handleConfirmDelete}
>
<p>์ •๋ง๋กœ ํฌ๋ฃจ๋ฅผ ์‚ญ์ œํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?</p>
</ConfirmCancelModal>

{/* ํƒˆํ‡ด ํ™•์ธ ๋ชจ๋‹ฌ */}
<ConfirmCancelModal
opened={leaveCrewModalOpened}
onClose={closeLeaveCrewModal}
onConfirm={handleConfirmLeaveCrew}
>
<p>์ •๋ง๋กœ ํฌ๋ฃจ์—์„œ ํƒˆํ‡ดํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?</p>
</ConfirmCancelModal>
</div>
);
}
4 changes: 2 additions & 2 deletions src/components/common/gathering-card/presenter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ export default function GatheringCardPresenter({
<Image
src={imageUrl}
alt={title}
layout="fill"
objectFit="cover"
fill
style={{ objectFit: 'cover' }}
className="rounded-t-lg"
/>
</div>
Expand Down
7 changes: 5 additions & 2 deletions src/components/common/profile/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ interface ProfileProps {
editable?: boolean;
isCaptain?: boolean;
onClick?: () => void;
priority?: boolean;
}

export function Profile({
Expand All @@ -31,6 +32,7 @@ export function Profile({
editable = false,
isCaptain = false,
onClick,
priority = false,
}: ProfileProps) {
let finalSize = size;
if (editable) {
Expand Down Expand Up @@ -67,9 +69,10 @@ export function Profile({
<Image
src={imageUrl && imageUrl.trim() ? imageUrl : defaultImage}
alt="ํ”„๋กœํ•„ ์ด๋ฏธ์ง€"
layout="fill"
objectFit="cover"
fill
style={{ objectFit: 'cover' }}
className="rounded-full"
priority={priority}
/>
</div>
</button>
Expand Down
Loading
Loading