Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(fe): create course detail home #2351

Open
wants to merge 41 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
16cb798
feat(fe): create new banner cover
mingyu27 Jan 28, 2025
6a0c01f
fix(fe): apply designed on-going assignments in Course Detail Page
mingyu27 Feb 2, 2025
a36a825
feat(fe): update grade and qna icon in Course Detail Page
mingyu27 Feb 2, 2025
906a95b
feat(fe): draft course description card in Course Detail Page
mingyu27 Feb 2, 2025
5be99f8
feat(fe): implement new Recent Notice design in Course Detail Page
mingyu27 Feb 3, 2025
917b887
feat(fe): implement new Recent Update design in Course Detail Page
mingyu27 Feb 3, 2025
5e8524c
feat(fe): create course info box in Course Detail Page
mingyu27 Feb 3, 2025
b17657f
chore(fe): uncomment status of Assignemnt line for resolving CI problem
mingyu27 Feb 3, 2025
6dd29dd
chore(fe): add status field for assignment in OngoingAssignment.tsx
mingyu27 Feb 3, 2025
62ae7c5
Update apps/frontend/app/(client)/(main)/course/[courseId]/_component…
mingyu27 Feb 3, 2025
e45e342
chore(fe): apply all comments
mingyu27 Feb 3, 2025
da912c7
chore(fe): apply comments again
mingyu27 Feb 3, 2025
f82b568
fix(fe): fix recent update component
mingyu27 Feb 7, 2025
7382e6c
chore(fe): apply cn functions and revise naming
mingyu27 Feb 7, 2025
263081f
chore(fe): try and recover installing svgr webpack
mingyu27 Feb 10, 2025
423a0f5
fix(fe): apply dynamic color for svg file
mingyu27 Feb 12, 2025
162c826
fix(fe): add responsive design for cover component
mingyu27 Feb 12, 2025
743a98a
chore(fe): no change in package.json
jimin9038 Feb 14, 2025
98e453c
feat(fe): create new banner cover
mingyu27 Jan 28, 2025
d287d7f
fix(fe): apply designed on-going assignments in Course Detail Page
mingyu27 Feb 2, 2025
a01ce44
feat(fe): update grade and qna icon in Course Detail Page
mingyu27 Feb 2, 2025
7a6250b
feat(fe): draft course description card in Course Detail Page
mingyu27 Feb 2, 2025
0306b19
feat(fe): implement new Recent Notice design in Course Detail Page
mingyu27 Feb 3, 2025
c0929f9
feat(fe): implement new Recent Update design in Course Detail Page
mingyu27 Feb 3, 2025
1e1869b
feat(fe): create course info box in Course Detail Page
mingyu27 Feb 3, 2025
ab2688e
chore(fe): uncomment status of Assignemnt line for resolving CI problem
mingyu27 Feb 3, 2025
3d54642
chore(fe): add status field for assignment in OngoingAssignment.tsx
mingyu27 Feb 3, 2025
ac4ff13
chore(fe): apply all comments
mingyu27 Feb 3, 2025
b9ab996
Update apps/frontend/app/(client)/(main)/course/[courseId]/_component…
mingyu27 Feb 3, 2025
d9f1185
fix(fe): fix recent update component
mingyu27 Feb 7, 2025
da36cce
chore(fe): apply cn functions and revise naming
mingyu27 Feb 7, 2025
a370ef0
chore(fe): try and recover installing svgr webpack
mingyu27 Feb 10, 2025
c47c5d9
fix(fe): apply dynamic color for svg file
mingyu27 Feb 12, 2025
81bebf8
fix(fe): add responsive design for cover component
mingyu27 Feb 12, 2025
054c2c1
chore(fe): no change in package.json
jimin9038 Feb 14, 2025
3045e6b
Merge branch 't1234-course-detail-home' of https://github.com/skkudin…
mingyu27 Feb 17, 2025
d88f8ba
chore(fe): rebuild pnpm-lock.yaml
mingyu27 Feb 17, 2025
e906e79
chore(fe): import cn
mingyu27 Feb 17, 2025
9cc5bbe
fix(fe): change gql paramenter
mingyu27 Feb 17, 2025
49a688b
Merge remote-tracking branch 'origin/main' into t1234-course-detail-home
mingyu27 Feb 19, 2025
9adf38f
chore(fe): remove font-mono
mingyu27 Feb 19, 2025
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
2 changes: 1 addition & 1 deletion apps/frontend/app/(client)/(main)/_components/Cover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const bgColors: { [key: string]: string } = {
contest: 'bg-gradient-to-b from-[#7BD9D3] to-[#A7A5A1]',
problem: 'bg-gradient-to-b from-[#5861B7] to-[#99978E]',
notice: 'bg-gradient-to-b from-[#2F4672] to-[#4671B3]',
course: 'bg-gradient-to-b from-[#9784E4] to-[#999999]'
course: 'bg-gradient-to-b from-[#7460C5] to-[#CAC1EE]'
}

const icons: { [key: string]: string } = {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export function AssignmentReminderBox() {
return (
<div className="mt-10 w-full border border-gray-300 p-10">
<h3 className="mb-2 text-lg font-bold">혜림님!</h3>
<div>
<h4 className="mb-8 font-bold">
아직 과제가 남아 있으니, 얼른 확인해 보세요! 😊
</h4>
<span className="text-xs text-gray-700 underline">
2주차 과제 바로 가기
</span>
</div>
</div>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
'use client'

import calendarIcon from '@/public/icons/calendar-default.svg'
import Person from '@/public/icons/person.svg'
import QuarterEllipse from '@/public/icons/quarter-ellipse.svg'
import Image from 'next/image'
// TODO: 백엔드 API 사용할 떄 필요할 것 같습니다(민규)
// import { useSearchParams } from 'next/navigation'
import { useState, useEffect } from 'react'

export function CourseInfoBox() {
// TODO: 백엔드 API 사용할 떄 필요할 것 같습니다(민규)
// const searchParams = useSearchParams()\
// const courseId = searchParams.get('courseId')
const [courseCode, setCourseCode] = useState('')
const [courseName, setCourseName] = useState('')
const [courseSemester, setCourseSemester] = useState('')
const [profName, setProfName] = useState('')

useEffect(() => {
const fetch = () => {
try {
setCourseCode('SWE3011_41')
setCourseName('강의명은최대열세글자까지')
setCourseSemester('2025 Spring')
setProfName('박진영')
} catch (err) {
throw new Error(`Failed to fetch data: ${err}`)
}
}
fetch()
}, [])

return (
<div className="relative m-4 flex h-[153px] w-[281px] flex-col justify-between rounded-xl p-4 shadow">
<div className="text-primary flex flex-col text-base font-semibold">
<span>[{courseCode}]</span>
<span>{courseName}</span>
</div>
<div>
<div className="flex flex-col gap-1">
<div className="flex">
<Image src={calendarIcon} alt="Calendar" height={20} />
<span className="ml-1 font-light">{courseSemester}</span>
</div>
<div className="flex">
<Image src={Person} alt="Person" height={20} />
<span className="ml-1 font-light">Prof. {profName}</span>
</div>
</div>
<Image
src={QuarterEllipse}
alt="Quarter Ellipse"
layout="intrinsic"
className="absolute bottom-0 right-0"
/>
</div>
</div>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
'use client'

import checkBlue from '@/public/icons/check-blue.svg'
import checkGray from '@/public/icons/check-gray.svg'
import type { Assignment } from '@/types/type'
import Image from 'next/image'
import { useEffect, useState } from 'react'
import { toast } from 'sonner'

export function OngoingAssignments() {
const [ongoings, setOngoings] = useState<Assignment[]>([])

useEffect(() => {
const fetchAssignments = async () => {
try {
const response = await new Promise<Assignment[]>((resolve) =>
setTimeout(
() =>
resolve([
{
id: 1,
title: 'HelloWorld.java',
startTime: new Date('2025-01-12'),
endTime: new Date('2024-01-22'),
group: { id: 'G1', groupName: 'Group A' },
isGraded: true,
status: 'ongoing'
},
{
id: 2,
title: 'HelloWorld.java',
startTime: new Date('2025-01-12'),
endTime: new Date('2024-01-22'),
group: { id: 'G1', groupName: 'Group A' },
isGraded: false,
status: 'ongoing'
}
]),
1000
)
)
setOngoings(response)
} catch (err) {
toast.error(`Failed to fetch assignments: ${err}`)
}
}
fetchAssignments()
}, [])

return (
<div className="p-10">
<h3 className="mb-10 text-lg font-bold">On-going Assignments</h3>
<table className="w-full border-collapse">
<thead>
<tr className="bg-gray-100">
<th className="px-4 py-2 font-medium">Title</th>
<th className="px-4 py-2 font-medium">Start Date</th>
<th className="px-4 py-2 font-medium">End Date</th>
<th className="px-4 py-2 font-medium">Submission</th>
</tr>
</thead>
<tbody>
{ongoings.map((assignment) => (
<tr key={assignment.id} className="border-b text-center">
<td className="px-4 py-2">{assignment.title}</td>
<td className="px-4 py-2">
{assignment.startTime.toLocaleDateString()}
</td>
<td className="px-4 py-2">
{assignment.endTime.toLocaleDateString()}
</td>
<td className="px-4 py-2">
{assignment.isGraded ? (
<Image
src={checkBlue}
alt="graded"
width={24}
height={24}
className="mr-2 inline-block"
/>
) : (
<Image
src={checkGray}
alt="not graded"
width={24}
height={24}
className="mr-2 inline-block"
/>
)}
</td>
</tr>
))}
</tbody>
</table>
</div>
)
}
Original file line number Diff line number Diff line change
@@ -1,21 +1,36 @@
'use client'

import { cn } from '@/libs/utils'
import type { CourseNotice } from '@/types/type'
import Link from 'next/link'
import { useSearchParams } from 'next/navigation'
import { useState, useEffect } from 'react'
import { toast } from 'sonner'

interface Notice {
id: number
title: string
isNew: boolean
const formatDate = (date: Date) => {
const year = date.getFullYear()
const month = date.getMonth() + 1
const day = date.getDate()

return `${year}.${month}.${day}`
}

interface DateBadgeProps {
date: string
}

function DateBadge({ date }: DateBadgeProps) {
return (
<div className="text-primary mb-1 inline-block rounded-full bg-gray-100 px-3 py-1 text-xs font-semibold">
<span>{date}</span>
</div>
)
}

export function RecentNotice() {
const searchParams = useSearchParams()
const courseId = searchParams.get('courseId')
const [notices, setNotices] = useState<Notice[]>([])
const [loading, setLoading] = useState(true)
const [error, setError] = useState<string | null>(null)
const [notices, setNotices] = useState<CourseNotice[]>([])

useEffect(() => {
const fetchNotices = () => {
Expand All @@ -40,67 +55,79 @@ export function RecentNotice() {
{
id: 999,
title: '[필독] 3주차 과제 1번 문제 수정사항',
isNew: true
isNew: true,
date: new Date(2025, 1, 27)
},
{
id: 998,
title: '[필독] 확인된 공지명은 이렇게 보이는 식',
isNew: false
isNew: false,
date: new Date(2025, 1, 27)
},
{ id: 997, title: '[샘플] 공지 테스트 3', isNew: false }
{
id: 997,
title: '[샘플] 공지 테스트 3',
isNew: false,
date: new Date(2025, 1, 27)
}
])
} catch (err) {
setError('공지사항을 불러오는 중 오류가 발생했습니다.')
} finally {
setLoading(false)
toast.error(`Failed to fetch notices: ${err}`)
}
}

fetchNotices()
}, [])

return (
<div className="w-full rounded-xl border border-gray-300 p-5">
<div className="mb-2 flex items-center justify-between">
<span className="font-semibold text-gray-800">최근 공지</span>
<div className="w-full rounded-xl p-4 shadow">
<div className="mb-2 flex justify-between">
<div>
<span className="text-primary mr-4 font-semibold">Recent Notice</span>
<span className="text-sm font-semibold">{notices.length}</span>
</div>

<Link
href={`/course/${courseId}/notice` as const}
className="flex items-center text-sm text-gray-500"
className="text-primary flex items-center text-xs font-semibold"
>
show more <span className="ml-1">+</span>
SHOW MORE<span className="ml-1">+</span>
</Link>
</div>

{loading && <p className="text-sm text-gray-500">Loading...</p>}

{error && <p className="text-sm text-red-500">{error}</p>}

{!loading && !error && (
<ul className="space-y-5 pt-3">
{notices.length > 0 ? (
notices.map((notice) => (
//TODO: 공지 요소 누르면 해당 페이지로 넘어가는 기능 필요!
<li
key={notice.id}
className="flex border-b pb-1 text-sm text-gray-700"
>
<ul className="space-y-5 pt-3">
{notices.length > 0 ? (
notices.map((notice) => (
//TODO: 공지 요소 누르면 해당 페이지로 넘어가는 기능 필요!
<li
key={notice.id}
className="flex justify-between border-b text-sm"
>
<div>
<span
className={`mr-2 text-xs ${
notice.isNew ? 'text-green-500' : 'text-red-500'
}`}
className={cn(
'mr-2 text-xs',
notice.isNew ? 'text-primary' : 'text-[#8A8A8A]'
)}
>
</span>
<span className={notice.isNew ? 'font-bold' : ''}>
<span
className={cn(
'mr-2 text-xs',
notice.isNew ? 'text-black' : 'text-[#8A8A8A]'
)}
>
{notice.title}
</span>
</li>
))
) : (
<p className="text-sm text-gray-500">공지사항이 없습니다.</p>
)}
</ul>
)}
</div>
<DateBadge date={formatDate(notice.date)} />
</li>
))
) : (
<p className="text-sm text-gray-500">공지사항이 없습니다.</p>
)}
</ul>
</div>
)
}
Loading
Loading