-
Notifications
You must be signed in to change notification settings - Fork 2
[FE] SISC1-149 [Feat] 게시판 UI 컴포넌트 구현 #21
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
Changes from all commits
bbfb1f6
3d72ce3
ce7f10b
0c3a3b3
fa72546
ba3d736
8d714df
237edca
e7be1cf
da16802
297d848
d3964d8
c5be2d0
7e022ce
6f99148
24eb51a
f192945
e8196db
1af8a6e
ef59b84
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,34 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import React from 'react'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import styles from './BoardActions.module.css'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import PlusIcon from '../../assets/board_plus.svg'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import DropdownArrowIcon from '../../assets/boardSelectArrow.svg'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const BoardActions = ({ sortOption, onSortChange, onWrite }) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div className={styles.boardActions}> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div className={styles.selectWrapper}> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <select | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| className={styles.sortSelect} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| value={sortOption} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onChange={(e) => onSortChange(e.target.value)} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <option value="latest">최신순</option> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <option value="oldest">오래된순</option> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <option value="popular">인기순</option> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </select> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <img | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| src={DropdownArrowIcon} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| alt="드롭다운" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| className={styles.selectArrow} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+10
to
+24
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 접근성: select 요소에 레이블이 필요합니다. select 요소가 시각적으로 표시되지 않는 레이블 없이 사용되고 있습니다. 스크린 리더 사용자를 위해 <div className={styles.selectWrapper}>
<select
className={styles.sortSelect}
value={sortOption}
onChange={(e) => onSortChange(e.target.value)}
+ aria-label="정렬 옵션"
>
<option value="latest">최신순</option>
<option value="oldest">오래된순</option>
<option value="popular">인기순</option>
</select>📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <button className={styles.writeButton} onClick={onWrite}> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <span>글 작성하기</span> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <img src={PlusIcon} alt="작성" /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </button> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export default BoardActions; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,118 @@ | ||
| :root { | ||
| --color-border: 0.6px solid rgba(0, 0, 0, 1); | ||
| --color-text: rgba(0, 0, 0, 1); | ||
| --color-bg-light: #fafafa; | ||
| --color-primary: #1d80f4; | ||
| --color-button-gradient: linear-gradient( | ||
| 92.89deg, | ||
| #d8e8ff 6.95%, | ||
| #d1d8ff 74.81%, | ||
| #dddeff 107.39% | ||
| ); | ||
| --font-family: | ||
| 'Pretendard', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; | ||
| --font-regular: 400; | ||
| --font-size: 16px; | ||
| --line-height: 100%; | ||
| --letter-spacing: 0%; | ||
| --gap: 12px; | ||
| --radius: 4px; | ||
| --transition: all 0.2s; | ||
| } | ||
|
|
||
| .boardActions { | ||
| width: 100%; | ||
| max-width: calc(100% - 20px); | ||
| padding-right: 20px; | ||
| box-sizing: border-box; | ||
| display: flex; | ||
| align-items: center; | ||
| justify-content: flex-end; | ||
| gap: var(--gap); | ||
| margin-bottom: 32px; | ||
| } | ||
|
|
||
| .selectWrapper { | ||
| position: relative; | ||
| flex-shrink: 0; | ||
| } | ||
|
|
||
| .sortSelect { | ||
| width: 90px; | ||
| height: 35px; | ||
| padding: 8px; | ||
| padding-right: 20px; | ||
| border: var(--color-border); | ||
| border-radius: var(--radius); | ||
| background: #fff; | ||
| cursor: pointer; | ||
| appearance: none; | ||
| -webkit-appearance: none; | ||
| -moz-appearance: none; | ||
| transition: var(--transition); | ||
| box-sizing: border-box; | ||
| font-family: var(--font-family); | ||
| font-weight: var(--font-regular); | ||
| font-size: var(--font-size); | ||
| line-height: var(--line-height); | ||
| letter-spacing: var(--letter-spacing); | ||
| color: var(--color-text); | ||
| } | ||
|
|
||
| .sortSelect:hover { | ||
| background: var(--color-bg-light); | ||
| } | ||
|
|
||
| .sortSelect:focus { | ||
| outline: none; | ||
| border-color: var(--color-primary); | ||
| box-shadow: 0 0 0 2px rgba(29, 128, 244, 0.1); | ||
| } | ||
|
|
||
| .selectArrow { | ||
| position: absolute; | ||
| right: 12px; | ||
| top: 50%; | ||
| transform: translateY(-50%); | ||
| pointer-events: none; | ||
| width: 14px; | ||
| height: 14px; | ||
| } | ||
|
|
||
| .writeButton { | ||
| display: flex; | ||
| align-items: center; | ||
| justify-content: center; | ||
| gap: var(--gap); | ||
| width: 122px; | ||
| height: 35px; | ||
| padding: 8px 12px; | ||
| background: var(--color-button-gradient); | ||
| border: none; | ||
| border-radius: var(--radius); | ||
| cursor: pointer; | ||
| transition: var(--transition); | ||
| white-space: nowrap; | ||
| font-family: var(--font-family); | ||
| font-weight: var(--font-regular); | ||
| font-size: var(--font-size); | ||
| line-height: var(--line-height); | ||
| letter-spacing: var(--letter-spacing); | ||
| color: var(--color-text); | ||
| box-sizing: border-box; | ||
| flex-shrink: 0; | ||
| } | ||
|
|
||
| .writeButton:hover { | ||
| opacity: 0.9; | ||
| box-shadow: 0 2px 8px rgba(29, 128, 244, 0.15); | ||
| } | ||
|
|
||
| .writeButton:active { | ||
| opacity: 0.8; | ||
| } | ||
|
|
||
| .writeButton img { | ||
| width: 9.67px; | ||
| height: 9.67px; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| import styles from './Modal.module.css'; | ||
| import FolderIcon from '../../assets/boardFolder.svg'; | ||
| import CloseIcon from '../../assets/boardCloseIcon.svg'; | ||
| import DropdownArrowIcon from '../../assets/boardSelectArrow.svg'; | ||
|
|
||
| const Modal = ({ title, setTitle, content, setContent, onSave, onClose }) => { | ||
| return ( | ||
| <div className={styles.overlay} onClick={onClose}> | ||
| <div className={styles.modal} onClick={(e) => e.stopPropagation()}> | ||
| <div className={styles.header}> | ||
| <h2 className={styles.title}>게시글 작성</h2> | ||
gxuoo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| <button className={styles.closeButton} onClick={onClose}> | ||
| <img src={CloseIcon} alt="닫기" /> | ||
| </button> | ||
| </div> | ||
|
|
||
| <div className={styles.form}> | ||
| <div className={styles.titleField}> | ||
| <label className={styles.label}>제목</label> | ||
| <input | ||
| className={styles.input} | ||
| type="text" | ||
| placeholder="제목을 입력해주세요." | ||
| value={title} | ||
| onChange={(e) => setTitle(e.target.value)} | ||
| /> | ||
| </div> | ||
|
|
||
| <div className={styles.contentField}> | ||
| <label className={styles.label}>내용</label> | ||
| <div className={styles.contentContainer}> | ||
| <div | ||
| className={styles.fileSection} | ||
| onClick={() => document.getElementById('fileUpload').click()} | ||
| > | ||
| <img src={FolderIcon} alt="폴더" /> | ||
| <span className={styles.fileText}>파일추가</span> | ||
| <input | ||
| type="file" | ||
| id="fileUpload" | ||
| className={styles.fileInput} | ||
| style={{ display: 'none' }} | ||
| /> | ||
| </div> | ||
| <div className={styles.divider}></div> | ||
| <textarea | ||
| className={styles.textarea} | ||
| placeholder="내용을 입력해주세요." | ||
| value={content} | ||
| onChange={(e) => setContent(e.target.value)} | ||
| /> | ||
| </div> | ||
| </div> | ||
|
|
||
| <div className={styles.accessField}> | ||
| <label className={styles.accessLabel}>접근 권한</label> | ||
| <div className={styles.selectWrapper}> | ||
| <select className={styles.select} defaultValue="세션선택"> | ||
| <option value="세션선택">세션선택</option> | ||
| </select> | ||
| <div className={styles.selectIcon}> | ||
| <img src={DropdownArrowIcon} alt="드롭다운 화살표" /> | ||
| </div> | ||
| </div> | ||
| </div> | ||
|
Comment on lines
+55
to
+65
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 접근 권한 선택값이 저장되지 않음 Line 58에서 부모 컴포넌트에 Board.jsx: const [accessControl, setAccessControl] = useState('세션선택');Modal.jsx: -const Modal = ({ title, setTitle, content, setContent, onSave, onClose }) => {
+const Modal = ({ title, setTitle, content, setContent, accessControl, setAccessControl, onSave, onClose }) => {
// ...
- <select className={styles.select} defaultValue="세션선택">
+ <select
+ className={styles.select}
+ value={accessControl}
+ onChange={(e) => setAccessControl(e.target.value)}
+ >🤖 Prompt for AI Agents |
||
| </div> | ||
|
|
||
| <button className={styles.saveButton} onClick={onSave}> | ||
| 저장 | ||
| </button> | ||
| </div> | ||
| </div> | ||
| ); | ||
| }; | ||
|
|
||
| export default Modal; | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
라우트 파라미터와 컴포넌트 구현 간 불일치를 확인하세요.
:postId파라미터가 정의되어 있지만,PostDetail컴포넌트는 이를 사용하지 않고location.state에만 의존합니다. 이는 URL 공유 및 새로고침 시 문제를 발생시킬 수 있습니다.일관성을 위해 URL 파라미터를 활용하거나, 현재 구현의 제약사항을 명확히 문서화하는 것을 권장합니다.
PostDetail.jsx의 리뷰 코멘트도 참고하세요.
🤖 Prompt for AI Agents