-
Notifications
You must be signed in to change notification settings - Fork 39
[김서연] Sprint6 #191
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
The head ref may contain hidden characters: "React-\uAE40\uC11C\uC5F0-sprint6"
[김서연] Sprint6 #191
Changes from all commits
6433a80
be3ab08
883d051
49c954b
234c269
2f97a7a
dfa1b7f
eed0fd5
440aa14
f2250bc
51a3a00
cca641f
a5c6aa9
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,107 @@ | ||||||||||||||||||||||||||||||||
| import { useState } from "react"; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| import ProductImg from "./components/ProductImg"; | ||||||||||||||||||||||||||||||||
| import styles from "./styles/AddItem.module.css"; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| export default function AddItemContent() { | ||||||||||||||||||||||||||||||||
| const [productName, setProductName] = useState(""); | ||||||||||||||||||||||||||||||||
| const [description, setDescription] = useState(""); | ||||||||||||||||||||||||||||||||
| const [price, setPrice] = useState(""); | ||||||||||||||||||||||||||||||||
| const [tagInput, setTagInput] = useState(""); | ||||||||||||||||||||||||||||||||
| const [tags, setTags] = useState([]); | ||||||||||||||||||||||||||||||||
| const [imagePreview, setImagePreview] = useState(null); | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| const isFormValid = | ||||||||||||||||||||||||||||||||
| productName && description && price && tags.length > 0 && imagePreview; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| const handleKeyDown = (e) => { | ||||||||||||||||||||||||||||||||
| if (e.key === "Enter") { | ||||||||||||||||||||||||||||||||
| e.preventDefault(); | ||||||||||||||||||||||||||||||||
| const trimmed = tagInput.trim(); | ||||||||||||||||||||||||||||||||
| if (trimmed && !tags.includes(trimmed)) { | ||||||||||||||||||||||||||||||||
| setTags((prev) => [...prev, trimmed]); | ||||||||||||||||||||||||||||||||
| setTagInput(""); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| const handleDelete = (targetTag) => { | ||||||||||||||||||||||||||||||||
| setTags((prevTags) => prevTags.filter((tag) => tag !== targetTag)); | ||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||||
| <main className={styles.container}> | ||||||||||||||||||||||||||||||||
| <section className={styles.addContainer}> | ||||||||||||||||||||||||||||||||
| <h1 className={styles.addTitle}>상품 등록하기</h1> | ||||||||||||||||||||||||||||||||
| <button disabled={!isFormValid} className={styles.addBtn} type="submit"> | ||||||||||||||||||||||||||||||||
| 등록 | ||||||||||||||||||||||||||||||||
| </button> | ||||||||||||||||||||||||||||||||
|
Comment on lines
+33
to
+38
Collaborator
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. 💊 제안
// case1: form에 onsubmit 이벤트 명시
<form class="login-form" onsubmit="login">
<input /> // input에서 enter 키 입력시 onsubmit 이벤트 실행
<button type="submit">login</button> // 해당 버튼 클릭 시 onsubmit 이벤트 실행
</form>
// case2: submit 버튼에 onclick 이벤트 명시
<form class="login-form">
<input /> // input에서 enter 키 입력시 button의 onclick 이벤트 실행
<button type="submit" onClick="login">login</button>
</form> |
||||||||||||||||||||||||||||||||
| </section> | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| <section className={styles.contentContainer}> | ||||||||||||||||||||||||||||||||
| <h2 className={styles.title}>상품 이미지</h2> | ||||||||||||||||||||||||||||||||
| <ProductImg preview={imagePreview} setPreview={setImagePreview} /> | ||||||||||||||||||||||||||||||||
| </section> | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| <section className={styles.contentContainer}> | ||||||||||||||||||||||||||||||||
| <h2 className={styles.title}>상품명</h2> | ||||||||||||||||||||||||||||||||
| <input | ||||||||||||||||||||||||||||||||
| className={styles.input} | ||||||||||||||||||||||||||||||||
| placeholder="상품명을 입력해주세요" | ||||||||||||||||||||||||||||||||
| value={productName} | ||||||||||||||||||||||||||||||||
| onChange={(e) => setProductName(e.target.value)} | ||||||||||||||||||||||||||||||||
| ></input> | ||||||||||||||||||||||||||||||||
| </section> | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| <section className={styles.contentContainer}> | ||||||||||||||||||||||||||||||||
| <h2 className={styles.title}>상품 소개</h2> | ||||||||||||||||||||||||||||||||
| <textarea | ||||||||||||||||||||||||||||||||
| className={styles.inputDescription} | ||||||||||||||||||||||||||||||||
| placeholder="상품 소개를 입력해주세요" | ||||||||||||||||||||||||||||||||
| value={description} | ||||||||||||||||||||||||||||||||
| onChange={(e) => setDescription(e.target.value)} | ||||||||||||||||||||||||||||||||
| ></textarea> | ||||||||||||||||||||||||||||||||
|
Comment on lines
+57
to
+63
Collaborator
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. 💊 제안
Suggested change
|
||||||||||||||||||||||||||||||||
| </section> | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| <section className={styles.contentContainer}> | ||||||||||||||||||||||||||||||||
| <h2 className={styles.title}>판매가격</h2> | ||||||||||||||||||||||||||||||||
| <input | ||||||||||||||||||||||||||||||||
| className={styles.input} | ||||||||||||||||||||||||||||||||
| placeholder="판매 가격을 입력해주세요" | ||||||||||||||||||||||||||||||||
| value={price} | ||||||||||||||||||||||||||||||||
| onChange={(e) => setPrice(e.target.value)} | ||||||||||||||||||||||||||||||||
| ></input> | ||||||||||||||||||||||||||||||||
| </section> | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| <section className={styles.contentContainer}> | ||||||||||||||||||||||||||||||||
| <h2 className={styles.title}>태그</h2> | ||||||||||||||||||||||||||||||||
| <input | ||||||||||||||||||||||||||||||||
|
Collaborator
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. ❗️ 수정요청 |
||||||||||||||||||||||||||||||||
| className={styles.input} | ||||||||||||||||||||||||||||||||
| placeholder="태그를 입력해주세요" | ||||||||||||||||||||||||||||||||
| value={tagInput} | ||||||||||||||||||||||||||||||||
| onChange={(e) => setTagInput(e.target.value)} | ||||||||||||||||||||||||||||||||
| onKeyDown={handleKeyDown} | ||||||||||||||||||||||||||||||||
| ></input> | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| <div className={styles.tagList}> | ||||||||||||||||||||||||||||||||
| {tags.map((tag, idx) => ( | ||||||||||||||||||||||||||||||||
| <span key={idx} className={styles.tag}> | ||||||||||||||||||||||||||||||||
| #{tag} | ||||||||||||||||||||||||||||||||
| <button | ||||||||||||||||||||||||||||||||
| className={styles.deleteBtn} | ||||||||||||||||||||||||||||||||
| type="button" | ||||||||||||||||||||||||||||||||
| onClick={() => handleDelete(tag)} | ||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||
| <img | ||||||||||||||||||||||||||||||||
| className={styles.deleteImg} | ||||||||||||||||||||||||||||||||
| src="/ic_X.svg" | ||||||||||||||||||||||||||||||||
| alt="등록 이미지 삭제 아이콘" | ||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||
| </button> | ||||||||||||||||||||||||||||||||
| </span> | ||||||||||||||||||||||||||||||||
| ))} | ||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||
| </section> | ||||||||||||||||||||||||||||||||
| </main> | ||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,54 @@ | ||||||
| import { useRef, useState } from "react"; | ||||||
| import styles from "./styles/ProductImg.module.css"; | ||||||
|
|
||||||
| export default function ProductImg({ preview, setPreview }) { | ||||||
| const inputRef = useRef(null); | ||||||
|
Collaborator
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. 💊 제안 |
||||||
|
|
||||||
| const handleImageChange = (e) => { | ||||||
| const file = e.target.files?.[0]; | ||||||
|
|
||||||
| if (preview) { | ||||||
| alert("이미지는 한 개만 업로드할 수 있습니다."); | ||||||
| return; | ||||||
| } | ||||||
|
|
||||||
| if (file) { | ||||||
| const reader = new FileReader(); | ||||||
| reader.onloadend = () => { | ||||||
| setPreview(reader.result); | ||||||
| }; | ||||||
| reader.readAsDataURL(file); | ||||||
| } | ||||||
| }; | ||||||
|
|
||||||
| const handleDelete = () => { | ||||||
| setPreview(null); | ||||||
| }; | ||||||
|
|
||||||
| return ( | ||||||
| <div className={styles.wrapper}> | ||||||
| <label className={styles.uploadBox}> | ||||||
| <div className={styles.textContainer}> | ||||||
| <span className={styles.plus}>+</span> | ||||||
| <p className={styles.text}>이미지 등록</p> | ||||||
| </div> | ||||||
| <input | ||||||
| type="file" | ||||||
| accept="image/*" | ||||||
|
Collaborator
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. 💊 제안 (사용자가 업로드창에서 옵션을 열어 확장자를 바꾸면 아래처럼 보입니다) https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/accept |
||||||
| ref={inputRef} | ||||||
| onChange={handleImageChange} | ||||||
| style={{ display: "none" }} | ||||||
| /> | ||||||
| </label> | ||||||
|
|
||||||
| {preview && ( | ||||||
| <div className={styles.imagePreviewBox}> | ||||||
| <button className={styles.deleteButton} onClick={handleDelete}> | ||||||
|
Collaborator
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. ❗️ 수정요청
Suggested change
|
||||||
| <img src="/ic_X.svg" alt="등록 이미지 삭제 아이콘" /> | ||||||
| </button> | ||||||
| <img src={preview} alt="미리보기" className={styles.previewImg} /> | ||||||
| </div> | ||||||
| )} | ||||||
| </div> | ||||||
| ); | ||||||
| } | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| .wrapper { | ||
| display: flex; | ||
| gap: 2.4rem; | ||
| } | ||
|
|
||
| .uploadBox { | ||
| width: 28.2rem; | ||
| height: 28.2rem; | ||
| background-color: var(--gray100); | ||
| border-radius: 1.2rem; | ||
|
|
||
| display: flex; | ||
| flex-direction: column; | ||
| align-items: center; | ||
| justify-content: center; | ||
|
|
||
| cursor: pointer; | ||
| overflow: hidden; | ||
|
|
||
| flex-shrink: 0; | ||
| } | ||
|
|
||
| .textContainer { | ||
| display: inline-flex; | ||
| flex-direction: column; | ||
| align-items: center; | ||
| } | ||
|
|
||
| .plus { | ||
| font-size: 5rem; | ||
| font-weight: 100; | ||
| color: var(--gray400); | ||
| } | ||
|
|
||
| .text { | ||
| font-size: 1rem; | ||
| color: var(--gray400); | ||
| font-weight: 400; | ||
| } | ||
|
|
||
| .imagePreviewBox { | ||
| width: 28.2rem; | ||
| height: 28.2rem; | ||
| border-radius: 1.2rem; | ||
| overflow: hidden; | ||
| position: relative; | ||
|
|
||
| flex-shrink: 0; | ||
| } | ||
|
|
||
| .previewImg { | ||
| width: 100%; | ||
| height: 100%; | ||
| object-fit: cover; | ||
| display: block; | ||
| } | ||
|
|
||
| .deleteButton { | ||
| position: absolute; | ||
| top: 8px; | ||
| right: 15px; | ||
|
|
||
| width: 20px; | ||
| height: 20px; | ||
| } | ||
|
|
||
| @media (max-width: 768px) { | ||
| .uploadBox { | ||
| width: 16.8rem; | ||
| height: 16.8rem; | ||
| } | ||
|
|
||
| .imagePreviewBox { | ||
| width: 16.8rem; | ||
| height: 16.8rem; | ||
| } | ||
| } |

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.
💊 제안
중복되는 태그가 생성되지 않도록 해주신 점 좋습니다!
다만 이렇게 되면 사용자가 해당 동작에 대한 피드백을 받지 못하므로, alert, toast, input error 와 같은 방식으로 피드백을 주시면 더 좋을 것 같아요.