-
-
+
+
{studyDetail?.detailInfo.title}
@@ -120,104 +34,25 @@ export default function StudyDetailPage({ id: groupStudyId }: { id: number }) {
{studyDetail?.detailInfo.summary}
-
-
-
-
+
{} },
+ { label: '삭제하기', value: 'remove', onMenuClick: () => {} },
+ ]}
+ />
-
-
-
-
-
-
스터디 소개
-
- {studyDetail?.detailInfo.description}
-
-
-
-
기본 정보
-
프로필박스
-
- {basicInfoItems(studyDetail?.basicInfo).map((item, index) => {
- return (
-
- );
- })}
-
-
-
-
- 실시간 신청자 목록
- {`${studyDetail.basicInfo.approvedCount}명`}
-
-
- {applicants?.pages.map((page, pageIndex) => (
-
- {page.content.map((applicant) => {
- const temperPreset = getSincerityPresetByLevelName(
- applicant.applicantInfo.sincerityTemp.levelName as string,
- );
- return (
-
-
-
-
-
- {applicant.applicantInfo.memberName}
-
-
- {`${applicant.applicantInfo.sincerityTemp.temperature}`}{' '}
- ℃
-
-
-
-
- 프로필
-
- }
- />
-
- );
- })}
-
- ))}
-
-
-
-
-
스터디정보
-
-
-
-
-
-
+ {/** 탭리스트 */}
+
setActive(value)}
+ />
+ {active === 'intro' && (
+
+ )}
+ {active === 'members' && 참가자 목록
}
+ {active === 'channel' && 채널 내용
}
);
}
diff --git a/src/features/study/group/ui/group-study-list.tsx b/src/features/study/group/ui/group-study-list.tsx
index b107deb5..9c9f4c7c 100644
--- a/src/features/study/group/ui/group-study-list.tsx
+++ b/src/features/study/group/ui/group-study-list.tsx
@@ -1,11 +1,11 @@
'use client';
import { useInfiniteQuery } from '@tanstack/react-query';
import Image from 'next/image';
+import { useRouter } from 'next/navigation';
import React, { Fragment } from 'react';
import Badge from '@/shared/ui/badge';
import { getGroupStudyList } from '../api/get-group-study-list';
-import { useRouter } from 'next/navigation';
import { BasicInfoDetail } from '../api/group-study-types';
import {
EXPERIENCE_LEVEL_LABELS,
@@ -16,7 +16,7 @@ import {
export default function GroupStudyList() {
const router = useRouter();
- const { data, fetchNextPage } = useInfiniteQuery({
+ const { data } = useInfiniteQuery({
queryKey: ['groupStudies'],
queryFn: async ({ pageParam }) => {
const response = await getGroupStudyList({
diff --git a/src/features/study/group/ui/study-info-section.tsx b/src/features/study/group/ui/study-info-section.tsx
new file mode 100644
index 00000000..904ef85a
--- /dev/null
+++ b/src/features/study/group/ui/study-info-section.tsx
@@ -0,0 +1,257 @@
+import dayjs from 'dayjs';
+import {
+ Calendar,
+ Clock,
+ File,
+ Folder,
+ Globe,
+ HandCoins,
+ MapPin,
+ SignpostBig,
+ UserCheck,
+ Users,
+} from 'lucide-react';
+import Image from 'next/image';
+import React from 'react';
+import UserProfileModal from '@/entities/user/ui/user-profile-modal';
+import { getSincerityPresetByLevelName } from '@/shared/config/sincerity-temp-presets';
+import { cn } from '@/shared/shadcn/lib/utils';
+import UserAvatar from '@/shared/ui/avatar';
+import InfoCard from '@/widgets/study/group/ui/group-detail/Info-card';
+import SummaryStudyInfo from './summary-study-info';
+
+import {
+ BasicInfoDetail,
+ GroupStudyDetailResponse,
+} from '../api/group-study-types';
+
+import { useApplicantsByStatusQuery } from '../application/model/use-applicant-qeury';
+import {
+ EXPERIENCE_LEVEL_LABELS,
+ REGULAR_MEETING_LABELS,
+ ROLE_LABELS,
+ STUDY_METHOD_LABELS,
+ STUDY_STATUS_LABELS,
+ STUDY_TYPE_LABELS,
+} from '../const/group-study-const';
+
+interface StudyInfoSectionProps {
+ study: GroupStudyDetailResponse;
+ groupStudyId: number;
+}
+
+export default function StudyInfoSection({
+ study: studyDetail,
+ groupStudyId,
+}: StudyInfoSectionProps) {
+ const { data: applicants } = useApplicantsByStatusQuery({
+ groupStudyId,
+ status: 'APPROVED',
+ });
+
+ const basicInfoItems = (basicInfo: BasicInfoDetail) => {
+ const getDurationText = (startDate: string, endDate: string): string => {
+ const start = new Date(startDate);
+ const end = new Date(endDate);
+
+ const diffTime = end.getTime() - start.getTime();
+ if (diffTime < 0) return '기간이 잘못되었습니다.';
+
+ const diffDays = diffTime / (1000 * 60 * 60 * 24);
+ const diffWeeks = diffDays / 7;
+ const diffMonths = diffDays / 30; // 대략적인 월 계산 (평균 30일)
+
+ return diffMonths < 1
+ ? `약 ${Math.round(diffWeeks)}주`
+ : `약 ${Math.round(diffMonths)}개월`;
+ };
+
+ return [
+ {
+ label: '유형',
+ value: STUDY_TYPE_LABELS[basicInfo.type],
+ icon:
,
+ },
+ {
+ label: '주제',
+ value: basicInfo.targetRoles
+ .map((role) => {
+ return ROLE_LABELS[role];
+ })
+ .join(', '),
+ icon:
,
+ },
+ {
+ label: '경력',
+ value:
+ basicInfo.experienceLevels
+ .map((level) => {
+ return EXPERIENCE_LEVEL_LABELS[level];
+ })
+ .join(', ') || '무관',
+ icon:
,
+ },
+ {
+ label: '진행 방식',
+ value: `${STUDY_METHOD_LABELS[basicInfo.method]}, ${basicInfo.location}`,
+ icon:
,
+ },
+ {
+ label: '진행 기간',
+ value: getDurationText(basicInfo.startDate, basicInfo.endDate),
+ icon:
,
+ },
+ {
+ label: '정기모임',
+ value: REGULAR_MEETING_LABELS[basicInfo.regularMeeting],
+ icon:
,
+ },
+ {
+ label: '모집인원',
+ value: `${basicInfo.maxMembersCount}명`,
+ icon:
,
+ },
+ {
+ label: '시작일자',
+ value: dayjs(basicInfo.createdAt).format('YYYY.MM.DD'),
+ icon:
,
+ },
+ {
+ label: '참가비',
+ value:
+ basicInfo.price === 0
+ ? '무료'
+ : `${basicInfo.price.toLocaleString()}원`,
+ icon:
,
+ },
+ {
+ label: '상태',
+ value: `${STUDY_STATUS_LABELS[basicInfo.status]}`,
+ icon:
,
+ },
+ ];
+ };
+
+ const summaryBasicInfoItems = (basicInfo: BasicInfoDetail) => {
+ return [
+ {
+ label: '주제',
+ value: basicInfo.targetRoles
+ .map((role) => {
+ return ROLE_LABELS[role];
+ })
+ .join(', '),
+ icon:
,
+ },
+ {
+ label: '정기모임',
+ value: `${REGULAR_MEETING_LABELS[basicInfo.regularMeeting]}, ${basicInfo.location}`,
+ icon:
,
+ },
+ {
+ label: '경력',
+ value:
+ basicInfo.experienceLevels
+ .map((level) => {
+ return EXPERIENCE_LEVEL_LABELS[level];
+ })
+ .join(', ') || '무관',
+ icon:
,
+ },
+ {
+ label: '모집인원',
+ value: `${basicInfo.maxMembersCount}명`,
+ icon:
,
+ },
+ ];
+ };
+
+ return (
+
+
+
+
+
+
스터디 소개
+
+ {studyDetail?.detailInfo.description}
+
+
+
+
기본 정보
+ {/*
프로필박스
*/}
+
+ {basicInfoItems(studyDetail?.basicInfo).map((item) => {
+ return (
+
+ );
+ })}
+
+
+
+
+ 실시간 신청자 목록
+ {`${studyDetail.basicInfo.approvedCount}명`}
+
+ {applicants?.pages.map((applicant, i) => (
+
+ {applicant.content.map((data) => {
+ const temperPreset = getSincerityPresetByLevelName(
+ data.applicantInfo.sincerityTemp.levelName as string,
+ );
+
+ return (
+
+
+
+
+
+ {data.applicantInfo.memberName}
+
+
+ {`${data.applicantInfo.sincerityTemp.temperature}`}℃
+
+
+
+
+ 프로필
+
+ }
+ />
+
+ );
+ })}
+
+ ))}
+
+
+
+
+
+ );
+}
diff --git a/src/features/study/group/ui/summary-study-info.tsx b/src/features/study/group/ui/summary-study-info.tsx
new file mode 100644
index 00000000..9a15c85e
--- /dev/null
+++ b/src/features/study/group/ui/summary-study-info.tsx
@@ -0,0 +1,50 @@
+import React from 'react';
+import Button from '@/shared/ui/button';
+
+interface SummaryStudyInfoProps {
+ data: {
+ label: string;
+ value: string;
+ icon: React.ReactNode;
+ }[];
+ title: string;
+}
+
+export default function SummaryStudyInfo({
+ data,
+ title,
+}: SummaryStudyInfoProps) {
+ return (
+