diff --git a/package-lock.json b/package-lock.json index 722f503ad..22f62539f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,17 +8,23 @@ "name": "panda", "version": "0.1.0", "dependencies": { - "@react-hook/media-query": "^1.1.1", "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", + "@types/jest": "^29.5.12", + "@types/node": "^20.12.12", + "@types/react": "^18.3.3", + "@types/react-dom": "^18.3.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-responsive": "^10.0.0", "react-router-dom": "^6.22.3", "react-scripts": "5.0.1", - "recoil": "^0.7.7", + "typescript": "^4.5.5", "web-vitals": "^2.1.4" + }, + "devDependencies": { + "@babel/plugin-proposal-private-property-in-object": "^7.21.11" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -663,9 +669,17 @@ } }, "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, + "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" }, @@ -1908,6 +1922,17 @@ "@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==", + "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", @@ -3372,14 +3397,6 @@ } } }, - "node_modules/@react-hook/media-query": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@react-hook/media-query/-/media-query-1.1.1.tgz", - "integrity": "sha512-VM14wDOX5CW5Dn6b2lTiMd79BFMTut9AZj2+vIRT3LCKgMCYmdqruTtzDPSnIVDQdtxdPgtOzvU9oK20LopuOw==", - "peerDependencies": { - "react": ">=16.8" - } - }, "node_modules/@remix-run/router": { "version": "1.15.3", "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.3.tgz", @@ -4435,9 +4452,9 @@ "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==" }, "node_modules/@types/node": { - "version": "20.12.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.7.tgz", - "integrity": "sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==", + "version": "20.12.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.12.tgz", + "integrity": "sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==", "dependencies": { "undici-types": "~5.26.4" } @@ -4481,18 +4498,18 @@ "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==" }, "node_modules/@types/react": { - "version": "18.2.76", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.76.tgz", - "integrity": "sha512-T6z/v7YxpswDM61Vq5KoSPTJqCkroJfsDIsvXCr4+qOY6gik5Ju4w0jf67cpC5z7ydOnp/E0V0W08pDRy8u9Xw==", + "version": "18.3.3", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.3.tgz", + "integrity": "sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==", "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" } }, "node_modules/@types/react-dom": { - "version": "18.2.25", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.25.tgz", - "integrity": "sha512-o/V48vf4MQh7juIKZU2QGDfli6p1+OOi5oXx36Hffpc9adsHeXjVp8rHuPkjd8VT8sOJ2Zp05HR7CdpGTIUFUA==", + "version": "18.3.0", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.0.tgz", + "integrity": "sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==", "dependencies": { "@types/react": "*" } @@ -9131,11 +9148,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/hamt_plus": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/hamt_plus/-/hamt_plus-1.0.2.tgz", - "integrity": "sha512-t2JXKaehnMb9paaYA7J0BX8QQAY8lwfQ9Gjf4pg/mk4krt+cmwmU652HOoWonf+7+EQV97ARPMhhVgU1ra2GhA==" - }, "node_modules/handle-thing": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", @@ -15303,25 +15315,6 @@ "node": ">=8.10.0" } }, - "node_modules/recoil": { - "version": "0.7.7", - "resolved": "https://registry.npmjs.org/recoil/-/recoil-0.7.7.tgz", - "integrity": "sha512-8Og5KPQW9LwC577Vc7Ug2P0vQshkv1y3zG3tSSkWMqkWSwHmE+by06L8JtnGocjW6gcCvfwB3YtrJG6/tWivNQ==", - "dependencies": { - "hamt_plus": "1.0.2" - }, - "peerDependencies": { - "react": ">=16.13.1" - }, - "peerDependenciesMeta": { - "react-dom": { - "optional": true - }, - "react-native": { - "optional": true - } - } - }, "node_modules/recursive-readdir": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz", @@ -17298,7 +17291,6 @@ "version": "4.9.5", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" diff --git a/package.json b/package.json index d42c0959f..bcaa76b6b 100644 --- a/package.json +++ b/package.json @@ -6,11 +6,16 @@ "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", + "@types/jest": "^29.5.12", + "@types/node": "^20.12.12", + "@types/react": "^18.3.3", + "@types/react-dom": "^18.3.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-responsive": "^10.0.0", "react-router-dom": "^6.22.3", "react-scripts": "5.0.1", + "typescript": "^4.5.5", "web-vitals": "^2.1.4" }, "scripts": { @@ -36,5 +41,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 88a2c328f..63652cb6f 100644 --- a/src/App.js +++ b/src/App.js @@ -1,10 +1,11 @@ -import Router from "./Router"; -import React from "react"; - -export default function App() { - return ( - <> - - - ); +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const Router_1 = __importDefault(require("./Router")); +const react_1 = __importDefault(require("react")); +function App() { + return (react_1.default.createElement(react_1.default.Fragment, null, + react_1.default.createElement(Router_1.default, null))); } +exports.default = App; diff --git a/src/App.tsx b/src/App.tsx new file mode 100644 index 000000000..88a2c328f --- /dev/null +++ b/src/App.tsx @@ -0,0 +1,10 @@ +import Router from "./Router"; +import React from "react"; + +export default function App() { + return ( + <> + + + ); +} diff --git a/src/Router.js b/src/Router.js index 1d1069b4a..cd396b8fc 100644 --- a/src/Router.js +++ b/src/Router.js @@ -1,16 +1,26 @@ -import { Route, Routes } from "react-router-dom"; -import ItemPage from "./pages/items"; -import MarketPage from "./pages/market"; -import HomePage from "./pages/home"; -import ProductPage from "./pages/product"; - -export default function Router() { - return ( - - } /> - } /> - } /> - } /> - - ); +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const react_router_dom_1 = require("react-router-dom"); +const react_1 = __importDefault(require("react")); +const home_1 = __importDefault(require("./pages/home")); +const faq_1 = __importDefault(require("./pages/faq")); +const market_1 = __importDefault(require("./pages/market")); +const product_1 = __importDefault(require("./pages/product")); +const items_1 = __importDefault(require("./pages/items")); +const signin_1 = __importDefault(require("./pages/signin")); +const signup_1 = __importDefault(require("./pages/signup")); +const privacy_1 = __importDefault(require("./pages/privacy")); +function Router() { + return (react_1.default.createElement(react_router_dom_1.Routes, null, + react_1.default.createElement(react_router_dom_1.Route, { path: "/", element: react_1.default.createElement(home_1.default, null) }), + react_1.default.createElement(react_router_dom_1.Route, { path: "items", element: react_1.default.createElement(market_1.default, null) }), + react_1.default.createElement(react_router_dom_1.Route, { path: "items/:id", element: react_1.default.createElement(product_1.default, null) }), + react_1.default.createElement(react_router_dom_1.Route, { path: "additem", element: react_1.default.createElement(items_1.default, null) }), + react_1.default.createElement(react_router_dom_1.Route, { path: "signin", element: react_1.default.createElement(signin_1.default, null) }), + react_1.default.createElement(react_router_dom_1.Route, { path: "signup", element: react_1.default.createElement(signup_1.default, null) }), + react_1.default.createElement(react_router_dom_1.Route, { path: "privacy", element: react_1.default.createElement(privacy_1.default, null) }), + react_1.default.createElement(react_router_dom_1.Route, { path: "faq", element: react_1.default.createElement(faq_1.default, null) }))); } +exports.default = Router; diff --git a/src/Router.tsx b/src/Router.tsx new file mode 100644 index 000000000..6d0c5e8dc --- /dev/null +++ b/src/Router.tsx @@ -0,0 +1,25 @@ +import { Route, Routes } from "react-router-dom"; +import React from "react"; +import HomePage from "./pages/home"; +import Faq from "./pages/faq"; +import MarketPage from "./pages/market"; +import ProductPage from "./pages/product"; +import ItemPage from "./pages/items"; +import Signin from "./pages/signin"; +import Signup from "./pages/signup"; +import Privacy from "./pages/privacy"; + +export default function Router() { + return ( + + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + + ); +} diff --git a/src/assets/icon/eye-invisible.svg b/src/assets/icon/eye-invisible.svg new file mode 100644 index 000000000..92252b05d --- /dev/null +++ b/src/assets/icon/eye-invisible.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/assets/icon/eye-visible.svg b/src/assets/icon/eye-visible.svg new file mode 100644 index 000000000..35a75305e --- /dev/null +++ b/src/assets/icon/eye-visible.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/icon/google-logo.png b/src/assets/icon/google-logo.png new file mode 100644 index 000000000..199f3d628 Binary files /dev/null and b/src/assets/icon/google-logo.png differ diff --git a/src/assets/icon/ic_facebook.svg b/src/assets/icon/ic_facebook.svg new file mode 100644 index 000000000..8491c2f83 --- /dev/null +++ b/src/assets/icon/ic_facebook.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/icon/ic_instagram.svg b/src/assets/icon/ic_instagram.svg new file mode 100644 index 000000000..c83306f84 --- /dev/null +++ b/src/assets/icon/ic_instagram.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/icon/ic_twitter.svg b/src/assets/icon/ic_twitter.svg new file mode 100644 index 000000000..14a6069a1 --- /dev/null +++ b/src/assets/icon/ic_twitter.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/icon/ic_youtube.svg b/src/assets/icon/ic_youtube.svg new file mode 100644 index 000000000..631769017 --- /dev/null +++ b/src/assets/icon/ic_youtube.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/assets/icon/kakao-logo.png b/src/assets/icon/kakao-logo.png new file mode 100644 index 000000000..bfadc1d35 Binary files /dev/null and b/src/assets/icon/kakao-logo.png differ diff --git a/src/assets/img/img_home_01.svg b/src/assets/img/img_home_01.svg new file mode 100644 index 000000000..97d6c2247 --- /dev/null +++ b/src/assets/img/img_home_01.svg @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/img/img_home_02.svg b/src/assets/img/img_home_02.svg new file mode 100644 index 000000000..b9793783f --- /dev/null +++ b/src/assets/img/img_home_02.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/img/img_home_03.svg b/src/assets/img/img_home_03.svg new file mode 100644 index 000000000..0e39c3e5d --- /dev/null +++ b/src/assets/img/img_home_03.svg @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/img/img_home_bottom.svg b/src/assets/img/img_home_bottom.svg new file mode 100644 index 000000000..633b68836 --- /dev/null +++ b/src/assets/img/img_home_bottom.svg @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/img/img_home_top.svg b/src/assets/img/img_home_top.svg new file mode 100644 index 000000000..8ea14c075 --- /dev/null +++ b/src/assets/img/img_home_top.svg @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/img/panda_logo.svg b/src/assets/img/panda_logo.svg new file mode 100644 index 000000000..6a770738b --- /dev/null +++ b/src/assets/img/panda_logo.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/src/common/Button.js b/src/common/Button.js index 09f578c01..d94cd0afe 100644 --- a/src/common/Button.js +++ b/src/common/Button.js @@ -1,11 +1,12 @@ -import { Link } from "react-router-dom"; -import "../style/header.css"; -import React from "react"; - -export default function LinkButton({ children, to = "/" }) { - return ( - -
{children}
- - ); +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const react_1 = __importDefault(require("react")); +const react_router_dom_1 = require("react-router-dom"); +require("../style/header.css"); +function LinkButton({ children, to = "/" }) { + return (react_1.default.createElement(react_router_dom_1.Link, { to: to }, + react_1.default.createElement("div", { className: "button" }, children))); } +exports.default = LinkButton; diff --git a/src/common/Button.tsx b/src/common/Button.tsx new file mode 100644 index 000000000..1a93c5d1e --- /dev/null +++ b/src/common/Button.tsx @@ -0,0 +1,16 @@ +import React from "react"; +import { Link } from "react-router-dom"; +import "../style/header.css"; + +interface LinkButtonProps { + children: React.ReactNode; + to?: string; +} + +export default function LinkButton({ children, to = "/" }: LinkButtonProps) { + return ( + +
{children}
+ + ); +} diff --git a/src/components/API.js b/src/components/API.js deleted file mode 100644 index 8e2ff1a6d..000000000 --- a/src/components/API.js +++ /dev/null @@ -1,22 +0,0 @@ -export async function getMarketData({ page = 1, size = 10, order = "recent" }) { - const query = `page=${page}&pageSize=${size}&orderBy=${order}`; - const res = await fetch( - `https://panda-market-api.vercel.app/products?${query}` - ); - const body = await res.json(); - return body; -} - -export async function getProductData(id) { - const res = await fetch(`https://panda-market-api.vercel.app/products/${id}`); - const data = await res.json(); - return data; -} - -export async function getProductCommentData(id) { - const res = await fetch( - `https://panda-market-api.vercel.app/products/${id}/comments?limit=10` - ); - const data = await res.json(); - return data; -} diff --git a/src/components/API/API.js b/src/components/API/API.js new file mode 100644 index 000000000..250bd50b9 --- /dev/null +++ b/src/components/API/API.js @@ -0,0 +1,36 @@ +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.getProductCommentData = exports.getProductData = exports.getMarketData = void 0; +function getMarketData(_a) { + return __awaiter(this, arguments, void 0, function* ({ page = 1, size = 10, order = "recent" }) { + const query = `page=${page}&pageSize=${size}&orderBy=${order}`; + const res = yield fetch(`https://panda-market-api.vercel.app/products?${query}`); + const body = yield res.json(); + return body; + }); +} +exports.getMarketData = getMarketData; +function getProductData(id) { + return __awaiter(this, void 0, void 0, function* () { + const res = yield fetch(`https://panda-market-api.vercel.app/products/${id}`); + const data = yield res.json(); + return data; + }); +} +exports.getProductData = getProductData; +function getProductCommentData(id) { + return __awaiter(this, void 0, void 0, function* () { + const res = yield fetch(`https://panda-market-api.vercel.app/products/${id}/comments?limit=10`); + const data = yield res.json(); + return data; + }); +} +exports.getProductCommentData = getProductCommentData; diff --git a/src/components/API/API.ts b/src/components/API/API.ts new file mode 100644 index 000000000..35ba81d87 --- /dev/null +++ b/src/components/API/API.ts @@ -0,0 +1,67 @@ +interface MarketData { + id: number; + name: string; + description: string; + price: number; + tags: string[]; + images: string[]; + ownerId: number; + favoriteCount: number; + createdAt: string; + updatedAt: string; +} + +interface MarketResponse { + list: MarketData[]; + totalCount: number; +} + +export interface ProductData { + id: number; + name: string; + description: string; + price: number; + tags: string[]; + images: string[]; + ownerId: number; + favoriteCount: number; + createdAt: string; + updatedAt: string; + isFavorite: boolean; +} + +export interface CommentData { + id: number; + content: string; + createdAt: string; + updatedAt: string; + writer: { + id: number; + nickname: string; + image: string; + }; +} + +export interface CommentResponse { + list: CommentData[]; + nextCursor: string | null; +} + +export async function getMarketData({ page = 1, size = 10, order = "recent" }: { page?: number; size?: number; order?: string }): Promise { + const query = `page=${page}&pageSize=${size}&orderBy=${order}`; + const res = await fetch(`https://panda-market-api.vercel.app/products?${query}`); + const body = await res.json(); + return body as MarketResponse; +} + +export async function getProductData(id: number): Promise { + const res = await fetch(`https://panda-market-api.vercel.app/products/${id}`); + const data = await res.json(); + return data as ProductData; +} + +export async function getProductCommentData(id: number): Promise { + const res = await fetch(`https://panda-market-api.vercel.app/products/${id}/comments?limit=10`); + const data = await res.json(); + return data as CommentResponse; +} \ No newline at end of file diff --git a/src/components/AddItems/AddItem.js b/src/components/AddItems/AddItem.js new file mode 100644 index 000000000..c199204cb --- /dev/null +++ b/src/components/AddItems/AddItem.js @@ -0,0 +1,149 @@ +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +require("../../style/additem.css"); +const react_1 = __importStar(require("react")); +const ic_x_gray_svg_1 = __importDefault(require("../../assets/icon/ic_x_gray.svg")); +const ic_x_blue_svg_1 = __importDefault(require("../../assets/icon/ic_x_blue.svg")); +function AddItem() { + const [imageSrc, setImageSrc] = (0, react_1.useState)(""); + const [isAllInputFilled, setIsAllInputFilled] = (0, react_1.useState)(false); + const [inputPrice, setInputPrice] = (0, react_1.useState)(""); + const [inputName, setInputName] = (0, react_1.useState)(""); + const [inputDes, setInputDes] = (0, react_1.useState)(""); + const [tags, setTags] = (0, react_1.useState)([]); + const [inputTag, setInputTag] = (0, react_1.useState)(""); + const [imageHovered, setImageHovered] = (0, react_1.useState)(false); + const [tagHovered, setTagHovered] = (0, react_1.useState)(Array(10).fill(false)); + const handleImageChange = (e) => { + var _a; + const file = (_a = e.target.files) === null || _a === void 0 ? void 0 : _a[0]; + if (file) { + const reader = new FileReader(); + reader.onload = (event) => { + var _a; + const result = (_a = event.target) === null || _a === void 0 ? void 0 : _a.result; + if (result) { + setImageSrc(result.toString()); + } + }; + reader.readAsDataURL(file); + } + }; + function Commas(n) { + return n.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); + } + const handlePriceChange = (e) => { + const price = e.target.value.replace(/\D/g, ""); + const formattedPrice = Commas(price); + setInputPrice(formattedPrice); + handleInputChange(); + }; + const handleInputChange = (0, react_1.useCallback)(() => { + const isFilled = inputName.trim() !== "" && + inputDes.trim() !== "" && + inputPrice.trim() !== "" && + tags.length > 0; + setIsAllInputFilled(isFilled); + }, [inputName, inputDes, inputPrice, tags]); + (0, react_1.useEffect)(() => { + handleInputChange(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [tags]); + const handleNameChange = (e) => { + setInputName(e.target.value); + handleInputChange(); + }; + const handleDesChange = (e) => { + setInputDes(e.target.value); + handleInputChange(); + }; + const handleTagInputKeyDown = (e) => { + if (e.key === "Enter" && inputTag.trim() !== "") { + const newTag = inputTag.trim(); + if (!tags.includes(newTag)) { + setTags([...tags, newTag]); + setInputTag(""); + handleInputChange(); + } + else { + alert("이미 추가된 태그입니다."); + } + } + }; + const handleTagDelete = (index) => { + const updatedTags = [...tags]; + updatedTags.splice(index, 1); + setTags(updatedTags); + handleInputChange(); + }; + const handleImageDelete = () => { + setImageSrc(""); + }; + return (react_1.default.createElement(react_1.default.Fragment, null, + react_1.default.createElement("div", { className: "add-header flexrow margin-bottom10" }, + react_1.default.createElement("p", null, "\uC0C1\uD488 \uB4F1\uB85D\uD558\uAE30"), + react_1.default.createElement("button", { className: isAllInputFilled ? "filled" : "" }, "\uB4F1\uB85D")), + react_1.default.createElement("div", { className: "flexcolumn margin-bottom10" }, + react_1.default.createElement("div", null, + react_1.default.createElement("p", null, "\uC0C1\uD488 \uC774\uBBF8\uC9C0"), + react_1.default.createElement("div", { className: "flexrow" }, + react_1.default.createElement("div", { className: "add-img" }, + react_1.default.createElement("label", { className: "input-img-btn", htmlFor: "imageInput" }, + react_1.default.createElement("span", { className: "input-img-text" }, "+"), + react_1.default.createElement("br", null), + react_1.default.createElement("span", null, "\uC774\uBBF8\uC9C0 \uB4F1\uB85D"), + react_1.default.createElement("input", { id: "imageInput", type: "file", accept: "image/*", style: { display: "none" }, onChange: handleImageChange }))), + react_1.default.createElement("div", null, + react_1.default.createElement("div", { className: "input-img-btn" }, imageSrc && (react_1.default.createElement(react_1.default.Fragment, null, + react_1.default.createElement("img", { src: imageSrc, className: "handleImage", alt: "add img" }), + react_1.default.createElement("img", { src: imageHovered ? ic_x_blue_svg_1.default : ic_x_gray_svg_1.default, alt: "delete img", className: "delete-btn-img", onClick: handleImageDelete, onMouseEnter: () => setImageHovered(true), onMouseLeave: () => setImageHovered(false) })))))))), + react_1.default.createElement("div", { className: "add-name flexcolumn margin-bottom10" }, + react_1.default.createElement("p", null, "\uC0C1\uD488\uBA85"), + react_1.default.createElement("input", { type: "text", value: inputName, placeholder: "\uC0C1\uD488\uBA85\uC744 \uC785\uB825\uD574\uC8FC\uC138\uC694.", onChange: handleNameChange })), + react_1.default.createElement("div", { className: "add-text flexcolumn margin-bottom10" }, + react_1.default.createElement("p", null, "\uC0C1\uD488 \uC18C\uAC1C"), + react_1.default.createElement("textarea", { className: "add-des", value: inputDes, placeholder: "\uC0C1\uD488 \uC18C\uAC1C\uB97C \uC785\uB825\uD574\uC8FC\uC138\uC694.", onChange: handleDesChange })), + react_1.default.createElement("div", { className: "add-price flexcolumn margin-bottom10" }, + react_1.default.createElement("p", null, "\uD310\uB9E4\uAC00\uACA9"), + react_1.default.createElement("input", { value: inputPrice, placeholder: "\uD310\uB9E4 \uAC00\uACA9\uC744 \uC785\uB825\uD574\uC8FC\uC138\uC694.", onChange: handlePriceChange })), + react_1.default.createElement("div", { className: "add-tag flexcolumn margin-bottom10" }, + react_1.default.createElement("p", null, "\uD0DC\uADF8"), + react_1.default.createElement("input", { placeholder: "\uD0DC\uADF8\uB97C \uC785\uB825\uD574\uC8FC\uC138\uC694.", value: inputTag, onChange: (e) => setInputTag(e.target.value), onKeyDown: handleTagInputKeyDown })), + react_1.default.createElement("div", { className: "tags flexrow" }, tags.map((tag, index) => (react_1.default.createElement("div", { key: index, className: "tag" }, + react_1.default.createElement("p", null, tag), + react_1.default.createElement("img", { src: tagHovered[index] ? ic_x_blue_svg_1.default : ic_x_gray_svg_1.default, alt: "delete img", className: "delete-btn-tag", onClick: () => handleTagDelete(index), onMouseEnter: () => { + let newTagHovered = [...tagHovered]; + newTagHovered[index] = true; + setTagHovered(newTagHovered); + }, onMouseLeave: () => { + let newTagHovered = [...tagHovered]; + newTagHovered[index] = false; + setTagHovered(newTagHovered); + } }))))))); +} +exports.default = AddItem; diff --git a/src/components/AddItem.js b/src/components/AddItems/AddItem.tsx similarity index 78% rename from src/components/AddItem.js rename to src/components/AddItems/AddItem.tsx index 3b4687370..3a898d875 100644 --- a/src/components/AddItem.js +++ b/src/components/AddItems/AddItem.tsx @@ -1,35 +1,38 @@ -import "../style/additem.css"; -import { useCallback, useEffect, useState } from "react"; -import ic_x_gray from "../assets/icon/ic_x_gray.svg"; -import ic_x_blue from "../assets/icon/ic_x_blue.svg"; +import "../../style/additem.css"; +import React, { useCallback, useEffect, useState } from "react"; +import ic_x_gray from "../../assets/icon/ic_x_gray.svg"; +import ic_x_blue from "../../assets/icon/ic_x_blue.svg"; export default function AddItem() { - const [imageSrc, setImageSrc] = useState(""); - const [isAllInputFilled, setIsAllInputFilled] = useState(false); - const [inputPrice, setInputPrice] = useState(""); - const [inputName, setInputName] = useState(""); - const [inputDes, setInputDes] = useState(""); - const [tags, setTags] = useState([]); - const [inputTag, setInputTag] = useState(""); - const [imageHovered, setImageHovered] = useState(false); - const [tagHovered, setTagHovered] = useState(Array(10).fill(false)); + const [imageSrc, setImageSrc] = useState(""); + const [isAllInputFilled, setIsAllInputFilled] = useState(false); + const [inputPrice, setInputPrice] = useState(""); + const [inputName, setInputName] = useState(""); + const [inputDes, setInputDes] = useState(""); + const [tags, setTags] = useState([]); + const [inputTag, setInputTag] = useState(""); + const [imageHovered, setImageHovered] = useState(false); + const [tagHovered, setTagHovered] = useState(Array(10).fill(false)); - const handleImageChange = (e) => { - const file = e.target.files[0]; + const handleImageChange = (e: React.ChangeEvent) => { + const file = e.target.files?.[0]; if (file) { const reader = new FileReader(); reader.onload = (event) => { - setImageSrc(event.target.result); + const result = event.target?.result; + if (result) { + setImageSrc(result.toString()); + } }; reader.readAsDataURL(file); } }; - function Commas(n) { + function Commas(n: string) { return n.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); } - const handlePriceChange = (e) => { + const handlePriceChange = (e: React.ChangeEvent) => { const price = e.target.value.replace(/\D/g, ""); const formattedPrice = Commas(price); setInputPrice(formattedPrice); @@ -50,17 +53,17 @@ export default function AddItem() { // eslint-disable-next-line react-hooks/exhaustive-deps }, [tags]); - const handleNameChange = (e) => { + const handleNameChange = (e: React.ChangeEvent) => { setInputName(e.target.value); handleInputChange(); }; - const handleDesChange = (e) => { + const handleDesChange = (e: React.ChangeEvent) => { setInputDes(e.target.value); handleInputChange(); }; - const handleTagInputKeyDown = (e) => { + const handleTagInputKeyDown = (e: React.KeyboardEvent) => { if (e.key === "Enter" && inputTag.trim() !== "") { const newTag = inputTag.trim(); if (!tags.includes(newTag)) { @@ -73,7 +76,7 @@ export default function AddItem() { } }; - const handleTagDelete = (index) => { + const handleTagDelete = (index: number) => { const updatedTags = [...tags]; updatedTags.splice(index, 1); setTags(updatedTags); diff --git a/src/components/Header.tsx b/src/components/Header.tsx new file mode 100644 index 000000000..1185aa35c --- /dev/null +++ b/src/components/Header.tsx @@ -0,0 +1,35 @@ +import React from "react"; +import MainLogo from "../assets/icon/main_logo.svg"; +import SmallMainLogo from "../assets/icon/main_logo_small.svg"; +import { Link, useLocation } from "react-router-dom"; +import "../style/header.css"; +import LinkButton from "../common/Button"; + +export default function NavBar(): JSX.Element { + const location = useLocation(); + return ( + + ); +} diff --git a/src/components/Home/BannerBottom.js b/src/components/Home/BannerBottom.js new file mode 100644 index 000000000..4989652a6 --- /dev/null +++ b/src/components/Home/BannerBottom.js @@ -0,0 +1,14 @@ +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const react_1 = __importDefault(require("react")); +const BannerBottom = () => { + return (react_1.default.createElement("section", { className: "banner-bottom" }, + react_1.default.createElement("div", null, + react_1.default.createElement("h1", null, + "\uBBFF\uC744 \uC218 \uC788\uB294", + react_1.default.createElement("br", null), + "\uD310\uB2E4\uB9C8\uCF13 \uC911\uACE0\uAC70\uB798")))); +}; +exports.default = BannerBottom; diff --git a/src/components/Home/BannerBottom.tsx b/src/components/Home/BannerBottom.tsx new file mode 100644 index 000000000..5698b9cba --- /dev/null +++ b/src/components/Home/BannerBottom.tsx @@ -0,0 +1,13 @@ +import React from 'react'; + +const BannerBottom: React.FC = () => { + return ( +
+
+

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

+
+
+ ); +}; + +export default BannerBottom; diff --git a/src/components/Home/BannerMiddle.js b/src/components/Home/BannerMiddle.js new file mode 100644 index 000000000..71ce6fb46 --- /dev/null +++ b/src/components/Home/BannerMiddle.js @@ -0,0 +1,51 @@ +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const react_1 = __importDefault(require("react")); +const img_home_01_svg_1 = __importDefault(require("../../assets/img/img_home_01.svg")); +const img_home_02_svg_1 = __importDefault(require("../../assets/img/img_home_02.svg")); +const img_home_03_svg_1 = __importDefault(require("../../assets/img/img_home_03.svg")); +const BannerMiddle = () => { + return (react_1.default.createElement("section", { className: "banner-middle" }, + react_1.default.createElement("div", { className: "item" }, + react_1.default.createElement("img", { src: img_home_01_svg_1.default, alt: "\uC544\uC774\uD15C1" }), + react_1.default.createElement("div", { className: "item-text" }, + react_1.default.createElement("h2", { className: "title" }, "Hot Item"), + react_1.default.createElement("h3", { className: "subtitle" }, + "\uC778\uAE30 \uC0C1\uD488\uC744 ", + react_1.default.createElement("span", { className: "break-on-desktop" }, + react_1.default.createElement("br", null)), + "\uD655\uC778\uD574\uBCF4\uC138\uC694"), + react_1.default.createElement("p", { className: "description" }, + "\uAC00\uC7A5 HOT\uD55C \uC911\uACE0\uAC70\uB798 \uBB3C\uD488\uC744", + react_1.default.createElement("br", null), + "\uD310\uB2E4 \uB9C8\uCF13\uC5D0\uC11C \uD655\uC778\uD574 \uBCF4\uC138\uC694"))), + react_1.default.createElement("div", { className: "item" }, + react_1.default.createElement("img", { src: img_home_02_svg_1.default, alt: "\uC544\uC774\uD15C2" }), + react_1.default.createElement("div", { className: "item-text right" }, + react_1.default.createElement("h2", { className: "title" }, "Search"), + react_1.default.createElement("h3", { className: "subtitle" }, + "\uAD6C\uB9E4\uB97C \uC6D0\uD558\uB294 ", + react_1.default.createElement("span", { className: "break-on-desktop" }, + react_1.default.createElement("br", null)), + "\uC0C1\uD488\uC744 \uAC80\uC0C9\uD558\uC138\uC694"), + react_1.default.createElement("p", { className: "description" }, + "\uAD6C\uB9E4\uD558\uACE0 \uC2F6\uC740 \uBB3C\uD488\uC740 \uAC80\uC0C9\uD574\uC11C", + react_1.default.createElement("br", null), + "\uC27D\uAC8C \uCC3E\uC544\uBCF4\uC138\uC694"))), + react_1.default.createElement("div", { className: "item" }, + react_1.default.createElement("img", { src: img_home_03_svg_1.default, alt: "\uC544\uC774\uD15C3" }), + react_1.default.createElement("div", { className: "item-text" }, + react_1.default.createElement("h2", { className: "title" }, "Register"), + react_1.default.createElement("h3", { className: "subtitle" }, + "\uD310\uB9E4\uB97C \uC6D0\uD558\uB294 ", + react_1.default.createElement("span", { className: "break-on-desktop" }, + react_1.default.createElement("br", null)), + "\uC0C1\uD488\uC744 \uB4F1\uB85D\uD558\uC138\uC694"), + react_1.default.createElement("p", { className: "description" }, + "\uC5B4\uB5A4 \uBB3C\uAC74\uC774\uB4E0 \uD310\uB9E4\uD558\uACE0 \uC2F6\uC740 \uC0C1\uD488\uC744", + react_1.default.createElement("br", null), + "\uC27D\uAC8C \uB4F1\uB85D\uD558\uC138\uC694"))))); +}; +exports.default = BannerMiddle; diff --git a/src/components/Home/BannerMiddle.tsx b/src/components/Home/BannerMiddle.tsx new file mode 100644 index 000000000..e29952d6c --- /dev/null +++ b/src/components/Home/BannerMiddle.tsx @@ -0,0 +1,37 @@ +import React from 'react'; +import img1 from "../../assets/img/img_home_01.svg"; +import img2 from "../../assets/img/img_home_02.svg"; +import img3 from "../../assets/img/img_home_03.svg"; + +const BannerMiddle: React.FC = () => { + return ( +
+
+ 아이템1 +
+

Hot Item

+

인기 상품을
확인해보세요

+

가장 HOT한 중고거래 물품을
판다 마켓에서 확인해 보세요

+
+
+
+ 아이템2 +
+

Search

+

구매를 원하는
상품을 검색하세요

+

구매하고 싶은 물품은 검색해서
쉽게 찾아보세요

+
+
+
+ 아이템3 +
+

Register

+

판매를 원하는
상품을 등록하세요

+

어떤 물건이든 판매하고 싶은 상품을
쉽게 등록하세요

+
+
+
+ ); +}; + +export default BannerMiddle; \ No newline at end of file diff --git a/src/components/Home/BannerTop.js b/src/components/Home/BannerTop.js new file mode 100644 index 000000000..8600bb39d --- /dev/null +++ b/src/components/Home/BannerTop.js @@ -0,0 +1,16 @@ +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const react_1 = __importDefault(require("react")); +const react_router_dom_1 = require("react-router-dom"); +const BannerTop = () => { + return (react_1.default.createElement("section", { className: "banner-top" }, + react_1.default.createElement("div", null, + react_1.default.createElement("h1", null, + "\uC77C\uC0C1\uC758 \uBAA8\uB4E0 \uBB3C\uAC74\uC744 ", + react_1.default.createElement("br", null), + "\uAC70\uB798\uD574\uBCF4\uC138\uC694"), + react_1.default.createElement(react_router_dom_1.Link, { to: "/items", className: "btn-items btn" }, "\uAD6C\uACBD\uD558\uB7EC\uAC00\uAE30")))); +}; +exports.default = BannerTop; diff --git a/src/components/Home/BannerTop.tsx b/src/components/Home/BannerTop.tsx new file mode 100644 index 000000000..5be1f6b2b --- /dev/null +++ b/src/components/Home/BannerTop.tsx @@ -0,0 +1,20 @@ +import React from "react"; +import { Link } from "react-router-dom"; + +const BannerTop:React.FC = () => { + return ( +
+
+

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

+ + 구경하러가기 + +
+
+ ); +} + +export default BannerTop; \ No newline at end of file diff --git a/src/components/Home/Footer.js b/src/components/Home/Footer.js new file mode 100644 index 000000000..26b64f98f --- /dev/null +++ b/src/components/Home/Footer.js @@ -0,0 +1,29 @@ +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const react_1 = __importDefault(require("react")); +const react_router_dom_1 = require("react-router-dom"); +const ic_facebook_svg_1 = __importDefault(require("../../assets/icon/ic_facebook.svg")); +const ic_twitter_svg_1 = __importDefault(require("../../assets/icon/ic_twitter.svg")); +const ic_youtube_svg_1 = __importDefault(require("../../assets/icon/ic_youtube.svg")); +const ic_instagram_svg_1 = __importDefault(require("../../assets/icon/ic_instagram.svg")); +const Footer = () => { + return (react_1.default.createElement("footer", null, + react_1.default.createElement("div", null, + react_1.default.createElement("p", { className: "codeit" }, "@codeit - 2024")), + react_1.default.createElement("div", { className: "FAQ" }, + react_1.default.createElement(react_router_dom_1.Link, { to: "/privacy" }, "Privacy Policy"), + "\u2003", + react_1.default.createElement(react_router_dom_1.Link, { to: "/faq" }, "FAQ")), + react_1.default.createElement("div", null, + react_1.default.createElement("a", { target: "_blank", rel: "noopener noreferrer", href: "https://www.facebook.com" }, + react_1.default.createElement("img", { src: ic_facebook_svg_1.default, alt: "facebook" })), + react_1.default.createElement("a", { target: "_blank", rel: "noopener noreferrer", href: "https://www.twitter.com" }, + react_1.default.createElement("img", { src: ic_twitter_svg_1.default, alt: "twitter" })), + react_1.default.createElement("a", { target: "_blank", rel: "noopener noreferrer", href: "https://www.youtube.com" }, + react_1.default.createElement("img", { src: ic_youtube_svg_1.default, alt: "youtube" })), + react_1.default.createElement("a", { target: "_blank", rel: "noopener noreferrer", href: "https://www.instagram.com/" }, + react_1.default.createElement("img", { src: ic_instagram_svg_1.default, alt: "instagram" }))))); +}; +exports.default = Footer; diff --git a/src/components/Home/Footer.tsx b/src/components/Home/Footer.tsx new file mode 100644 index 000000000..b939c86f0 --- /dev/null +++ b/src/components/Home/Footer.tsx @@ -0,0 +1,53 @@ +import React from "react"; +import { Link } from "react-router-dom"; +import fb from "../../assets/icon/ic_facebook.svg"; +import tw from "../../assets/icon/ic_twitter.svg"; +import yt from "../../assets/icon/ic_youtube.svg"; +import it from "../../assets/icon/ic_instagram.svg"; + +const Footer: React.FC = () => { + return ( + + ); +}; + +export default Footer; diff --git a/src/components/Market/AllMarket.js b/src/components/Market/AllMarket.js new file mode 100644 index 000000000..89010e99c --- /dev/null +++ b/src/components/Market/AllMarket.js @@ -0,0 +1,144 @@ +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const react_1 = __importStar(require("react")); +require("../../style/allMarket.css"); +const ic_heart_svg_1 = __importDefault(require("../../assets/icon/ic_heart.svg")); +const ic_arrow_left_svg_1 = __importDefault(require("../../assets/icon/ic_arrow_left.svg")); +const ic_arrow_right_svg_1 = __importDefault(require("../../assets/icon/ic_arrow_right.svg")); +const react_router_dom_1 = require("react-router-dom"); +const SelectBtn_1 = __importDefault(require("./SelectBtn")); +const API_1 = require("../API/API"); +const react_responsive_1 = require("react-responsive"); +const SearchInput_1 = require("./SearchInput"); +const Commas_1 = __importDefault(require("../../util/Commas")); +const Button_1 = __importDefault(require("../../common/Button")); +function AllMarket() { + const [data, setData] = (0, react_1.useState)([]); + const [order, setOrder] = (0, react_1.useState)("recent"); + const [currentPage, setCurrentPage] = (0, react_1.useState)(1); + const [totalPages, setTotalPages] = (0, react_1.useState)(1); + const isMobile = (0, react_responsive_1.useMediaQuery)({ + query: "(max-width: 767px)", + }); + const isTablet = (0, react_responsive_1.useMediaQuery)({ + query: "(min-width: 768px) and (max-width: 1279px)", + }); + (0, react_1.useEffect)(() => { + const fetchData = () => __awaiter(this, void 0, void 0, function* () { + try { + let pageSize = 10; + if (isMobile) + pageSize = 4; + else if (isTablet) + pageSize = 6; + const { list, totalCount } = yield (0, API_1.getMarketData)({ + page: currentPage, + size: pageSize, + order, + }); + setData(list); + setTotalPages(Math.ceil(totalCount / pageSize)); + } + catch (error) { + console.error(error); + } + }); + fetchData(); + }, [currentPage, order, isMobile, isTablet]); + const handleSortOrderChange = (selectedOrder) => { + setOrder(selectedOrder); + }; + const handlePageChange = (pageNumber) => { + if (pageNumber >= 1 && pageNumber <= totalPages) { + setCurrentPage(pageNumber); + } + }; + const handleHeaderChange = () => { + if (isMobile) { + return (react_1.default.createElement("div", { className: "all-header" }, + react_1.default.createElement("div", { className: "all-header-top" }, + react_1.default.createElement("div", null, + react_1.default.createElement("h1", { className: "all-title" }, "\uD310\uB9E4 \uC911\uC778 \uC0C1\uD488")), + react_1.default.createElement("div", { className: "button" }, + react_1.default.createElement(Button_1.default, { to: "/additem" }, "\uC0C1\uD488 \uB4F1\uB85D\uD558\uAE30"))), + react_1.default.createElement("div", { className: "all-search-input" }, + react_1.default.createElement("div", { className: "searchInput" }, + react_1.default.createElement(SearchInput_1.SearchInput, null)), + react_1.default.createElement("div", { className: "selectInput" }, + react_1.default.createElement(SelectBtn_1.default, { onChange: handleSortOrderChange }))))); + } + else { + return (react_1.default.createElement("div", { className: "all-header" }, + react_1.default.createElement("div", null, + react_1.default.createElement("h1", { className: "all-title" }, isTablet ? "판매 중인 상품" : "전체 상품")), + react_1.default.createElement("div", { className: "all-search-input" }, + react_1.default.createElement("div", { className: "searchInput" }, + react_1.default.createElement(SearchInput_1.SearchInput, null)), + react_1.default.createElement("div", { className: "button" }, + react_1.default.createElement(Button_1.default, { to: "/additem" }, "\uC0C1\uD488 \uB4F1\uB85D\uD558\uAE30")), + react_1.default.createElement("div", { className: "selectInput" }, + react_1.default.createElement(SelectBtn_1.default, { onChange: handleSortOrderChange }))))); + } + }; + const renderPageNumbers = () => { + const pageNumbers = []; + for (let i = 1; i <= totalPages; i++) { + pageNumbers.push(react_1.default.createElement("div", { key: i, onClick: () => handlePageChange(i), className: "pageBtn " + (currentPage === i ? "active" : "") }, i)); + } + return pageNumbers; + }; + return (react_1.default.createElement(react_1.default.Fragment, null, + react_1.default.createElement("div", { className: "all-market" }, + handleHeaderChange(), + data.length > 0 && (react_1.default.createElement("div", { className: "cards" }, data.map((item) => (react_1.default.createElement(react_router_dom_1.Link, { to: `/items/${item.id}`, key: item.id }, + react_1.default.createElement("div", { className: "card" }, + react_1.default.createElement("img", { className: "all-img", src: item.images[0], alt: item.name }), + react_1.default.createElement("p", { className: "all-name" }, item.name), + react_1.default.createElement("p", { className: "all-price" }, + (0, Commas_1.default)(item.price), + "\uC6D0"), + react_1.default.createElement("div", { className: "like" }, + react_1.default.createElement("img", { src: ic_heart_svg_1.default, alt: "Heart" }), + react_1.default.createElement("p", null, item.favoriteCount))))))))), + react_1.default.createElement("div", { className: "pagination" }, + react_1.default.createElement("div", { className: "pageBtn" }, + react_1.default.createElement("img", { src: ic_arrow_left_svg_1.default, onClick: () => handlePageChange(currentPage - 1), alt: "arrow_left" })), + renderPageNumbers(), + react_1.default.createElement("div", { className: "pageBtn" }, + react_1.default.createElement("img", { src: ic_arrow_right_svg_1.default, onClick: () => handlePageChange(currentPage + 1), alt: "arrow_right" }))))); +} +exports.default = AllMarket; diff --git a/src/components/AllMarket.js b/src/components/Market/AllMarket.tsx similarity index 87% rename from src/components/AllMarket.js rename to src/components/Market/AllMarket.tsx index 208ccf4ac..58a0b5918 100644 --- a/src/components/AllMarket.js +++ b/src/components/Market/AllMarket.tsx @@ -1,18 +1,26 @@ import React, { useEffect, useState } from "react"; -import "../style/allMarket.css"; -import LinkButton from "../common/Button"; -import HeartIcon from "../assets/icon/ic_heart.svg"; -import Arrow_left from "../assets/icon/ic_arrow_left.svg"; -import Arrow_right from "../assets/icon/ic_arrow_right.svg"; +import "../../style/allMarket.css"; +import HeartIcon from "../../assets/icon/ic_heart.svg"; +import Arrow_left from "../../assets/icon/ic_arrow_left.svg"; +import Arrow_right from "../../assets/icon/ic_arrow_right.svg"; import { Link } from "react-router-dom"; -import { SearchInput } from "./SearchInput"; -import Commas from "../util/Commas"; import SelectBtn from "./SelectBtn"; -import { getMarketData } from "./API"; +import { getMarketData } from "../API/API"; import { useMediaQuery } from "react-responsive"; +import { SearchInput } from "./SearchInput"; +import Commas from "../../util/Commas"; +import LinkButton from "../../common/Button"; + +interface Item { + id: number; + name: string; + images: string[]; + price: number; + favoriteCount: number; +} export default function AllMarket() { - const [data, setData] = useState([]); + const [data, setData] = useState([]); const [order, setOrder] = useState("recent"); const [currentPage, setCurrentPage] = useState(1); const [totalPages, setTotalPages] = useState(1); @@ -45,11 +53,11 @@ export default function AllMarket() { fetchData(); }, [currentPage, order, isMobile, isTablet]); - const handleSortOrderChange = (selectedOrder) => { + const handleSortOrderChange = (selectedOrder: string) => { setOrder(selectedOrder); }; - const handlePageChange = (pageNumber) => { + const handlePageChange = (pageNumber: number) => { if (pageNumber >= 1 && pageNumber <= totalPages) { setCurrentPage(pageNumber); } @@ -148,7 +156,6 @@ export default function AllMarket() { handlePageChange(currentPage - 1)} - disabled={currentPage === 1} alt="arrow_left" /> @@ -157,7 +164,6 @@ export default function AllMarket() { handlePageChange(currentPage + 1)} - disabled={currentPage === totalPages} alt="arrow_right" /> diff --git a/src/components/Market/BestMarket.js b/src/components/Market/BestMarket.js new file mode 100644 index 000000000..66916ec40 --- /dev/null +++ b/src/components/Market/BestMarket.js @@ -0,0 +1,82 @@ +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const react_1 = __importStar(require("react")); +const react_router_dom_1 = require("react-router-dom"); +require("../../style/bestMarket.css"); +const ic_heart_svg_1 = __importDefault(require("../../assets/icon/ic_heart.svg")); +const API_1 = require("../API/API"); +const react_responsive_1 = require("react-responsive"); +const Commas_1 = __importDefault(require("../../util/Commas")); +function BestMarket() { + const [bestData, setBestData] = (0, react_1.useState)([]); + const isMobile = (0, react_responsive_1.useMediaQuery)({ + query: "(max-width: 767px)", + }); + const isTablet = (0, react_responsive_1.useMediaQuery)({ + query: "(min-width: 768px) and (max-width: 1279px)", + }); + (0, react_1.useEffect)(() => { + const fetchBestData = () => __awaiter(this, void 0, void 0, function* () { + try { + let size = 4; // 기본적으로 4개의 데이터를 가져옴 + if (isMobile) + size = 1; // 모바일일 때는 1개의 데이터만 가져옴 + else if (isTablet) + size = 2; // 태블릿일 때는 2개의 데이터를 가져옴 + const data = yield (0, API_1.getMarketData)({ page: 1, size, order: "favorite" }); + setBestData(data.list); + } + catch (error) { + console.error(error); + } + }); + fetchBestData(); + }, [isMobile, isTablet]); + return (react_1.default.createElement("div", { className: "best-market" }, + react_1.default.createElement("h1", { className: "best-title" }, "\uBCA0\uC2A4\uD2B8 \uC0C1\uD488"), + react_1.default.createElement("div", { className: "cards" }, bestData.map((item) => (react_1.default.createElement(react_router_dom_1.Link, { to: `/items/${item.id}`, key: item.id }, + react_1.default.createElement("div", { className: "card" }, + react_1.default.createElement("img", { className: "best-img", src: item.images[0], alt: item.name }), + react_1.default.createElement("p", { className: "best-name" }, item.name), + react_1.default.createElement("p", { className: "best-price" }, + (0, Commas_1.default)(item.price), + "\uC6D0"), + react_1.default.createElement("div", { className: "like" }, + react_1.default.createElement("img", { src: ic_heart_svg_1.default, alt: "Heart" }), + react_1.default.createElement("p", null, item.favoriteCount))))))))); +} +exports.default = BestMarket; diff --git a/src/components/BestMarket.js b/src/components/Market/BestMarket.tsx similarity index 79% rename from src/components/BestMarket.js rename to src/components/Market/BestMarket.tsx index 350ea0104..a0e1465fb 100644 --- a/src/components/BestMarket.js +++ b/src/components/Market/BestMarket.tsx @@ -1,13 +1,21 @@ -import { useState, useEffect } from "react"; +import React, { useState, useEffect } from "react"; import { Link } from "react-router-dom"; -import "../style/bestMarket.css"; -import Commas from "../util/Commas"; -import HeartIcon from "../assets/icon/ic_heart.svg"; -import { getMarketData } from "./API"; +import "../../style/bestMarket.css"; +import HeartIcon from "../../assets/icon/ic_heart.svg"; +import { getMarketData } from "../API/API"; import { useMediaQuery } from "react-responsive"; +import Commas from "../../util/Commas"; + +interface MarketItem { + id: number; + name: string; + images: string[]; + price: number; + favoriteCount: number; +} export default function BestMarket() { - const [bestData, setBestData] = useState([]); + const [bestData, setBestData] = useState([]); const isMobile = useMediaQuery({ query: "(max-width: 767px)", }); diff --git a/src/components/Market/SearchInput.js b/src/components/Market/SearchInput.js new file mode 100644 index 000000000..c54428d8a --- /dev/null +++ b/src/components/Market/SearchInput.js @@ -0,0 +1,14 @@ +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.SearchInput = void 0; +const react_1 = __importDefault(require("react")); +const ic_search_svg_1 = __importDefault(require("../../assets/icon/ic_search.svg")); +require("../../style/SearchInput.css"); +function SearchInput() { + return (react_1.default.createElement("div", { className: "search" }, + react_1.default.createElement("img", { src: ic_search_svg_1.default, alt: "\uAC80\uC0C9" }), + react_1.default.createElement("input", { placeholder: "\uAC80\uC0C9\uD560 \uC0C1\uD488\uC744 \uC785\uB825\uD574\uC8FC\uC138\uC694." }))); +} +exports.SearchInput = SearchInput; diff --git a/src/components/SearchInput.js b/src/components/Market/SearchInput.tsx similarity index 50% rename from src/components/SearchInput.js rename to src/components/Market/SearchInput.tsx index 06236d8bf..da96b5dcf 100644 --- a/src/components/SearchInput.js +++ b/src/components/Market/SearchInput.tsx @@ -1,7 +1,8 @@ -import SearchIcon from "../assets/icon/ic_search.svg"; -import "../style/SearchInput.css"; +import React from "react"; +import SearchIcon from "../../assets/icon/ic_search.svg"; +import "../../style/SearchInput.css"; -export function SearchInput() { +export function SearchInput(): JSX.Element { return (
검색 diff --git a/src/components/Market/SelectBtn.js b/src/components/Market/SelectBtn.js new file mode 100644 index 000000000..e4c46bf86 --- /dev/null +++ b/src/components/Market/SelectBtn.js @@ -0,0 +1,79 @@ +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const react_1 = __importStar(require("react")); +require("../../style/SelectBtn.css"); +const ic_arrow_down_svg_1 = __importDefault(require("../../assets/icon/ic_arrow_down.svg")); +const ic_sort_svg_1 = __importDefault(require("../../assets/icon/ic_sort.svg")); +const react_responsive_1 = require("react-responsive"); +const SelectBtn = ({ onChange }) => { + const [isDropdownOpen, setDropdownOpen] = (0, react_1.useState)(false); + const [order, setOrder] = (0, react_1.useState)("recent"); + const ORDER_KR = { + recent: "최신순", + favorite: "좋아요순", + }; + const isMobile = (0, react_responsive_1.useMediaQuery)({ + query: "(max-width: 767px)", + }); + const handleMobileChange = () => { + if (isMobile) + return react_1.default.createElement("img", { className: "img-arrow", src: ic_sort_svg_1.default, alt: "arrow-down" }); + else + return (react_1.default.createElement(react_1.default.Fragment, null, + react_1.default.createElement("span", null, ORDER_KR[order]), + react_1.default.createElement("img", { src: ic_arrow_down_svg_1.default, alt: "arrow-down" }))); + }; + const dropdownRef = (0, react_1.useRef)(null); + const toggleDropdown = () => { + setDropdownOpen((prev) => !prev); + }; + // 정렬 옵션 선택시 호출되는 함수 + const handleOrderChange = (order) => { + setOrder(order); // 상태 업데이트 + onChange(order); // 부모 컴포넌트로 선택된 정렬 순서 전달 + setDropdownOpen(false); // 드롭다운 닫기 + }; + (0, react_1.useEffect)(() => { + const handleClickOutside = (e) => { + if (dropdownRef.current && !dropdownRef.current.contains(e.target)) { + setDropdownOpen(false); + } + }; + document.addEventListener("mousedown", handleClickOutside); + return () => { + document.removeEventListener("mousedown", handleClickOutside); + }; + }, []); + return (react_1.default.createElement("div", { className: "dropdown", ref: dropdownRef }, + react_1.default.createElement("div", { className: "dropdown_button", onClick: toggleDropdown }, + react_1.default.createElement("div", { className: "select" }, handleMobileChange())), + isDropdownOpen && (react_1.default.createElement("ul", { className: "dropdown_contents" }, + react_1.default.createElement("li", { onClick: () => handleOrderChange("recent") }, "\uCD5C\uC2E0\uC21C"), + react_1.default.createElement("li", { onClick: () => handleOrderChange("favorite") }, "\uC88B\uC544\uC694\uC21C"))))); +}; +exports.default = SelectBtn; diff --git a/src/components/SelectBtn.js b/src/components/Market/SelectBtn.tsx similarity index 73% rename from src/components/SelectBtn.js rename to src/components/Market/SelectBtn.tsx index 657da1f7f..ca285cf4e 100644 --- a/src/components/SelectBtn.js +++ b/src/components/Market/SelectBtn.tsx @@ -1,16 +1,21 @@ -import { useState, useEffect, useRef } from "react"; -import "../style/SelectBtn.css"; -import ArrowDownIcon from "../assets/icon/ic_arrow_down.svg"; -import sortBtn from "../assets/icon/ic_sort.svg"; +import React, { FC, useState, useEffect, useRef } from "react"; +import "../../style/SelectBtn.css"; +import ArrowDownIcon from "../../assets/icon/ic_arrow_down.svg"; +import sortBtn from "../../assets/icon/ic_sort.svg"; import { useMediaQuery } from "react-responsive"; -export default function SelectBtn({ onChange }) { +interface SelectBtnProps { + onChange: (order: string) => void; +} + +const SelectBtn: FC = ({ onChange }) => { const [isDropdownOpen, setDropdownOpen] = useState(false); const [order, setOrder] = useState("recent"); - const ORDER_KR = { + const ORDER_KR: { [key: string]: string } = { recent: "최신순", favorite: "좋아요순", }; + const isMobile = useMediaQuery({ query: "(max-width: 767px)", @@ -28,22 +33,22 @@ export default function SelectBtn({ onChange }) { ); }; - const dropdownRef = useRef(null); + const dropdownRef = useRef(null); const toggleDropdown = () => { setDropdownOpen((prev) => !prev); }; // 정렬 옵션 선택시 호출되는 함수 - const handleOrderChange = (order) => { + const handleOrderChange = (order: string) => { setOrder(order); // 상태 업데이트 onChange(order); // 부모 컴포넌트로 선택된 정렬 순서 전달 setDropdownOpen(false); // 드롭다운 닫기 }; useEffect(() => { - const handleClickOutside = (e) => { - if (dropdownRef.current && !dropdownRef.current.contains(e.target)) { + const handleClickOutside = (e: MouseEvent) => { + if (dropdownRef.current && !dropdownRef.current.contains(e.target as Node)) { setDropdownOpen(false); } }; @@ -67,4 +72,6 @@ export default function SelectBtn({ onChange }) { )}
); -} +}; + +export default SelectBtn; diff --git a/src/components/Product/Product.js b/src/components/Product/Product.js new file mode 100644 index 000000000..f01295fc1 --- /dev/null +++ b/src/components/Product/Product.js @@ -0,0 +1,122 @@ +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const react_1 = __importStar(require("react")); +require("../../style/product.css"); +const ic_heart_svg_1 = __importDefault(require("../../assets/icon/ic_heart.svg")); +const API_1 = require("../API/API"); +const react_router_dom_1 = require("react-router-dom"); +const img_inquiry_empty_svg_1 = __importDefault(require("../../assets/img/img_inquiry_empty.svg")); +const ic_back_svg_1 = __importDefault(require("../../assets/icon/ic_back.svg")); +const Commas_1 = __importDefault(require("../../util/Commas")); +const times_1 = __importDefault(require("../../util/times")); +function Product() { + const [productData, setProductData] = (0, react_1.useState)(null); + const [productCommentData, setProductCommentData] = (0, react_1.useState)(null); + const [comment, setComment] = (0, react_1.useState)(""); + const [isFilled, setIsFilled] = (0, react_1.useState)(false); + const { id } = (0, react_router_dom_1.useParams)(); + (0, react_1.useEffect)(() => { + const fetchData = () => __awaiter(this, void 0, void 0, function* () { + try { + const data = yield (0, API_1.getProductData)(id); + setProductData(data); + } + catch (e) { + console.error(e); + } + }); + const fetchCommentData = () => __awaiter(this, void 0, void 0, function* () { + try { + const commentData = yield (0, API_1.getProductCommentData)(id); + setProductCommentData(commentData); + } + catch (e) { + console.error(e); + } + }); + fetchData(); + fetchCommentData(); + }, [id]); + const handleCommentChange = (e) => { + const text = e.target.value; + setComment(text); + setIsFilled(text.trim().length > 0); + }; + return (react_1.default.createElement(react_1.default.Fragment, null, productData && (react_1.default.createElement("div", { className: "product-container" }, + react_1.default.createElement("div", { className: "product-header" }, + react_1.default.createElement("div", { className: "product-header-img" }, + react_1.default.createElement("img", { src: productData.images[0], alt: "Product" })), + react_1.default.createElement("div", { className: "product-header-text" }, + react_1.default.createElement("p", { className: "product-header-name" }, productData.name), + react_1.default.createElement("p", { className: "product-header-price" }, + (0, Commas_1.default)(productData.price), + "\uC6D0"), + react_1.default.createElement("hr", null), + react_1.default.createElement("p", { className: "product-header-des" }, + "\uC0C1\uD488 \uC18C\uAC1C ", + react_1.default.createElement("br", null), + productData.description), + react_1.default.createElement("p", { className: "product-header-tag" }, "\uC0C1\uD488 \uD0DC\uADF8"), + react_1.default.createElement("div", { className: "product-header-tags" }, productData.tags.map((tag, index) => (react_1.default.createElement("span", { key: index, className: "tag" }, + "#", + tag)))), + react_1.default.createElement("div", { className: "like" }, + react_1.default.createElement("div", { className: "like2" }, + react_1.default.createElement("img", { src: ic_heart_svg_1.default, alt: "Heart" }), + react_1.default.createElement("p", null, productData.favoriteCount))))), + react_1.default.createElement("hr", null), + react_1.default.createElement("div", { className: "add-comment-container" }, + react_1.default.createElement("p", null, "\uBB38\uC758\uD558\uAE30"), + react_1.default.createElement("textarea", { className: "add-comment-des", placeholder: "\uAC1C\uC778\uC815\uBCF4\uB97C \uACE0\uC720 \uBC0F \uC694\uCCAD\uD558\uAC70\uB098, \uBA85\uC608 \uD6FC\uC190, \uBB34\uB2E8 \uAD11\uACE0, \uBD88\uBC95 \uC815\uBCF4 \uC720\uD3EC\uC2DC \uBAA8\uB2C8\uD130\uB9C1 \uD6C4 \uC0AD\uC81C\uB420 \uC218 \uC788\uC73C\uBA70, \uC774\uC5D0 \uB300\uD55C \uBBFC\uD615\uC0AC\uC0C1 \uCC45\uC784\uC740 \uAC8C\uC2DC\uC790\uC5D0\uAC8C \uC788\uC2B5\uB2C8\uB2E4.", value: comment, onChange: handleCommentChange }), + react_1.default.createElement("button", { className: isFilled ? "filled" : "" }, "\uB4F1\uB85D")), + react_1.default.createElement("div", { className: "comment-container" }, + productCommentData && productCommentData.list.length > 0 ? (react_1.default.createElement("div", null, productCommentData.list.map((comment) => (react_1.default.createElement("div", { key: comment.id, className: "comments" }, + react_1.default.createElement("p", { className: "comments-des" }, comment.content), + react_1.default.createElement("div", { className: "comment-writer" }, + react_1.default.createElement("img", { src: comment.writer.image, alt: "writer img" }), + react_1.default.createElement("div", { className: "comment-writer-nickname" }, + react_1.default.createElement("p", { className: "comments-writer" }, comment.writer.nickname), + react_1.default.createElement("p", { className: "comments-times" }, (0, times_1.default)(comment.updatedAt)))), + react_1.default.createElement("hr", null)))))) : (react_1.default.createElement("div", { className: "comment-empty-container" }, + react_1.default.createElement("img", { src: img_inquiry_empty_svg_1.default, alt: "no comment" }), + react_1.default.createElement("p", null, "\uC544\uC9C1 \uBB38\uC758\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4."))), + react_1.default.createElement("div", { className: "back-container" }, + react_1.default.createElement("div", null, + react_1.default.createElement(react_router_dom_1.Link, { to: "/items", className: "back-btn" }, "\uBAA9\uB85D\uC73C\uB85C \uB3CC\uC544\uAC00\uAE30"), + react_1.default.createElement("img", { src: ic_back_svg_1.default, alt: "back to items", className: "back-img" })))))))); +} +exports.default = Product; diff --git a/src/components/Product.js b/src/components/Product/Product.tsx similarity index 78% rename from src/components/Product.js rename to src/components/Product/Product.tsx index 2284ffd7d..378aecca5 100644 --- a/src/components/Product.js +++ b/src/components/Product/Product.tsx @@ -1,24 +1,46 @@ import React, { useEffect, useState } from "react"; -import "../style/product.css"; -import HeartIcon from "../assets/icon/ic_heart.svg"; -import { getProductData, getProductCommentData } from "./API"; +import "../../style/product.css"; +import HeartIcon from "../../assets/icon/ic_heart.svg"; +import { getProductData, getProductCommentData } from "../API/API"; import { Link, useParams } from "react-router-dom"; -import Commas from "../util/Commas"; -import TimeString from "../util/times"; -import inquiry_empty from "../assets/img/img_inquiry_empty.svg"; -import ic_back from "../assets/icon/ic_back.svg"; +import inquiry_empty from "../../assets/img/img_inquiry_empty.svg"; +import ic_back from "../../assets/icon/ic_back.svg"; +import Commas from "../../util/Commas"; +import TimeString from "../../util/times"; + +interface ProductData { + id: number; + name: string; + price: number; + description: string; + favoriteCount: number; + tags: string[]; + images: string[]; +} + +interface CommentData { + id: number; + content: string; + updatedAt: string; + writer: { + image: string; + nickname: string; + }; +} export default function Product() { - const [productData, setProductData] = useState(null); - const [productCommentData, setProductCommentData] = useState(null); + const [productData, setProductData] = useState(null); + const [productCommentData, setProductCommentData] = useState<{ + list: CommentData[]; + } | null>(null); const [comment, setComment] = useState(""); const [isFilled, setIsFilled] = useState(false); - const { id } = useParams(); + const { id } = useParams<{ id: string }>(); useEffect(() => { const fetchData = async () => { try { - const data = await getProductData(id); + const data = await getProductData(id as unknown as number); setProductData(data); } catch (e) { console.error(e); @@ -27,9 +49,8 @@ export default function Product() { const fetchCommentData = async () => { try { - const commentData = await getProductCommentData(id); + const commentData = await getProductCommentData(id as unknown as number); setProductCommentData(commentData); - console.log(productCommentData); } catch (e) { console.error(e); } @@ -37,11 +58,9 @@ export default function Product() { fetchData(); fetchCommentData(); - console.log(productCommentData); - // eslint-disable-next-line react-hooks/exhaustive-deps }, [id]); - const handleCommentChange = (e) => { + const handleCommentChange = (e: React.ChangeEvent) => { const text = e.target.value; setComment(text); setIsFilled(text.trim().length > 0); diff --git a/src/components/Signin/SigninForm.js b/src/components/Signin/SigninForm.js new file mode 100644 index 000000000..09748468b --- /dev/null +++ b/src/components/Signin/SigninForm.js @@ -0,0 +1,89 @@ +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const react_1 = __importStar(require("react")); +const react_router_dom_1 = require("react-router-dom"); +const eye_invisible_svg_1 = __importDefault(require("../../assets/icon/eye-invisible.svg")); +const eye_visible_svg_1 = __importDefault(require("../../assets/icon/eye-visible.svg")); +function SigninForm() { + const [email, setEmail] = (0, react_1.useState)(""); + const [emailError, setEmailError] = (0, react_1.useState)(""); + const [password, setPassword] = (0, react_1.useState)(""); + const [passwordError, setPasswordError] = (0, react_1.useState)(""); + const [showPassword, setShowPassword] = (0, react_1.useState)(false); + const navigate = (0, react_router_dom_1.useNavigate)(); + function validateEmail() { + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + if (!email) { + setEmailError("이메일을 입력하세요."); + return false; + } + else if (!emailRegex.test(email)) { + setEmailError("올바른 이메일 형식이 아닙니다."); + return false; + } + else { + setEmailError(""); + return true; + } + } + function validatePassword() { + if (!password) { + setPasswordError("비밀번호를 입력하세요."); + return false; + } + else if (password.length < 8) { + setPasswordError("비밀번호는 최소 8자 이상이어야 합니다."); + return false; + } + else { + setPasswordError(""); + return true; + } + } + function handleSubmit(e) { + e.preventDefault(); + if (validateEmail() && validatePassword()) { + navigate("/items"); + } + } + function togglePasswordVisibility() { + setShowPassword(!showPassword); + } + return (react_1.default.createElement("form", { id: "signinForm", className: "signin-form", onSubmit: handleSubmit }, + react_1.default.createElement("div", null, + react_1.default.createElement("label", { htmlFor: "email", className: "signin-label" }, "\uC774\uBA54\uC77C"), + react_1.default.createElement("input", { id: "email", name: "email", type: "email", placeholder: "\uC774\uBA54\uC77C\uC744 \uC785\uB825\uD574 \uC8FC\uC138\uC694.", className: "signin-input", value: email, onChange: (e) => setEmail(e.target.value), onBlur: validateEmail, style: { border: emailError ? "1px solid #f74747" : "none" } }), + emailError && (react_1.default.createElement("span", { id: "email-Error", className: "Error-message" }, emailError))), + react_1.default.createElement("div", { className: "signin-pwd" }, + react_1.default.createElement("label", { htmlFor: "pwd", className: "signin-label" }, "\uBE44\uBC00\uBC88\uD638"), + react_1.default.createElement("input", { id: "pwd", name: "pwd", type: showPassword ? "text" : "password", placeholder: "\uBE44\uBC00\uBC88\uD638\uB97C \uC785\uB825\uD574 \uC8FC\uC138\uC694.", className: "signin-input", value: password, onChange: (e) => setPassword(e.target.value), onBlur: validatePassword, style: { border: passwordError ? "1px solid #f74747" : "none" } }), + react_1.default.createElement("img", { id: "pwd-toggle", src: showPassword ? eye_visible_svg_1.default : eye_invisible_svg_1.default, className: "pwd-toggle", alt: "\uBE44\uBC00\uBC88\uD638 \uD1A0\uAE00", onClick: togglePasswordVisibility }), + passwordError && (react_1.default.createElement("span", { id: "pwd-Error", className: "Error-message" }, passwordError))), + react_1.default.createElement("button", { type: "submit", className: "signin-btn btn" }, "\uB85C\uADF8\uC778"))); +} +exports.default = SigninForm; diff --git a/src/components/Signin/SigninForm.tsx b/src/components/Signin/SigninForm.tsx new file mode 100644 index 000000000..a79827352 --- /dev/null +++ b/src/components/Signin/SigninForm.tsx @@ -0,0 +1,108 @@ +import React, { useState } from "react"; +import { useNavigate } from "react-router-dom"; +import eyeInvisible from "../../assets/icon/eye-invisible.svg"; +import eyeVisible from "../../assets/icon/eye-visible.svg"; + +export default function SigninForm(): JSX.Element { + const [email, setEmail] = useState(""); + const [emailError, setEmailError] = useState(""); + const [password, setPassword] = useState(""); + const [passwordError, setPasswordError] = useState(""); + const [showPassword, setShowPassword] = useState(false); + const navigate = useNavigate(); + + function validateEmail(): boolean { + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + if (!email) { + setEmailError("이메일을 입력하세요."); + return false; + } else if (!emailRegex.test(email)) { + setEmailError("올바른 이메일 형식이 아닙니다."); + return false; + } else { + setEmailError(""); + return true; + } + } + + function validatePassword(): boolean { + if (!password) { + setPasswordError("비밀번호를 입력하세요."); + return false; + } else if (password.length < 8) { + setPasswordError("비밀번호는 최소 8자 이상이어야 합니다."); + return false; + } else { + setPasswordError(""); + return true; + } + } + + function handleSubmit(e: React.FormEvent): void { + e.preventDefault(); + if (validateEmail() && validatePassword()) { + navigate("/items"); + } + } + + function togglePasswordVisibility(): void { + setShowPassword(!showPassword); + } + + return ( +
+
+ + setEmail(e.target.value)} + onBlur={validateEmail} + style={{ border: emailError ? "1px solid #f74747" : "none" }} + /> + {emailError && ( + + {emailError} + + )} +
+
+ + setPassword(e.target.value)} + onBlur={validatePassword} + style={{ border: passwordError ? "1px solid #f74747" : "none" }} + /> + 비밀번호 토글 + {passwordError && ( + + {passwordError} + + )} +
+ +
+ ); +} diff --git a/src/components/Signup/SignupForm.js b/src/components/Signup/SignupForm.js new file mode 100644 index 000000000..c1ea98949 --- /dev/null +++ b/src/components/Signup/SignupForm.js @@ -0,0 +1,131 @@ +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const react_1 = __importStar(require("react")); +const react_router_dom_1 = require("react-router-dom"); +const eye_invisible_svg_1 = __importDefault(require("../../assets/icon/eye-invisible.svg")); +const eye_visible_svg_1 = __importDefault(require("../../assets/icon/eye-visible.svg")); +function SignupForm() { + const [email, setEmail] = (0, react_1.useState)(""); + const [emailError, setEmailError] = (0, react_1.useState)(""); + const [nickname, setNickname] = (0, react_1.useState)(""); + const [nicknameError, setNicknameError] = (0, react_1.useState)(""); + const [password, setPassword] = (0, react_1.useState)(""); + const [passwordError, setPasswordError] = (0, react_1.useState)(""); + const [passwordConfirm, setPasswordConfirm] = (0, react_1.useState)(""); + const [passwordConfirmError, setPasswordConfirmError] = (0, react_1.useState)(""); + const [showPassword, setShowPassword] = (0, react_1.useState)(false); + const [showPasswordConfirm, setShowPasswordConfirm] = (0, react_1.useState)(false); + const navigate = (0, react_router_dom_1.useNavigate)(); + function validateEmail() { + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + if (!email) { + setEmailError("이메일을 입력하세요."); + return false; + } + else if (!emailRegex.test(email)) { + setEmailError("올바른 이메일 형식이 아닙니다."); + return false; + } + else { + setEmailError(""); + return true; + } + } + function validateNickname() { + if (!nickname) { + setNicknameError("닉네임을 입력하세요."); + return false; + } + else { + setNicknameError(""); + return true; + } + } + function validatePassword() { + if (!password) { + setPasswordError("비밀번호를 입력하세요."); + return false; + } + else if (password.length < 8) { + setPasswordError("비밀번호는 최소 8자 이상이어야 합니다."); + return false; + } + else { + setPasswordError(""); + return true; + } + } + function validatePasswordConfirm() { + if (password !== passwordConfirm) { + setPasswordConfirmError("비밀번호가 일치하지 않습니다."); + return false; + } + else { + setPasswordConfirmError(""); + return true; + } + } + function handleSubmit(e) { + e.preventDefault(); + if (validateEmail() && + validateNickname() && + validatePassword() && + validatePasswordConfirm()) { + navigate("/items"); + } + } + function togglePasswordVisibility() { + setShowPassword(!showPassword); + } + function togglePasswordConfirmVisibility() { + setShowPasswordConfirm(!showPasswordConfirm); + } + return (react_1.default.createElement("form", { id: "signupForm", className: "signin-form", onSubmit: handleSubmit }, + react_1.default.createElement("div", null, + react_1.default.createElement("label", { htmlFor: "email", className: "signin-label" }, "\uC774\uBA54\uC77C"), + react_1.default.createElement("input", { id: "email", name: "email", type: "email", placeholder: "\uC774\uBA54\uC77C\uC744 \uC785\uB825\uD574 \uC8FC\uC138\uC694.", className: "signin-input", value: email, onChange: (e) => setEmail(e.target.value), onBlur: validateEmail, style: { border: emailError ? "1px solid #f74747" : "none" } }), + emailError && (react_1.default.createElement("span", { id: "email-Error", className: "Error-message" }, emailError))), + react_1.default.createElement("div", null, + react_1.default.createElement("label", { htmlFor: "nickname", className: "signin-label" }, "\uB2C9\uB124\uC784"), + react_1.default.createElement("input", { id: "nickname", name: "nickname", type: "text", placeholder: "\uB2C9\uB124\uC784\uC744 \uC785\uB825\uD574 \uC8FC\uC138\uC694.", className: "signin-input", value: nickname, onChange: (e) => setNickname(e.target.value), onBlur: validateNickname, style: { border: nicknameError ? "1px solid #f74747" : "none" } }), + nicknameError && (react_1.default.createElement("span", { id: "nickname-Error", className: "Error-message" }, nicknameError))), + react_1.default.createElement("div", { className: "signin-pwd" }, + react_1.default.createElement("label", { htmlFor: "pwd", className: "signin-label" }, "\uBE44\uBC00\uBC88\uD638"), + react_1.default.createElement("input", { id: "pwd", name: "pwd", type: showPassword ? "text" : "password", placeholder: "\uBE44\uBC00\uBC88\uD638\uB97C \uC785\uB825\uD574 \uC8FC\uC138\uC694.", className: "signin-input", value: password, onChange: (e) => setPassword(e.target.value), onBlur: validatePassword, style: { border: passwordError ? "1px solid #f74747" : "none" } }), + react_1.default.createElement("img", { id: "pwd-toggle", src: showPassword ? eye_visible_svg_1.default : eye_invisible_svg_1.default, className: "pwd-toggle", alt: "\uBE44\uBC00\uBC88\uD638 \uD1A0\uAE00", onClick: togglePasswordVisibility }), + passwordError && (react_1.default.createElement("span", { id: "pwd-Error", className: "Error-message" }, passwordError))), + react_1.default.createElement("div", { className: "signin-pwd" }, + react_1.default.createElement("label", { htmlFor: "pwd-same", className: "signin-label" }, "\uBE44\uBC00\uBC88\uD638 \uD655\uC778"), + react_1.default.createElement("input", { id: "pwd-same", name: "pwd-same", type: showPasswordConfirm ? "text" : "password", placeholder: "\uBE44\uBC00\uBC88\uD638\uB97C \uB2E4\uC2DC \uD55C \uBC88 \uC785\uB825\uD574 \uC8FC\uC138\uC694.", className: "signin-input", value: passwordConfirm, onChange: (e) => setPasswordConfirm(e.target.value), onBlur: validatePasswordConfirm, style: { + border: passwordConfirmError ? "1px solid #f74747" : "none", + } }), + react_1.default.createElement("img", { id: "pwd-same-toggle", src: showPasswordConfirm ? eye_visible_svg_1.default : eye_invisible_svg_1.default, className: "pwd-toggle", alt: "\uBE44\uBC00\uBC88\uD638 \uD1A0\uAE00", onClick: togglePasswordConfirmVisibility }), + passwordConfirmError && (react_1.default.createElement("span", { id: "pwd-same-Error", className: "Error-message" }, passwordConfirmError))), + react_1.default.createElement("button", { type: "submit", className: "signin-btn btn" }, "\uD68C\uC6D0\uAC00\uC785"))); +} +exports.default = SignupForm; diff --git a/src/components/Signup/SignupForm.tsx b/src/components/Signup/SignupForm.tsx new file mode 100644 index 000000000..bb974e534 --- /dev/null +++ b/src/components/Signup/SignupForm.tsx @@ -0,0 +1,193 @@ +import React, { useState } from "react"; +import { useNavigate } from "react-router-dom"; +import eyeInvisible from "../../assets/icon/eye-invisible.svg"; +import eyeVisible from "../../assets/icon/eye-visible.svg"; + +export default function SignupForm(): JSX.Element { + const [email, setEmail] = useState(""); + const [emailError, setEmailError] = useState(""); + const [nickname, setNickname] = useState(""); + const [nicknameError, setNicknameError] = useState(""); + const [password, setPassword] = useState(""); + const [passwordError, setPasswordError] = useState(""); + const [passwordConfirm, setPasswordConfirm] = useState(""); + const [passwordConfirmError, setPasswordConfirmError] = useState(""); + const [showPassword, setShowPassword] = useState(false); + const [showPasswordConfirm, setShowPasswordConfirm] = useState(false); + const navigate = useNavigate(); + + function validateEmail(): boolean { + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + if (!email) { + setEmailError("이메일을 입력하세요."); + return false; + } else if (!emailRegex.test(email)) { + setEmailError("올바른 이메일 형식이 아닙니다."); + return false; + } else { + setEmailError(""); + return true; + } + } + + function validateNickname(): boolean { + if (!nickname) { + setNicknameError("닉네임을 입력하세요."); + return false; + } else { + setNicknameError(""); + return true; + } + } + + function validatePassword(): boolean { + if (!password) { + setPasswordError("비밀번호를 입력하세요."); + return false; + } else if (password.length < 8) { + setPasswordError("비밀번호는 최소 8자 이상이어야 합니다."); + return false; + } else { + setPasswordError(""); + return true; + } + } + + function validatePasswordConfirm(): boolean { + if (password !== passwordConfirm) { + setPasswordConfirmError("비밀번호가 일치하지 않습니다."); + return false; + } else { + setPasswordConfirmError(""); + return true; + } + } + + function handleSubmit(e: React.FormEvent): void { + e.preventDefault(); + if ( + validateEmail() && + validateNickname() && + validatePassword() && + validatePasswordConfirm() + ) { + navigate("/items"); + } + } + + function togglePasswordVisibility(): void { + setShowPassword(!showPassword); + } + + function togglePasswordConfirmVisibility(): void { + setShowPasswordConfirm(!showPasswordConfirm); + } + + return ( +
+
+ + setEmail(e.target.value)} + onBlur={validateEmail} + style={{ border: emailError ? "1px solid #f74747" : "none" }} + /> + {emailError && ( + + {emailError} + + )} +
+
+ + setNickname(e.target.value)} + onBlur={validateNickname} + style={{ border: nicknameError ? "1px solid #f74747" : "none" }} + /> + {nicknameError && ( + + {nicknameError} + + )} +
+
+ + setPassword(e.target.value)} + onBlur={validatePassword} + style={{ border: passwordError ? "1px solid #f74747" : "none" }} + /> + 비밀번호 토글 + {passwordError && ( + + {passwordError} + + )} +
+
+ + setPasswordConfirm(e.target.value)} + onBlur={validatePasswordConfirm} + style={{ + border: passwordConfirmError ? "1px solid #f74747" : "none", + }} + /> + 비밀번호 토글 + {passwordConfirmError && ( + + {passwordConfirmError} + + )} +
+ +
+ ); +} diff --git a/src/components/SocialLogin.js b/src/components/SocialLogin.js new file mode 100644 index 000000000..d815d17e8 --- /dev/null +++ b/src/components/SocialLogin.js @@ -0,0 +1,20 @@ +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const react_1 = __importDefault(require("react")); +const google_logo_png_1 = __importDefault(require("../assets/icon/google-logo.png")); +const kakao_logo_png_1 = __importDefault(require("../assets/icon/kakao-logo.png")); +function SocialLogin() { + return (react_1.default.createElement("div", { className: "social-login" }, + react_1.default.createElement("div", null, + react_1.default.createElement("p", null, "\uAC04\uD3B8 \uB85C\uADF8\uC778\uD558\uAE30")), + react_1.default.createElement("div", { className: "social-login-img" }, + react_1.default.createElement("div", null, + react_1.default.createElement("a", { href: "https://www.google.com", target: "_blank", rel: "noopener noreferrer" }, + react_1.default.createElement("img", { src: google_logo_png_1.default, alt: "\uAD6C\uAE00\uB85C\uADF8\uC778", width: "42px" }))), + react_1.default.createElement("div", null, + react_1.default.createElement("a", { href: "https://www.kakaocorp.com/page", target: "_blank", rel: "noopener noreferrer" }, + react_1.default.createElement("img", { src: kakao_logo_png_1.default, alt: "\uCE74\uCE74\uC624\uB85C\uADF8\uC778", width: "42px" })))))); +} +exports.default = SocialLogin; diff --git a/src/components/SocialLogin.tsx b/src/components/SocialLogin.tsx new file mode 100644 index 000000000..87ef6af19 --- /dev/null +++ b/src/components/SocialLogin.tsx @@ -0,0 +1,33 @@ +import React from "react"; +import googleLogo from "../assets/icon/google-logo.png"; +import kakaoLogo from "../assets/icon/kakao-logo.png"; + +export default function SocialLogin(): JSX.Element { + return ( +
+
+

간편 로그인하기

+
+
+
+ + 구글로그인 + +
+
+ + 카카오로그인 + +
+
+
+ ); +} diff --git a/src/components/header.js b/src/components/header.js index 3005356d8..aa3fecf71 100644 --- a/src/components/header.js +++ b/src/components/header.js @@ -1,34 +1,27 @@ -import MainLogo from "../assets/icon/main_logo.svg"; -import SmallMainLogo from "../assets/icon/main_logo_small.svg"; -import { Link, useLocation } from "react-router-dom"; -import "../style/header.css"; -import LinkButton from "../common/Button"; - -export default function NavBar() { - const location = useLocation(); - return ( - - ); +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const react_1 = __importDefault(require("react")); +const main_logo_svg_1 = __importDefault(require("../assets/icon/main_logo.svg")); +const main_logo_small_svg_1 = __importDefault(require("../assets/icon/main_logo_small.svg")); +const react_router_dom_1 = require("react-router-dom"); +require("../style/header.css"); +const Button_1 = __importDefault(require("../common/Button")); +function NavBar() { + const location = (0, react_router_dom_1.useLocation)(); + return (react_1.default.createElement("nav", { className: "navvar" }, + react_1.default.createElement(react_router_dom_1.Link, { to: "/" }, + react_1.default.createElement("img", { className: "mainlogo", src: main_logo_svg_1.default, alt: "\uB85C\uACE0" }), + react_1.default.createElement("img", { className: "mainlogo", src: main_logo_small_svg_1.default, alt: "\uB85C\uACE0" })), + react_1.default.createElement("div", { className: "menus" }, + react_1.default.createElement(react_router_dom_1.Link, { to: "/" }, + react_1.default.createElement("span", null, "\uC790\uC720\uAC8C\uC2DC\uD310")), + react_1.default.createElement(react_router_dom_1.Link, { to: "/items", className: location.pathname.startsWith("/items") || + location.pathname === "/additem" + ? "focus" + : "" }, + react_1.default.createElement("span", null, "\uC911\uACE0\uB9C8\uCF13"))), + react_1.default.createElement(Button_1.default, { to: "/signin" }, "\uB85C\uADF8\uC778"))); } +exports.default = NavBar; diff --git a/src/index.js b/src/index.js index 34aa4a3a2..011a7a44b 100644 --- a/src/index.js +++ b/src/index.js @@ -1,12 +1,17 @@ -import ReactDOM from "react-dom/client"; -import App from "./App.js"; -import { BrowserRouter as Router } from "react-router-dom"; - -const root = ReactDOM.createRoot(document.getElementById("root")); -root.render( - - - , - - root -); +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const client_1 = __importDefault(require("react-dom/client")); +const react_1 = __importDefault(require("react")); +const App_js_1 = __importDefault(require("./App.js")); +const react_router_dom_1 = require("react-router-dom"); +const rootElement = document.getElementById("root"); +if (rootElement) { + const root = client_1.default.createRoot(rootElement); + root.render(react_1.default.createElement(react_router_dom_1.BrowserRouter, null, + react_1.default.createElement(App_js_1.default, null))); +} +else { + console.error("Root element not found"); +} diff --git a/src/index.tsx b/src/index.tsx new file mode 100644 index 000000000..d4fc870d7 --- /dev/null +++ b/src/index.tsx @@ -0,0 +1,17 @@ +import ReactDOM from "react-dom/client"; +import React from "react"; +import App from "./App.js"; +import { BrowserRouter as Router } from "react-router-dom"; + +const rootElement = document.getElementById("root"); + +if (rootElement) { + const root = ReactDOM.createRoot(rootElement); + root.render( + + + + ); +} else { + console.error("Root element not found"); +} diff --git a/src/pages/faq.js b/src/pages/faq.js new file mode 100644 index 000000000..ea80f9ac6 --- /dev/null +++ b/src/pages/faq.js @@ -0,0 +1,11 @@ +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const Header_1 = __importDefault(require("../components/Header")); +const react_1 = __importDefault(require("react")); +function Faq() { + return (react_1.default.createElement(react_1.default.Fragment, null, + react_1.default.createElement(Header_1.default, null))); +} +exports.default = Faq; diff --git a/src/pages/faq.tsx b/src/pages/faq.tsx new file mode 100644 index 000000000..6568c20fb --- /dev/null +++ b/src/pages/faq.tsx @@ -0,0 +1,10 @@ +import NavBar from "../components/Header"; +import React from "react"; + +export default function Faq() { + return ( + <> + + + ); +} diff --git a/src/pages/home.js b/src/pages/home.js index 49e47cf94..306498d73 100644 --- a/src/pages/home.js +++ b/src/pages/home.js @@ -1,9 +1,21 @@ -import NavBar from "../components/header"; - -export default function HomePage() { - return ( - <> - - - ); +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const react_1 = __importDefault(require("react")); +const BannerBottom_1 = __importDefault(require("../components/Home/BannerBottom")); +const BannerMiddle_1 = __importDefault(require("../components/Home/BannerMiddle")); +const BannerTop_1 = __importDefault(require("../components/Home/BannerTop")); +const Footer_1 = __importDefault(require("../components/Home/Footer")); +require("../style/home.css"); +const Header_1 = __importDefault(require("../components/Header")); +function HomePage() { + return (react_1.default.createElement(react_1.default.Fragment, null, + react_1.default.createElement(Header_1.default, null), + react_1.default.createElement("main", null, + react_1.default.createElement(BannerTop_1.default, null), + react_1.default.createElement(BannerMiddle_1.default, null), + react_1.default.createElement(BannerBottom_1.default, null)), + react_1.default.createElement(Footer_1.default, null))); } +exports.default = HomePage; diff --git a/src/pages/home.tsx b/src/pages/home.tsx new file mode 100644 index 000000000..3f1cb24e4 --- /dev/null +++ b/src/pages/home.tsx @@ -0,0 +1,21 @@ +import React from "react"; +import BannerBottom from "../components/Home/BannerBottom"; +import BannerMiddle from "../components/Home/BannerMiddle"; +import BannerTop from "../components/Home/BannerTop"; +import Footer from "../components/Home/Footer"; +import "../style/home.css"; +import NavBar from "../components/Header"; + +export default function HomePage() { + return ( + <> + +
+ + + +
+