Skip to content
Merged
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
.container {
display: flex;
flex-direction: column;
gap: 10px;
background-color: $color-blue;
padding: $spacing-3xl $spacing-xl;
color: $color-white;
}

.classInfo {
display: flex;
flex-direction: column;
gap: $spacing-sm;
}

.classInfoTitleContainer {
display: flex;
flex-direction: row;
gap: $spacing-sm;
align-items: center;

.classInfoTitle {
font-size: $font-size-2xl;
}

.classInfoProfessor {
font-size: $font-size-lg;
}
}

.classInfoDate {
display: flex;
flex-direction: row;
gap: $spacing-sm;
align-items: center;
font-size: $font-size-lg;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
"use client";

import React, { useEffect, useState } from "react";
import styles from "./ClassInfoSection.module.scss";
import { useParams } from "next/navigation";
import { fetchClassInfoByClassId } from "@/api/classes/fetchClassInfoByClassId";
import { FetchClassInfoByClassIdResult } from "@/types/classes/fetchClassInfoByClassIdTypes";
import { Calendar, Clock } from "lucide-react";

export default function ClassInfoSection() {
const { classId } = useParams();

const [classInfo, setClassInfo] =
useState<FetchClassInfoByClassIdResult | null>(null);

useEffect(() => {
fetchClassInfoByClassId(classId as string).then((res) => {
if (res.isSuccess) {
setClassInfo(res.result || null);
}
});
}, [classId]);

return (
<div className={styles.container}>
<div className={styles.classInfo}>
<div className={styles.classInfoTitleContainer}>
<div className={styles.classInfoTitle}>{classInfo?.className}</div>
<div className={styles.classInfoProfessor}>
{classInfo?.professorName}
</div>
</div>
<div className={styles.classInfoDate}>
<Clock strokeWidth={1.5} />
{classInfo?.classDate}
</div>
<div className={styles.classInfoDate}>
<Calendar strokeWidth={1.5} />
{classInfo?.startDate} ~ {classInfo?.endDate}
</div>
</div>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
.container {
width: 100%;
padding: $spacing-lg;
}

.lectureList {
display: flex;
flex-direction: column;
gap: $spacing-md;
}

.lectureCard {
display: flex;
justify-content: space-between;
align-items: center;
background: $color-white;
border-radius: $radius-md;
padding: $spacing-xl;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
transition: all 0.2s ease;
cursor: pointer;

&:hover {
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
transform: translateY(-2px);
}
}

.lectureInfo {
flex: 1;
display: flex;
flex-direction: column;
gap: $spacing-sm;
}

.lectureTitle {
font-size: $font-size-xl;
display: flex;
justify-content: space-between;
align-items: center;
color: $color-black;
line-height: 1.4;
border-bottom: 1px solid $color-neutral-7;
padding-bottom: $spacing-sm;
}

.lectureDetails {
display: flex;
gap: $spacing-md;
color: $color-mutedblue;
font-size: $font-size-sm;
}

.status {
margin-left: $spacing-sm;
color: $color-mutedblue;
font-size: $font-size-sm;
}

.dateInfo,
.timeInfo {
display: flex;
align-items: center;
gap: $spacing-sm;

svg {
color: $color-mutedblue;
}
}

.viewButton {
width: 40px;
height: 40px;
border-radius: 50%;
background: $color-skyblue;
border: none;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.2s ease;

svg {
color: $color-blue;
width: 16px;
height: 16px;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import React, { useEffect, useState } from "react";
import styles from "./LectureList.module.scss";
import { useParams } from "next/navigation";
import { fetchLecturesByClass } from "@/api/classes/fetchLecturesByClass";
import { FetchLecturesByClassResult } from "@/types/classes/fetchLecturesByClassTypes";
import { Calendar, ChevronRight, Clock } from "lucide-react";
import { useRouter } from "next/navigation";
import { ROUTES } from "@/constants/routes";
import LoadingSpinner from "@/components/LoadingSpinner/LoadingSpinner";

export default function LectureList() {
const { classId } = useParams();
const [lectures, setLectures] = useState<FetchLecturesByClassResult[]>([]);
const [loading, setLoading] = useState(true);
const router = useRouter();

useEffect(() => {
const fetchLectures = async () => {
try {
setLoading(true);
const response = await fetchLecturesByClass(classId as string);
if (response.isSuccess && response.result) {
setLectures(response.result);
}
} catch (error) {
console.error("강의 목록을 불러오는데 실패했습니다:", error);
} finally {
setLoading(false);
}
};
fetchLectures();
}, [classId]);

const getStatusText = (status: string) => {
switch (status) {
case "beforeLecture":
return "강의 전";
case "onLecture":
return "강의 중";
case "afterLecture":
return "강의 종료";
default:
return "알 수 없음";
}
};

if (loading) {
return (
<div className={styles.container}>
<LoadingSpinner text="강의 목록 불러오는 중..." />
</div>
);
}

if (lectures.length === 0) {
return <div className={styles.container}>등록된 강의가 없습니다.</div>;
}

return (
<div className={styles.container}>
<div className={styles.lectureList}>
{lectures.map((lecture) => (
<div
key={lecture.lectureId}
className={styles.lectureCard}
onClick={() => {
router.push(ROUTES.studentLectureDetail(lecture.lectureId));
}}
>
<div className={styles.lectureInfo}>
<div className={styles.lectureTitle}>
<div>
{String(lecture.session).padStart(2, "0")}.
{lecture.lectureName}
<span className={styles.status}>
{getStatusText(lecture.status)}
</span>
</div>
<button className={styles.viewButton}>
<ChevronRight />
</button>
</div>
<div className={styles.lectureDetails}>
<div className={styles.dateInfo}>
<Calendar size={16} />
<span>{lecture.lectureDate}</span>
</div>
<div className={styles.timeInfo}>
<Clock size={16} />
<span>
{lecture.startTime} ~ {lecture.endTime}
</span>
</div>
</div>
</div>
</div>
))}
</div>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.container {
width: 100%;
display: flex;
flex-direction: column;
}

.content {
width: 100%;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
"use client";
import React, { useState } from "react";
import styles from "./LectureListAndNote.module.scss";
import LectureList from "../LectureList/LectureList";
import LectureNote from "../LectureNote/LectureNote";
import Tab from "../../../../../../components/Tab/Tab";

export default function LectureListAndNote() {
const [selectedTab, setSelectedTab] = useState("강의 목록");

const tabs = ["강의 목록", "강의자료"];

const handleTabSelect = (tab: string) => {
setSelectedTab(tab);
};

return (
<div className={styles.container}>
<Tab tabs={tabs} onSelectTab={handleTabSelect} />
<div className={styles.content}>
{selectedTab === "강의 목록" ? <LectureList /> : <LectureNote />}
</div>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
.container {
width: 100%;
padding: $spacing-lg;
}

.title {
font-size: $font-size-xl;
font-weight: $font-weight-bold;
margin-bottom: $spacing-lg;
color: $color-black;
}

.fileList {
display: flex;
flex-direction: column;
gap: $spacing-md;
}

.fileItem {
display: flex;
align-items: center;
justify-content: space-between;
padding: $spacing-md;
border: 1px solid $color-neutral-7;
border-radius: $radius-md;
background-color: $color-white;
cursor: pointer;
transition: background-color 0.3s ease;
&:hover {
background-color: $color-skyblue;
}
}

.fileItem > :first-child {
flex: 1;
min-width: 0;
overflow: hidden;
}

.fileItem > :first-child > span {
display: block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 100%;
}

.fileInfo {
display: flex;
flex-direction: column;
align-items: flex-end;
gap: $spacing-xs;
flex-shrink: 0;
margin-left: $spacing-md;
}

.fileSize {
font-size: $font-size-sm;
color: $color-mutedblue;
}
Loading