From 1512a952ff77608f6bb9182582e74ba5757b2e2d Mon Sep 17 00:00:00 2001 From: minseokim Date: Sun, 28 Dec 2025 16:41:24 +0900 Subject: [PATCH 1/6] =?UTF-8?q?feat:=20=EC=8A=B9=EC=9D=B8=20=EB=8C=80?= =?UTF-8?q?=EA=B8=B0=EC=9E=90=20=EB=AA=A9=EB=A1=9D=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/group/[groupId]/pending/page.tsx | 27 ++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 src/app/group/[groupId]/pending/page.tsx diff --git a/src/app/group/[groupId]/pending/page.tsx b/src/app/group/[groupId]/pending/page.tsx new file mode 100644 index 00000000..7c8d7ea1 --- /dev/null +++ b/src/app/group/[groupId]/pending/page.tsx @@ -0,0 +1,27 @@ +'use client'; + +import { use } from 'react'; + +import { + GroupPendingHeader, + GroupPendingMembers, + GroupPendingSummary, +} from '@/components/pages/group'; + +interface Props { + params: Promise<{ groupId: string }>; +} + +const GroupPendingMembersPage = ({ params }: Props) => { + const { groupId } = use(params); + + return ( + <> + + + + + ); +}; + +export default GroupPendingMembersPage; From 1646170a91432d596b379f40010ed8b5ccfef32b Mon Sep 17 00:00:00 2001 From: minseokim Date: Sun, 28 Dec 2025 16:55:26 +0900 Subject: [PATCH 2/6] =?UTF-8?q?feat:=20=EC=8A=B9=EC=9D=B8=20=EB=8C=80?= =?UTF-8?q?=EA=B8=B0=EC=9E=90=20=EB=AA=A9=EB=A1=9D=20=ED=97=A4=EB=8D=94=20?= =?UTF-8?q?=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../group/group-pending-header/index.tsx | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 src/components/pages/group/group-pending-header/index.tsx diff --git a/src/components/pages/group/group-pending-header/index.tsx b/src/components/pages/group/group-pending-header/index.tsx new file mode 100644 index 00000000..a7d595fc --- /dev/null +++ b/src/components/pages/group/group-pending-header/index.tsx @@ -0,0 +1,26 @@ +'use client'; + +import { useRouter } from 'next/navigation'; + +import { Icon } from '@/components/icon'; + +export const GroupPendingHeader = () => { + const router = useRouter(); + + const handleBackClick = () => { + router.back(); + }; + + return ( +
+ +

참여 신청

+
+ ); +}; From 2160107d8ff0b5263f0f7f28b80fceea9969a9ae Mon Sep 17 00:00:00 2001 From: minseokim Date: Sun, 28 Dec 2025 21:13:33 +0900 Subject: [PATCH 3/6] =?UTF-8?q?feat:=20=EC=8A=B9=EC=9D=B8=20=EB=8C=80?= =?UTF-8?q?=EA=B8=B0=EC=9E=90=20=EC=B9=B4=EB=93=9C=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pending-member-card/index.tsx | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 src/components/pages/group/group-pending-members/pending-member-card/index.tsx diff --git a/src/components/pages/group/group-pending-members/pending-member-card/index.tsx b/src/components/pages/group/group-pending-members/pending-member-card/index.tsx new file mode 100644 index 00000000..dd2b5d7d --- /dev/null +++ b/src/components/pages/group/group-pending-members/pending-member-card/index.tsx @@ -0,0 +1,60 @@ +'use client'; + +import Link from 'next/link'; + +import { Button, ImageWithFallback } from '@/components/ui'; +import { GetPendingMembersResponse } from '@/types/service/group'; + +type PendingMember = GetPendingMembersResponse['pendingMembers'][number]; + +interface Props { + member: PendingMember; + onReject: () => void; + onApprove: () => void; +} + +export const PendingMemberCard = ({ member, onReject, onApprove }: Props) => { + const profileUrl = `/profile/${member.userId}`; + + return ( +
+ + + +
+

{member.nickName}

+ {member.profileMessage && ( +

{member.profileMessage}

+ )} +
+ + + {member.requestMessage && ( +

+ {member.requestMessage} +

+ )} + +
+ + +
+
+ ); +}; From 17f836e075923a6a28972f660bc2294b8992f0a3 Mon Sep 17 00:00:00 2001 From: minseokim Date: Sun, 28 Dec 2025 21:17:13 +0900 Subject: [PATCH 4/6] =?UTF-8?q?feat:=20=EC=8A=B9=EC=9D=B8=20=EB=8C=80?= =?UTF-8?q?=EA=B8=B0=EC=9E=90=20=EB=AA=A9=EB=A1=9D=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../group/group-pending-members/index.tsx | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 src/components/pages/group/group-pending-members/index.tsx diff --git a/src/components/pages/group/group-pending-members/index.tsx b/src/components/pages/group/group-pending-members/index.tsx new file mode 100644 index 00000000..e04a9c4d --- /dev/null +++ b/src/components/pages/group/group-pending-members/index.tsx @@ -0,0 +1,83 @@ +'use client'; + +import { useCallback } from 'react'; + +import { EmptyState } from '@/components/layout/empty-state'; +import { GetPendingMembersResponse } from '@/types/service/group'; + +import { PendingMemberCard } from './pending-member-card'; + +interface Props { + groupId: string; + pendingMembers?: GetPendingMembersResponse['pendingMembers']; +} + +const MOCK_PENDING_MEMBERS: GetPendingMembersResponse['pendingMembers'] = [ + { + userId: 101, + groupUserId: 1, + nickName: 'Hope Lee', + profileImage: null, + profileMessage: 'Here We Go Again :)', + requestMessage: '안녕하세요 함께하고 싶습니다!', + requestedAt: '2025-12-24T13:20:10.123456', + }, + { + userId: 102, + groupUserId: 2, + nickName: '바다소년', + profileImage: null, + profileMessage: '물고기를 좋아합니다.', + requestMessage: '보드게임 정말 좋아해서 신청합니다! 앞으로 잘 부탁드려요', + requestedAt: '2025-12-24T13:25:15.789012', + }, + { + userId: 103, + groupUserId: 3, + nickName: '박디자인', + profileImage: null, + profileMessage: 'UI/UX 디자이너', + requestMessage: null, + requestedAt: '2025-12-24T13:30:20.456789', + }, +]; + +export const GroupPendingMembers = ({ groupId, pendingMembers = MOCK_PENDING_MEMBERS }: Props) => { + const handleReject = useCallback( + (memberId: number) => { + console.log('거절:', groupId, memberId); + }, + [groupId], + ); + + const handleApprove = useCallback( + (memberId: number) => { + console.log('승인:', groupId, memberId); + }, + [groupId], + ); + + if (pendingMembers?.length === 0) { + return ( +
+ 승인 대기 중인 멤버가 없습니다 +
+ ); + } + + return ( +
+
    + {pendingMembers.map((member) => ( +
  • + handleApprove(member.groupUserId)} + onReject={() => handleReject(member.groupUserId)} + /> +
  • + ))} +
+
+ ); +}; From 9983fab674e641d4cc1c587ebf2aa648ff5ab882 Mon Sep 17 00:00:00 2001 From: minseokim Date: Sun, 28 Dec 2025 21:22:21 +0900 Subject: [PATCH 5/6] =?UTF-8?q?feat:=20=EC=8A=B9=EC=9D=B8=20=EB=8C=80?= =?UTF-8?q?=EA=B8=B0=EC=9E=90=20=EC=9A=94=EC=95=BD=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../group/group-pending-summary/index.tsx | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 src/components/pages/group/group-pending-summary/index.tsx diff --git a/src/components/pages/group/group-pending-summary/index.tsx b/src/components/pages/group/group-pending-summary/index.tsx new file mode 100644 index 00000000..b39e0c1f --- /dev/null +++ b/src/components/pages/group/group-pending-summary/index.tsx @@ -0,0 +1,49 @@ +'use client'; + +import { DEFAULT_GROUP_IMAGE } from 'constants/default-images'; + +import { ImageWithFallback } from '@/components/ui'; + +interface Props { + thumbnail?: string | null; + title?: string; + pendingCount?: number; +} + +const MOCK_DATA = { + thumbnail: null, + title: '매우 긴 제목을 가진 모임입니다 이 제목은 너무 길어서 잘려야 합니다', + pendingCount: 5, +}; + +export const GroupPendingSummary = ({ + thumbnail = MOCK_DATA.thumbnail, + title = MOCK_DATA.title, + pendingCount = MOCK_DATA.pendingCount, +}: Props) => { + return ( +
+
+ +
+ +
+

{title}

+ +
+ 신청한 유저 + {pendingCount} + +
+
+
+ ); +}; From 65221a654fec6db14caae94150eacbccf16aa43c Mon Sep 17 00:00:00 2001 From: minseokim Date: Sun, 28 Dec 2025 21:29:22 +0900 Subject: [PATCH 6/6] =?UTF-8?q?fix:=20=EC=82=AC=EC=9A=A9=ED=95=98=EC=A7=80?= =?UTF-8?q?=20=EC=95=8A=EB=8A=94=20Group=20=ED=83=80=EC=9E=85=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/pages/group/index.ts | 3 ++ src/mock/service/group/group-mock.ts | 28 ----------------- src/types/service/group.ts | 46 ++++++++++------------------ 3 files changed, 20 insertions(+), 57 deletions(-) delete mode 100644 src/mock/service/group/group-mock.ts diff --git a/src/components/pages/group/index.ts b/src/components/pages/group/index.ts index 94287ece..e498876c 100644 --- a/src/components/pages/group/index.ts +++ b/src/components/pages/group/index.ts @@ -2,3 +2,6 @@ export { GroupBannerImages } from './group-banner-images'; export { GroupButtons } from './group-buttons'; export { GroupDescriptions } from './group-descriptions'; export { GroupMembers } from './group-members'; +export { GroupPendingHeader } from './group-pending-header'; +export { GroupPendingMembers } from './group-pending-members'; +export { GroupPendingSummary } from './group-pending-summary'; diff --git a/src/mock/service/group/group-mock.ts b/src/mock/service/group/group-mock.ts deleted file mode 100644 index baf9db90..00000000 --- a/src/mock/service/group/group-mock.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { Group } from '@/types/service/group'; - -export const groupMockItem: Group[] = [ - { - id: 1, - title: '동탄 호수공원에서 피크닉하실 분!', - location: '화성시', - locationDetail: '동탄 호수공원', - startTime: '2025-12-07T17:00:00+09:00', - endTime: '2025-12-07T19:00:00+09:00', - images: [ - 'https://images.unsplash.com/photo-1518020382113-a7e8fc38eac9?q=80&w=717&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', - ], - tags: ['게임', '피크닉'], - description: '동탄 호수공원에서 어쩌구 저쩌구', - participantCount: 3, - maxParticipants: 12, - createdBy: { - userId: 1, - nickName: '리오넬 메시', - profileImage: - 'https://images.unsplash.com/photo-1518020382113-a7e8fc38eac9?q=80&w=717&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', - }, - createdAt: '2025-12-06T17:00:00+09:00', - updatedAt: '2025-12-06T17:00:00+09:00', - joinedCount: 3, - }, -]; diff --git a/src/types/service/group.ts b/src/types/service/group.ts index 66e06114..5b91251e 100644 --- a/src/types/service/group.ts +++ b/src/types/service/group.ts @@ -84,35 +84,6 @@ export interface GetMyGroupsResponse { nextCursor: number | null; } -/** - - * 밑에 타입들은 다른 분들이 아직 다른 파일에서 사용 중이므로 그냥 제거해버리시면 안됩니다(다른 분들도 다 수정 후에 제거 예정) - * 아직 사용 중인 파일: - * - src/types/service/notification.ts (Notification.group) - * - src/mock/service/group/group-mock.ts (groupMockItem) - */ -export interface Group { - id: number; - title: string; - location: string; - locationDetail: string; - startTime: string; - endTime: string; - images: string[]; - tags: string[]; - description: string; - participantCount: number; - maxParticipants: number; - createdBy: { - userId: number; - nickName: string; - profileImage: null | string; - }; - createdAt: string; - updatedAt: string; - joinedCount: number; -} - export interface PreUploadGroupImagePayload { images: File[]; } @@ -185,6 +156,7 @@ export interface CreateGroupResponse { export interface GetGroupDetailsResponse { id: number; title: string; + joinPolicy: GroupV2JoinPolicy; status: GroupV2Status; address: { location: string; @@ -233,3 +205,19 @@ export interface GetGroupDetailsResponse { export interface GroupIdParams { groupId: string; } + +// 승인 대기자 목록 조회 응답 (GET /api/v2/groups/{groupId}/attendance/pending) +export interface GetPendingMembersResponse { + groupId: number; + joinPolicy: GroupV2JoinPolicy; + pendingMembers: { + userId: number; + groupUserId: number; + nickName: string; + profileImage: string | null; + profileMessage: string | null; + requestMessage: string | null; + requestedAt: string; + }[]; + serverTime: string; +}