diff --git a/package-lock.json b/package-lock.json index 58accf843..ba1f45613 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,11 +11,15 @@ "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", + "clsx": "^2.1.1", "react": "^18.2.0", "react-dom": "^18.2.0", "react-router-dom": "^6.28.0", "react-scripts": "5.0.1", "web-vitals": "^2.1.4" + }, + "devDependencies": { + "@babel/plugin-proposal-private-property-in-object": "^7.21.11" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -631,9 +635,18 @@ } }, "node_modules/@babel/plugin-proposal-private-property-in-object": { - "version": "7.21.0-placeholder-for-preset-env.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", - "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "version": "7.21.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.11.tgz", + "integrity": "sha512-0QZ8qP/3RLDVBwBFoWAwCtgcDZJVwA5LUJRZU8x2YFfKNuFq161wK3cuGrALu5yiPu+vzwTAg/sMWVNeWeNyaw==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-property-in-object instead.", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-create-class-features-plugin": "^7.21.0", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + }, "engines": { "node": ">=6.9.0" }, @@ -1876,6 +1889,18 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/preset-env/node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/preset-env/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -5993,6 +6018,15 @@ "wrap-ansi": "^7.0.0" } }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", diff --git a/package.json b/package.json index 7b54ab4b6..89fc7bf69 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", + "clsx": "^2.1.1", "react": "^18.2.0", "react-dom": "^18.2.0", "react-router-dom": "^6.28.0", @@ -35,5 +36,8 @@ "last 1 firefox version", "last 1 safari version" ] + }, + "devDependencies": { + "@babel/plugin-proposal-private-property-in-object": "^7.21.11" } } diff --git a/src/App.js b/src/App.js index bc540ff20..1a1affc57 100644 --- a/src/App.js +++ b/src/App.js @@ -1,20 +1,23 @@ import "./styles/App.css"; import { BrowserRouter, Routes, Route } from "react-router-dom"; -import Header from "./components/Layout/Header"; +import ItemDetailPage from "./pages/ItemDetailPage/ItemDetailPage.jsx"; import MarketPage from "./pages/MarketPage/MarketPage"; import AddItemPage from "./pages/AddItemPage/AddItemPage.jsx"; +import SamplePage from "./pages/SamplePage/SamplePage"; +import MainPage from "./pages/Main/MainPage.jsx"; function App() { return ( -
- -
- + + } /> + } /> - } /> - -
+ } /> + + } /> + } /> + ); } diff --git a/src/api/ItemApi.js b/src/api/ItemApi.js index 60114b1e0..303dfcf78 100644 --- a/src/api/ItemApi.js +++ b/src/api/ItemApi.js @@ -1,8 +1,14 @@ -export async function getProductList({ page = 1, pageSize = 10, orderBy = "recent" }) { +export async function getProductList({ + page = 1, + pageSize = 10, + orderBy = "recent", +}) { const query = `page=${page}&pageSize=${pageSize}&orderBy=${orderBy}`; try { - const response = await fetch(`https://panda-market-api.vercel.app/products?${query}`); + const response = await fetch( + `https://panda-market-api.vercel.app/products?${query}` + ); if (!response.ok) { throw new Error(`error : ${response.status}`); } @@ -14,9 +20,11 @@ export async function getProductList({ page = 1, pageSize = 10, orderBy = "recen } } -export async function getProduct({ id = 1 }) { +export async function getProduct(id) { try { - const response = await fetch(`https://panda-market-api.vercel.app/products/${id}`); + const response = await fetch( + `https://panda-market-api.vercel.app/products/${id}` + ); if (!response.ok) { throw new Error(`error : ${response.status}`); } @@ -27,3 +35,19 @@ export async function getProduct({ id = 1 }) { throw error; } } + +export async function getProductComment(id) { + try { + const response = await fetch( + `https://panda-market-api.vercel.app/products/${id}/comments?limit=3` + ); + if (!response.ok) { + throw new Error(`error : ${response.status}`); + } + const body = await response.json(); + return body; + } catch (error) { + console.log("상품ID의 Comments 데이터를 불러오는데 실패했습니다.", error); + throw error; + } +} diff --git a/src/assets/css/global.css b/src/assets/css/global.css new file mode 100644 index 000000000..c53c7a90e --- /dev/null +++ b/src/assets/css/global.css @@ -0,0 +1,34 @@ +@import url("https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/static/pretendard.min.css"); + +:root { + /* Typography */ + --font-family: "Pretendard Variable", Pretendard, -apple-system, + BlinkMacSystemFont, system-ui, Roboto, "Helvetica Neue", "Segoe UI", + "Apple SD Gothic Neo", "Noto Sans KR", "Malgun Gothic", "Apple Color Emoji", + "Segoe UI Emoji", "Segoe UI Symbol", sans-serif; + + --color-primary-100: #3692ff; + --color-primary-200: #1967d6; + --color-primary-300: #1251aa; + + --color-secondary-900: #111827; + --color-secondary-800: #1f2937; + --color-secondary-700: #374151; + --color-secondary-600: #4b5563; + --color-secondary-500: #6b7280; + --color-secondary-400: #9ca3af; + --color-secondary-200: #e5e7eb; + --color-secondary-100: #f3f4f6; + --color-secondary-50: #f9fafb; + + --color-error: #f74747; +} + +html { + font-size: 62.5%; /* 1rem = 10px */ +} + +body { + font-family: var(--font-family); + min-width: 37.5rem; +} diff --git a/src/assets/css/reset.css b/src/assets/css/reset.css new file mode 100644 index 000000000..0a66c7515 --- /dev/null +++ b/src/assets/css/reset.css @@ -0,0 +1,79 @@ +/* prettier-ignore */ +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td, +article, aside, canvas, details, embed, +figure, figcaption, footer, header, hgroup, +menu, nav, output, ruby, section, summary, +time, mark, audio, video, input, textarea { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline; + box-sizing: border-box; +} +/* HTML5 display-role reset for older browsers */ +article, +aside, +details, +figcaption, +figure, +footer, +header, +main, +hgroup, +menu, +nav, +section { + display: block; + box-sizing: border-box; +} +body { + line-height: 1; +} +dl, +ol, +ul { + list-style: none; +} +blockquote, +q { + quotes: none; +} +blockquote:before, +blockquote:after, +q:before, +q:after { + content: ""; + content: none; +} +table { + border-collapse: collapse; + border-spacing: 0; +} +button { + all: unset; + cursor: pointer; +} +textarea { + resize: none; +} +a, +a:link, +a:visited, +a:hover, +a:active { + color: inherit; + text-decoration: none; +} +img { + vertical-align: middle; +} diff --git a/src/assets/images/Img_home_01.svg b/src/assets/images/Img_home_01.svg new file mode 100644 index 000000000..db9bdf5a5 --- /dev/null +++ b/src/assets/images/Img_home_01.svg @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/images/Img_home_02.svg b/src/assets/images/Img_home_02.svg new file mode 100644 index 000000000..3e963d32e --- /dev/null +++ b/src/assets/images/Img_home_02.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/images/Img_home_03.svg b/src/assets/images/Img_home_03.svg new file mode 100644 index 000000000..f4c9260a5 --- /dev/null +++ b/src/assets/images/Img_home_03.svg @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/images/Img_home_bottom.svg b/src/assets/images/Img_home_bottom.svg new file mode 100644 index 000000000..8d1f0304d --- /dev/null +++ b/src/assets/images/Img_home_bottom.svg @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/images/Img_home_top.svg b/src/assets/images/Img_home_top.svg new file mode 100644 index 000000000..7ae9aacc8 --- /dev/null +++ b/src/assets/images/Img_home_top.svg @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/images/dropdown.svg b/src/assets/images/dropdown.svg new file mode 100644 index 000000000..358e4b87c --- /dev/null +++ b/src/assets/images/dropdown.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/images/ic_X.png b/src/assets/images/ic_X.png similarity index 100% rename from src/images/ic_X.png rename to src/assets/images/ic_X.png diff --git a/src/images/ic_arrow_down.png b/src/assets/images/ic_arrow_down.png similarity index 100% rename from src/images/ic_arrow_down.png rename to src/assets/images/ic_arrow_down.png diff --git a/src/assets/images/ic_back.svg b/src/assets/images/ic_back.svg new file mode 100644 index 000000000..9ba5ad945 --- /dev/null +++ b/src/assets/images/ic_back.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/images/ic_facebook.svg b/src/assets/images/ic_facebook.svg new file mode 100644 index 000000000..8491c2f83 --- /dev/null +++ b/src/assets/images/ic_facebook.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/images/ic_heart.png b/src/assets/images/ic_heart.png similarity index 100% rename from src/images/ic_heart.png rename to src/assets/images/ic_heart.png diff --git a/src/assets/images/ic_heart.svg b/src/assets/images/ic_heart.svg new file mode 100644 index 000000000..172801d4e --- /dev/null +++ b/src/assets/images/ic_heart.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/images/ic_heart_itemDetail.png b/src/assets/images/ic_heart_itemDetail.png similarity index 100% rename from src/images/ic_heart_itemDetail.png rename to src/assets/images/ic_heart_itemDetail.png diff --git a/src/assets/images/ic_instagram.svg b/src/assets/images/ic_instagram.svg new file mode 100644 index 000000000..c83306f84 --- /dev/null +++ b/src/assets/images/ic_instagram.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/images/ic_plus.png b/src/assets/images/ic_plus.png similarity index 100% rename from src/images/ic_plus.png rename to src/assets/images/ic_plus.png diff --git a/src/assets/images/ic_profile.svg b/src/assets/images/ic_profile.svg new file mode 100644 index 000000000..834caf762 --- /dev/null +++ b/src/assets/images/ic_profile.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/images/ic_search.svg b/src/assets/images/ic_search.svg new file mode 100644 index 000000000..c7e94924e --- /dev/null +++ b/src/assets/images/ic_search.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/ic_twitter.svg b/src/assets/images/ic_twitter.svg new file mode 100644 index 000000000..14a6069a1 --- /dev/null +++ b/src/assets/images/ic_twitter.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/ic_youtube.svg b/src/assets/images/ic_youtube.svg new file mode 100644 index 000000000..8270b35a4 --- /dev/null +++ b/src/assets/images/ic_youtube.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/assets/images/item-empty-img.svg b/src/assets/images/item-empty-img.svg new file mode 100644 index 000000000..130270f7b --- /dev/null +++ b/src/assets/images/item-empty-img.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/src/assets/images/logo-img.svg b/src/assets/images/logo-img.svg new file mode 100644 index 000000000..763adc2c1 --- /dev/null +++ b/src/assets/images/logo-img.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/src/assets/images/menu.svg b/src/assets/images/menu.svg new file mode 100644 index 000000000..51b03fba0 --- /dev/null +++ b/src/assets/images/menu.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/images/user-info-img.png b/src/assets/images/user-info-img.png similarity index 100% rename from src/images/user-info-img.png rename to src/assets/images/user-info-img.png diff --git a/src/components/Button/LinkButton.jsx b/src/components/Button/LinkButton.jsx new file mode 100644 index 000000000..4c0010326 --- /dev/null +++ b/src/components/Button/LinkButton.jsx @@ -0,0 +1,28 @@ +import styles from "./LinkButton.module.css"; +import clsx from "clsx"; + +const LinkButton = ({ + onClick, + children, + type, + className, + disabled = false, + size, + color = "primary", + ...props +}) => { + console.log(styles); + return ( + + ); +}; + +export default LinkButton; diff --git a/src/components/Button/LinkButton.module.css b/src/components/Button/LinkButton.module.css new file mode 100644 index 000000000..d14e49154 --- /dev/null +++ b/src/components/Button/LinkButton.module.css @@ -0,0 +1,38 @@ +.button { + display: flex; + align-items: center; + gap: 0.8rem; + padding: 0.8rem 2.3rem; + border-radius: 0.8rem; + font-size: 1.6rem; + font-weight: 600; + line-height: 2.6rem; + background-color: var(--color-primary-100); + color: var(--color-secondary-100); +} + +.button:hover { + background-color: var(--color-primary-300); +} + +.button:disabled { + background-color: var(--color-secondary-400); + cursor: not-allowed; +} + +.large { + padding: 1.6rem 12.4rem; + border-radius: 4rem; + font-size: 2rem; +} + +.medium { + padding: 1.2rem 7.1rem; + border-radius: 4rem; + font-size: 1.8rem; +} + +.small { + padding: 1.2rem 2.3rem; + border-radius: 0.8rem; +} diff --git a/src/components/Header/MainHeader.jsx b/src/components/Header/MainHeader.jsx new file mode 100644 index 000000000..26b9bb234 --- /dev/null +++ b/src/components/Header/MainHeader.jsx @@ -0,0 +1,14 @@ +import LogoImg from "../../assets/images/logo-img.svg"; +import LinkButton from "../Button/LinkButton"; +import styles from "./MainHeader.module.css"; + +const MainHeader = () => { + return ( +
+ 로고이미지 + 로그인 +
+ ); +}; + +export default MainHeader; diff --git a/src/components/Header/MainHeader.module.css b/src/components/Header/MainHeader.module.css new file mode 100644 index 000000000..4c86796f9 --- /dev/null +++ b/src/components/Header/MainHeader.module.css @@ -0,0 +1,11 @@ +.container { + display: flex; + justify-content: space-between; + width: 100%; + padding: 1rem 40rem; + border: 1px solid #dfdfdf; + + @media (max-width: 1920px) { + padding: 1rem 20rem; + } +} diff --git a/src/components/Layout/Header.css b/src/components/Header/MarketHeader.css similarity index 92% rename from src/components/Layout/Header.css rename to src/components/Header/MarketHeader.css index 5d00b9167..c06f10adf 100644 --- a/src/components/Layout/Header.css +++ b/src/components/Header/MarketHeader.css @@ -2,7 +2,7 @@ display: flex; align-items: center; justify-content: space-between; - width: 100vw; + width: 100%; height: 70px; padding: 0 200px; border-bottom: 1px solid #dfdfdf; @@ -12,6 +12,10 @@ } } +.logo:hover { + cursor: pointer; +} + .header-left { display: flex; align-items: center; diff --git a/src/components/Layout/Header.jsx b/src/components/Header/MarketHeader.jsx similarity index 62% rename from src/components/Layout/Header.jsx rename to src/components/Header/MarketHeader.jsx index f7cfed661..738ebfa43 100644 --- a/src/components/Layout/Header.jsx +++ b/src/components/Header/MarketHeader.jsx @@ -1,13 +1,15 @@ import { Link } from "react-router-dom"; -import logoImg from "../../images/logo-img.png"; -import userInfoImg from "../../images/user-info-img.png"; -import "../Layout/Header.css"; +import logoImg from "../../assets/images/logo-img.svg"; +import userInfoImg from "../../assets/images/user-info-img.png"; +import "./MarketHeader.css"; -function Header() { +function MarketHeader() { return (
- 로고이미지 + + 로고이미지 +
+ ); } diff --git a/src/components/UI/PaginationBar.css b/src/components/Pagination/PaginationBar.css similarity index 94% rename from src/components/UI/PaginationBar.css rename to src/components/Pagination/PaginationBar.css index 94bc5afa4..fef4ee61a 100644 --- a/src/components/UI/PaginationBar.css +++ b/src/components/Pagination/PaginationBar.css @@ -9,6 +9,7 @@ height: 40px; border: 1px solid #e5e7eb; border-radius: 40px; + text-align: center; font-size: 16px; font-weight: 600; color: #6b7280; diff --git a/src/components/UI/PaginationBar.jsx b/src/components/Pagination/PaginationBar.jsx similarity index 100% rename from src/components/UI/PaginationBar.jsx rename to src/components/Pagination/PaginationBar.jsx diff --git a/src/components/Tag/Tag.jsx b/src/components/Tag/Tag.jsx new file mode 100644 index 000000000..e5463afaf --- /dev/null +++ b/src/components/Tag/Tag.jsx @@ -0,0 +1,21 @@ +import styles from "./Tag.module.css"; + +const Tag = ({ tags }) => { + console.log(tags); + if (!tags) { + return

태그가 없습니다.

; + } + return ( +
+ {tags.map((tag) => { + return ( +
+ #{tag} +
+ ); + })} +
+ ); +}; + +export default Tag; diff --git a/src/components/Tag/Tag.module.css b/src/components/Tag/Tag.module.css new file mode 100644 index 000000000..2a76e8467 --- /dev/null +++ b/src/components/Tag/Tag.module.css @@ -0,0 +1,12 @@ +.container { + display: flex; + gap: 0.8rem; +} + +.tag { + padding: 0.6rem 1.6rem; + border-radius: 2.6rem; + font-weight: 400; + font-size: 1.6rem; + background-color: var(--color-secondary-100); +} diff --git a/src/components/UI/onSortSelect.jsx b/src/components/UI/onSortSelect.jsx index 6844b225c..6a32b5886 100644 --- a/src/components/UI/onSortSelect.jsx +++ b/src/components/UI/onSortSelect.jsx @@ -1,6 +1,6 @@ import "./onSortSelect.css"; -function SortSelect({ onSortSelection }) { +function onSortSelect({ onSortSelection }) { return (
onSortSelection("recent")}> @@ -13,4 +13,4 @@ function SortSelect({ onSortSelection }) { ); } -export default SortSelect; +export default onSortSelect; diff --git a/src/images/ic_search.png b/src/images/ic_search.png deleted file mode 100644 index 587463217..000000000 Binary files a/src/images/ic_search.png and /dev/null differ diff --git a/src/images/logo-img.png b/src/images/logo-img.png deleted file mode 100644 index 1fa247eda..000000000 Binary files a/src/images/logo-img.png and /dev/null differ diff --git a/src/index.js b/src/index.js index 71a354b9f..da7bd2968 100644 --- a/src/index.js +++ b/src/index.js @@ -1,6 +1,14 @@ import React from "react"; +import "../src/assets//css/reset.css"; +import "../src/assets/css/global.css"; +import { StrictMode } from "react"; import ReactDOM from "react-dom/client"; import App from "./App"; +import "./assets/css/global.css"; const root = ReactDOM.createRoot(document.getElementById("root")); -root.render(); +root.render( + + + +); diff --git a/src/pages/AddItemPage/AddItemPage.css b/src/pages/AddItemPage/AddItemPage.css index 9cf5a4151..6c65f0c16 100644 --- a/src/pages/AddItemPage/AddItemPage.css +++ b/src/pages/AddItemPage/AddItemPage.css @@ -74,7 +74,7 @@ width: 282px; height: 282px; padding: 158px 104px 98px; - background: url(../../images/ic_plus.png) no-repeat center; + background: url(../../assets/images/ic_plus.png) no-repeat center; background-size: 48px; background-color: #f3f4f6; @media (max-width: 768px) { @@ -117,7 +117,7 @@ top: 12px; right: 12px; border: none; - background: url("/src/images/ic_X.png"); + background: url("../../assets/images/ic_X.png"); } .clearBtn:hover { diff --git a/src/pages/AddItemPage/AddItemPage.jsx b/src/pages/AddItemPage/AddItemPage.jsx index 9c00bdaf5..9d74c7683 100644 --- a/src/pages/AddItemPage/AddItemPage.jsx +++ b/src/pages/AddItemPage/AddItemPage.jsx @@ -10,7 +10,7 @@ const AddItemPage = () => { tag: "", imgFile: null, }); - const [disabled, setDisabled] = useState(true); + const [canSubmit, setCanSubmit] = useState(true); const handleChange = (name, value) => { setValues((prevValues) => ({ @@ -22,7 +22,7 @@ const AddItemPage = () => { const handleInputChange = (e) => { const { name, value } = e.target; handleChange(name, value); - setDisabled(isSubmitDisabled); + setCanSubmit(isSubmitDisabled); }; const handleSubmit = (e) => { @@ -37,7 +37,7 @@ const AddItemPage = () => {

상품 등록하기

-
diff --git a/src/pages/ItemDetailPage/ItemDetailPage.css b/src/pages/ItemDetailPage/ItemDetailPage.css deleted file mode 100644 index 8504e9e59..000000000 --- a/src/pages/ItemDetailPage/ItemDetailPage.css +++ /dev/null @@ -1,9 +0,0 @@ -.itemDetailPageContainer { - width: 1200px; - margin: 24px auto; - padding: 0 0 40px; - display: flex; - flex-direction: column; - gap: 64px; - border-bottom: 1px solid #e5e7eb; -} diff --git a/src/pages/ItemDetailPage/ItemDetailPage.jsx b/src/pages/ItemDetailPage/ItemDetailPage.jsx index cd9524166..e4fe26dc5 100644 --- a/src/pages/ItemDetailPage/ItemDetailPage.jsx +++ b/src/pages/ItemDetailPage/ItemDetailPage.jsx @@ -1,41 +1,30 @@ -import ItemDetail from "./components/ItemDetail.jsx"; -import "./ItemDetailPage.css"; -import { getProduct } from "../../api/ItemApi"; -import { useState } from "react"; -import { useEffect } from "react"; -import QuestionForm from "./components/QuestionForm.jsx"; +import styles from "./ItemDetailPage.module.css"; +import { useParams } from "react-router-dom"; +import MarketHeader from "../../components/Header/MarketHeader.jsx"; +import ProductSection from "./components/ProductSection.jsx"; +import InputSection from "./components/InputSection.jsx"; +import CommentSection from "./components/CommentSection.jsx"; +import LinkButton from "../../components/Button/LinkButton.jsx"; +import Icon from "../../components/Icon/Icon.jsx"; +import { Link } from "react-router-dom"; function ItemDetailPage() { - const [product, setProduct] = useState({ - images: [], - name: "", - tags: [], - description: "", - favoriteCount: 0, - }); - - const loadProduct = async ({ id }) => { - try { - const product = await getProduct({ id }); - setProduct(product); - } catch (error) { - console.error("데이터 로드 실패", error); - } - }; - - useEffect(() => { - loadProduct(1); - }, []); + const { id } = useParams(); return ( -
-
- -
-
- -
-
+ <> + +
+ + + + + + 목록으로 돌아가기 + + +
+ ); } diff --git a/src/pages/ItemDetailPage/ItemDetailPage.module.css b/src/pages/ItemDetailPage/ItemDetailPage.module.css new file mode 100644 index 000000000..18d5bb477 --- /dev/null +++ b/src/pages/ItemDetailPage/ItemDetailPage.module.css @@ -0,0 +1,20 @@ +.container { + display: flex; + flex-direction: column; + gap: 6.4rem; + max-width: 120rem; + margin: 2.4rem auto; + + @media (max-width: 1200px) { + padding: 2.4rem; + } +} + +.btn { + box-sizing: border-box; + width: 24rem; + margin: 0 auto; + padding: 1.2rem 3.6rem; + font-weight: 600; + font-size: 1.8rem; +} diff --git a/src/pages/ItemDetailPage/components/CommentSection.jsx b/src/pages/ItemDetailPage/components/CommentSection.jsx new file mode 100644 index 000000000..c38267aad --- /dev/null +++ b/src/pages/ItemDetailPage/components/CommentSection.jsx @@ -0,0 +1,69 @@ +import styles from "./CommentSection.module.css"; +import { useEffect, useState } from "react"; +import { getProductComment } from "../../../api/ItemApi"; +import Icon from "../../../components/Icon/Icon"; +import userImg from "../../../assets/images/ic_profile.svg"; + +const CommentSection = ({ id }) => { + const [productComment, setProductComment] = useState({ list: [] }); + const [isDropdownOpenId, setIsDropdownOpenId] = useState(null); + + // dropdown메뉴가 열린 commentId가 일치해야 메뉴가 열린다. + const toggleDropdown = (commentId) => { + setIsDropdownOpenId((prev) => (prev === commentId ? null : commentId)); + }; + + const loadComment = async (id) => { + try { + const commentData = await getProductComment(id); + console.log(commentData); + setProductComment(commentData); + } catch (error) { + console.error("댓글 데이터 로드 실패", error); + } + }; + + useEffect(() => { + if (id) { + loadComment(id); + } + }, [id]); + + return ( + <> + {productComment.list.map((comment) => ( +
+
+

{comment.content}

+
+ {`${comment.writer.nickname}의 +
+

{comment.writer.nickname}

+

+ {new Date(comment.createdAt).toLocaleString()} +

+
+
+
+ toggleDropdown(comment.id)} + /> + {isDropdownOpenId === comment.id && ( +
+

수정하기

+

삭제하기

+
+ )} +
+ ))} + + ); +}; + +export default CommentSection; diff --git a/src/pages/ItemDetailPage/components/CommentSection.module.css b/src/pages/ItemDetailPage/components/CommentSection.module.css new file mode 100644 index 000000000..be07d5956 --- /dev/null +++ b/src/pages/ItemDetailPage/components/CommentSection.module.css @@ -0,0 +1,64 @@ +.container { + position: relative; + display: flex; + justify-content: space-between; + padding-bottom: 1.2rem; + border-bottom: 1px solid var(--color-secondary-200); + font-weight: 400; +} + +.content-box { + display: flex; + flex-direction: column; + gap: 2.4rem; +} + +.content { + font-size: 1.4rem; + color: var(--color-secondary-800); +} + +.user { + display: flex; + gap: 0.8rem; +} + +.user-info { + display: flex; + flex-direction: column; + justify-content: space-between; + gap: 0.4rem; +} + +.name { + font-size: 1.2rem; + color: var(--color-secondary-600); +} + +.date { + font-size: 1.2rem; + color: var(--color-secondary-400); +} + +.menu:hover { + cursor: pointer; +} + +.dropdown { + position: absolute; + top: 3.4rem; + right: 0; + display: flex; + flex-direction: column; + border: 1px solid #d1d5db; + border-radius: 0.8rem; + background-color: #ffffff; +} + +.dropdown-text { + padding: 1.2rem 1.6rem; + font-weight: 400; + font-size: 1.6rem; + text-align: center; + color: var(--color-secondary-500); +} diff --git a/src/pages/ItemDetailPage/components/InputSection.jsx b/src/pages/ItemDetailPage/components/InputSection.jsx new file mode 100644 index 000000000..35a7bda12 --- /dev/null +++ b/src/pages/ItemDetailPage/components/InputSection.jsx @@ -0,0 +1,31 @@ +import styles from "./InputSection.module.css"; +import Input from "../../../components/Input/Input"; +import LinkButton from "../../../components/Button/LinkButton"; +import { useState } from "react"; + +const InputSection = () => { + const [inputValue, setInputValue] = useState(""); + + const isDisabled = inputValue.trim() === ""; + + const onChangeInput = (e) => { + setInputValue(e.target.value); + }; + + return ( + + + + + 등록 + + + ); +}; + +export default InputSection; diff --git a/src/pages/ItemDetailPage/components/InputSection.module.css b/src/pages/ItemDetailPage/components/InputSection.module.css new file mode 100644 index 000000000..977c48e3a --- /dev/null +++ b/src/pages/ItemDetailPage/components/InputSection.module.css @@ -0,0 +1,22 @@ +.container { + display: flex; + flex-direction: column; + gap: 1rem; +} + +.header { + font-weight: 600; + font-size: 1.6rem; + line-height: 2.6rem; + color: var(--color-secondary-900); +} + +.text { + height: 10.4rem; +} + +.btn { + box-sizing: border-box; + width: 7.4rem; + margin-left: auto; +} diff --git a/src/pages/ItemDetailPage/components/ItemDetail.css b/src/pages/ItemDetailPage/components/ItemDetail.css deleted file mode 100644 index da2ede80a..000000000 --- a/src/pages/ItemDetailPage/components/ItemDetail.css +++ /dev/null @@ -1,109 +0,0 @@ -.productContainer { - display: flex; - gap: 24px; -} - -.productImg { - width: 486px; - height: 486px; - border-radius: 16px; -} - -.productDesc { - display: flex; - flex-direction: column; - gap: 24px; - flex: 1; -} - -.productHeader { - display: flex; - flex-direction: column; - gap: 16px; - border-bottom: 1px solid #e5e7eb; -} - -.productName { - font-size: 24p; - font-weight: 600; - color: #1f2937; -} - -.productPrice { - font-size: 40px; - font-weight: 600; - color: #1f2937; -} - -.productInfo { - display: flex; - flex-direction: column; - gap: 24px; -} - -.productInfo p { - font-size: 16px; - font-weight: 600; - color: #4b5563; -} - -.productDescription { - font-size: 16px; - font-weight: 400; - color: #4b5563; -} - -.productOwnerContainer { - display: flex; - justify-content: space-between; - align-items: center; -} - -.productOwnerInfo { - display: flex; - gap: 16px; -} - -.productOwnerInfo div { - display: flex; - flex-direction: column; - gap: 2px; -} - -.userImg { - width: 40px; - height: 40px; -} - -.userName { - font-size: 14px; - font-weight: 500; - color: #4b5563; -} - -.date { - font-size: 14px; - font-weight: 400; - color: #9ca3af; -} - -.favoriteCountContainer { - height: 40px; - display: flex; - align-items: center; - gap: 10px; - padding: 4px 12px; - border: 1px solid #e5e7eb; - border-radius: 35px; -} - -.heartImg { - width: 32px; - height: 32px; -} - -.favoriteCount { - font-size: 16px; - font-weight: 500; - color: #6b7280; -} diff --git a/src/pages/ItemDetailPage/components/ItemDetail.jsx b/src/pages/ItemDetailPage/components/ItemDetail.jsx deleted file mode 100644 index 6a3b49034..000000000 --- a/src/pages/ItemDetailPage/components/ItemDetail.jsx +++ /dev/null @@ -1,39 +0,0 @@ -import Tag from "./Tag.jsx"; -import "./ItemDetail.css"; -import userImg from "../../../images/user-info-img.png"; -import itemHeart from "../../../images/ic_heart_itemDetail.png"; - -function ItemDetail({ product }) { - return ( -
- -
-
-

{product.name}

-

{product.price}원

-
-
-

상품소개

-

{product.description}

-

상품태그

- -
-
-
- -
-
{product.ownerNickname}
-
{new Date(product.createdAt).toLocaleDateString()}
-
-
-
- -
{product.favoriteCount}
-
-
-
-
- ); -} - -export default ItemDetail; diff --git a/src/pages/ItemDetailPage/components/ProductSection.jsx b/src/pages/ItemDetailPage/components/ProductSection.jsx new file mode 100644 index 000000000..00e0362f3 --- /dev/null +++ b/src/pages/ItemDetailPage/components/ProductSection.jsx @@ -0,0 +1,79 @@ +import { useState, useEffect } from "react"; +import { getProduct } from "../../../api/ItemApi.js"; +import styles from "./ProductSection.module.css"; +import menuIcon from "../../../assets/images//menu.svg"; +import Tag from "../../../components/Tag/Tag.jsx"; +import Icon from "../../../components/Icon/Icon.jsx"; +import userImg from "../../../assets/images/ic_profile.svg"; + +const ProductSection = ({ id }) => { + const [product, setProduct] = useState(1); + + const loadProduct = async (id) => { + try { + const productData = await getProduct(id); + setProduct(productData); + } catch (error) { + console.error("상품 데이터 로드 실패", error); + } + }; + + useEffect(() => { + if (id) { + loadProduct(id); + } + }, [id]); + + return ( +
+ {product.images && product.images.length > 0 ? ( + {`${product.name} + ) : ( +

이미지가 없습니다.

+ )} +
+
+
+

{product.name}

+

{product.price}

+
+ 설정 메뉴 +
+
+

상품 소개

+

{product.description}

+
+
+

상품 태그

+
+ +
+
+
+
+ 프로필 이미지 +
+

{product.ownerNickname}

+

{product.createdAt}

+
+
+ +
+
+
+ ); +}; + +export default ProductSection; diff --git a/src/pages/ItemDetailPage/components/ProductSection.module.css b/src/pages/ItemDetailPage/components/ProductSection.module.css new file mode 100644 index 000000000..2eaf3e046 --- /dev/null +++ b/src/pages/ItemDetailPage/components/ProductSection.module.css @@ -0,0 +1,134 @@ +.container { + display: flex; + gap: 2.4rem; + + @media (max-width: 502px) { + flex-direction: column; + gap: 1.6rem; + } +} + +.img { + width: 48.6rem; + height: 48.6rem; + border-radius: 1.6rem; + + @media (max-width: 1200px) { + width: 34rem; + height: 34rem; + } +} + +.content { + display: flex; + flex-direction: column; + justify-content: space-between; + flex: 1; + gap: 2.4rem; +} + +.content-header { + display: flex; + align-items: start; + justify-content: space-between; + width: 100%; + border-bottom: 0.1rem solid #e5e7eb; + font-weight: 600; + color: var(--color-secondary-800); +} + +.header-content { + display: flex; + flex-direction: column; + gap: 1.6rem; +} + +.description { + font-size: 2.4rem; +} + +.price { + margin-bottom: 1.6rem; + font-size: 4rem; +} + +.content-mid { + display: flex; + flex-direction: column; + gap: 1.6rem; + font-size: 1.6rem; + color: var(--color-secondary-600); +} + +.mid-header { + font-weight: 600; +} + +.mid-content { + font-weight: 400; +} + +.content-bottom { + display: flex; + flex-direction: column; + gap: 1.6rem; +} + +.content-bottom > p { + font-weight: 600; + font-size: 1.6rem; + color: var(--color-secondary-600); +} + +.content-footer { + display: flex; + justify-content: space-between; +} + +.user-info { + display: flex; + align-items: center; + gap: 1.6rem; + flex: 1; + border-right: 1px solid var(--color-secondary-200); +} + +.footer-text { + display: flex; + flex-direction: column; + gap: 0.2rem; +} + +.user-name { + font-weight: 500; + font-size: 1.4rem; + line-height: 2.4rem; + color: var(--color-secondary-600); +} + +.date { + font-weight: 400; + font-size: 1.4rem; + line-height: 2.4rem; + color: var(--color-secondary-400); +} + +.like-btn { + display: flex; + align-items: center; + gap: 1rem; + margin-left: 2.4rem; + padding: 0.4rem 1.2rem; + border: 1px solid var(--color-secondary-200); + border-radius: 3.5rem; +} + +.heart { +} + +.like-count { + font-weight: 500; + font-size: 1.6rem; + line-height: 2.6rem; + color: var(--color-secondary-500); +} diff --git a/src/pages/ItemDetailPage/components/QuestionForm.css b/src/pages/ItemDetailPage/components/QuestionForm.css deleted file mode 100644 index 4fd495d7a..000000000 --- a/src/pages/ItemDetailPage/components/QuestionForm.css +++ /dev/null @@ -1,42 +0,0 @@ -.questionContainer { - display: flex; - flex-direction: column; - gap: 12px; -} - -.questionContainer > p { - font-size: 16px; - font-weight: 600; - color: #111827; -} - -.qsInput { - height: 104px; - padding: 16px 24px; - border: none; - border-radius: 12px; - background-color: #f3f4f6; -} - -.qsInput::placeholder { - font-size: 16px; - font-weight: 400; - color: #9ca3af; -} - -.qsBtn { - width: 74px; - height: 42px; - padding: 12px 23px; - justify-self: flex-end; - align-self: flex-end; - border: none; - border-radius: 8px; - color: #f3f4f6; - background-color: #9ca3af; -} - -.qsBtn:hover { - cursor: pointer; - background-color: #3692ff; -} diff --git a/src/pages/ItemDetailPage/components/QuestionForm.jsx b/src/pages/ItemDetailPage/components/QuestionForm.jsx deleted file mode 100644 index 17e63825d..000000000 --- a/src/pages/ItemDetailPage/components/QuestionForm.jsx +++ /dev/null @@ -1,28 +0,0 @@ -import { useState } from "react"; -import "./QuestionForm.css"; - -function QuestionForm() { - const [inputValue, setInputValue] = useState(""); - - const onHandleValue = (e) => { - setInputValue(e.target.value); - }; - - const isInputValid = !inputValue; - - return ( -
- - - -
- ); -} - -export default QuestionForm; diff --git a/src/pages/ItemDetailPage/components/Tag.jsx b/src/pages/ItemDetailPage/components/Tag.jsx deleted file mode 100644 index d6d690ae2..000000000 --- a/src/pages/ItemDetailPage/components/Tag.jsx +++ /dev/null @@ -1,9 +0,0 @@ -function Tag() { - return ( -
-
태그입니다.
-
- ); -} - -export default Tag; diff --git a/src/pages/Main/MainPage.jsx b/src/pages/Main/MainPage.jsx new file mode 100644 index 000000000..29afb679a --- /dev/null +++ b/src/pages/Main/MainPage.jsx @@ -0,0 +1,57 @@ +import styles from "./MainPage.module.css"; +import MainHeader from "../../components/Header/MainHeader"; +import TopSection from "./components/TopSection"; +import MidSection from "./components/MidSection"; + +//images +import HotItemImg from "../../assets/images/Img_home_01.svg"; +import SearchImg from "../../assets/images/Img_home_02.svg"; +import RegsiterImg from "../../assets//images//Img_home_03.svg"; +import BottomSection from "./components/BottomSection"; +import MainFooter from "./components/MainFooter"; + +const MainPage = () => { + return ( +
+ + + + 인기 상품을
확인해 보세요 + + } + content="가장 HOT한 중고거래 물품을 판다 마켓에서 확인해 보세요" + /> + + 구매를 원하는 +
상품을 검색하세요 + + } + content="구매하고 싶은 물품은 검색해서 쉽게 찾아보세요" + opposite={true} + /> + + 판매를 원하는 +
상품을 등록하세요 + + } + content="어떤 물건이든 판마해고 싶은 상품을 쉽게 등록하세요" + /> + + +
+ ); +}; + +export default MainPage; diff --git a/src/pages/Main/MainPage.module.css b/src/pages/Main/MainPage.module.css new file mode 100644 index 000000000..8dfba1c05 --- /dev/null +++ b/src/pages/Main/MainPage.module.css @@ -0,0 +1,4 @@ +.container { + width: 100%; + min-height: 100vh; +} diff --git a/src/pages/Main/components/BottomSection.jsx b/src/pages/Main/components/BottomSection.jsx new file mode 100644 index 000000000..66097910c --- /dev/null +++ b/src/pages/Main/components/BottomSection.jsx @@ -0,0 +1,18 @@ +import styles from "./BottomSection.module.css"; +import BottomSectionImg from "../../../assets/images/Img_home_bottom.svg"; + +const BottomSection = () => { + return ( +
+
+

+ 믿을 수 있는 +
판다마켓 중고 거래 +

+ 동산 위 인사하는 판다 이미지 +
+
+ ); +}; + +export default BottomSection; diff --git a/src/pages/Main/components/BottomSection.module.css b/src/pages/Main/components/BottomSection.module.css new file mode 100644 index 000000000..3aaa8ef4f --- /dev/null +++ b/src/pages/Main/components/BottomSection.module.css @@ -0,0 +1,18 @@ +.container { + padding: 14.3rem 40.5rem 0; + background-color: #cfe5ff; +} + +.wrapper { + display: flex; + align-items: center; + justify-content: center; + gap: 6.9rem; +} + +.slogan { + padding-bottom: 6rem; + font-size: 4rem; + font-weight: 700; + line-height: 5.6rem; +} diff --git a/src/pages/Main/components/MainFooter.jsx b/src/pages/Main/components/MainFooter.jsx new file mode 100644 index 000000000..5d7118f1f --- /dev/null +++ b/src/pages/Main/components/MainFooter.jsx @@ -0,0 +1,38 @@ +import styles from "./MainFooter.module.css"; +import Icon from "../../../components/Icon/Icon"; +import clsx from "clsx"; + +const MainFooter = () => { + return ( +
+
+

@codeit - 2024

+ +
    +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
+
+
+ ); +}; + +export default MainFooter; diff --git a/src/pages/Main/components/MainFooter.module.css b/src/pages/Main/components/MainFooter.module.css new file mode 100644 index 000000000..1ae44a9ee --- /dev/null +++ b/src/pages/Main/components/MainFooter.module.css @@ -0,0 +1,30 @@ +.container { + height: 16rem; + padding: 3.2rem 40rem; + background-color: var(--color-secondary-900); +} + +.content { + display: flex; + justify-content: space-between; +} + +.policy-link { + display: flex; + gap: 30px; +} + +.text { + font-weight: 400; + font-size: 1.6rem; + color: var(--color-secondary-400); +} + +.text.link { + color: var(--color-secondary-200); +} + +.sns-list { + display: flex; + gap: 12px; +} diff --git a/src/pages/Main/components/MidSection.jsx b/src/pages/Main/components/MidSection.jsx new file mode 100644 index 000000000..f0ff331df --- /dev/null +++ b/src/pages/Main/components/MidSection.jsx @@ -0,0 +1,23 @@ +import clsx from "clsx"; +import styles from "./MidSection.module.css"; + +const MidSection = ({ src, category, title, content, opposite = false }) => { + return ( +
+
+ {`${src}이미지`} +
+

{category}

+

{title}

+

{content}

+
+
+
+ ); +}; + +export default MidSection; diff --git a/src/pages/Main/components/MidSection.module.css b/src/pages/Main/components/MidSection.module.css new file mode 100644 index 000000000..9db1c75b4 --- /dev/null +++ b/src/pages/Main/components/MidSection.module.css @@ -0,0 +1,50 @@ +.container { + background-color: #ffffff; +} + +.contentBox { + display: flex; + gap: 6.4rem; + width: 98.8rem; + margin: 13.8rem auto; + border-radius: 1.2rem; + background-color: #fcfcfc; +} + +.opposite { + order: 2; +} + +.textBox { + display: flex; + flex-direction: column; + gap: 1.2rem; + padding: 10rem 2.3rem 10rem 0; +} + +.oppositeText { + text-align: right; +} + +.category { + font-size: 1.8rem; + font-weight: 700; + line-height: 2.6rem; + color: var(--color-primary-100); +} + +.title { + font-size: 4rem; + font-weight: 700; + line-height: 5.6rem; + word-break: keep-all; + color: var(--color-secondary-700); +} + +.content { + font-size: 2.4rem; + font-weight: 500; + line-height: 3.2rem; + word-break: keep-all; + color: var(--color-secondary-700); +} diff --git a/src/pages/Main/components/TopSection.jsx b/src/pages/Main/components/TopSection.jsx new file mode 100644 index 000000000..b4a37d672 --- /dev/null +++ b/src/pages/Main/components/TopSection.jsx @@ -0,0 +1,25 @@ +import styles from "./TopSection.module.css"; +import HomeTopImg from "../../../assets/images/Img_home_top.svg"; +import LinkButton from "../../../components/Button/LinkButton"; +import { Link } from "react-router-dom"; + +const TopSection = () => { + return ( +
+
+
+

+ 일상의 모든 물건을 +
거래해 보세요 +

+ + 구경하러 가기 + +
+
+ 동산위의판다이미지 +
+ ); +}; + +export default TopSection; diff --git a/src/pages/Main/components/TopSection.module.css b/src/pages/Main/components/TopSection.module.css new file mode 100644 index 000000000..84ccddac5 --- /dev/null +++ b/src/pages/Main/components/TopSection.module.css @@ -0,0 +1,29 @@ +.container { + display: flex; + align-items: center; + justify-content: center; + padding: 20rem 40.5rem 0; + background-color: #cfe5ff; +} + +.content { + display: flex; + gap: 7px; +} + +.box { + display: flex; + flex-direction: column; + gap: 32px; + margin: 4rem 0; +} + +.text { + font-size: 4rem; + font-weight: 700; + line-height: 5.6rem; +} + +.btn { + margin-bottom: 6rem; +} diff --git a/src/pages/MarketPage/MarketPage.css b/src/pages/MarketPage/MarketPage.css index f3a121dd3..7a447766e 100644 --- a/src/pages/MarketPage/MarketPage.css +++ b/src/pages/MarketPage/MarketPage.css @@ -5,40 +5,6 @@ width: 100%; } -.bestItemContainer { - margin: 24px auto; - max-width: 1200px; - - @media (max-width: 1280px) { - margin: 24px; - } - - @media (max-width: 768px) { - margin: 16px; - } -} - -.sectionTitle { - margin-bottom: 16px; - font-size: 20px; - font-weight: 700; - color: #111827; -} - -.bestItemList { - display: grid; - grid-template-columns: repeat(4, 1fr); - gap: 16px; - - @media (max-width: 1280px) { - grid-template-columns: repeat(2, 1fr); - } - - @media (max-width: 768px) { - grid-template-columns: repeat(1, 1fr); - } -} - .allItemContainer { max-width: 1200px; diff --git a/src/pages/MarketPage/MarketPage.jsx b/src/pages/MarketPage/MarketPage.jsx index 6a73bcbb2..e9332a462 100644 --- a/src/pages/MarketPage/MarketPage.jsx +++ b/src/pages/MarketPage/MarketPage.jsx @@ -1,14 +1,17 @@ import "./MarketPage.css"; import BestItemSection from "./components/BestItemSection"; import AllItemSection from "./components/AllItemSection"; -import ItemDetailPage from "../ItemDetailPage/ItemDetailPage"; +import MarketHeader from "../../components/Header/MarketHeader"; +import { useParams } from "react-router-dom"; function MarketPage() { + useParams(); return (
- {/* - */} - + + + + {/* */}
); } diff --git a/src/pages/MarketPage/components/AllItemSection.jsx b/src/pages/MarketPage/components/AllItemSection.jsx index 7767ca910..879700f69 100644 --- a/src/pages/MarketPage/components/AllItemSection.jsx +++ b/src/pages/MarketPage/components/AllItemSection.jsx @@ -1,12 +1,9 @@ import { useEffect } from "react"; import { useState } from "react"; import { getProductList } from "../../../api/ItemApi"; -import ItemCard from "./ItemCard"; -import searchIcon from "../../../images/ic_search.png"; -import dropdownIcon from "../../../images/ic_arrow_down.png"; -import { Link } from "react-router-dom"; -import PaginationBar from "../../../components/UI/PaginationBar"; -import SortSelect from "../../../components/UI/onSortSelect"; +import ItemCard from "../../../components/ItemCard/ItemCard.jsx"; +import PaginationBar from "../../../components/Pagination/PaginationBar.jsx"; +import AllItemSectionHeader from "./AllItemSectionHeader.jsx"; const getPageSize = () => { const width = window.innerWidth; @@ -22,10 +19,10 @@ const getPageSize = () => { function AllItemSection() { const [itemList, setItemList] = useState([]); - const [orderBy, setOrderBy] = useState("recent"); + const [pageSize, setPageSize] = useState(getPageSize()); const [page, setPage] = useState(1); - const [dropdownToggle, setDropdownToggle] = useState(false); + const [totalPageNum, setTotalPageNum] = useState(); const loadProductList = async ({ orderBy, page, pageSize }) => { @@ -34,15 +31,6 @@ function AllItemSection() { setItemList(products.list); }; - const toggleDropdownWrapper = () => { - setDropdownToggle(!dropdownToggle); - }; - - const handleSortLoadList = (sortOption) => { - setOrderBy(sortOption); - setDropdownToggle(false); - }; - const onChangePage = (pageNumber) => { setPage(pageNumber); }; @@ -53,37 +41,23 @@ function AllItemSection() { }; window.addEventListener("resize", handleResize); - loadProductList({ orderBy, pageSize, page }); - }, [orderBy, pageSize, page]); + loadProductList({ pageSize, page }); + }, [pageSize, page]); return (
-
-

전체 상품

-
-
- 검색아이콘 - -
- - 상품 등록하기 - -
-
- 드롭다운아이콘 - -
- {dropdownToggle && } -
-
-
+
{itemList?.map((item) => ( ))}
- +
); diff --git a/src/pages/MarketPage/components/AllItemSection.module.css b/src/pages/MarketPage/components/AllItemSection.module.css new file mode 100644 index 000000000..6c4b4c639 --- /dev/null +++ b/src/pages/MarketPage/components/AllItemSection.module.css @@ -0,0 +1,31 @@ +.allItemContainer { + max-width: 120rem; + + @media (max-width: 1280px) { + margin: 2.4rem; + } + + @media (max-width: 768px) { + margin: 1.6rem; + } +} + +.allItemList { + display: grid; + grid-template-columns: repeat(5, 1fr); + grid-template-rows: repeat(2, 1fr); + gap: 24px; + + @media (max-width: 1280px) { + grid-template-columns: repeat(3, 1fr); + } + + @media (max-width: 768px) { + grid-template-columns: repeat(2, 1fr); + } +} + +.paginationBar { + margin: 0 auto; + width: 400px; +} diff --git a/src/pages/MarketPage/components/AllItemSectionHeader.jsx b/src/pages/MarketPage/components/AllItemSectionHeader.jsx new file mode 100644 index 000000000..9cae217f2 --- /dev/null +++ b/src/pages/MarketPage/components/AllItemSectionHeader.jsx @@ -0,0 +1,63 @@ +import styles from "./AllItemSectionHeader.module.css"; +import dropdownIcon from "../../../assets/images/ic_arrow_down.png"; +import { Link } from "react-router-dom"; +import { useState } from "react"; +import SortSelect from "../../../components/UI/onSortSelect"; +import Icon from "../../../components/Icon/Icon"; +import LinkButton from "../../../components/Button/LinkButton.jsx"; + +const AllItemSectionHeader = () => { + const [dropdownToggle, setDropdownToggle] = useState(false); + const [orderBy, setOrderBy] = useState("recent"); + + const toggleDropdownWrapper = () => { + setDropdownToggle(!dropdownToggle); + }; + + const handleSortLoadList = (sortOption) => { + setOrderBy(sortOption); + setDropdownToggle(false); + }; + + return ( +
+

전체 상품

+
+
+ + +
+ + 상품 등록하기 + +
+
+ + +
+ {dropdownToggle && ( + + )} +
+
+
+ ); +}; + +export default AllItemSectionHeader; diff --git a/src/pages/MarketPage/components/AllItemSectionHeader.module.css b/src/pages/MarketPage/components/AllItemSectionHeader.module.css new file mode 100644 index 000000000..a370319a8 --- /dev/null +++ b/src/pages/MarketPage/components/AllItemSectionHeader.module.css @@ -0,0 +1,91 @@ +.container { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 24px; +} + +.title { + font-weight: 700; + font-size: 2rem; + line-height: 3.2rem; + color: var(--color-secondary-900); +} + +.options { + display: flex; + align-items: center; + gap: 12px; +} + +.search-bar { + display: flex; + gap: 0.4rem; + position: relative; +} + +.search-icon { + position: absolute; + top: 0.9rem; + left: 1.6rem; +} + +.input { + width: 32.5rem; + height: 4.2rem; + padding: 0.9rem 4rem; + border: none; + border-radius: 12px; + background-color: #f3f4f6; + font-weight: 400; + font-size: 1.6rem; + line-height: 2.6rem; +} + +.input::placeholder { + font-weight: 400; + font-size: 1.6rem; + line-height: 2.6rem; + color: #9ca3af; +} + +.link { + padding: 1.2rem 2.3rem; +} + +.link:hover { + cursor: pointer; +} + +.dropdown { + width: 13rem; + height: 4.2rem; +} + +.sort-wrapper { + position: relative; + display: flex; + align-items: center; +} + +.dropdown-icon { + position: absolute; + right: 2rem; + top: 1.2rem; +} + +.showDropdownWrapperBtn { + padding: 12px 20px; + border: 1px solid #e5e7eb; + border-radius: 12px; + text-align: left; + font-size: 16px; + font-weight: 400; + color: #1f2937; + background-color: #ffffff; +} + +.dropdownIcon:hover { + background-color: #b5b3b3; + cursor: pointer; +} diff --git a/src/pages/MarketPage/components/BestItemSection.jsx b/src/pages/MarketPage/components/BestItemSection.jsx index 433e8a82a..7dc722898 100644 --- a/src/pages/MarketPage/components/BestItemSection.jsx +++ b/src/pages/MarketPage/components/BestItemSection.jsx @@ -1,8 +1,9 @@ +import styles from "./BestItemSection.module.css"; import { useEffect } from "react"; import { useState } from "react"; import { getProductList } from "../../../api/ItemApi"; import "../MarketPage"; -import ItemCard from "./ItemCard"; +import ItemCard from "../../../components/ItemCard/ItemCard.jsx"; const getPageSize = () => { const width = window.innerWidth; @@ -36,9 +37,9 @@ function BestItemSection() { }, [pageSize]); return ( -
-

베스트 상품

-
+
+

베스트 상품

+
{itemList?.map((item) => ( ))} diff --git a/src/pages/MarketPage/components/BestItemSection.module.css b/src/pages/MarketPage/components/BestItemSection.module.css new file mode 100644 index 000000000..aec4997ed --- /dev/null +++ b/src/pages/MarketPage/components/BestItemSection.module.css @@ -0,0 +1,33 @@ +.container { + margin: 24px auto; + max-width: 1200px; + + @media (max-width: 1280px) { + margin: 24px; + } + + @media (max-width: 768px) { + margin: 16px; + } +} + +.title { + margin-bottom: 16px; + font-size: 20px; + font-weight: 700; + color: #111827; +} + +.list { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 16px; + + @media (max-width: 1280px) { + grid-template-columns: repeat(2, 1fr); + } + + @media (max-width: 768px) { + grid-template-columns: repeat(1, 1fr); + } +} diff --git a/src/pages/MarketPage/components/ItemCard.css b/src/pages/MarketPage/components/ItemCard.css deleted file mode 100644 index c1871f963..000000000 --- a/src/pages/MarketPage/components/ItemCard.css +++ /dev/null @@ -1,50 +0,0 @@ -.itemCard { - overflow: hidden; -} - -.itemCardImg { - width: 100%; - height: auto; - margin-bottom: 16px; - object-fit: cover; - border-radius: 16px; - overflow: hidden; - aspect-ratio: 1; -} - -.itemContainer { - display: flex; - flex-direction: column; - gap: 6px; -} - -.itemName { - font-size: 14px; - font-weight: 500; - line-height: 24px; - color: #1f2937; -} - -.itemPrice { - font-size: 16px; - font-weight: 700; - line-height: 26px; - color: #1f2937; -} - -.iconContainer { - display: flex; - align-items: center; - gap: 4px; -} - -.heartIcon { - width: 16px; - height: 16px; -} - -.favoriteCount { - font-size: 12px; - font-weight: 500; - line-height: 18px; -} diff --git a/src/pages/SamplePage/SamplePage.jsx b/src/pages/SamplePage/SamplePage.jsx new file mode 100644 index 000000000..52f3656a3 --- /dev/null +++ b/src/pages/SamplePage/SamplePage.jsx @@ -0,0 +1,12 @@ +import styles from "./SamplePage.module.css"; +import ItemCard from "../../components/ItemCard/ItemCard"; + +const SamplePage = () => { + return ( +
+ +
+ ); +}; + +export default SamplePage; diff --git a/src/pages/SamplePage/SamplePage.module.css b/src/pages/SamplePage/SamplePage.module.css new file mode 100644 index 000000000..872a769a3 --- /dev/null +++ b/src/pages/SamplePage/SamplePage.module.css @@ -0,0 +1,6 @@ +.container { + display: flex; + flex-direction: column; + gap: 2rem; + padding: 15rem; +} diff --git a/src/styles/global.css b/src/styles/global.css deleted file mode 100644 index c8de1302d..000000000 --- a/src/styles/global.css +++ /dev/null @@ -1,9 +0,0 @@ -* { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -body { - font-family: "Pretendard", sans-serif; -}