+
{children}
);
diff --git a/src/components/Tab/index.css.ts b/src/components/Tab/index.css.ts
index a0762e2..7c1426d 100644
--- a/src/components/Tab/index.css.ts
+++ b/src/components/Tab/index.css.ts
@@ -87,3 +87,6 @@ export const tabListStyle = style({
export const tabRootStyle = style({
width: '100%',
});
+export const tabPanelStyle = style({
+ width: '100%',
+});
diff --git a/src/constants/mocks/mockLessonData.ts b/src/constants/mocks/mockLessonData.ts
new file mode 100644
index 0000000..d947234
--- /dev/null
+++ b/src/constants/mocks/mockLessonData.ts
@@ -0,0 +1,34 @@
+export const LESSON_DATA = {
+ lessonImageUrl: "https://hankki-prod-bucket.s3.ap-northeast-2.amazonaws.com/dummy/%E1%84%80%E1%85%A9%E1%84%85%E1%85%A7%E1%84%83%E1%85%A2+%E1%84%86%E1%85%B5%E1%86%AF%E1%84%91%E1%85%B3%E1%86%AF%E1%84%85%E1%85%A2%E1%86%AB%E1%84%87%E1%85%B5.jpeg",
+ lessonGenre: "힙합",
+ lessonName: "힙합 댄스 기초",
+ teacherId: 101,
+ teacherNickname: "김태훈",
+ teacherImageUrl: "https://hankki-prod-bucket.s3.ap-northeast-2.amazonaws.com/dummy/%E1%84%80%E1%85%A9%E1%84%85%E1%85%A7%E1%84%83%E1%85%A2+%E1%84%86%E1%85%B5%E1%86%AF%E1%84%91%E1%85%B3%E1%86%AF%E1%84%85%E1%85%A2%E1%86%AB%E1%84%87%E1%85%B5.jpeg",
+ reservationCount: 25,
+ maxReservationCount: 30,
+ individualPrice: 15000,
+ lessonDetail: "안녕하세요, 안무가 김태훈입니다.\n\n저는 인기 아이돌의 안무가로서 다양한 작업에 참여해왔고 현재는 안무 작업 뿐만 아니라 강습도 함께 진행하고 있습니다.\n\n이번 강의에서는 기본기와 프리스타일 위주로 진행하려고 합니다.\n\n💜먼저, 연결이라는 주제로 움직여보면서 자신만의 무브를 찾아가 볼 예정이고 익숙해진 후에는 컨트롤을 주제로 다양한 루틴을 시도해 볼 예정입니다.",
+ lessonRecommendation: "초급자에게 적합한 수업입니다.\n김태훈만의 트렌디한 힙합 베이스를 배우고 싶은 분\n멋있다ㄷ",
+ lessonLevel: "초급",
+ lessonLevelDetail: "기초적인 동작과 리듬을 익힐 ",
+ lessonRound: [
+ {
+ lessonStartDateTime: "2023-12-10T10:00:00",
+ lessonEndDateTime: "2023-12-10T12:30:00",
+ },
+ {
+ lessonStartDateTime: "2024-12-10T10:00:00",
+ lessonEndDateTime: "2024-12-10T12:00:00",
+ },
+ {
+ lessonStartDateTime: "2023-12-11T10:00:00",
+ lessonEndDateTime: "2023-12-12T22:00:00",
+ },
+ ],
+ lessonLocation: "로제이 댄스홀 합정점",
+ lessonStreetAddress: "서울특별시 송파구 올림픽로 240\n잠실종합운동장제2경기장 동문 앞 주차장",
+ lessonOldStreetAddress: "서교동 395-124",
+ favoriteCount: 120,
+ favoriteStatus: true,
+};
diff --git a/src/pages/class/Card/index.css.ts b/src/pages/class/Card/index.css.ts
new file mode 100644
index 0000000..bab17bf
--- /dev/null
+++ b/src/pages/class/Card/index.css.ts
@@ -0,0 +1,16 @@
+import { style } from '@vanilla-extract/css';
+import { vars } from '@/styles/theme.css';
+
+export const cardStyle = style({
+ display: 'inline-flex',
+ alignItems: 'center',
+
+ width: '100%',
+ padding: '1.6rem 2rem',
+ gap: '0.8rem',
+
+ borderRadius: '4px',
+ border: `1px solid ${vars.colors.gray02}`,
+ borderColor: vars.colors.gray02,
+ backgroundColor: vars.colors.white,
+});
diff --git a/src/pages/class/Card/index.tsx b/src/pages/class/Card/index.tsx
new file mode 100644
index 0000000..c1074fc
--- /dev/null
+++ b/src/pages/class/Card/index.tsx
@@ -0,0 +1,14 @@
+import type { ComponentProps } from 'react';
+import { cardStyle } from './index.css';
+
+type CardProps = ComponentProps<'div'>;
+
+const Card = ({ children, ...props }: CardProps) => {
+ return (
+
+ {children}
+
+ );
+};
+
+export default Card;
diff --git a/src/pages/class/TabWrapper/TabIntro/index.css.ts b/src/pages/class/TabWrapper/TabIntro/index.css.ts
new file mode 100644
index 0000000..e69de29
diff --git a/src/pages/class/TabWrapper/TabIntro/index.tsx b/src/pages/class/TabWrapper/TabIntro/index.tsx
new file mode 100644
index 0000000..5b90164
--- /dev/null
+++ b/src/pages/class/TabWrapper/TabIntro/index.tsx
@@ -0,0 +1,19 @@
+import Flex from '@/components/Flex';
+import Text from '@/components/Text';
+import { LESSON_DATA } from '@/constants/mocks/mockLessonData';
+
+const Intro = () => {
+ const { lessonDetail } = LESSON_DATA;
+
+ return (
+ <>
+
+
+ {lessonDetail}
+
+
+ >
+ );
+};
+
+export default Intro;
diff --git a/src/pages/class/TabWrapper/TabLevel/index.css.ts b/src/pages/class/TabWrapper/TabLevel/index.css.ts
new file mode 100644
index 0000000..e69de29
diff --git a/src/pages/class/TabWrapper/TabLevel/index.tsx b/src/pages/class/TabWrapper/TabLevel/index.tsx
new file mode 100644
index 0000000..0dffd52
--- /dev/null
+++ b/src/pages/class/TabWrapper/TabLevel/index.tsx
@@ -0,0 +1,50 @@
+import Card from '@/pages/class/Card';
+import Flex from '@/components/Flex';
+import Head from '@/components/Head';
+import Text from '@/components/Text';
+import { LESSON_DATA } from '@/constants/mocks/mockLessonData';
+import { IcClose, IcQuesitonmark } from '@/assets/svg';
+
+const Level = () => {
+ const { lessonLevel, lessonLevelDetail, lessonRecommendation } = LESSON_DATA;
+
+ return (
+
+
+
+
+
+
+ {lessonLevel}
+
+
+ {lessonLevelDetail}
+
+
+
+
+
+ 클래스 난이도는 이렇게 설정되어있어요!
+
+
+
+
+
+
+
+
+ 이런 분들에게 해당 클래스를 추천해요!
+
+
+
+
+ {lessonRecommendation}
+
+
+
+ );
+};
+
+export default Level;
diff --git a/src/pages/class/TabWrapper/TabLocation/index.css.ts b/src/pages/class/TabWrapper/TabLocation/index.css.ts
new file mode 100644
index 0000000..e69de29
diff --git a/src/pages/class/TabWrapper/TabLocation/index.tsx b/src/pages/class/TabWrapper/TabLocation/index.tsx
new file mode 100644
index 0000000..280c299
--- /dev/null
+++ b/src/pages/class/TabWrapper/TabLocation/index.tsx
@@ -0,0 +1,52 @@
+import Card from '@/pages/class/Card';
+import Flex from '@/components/Flex';
+import Text from '@/components/Text';
+import { LESSON_DATA } from '@/constants/mocks/mockLessonData';
+import { IcClose } from '@/assets/svg';
+
+const LocationInfo = () => {
+ const { lessonLocation, lessonStreetAddress, lessonOldStreetAddress } = LESSON_DATA;
+
+ return (
+
+
+
+ {/* 왼쪽 */}
+
+
+ {lessonLocation}
+
+
+
+
+
+ 주소
+
+
+
+ {lessonStreetAddress}
+
+
+
+
+
+
+ 지번
+
+
+
+ {lessonOldStreetAddress}
+
+
+
+
+
+ {/* 오른쪽 */}
+
+
+
+
+ );
+};
+
+export default LocationInfo;
diff --git a/src/pages/class/TabWrapper/TabPeriod/index.css.ts b/src/pages/class/TabWrapper/TabPeriod/index.css.ts
new file mode 100644
index 0000000..237c8f8
--- /dev/null
+++ b/src/pages/class/TabWrapper/TabPeriod/index.css.ts
@@ -0,0 +1,14 @@
+import { style } from '@vanilla-extract/css';
+import { vars } from "@/styles/theme.css";
+
+export const roundBoxStyle = style({
+ display: 'flex',
+ justifyContent: 'center',
+ alignItems: 'center',
+
+ marginRight: '1rem',
+ padding: '0.6rem 1.2rem',
+
+ borderRadius: '4px',
+ backgroundColor: vars.colors.main04,
+});
diff --git a/src/pages/class/TabWrapper/TabPeriod/index.tsx b/src/pages/class/TabWrapper/TabPeriod/index.tsx
new file mode 100644
index 0000000..0220dc2
--- /dev/null
+++ b/src/pages/class/TabWrapper/TabPeriod/index.tsx
@@ -0,0 +1,43 @@
+import Card from '@/pages/class/Card';
+import { roundBoxStyle } from '@/pages/class/TabWrapper/TabPeriod/index.css';
+import Flex from '@/components/Flex';
+import Text from '@/components/Text';
+import { LESSON_DATA } from '@/constants/mocks/mockLessonData';
+import { calculatePeriod, formatDate } from '@/utils/dateCalculate';
+
+const Period = () => {
+ const { lessonRound } = LESSON_DATA;
+
+ return (
+
+ {lessonRound.map((item, index) => {
+ const { lessonStartDateTime, lessonEndDateTime } = item;
+ const { startTime, formattedEndTime, durationString } = calculatePeriod(lessonStartDateTime, lessonEndDateTime);
+
+ return (
+
+
+
+
+
+ {index + 1}회차
+
+
+
+
+ {formatDate(lessonStartDateTime)}
+
+
+ {startTime} - {formattedEndTime} ({durationString})
+
+
+
+
+
+ );
+ })}
+
+ );
+};
+
+export default Period;
diff --git a/src/pages/class/TabWrapper/index.css.ts b/src/pages/class/TabWrapper/index.css.ts
new file mode 100644
index 0000000..e46e890
--- /dev/null
+++ b/src/pages/class/TabWrapper/index.css.ts
@@ -0,0 +1,9 @@
+import { style } from '@vanilla-extract/css';
+import { vars } from '@/styles/theme.css';
+
+export const tabPanelStyle = style({
+ padding: '2.4rem 2rem',
+ borderTop: '1px solid',
+
+ borderColor: vars.colors.gray01,
+});
diff --git a/src/pages/class/TabWrapper/index.tsx b/src/pages/class/TabWrapper/index.tsx
new file mode 100644
index 0000000..f0a245b
--- /dev/null
+++ b/src/pages/class/TabWrapper/index.tsx
@@ -0,0 +1,57 @@
+import { useState } from 'react';
+import Intro from '@/pages/class/TabWrapper/TabIntro';
+import Level from '@/pages/class/TabWrapper/TabLevel';
+import LocationInfo from '@/pages/class/TabWrapper/TabLocation';
+import Period from '@/pages/class/TabWrapper/TabPeriod';
+import Flex from '@/components/Flex';
+import { TabRoot, TabList, TabButton, TabPanel } from '@/components/Tab';
+import { vars } from '@/styles/theme.css';
+
+interface TabWrapperProps {
+ colorScheme: 'primary' | 'secondary';
+}
+
+const TabWrapper = ({ colorScheme }: TabWrapperProps) => {
+ const [selectedTab, setSelectedTab] = useState(0);
+
+ const tabs = [
+ { id: 1, label: '소개', component:
},
+ { id: 2, label: '난이도', component:
},
+ { id: 3, label: '기간', component:
},
+ { id: 4, label: '위치', component:
},
+ ];
+
+ return (
+
+
+
+ {tabs.map((tab) => (
+ setSelectedTab(tab.id - 1)}
+ colorScheme={colorScheme}
+ >
+ {tab.label}
+
+ ))}
+
+
+
+
+ {tabs.map((tab) => (
+
+ {tab.component}
+
+ ))}
+
+
+ );
+};
+
+export default TabWrapper;
\ No newline at end of file
diff --git a/src/pages/class/index.css.ts b/src/pages/class/index.css.ts
new file mode 100644
index 0000000..e69de29
diff --git a/src/pages/class/index.tsx b/src/pages/class/index.tsx
index c38576e..c0373fe 100644
--- a/src/pages/class/index.tsx
+++ b/src/pages/class/index.tsx
@@ -1,5 +1,11 @@
+import TabWrapper from "@/pages/class/TabWrapper";
+
const Class = () => {
- return
수업 정보 상세
;
+ return(
+ <>
+
+ >
+ );
};
export default Class;
diff --git a/src/utils/dateCalculate.ts b/src/utils/dateCalculate.ts
new file mode 100644
index 0000000..9445bb2
--- /dev/null
+++ b/src/utils/dateCalculate.ts
@@ -0,0 +1,32 @@
+// 시간을 계산, "익일" 여부를 판단
+export const calculatePeriod = (start: string, end: string) => {
+ const startDate = new Date(start);
+ const endDate = new Date(end);
+
+ const startTime = `${startDate.getHours().toString().padStart(2, '0')}:${startDate
+ .getMinutes()
+ .toString()
+ .padStart(2, '0')}`;
+ const endTime = `${endDate.getHours().toString().padStart(2, '0')}:${endDate
+ .getMinutes()
+ .toString()
+ .padStart(2, '0')}`;
+
+ const isNextDay = endDate.getDate() !== startDate.getDate();
+ const formattedEndTime = isNextDay ? `익일 ${endTime}` : endTime;
+
+ const totalMinutes = Math.abs(endDate.getTime() - startDate.getTime()) / 1000 / 60;
+ const hours = Math.floor(totalMinutes / 60);
+ const minutes = totalMinutes % 60;
+
+ const durationString = minutes > 0 ? `총 ${hours}시간 ${minutes}분` : `총 ${hours}시간`;
+
+ return { startTime, formattedEndTime, durationString };
+};
+
+// 날짜를 포맷팅 (ex. "2025년 1월 8일 수요일")
+export const formatDate = (dateString: string) => {
+ const date = new Date(dateString);
+ const days = ['일요일', '월요일', '화요일', '수요일', '목요일', '금요일', '토요일'];
+ return `${date.getFullYear()}년 ${date.getMonth() + 1}월 ${date.getDate()}일 ${days[date.getDay()]}`;
+};
\ No newline at end of file