Merge pull request #73 from TeamBNBN/fe/router

[Front-End] feat: Router 기초 설정, 페이지 디자인 수정
This commit is contained in:
FlashingFuture 2024-07-22 09:05:29 +09:00 committed by GitHub
commit 70b7237fa9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
66 changed files with 418 additions and 431 deletions

View File

@ -1,10 +1,79 @@
/* eslint-disable react-refresh/only-export-components */
import { createBrowserRouter } from 'react-router-dom';
import { Home } from './pages/HomePage';
import PageLayout from './components/Layout/PageLayout';
import HomePage from './pages/HomePage';
import NotFoundPage from './pages/NotFoundPage';
import { lazy } from 'react';
const LectureLayout = lazy(async () => await import('./components/Layout/LectureLayout'));
const LearningLectureDetailPage = lazy(async () => await import('./pages/LearningLectureDetailPage'));
const NoticeListPage = lazy(async () => await import('./pages/NoticeListPage'));
const NoticeDetailPage = lazy(async () => await import('./pages/NoticeDetailPage'));
const LectureInfoPage = lazy(async () => await import('./pages/LectureInfoPage'));
const QuestionListPage = lazy(async () => await import('./pages/QuestionListPage'));
const QuestionDetailPage = lazy(async () => await import('./pages/QuestionDetailPage'));
const CreateQuestionPage = lazy(async () => await import('./pages/CreateQuestionPage'));
const NoticeWritePage = lazy(async () => await import('./pages/NoticeWritePage/NoticeWritePage'));
const router = createBrowserRouter([
{
path: '/',
element: <Home />,
path: '',
element: <PageLayout />,
errorElement: <NotFoundPage />,
children: [
{
index: true,
element: <HomePage />,
},
{
path: 'lecture/:lectureId/info',
element: <LectureInfoPage />,
},
{
path: 'lecture/:lectureId',
element: <LectureLayout />,
children: [
{
index: true,
element: <LearningLectureDetailPage />,
},
{
path: 'notice',
children: [
{
index: true,
element: <NoticeListPage />,
},
{
path: ':noticeId',
element: <NoticeDetailPage />,
},
{
path: 'write',
element: <NoticeWritePage />,
},
],
},
{
path: 'qna',
children: [
{
index: true,
element: <QuestionListPage />,
},
{
path: ':questionId',
element: <QuestionDetailPage />,
},
{
path: 'write',
element: <CreateQuestionPage />,
},
],
},
],
},
],
},
]);

View File

@ -10,7 +10,7 @@ export default function ArticleDetail({ topic, title, author = null, content, an
<header>
<div>
<Link
to={'/'}
to={'..'}
className={styles.backButton}
>
<div>-</div>

View File

@ -1,4 +1,7 @@
.articleDetail {
display: flex;
flex-direction: column;
gap: 20px;
width: 100%;
background-color: var(--background-default);
box-sizing: border-box;
@ -6,10 +9,6 @@
padding: 0;
}
.articleDetail > * {
padding-bottom: 40px;
}
.backButton {
display: flex;
color: var(--text-color-secondary);
@ -36,3 +35,7 @@
line-height: 1.4;
color: var(--text-color-secondary);
}
.content {
margin: 0 auto 20px;
}

View File

@ -60,7 +60,7 @@ export default function CreateArticle({ topic, title, backPath = '/' }) {
onClick={handleSubmit}
disabled={!articleTitle || !articleContent}
>
<div>| i |</div>
<div>i</div>
<div> 쓰기</div>
</button>
</form>

View File

@ -74,26 +74,27 @@
.button {
display: flex;
flex-direction: row;
padding: 16px 24px;
border-radius: 8px;
box-sizing: border-box;
gap: 8px;
padding: 12px 16px;
border: 1px solid var(--primary-color);
cursor: pointer;
background-color: var(--primary-color);
color: white;
font-family: inherit;
color: var(--on-primary);
font-size: 16px;
line-height: 1.4;
font-weight: 700;
padding-left: 8px;
align-self: end;
gap: 8px;
border-radius: 8px;
cursor: pointer;
transition:
background-color 0.25s,
border-color 0.25s,
color 0.25s;
}
.button:disabled,
.button[disabled] {
border: 1px solid var(--background-tertiary);
border-color: var(--border-color);
background-color: var(--background-tertiary);
color: var(--text-color-tertiary);
cursor: not-allowed;
}

View File

@ -1,25 +1,21 @@
import { useNavigate } from 'react-router-dom';
import { Link } from 'react-router-dom';
import styles from './ArticleBoard.module.css';
export default function ArticleBoard({ title, canCreate, createArticlePath, children }) {
const navigate = useNavigate();
const createArticle = () => {
navigate(createArticlePath);
};
export default function ArticleBoard({ title, canCreate, children }) {
// TODO :
return (
<div className={styles.articleBoard}>
<div className={styles.header}>
<div className={styles.title}>{title}</div>
{canCreate && (
<button
<Link
type="button"
className={styles.button}
onClick={createArticle}
className={styles.writeButton}
to="write"
>
<div></div>
<div className={styles.buttonText}>글쓰기</div>
</button>
</Link>
)}
</div>
<div className={styles.article}>{children}</div>

View File

@ -2,7 +2,7 @@
width: 100%;
display: flex;
flex-direction: column;
gap: 24px;
gap: 12px;
align-items: flex-start;
background-color: var(--background);
}
@ -19,16 +19,20 @@
.title {
font-size: 32px;
line-height: 1.2;
font-weight: 900;
font-weight: 700;
}
.button {
.writeButton {
display: flex;
align-items: center;
gap: 8px;
padding: 12px 16px;
padding: 12px;
background: var(--background);
border: 1px solid var(--border-color);
border-radius: 8px;
font-size: 14px;
line-height: 1.4;
font-weight: 700;
cursor: pointer;
}
@ -43,5 +47,4 @@
display: flex;
flex-direction: column;
width: 100%;
gap: 12px;
}

View File

@ -1,13 +1,13 @@
import { Link } from 'react-router-dom';
import styles from './ArticleLink.module.css';
export default function ArticleLink({ path, title, sub }) {
export default function ArticleLink({ to, title, sub }) {
return (
<Link
to={path}
to={to}
className={styles.articleLink}
>
<span className={styles.note}>{title}</span>
<h3 className={styles.title}>{title}</h3>
<span className={styles.date}>{sub}</span>
</Link>
);

View File

@ -5,16 +5,18 @@
justify-content: space-between;
box-sizing: border-box;
padding: 16px 20px;
transition: background-color 0.25s;
}
.articleLink:hover {
background-color: var(--background-secondary);
}
.note {
font-size: 20px;
line-height: 1.2;
.title {
font-size: 16px;
line-height: 1.4;
font-weight: 400;
margin: 0;
}
.date {

View File

@ -11,7 +11,7 @@
.loginText {
font-size: 36px;
line-height: 1.2;
font-weight: 900;
font-weight: 700;
}
.formGroup {

View File

@ -1,2 +1,2 @@
export { default as AuthForm } from './AuthForm';
export { default as InputBox } from './InputBox';
export { default as InputBox } from './InputBox';

View File

@ -1 +1 @@
export { default as ChatInput } from './ChatInput';
export { default as ChatInput } from './ChatInput';

View File

@ -1,19 +1,16 @@
.card {
display: flex;
flex-direction: column;
gap: 20px;
width: 295px;
height: 295px;
font-size: 20px;
line-height: 1.2;
gap: 12px;
font-size: 16px;
line-height: 1.4;
font-weight: 400;
}
.thumbnail {
width: 100%;
height: 100%;
width: 295px;
height: 220px;
border-radius: 20px;
background-color: var(--background-secondary);
border: 1px solid var(--border-color);
box-sizing: border-box;
}

View File

@ -3,7 +3,7 @@
line-height: 1.2;
font-weight: 700;
color: var(--text-color);
margin: 0 0 40px;
margin: 0 0 20px;
}
.grid {
@ -11,4 +11,5 @@
grid-template-columns: repeat(auto-fill, minmax(295px, auto));
gap: 20px;
justify-content: start;
margin-bottom: 40px;
}

View File

@ -1 +1 @@
export { default as ClassInfo } from './ClassInfo';
export { default as ClassInfo } from './ClassInfo';

View File

@ -1 +1 @@
export { default as CreateClass } from './CreateClass';
export { default as CreateClass } from './CreateClass';

View File

@ -5,7 +5,7 @@
top: 0;
left: 0;
width: 100%;
height: 80px;
height: 64px;
background-color: var(--background);
border-bottom: 1px solid var(--border-color);
}
@ -19,7 +19,7 @@
max-width: 1320px;
padding: 0 40px;
margin: 0 auto;
font-size: 16px;
font-size: 14px;
line-height: 1.4;
font-weight: 700;
color: var(--text-color);

View File

@ -0,0 +1,44 @@
import { Outlet } from 'react-router-dom';
import LectureHeader from '../LectureHeader/LectureHeader';
import { SideBar, SideLink } from '../SideBar';
import MaxWidthLayout from './MaxWidthLayout';
import { Suspense } from 'react';
export default function LectureLayout() {
const lecture = {
title: '정보처리기사 실기 완전정복',
tutor: '박정민',
isLive: true,
};
return (
<>
<LectureHeader
title={lecture.title}
tutor={lecture.tutor}
isLive={lecture.isLive}
/>
<MaxWidthLayout hasSideBar>
<aside>
<SideBar title="바로가기">
<SideLink
to={''}
end
>
수업
</SideLink>
<SideLink to={'notice'}>공지사항</SideLink>
<SideLink to={'qna'}>Q&A</SideLink>
<SideLink to={'file'}>수업자료</SideLink>
<SideLink to={'quiz'}>퀴즈</SideLink>
</SideBar>
</aside>
<main>
<Suspense fallback={<div>loading</div>}>
<Outlet />
</Suspense>
</main>
</MaxWidthLayout>
</>
);
}

View File

@ -1,13 +1,19 @@
import { Outlet } from 'react-router-dom';
import { Footer } from '../Footer';
import { Header } from '../Header';
import styles from './PageLayout.module.css';
import { Suspense } from 'react';
export default function PageLayout({ children }) {
export default function PageLayout() {
return (
<>
<Header />
<div className={styles.body}>
<div className={styles.contents}>{children}</div>
<div className={styles.contents}>
<Suspense fallback={<div>loading</div>}>
<Outlet />
</Suspense>
</div>
<Footer />
</div>
</>

View File

@ -6,5 +6,5 @@
}
.contents {
margin-top: 100px;
margin-top: 84px;
}

View File

@ -19,8 +19,8 @@
.thumbnail {
flex-shrink: 0;
width: 160px;
height: 160px;
width: 120px;
height: 120px;
border-radius: 16px;
border: 1px solid var(--border-color);
}
@ -35,13 +35,14 @@
.title {
font-size: 36px;
line-height: 1.2;
font-weight: 900;
font-weight: 700;
margin: 0;
}
.desc {
display: flex;
justify-content: space-between;
align-items: center;
}
.tutor {
@ -60,7 +61,7 @@
.liveButton {
padding: 12px 16px;
text-align: center;
font-size: 16px;
font-size: 14px;
line-height: 1.4;
font-family: inherit;
font-weight: 700;

View File

@ -1,15 +1,16 @@
import { Link } from 'react-router-dom';
import { NavLink } from 'react-router-dom';
import styles from './SideLink.module.css';
export default function SideLink({ children, path }) {
export default function SideLink({ children, to, end = false }) {
return (
<li className={styles.list}>
<Link
to={path}
className={styles.link}
<NavLink
to={to}
className={({ isActive }) => (isActive ? styles.active : styles.link)}
end={end}
>
{children}
</Link>
</NavLink>
</li>
);
}

View File

@ -3,11 +3,17 @@
margin-bottom: 16px;
}
.link {
.link,
.active {
width: 100%;
text-decoration: none;
color: var(--text-color);
font-size: 20px;
line-height: 1.2;
font-size: 16px;
line-height: 1.4;
font-weight: 400;
}
.active {
font-weight: 700;
text-decoration: underline;
}

View File

@ -173,3 +173,14 @@ a {
color: inherit;
text-decoration: inherit;
}
button,
input,
optgroup,
select,
textarea {
margin: 0;
font-family: inherit;
font-size: inherit;
line-height: inherit;
}

View File

@ -1,48 +1,10 @@
import LectureHeader from '../../components/LectureHeader/LectureHeader';
import { SideBar, SideLink, SideItem } from '../../components/SideBar';
import { MaxWidthLayout } from '../../components/Layout';
import { CreateArticle } from '../../components/Article';
export default function CreateQuestionPage() {
const lecture = {
title: '정보처리기사 실기 완전정복',
tutor: '박정민',
isLive: true,
};
return (
<>
<LectureHeader
title={lecture.title}
tutor={lecture.tutor}
isLive={lecture.isLive}
/>
<MaxWidthLayout hasSideBar>
<aside>
<SideBar title="바로가기">
<SideLink to={'/'}>공지사항</SideLink>
<SideLink to={'/'}>Q&A</SideLink>
<SideLink to={'/'}>수업자료</SideLink>
<SideLink to={'/'}>퀴즈</SideLink>
</SideBar>
<SideBar title="내 학습">
<SideItem
name={'학습 진도'}
sub={'2 / 12'}
/>
<SideItem
name={'퀴즈 점수'}
sub={'80%'}
/>
</SideBar>
</aside>
<main>
<CreateArticle
topic="질문하기"
title="Q&A"
/>
</main>
</MaxWidthLayout>
</>
<CreateArticle
topic="질문하기"
title="Q&A"
/>
);
}

View File

@ -0,0 +1 @@
export { default } from './CreateQuestionPage';

View File

@ -1,10 +1,15 @@
import { Footer } from '../../components/Footer';
import useBoundStore from '../../store';
import StudentHomePage from '../StudentHomePage/StudentHomePage';
export default function Home() {
return (
<div>
<div>asdf</div>
<Footer />
</div>
);
export default function HomePage() {
const userType = useBoundStore((state) => state.userType);
switch (userType) {
case 'student':
return <StudentHomePage />;
case 'teacher':
return <div>teacher home</div>;
default:
return <StudentHomePage />;
}
}

View File

@ -1 +1 @@
export { default as Home } from './HomePage';
export { default } from './HomePage';

View File

@ -1,48 +1,11 @@
import LectureHeader from '../../components/LectureHeader/LectureHeader';
import { SideBar, SideLink, SideItem } from '../../components/SideBar';
import { MaxWidthLayout } from '../../components/Layout';
import ArticlePreview from '../../components/Article/ArticlePreview/ArticlePreview';
import styles from './LearningLectureDetailPage.module.css';
export default function LearningLectureDetailPage() {
const lecture = {
title: '정보처리기사 실기 완전정복',
tutor: '박정민',
isLive: true,
};
return (
<>
<LectureHeader
title={lecture.title}
tutor={lecture.tutor}
isLive={lecture.isLive}
/>
<MaxWidthLayout hasSideBar>
<aside>
<SideBar title="바로가기">
<SideLink to={'/'}>공지사항</SideLink>
<SideLink to={'/'}>Q&A</SideLink>
<SideLink to={'/'}>수업자료</SideLink>
<SideLink to={'/'}>퀴즈</SideLink>
</SideBar>
<SideBar title="내 학습">
<SideItem
name={'학습 진도'}
sub={'2 / 12'}
/>
<SideItem
name={'퀴즈 점수'}
sub={'80%'}
/>
</SideBar>
</aside>
<main className={styles.previews}>
<ArticlePreview />
<ArticlePreview />
<ArticlePreview />
</main>
</MaxWidthLayout>
<ArticlePreview />
<ArticlePreview />
<ArticlePreview />
</>
);
}

View File

@ -0,0 +1 @@
export { default } from './LearningLectureDetailPage';

View File

@ -0,0 +1 @@
export { default } from './LectureInfoPage';

View File

@ -1,48 +0,0 @@
import LectureHeader from '../../components/LectureHeader/LectureHeader';
import { SideBar, SideLink, SideItem } from '../../components/SideBar';
import { MaxWidthLayout } from '../../components/Layout';
import { CreateArticle } from '../../components/Article';
export default function LecturerCreateNoticePage() {
const lecture = {
title: '정보처리기사 실기 완전정복',
tutor: '박정민',
isLive: true,
};
return (
<>
<LectureHeader
title={lecture.title}
tutor={lecture.tutor}
isLive={lecture.isLive}
/>
<MaxWidthLayout hasSideBar>
<aside>
<SideBar title="바로가기">
<SideLink to={'/'}>공지사항</SideLink>
<SideLink to={'/'}>Q&A</SideLink>
<SideLink to={'/'}>수업자료</SideLink>
<SideLink to={'/'}>퀴즈</SideLink>
</SideBar>
<SideBar title="내 강의">
<SideItem
name={'학습 진도'}
sub={'2 / 12'}
/>
<SideItem
name={'퀴즈 점수'}
sub={'80%'}
/>
</SideBar>
</aside>
<main>
<CreateArticle
topic="글 쓰기"
title="공지사항"
/>
</main>
</MaxWidthLayout>
</>
);
}

View File

@ -0,0 +1 @@
export { default } from './LoginPage';

View File

@ -0,0 +1 @@
export { default } from './MyInfoChangePage';

View File

@ -0,0 +1,23 @@
import styles from './NotFoundPage.module.css';
import { Footer } from '../../components/Footer';
import { Header } from '../../components/Header';
export default function NotFoundPage() {
return (
<>
<Header />
<div className={styles.body}>
<div className={styles.notFound}>
<h1>페이지를 찾을 없습니다.</h1>
<button
className={styles.goBack}
onClick={() => window.history.back()}
>
이전 페이지로 돌아가기
</button>
</div>
<Footer />
</div>
</>
);
}

View File

@ -0,0 +1,51 @@
@keyframes variable-loop {
0% {
font-weight: 50;
}
50% {
font-weight: 1000;
}
100% {
font-weight: 50;
}
}
.body {
display: flex;
flex-direction: column;
justify-content: space-between;
min-height: 100vh;
}
.notFound {
flex-grow: 1;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
font-size: 16px;
line-height: 1.4;
font-weight: 400;
color: var(--text-color);
& > h1 {
animation: variable-loop 4s infinite;
font-size: 64px;
line-height: 1.2;
margin: 0;
}
}
.goBack {
display: block;
color: var(--primary-color);
text-decoration: none;
text-align: center;
margin: 10px auto 0;
padding: 0;
background: none;
border: none;
cursor: pointer;
}

View File

@ -0,0 +1 @@
export { default } from './NotFoundPage';

View File

@ -1,53 +1,15 @@
import LectureHeader from '../../components/LectureHeader/LectureHeader';
import { SideBar, SideLink, SideItem } from '../../components/SideBar';
import { MaxWidthLayout } from '../../components/Layout';
import { ArticleDetail } from '../../components/Article';
export default function NoticeDetailPage() {
const lecture = {
title: '정보처리기사 실기 완전정복',
tutor: '박정민',
isLive: true,
};
const title = '2주차 공지사항';
const content =
'이런저런 꿀팁 공부를 열심히 하는 사람을 떨어지지 않는다.' +
'동해 물과 백두산이 마르고 닳도록 하느님이 보우하사 우리 나라 만세. 무궁화 삼천리 화려강산 대한 사람 대한으로 길이 보전하세. 줄이 길어지면 다음 줄로 자동 줄바꿈 된다';
return (
<>
<LectureHeader
title={lecture.title}
tutor={lecture.tutor}
isLive={lecture.isLive}
/>
<MaxWidthLayout hasSideBar>
<aside>
<SideBar title="바로가기">
<SideLink to={'/'}>공지사항</SideLink>
<SideLink to={'/'}>Q&A</SideLink>
<SideLink to={'/'}>수업자료</SideLink>
<SideLink to={'/'}>퀴즈</SideLink>
</SideBar>
<SideBar title="내 학습">
<SideItem
name={'학습 진도'}
sub={'2 / 12'}
/>
<SideItem
name={'퀴즈 점수'}
sub={'80%'}
/>
</SideBar>
</aside>
<main>
<ArticleDetail
topic="공지사항"
title={title}
content={content}
/>
</main>
</MaxWidthLayout>
</>
<ArticleDetail
topic="공지사항"
title={title}
content={content}
/>
);
}

View File

@ -0,0 +1 @@
export { default } from './NoticeDetailPage';

View File

@ -1,69 +1,27 @@
import LectureHeader from '../../components/LectureHeader/LectureHeader';
import { SideBar, SideLink, SideItem } from '../../components/SideBar';
import { ArticleLink } from '../../components/ArticleLink';
import { MaxWidthLayout } from '../../components/Layout';
import ArticleBoard from '../../components/ArticleBoard/ArticleBoard';
export default function NoticeListPage() {
const notices = [
{},
{ title: '공지사항1', sub: '7-12 오전 11:40:57' },
{ title: '공지사하앙2', sub: '7-12 오전 11:40:57' },
{ title: '공지사하앙33', sub: '7-15 오전 11:40:57' },
{ title: '제목만 있는 경우' },
{ sub: '날짜만 있는 경우' },
{ id: 1, title: '공지사항1', sub: '7-12 오전 11:40:57' },
{ id: 2, title: '공지사하앙2', sub: '7-12 오전 11:40:57' },
{ id: 3, title: '공지사하앙33', sub: '7-15 오전 11:40:57' },
{ id: 4, title: '제목만 있는 경우' },
];
const lecture = {
title: '정보처리기사 실기 완전정복',
tutor: '박정민',
isLive: true,
};
return (
<>
<LectureHeader
title={lecture.title}
tutor={lecture.tutor}
isLive={lecture.isLive}
/>
<MaxWidthLayout hasSideBar>
<aside>
<SideBar title="바로가기">
<SideLink path={'/'}>공지사항</SideLink>
<SideLink path={'/'}>Q&A</SideLink>
<SideLink path={'/'}>수업자료</SideLink>
<SideLink path={'/'}>퀴즈</SideLink>
</SideBar>
<SideBar title="내 학습">
<SideItem
name="진도율"
sub="2 / 12"
/>
<SideItem
name="퀴즈 정답률"
sub="80%"
/>
</SideBar>
</aside>
<main>
<ArticleBoard
title="공지사항"
canCreate={true}
>
{notices.map((notice) => {
if (notice.sub && notice.title) {
return (
<ArticleLink
key={`${notice.title}${notice.sub}`}
title={notice.title}
sub={notice.sub}
/>
);
}
})}
</ArticleBoard>
</main>
</MaxWidthLayout>
</>
<ArticleBoard
title="공지사항"
canCreate={true}
>
{notices.map((notice) => (
<ArticleLink
key={`${notice.title}${notice.sub}`}
title={notice.title}
sub={notice.sub}
to={`${notice.id}`}
/>
))}
</ArticleBoard>
);
}

View File

@ -0,0 +1 @@
export { default } from './NoticeListPage';

View File

@ -0,0 +1,10 @@
import { CreateArticle } from '../../components/Article';
export default function NoticeWritePage() {
return (
<CreateArticle
topic="글 쓰기"
title="공지사항"
/>
);
}

View File

@ -0,0 +1 @@
export { default } from './NoticeWritePage';

View File

@ -0,0 +1 @@
export { default } from './PasswordChangePage';

View File

@ -0,0 +1 @@
export { default } from './PasswordResetPage';

View File

@ -1,55 +1,18 @@
import LectureHeader from '../../components/LectureHeader/LectureHeader';
import { SideBar, SideLink, SideItem } from '../../components/SideBar';
import { MaxWidthLayout } from '../../components/Layout';
import { ArticleDetail } from '../../components/Article';
export default function QuestionDetailPage() {
const lecture = {
title: '정보처리기사 실기 완전정복',
tutor: '박정민',
isLive: true,
};
const title = '헷갈리는게 있어요';
const author = '이재용';
const content = 'Lorem ipsum dolor, sit amet consectetur adipisicing elit. Perferendis sed dolorem vitae?';
const answer = { answerId: '144632619Ux15326', content: '우리 재용이는 참 예의가 없구나' };
return (
<>
<LectureHeader
title={lecture.title}
tutor={lecture.tutor}
isLive={lecture.isLive}
/>
<MaxWidthLayout hasSideBar>
<aside>
<SideBar title="바로가기">
<SideLink to={'/'}>공지사항</SideLink>
<SideLink to={'/'}>Q&A</SideLink>
<SideLink to={'/'}>수업자료</SideLink>
<SideLink to={'/'}>퀴즈</SideLink>
</SideBar>
<SideBar title="내 학습">
<SideItem
name={'학습 진도'}
sub={'2 / 12'}
/>
<SideItem
name={'퀴즈 점수'}
sub={'80%'}
/>
</SideBar>
</aside>
<main>
<ArticleDetail
topic="Q&A"
title={title}
author={author}
content={content}
answer={answer.content}
/>
</main>
</MaxWidthLayout>
</>
return (
<ArticleDetail
topic="Q&A"
title={title}
author={author}
content={content}
answer={answer.content}
/>
);
}

View File

@ -0,0 +1 @@
export { default } from './QuestionDetailPage';

View File

@ -1,70 +1,27 @@
import LectureHeader from '../../components/LectureHeader/LectureHeader';
import { SideBar, SideLink, SideItem } from '../../components/SideBar';
import { ArticleLink } from '../../components/ArticleLink';
import { MaxWidthLayout } from '../../components/Layout';
import ArticleBoard from '../../components/ArticleBoard/ArticleBoard';
export default function QuestionListPage() {
const notices = [
{},
{ title: 'Question1', sub: '7-12 오전 11:40:57' },
{ title: 'Question2', sub: '7-12 오전 11:40:57' },
{ title: '헷갈리는게 있어요', sub: '7-15 오전 11:40:57' },
{ title: '궁금궁금', sub: '7-15 오전 11:40:57' },
{ sub: '날짜만 있는 경우' },
const questions = [
{ id: 2, title: 'Question1', sub: '7-12 오전 11:40:57' },
{ id: 3, title: 'Question2', sub: '7-12 오전 11:40:57' },
{ id: 4, title: '헷갈리는게 있어요', sub: '7-15 오전 11:40:57' },
{ id: 5, title: '궁금궁금', sub: '7-15 오전 11:40:57' },
];
const lecture = {
title: '정보처리기사 실기 완전정복',
tutor: '박정민',
isLive: true,
};
return (
<>
<LectureHeader
title={lecture.title}
tutor={lecture.tutor}
isLive={lecture.isLive}
/>
<MaxWidthLayout hasSideBar>
<aside>
<SideBar title="바로가기">
<SideLink path={'/'}>공지사항</SideLink>
<SideLink path={'/'}>Q&A</SideLink>
<SideLink path={'/'}>수업자료</SideLink>
<SideLink path={'/'}>퀴즈</SideLink>
</SideBar>
<SideBar title="내 학습">
<SideItem
name="진도율"
sub="2 / 12"
/>
<SideItem
name="퀴즈 정답률"
sub="80%"
/>
</SideBar>
</aside>
<main>
<ArticleBoard
title="Q&A"
canCreate={true}
createPath="/"
>
{notices.map((notice) => {
if (notice.sub && notice.title) {
return (
<ArticleLink
key={`${notice.title}${notice.sub}`}
title={notice.title}
sub={notice.sub}
/>
);
}
})}
</ArticleBoard>
</main>
</MaxWidthLayout>
</>
<ArticleBoard
title="Q&A"
canCreate={true}
>
{questions.map((question) => (
<ArticleLink
key={`${question.title}${question.sub}`}
title={question.title}
sub={question.sub}
to={`${question.id}`}
/>
))}
</ArticleBoard>
);
}

View File

@ -0,0 +1 @@
export { default } from './QuestionListPage';

View File

@ -2,7 +2,7 @@ import { ClassCard } from '../../components/ClassCard';
import { ClassGrid } from '../../components/ClassGrid';
import { MaxWidthLayout } from '../../components/Layout';
export default function StudentHomeMainPage() {
export default function StudentHomePage() {
const onGoingClasses = [
{ lecture_id: 1, title: '한국어' },
{ lecture_id: 2, title: '영어' },
@ -23,7 +23,7 @@ export default function StudentHomeMainPage() {
{onGoingClasses.map((lecture) => (
<ClassCard
key={lecture.lecture_id}
path={`/class/${lecture.lecture_id}`}
path={`/lecture/${lecture.lecture_id}`}
>
{lecture.title}
</ClassCard>

View File

@ -0,0 +1 @@
export { default } from './StudentHomePage';

View File

@ -4,7 +4,7 @@ import { ArticleLink } from '../../components/ArticleLink';
import { MaxWidthLayout } from '../../components/Layout';
import ArticleBoard from '../../components/ArticleBoard/ArticleBoard';
export default function QuestionListPage() {
export default function StudentListPage() {
const students = [
{ name: '학생1', quizScore: 40 },
{ name: '학생2', quizScore: 40 },
@ -41,7 +41,7 @@ export default function QuestionListPage() {
</SideBar>
</aside>
<main>
<ArticleBoard title="Q&A">
<ArticleBoard title="수강생 관리">
{students.map((student) => {
return (
<ArticleLink

View File

@ -0,0 +1 @@
export { default } from './StudentListPage';

View File

@ -2,9 +2,9 @@ import LectureHeader from '../../components/LectureHeader/LectureHeader';
import { SideBar, SideLink, SideItem } from '../../components/SideBar';
import { MaxWidthLayout } from '../../components/Layout';
import ArticlePreview from '../../components/Article/ArticlePreview/ArticlePreview';
import styles from './LecturerLectureDetailPage.module.css';
import styles from './TeacherLectureDetailPage.module.css';
export default function LecturerLectureDetailPage() {
export default function TeacherLectureDetailPage() {
const lecture = {
title: '정보처리기사 실기 완전정복',
tutor: '박정민',

View File

@ -0,0 +1 @@
export { default } from './TeacherLectureDetailPage';

View File

@ -4,7 +4,7 @@ import { ArticleLink } from '../../components/ArticleLink';
import { MaxWidthLayout } from '../../components/Layout';
import ArticleBoard from '../../components/ArticleBoard/ArticleBoard';
export default function LecturerNoticeListPage() {
export default function TeacherNoticeListPage() {
const notices = [
{},
{ title: '공지사항1', sub: '7-12 오전 11:40:57' },

View File

@ -0,0 +1 @@
export { default } from './TeacherNoticeListPage';

View File

@ -17,7 +17,7 @@
.RegisterText {
font-size: 36px;
line-height: 1.2;
font-weight: 900;
font-weight: 700;
}
.textBodyStrong {

View File

@ -0,0 +1 @@
export { default } from './UserRegisterPage';

View File

@ -0,0 +1,10 @@
import { create } from 'zustand';
import { userTypeSlice } from './userTypeSlice';
import { tokenSlice } from './tokenSlice';
const useBoundStore = create((...a) => ({
...userTypeSlice(...a),
...tokenSlice(...a),
}));
export default useBoundStore;

View File

@ -0,0 +1,4 @@
export const tokenSlice = (set) => ({
token: null,
setToken: (token) => set({ token }),
});

View File

@ -0,0 +1,9 @@
// userType : null, 'teacher', 'student'
export const userTypeSlice = (set) => ({
userType: null,
setUserType: (userType) => set({ userType }),
isTeacher: () => set.userType === 'teacher',
isStudent: () => set.userType === 'student',
isGuest: () => set.userType === null,
});

View File

@ -1,6 +1,5 @@
import axios from 'axios';
const ACCESS_TOKEN_KEY = import.meta.env.VITE_ACCESS_TOKEN_KEY;
import useBoundStore from '../../store';
const instance = axios.create({
baseURL: import.meta.env.VITE_API_URL,
@ -9,7 +8,7 @@ const instance = axios.create({
});
instance.interceptors.request.use((config) => {
const accessToken = sessionStorage.getItem(ACCESS_TOKEN_KEY);
const accessToken = useBoundStore.getState().accessToken;
if (accessToken) {
config.headers.Authorization = `Bearer ${accessToken}`;
@ -31,7 +30,7 @@ instance.interceptors.response.use(
return instance.post(REFRESH_API_URL).then((response) => {
const { accessToken } = response.data;
sessionStorage.setItem(ACCESS_TOKEN_KEY, accessToken);
useBoundStore.setState({ accessToken });
error.config.headers.Authorization = `Bearer ${accessToken}`;
return instance(error.config);
});