Skip to content

Commit

Permalink
[나지원] sprint7 (#102)
Browse files Browse the repository at this point in the history
* feat: add router link to product in ProductList

* feat: implement ProductDetail component

- Separated Tags component as a shared component
- Created HeartButton component

* feat: implement Inquiry component

* feat: implement Comments component

- Separated AuthorInfo component into the Product feature

* feat: enable edit input field on comment edit button click

* feat: add relative titme caculation function

* feat: disable button based on textarea content during comment edit

* fix: update accept attribute to separate multiple value with comma

* chore: remove comments

---------

Co-authored-by: naji <jitwona99.gmail.com>
  • Loading branch information
najitwo authored Sep 23, 2024
1 parent 2a79307 commit 62ee2bd
Show file tree
Hide file tree
Showing 42 changed files with 968 additions and 83 deletions.
4 changes: 4 additions & 0 deletions src/assets/images/ic_back.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions src/assets/images/ic_kebab.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
17 changes: 17 additions & 0 deletions src/assets/images/img_inquiry_empty.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 6 additions & 4 deletions src/components/Button.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import styles from "./Button.module.css";

const Button = ({ className, isDisabled, children }) => {
const Button = ({ type, className = "", disabled, children }) => {
const handleClick = (event) => {
event.preventDefault();
if (type === "submit") {
event.preventDefault();
}
};

return (
<button
type="submit"
type={type}
className={`${styles.button} ${className}`}
onClick={handleClick}
disabled={isDisabled}
disabled={disabled}
>
{children}
</button>
Expand Down
1 change: 1 addition & 0 deletions src/components/Button.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
border-radius: 8px;
font-size: 1rem;
font-weight: 600;
line-height: 1.625rem;
color: #ffffff;
background-color: var(--blue);
cursor: pointer;
Expand Down
23 changes: 12 additions & 11 deletions src/components/Card.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
import { Link } from "react-router-dom";
import styles from "./Card.module.css";
import heartImg from "assets/images/ic_heart.svg";
import { ReactComponent as HeartIcon } from "assets/images/ic_heart.svg";

const Card = ({ product }) => {
return (
<li className={styles.card}>
<img
src={product.images}
alt={product.name}
className={styles.productImg}
/>
<h3 className={styles.name}>{product.name}</h3>
<p className={styles.price}>
{`${product.price.toLocaleString("ko-KR")}`}
</p>
<Link to={`/items/${product.id}`}>
<img
src={product.images}
alt={product.name}
className={styles.productImg}
/>
<h3 className={styles.name}>{product.name}</h3>
<p className={styles.price}>{product.price.toLocaleString()}</p>
</Link>
<span className={styles.favorite}>
<img src={heartImg} alt="하트" className={styles.heart} />
<HeartIcon />
{product.favoriteCount}
</span>
</li>
Expand Down
5 changes: 0 additions & 5 deletions src/components/Card.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,6 @@
color: var(--gray600);
}

.heart {
width: 16px;
height: 16px;
}

@media screen and (min-width: 1200px) {
.productImg {
margin-bottom: 10px;
Expand Down
13 changes: 13 additions & 0 deletions src/components/HeartButton.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { ReactComponent as HeartIcon } from "assets/images/ic_heart.svg";
import styles from "./HeartButton.module.css";

const HeartButton = ({ favoriteCount }) => {
return (
<button aria-label="좋아요 버튼" className={styles.heartButton}>
<HeartIcon className={styles.heartIcon} />
<span>{favoriteCount}</span>
</button>
);
};

export default HeartButton;
30 changes: 30 additions & 0 deletions src/components/HeartButton.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
.heartButton {
display: flex;
align-items: center;
gap: 4px;
padding: 4px 12px 2px;
border-radius: 35px;
border: 1px solid var(--gray200);
font-size: 1rem;
font-weight: 500;
line-height: 1.625rem;
color: var(--gray500);
cursor: pointer;
}

.heartButton:hover path {
fill: var(--red);
stroke: var(--red);
}

.heartIcon {
width: 24px;
height: 24px;
}

@media screen and (min-width: 1200px) {
.heartIcon {
width: 32px;
height: 32px;
}
}
6 changes: 3 additions & 3 deletions src/components/Input.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import styles from "./Input.module.css";

const Input = ({ name, label, placeholder, onChange, onKeyDown }) => {
const Input = ({ type, name, label, placeholder, onChange, onKeyUp }) => {
return (
<div className={styles.container}>
<label className={styles.label} htmlFor={name}>
{label}
</label>
<input
className={styles.input}
type="text"
type={type}
name={name}
id={name}
placeholder={placeholder}
onChange={onChange}
onKeyDown={onKeyDown}
onKeyUp={onKeyUp}
/>
</div>
);
Expand Down
30 changes: 30 additions & 0 deletions src/components/Tags.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import XButton from "./XButton";
import styles from "./Tags.module.css";

const Tags = ({ tags, onRemove, className = "" }) => {
return (
<>
{tags.length ? (
<ul className={`${styles.tags} ${className}`}>
{tags.map((tag) => {
return (
<li key={tag} className={styles.tag}>
<span>#{tag}</span>
{onRemove && (
<XButton
className={styles.icon}
onClick={(event) => onRemove(event, tag)}
/>
)}
</li>
);
})}
</ul>
) : (
""
)}
</>
);
};

export default Tags;
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
display: flex;
gap: 12px;
flex-wrap: wrap;
margin-top: -10px;
}

.tag {
Expand All @@ -11,7 +10,7 @@
gap: 8px;
border-radius: 26px;
background-color: var(--gray100);
padding: 5px 12px 5px 16px;
padding: 5px 16px;
font-size: 1rem;
font-weight: 400;
line-height: 1.625rem;
Expand Down
15 changes: 12 additions & 3 deletions src/components/Textarea.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
import styles from "./Textarea.module.css";

const Textarea = ({ name, label, placeholder, onChange }) => {
const Textarea = ({
name,
label = "",
className = "",
placeholder = "",
value = "",
onChange,
}) => {
return (
<div className={styles.container}>
<div className={`${styles.container} ${className}`}>
<label className={styles.label} htmlFor={name}>
{label}
</label>
Expand All @@ -12,7 +19,9 @@ const Textarea = ({ name, label, placeholder, onChange }) => {
id={name}
placeholder={placeholder}
onChange={onChange}
></textarea>
>
{value}
</textarea>
</div>
);
};
Expand Down
1 change: 0 additions & 1 deletion src/components/Textarea.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
.label {
font-size: 1.125rem;
font-weight: 700;
line-height: 1.625rem;
}

.textarea {
Expand Down
4 changes: 2 additions & 2 deletions src/components/XButton.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import styles from "./XButton.module.css";
import { ReactComponent as XIcon } from "assets/images/ic_x.svg";

const XButton = ({ onClick, className }) => {
const XButton = ({ onClick, className = "" }) => {
return (
<button aria-label="닫기 아이콘" onClick={onClick}>
<button aria-label="닫기 버튼" onClick={onClick}>
<XIcon className={`${styles.icon} ${className}`} />
</button>
);
Expand Down
1 change: 1 addition & 0 deletions src/components/XButton.module.css
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.icon {
margin-right: -4px;
cursor: pointer;
}

Expand Down
1 change: 1 addition & 0 deletions src/constants/api.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const BASE_URL = "https://panda-market-api.vercel.app/products";
2 changes: 2 additions & 0 deletions src/constants/message.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const INQUIRY_PLACEHOLDER =
"개인정보를 공유 및 요청하거나, 명예 훼손, 무단 광고, 불법 정보 유포시 모니터링 후 삭제될 수 있으며, 이에 대한 민형사상 책임은 게시자에게 있습니다.";
23 changes: 13 additions & 10 deletions src/features/AddProduct/components/AddProductForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import FileInput from "./FileInput";
import Input from "components/Input";
import Textarea from "components/Textarea";
import Button from "components/Button";
import Tags from "components/Tags";
import styles from "./AddProductForm.module.css";
import Tags from "./Tags";

const INITIAL_VALUES = {
imgFile: null,
Expand All @@ -14,7 +14,7 @@ const INITIAL_VALUES = {
tags: [],
};

const AddProudctForm = () => {
const AddProuductForm = () => {
const [isDisabled, setIsDisabled] = useState(true);
const [values, setValues] = useState(INITIAL_VALUES);

Expand Down Expand Up @@ -42,7 +42,7 @@ const AddProudctForm = () => {
});
};

const handleKeyDown = (e) => {
const handleKeyUp = (e) => {
if (e.key === "Enter") {
const { name, value } = e.target;
e.target.value = "";
Expand All @@ -52,10 +52,9 @@ const AddProudctForm = () => {
}
};

const handleTagRemove = (e) => {
e.preventDefault();
const removeItem = e.currentTarget.parentNode.dataset.tag;
const nextValue = values.tags.filter((tag) => tag !== removeItem);
const handleTagRemove = (event, target) => {
event.preventDefault();
const nextValue = values.tags.filter((tag) => tag !== target);
handleChange("tags", nextValue);
};

Expand All @@ -67,7 +66,7 @@ const AddProudctForm = () => {
<form className={styles.form}>
<header className={styles.header}>
<h2 className={styles.title}>상품 등록하기</h2>
<Button className={styles.button} isDisabled={isDisabled}>
<Button type="submit" className={styles.button} disabled={isDisabled}>
등록
</Button>
</header>
Expand All @@ -78,6 +77,7 @@ const AddProudctForm = () => {
onChange={handleChange}
/>
<Input
type="text"
label="상품명"
name="product"
placeholder="상품명을 입력해주세요"
Expand All @@ -92,21 +92,24 @@ const AddProudctForm = () => {
onChange={handleInputChange}
/>
<Input
type="text"
label="판매가격"
name="price"
placeholder="판매가격을 입력해주세요"
value={values.price}
onChange={handleInputChange}
/>
<Input
type="text"
label="태그"
name="tags"
placeholder="태그를 입력해주세요"
onKeyDown={handleKeyDown}
onKeyUp={handleKeyUp}
className={styles.tags}
/>
<Tags tags={values.tags} onRemove={handleTagRemove} />
</form>
);
};

export default AddProudctForm;
export default AddProuductForm;
5 changes: 5 additions & 0 deletions src/features/AddProduct/components/AddProductForm.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,9 @@

.button {
padding: 12px 23px 12px 23px;
line-height: 1.125rem;
}

.tags {
margin-top: -10px;
}
2 changes: 1 addition & 1 deletion src/features/AddProduct/components/FileInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ const FileInput = ({ label, name, value, onChange }) => {
<input
className={styles.input}
type="file"
accept="image/png image/jpeg"
accept="image/png, image/jpeg"
name={name}
id={name}
onChange={handleChange}
Expand Down
Loading

0 comments on commit 62ee2bd

Please sign in to comment.