Merge branch 'fe/infiniteScroll' into 'frontend'

[Front-End] 공지사항, 자유게시판에 무한스크롤 추가

See merge request s11-webmobile1-sub2/S11P12A701!158
This commit is contained in:
정기영 2024-08-12 08:41:42 +09:00
commit d755840501
51 changed files with 191 additions and 313 deletions

View File

@ -31,7 +31,6 @@ export default function ArticleDetailAnswerInput({ onSubmit, initialAnswer = '',
const handleSubmit = async (e) => { const handleSubmit = async (e) => {
e.preventDefault(); e.preventDefault();
console.log(isEditing);
if (isEditing) { if (isEditing) {
await answerEdit(questionId, answer); await answerEdit(questionId, answer);
} else { } else {
@ -40,8 +39,6 @@ export default function ArticleDetailAnswerInput({ onSubmit, initialAnswer = '',
onSubmit(answer); onSubmit(answer);
}; };
console.log(answer);
return ( return (
<form <form
onSubmit={handleSubmit} onSubmit={handleSubmit}

View File

@ -3,9 +3,7 @@
flex-direction: column; flex-direction: column;
gap: 8px; gap: 8px;
width: 100%; width: 100%;
border: 1px solid var(--border-color); padding: 12px 0;
border-radius: 16px;
padding: 12px 16px;
box-sizing: border-box; box-sizing: border-box;
} }
@ -43,6 +41,7 @@
line-height: 1.4; line-height: 1.4;
margin: 0; margin: 0;
color: var(--text-color); color: var(--text-color);
word-break: break-word;
} }
.input { .input {

View File

@ -56,18 +56,22 @@ export default function FreeboardDetail({ topic, title, author, content, onDelet
<div> <div>
<p className={styles.content}>{content}</p> <p className={styles.content}>{content}</p>
</div> </div>
{comments &&
comments.map((comment) => ( {comments && (
<FreeboardComment <div className={styles.commentWrapper}>
key={comment.id} {comments.map((comment) => (
content={comment.content} <FreeboardComment
author={comment.name} key={comment.id}
commentId={comment.id} content={comment.content}
isMine={comment.mine} author={comment.name}
onDeleteSubmit={refetch} commentId={comment.id}
onEditSubmit={refetch} isMine={comment.mine}
/> onDeleteSubmit={refetch}
))} onEditSubmit={refetch}
/>
))}
</div>
)}
<FreeboardCommentInput onCommentSubmit={handleCommentSubmit} /> <FreeboardCommentInput onCommentSubmit={handleCommentSubmit} />
</div> </div>
); );

View File

@ -114,3 +114,11 @@
.delete { .delete {
color: var(--error-color); color: var(--error-color);
} }
.commentWrapper {
display: flex;
flex-direction: column;
border-top: 1px solid var(--border-color);
margin-top: 10px;
padding-top: 18px;
}

View File

@ -1,11 +1,13 @@
.articleLink { .articleLink {
border-radius: 8px;
width: 100%;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
box-sizing: border-box; gap: 20px;
min-width: 0;
width: 100%;
padding: 16px 20px; padding: 16px 20px;
border-radius: 8px;
transition: background-color 0.25s; transition: background-color 0.25s;
box-sizing: border-box;
} }
.articleLink:hover { .articleLink:hover {
@ -17,9 +19,13 @@
line-height: 1.4; line-height: 1.4;
font-weight: 400; font-weight: 400;
margin: 0; margin: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
} }
.date { .date {
flex-shrink: 0;
font-size: 14px; font-size: 14px;
line-height: 1.4; line-height: 1.4;
font-weight: 400; font-weight: 400;

View File

@ -1,27 +0,0 @@
import styles from './ChatInput.module.css';
import { useRef } from 'react';
export default function ChattingInput() {
// TODO:
// TODO:
const message = useRef('');
const handleSubmit = (e) => {
e.preventDefault();
// TODO:
console.log(message.current.value);
message.current.value = '';
};
return (
<form
onSubmit={handleSubmit}
className={styles.chattingInput}
>
<input
ref={message}
className={styles.input}
placeholder="메시지를 입력하세요"
/>
<div onClick={handleSubmit}></div>
</form>
);
}

View File

@ -1,25 +0,0 @@
.chattingInput {
border-radius: 9999px;
background-color: var(--background);
width: 100%;
box-sizing: border-box;
padding: 12px 16px;
display: flex;
flex-direction: row;
justify-content: space-between;
gap: 8px;
}
.input {
font-size: 16px;
font-weight: 400;
line-height: 1;
color: var(--text-color);
border: 0;
outline: none;
width: 100%;
}
.input::placeholder {
color: var(--text-color-tertiary);
}

View File

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

View File

@ -12,6 +12,7 @@
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
position: relative;
padding: 16px; padding: 16px;
& > .quizButton { & > .quizButton {

View File

@ -1,60 +0,0 @@
import styles from './CreateClass.module.css';
import { useRef } from 'react';
export default function CreateClass() {
// TODO:
const classTime = useRef('');
const numOfLecture = useRef('');
const significant = useRef('');
const handleSubmit = () => {
// TODO :
const payload = {
classTime: classTime.current.value,
numOfLecture: numOfLecture.current.value,
significant: significant.current.value,
};
alert(`특이사항 : ${payload.significant}`);
};
return (
<form
className={styles.createClass}
onSubmit={handleSubmit}
>
<div className={styles.inputField}>
<label className={styles.label}>수업 시간</label>
<input
className={styles.input}
ref={classTime}
type="text"
placeholder="수업 시간을 입력하세요"
/>
</div>
<div className={styles.inputField}>
<label className={styles.label}>강의 </label>
<input
className={styles.input}
ref={numOfLecture}
type="text"
placeholder="총 강의 수를 입력하세요"
/>
</div>
<div className={styles.inputField}>
<label className={styles.label}>특이사항</label>
<textarea
ref={significant}
className={styles.textarea}
placeholder="이 수업만의 특이사항이 있다면 입력하세요"
></textarea>
</div>
<button
type="submit"
className={styles.button}
>
<div></div>
<div className={styles.buttonText}> 쓰기</div>
</button>
</form>
);
}

View File

@ -1,67 +0,0 @@
.createClass {
background: var(--background-color);
width: 100%;
display: flex;
flex-direction: column;
gap: 40px;
}
.inputField {
display: flex;
flex-direction: column;
gap: 8px;
}
.label {
color: var(--text-color);
font-size: 16px;
font-weight: 400;
line-height: 1.4;
}
.input {
background: var(--background-color);
padding: 20px;
border: 1px solid var(--border-color);
border-radius: 8px;
font-size: 20px;
line-height: 1.2;
font-weight: 400;
}
.input::placeholder {
color: var(--text-color-tertiary);
}
.textarea {
padding: 20px;
height: 150px;
background: var(--background-color);
border: 1px solid var(--border-color);
border-radius: 8px;
font-size: 16px;
line-height: 1.4;
font-weight: 400;
}
.textarea::placeholder {
color: var(--text-color-tertiary);
}
.button {
display: flex;
flex-direction: row;
padding: 16px 24px;
border-radius: 8px;
border: 1px solid var(--primary-color);
background-color: var(--primary-color);
color: var(--on-primary);
gap: 8px;
align-self: end;
}
.buttonText {
font-size: 16px;
line-height: 1.4;
font-weight: 700;
}

View File

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

View File

@ -0,0 +1,33 @@
import { useEffect, useRef } from 'react';
export default function IntersectionArea({ onObserve, once = false }) {
const ref = useRef(null);
useEffect(() => {
const callback = (entries, observer) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
onObserve();
if (once) {
observer.disconnect();
}
}
});
};
const observer = new IntersectionObserver(callback, {
root: null,
rootMargin: '0px',
threshold: 0.5,
});
if (ref.current) {
observer.observe(ref.current);
}
return () => {
observer.disconnect();
};
}, [onObserve, once]);
return <div ref={ref} />;
}

View File

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

View File

@ -19,6 +19,7 @@
} }
& > main { & > main {
min-width: 0;
width: 100%; width: 100%;
} }
} }

View File

@ -14,7 +14,6 @@ export default function LectureForm({ title, topic, to = '..', initialValues = {
const timeRef = useRef(''); const timeRef = useRef('');
const imageFileRef = useRef(null); const imageFileRef = useRef(null);
//
useEffect(() => { useEffect(() => {
if (initialValues.title) titleRef.current.value = initialValues.title; if (initialValues.title) titleRef.current.value = initialValues.title;
if (initialValues.description) descriptionRef.current.value = initialValues.description; if (initialValues.description) descriptionRef.current.value = initialValues.description;
@ -60,7 +59,10 @@ export default function LectureForm({ title, topic, to = '..', initialValues = {
</Link> </Link>
<div className={styles.title}>{topic}</div> <div className={styles.title}>{topic}</div>
</header> </header>
<form onSubmit={handleSubmit}> <form
onSubmit={handleSubmit}
className={styles.form}
>
<div className={styles.inputField}> <div className={styles.inputField}>
<label className={styles.label}>강의명</label> <label className={styles.label}>강의명</label>
<input <input
@ -89,16 +91,24 @@ export default function LectureForm({ title, topic, to = '..', initialValues = {
</div> </div>
<div className={styles.inputField}> <div className={styles.inputField}>
<label className={styles.label}>강의 기간</label> <label className={styles.label}>강의 기간</label>
<input <div className={styles.dateWrapper}>
className={styles.input} <div className={styles.date}>
ref={startDateRef} <input
type="date" className={styles.input}
/> ref={startDateRef}
<input type="date"
className={styles.input} />
ref={endDateRef} <span className={styles.label}>부터</span>
type="date" </div>
/> <div className={styles.date}>
<input
className={styles.input}
ref={endDateRef}
type="date"
/>
<span className={styles.label}>까지</span>
</div>
</div>
</div> </div>
<div className={styles.inputField}> <div className={styles.inputField}>
<label className={styles.label}>수업 시간</label> <label className={styles.label}>수업 시간</label>
@ -117,6 +127,7 @@ export default function LectureForm({ title, topic, to = '..', initialValues = {
type="file" type="file"
ref={imageFileRef} ref={imageFileRef}
accept=".png, .jpg, .jpeg" accept=".png, .jpg, .jpeg"
className={styles.input}
/> />
</div> </div>
)} )}

View File

@ -32,6 +32,33 @@
font-weight: 800; font-weight: 800;
} }
.form {
display: flex;
flex-direction: column;
gap: 20px;
}
.dateWrapper {
display: flex;
gap: 40px;
}
.date {
display: flex;
justify-content: stretch;
align-items: center;
gap: 12px;
width: 100%;
& > input {
width: 100%;
}
& > span {
flex-shrink: 0;
}
}
.inputField { .inputField {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -47,11 +74,11 @@
.input { .input {
background: var(--background-color); background: var(--background-color);
padding: 20px; padding: 16px;
border: 1px solid var(--border-color); border: 1px solid var(--border-color);
border-radius: 8px; border-radius: 8px;
font-size: 20px; font-size: 16px;
line-height: 1.2; line-height: 1.4;
font-weight: 400; font-weight: 400;
} }
@ -60,7 +87,7 @@
} }
.textarea { .textarea {
padding: 20px; padding: 16px;
height: 80px; height: 80px;
background: var(--background-color); background: var(--background-color);
border: 1px solid var(--border-color); border: 1px solid var(--border-color);

View File

@ -116,7 +116,7 @@ export default function LiveRoom() {
<span>{participants.length}</span> <span>{participants.length}</span>
</div> </div>
</header> </header>
<div className="lk-video-conference"> <div className={`lk-video-conference ${styles.videoRoom}`}>
<LayoutContextProvider value={layoutContext}> <LayoutContextProvider value={layoutContext}>
<div className="lk-video-conference-inner"> <div className="lk-video-conference-inner">
{!focusTrack ? ( {!focusTrack ? (

View File

@ -30,3 +30,7 @@
line-height: 1.4; line-height: 1.4;
font-weight: 500; font-weight: 500;
} }
.videoRoom {
height: calc(100% - 48px) !important;
}

View File

@ -16,7 +16,10 @@
width: 100%; width: 100%;
height: 100%; height: 100%;
padding: 0 16px; padding: 0 16px;
white-space: pre-line;
word-break: break-word;
box-sizing: border-box; box-sizing: border-box;
overflow-y: auto;
} }
.header { .header {

View File

@ -2,7 +2,6 @@ import styles from './QuizCard.module.css';
import { STATIC_URL } from '../../constants'; import { STATIC_URL } from '../../constants';
export default function QuizDetailCard({ index, question, answer, image, choices, userAnswer = null, correct = true }) { export default function QuizDetailCard({ index, question, answer, image, choices, userAnswer = null, correct = true }) {
console.log(correct);
return ( return (
<div className={`${styles.card} ${!correct && styles.incorrect}`}> <div className={`${styles.card} ${!correct && styles.incorrect}`}>
<div className={styles.header}> <div className={styles.header}>

View File

@ -16,7 +16,6 @@ export default function QuizsetForm({ headerTitle, topic, to, onSubmit, initialV
setTitle(initialValue.title || ''); setTitle(initialValue.title || '');
setQuizzes(initialValue.quizzes || []); setQuizzes(initialValue.quizzes || []);
setQuizId(initialValue.quizzes ? initialValue.quizzes[initialValue.quizzes.length - 1].id + 1 : 0); setQuizId(initialValue.quizzes ? initialValue.quizzes[initialValue.quizzes.length - 1].id + 1 : 0);
console.log(initialValue.quizzes.length);
} }
}, [initialValue]); }, [initialValue]);
@ -79,7 +78,7 @@ export default function QuizsetForm({ headerTitle, topic, to, onSubmit, initialV
</div> </div>
<button <button
type="submit" type="submit"
className={`${styles.button} ${styles.add} ${styles.create}`} className={`${styles.button} ${styles.add}`}
> >
<EditIcon /> <EditIcon />
<div>퀴즈 생성하기</div> <div>퀴즈 생성하기</div>

View File

@ -109,10 +109,6 @@
stroke: var(--on-primary); stroke: var(--on-primary);
} }
.create {
margin-top: 60px;
}
.edit { .edit {
font-size: 32px; font-size: 32px;
} }

View File

@ -29,7 +29,6 @@ export default function QuizModal({ startQuiz, quizSets, closeModal }) {
<li <li
key={quizSet.quizSetId} key={quizSet.quizSetId}
onClick={() => { onClick={() => {
console.log(quizSet.quizSetId);
startQuiz(quizSet.quizSetId); startQuiz(quizSet.quizSetId);
closeModal(); closeModal();
}} }}

View File

@ -91,4 +91,5 @@
font-weight: 500; font-weight: 500;
cursor: pointer; cursor: pointer;
transition: background-color 0.25s; transition: background-color 0.25s;
word-break: break-word;
} }

View File

@ -4,7 +4,6 @@ import styles from './QuizsetDetail.module.css';
import { QuizDetailCard } from '../QuizForm'; import { QuizDetailCard } from '../QuizForm';
export default function QuizsetDetail({ topic, title, quizzes = [], onDelete, onEdit, tested = false }) { export default function QuizsetDetail({ topic, title, quizzes = [], onDelete, onEdit, tested = false }) {
console.log('topic', topic, 'title', title, 'quizzes', quizzes);
return ( return (
<div className={styles.quizsetDetail}> <div className={styles.quizsetDetail}>
<header className={styles.header}> <header className={styles.header}>

View File

@ -2,3 +2,4 @@ export const API_URL = import.meta.env.VITE_API_URL;
export const ROOM_URL = import.meta.env.VITE_ROOM_URL; export const ROOM_URL = import.meta.env.VITE_ROOM_URL;
export const CHAT_URL = import.meta.env.VITE_CHAT_URL; export const CHAT_URL = import.meta.env.VITE_CHAT_URL;
export const STATIC_URL = import.meta.env.VITE_STATIC_URL; export const STATIC_URL = import.meta.env.VITE_STATIC_URL;
export const PAGE_SIZE = 20;

View File

@ -40,12 +40,7 @@ export function useAuth() {
const logout = () => { const logout = () => {
return instance return instance
.post(`${API_URL}/user/logout`) .post(`${API_URL}/user/logout`)
.then((response) => { .catch(() => {})
console.log(response);
})
.catch((e) => {
console.log(e);
})
.finally(() => { .finally(() => {
setUserType(null); setUserType(null);
setToken(null); setToken(null);
@ -66,7 +61,6 @@ export function useAuth() {
newPassword: newPw, newPassword: newPw,
newPasswordCheck: newPwCheck, newPasswordCheck: newPwCheck,
}; };
console.log(passwordBody);
return instance.put(`${API_URL}/user/updatepassword`, passwordBody); return instance.put(`${API_URL}/user/updatepassword`, passwordBody);
}; };

View File

@ -1,10 +1,17 @@
import { useSuspenseQuery } from '@tanstack/react-query'; import { useSuspenseInfiniteQuery } from '@tanstack/react-query';
import instance from '../../utils/axios/instance'; import instance from '../../utils/axios/instance';
import { API_URL } from '../../constants'; import { API_URL, PAGE_SIZE } from '../../constants';
export function useFreeboards(lectureId, page = 0) { export function useFreeboards(lectureId, page = 0) {
return useSuspenseQuery({ return useSuspenseInfiniteQuery({
queryKey: ['freeboardlist', lectureId, page], queryKey: ['freeboardlist', lectureId, page],
queryFn: () => instance.get(`${API_URL}/board?lectureId=${lectureId}&category=freeboard&pageNo=${page}`), queryFn: ({ pageParam = 0 }) =>
instance.get(`${API_URL}/board?lectureId=${lectureId}&category=freeboard&pageNo=${pageParam}`),
getNextPageParam: (lastPage, allPages) => {
if (lastPage.data.length < PAGE_SIZE) {
return undefined;
}
return allPages.length + 1;
},
}); });
} }

View File

@ -3,7 +3,6 @@ import { API_URL } from '../../constants';
export function useLectureCreate() { export function useLectureCreate() {
const lectureCreate = (formData) => { const lectureCreate = (formData) => {
// return instance.post(`${API_URL}/lecture`, lectureObject, image);
return instance.post(`${API_URL}/lecture`, formData, { return instance.post(`${API_URL}/lecture`, formData, {
headers: { headers: {
'Content-type': 'multipart/form-data', 'Content-type': 'multipart/form-data',

View File

@ -7,7 +7,7 @@ export function useNoticeEdit() {
title, title,
content, content,
}; };
console.log(newNotice);
return instance.put(`${API_URL}/board/${boardId}`, newNotice); return instance.put(`${API_URL}/board/${boardId}`, newNotice);
}; };

View File

@ -1,10 +1,17 @@
import { useSuspenseQuery } from '@tanstack/react-query'; import { useSuspenseInfiniteQuery } from '@tanstack/react-query';
import instance from '../../utils/axios/instance'; import instance from '../../utils/axios/instance';
import { API_URL } from '../../constants'; import { API_URL, PAGE_SIZE } from '../../constants';
export function useNotices(lectureId, page = 0) { export function useNotices(lectureId) {
return useSuspenseQuery({ return useSuspenseInfiniteQuery({
queryKey: ['noticelist', lectureId, page], queryKey: ['noticelist', lectureId],
queryFn: () => instance.get(`${API_URL}/board?lectureId=${lectureId}&category=announcement&pageNo=${page}`), queryFn: ({ pageParam = 0 }) =>
instance.get(`${API_URL}/board?lectureId=${lectureId}&category=announcement&pageNo=${pageParam}`),
getNextPageParam: (lastPage, allPages) => {
if (lastPage.data.length < PAGE_SIZE) {
return undefined;
}
return allPages.length + 1;
},
}); });
} }

View File

@ -3,7 +3,6 @@ import { API_URL } from '../../constants';
export function usePasswordReset() { export function usePasswordReset() {
const sendEmail = (email) => { const sendEmail = (email) => {
console.log(email);
return instance.post(`${API_URL}/mail/sendcode?email=${email}`); return instance.post(`${API_URL}/mail/sendcode?email=${email}`);
}; };

View File

@ -6,7 +6,6 @@ import { useLectures } from '../../hooks/api/useLectures';
export default function StudentHomePage() { export default function StudentHomePage() {
const { data: allLectures } = useLectures(); const { data: allLectures } = useLectures();
const allClasses = allLectures?.data ?? []; const allClasses = allLectures?.data ?? [];
console.log(allClasses);
return ( return (
<MaxWidthLayout> <MaxWidthLayout>

View File

@ -2,25 +2,28 @@ import { ArticleLink } from '../../components/ArticleLink';
import ArticleBoard from '../../components/ArticleBoard/ArticleBoard'; import ArticleBoard from '../../components/ArticleBoard/ArticleBoard';
import { useFreeboards } from '../../hooks/api/useFreeboards'; import { useFreeboards } from '../../hooks/api/useFreeboards';
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import IntersectionArea from '../../components/IntersectionArea/IntersectionObserver';
export default function NoticeListPage() { export default function NoticeListPage() {
const { lectureId } = useParams(); const { lectureId } = useParams();
const { data } = useFreeboards(lectureId); const { data, fetchNextPage, hasNextPage } = useFreeboards(lectureId);
const notices = data?.data; const articles = data?.pages.flatMap((page) => page.data);
return ( return (
<ArticleBoard <ArticleBoard
title="자유게시판" title="자유게시판"
canCreate={true} canCreate={true}
> >
{notices.map?.((notice) => ( {articles.length &&
<ArticleLink articles.map?.((notice) => (
key={`${notice.id}`} <ArticleLink
title={notice.title} key={`${notice.id}`}
sub={`${new Date(notice.createdAt).toLocaleDateString()} ${new Date(notice.createdAt).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}`} title={notice.title}
to={`${notice.id}`} sub={`${new Date(notice.createdAt).toLocaleDateString()} ${new Date(notice.createdAt).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}`}
/> to={`${notice.id}`}
))} />
))}
{hasNextPage && <IntersectionArea onObserve={() => fetchNextPage()} />}
</ArticleBoard> </ArticleBoard>
); );
} }

View File

@ -7,7 +7,7 @@ import { useQnas } from '../../hooks/api/useQnas';
export default function LearningLectureDetailPage() { export default function LearningLectureDetailPage() {
const { lectureId } = useParams(); const { lectureId } = useParams();
const { data: noticesData } = useNotices(lectureId); const { data: noticesData } = useNotices(lectureId);
const notices = noticesData?.data.slice(0, 3); const notices = noticesData.pages[0]?.data.slice(0, 3);
const { data: qnasData } = useQnas(lectureId); const { data: qnasData } = useQnas(lectureId);
const questions = qnasData?.data.slice(0, 3); const questions = qnasData?.data.slice(0, 3);

View File

@ -10,15 +10,12 @@ export default function LecutreEditPage() {
const navigate = useNavigate(); const navigate = useNavigate();
const { lectureEdit } = useLectureEdit(); const { lectureEdit } = useLectureEdit();
const handleSubmit = async (lectureObject) => { const handleSubmit = async (lectureObject) =>
const response = await lectureEdit(lectureId, lectureObject) await lectureEdit(lectureId, lectureObject)
.then((res) => { .then(() => {
console.log(res);
navigate('..'); navigate('..');
}) })
.catch((err) => console.log(err)); .catch(() => {});
console.log(response?.data);
};
return ( return (
<div> <div>

View File

@ -33,9 +33,7 @@ export default function LectureInfoPage() {
window.alert('강사가 수강신청 수락시 수업이 시작됩니다.'); window.alert('강사가 수강신청 수락시 수업이 시작됩니다.');
navigate('/'); navigate('/');
}) })
.catch((err) => { .catch(() => {});
console.log(err);
});
} }
}; };

View File

@ -16,8 +16,6 @@ export default function MyInfoChangePage() {
await updateInfo(username, useremail) await updateInfo(username, useremail)
.then(() => navigate('/')) .then(() => navigate('/'))
.catch((err) => { .catch((err) => {
console.log(err);
console.log(err.response.data);
if (err.response.data === '이미 사용 중인 이메일입니다.') { if (err.response.data === '이미 사용 중인 이메일입니다.') {
setUsingEmail(true); setUsingEmail(true);
} }

View File

@ -3,11 +3,12 @@ import ArticleBoard from '../../components/ArticleBoard/ArticleBoard';
import { useNotices } from '../../hooks/api/useNotices'; import { useNotices } from '../../hooks/api/useNotices';
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import useBoundStore from '../../store'; import useBoundStore from '../../store';
import IntersectionArea from '../../components/IntersectionArea/IntersectionObserver';
export default function NoticeListPage() { export default function NoticeListPage() {
const { lectureId } = useParams(); const { lectureId } = useParams();
const { data } = useNotices(lectureId); const { data, fetchNextPage, hasNextPage } = useNotices(lectureId);
const notices = data?.data; const notices = data?.pages.flatMap((page) => page.data);
const userType = useBoundStore((state) => state.userType); const userType = useBoundStore((state) => state.userType);
return ( return (
@ -24,6 +25,7 @@ export default function NoticeListPage() {
to={`${notice.id}`} to={`${notice.id}`}
/> />
))} ))}
{hasNextPage && <IntersectionArea onObserve={() => fetchNextPage()} />}
</ArticleBoard> </ArticleBoard>
); );
} }

View File

@ -9,14 +9,11 @@ export default function PasswordChangePage() {
const { updatePassword } = useAuth(); const { updatePassword } = useAuth();
const handleSubmit = async (currentPw, newPw, newPwCheck) => { const handleSubmit = async (currentPw, newPw, newPwCheck) => {
await updatePassword(currentPw, newPw, newPwCheck) await updatePassword(currentPw, newPw, newPwCheck)
.then((res) => { .then(() => {
console.log(res);
navigate('/'); navigate('/');
}) })
.catch((err) => { .catch((err) => {
console.log(err.response.data);
if (err.response.data === 'Current password is incorrect') { if (err.response.data === 'Current password is incorrect') {
console.log('현재 비밀번호 에러');
setPwError(true); setPwError(true);
} }
}); });

View File

@ -21,14 +21,11 @@ export default function PasswordResetPage() {
const handleSubmit = (e) => { const handleSubmit = (e) => {
e.preventDefault(); e.preventDefault();
setAuthError(false); setAuthError(false);
console.log(authNumRef.current.value, email);
verify(authNumRef.current.value, email) verify(authNumRef.current.value, email)
.then((res) => { .then(() => {
console.log(res);
setSentAuthNum(true); setSentAuthNum(true);
}) })
.catch((err) => { .catch((err) => {
console.log(err);
if (err.message === 'Request failed with status code 404') { if (err.message === 'Request failed with status code 404') {
setAuthError(true); setAuthError(true);
} }

View File

@ -1,5 +1,5 @@
import { AuthForm, InputBox } from '../../components/AuthForm'; import { AuthForm, InputBox } from '../../components/AuthForm';
import { useRef, useState, useEffect } from 'react'; import { useRef, useState } from 'react';
import styles from './PasswordResetPage.module.css'; import styles from './PasswordResetPage.module.css';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { usePasswordReset } from '../../hooks/api/usePasswordReset'; import { usePasswordReset } from '../../hooks/api/usePasswordReset';
@ -11,24 +11,14 @@ export default function PasswordResetPage() {
const { sendEmail } = usePasswordReset(); const { sendEmail } = usePasswordReset();
useEffect(() => {
if (emailSent) {
console.log('Updated emailSent:', emailSent);
}
}, [emailSent]);
const handleSubmit = async (e) => { const handleSubmit = async (e) => {
e.preventDefault(); e.preventDefault();
setNotFound(false); setNotFound(false);
await sendEmail(emailRef.current.value) await sendEmail(emailRef.current.value)
.then(() => { .then(() => {
const email = emailRef.current.value; setEmailSent(emailRef.current.value);
console.log(email);
setEmailSent(email);
console.log(emailSent);
}) })
.catch((err) => { .catch((err) => {
console.log(err);
if (err.message === 'Request failed with status code 404') { if (err.message === 'Request failed with status code 404') {
setNotFound(true); setNotFound(true);
} }

View File

@ -10,7 +10,6 @@ export default function QuestionListPage() {
const questions = data?.data; const questions = data?.data;
const userType = useBoundStore((state) => state.userType); const userType = useBoundStore((state) => state.userType);
console.log(questions);
return ( return (
<ArticleBoard <ArticleBoard
title="Q&A" title="Q&A"

View File

@ -10,15 +10,14 @@ export default function QuizsetDetailPage() {
const { data } = useTeacherQuizsetDetail(quizsetId); const { data } = useTeacherQuizsetDetail(quizsetId);
const quizset = data.data; const quizset = data.data;
const tested = quizset.tested; const tested = quizset.tested;
console.log(tested);
const handleEdit = () => { const handleEdit = () => {
navigate('edit', { state: { initialValue: quizset } }); navigate('edit', { state: { initialValue: quizset } });
}; };
const handleDelete = async () => { const handleDelete = async () => {
await quizsetDelete(quizsetId); await quizsetDelete(quizsetId);
navigate('..'); navigate('..');
}; };
return ( return (
<QuizsetDetail <QuizsetDetail
topic={'퀴즈 목록'} topic={'퀴즈 목록'}

View File

@ -9,7 +9,6 @@ export default function QuizsetEditPage() {
const location = useLocation(); const location = useLocation();
const initialValue = location.state.initialValue; const initialValue = location.state.initialValue;
const { quizsetEdit } = useQuizsetEdit(); const { quizsetEdit } = useQuizsetEdit();
console.log(initialValue);
const handleSubmit = async (e, title, quizzes) => { const handleSubmit = async (e, title, quizzes) => {
e.preventDefault(); e.preventDefault();
@ -42,10 +41,6 @@ export default function QuizsetEditPage() {
} }
}); });
formData.forEach((value, key) => {
console.log(`FormData - Key: ${key}, Value:`, value);
});
await quizsetEdit(formData); await quizsetEdit(formData);
navigate('..'); navigate('..');
}; };

View File

@ -2,14 +2,12 @@ import { ArticleLink } from '../../components/ArticleLink';
import ArticleBoard from '../../components/ArticleBoard/ArticleBoard'; import ArticleBoard from '../../components/ArticleBoard/ArticleBoard';
import { useQuizsets } from '../../hooks/api/useQuizsets'; import { useQuizsets } from '../../hooks/api/useQuizsets';
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
// import useBoundStore from '../../store';
export default function QuizsetListPage() { export default function QuizsetListPage() {
const { lectureId } = useParams(); const { lectureId } = useParams();
const { data } = useQuizsets(lectureId); const { data } = useQuizsets(lectureId);
const quizsets = data?.data ?? []; const quizsets = data?.data ?? [];
// const userType = useBoundStore((state) => state.userType);
console.log(quizsets);
return ( return (
<ArticleBoard <ArticleBoard
title="퀴즈 목록" title="퀴즈 목록"

View File

@ -8,7 +8,6 @@ export default function QuizsetWritePage() {
const handleSubmit = async (e, title, quizzes) => { const handleSubmit = async (e, title, quizzes) => {
e.preventDefault(); e.preventDefault();
console.log(quizzes);
if (quizzes.length === 0) { if (quizzes.length === 0) {
window.alert('퀴즈가 없는 퀴즈셋은 생성할 수 없습니다'); window.alert('퀴즈가 없는 퀴즈셋은 생성할 수 없습니다');
return; return;

View File

@ -8,7 +8,6 @@ export default function StudentReportDetailPage() {
const { lectureId, reportId } = useParams(); const { lectureId, reportId } = useParams();
const { data } = useStudentReportDetail(reportId); const { data } = useStudentReportDetail(reportId);
const report = data.data; const report = data.data;
console.log(report);
const { allCount, correctCount, quizzes, title } = report; const { allCount, correctCount, quizzes, title } = report;
const score = Math.round((100 * correctCount) / allCount); const score = Math.round((100 * correctCount) / allCount);
return ( return (

View File

@ -1,5 +1,3 @@
// userType : null, 'teacher', 'student'
export const userTypeSlice = (set) => ({ export const userTypeSlice = (set) => ({
userType: null, userType: null,
setUserType: (userType) => set({ userType }), setUserType: (userType) => set({ userType }),

View File

@ -33,16 +33,12 @@ instance.interceptors.response.use(
.then((response) => { .then((response) => {
const { accessToken } = response.data; const { accessToken } = response.data;
console.log(accessToken);
useBoundStore.setState({ token: accessToken }); useBoundStore.setState({ token: accessToken });
error.config.headers.Authorization = `${accessToken}`; error.config.headers.Authorization = `${accessToken}`;
return instance(error.config); return instance(error.config);
}) })
.catch((error) => { .catch((error) => {
useBoundStore.setState({ token: null, userType: null }); useBoundStore.setState({ token: null, userType: null });
console.log(error);
console.log('---로그아웃----');
// TODO: redirect to home
return Promise.reject(error); return Promise.reject(error);
}); });
} }