Merge branch 'fe/adjustDesign' into 'frontend'

[Front-End] design: 게시판 디자인 수정

See merge request s11-webmobile1-sub2/S11P12A701!116
This commit is contained in:
조민우 2024-08-08 17:37:15 +09:00
commit 0d8b15a915
26 changed files with 173 additions and 157 deletions

View File

@ -5,7 +5,6 @@ import HomePage from './pages/HomePage';
import NotFoundPage from './pages/NotFoundPage'; import NotFoundPage from './pages/NotFoundPage';
import { lazy, Suspense } from 'react'; import { lazy, Suspense } from 'react';
import MyPageLayout from './components/Layout/MyPageLayout'; import MyPageLayout from './components/Layout/MyPageLayout';
// import LivePage from './pages/LivePage';
import ErrorPage from './pages/ErrorPage'; import ErrorPage from './pages/ErrorPage';
import { LectureLayout } from './components/Layout'; import { LectureLayout } from './components/Layout';

View File

@ -75,9 +75,8 @@ export default function ArticleDetail({
)} )}
</div> </div>
</header> </header>
<div> <p className={styles.content}>{content}</p>
<p className={styles.content}>{content}</p> <div></div>
</div>
{/* TODO: 이 부분에서 answer 만든다음 뒤로가기로 나갔다가 돌아오면 0.1초 정도 input 칸이 보였다가 answer 로 바뀜. 수정필요 */} {/* TODO: 이 부분에서 answer 만든다음 뒤로가기로 나갔다가 돌아오면 0.1초 정도 input 칸이 보였다가 answer 로 바뀜. 수정필요 */}
{isQna && {isQna &&
(submittedAnswer && !isEditing ? ( (submittedAnswer && !isEditing ? (

View File

@ -53,7 +53,7 @@
line-height: 1.4; line-height: 1.4;
font-weight: 400; font-weight: 400;
margin: 0; margin: 0;
white-space: pre-line; white-space: pre-wrap;
} }
.icon { .icon {
@ -61,6 +61,7 @@
} }
.actionGroup { .actionGroup {
flex-shrink: 0;
display: flex; display: flex;
align-items: end; align-items: end;
gap: 20px; gap: 20px;

View File

@ -27,6 +27,7 @@
} }
.actionGroup { .actionGroup {
flex-shrink: 0;
display: flex; display: flex;
gap: 12px; gap: 12px;
} }

View File

@ -3,6 +3,8 @@ import { useAnswerWrite } from '../../../../hooks/api/useAnswerWrite';
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import { useState } from 'react'; import { useState } from 'react';
import SendIcon from '/src/assets/icons/send.svg?react';
export default function ArticleDetailAnswerInput({ onSubmit, initialAnswer }) { export default function ArticleDetailAnswerInput({ onSubmit, initialAnswer }) {
const { answerWrite } = useAnswerWrite(); const { answerWrite } = useAnswerWrite();
const { questionId } = useParams(); const { questionId } = useParams();
@ -19,19 +21,19 @@ export default function ArticleDetailAnswerInput({ onSubmit, initialAnswer }) {
onSubmit={handleSubmit} onSubmit={handleSubmit}
className={styles.answer} className={styles.answer}
> >
{/* TODO: 여기 css 부분은 내가 임의로 넣었음 */}
<input <input
type="text" type="text"
maxLength={255}
value={newAnswer} value={newAnswer}
onChange={(e) => setNewAnswer(e.target.value)} onChange={(e) => setNewAnswer(e.target.value)}
placeholder="답변 작성" placeholder="답변 작성하기"
className={styles.input} className={styles.input}
/> />
<button <button
type="submit" type="submit"
className={styles.button} className={styles.button}
> >
작성 <SendIcon />
</button> </button>
</form> </form>
); );

View File

@ -1,6 +1,6 @@
.answer { .answer {
border: 1px solid #ccc; border: 1px solid var(--border-color);
padding: 16px; padding: 8px;
border-radius: 8px; border-radius: 8px;
display: flex; display: flex;
align-items: center; align-items: center;
@ -17,14 +17,13 @@
} }
.button { .button {
padding: 8px 16px; display: flex;
font-size: 16px; justify-content: center;
line-height: 1.4; align-items: center;
font-weight: 700; padding: 12px 16px;
background-color: var(--primary-color); background-color: var(--primary-color);
color: var(--on-primary);
stroke: var(--on-primary); stroke: var(--on-primary);
border: 1px solid var(--primary-color); border: none;
border-radius: 8px; border-radius: 8px;
cursor: pointer; cursor: pointer;
} }

View File

@ -34,6 +34,7 @@ export default function CreateArticle({ topic, title, onSubmit }) {
<label className={styles.label}>제목</label> <label className={styles.label}>제목</label>
<input <input
type="text" type="text"
maxLength={255}
className={styles.titleInput} className={styles.titleInput}
placeholder="제목을 입력하세요" placeholder="제목을 입력하세요"
value={articleTitle} value={articleTitle}

View File

@ -48,6 +48,7 @@ export default function EditArticle({ topic, title, prevTitle, prevContent, onSu
<label className={styles.label}>제목</label> <label className={styles.label}>제목</label>
<input <input
type="text" type="text"
maxLength={255}
className={styles.titleInput} className={styles.titleInput}
placeholder="제목을 입력하세요" placeholder="제목을 입력하세요"
value={articleTitle} value={articleTitle}

View File

@ -34,6 +34,7 @@ export default function EditFreeboard({ topic, title, prevContent, prevTitle, on
<label className={styles.label}>제목</label> <label className={styles.label}>제목</label>
<input <input
type="text" type="text"
maxLength={255}
className={styles.titleInput} className={styles.titleInput}
placeholder={'제목을 입력하세요'} placeholder={'제목을 입력하세요'}
value={articleTitle} value={articleTitle}

View File

@ -34,6 +34,7 @@ export default function EditQna({ topic, title, prevContent, prevTitle, onSubmit
<label className={styles.label}>제목</label> <label className={styles.label}>제목</label>
<input <input
type="text" type="text"
maxLength={255}
className={styles.titleInput} className={styles.titleInput}
placeholder={'제목을 입력하세요'} placeholder={'제목을 입력하세요'}
value={articleTitle} value={articleTitle}

View File

@ -4,7 +4,9 @@ import { useCommentDelete } from '../../../../hooks/api/useCommentDelete';
import { useState } from 'react'; import { useState } from 'react';
import { useCommentEdit } from '../../../../hooks/api/useCommentEdit'; import { useCommentEdit } from '../../../../hooks/api/useCommentEdit';
export default function FreeboardComment({ content, author, onDeleteSubmit, onEditSubmit, commentId }) { import SendIcon from '/src/assets/icons/send.svg?react';
export default function FreeboardComment({ content, author, onDeleteSubmit, onEditSubmit, commentId, isMine }) {
const [isEditing, setIsEditing] = useState(false); const [isEditing, setIsEditing] = useState(false);
const { commentDelete } = useCommentDelete(); const { commentDelete } = useCommentDelete();
const { commentEdit } = useCommentEdit(); const { commentEdit } = useCommentEdit();
@ -40,7 +42,7 @@ export default function FreeboardComment({ content, author, onDeleteSubmit, onEd
type="text" type="text"
value={newComment} value={newComment}
onChange={(e) => setNewComment(e.target.value)} onChange={(e) => setNewComment(e.target.value)}
placeholder="답변 작성" placeholder="댓글 수정하기"
className={styles.input} className={styles.input}
required required
/> />
@ -48,30 +50,38 @@ export default function FreeboardComment({ content, author, onDeleteSubmit, onEd
type="submit" type="submit"
className={styles.button} className={styles.button}
> >
작성 <SendIcon />
</button> </button>
</form> </form>
) : ( ) : (
<section className={styles.comment}> <section className={styles.comment}>
<div className={styles.commentHeader}> <div className={styles.commentHeader}>
<ReplyIcon /> <div className={styles.title}>
<div className={styles.author}>{author} 답변</div> <ReplyIcon />
<div className={styles.author}>{author}</div>
</div>
{isMine && (
<div className={styles.actionGroup}>
<button
type="button"
className={styles.edit}
to={'edit'}
onClick={onEditClick}
>
수정
</button>
<button
type="button"
className={styles.delete}
onClick={handleDeleteSubmit}
>
삭제
</button>
</div>
)}
</div> </div>
<p className={styles.content}>{content}</p> <p className={styles.content}>{content}</p>
<button
type="button"
className={styles.deleteButton}
onClick={handleDeleteSubmit}
>
<div>삭제</div>
</button>
<button
type="button"
className={styles.editButton}
onClick={onEditClick}
>
수정
</button>
</section> </section>
)} )}
</> </>

View File

@ -10,8 +10,8 @@
} }
.commentEdit { .commentEdit {
border: 1px solid #ccc; border: 1px solid var(--border-color);
padding: 16px; padding: 8px;
border-radius: 8px; border-radius: 8px;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
@ -20,6 +20,11 @@
} }
.commentHeader { .commentHeader {
display: flex;
justify-content: space-between;
}
.title {
display: flex; display: flex;
gap: 4px; gap: 4px;
color: var(--text-color-secondary); color: var(--text-color-secondary);
@ -40,40 +45,6 @@
color: var(--text-color); color: var(--text-color);
} }
.editButton {
display: flex;
align-items: center;
gap: 8px;
padding: 12px 16px;
border: 1px solid var(--primary-color);
background-color: var(--primary-color);
color: var(--on-primary);
stroke: var(--on-primary);
font-size: 16px;
line-height: 1.4;
font-weight: 700;
align-self: end;
border-radius: 8px;
cursor: pointer;
}
.deleteButton {
display: flex;
align-items: center;
gap: 8px;
padding: 12px 16px;
border: 1px solid var(--error-color);
background-color: var(--error-color);
color: var(--on-primary);
stroke: var(--on-primary);
font-size: 16px;
line-height: 1.4;
font-weight: 700;
align-self: end;
border-radius: 8px;
cursor: pointer;
}
.input { .input {
flex: 1; flex: 1;
border: none; border: none;
@ -84,14 +55,39 @@
} }
.button { .button {
padding: 8px 16px; display: flex;
font-size: 16px; justify-content: center;
line-height: 1.4; align-items: center;
font-weight: 700; padding: 12px 16px;
background-color: var(--primary-color); background-color: var(--primary-color);
color: var(--on-primary);
stroke: var(--on-primary); stroke: var(--on-primary);
border: 1px solid var(--primary-color); border: none;
border-radius: 8px; border-radius: 8px;
cursor: pointer; cursor: pointer;
} }
.actionGroup {
flex-shrink: 0;
display: flex;
align-items: end;
gap: 16px;
color: var(--text-color);
margin-right: 8px;
}
.edit,
.delete {
padding: 0;
margin: 0;
border: none;
background-color: var(--background);
color: var(--text-color-tertiary);
font-size: 14px;
line-height: 1.4;
font-weight: 500;
cursor: pointer;
}
.delete {
color: var(--error-color);
}

View File

@ -1,5 +1,6 @@
import styles from './FreeboardCommentInput.module.css'; import styles from './FreeboardCommentInput.module.css';
import { useState } from 'react'; import { useState } from 'react';
import SendIcon from '/src/assets/icons/send.svg?react';
export default function FreeboardCommentInput({ onCommentSubmit }) { export default function FreeboardCommentInput({ onCommentSubmit }) {
const [newComment, setNewComment] = useState(''); const [newComment, setNewComment] = useState('');
@ -19,7 +20,7 @@ export default function FreeboardCommentInput({ onCommentSubmit }) {
type="text" type="text"
value={newComment} value={newComment}
onChange={(e) => setNewComment(e.target.value)} onChange={(e) => setNewComment(e.target.value)}
placeholder="답변 작성" placeholder="댓글 작성하기"
className={styles.input} className={styles.input}
required required
/> />
@ -27,7 +28,7 @@ export default function FreeboardCommentInput({ onCommentSubmit }) {
type="submit" type="submit"
className={styles.button} className={styles.button}
> >
작성 <SendIcon />
</button> </button>
</form> </form>
); );

View File

@ -1,6 +1,6 @@
.comment { .comment {
border: 1px solid #ccc; border: 1px solid var(--border-color);
padding: 16px; padding: 8px;
border-radius: 8px; border-radius: 8px;
display: flex; display: flex;
align-items: center; align-items: center;
@ -17,14 +17,12 @@
} }
.button { .button {
padding: 8px 16px; display: flex;
font-size: 16px; align-items: center;
line-height: 1.4; padding: 12px 16px;
font-weight: 700;
background-color: var(--primary-color); background-color: var(--primary-color);
color: var(--on-primary);
stroke: var(--on-primary); stroke: var(--on-primary);
border: 1px solid var(--primary-color); border: none;
border-radius: 8px; border-radius: 8px;
cursor: pointer; cursor: pointer;
} }

View File

@ -5,10 +5,9 @@ import FreeboardCommentInput from './FreeDetailComments/FreeboardCommentInput';
import FreeboardComment from './FreeDetailComments/FreeboardComment'; import FreeboardComment from './FreeDetailComments/FreeboardComment';
import { useComments } from '../../../hooks/api/useComments'; import { useComments } from '../../../hooks/api/useComments';
import { useCommentWrite } from '../../../hooks/api/useCommentWrite'; import { useCommentWrite } from '../../../hooks/api/useCommentWrite';
import EditIcon from '/src/assets/icons/edit.svg?react';
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
export default function FreeboardDetail({ topic, title, author, content, onDelete }) { export default function FreeboardDetail({ topic, title, author, content, onDelete, isMine }) {
const { freeboardId } = useParams(); const { freeboardId } = useParams();
const { data, refetch } = useComments(freeboardId); const { data, refetch } = useComments(freeboardId);
const { commentWrite } = useCommentWrite(); const { commentWrite } = useCommentWrite();
@ -19,14 +18,6 @@ export default function FreeboardDetail({ topic, title, author, content, onDelet
refetch(); refetch();
}; };
const handleDeleteSubmit = () => {
refetch();
};
const handleEditSubmit = () => {
refetch();
};
return ( return (
<div className={styles.freeboardDetail}> <div className={styles.freeboardDetail}>
<header className={styles.header}> <header className={styles.header}>
@ -43,22 +34,24 @@ export default function FreeboardDetail({ topic, title, author, content, onDelet
{author && <span className={styles.author}>{author}</span>} {author && <span className={styles.author}>{author}</span>}
</div> </div>
</div> </div>
<Link {isMine && (
type="button" <div className={styles.actionGroup}>
className={styles.editButton} <Link
to={'edit'} to="edit"
state={{ title: title, content: content }} className={styles.edit}
> state={{ title: title, content: content }}
<EditIcon className={styles.icon} /> >
<span>수정하기</span> 수정
</Link> </Link>
<button <button
type="button" type="button"
className={styles.deleteButton} className={styles.delete}
onClick={onDelete} onClick={onDelete}
> >
삭제하기 <div>삭제</div>
</button> </button>
</div>
)}
</header> </header>
<div> <div>
<p className={styles.content}>{content}</p> <p className={styles.content}>{content}</p>
@ -70,8 +63,9 @@ export default function FreeboardDetail({ topic, title, author, content, onDelet
content={comment.content} content={comment.content}
author={comment.name} author={comment.name}
commentId={comment.id} commentId={comment.id}
onDeleteSubmit={handleDeleteSubmit} isMine={comment.mine}
onEditSubmit={handleEditSubmit} onDeleteSubmit={refetch}
onEditSubmit={refetch}
/> />
))} ))}
<FreeboardCommentInput onCommentSubmit={handleCommentSubmit} /> <FreeboardCommentInput onCommentSubmit={handleCommentSubmit} />

View File

@ -13,7 +13,7 @@
.header { .header {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: start; align-items: end;
} }
.headerInside { .headerInside {
@ -91,3 +91,26 @@
border-radius: 8px; border-radius: 8px;
cursor: pointer; cursor: pointer;
} }
.actionGroup {
flex-shrink: 0;
display: flex;
gap: 12px;
}
.edit,
.delete {
padding: 0;
margin: 0;
border: none;
background-color: var(--background);
font-size: 14px;
line-height: 1.4;
font-weight: 500;
color: var(--text-color-tertiary);
cursor: pointer;
}
.delete {
color: var(--error-color);
}

View File

@ -17,7 +17,6 @@ export default function LectureLayout() {
const { lectureDelete } = useLectureDelete(); const { lectureDelete } = useLectureDelete();
const { data } = useLectureInfo(lectureId); const { data } = useLectureInfo(lectureId);
const lecture = data?.data; const lecture = data?.data;
console.log(lecture);
const userType = useBoundStore((state) => state.userType); const userType = useBoundStore((state) => state.userType);
const handleDelete = () => { const handleDelete = () => {
confirm('강의를 삭제할까요??') && confirm('강의를 삭제할까요??') &&

View File

@ -57,6 +57,7 @@ export default function QuizsetForm({ headerTitle, topic, to, onSubmit, initialV
<input <input
className={styles.input} className={styles.input}
type="text" type="text"
maxLength={255}
value={title} value={title}
onChange={(e) => setTitle(e.target.value)} onChange={(e) => setTitle(e.target.value)}
placeholder="퀴즈셋 제목을 입력해주세요" placeholder="퀴즈셋 제목을 입력해주세요"

View File

@ -19,9 +19,9 @@ export default function QuizSet({ quizSetId, reportSetId, finish }) {
const requestData = { const requestData = {
answer: data, answer: data,
}; };
instance.post(`${API_URL}/report/submit/${reportSetId}/quizset/${quizSetId}`, requestData).catch(() => {}); instance.post(`${API_URL}/report/submit/quizSet/${reportSetId}`, requestData).catch(() => {});
}, },
[quizSetId, reportSetId] [reportSetId]
); );
const QuizComponents = [ const QuizComponents = [
...quizList.map((quiz, index) => ( ...quizList.map((quiz, index) => (

View File

@ -15,6 +15,7 @@ export default function FreeboardDetailPage() {
await freeboardDelete(freeboardId); await freeboardDelete(freeboardId);
navigate('..'); navigate('..');
}; };
return ( return (
<FreeboardDetail <FreeboardDetail
topic="자유게시판" topic="자유게시판"
@ -22,6 +23,7 @@ export default function FreeboardDetailPage() {
author={freeboard.name} author={freeboard.name}
content={freeboard.content} content={freeboard.content}
onDelete={handleDelete} onDelete={handleDelete}
isMine={freeboard.mine}
/> />
); );
} }

View File

@ -7,7 +7,7 @@ export default function NoticeListPage() {
const { lectureId } = useParams(); const { lectureId } = useParams();
const { data } = useFreeboards(lectureId); const { data } = useFreeboards(lectureId);
const notices = data?.data; const notices = data?.data;
console.log(notices);
return ( return (
<ArticleBoard <ArticleBoard
title="자유게시판" title="자유게시판"

View File

@ -47,7 +47,7 @@ export default function LectureInfoPage() {
tutor={lectureData.teacherName} tutor={lectureData.teacherName}
tutorImg={lectureData.tutorImg} tutorImg={lectureData.tutorImg}
/> />
<MaxWidthLayout hasSideBar> <div className={styles.wrapper}>
<main> <main>
<div className={styles.group}> <div className={styles.group}>
<h2>수업소개</h2> <h2>수업소개</h2>
@ -66,7 +66,7 @@ export default function LectureInfoPage() {
status={status} status={status}
/> />
</aside> </aside>
</MaxWidthLayout> </div>
</> </>
); );
} }

View File

@ -1,3 +1,25 @@
.wrapper {
display: flex;
gap: 20px;
width: 100%;
max-width: 1320px;
padding: 0 40px;
margin: 0 auto;
box-sizing: border-box;
& > main {
width: 100%;
}
& > aside {
flex-shrink: 0;
display: flex;
flex-direction: column;
gap: 32px;
width: 320px;
}
}
.group { .group {
display: flex; display: flex;
flex-direction: column; flex-direction: column;

View File

@ -1,33 +0,0 @@
import { ArticleLink } from '../../components/ArticleLink';
import ArticleBoard from '../../components/ArticleBoard/ArticleBoard';
export default function TeacherNoticeListPage() {
const { data: notices } = {
data: [
{ 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: '제목만 있는 경우' },
],
};
return (
<ArticleBoard
title="공지사항"
canCreate={true}
>
{notices.length &&
notices.map?.((notice) => {
if (notice.sub && notice.title) {
return (
<ArticleLink
key={`${notice.title}${notice.sub}`}
title={notice.title}
sub={notice.sub}
/>
);
}
})}
</ArticleBoard>
);
}

View File

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

View File

@ -12,7 +12,6 @@ const instance = axios.create({
instance.interceptors.request.use((config) => { instance.interceptors.request.use((config) => {
const accessToken = useBoundStore.getState().token; const accessToken = useBoundStore.getState().token;
console.log(accessToken);
if (accessToken) { if (accessToken) {
config.headers.Authorization = `${accessToken}`; config.headers.Authorization = `${accessToken}`;
} }