Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10,854 changes: 6,099 additions & 4,755 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
"name": "1-weekly-mission",
"version": "0.1.0",
"private": true,
"engines": {
"node": "22.x"
},
"dependencies": {
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
Expand Down
6 changes: 6 additions & 0 deletions src/API/deleteComment.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { api } from "./api";

export default async function deleteComment(commentId) {
const res = await api.delete(`/comments/${commentId}`);
return res.data;
}
7 changes: 7 additions & 0 deletions src/API/getComments.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { api } from "./api";
export default async function getComments(productIdNum, limit, cursor) {
const res = await api.get(
`/products/${productIdNum}/comments?limit=${limit}&cursor=${cursor}`
);
return res.data;
}
5 changes: 5 additions & 0 deletions src/API/getProduct.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { api } from "./api";
export default async function getProduct(id) {
const res = await api.get(`/products/${id}`);
return res.data;
}
7 changes: 7 additions & 0 deletions src/API/patchComment.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { api } from "./api";
export default async function deleteComment(commentId, newContent) {
const res = await api.patch(`/comments/${commentId}`, {
content: newContent,
});
return res.data;
}
12 changes: 12 additions & 0 deletions src/API/postComment.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { api } from "./api";
export default async function postComment(id, message) {
const res = await api.post(`/products/${id}/comments`, { message });
return res.data;
}

// {
// headers: {
// "Content-Type": "application/json",
// Accept: "application/json",
// },
// }
2 changes: 2 additions & 0 deletions src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import AddItem from "./components/AddItem/AddItem";
import Nav from "./common/Nav/Nav";
import Items from "./components/Items/Items";
import Products from "./components/Products/Products";
import Product from "./components/Product/Product";

export default function App() {
return (
Expand All @@ -11,6 +12,7 @@ export default function App() {
<Routes>
<Route path="/" element={<AddItem />} />
<Route path="/items" element={<Items />} />
<Route path="/items/:productId" element={<Product />} />
<Route path="/products" element={<Products />} />
<Route path="/additem" element={<AddItem />} />
</Routes>
Expand Down
2 changes: 1 addition & 1 deletion src/components/AddItem/AddItem.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export default function AddItem() {
}
const handleSubmit = (e) => {
e.preventDefault();
console.log(formData); // 서버 전송 등 처리
console.log(formData);
};

console.log(formData);
Expand Down
14 changes: 8 additions & 6 deletions src/components/AllItems/AllItems.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import Item from "../Item/Item";
import Pagination from "./Pagination";
import AllItemsHeader from "../AllItemsHeader/AllItemsHeader";
import getDeviceType from "./getDeviceType";
import { useNavigate } from "react-router";

export default function AllItems() {
const [allItems, setAllItems] = useState([]);
Expand All @@ -13,6 +14,7 @@ export default function AllItems() {
const [page, setPage] = useState(1);
const [pages, setPages] = useState([1, 5]);
const [deviceType, setDeviceType] = useState(getDeviceType());
const navigate = useNavigate();

const pageSize = useMemo(() => {
return deviceType === "desktop" ? 10 : deviceType === "tablet" ? 6 : 4;
Expand Down Expand Up @@ -45,16 +47,12 @@ export default function AllItems() {
const items = await getItems(page, pageSize, sort);
const newTotal = items.totalCount;
const newTotalPages = Math.ceil(newTotal / pageSize);
const currentDevice = getDeviceType();

// ✅ 페이지 초과 방지
if (page > newTotalPages) {
const lastPage = newTotalPages;
const newStart = Math.max(lastPage - 4, 1);
setPages([newStart, lastPage]);
setPage(lastPage);

// 이 경우 fetch 다시 필요함 (return으로 막고, useEffect 재호출 유도)
return;
}

Expand All @@ -74,15 +72,19 @@ export default function AllItems() {

<div className={styles.items}>
{allItems.map((item) => (
<Item item={item} listType="all" key={item.id} />
<Item
item={item}
listType="all"
key={item.id}
onClick={() => navigate(`/items/${item.id}`)}
/>
))}
</div>

<Pagination
pages={pages}
page={page}
setPage={setPage}
setLength={() => {}} // 필요 없다면 제거 가능
totalPages={totalPages}
setPages={setPages}
/>
Expand Down
2 changes: 0 additions & 2 deletions src/components/AllItems/Pagination.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ export default function Pagination({
pages,
page,
setPage,
setLength,
totalPages,
setPages,
}) {
Expand All @@ -14,7 +13,6 @@ export default function Pagination({
const start = pages[0];
const end = pages[1];
const arr = Array.from({ length: end - start + 1 }, (_, i) => start + i);
setLength(arr.length);
setPagesArr(arr);
}, [pages]);

Expand Down
9 changes: 8 additions & 1 deletion src/components/BestItem/BestItem.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ import styles from "./BestItem.module.scss";
import { useEffect, useState } from "react";
import getItems from "../../API/getItems";
import Item from "../Item/Item";
import { useNavigate } from "react-router";

export default function BestItems() {
const [bestItems, setBestItems] = useState([]);
const navigate = useNavigate();

useEffect(() => {
async function fetchItems() {
Expand All @@ -24,7 +26,12 @@ export default function BestItems() {
<h3 className={styles.title}>베스트 상품</h3>
<div className={styles.items}>
{bestItems.map((item) => (
<Item item={item} listType="best" key={item.id} />
<Item
item={item}
listType="best"
key={item.id}
onClick={() => navigate(`/items/${item.id}`)}
/>
))}
</div>
</div>
Expand Down
77 changes: 77 additions & 0 deletions src/components/Comment/Comment.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import timeSince from "../../utils/timeSince";
import styles from "./Comment.module.scss";
import blankProfile from "../../image/blankProfile.svg";
import { useState } from "react";
export default function Comment({ comment, handleModify, handleDelete }) {
const { content, updatedAt, id, writer } = comment;
const [show, setShow] = useState(false);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💬 여담
reducer 를 사용해 아래처럼 간단하게도 작성 가능합니다~

Suggested change
const [show, setShow] = useState(false);
const [isShow, toggleIsShow] = useReducer((prev) => !prev, false);

const passedTime = timeSince(updatedAt);
const [edit, setEditing] = useState(false);
const [value, setValue] = useState("");

function modifyContent() {
setEditing(true);
setShow(false);
}

function modifyCancel() {
setEditing(false);
}
function handleSubmit(e) {
e.preventDefault();
setEditing(false);
}
return (
<>
{edit && (
<form onSubmit={handleSubmit} className={styles.modify}>
<textarea
className={styles["modify__textarea"]}
value={value}
onChange={(e) => setValue(e.target.value)}
placeholder="질문 수정하기"
></textarea>
<div className={styles["modify__button"]}>
<button
className={styles["button-cancel"]}
type="button"
onClick={modifyCancel}
>
취소
</button>
<button className={styles["button-modify"]} type="submit">
수정 완료
</button>
</div>
</form>
)}
<div className={styles["comment"]}>
<p className={styles["comment__content"]}>{content}</p>
<button
className={styles["comment__control"]}
onClick={() => setShow((prev) => !prev)}
/>
<div className={show ? styles.options : styles.hidden}>
<div onClick={() => modifyContent()}>수정하기</div>
<div
className={styles.delete}
onClick={() => handleDelete(setShow, id)}
>
삭제하기
</div>
</div>
</div>
<div className={styles["comment-profile"]}>
<img
className={styles.profile}
src={writer.image ?? blankProfile}
alt="writer profile image"
/>
<span className={styles.nickname}>{writer.nickname}</span>
{/* id 사용해서 수정 권한 있는 사용자인지 본인체크해야할거 같은데 */}
<span className={styles.passedTime}>{passedTime} 전</span>
</div>
<div className={styles.borderline}></div>
</>
);
}
111 changes: 111 additions & 0 deletions src/components/Comment/Comment.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
@import "../../styles/variables.scss";
.comment {
position: relative;
display: flex;
width: 100%;
justify-content: space-between;
}
.comment__content {
@include font-14-regular;
color: #1f2937;
}
.comment__control {
padding: 5px 10px;
background-image: url("../../image/options.svg");
background-repeat: no-repeat;
background-position: center;
}
.options {
@include font-16-regular;
display: flex;
flex-direction: column;
align-items: center;
gap: 16px;
position: absolute;
width: 139px;

padding: 12px;
color: #6b7280;
background-color: white;
border-radius: 8px;
border: 1px solid #d1d5db;
top: 34px;
left: 1061px;
}

.hidden {
display: none;
}

.comment-profile {
@include font-12-regular;
display: grid;
width: 100%;
height: 40px;
grid-template-areas:
"a b"
"a c";
grid-template-columns: 0fr 1fr;
grid-template-rows: auto auto;
margin-bottom: 10px;
}
.profile {
grid-area: a;
margin-right: 8px;
}
.nickname {
color: #4b5563;
grid-area: b;
}
.passedTime {
color: #9ca3af;
grid-area: c;
}
.borderline {
height: 1px;
background-color: #e5e7eb;
width: 100%;
}

.modify__textarea {
@include font-16-regular;
width: 100%;
max-width: 1152px;
height: 80px;
padding: 16px 24px;
margin-bottom: 16px;
background-color: #f3f4f6;
border-radius: 12px;
overflow: scroll;
color: #111827;
}

.modify__button {
display: flex;
justify-content: end;
}
.modify__button button {
@include font-16-semiBold;
padding: 8px 23px;
border-radius: 8px;
}

.button-cancel {
color: #737373;
background-color: white;
}
.button-modify {
color: #f3f4f6;
background-color: #3692ff;
}

@media (max-width: 744px) {
.modify__textarea {
max-width: 655px;
}
}
@media (max-width: 376px) {
.modify__textarea {
max-width: 293px;
}
}
Loading
Loading