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 (
+
+
+
+
+
Hot Item
+
인기 상품을
확인해보세요
+
가장 HOT한 중고거래 물품을
판다 마켓에서 확인해 보세요
+
+
+
+
+
+
Search
+
구매를 원하는
상품을 검색하세요
+
구매하고 싶은 물품은 검색해서
쉽게 찾아보세요
+
+
+
+
+
+
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 (
+
+ );
+}
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 (
+
+ );
+}
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 (
+ <>
+
+
+
+
+
+
+
+ >
+ );
+}
diff --git a/src/pages/items.js b/src/pages/items.js
index a7b81ab9d..3041907d3 100644
--- a/src/pages/items.js
+++ b/src/pages/items.js
@@ -1,14 +1,15 @@
-import NavBar from "../components/header";
-import AddItem from "../components/AddItem";
-import "../style/additem.css";
-
-export default function ItemPage() {
- 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 Header_1 = __importDefault(require("../components/Header"));
+require("../style/additem.css");
+const AddItem_1 = __importDefault(require("../components/AddItems/AddItem"));
+function ItemPage() {
+ return (react_1.default.createElement(react_1.default.Fragment, null,
+ react_1.default.createElement(Header_1.default, null),
+ react_1.default.createElement("div", { className: "item-container" },
+ react_1.default.createElement(AddItem_1.default, null))));
}
+exports.default = ItemPage;
diff --git a/src/pages/items.tsx b/src/pages/items.tsx
new file mode 100644
index 000000000..0dae5d73d
--- /dev/null
+++ b/src/pages/items.tsx
@@ -0,0 +1,15 @@
+import React from "react";
+import NavBar from "../components/Header";
+import "../style/additem.css";
+import AddItem from "../components/AddItems/AddItem";
+
+export default function ItemPage() {
+ return (
+ <>
+
+
+ >
+ );
+}
diff --git a/src/pages/market.js b/src/pages/market.js
index 563a4f9a3..ddc4a7def 100644
--- a/src/pages/market.js
+++ b/src/pages/market.js
@@ -1,16 +1,17 @@
-import AllMarket from "../components/AllMarket";
-import BestMarket from "../components/BestMarket";
-import NavBar from "../components/header";
-import "../style/market.css";
-
-export default function MarketPage() {
- 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 BestMarket_1 = __importDefault(require("../components/Market/BestMarket"));
+const AllMarket_1 = __importDefault(require("../components/Market/AllMarket"));
+const Header_1 = __importDefault(require("../components/Header"));
+require("../style/market.css");
+function MarketPage() {
+ return (react_1.default.createElement(react_1.default.Fragment, null,
+ react_1.default.createElement(Header_1.default, null),
+ react_1.default.createElement("div", { className: "market" },
+ react_1.default.createElement(BestMarket_1.default, null),
+ react_1.default.createElement(AllMarket_1.default, null))));
}
+exports.default = MarketPage;
diff --git a/src/pages/market.tsx b/src/pages/market.tsx
new file mode 100644
index 000000000..8a4f6bb5c
--- /dev/null
+++ b/src/pages/market.tsx
@@ -0,0 +1,17 @@
+import React from "react";
+import BestMarket from "../components/Market/BestMarket";
+import AllMarket from "../components/Market/AllMarket";
+import NavBar from "../components/Header";
+import "../style/market.css";
+
+export default function MarketPage() {
+ return (
+ <>
+
+
+ >
+ );
+}
diff --git a/src/pages/privacy.js b/src/pages/privacy.js
new file mode 100644
index 000000000..7bc71b51b
--- /dev/null
+++ b/src/pages/privacy.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 Privacy() {
+ return (react_1.default.createElement(react_1.default.Fragment, null,
+ react_1.default.createElement(Header_1.default, null)));
+}
+exports.default = Privacy;
diff --git a/src/pages/privacy.tsx b/src/pages/privacy.tsx
new file mode 100644
index 000000000..d1a5f8c55
--- /dev/null
+++ b/src/pages/privacy.tsx
@@ -0,0 +1,10 @@
+import NavBar from "../components/Header";
+import React from "react";
+
+export default function Privacy() {
+ return (
+ <>
+
+ >
+ );
+}
diff --git a/src/pages/product.js b/src/pages/product.js
index 9ef883a91..7d2eeb830 100644
--- a/src/pages/product.js
+++ b/src/pages/product.js
@@ -1,13 +1,14 @@
-import Product from "../components/Product";
-import NavBar from "../components/header";
-
-export default function ProductPage() {
- return (
- <>
-
-
- >
- );
+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"));
+const Product_1 = __importDefault(require("../components/Product/Product"));
+function ProductPage() {
+ return (react_1.default.createElement(react_1.default.Fragment, null,
+ react_1.default.createElement(Header_1.default, null),
+ react_1.default.createElement("div", { className: "ProductPage" },
+ react_1.default.createElement(Product_1.default, null))));
}
+exports.default = ProductPage;
diff --git a/src/pages/product.tsx b/src/pages/product.tsx
new file mode 100644
index 000000000..ac0e5682d
--- /dev/null
+++ b/src/pages/product.tsx
@@ -0,0 +1,14 @@
+import NavBar from "../components/Header";
+import React from "react";
+import Product from "../components/Product/Product";
+
+export default function ProductPage() {
+ return (
+ <>
+
+
+ >
+ );
+}
diff --git a/src/pages/signin.js b/src/pages/signin.js
new file mode 100644
index 000000000..8682375ec
--- /dev/null
+++ b/src/pages/signin.js
@@ -0,0 +1,21 @@
+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 panda_logo_svg_1 = __importDefault(require("../assets/img/panda_logo.svg"));
+require("../style/signin.css");
+const SocialLogin_1 = __importDefault(require("../components/SocialLogin"));
+const SigninForm_1 = __importDefault(require("../components/Signin/SigninForm"));
+function Signin() {
+ return (react_1.default.createElement("div", { id: "signin-form", className: "signin-container" },
+ react_1.default.createElement(react_router_dom_1.Link, { to: "/", className: "signin-logo" },
+ react_1.default.createElement("img", { src: panda_logo_svg_1.default, alt: "\uD310\uB2E4\uB9C8\uCF13", width: "396px" })),
+ react_1.default.createElement(SigninForm_1.default, null),
+ react_1.default.createElement(SocialLogin_1.default, null),
+ react_1.default.createElement("div", { className: "switch-signup" },
+ "\uD310\uB2E4\uB9C8\uCF13\uC774 \uCC98\uC74C\uC774\uC2E0\uAC00\uC694? ",
+ react_1.default.createElement(react_router_dom_1.Link, { to: "/signup" }, "\uD68C\uC6D0\uAC00\uC785"))));
+}
+exports.default = Signin;
diff --git a/src/pages/signin.tsx b/src/pages/signin.tsx
new file mode 100644
index 000000000..4298545cd
--- /dev/null
+++ b/src/pages/signin.tsx
@@ -0,0 +1,21 @@
+import React from "react";
+import { Link } from "react-router-dom";
+import pandaLogo from "../assets/img/panda_logo.svg";
+import "../style/signin.css";
+import SocialLogin from "../components/SocialLogin";
+import SigninForm from "../components/Signin/SigninForm";
+
+export default function Signin() {
+ return (
+
+ );
+}
diff --git a/src/pages/signup.js b/src/pages/signup.js
new file mode 100644
index 000000000..3ba6d0699
--- /dev/null
+++ b/src/pages/signup.js
@@ -0,0 +1,22 @@
+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 panda_logo_svg_1 = __importDefault(require("../assets/img/panda_logo.svg"));
+require("../style/signup.css");
+const SignupForm_1 = __importDefault(require("../components/Signup/SignupForm"));
+const SocialLogin_1 = __importDefault(require("../components/SocialLogin"));
+function Signup() {
+ return (react_1.default.createElement("div", { className: "signin-container" },
+ react_1.default.createElement("div", { className: "signin-logo" },
+ react_1.default.createElement(react_router_dom_1.Link, { to: "/" },
+ react_1.default.createElement("img", { src: panda_logo_svg_1.default, alt: "\uD310\uB2E4\uB9C8\uCF13", width: "396px" }))),
+ react_1.default.createElement(SignupForm_1.default, null),
+ react_1.default.createElement(SocialLogin_1.default, null),
+ react_1.default.createElement("div", { className: "switch-signup" },
+ "\uC774\uBBF8 \uD68C\uC6D0\uC774\uC2E0\uAC00\uC694? ",
+ react_1.default.createElement(react_router_dom_1.Link, { to: "/signin" }, "\uB85C\uADF8\uC778"))));
+}
+exports.default = Signup;
diff --git a/src/pages/signup.tsx b/src/pages/signup.tsx
new file mode 100644
index 000000000..e3e43b48e
--- /dev/null
+++ b/src/pages/signup.tsx
@@ -0,0 +1,23 @@
+import React from 'react';
+import { Link } from 'react-router-dom';
+import pandaLogo from '../assets/img/panda_logo.svg';
+import '../style/signup.css';
+import SignupForm from '../components/Signup/SignupForm';
+import SocialLogin from '../components/SocialLogin';
+
+export default function Signup() {
+ return (
+
+
+
+
+
+
+
+
+
+ 이미 회원이신가요? 로그인
+
+
+ );
+}
diff --git a/src/style/home.css b/src/style/home.css
new file mode 100644
index 000000000..582619f5a
--- /dev/null
+++ b/src/style/home.css
@@ -0,0 +1,252 @@
+main {
+ width: 100%;
+}
+
+.banner-top {
+ background-color: #CFE5FF;
+ width: 100%;
+ height: 540px;
+ display: flex;
+ align-items: center;
+ background-image: url('../assets/img/img_home_top.svg');
+ background-repeat: no-repeat;
+ background-position: 80% bottom;
+ background-size: 55%;
+}
+
+.banner-top>div {
+ margin: 0 auto;
+ width: 100%;
+ max-width: 1200px;
+ font-size: 32px;
+ color: #374151;
+ font-weight: 700;
+}
+
+.btn-items {
+ width: 355px;
+ height: 56px;
+ border-radius: 40px;
+ font-weight: 600;
+ font-size: 20px;
+ text-align: center;
+ color: white;
+ background-color: #3692FF;
+ display: inline-flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.banner-middle {
+ width: 100%;
+ max-width: 1200px;
+ margin: 0 auto;
+}
+
+.item img {
+ width: 50%;
+ margin-bottom: 0;
+}
+
+.item {
+ padding: 100px 0;
+ display: flex;
+ justify-content: space-between;
+}
+
+.item:nth-child(2) {
+ flex-direction: row-reverse;
+}
+
+.item-text {
+ flex-basis: 500px;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+}
+
+.item-text.right {
+ text-align: right;
+}
+
+.title {
+ font-weight: 700;
+ font-size: 18px;
+ color: #3692FF;
+ margin-bottom: 12px;
+}
+
+.subtitle {
+ font-weight: 700;
+ font-size: 40px;
+ color: #374151;
+}
+
+.description {
+ font-weight: 500;
+ font-size: 24px;
+ color: #374151;
+ margin-top: 24px;
+}
+
+.banner-bottom {
+ background-color: #CFE5FF;
+ height: 540px;
+ width: 100%;
+ display: flex;
+ align-items: center;
+ background-image: url('../assets/img/img_home_bottom.svg');
+ background-repeat: no-repeat;
+ background-position: 80% bottom;
+ background-size: 55%;
+}
+
+.banner-bottom>div {
+ margin: 0 auto;
+ max-width: 1200px;
+ width: 100%;
+ font-size: 40px;
+ font-weight: 700;
+ color: #374151;
+}
+
+/* 푸터부분 */
+footer {
+ padding-top: 15px;
+ padding-bottom: 30px;
+ background: #1B1D1F;
+ width: auto;
+ display: flex;
+ justify-content: space-between;
+ align-items: flex-start;
+}
+
+footer>div {
+ padding-top: 20px;
+ flex-basis: 350px;
+ text-align: center;
+}
+
+.codeit {
+ font-size: 16px;
+ font-weight: 400;
+ color: #9CA3AF;
+ margin-top: 0;
+}
+
+.FAQ {
+ font-size: 16px;
+ font-weight: 400;
+ gap: 255px;
+}
+
+footer>.FAQ>a {
+ background: none;
+ color: #E5E7EB;
+}
+
+/* 푸터부분 */
+
+/* 반응형 디자인 */
+
+@media (max-width: 1279px) {
+ header {
+ padding: 0 24px;
+ }
+
+ .banner-top {
+ height: 771px;
+ background-size: 120%;
+ text-align: center;
+ align-items: flex-start;
+ padding-top: 84px;
+ }
+
+ .banner-middle {
+ padding: 24px 24px 16px 24px;
+ }
+
+ .item {
+ display: block;
+ }
+
+ .item img {
+ width: 100%;
+ margin-bottom: 20px;
+ }
+
+ .banner-bottom {
+ height: 927px;
+ display: block;
+ text-align: center;
+ background-size: 120%;
+ padding-top: 84px;
+ }
+
+ .title {
+ font-size: 18px;
+ font-weight: 700;
+ margin-bottom: 12px;
+ }
+
+ .subtitle {
+ font-size: 32px;
+ font-weight: 700;
+ }
+
+ .description {
+ font-size: 18px;
+ font-weight: 500;
+ margin-top: 20px;
+ }
+
+ .break-on-desktop {
+ display: none;
+ }
+
+ .banner-top h1 br {
+ display: none;
+ }
+}
+
+@media (max-width: 767px) {
+ header {
+ padding: 0 16px;
+ }
+
+ .banner-top {
+ height: 540px;
+ background-position: bottom;
+ background-size: 130%;
+ }
+
+ .btn-items {
+ padding: 14px 33px;
+ width: auto;
+ }
+
+ .title {
+ font-size: 16px;
+ margin-bottom: 8px;
+ }
+
+ .subtitle {
+ font-size: 24px;
+ }
+
+ .description {
+ font-size: 16px;
+ }
+
+ .banner-bottom {
+ height: 540px;
+ display: block;
+ text-align: center;
+ background-size: 130%;
+ padding: 48px 16px 0 0;
+ }
+
+ .banner-top h1 br {
+ display: inline;
+ }
+}
diff --git a/src/style/signin.css b/src/style/signin.css
new file mode 100644
index 000000000..8cdb1aa6b
--- /dev/null
+++ b/src/style/signin.css
@@ -0,0 +1,104 @@
+.signin-container {
+ max-width: 640px;
+ margin: 0 auto;
+}
+
+.signin-logo {
+ display: block;
+ text-align: center;
+ width: 396px;
+ margin: 60px auto 40px auto;
+}
+
+.signin-logo img {
+ width: 100%;
+}
+
+.signin-label {
+ margin-bottom: 16px;
+ font-size: 18px;
+ font-weight: 700;
+}
+
+.signin-input {
+ width: 100%;
+ padding: 16px 24px;
+ border: none;
+ background-color: #f3f4f6;
+ border-radius: 12px;
+ font-size: 16px;
+ margin-bottom: 24px;
+}
+
+.signin-form>div {
+ display: flex;
+ flex-direction: column;
+}
+
+.signin-pwd {
+ position: relative;
+}
+
+.signin-btn {
+ width: 100%;
+ font-size: 20px;
+ font-weight: 700;
+ border-radius: 40px;
+ padding: 15px 100px;
+ background-color: #3692FF;
+ border: none;
+ color: white;
+}
+
+.signin-form div {
+ margin-bottom: 12px;
+}
+
+.signin-form input {
+ margin-bottom: 6px;
+}
+
+.Error-message {
+ color: #f74747;
+ font-weight: 600;
+ font-size: 15px;
+ line-height: 18px;
+ margin-top: 8px;
+ padding-left: 16px;
+ margin-top: 0;
+}
+
+.pwd-toggle {
+ position: absolute;
+ right: 15px;
+ top: 50px;
+}
+
+.social-login {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+ padding: 15px 30px;
+ margin: 24px 0;
+ background-color: #e6f2ff;
+ color: #1f2937;;
+ border-radius: 8px;
+}
+
+.social-login-img {
+ display: flex;
+ gap: 20px;
+}
+
+.switch-signup {
+ font-weight: 500;
+ font-size: 15px;
+ text-align: center;
+}
+
+.switch-signup>a {
+ color: #3182F6;
+ text-decoration: underline;
+ text-underline-offset: 2px;
+}
diff --git a/src/style/signup.css b/src/style/signup.css
new file mode 100644
index 000000000..3857759d4
--- /dev/null
+++ b/src/style/signup.css
@@ -0,0 +1,108 @@
+.signin-container {
+ max-width: 640px;
+ margin: 0 auto;
+}
+
+.signin-logo {
+ display: block;
+ text-align: center;
+ width: 396px;
+ margin: 60px auto 40px auto;
+}
+
+.signin-logo img {
+ width: 100%;
+}
+
+.signin-label {
+ margin-bottom: 16px;
+ font-size: 18px;
+ font-weight: 700;
+}
+
+.signin-input {
+ width: 100%;
+ padding: 16px 24px;
+ border: none;
+ background-color: #f3f3f6;
+ border-radius: 12px;
+ font-size: 16px;
+ margin-bottom: 24px;
+}
+
+.signin-form > div {
+ display: flex;
+ flex-direction: column;
+}
+
+.signin-pwd {
+ position: relative;
+}
+
+.signin-btn {
+ width: 100%;
+ font-size: 20px;
+ font-weight: 700;
+ border-radius: 40px;
+ padding: 15px 100px;
+ background-color: #3692ff;
+ border: none;
+ color: white;
+}
+
+.signin-form div {
+ margin-bottom: 12px;
+}
+
+.signin-form input {
+ margin-bottom: 6px;
+}
+
+.Error-message {
+ color: #f74747;
+ font-weight: 600;
+ font-size: 15px;
+ line-height: 18px;
+ margin-top: 8px;
+ padding-left: 16px;
+ margin-top: 0;
+}
+
+.pwd-toggle {
+ position: absolute;
+ right: 15px;
+ top: 50px;
+}
+
+.social-login {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+ padding: 15px 30px;
+ margin: 24px 0;
+ background-color: #e6f2ff;
+ color: #1f2937;
+ border-radius: 8px;
+}
+
+.social-login-img {
+ display: flex;
+ gap: 20px;
+}
+
+.switch-signup {
+ font-weight: 500;
+ font-size: 15px;
+ text-align: center;
+}
+
+.switch-signup > a {
+ color: #3182f6;
+ text-decoration: underline;
+ text-underline-offset: 2px;
+}
+
+.break-on-desktop {
+ display: inline;
+}
diff --git a/src/types/images.d.ts b/src/types/images.d.ts
new file mode 100644
index 000000000..eafef2479
--- /dev/null
+++ b/src/types/images.d.ts
@@ -0,0 +1,2 @@
+declare module '*.png';
+declare module '*.svg';
\ No newline at end of file
diff --git a/src/util/Commas.js b/src/util/Commas.js
index e1fe8c048..2a8a1acea 100644
--- a/src/util/Commas.js
+++ b/src/util/Commas.js
@@ -1,3 +1,5 @@
-export default function Commas(n) {
- return n.toLocaleString("en-US");
+Object.defineProperty(exports, "__esModule", { value: true });
+function Commas(n) {
+ return n.toLocaleString("en-US");
}
+exports.default = Commas;
diff --git a/src/util/Commas.ts b/src/util/Commas.ts
new file mode 100644
index 000000000..3df5923d8
--- /dev/null
+++ b/src/util/Commas.ts
@@ -0,0 +1,3 @@
+export default function Commas(n: number): string {
+ return n.toLocaleString("en-US");
+}
diff --git a/src/util/times.js b/src/util/times.js
index 2324aebb0..a0e7a0b40 100644
--- a/src/util/times.js
+++ b/src/util/times.js
@@ -1,20 +1,22 @@
-export default function TimeString(time) {
- const currentDate = new Date();
- const createdDate = new Date(time);
-
- const timeDiff = currentDate - createdDate;
-
- const hoursDiff = timeDiff / (1000 * 60 * 60);
- const daysDiff = hoursDiff / 24;
- const monthsDiff = daysDiff / 30;
-
- if (monthsDiff >= 1) {
- return Math.floor(monthsDiff) + "달 전";
- } else if (daysDiff >= 1) {
- return Math.floor(daysDiff) + "일 전";
- } else if (hoursDiff >= 1) {
- return Math.floor(hoursDiff) + "시간 전";
- } else {
- return "방금 전";
- }
+Object.defineProperty(exports, "__esModule", { value: true });
+function TimeString(time) {
+ const currentDate = new Date();
+ const createdDate = new Date(time);
+ const timeDiff = currentDate.getTime() - createdDate.getTime();
+ const hoursDiff = timeDiff / (1000 * 60 * 60);
+ const daysDiff = hoursDiff / 24;
+ const monthsDiff = daysDiff / 30;
+ if (monthsDiff >= 1) {
+ return Math.floor(monthsDiff) + "달 전";
+ }
+ else if (daysDiff >= 1) {
+ return Math.floor(daysDiff) + "일 전";
+ }
+ else if (hoursDiff >= 1) {
+ return Math.floor(hoursDiff) + "시간 전";
+ }
+ else {
+ return "방금 전";
+ }
}
+exports.default = TimeString;
diff --git a/src/util/times.tsx b/src/util/times.tsx
new file mode 100644
index 000000000..f820f22c0
--- /dev/null
+++ b/src/util/times.tsx
@@ -0,0 +1,20 @@
+export default function TimeString(time: string | number | Date): string {
+ const currentDate = new Date();
+ const createdDate = new Date(time);
+
+ const timeDiff = currentDate.getTime() - createdDate.getTime();
+
+ const hoursDiff = timeDiff / (1000 * 60 * 60);
+ const daysDiff = hoursDiff / 24;
+ const monthsDiff = daysDiff / 30;
+
+ if (monthsDiff >= 1) {
+ return Math.floor(monthsDiff) + "달 전";
+ } else if (daysDiff >= 1) {
+ return Math.floor(daysDiff) + "일 전";
+ } else if (hoursDiff >= 1) {
+ return Math.floor(hoursDiff) + "시간 전";
+ } else {
+ return "방금 전";
+ }
+}
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 000000000..0825333a8
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,110 @@
+{
+ "compilerOptions": {
+ /* Visit https://aka.ms/tsconfig to read more about this file */
+
+ /* Projects */
+ // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
+ // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
+ // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
+ // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
+ // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
+ // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
+
+ /* Language and Environment */
+ "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
+ // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
+ // "jsx": "preserve", /* Specify what JSX code is generated. */
+ // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
+ // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
+ // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
+ // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
+ // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
+ // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
+ // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
+ // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
+ // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
+
+ /* Modules */
+ "module": "commonjs", /* Specify what module code is generated. */
+ // "rootDir": "./", /* Specify the root folder within your source files. */
+ // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
+ // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
+ // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
+ // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
+ "typeRoots": ["./@types", "./node_modules/@types", "src/types"], /* Specify multiple folders that act like './node_modules/@types'. */
+ // "types": [], /* Specify type package names to be included without being referenced in a source file. */
+ // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
+ // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
+ // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
+ // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
+ // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
+ // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
+ // "resolveJsonModule": true, /* Enable importing .json files. */
+ // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
+ // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */
+
+ /* JavaScript Support */
+ // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
+ // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
+ // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
+
+ /* Emit */
+ // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
+ // "declarationMap": true, /* Create sourcemaps for d.ts files. */
+ // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
+ // "sourceMap": true, /* Create source map files for emitted JavaScript files. */
+ // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
+ // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
+ // "outDir": "./", /* Specify an output folder for all emitted files. */
+ // "removeComments": true, /* Disable emitting comments. */
+ // "noEmit": true, /* Disable emitting files from a compilation. */
+ // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
+ // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
+ // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
+ // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
+ // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
+ // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
+ // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
+ // "newLine": "crlf", /* Set the newline character for emitting files. */
+ // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
+ // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
+ // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
+ // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
+ // "declarationDir": "./", /* Specify the output directory for generated declaration files. */
+ // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
+
+ /* Interop Constraints */
+ // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
+ // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
+ // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
+ "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
+ // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
+ "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
+
+ /* Type Checking */
+ "strict": true, /* Enable all strict type-checking options. */
+ // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
+ // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
+ // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
+ // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
+ // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
+ // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
+ // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
+ // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
+ // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
+ // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
+ // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
+ // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
+ // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
+ // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
+ // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
+ // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
+ // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
+ // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
+
+ /* Completeness */
+ // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
+ "skipLibCheck": true, /* Skip type checking all .d.ts files. */
+ "jsx": "react"
+ }
+}