Merge branch 'fe/lectureRoute' into 'frontend'

[Front-End] feat: 접근할 수 없는 강의 페이지 접근 못하도록 로직 추가

See merge request s11-webmobile1-sub2/S11P12A701!108
This commit is contained in:
조민우 2024-08-08 14:50:55 +09:00
commit a868c6e3f7
13 changed files with 69 additions and 38 deletions

View File

@ -68,11 +68,11 @@ const router = createBrowserRouter([
element: <LectureCreatePage />, element: <LectureCreatePage />,
}, },
{ {
path: 'lecture/:lectureId/info', path: 'lecture/:lectureId',
element: <LectureInfoPage />, element: <LectureInfoPage />,
}, },
{ {
path: 'lecture/:lectureId', path: 'lecture/:lectureId/class',
element: <LectureLayout />, element: <LectureLayout />,
children: [ children: [
{ {

View File

@ -4,10 +4,21 @@ import styles from './ArticleDetail.module.css';
import ArticleDetailAnswer from './ArticleDetailAnswer/ArticleDetailAnswer'; import ArticleDetailAnswer from './ArticleDetailAnswer/ArticleDetailAnswer';
import ArticleDetailAnswerInput from './ArticleDetailAnswer/ArticleDetailAnswerInput'; import ArticleDetailAnswerInput from './ArticleDetailAnswer/ArticleDetailAnswerInput';
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import useBoundStore from '../../../store';
export default function ArticleDetail({ topic, title, author = null, content, answer = null, onDelete, isQna = true }) { export default function ArticleDetail({
topic,
title,
author = null,
content,
answer = null,
onDelete,
isMine = false,
isQna = true,
}) {
const [submittedAnswer, setSubmittedAnswer] = useState(answer); const [submittedAnswer, setSubmittedAnswer] = useState(answer);
const [isEditing, setIsEditing] = useState(false); const [isEditing, setIsEditing] = useState(false);
const userType = useBoundStore((state) => state.userType);
useEffect(() => { useEffect(() => {
setSubmittedAnswer(answer); setSubmittedAnswer(answer);
@ -44,20 +55,24 @@ export default function ArticleDetail({ topic, title, author = null, content, an
</div> </div>
</div> </div>
<div className={styles.actionGroup}> <div className={styles.actionGroup}>
<Link {(isMine || userType === 'teacher') && (
className={styles.edit} <>
to={'edit'} <Link
state={{ title: title, content: content, answer: answer }} className={styles.edit}
> to={'edit'}
수정 state={{ title: title, content: content, answer: answer }}
</Link> >
<button 수정
type="button" </Link>
className={styles.delete} <button
onClick={onDelete} type="button"
> className={styles.delete}
삭제 onClick={onDelete}
</button> >
삭제
</button>
</>
)}
</div> </div>
</header> </header>
<div> <div>

View File

@ -15,15 +15,17 @@ export default function ClassInfo({ classTerm, classTime, status = 'NOT_ENROLLED
<div className={styles.content}>{classTime}</div> <div className={styles.content}>{classTime}</div>
</div> </div>
</div> </div>
<button {status !== 'MANAGED_BY_OTHERS' && (
onClick={onSubmit} <button
className={styles.button} onClick={onSubmit}
disabled={status === 'PENDING'} className={styles.button}
> disabled={status === 'PENDING'}
{status === 'PENDING' && '수강신청 중'} >
{status === 'ENROLLED' && '강의 상세페이지로 이동'} {status === 'PENDING' && '수강신청 중'}
{status === 'NOT_ENROLLED' && '수강신청'} {status === 'ENROLLED' && '강의 상세페이지로 이동'}
</button> {status === 'NOT_ENROLLED' && '수강신청'}
</button>
)}
</div> </div>
); );
} }

View File

@ -3,7 +3,7 @@ import { Outlet, useParams } from 'react-router-dom';
import LectureHeader from '../LectureHeader/LectureHeader'; import LectureHeader from '../LectureHeader/LectureHeader';
import { SideBar, SideLink, SideItem } from '../SideBar'; import { SideBar, SideLink, SideItem } from '../SideBar';
import MaxWidthLayout from './MaxWidthLayout'; import MaxWidthLayout from './MaxWidthLayout';
import { Suspense } from 'react'; import { Suspense, useEffect } from 'react';
import useBoundStore from '../../store'; import useBoundStore from '../../store';
import { useLectureInfo } from '../../hooks/api/useLectureInfo'; import { useLectureInfo } from '../../hooks/api/useLectureInfo';
import LoadingIndicator from '../LoadingIndicator.jsx/LoadingIndicator'; import LoadingIndicator from '../LoadingIndicator.jsx/LoadingIndicator';
@ -34,6 +34,12 @@ export default function LectureLayout() {
time: lecture.time, time: lecture.time,
}; };
useEffect(() => {
if (['NOT_ENROLLED', 'MANAGED_BY_OTHERS', 'PENDING'].includes(lecture.status)) {
navigate('..');
}
}, [lecture.status, navigate]);
return ( return (
<> <>
<LectureHeader <LectureHeader

View File

@ -4,8 +4,12 @@ import SideBar from '../../components/SideBar/SideBar';
import SideLink from '../../components/SideBar/SideLink'; import SideLink from '../../components/SideBar/SideLink';
import { Suspense } from 'react'; import { Suspense } from 'react';
import LoadingIndicator from '../LoadingIndicator.jsx/LoadingIndicator'; import LoadingIndicator from '../LoadingIndicator.jsx/LoadingIndicator';
import useBoundStore from '../../store';
export default function MyPageLayout() { export default function MyPageLayout() {
const userType = useBoundStore((state) => state.userType);
const myLectureTitle = userType === 'student' ? '수강중인 강의' : '내 강의';
return ( return (
<> <>
<MaxWidthLayout hasSideBar> <MaxWidthLayout hasSideBar>
@ -15,7 +19,7 @@ export default function MyPageLayout() {
to={''} to={''}
end end
> >
수강중인 강의 {myLectureTitle}
</SideLink> </SideLink>
<SideLink to={'edit'}>개인정보 변경</SideLink> <SideLink to={'edit'}>개인정보 변경</SideLink>
<SideLink to={'changePw'}>비밀번호 변경</SideLink> <SideLink to={'changePw'}>비밀번호 변경</SideLink>

View File

@ -6,7 +6,7 @@ export function useAnswerDelete() {
const newAnswer = { const newAnswer = {
answer: null, answer: null,
}; };
return instance.post(`${API_URL}/qna/answer/create/${questionId}`, newAnswer); return instance.post(`${API_URL}/qna/answer/delete/${questionId}`, newAnswer);
}; };
return { answerDelete }; return { answerDelete };

View File

@ -14,7 +14,7 @@ export default function StudentHomePage() {
{allClasses.map?.((lecture) => ( {allClasses.map?.((lecture) => (
<ClassCard <ClassCard
key={lecture.id} key={lecture.id}
path={`/lecture/${lecture.id}/info`} path={`/lecture/${lecture.id}`}
img={lecture.image} img={lecture.image}
> >
{lecture.title} {lecture.title}

View File

@ -3,21 +3,24 @@ import { Link } from 'react-router-dom';
import { useMyLectures } from '../../hooks/api/useMyLectures'; import { useMyLectures } from '../../hooks/api/useMyLectures';
import CompassIcon from '/src/assets/icons/compass.svg?react'; import CompassIcon from '/src/assets/icons/compass.svg?react';
import { STATIC_URL } from '../../constants'; import { STATIC_URL } from '../../constants';
import useBoundStore from '../../store';
export default function LearningLecturesPage() { export default function LearningLecturesPage() {
const { data } = useMyLectures(); const { data } = useMyLectures();
const onGoingClasses = data?.data ?? []; const onGoingClasses = data?.data ?? [];
const hasOnGoingClasses = onGoingClasses.length > 0; const hasOnGoingClasses = onGoingClasses.length > 0;
const userType = useBoundStore((state) => state.userType);
const myLectureTitle = userType === 'student' ? '수강중인 강의' : '내 강의';
return ( return (
<section> <section>
<h2 className={styles.title}>수강중인 강의</h2> <h2 className={styles.title}>{myLectureTitle}</h2>
<div className={styles.grid}> <div className={styles.grid}>
{hasOnGoingClasses ? ( {hasOnGoingClasses ? (
onGoingClasses.map?.((lecture) => ( onGoingClasses.map?.((lecture) => (
<Link <Link
key={lecture.id} key={lecture.id}
to={`/lecture/${lecture.id}`} to={`/lecture/${lecture.id}/class`}
className={styles.card} className={styles.card}
> >
{lecture.image ? ( {lecture.image ? (

View File

@ -24,7 +24,7 @@ export default function LectureInfoPage() {
} }
if (status === 'ENROLLED') { if (status === 'ENROLLED') {
navigate(`/lecture/${lectureId}`); navigate(`/lecture/${lectureId}/class`);
} }
if (status === 'NOT_ENROLLED') { if (status === 'NOT_ENROLLED') {

View File

@ -23,6 +23,7 @@ export default function NoticeDetailPage() {
content={notice.content} content={notice.content}
onDelete={handleDelete} onDelete={handleDelete}
isQna={false} isQna={false}
isMine={notice.mine}
/> />
); );
} }

View File

@ -20,7 +20,7 @@ export default function StudentHomePage() {
onGoingClasses.map?.((lecture) => ( onGoingClasses.map?.((lecture) => (
<ClassCard <ClassCard
key={lecture.id} key={lecture.id}
path={`/lecture/${lecture.id}`} path={`/lecture/${lecture.id}/class`}
img={lecture.image} img={lecture.image}
> >
{lecture.title} {lecture.title}
@ -34,7 +34,7 @@ export default function StudentHomePage() {
{allClasses.map?.((lecture) => ( {allClasses.map?.((lecture) => (
<ClassCard <ClassCard
key={lecture.id} key={lecture.id}
path={`/lecture/${lecture.id}/info`} path={`/lecture/${lecture.id}`}
img={lecture.image} img={lecture.image}
> >
{lecture.title} {lecture.title}

View File

@ -16,7 +16,7 @@ export default function TeacherHomePage() {
{onGoingClasses.map((lecture) => ( {onGoingClasses.map((lecture) => (
<ClassCard <ClassCard
key={lecture.id} key={lecture.id}
path={`/lecture/${lecture.id}`} path={`/lecture/${lecture.id}/class`}
img={lecture.image} img={lecture.image}
> >
{lecture.title} {lecture.title}

View File

@ -5,8 +5,8 @@
align-items: center; align-items: center;
gap: 20px; gap: 20px;
margin-bottom: 32px; margin-bottom: 32px;
width: 295px; width: 100%;
height: 220px; aspect-ratio: 4/3;
background-color: var(--background); background-color: var(--background);
color: var(--text-color); color: var(--text-color);
stroke: var(--text-color); stroke: var(--text-color);