Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
508c614
✨ feat: 제품 상세 페이지에 대한 라우터 추가 및 링크 추가
Yun-Jinwoo May 8, 2025
1166138
📁 chore: api 폴더 생성하여, 관련 파일들 따로 분리
Yun-Jinwoo May 8, 2025
69fc11c
✨ feat: 상품 정보 보여줄 ProductInfo 컴포넌트 구현
Yun-Jinwoo May 8, 2025
2599cc8
✨ feat: 상품 정보 불러오는 getProduct 함수 구현
Yun-Jinwoo May 8, 2025
f2bd1ee
🎨 style: 전역 스타일에 h1~h4 태그 추가
Yun-Jinwoo May 10, 2025
a90d9c5
🖼️ feat: fallback 이미지 수정
Yun-Jinwoo May 10, 2025
ff8a038
✨ feat: ProductInfo 컴포넌트 추가 구현
Yun-Jinwoo May 10, 2025
7b854b6
🎨 style: ProductInfo 컴포넌트 스타일링
Yun-Jinwoo May 10, 2025
aac3cdc
🐛 fix: AddItem 컴포넌트 스타일이 다른 곳에도 적용되는 오류 수정
Yun-Jinwoo May 10, 2025
8def5de
✨ feat: Product 컴포넌트 구현
Yun-Jinwoo May 10, 2025
41a82d7
🐛 fix: AllItems 컴포넌트 스타일이 다른 곳에도 적용되는 오류 수정
Yun-Jinwoo May 10, 2025
42248a7
🎨 style: ProductInfo 컴포넌트 이미지 스타일 추가
Yun-Jinwoo May 10, 2025
3903e77
✨ feat: 상품 댓글 정보 받아오는 getComment 함수 구현
Yun-Jinwoo May 10, 2025
c1e2a73
✨ feat: Comment 컴포넌트 구현
Yun-Jinwoo May 10, 2025
6905274
🎨 style: Comment 컴포넌트 스타일링
Yun-Jinwoo May 10, 2025
ddfc9a4
✨ feat: 수정 및 삭제 기능 구현, 댓글 없을 경우 처리
Yun-Jinwoo May 10, 2025
5362443
🎨 style: 수정 및 삭제 관련 스타일링 추가
Yun-Jinwoo May 10, 2025
f329ea3
✨ feat: Product 컴포넌트에 Comment 컴포넌트 추가 및 돌아가기 기능 구현
Yun-Jinwoo May 10, 2025
beda885
🎨 style: ProductInfo 컴포넌트 반응형 구현
Yun-Jinwoo May 10, 2025
1b767d6
🎨 style: Comment 컴포넌트 반응형 구현
Yun-Jinwoo May 10, 2025
91b5966
🎨 style: 돌아가기 버튼 밑 여백 추가
Yun-Jinwoo May 10, 2025
fedba23
🎨 style: 버튼에 hover효과 추가
Yun-Jinwoo May 10, 2025
5d14a79
♻️ refactor: 반복되는 UI 리팩토링
Yun-Jinwoo May 10, 2025
9395cc0
♻️ refactor: 로그인/회원가입 페이지 분리
Yun-Jinwoo May 10, 2025
12fcd05
✨ feat: esc 또는 외부 클릭시 드롭다운이 닫히게 구현
Yun-Jinwoo May 10, 2025
8e30cce
♻️ refactor: Item 컴포넌트 코드 수정
Yun-Jinwoo May 10, 2025
8a1c515
🐛 fix: 불필요한 코드 삭제
Yun-Jinwoo May 10, 2025
24ff016
🔥 remove: 불필요한 콘솔 출력문 제거
Yun-Jinwoo May 10, 2025
7de0323
🐛 fix: 경로 오타 수정
Yun-Jinwoo May 10, 2025
706a8b2
🎨 style: skeleton ui 적용
Yun-Jinwoo May 10, 2025
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
9 changes: 6 additions & 3 deletions src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ import "./App.css";
import "./base.css";
import { Routes, Route } from "react-router-dom";
import Home from "./pages/Home/Home";
import Login from "./pages/Login/Login";
import LoginPage from "./pages/Login/LoginPage";
import SignupPage from "./pages/Login/SignupPage";
import Market from "./pages/Market/Market";
import Product from "./pages/Product/Product";
import Community from "./pages/Community/Community";
import AddItem from "./pages/AddItem/AddItem";
import NotFound from "./pages/NotFound/NotFound";
Expand All @@ -12,9 +14,10 @@ function App() {
return (
<Routes>
<Route path="/" element={<Home />} />
<Route path="/login" element={<Login type="login" />} />
<Route path="/signup" element={<Login type="signup" />} />
<Route path="/login" element={<LoginPage />} />
<Route path="/signup" element={<SignupPage />} />
<Route path="/items" element={<Market />} />
<Route path="/items/:id" element={<Product />} />
<Route path="/boards" element={<Community />} />
<Route path="/additem" element={<AddItem />} />
<Route path="*" element={<NotFound />} />
Expand Down
7 changes: 7 additions & 0 deletions src/api/getComments.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export async function getComments({ id, limit = 8 }) {
const response = await fetch(
`https://panda-market-api.vercel.app/products/${id}/comments?limit=${limit}`
);
const body = await response.json();
return body;
}
7 changes: 7 additions & 0 deletions src/api/getProduct.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export async function getProduct({ id }) {
const response = await fetch(
`https://panda-market-api.vercel.app/products/${id}`
);
const body = await response.json();
return body;
}
File renamed without changes.
Binary file modified src/assets/images/fallback.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions src/assets/images/go_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_heart.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions src/assets/images/ic_options.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 24 additions & 0 deletions src/assets/images/ic_profile.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/no_comment.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions src/base.css
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
box-sizing: border-box;
}

h1,
h2,
h3,
h4,
html,
body,
div,
Expand Down
9 changes: 9 additions & 0 deletions src/components/Header.css
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,15 @@
border: none;
}

.Header .login-button:hover {
background-color: var(--blue200);
color: white;
cursor: pointer;
transition: all 0.3s ease;
transform: translateY(2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}

.Header .header-left .link-section .active {
color: var(--blue100);
}
Expand Down
18 changes: 18 additions & 0 deletions src/hooks/useEmailValidator.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { useState } from "react";

export function useEmailValidator(initial = "") {
const [email, setEmail] = useState(initial);
const [touched, setTouched] = useState(false);

const isValid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
const hasError = touched && !isValid;

return {
email,
setEmail,
hasError,
isValid,
onChange: (e) => setEmail(e.target.value),
onBlur: () => setTouched(true),
};
}
20 changes: 20 additions & 0 deletions src/hooks/usePasswordInput.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { useState } from "react";

export function usePasswordInput(initial = "") {
const [value, setValue] = useState(initial);
const [touched, setTouched] = useState(false);
const [visible, setVisible] = useState(false);

const hasError = touched && value.length < 8;

return {
value,
setValue,
touched,
visible,
hasError,
toggleVisible: () => setVisible((v) => !v),
onChange: (e) => setValue(e.target.value),
onBlur: () => setTouched(true),
};
}
73 changes: 41 additions & 32 deletions src/pages/AddItem/AddItem.css
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@
gap: 24px;
}

.register {
.add-item .register {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 20px;
font-weight: 700;
}

.register button {
.add-item .register button {
padding: 12px 23px;
background-color: var(--blue100);
color: var(--gray100);
Expand All @@ -24,12 +24,21 @@
font-weight: 600;
}

.register button:disabled {
.add-item .register button:disabled {
background-color: var(--gray400);
cursor: not-allowed;
}

.info-section {
.add-item .register button:not(:disabled):hover {
background-color: var(--blue200);
color: white;
cursor: pointer;
transition: all 0.3s ease;
transform: translateY(2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}

.add-item .info-section {
display: flex;
flex-direction: column;
gap: 32px;
Expand All @@ -38,26 +47,26 @@
line-height: 26px;
}

.info-section.size-down {
.add-item .info-section.size-down {
gap: 24px;
}

.image-section,
.name-section,
.description-section,
.price-section,
.tag-section {
.add-item .image-section,
.add-item .name-section,
.add-item .description-section,
.add-item .price-section,
.add-item .tag-section {
display: flex;
flex-direction: column;
gap: 16px;
}

.upload-section {
.add-item .upload-section {
display: flex;
gap: 24px;
}

.upload-button {
.add-item .upload-button {
width: 282px;
height: 282px;
display: flex;
Expand All @@ -68,43 +77,43 @@
cursor: pointer;
}

.image-upload {
.add-item .image-upload {
display: flex;
flex-direction: column;
align-items: center;
gap: 12px;
}

.image-upload span {
.add-item .image-upload span {
font-size: 16px;
font-weight: 400;
line-height: 26px;
color: var(--gray400);
}

.image-uploaded {
.add-item .image-uploaded {
width: 282px;
height: 282px;
border-radius: 12px;
overflow: hidden;
position: relative;
}

.image-uploaded img {
.add-item .image-uploaded img {
width: 100%;
height: 100%;
}

.image-uploaded .delete-button {
.add-item .image-uploaded .delete-button {
position: absolute;
top: 12px;
right: 12px;
}

.name-input,
.description-input,
.price-input,
.tag-input {
.add-item .name-input,
.add-item .description-input,
.add-item .price-input,
.add-item .tag-input {
height: 56px;
padding: 16px 24px;
border-radius: 12px;
Expand All @@ -115,25 +124,25 @@
line-height: 26px;
}

.description-input {
.add-item .description-input {
height: 282px;
resize: none;
letter-spacing: -0.7px; /* textarea와 input의 간격 차이 때문에 추가 */
}

.show-tag {
.add-item .show-tag {
display: flex;
flex-direction: column;
gap: 14px;
}

.tag-list ul {
.add-item .tag-list ul {
display: flex;
flex-wrap: wrap;
gap: 12px;
}

.tag-list li {
.add-item .tag-list li {
display: flex;
justify-content: center;
align-items: center;
Expand All @@ -146,7 +155,7 @@
font-weight: 400;
}

.delete-button {
.add-item .delete-button {
background-image: url(../../assets/images/button_x.svg);
width: 20px;
height: 20px;
Expand All @@ -157,13 +166,13 @@
background-color: transparent;
}

.error-message {
.add-item .error-message {
color: var(--red);
font-size: 16px;
font-weight: 400;
}

.hidden {
.add-item .hidden {
display: none;
}

Expand All @@ -178,16 +187,16 @@
margin: 16px 24px;
}

.info-section {
.add-item .info-section {
gap: 24px;
}

.upload-section {
.add-item .upload-section {
gap: 10px;
}

.upload-button,
.image-uploaded {
.add-item .upload-button,
.add-item .image-uploaded {
width: 168px;
height: 168px;
}
Expand Down
Loading
Loading