diff --git a/vite-project/.env.development b/vite-project/.env.development
new file mode 100644
index 00000000..887a088a
--- /dev/null
+++ b/vite-project/.env.development
@@ -0,0 +1 @@
+VITE_API_BASE_URL=https://panda-market-api.vercel.app
\ No newline at end of file
diff --git a/vite-project/.env.production b/vite-project/.env.production
new file mode 100644
index 00000000..887a088a
--- /dev/null
+++ b/vite-project/.env.production
@@ -0,0 +1 @@
+VITE_API_BASE_URL=https://panda-market-api.vercel.app
\ No newline at end of file
diff --git a/vite-project/package-lock.json b/vite-project/package-lock.json
index e8e37d18..2d849630 100644
--- a/vite-project/package-lock.json
+++ b/vite-project/package-lock.json
@@ -10,7 +10,8 @@
"dependencies": {
"react": "^19.1.0",
"react-dom": "^19.1.0",
- "react-router-dom": "^7.7.0"
+ "react-router-dom": "^7.7.0",
+ "styled-components": "^6.1.19"
},
"devDependencies": {
"@eslint/js": "^9.25.0",
@@ -320,6 +321,27 @@
"node": ">=6.9.0"
}
},
+ "node_modules/@emotion/is-prop-valid": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.2.tgz",
+ "integrity": "sha512-uNsoYd37AFmaCdXlg6EYD1KaPOaRWRByMCYzbKUX4+hhMfrxdVSelShywL4JVaAeM/eHUOSprYBQls+/neX3pw==",
+ "license": "MIT",
+ "dependencies": {
+ "@emotion/memoize": "^0.8.1"
+ }
+ },
+ "node_modules/@emotion/memoize": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz",
+ "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==",
+ "license": "MIT"
+ },
+ "node_modules/@emotion/unitless": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz",
+ "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==",
+ "license": "MIT"
+ },
"node_modules/@esbuild/aix-ppc64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.8.tgz",
@@ -1387,6 +1409,12 @@
"@types/react": "^19.0.0"
}
},
+ "node_modules/@types/stylis": {
+ "version": "4.2.5",
+ "resolved": "https://registry.npmjs.org/@types/stylis/-/stylis-4.2.5.tgz",
+ "integrity": "sha512-1Xve+NMN7FWjY14vLoY5tL3BVEQ/n42YLwaqJIPYhotZ9uBHt87VceMwWQpzmdEt2TNXIorIFG+YeCUUW7RInw==",
+ "license": "MIT"
+ },
"node_modules/@vitejs/plugin-react": {
"version": "4.7.0",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz",
@@ -1532,6 +1560,15 @@
"node": ">=6"
}
},
+ "node_modules/camelize": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz",
+ "integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/caniuse-lite": {
"version": "1.0.30001727",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001727.tgz",
@@ -1628,11 +1665,30 @@
"node": ">= 8"
}
},
+ "node_modules/css-color-keywords": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz",
+ "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/css-to-react-native": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz",
+ "integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==",
+ "license": "MIT",
+ "dependencies": {
+ "camelize": "^1.0.0",
+ "css-color-keywords": "^1.0.0",
+ "postcss-value-parser": "^4.0.2"
+ }
+ },
"node_modules/csstype": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
- "dev": true,
"license": "MIT"
},
"node_modules/debug": {
@@ -2273,7 +2329,6 @@
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
- "dev": true,
"funding": [
{
"type": "github",
@@ -2389,7 +2444,6 @@
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
- "dev": true,
"license": "ISC"
},
"node_modules/picomatch": {
@@ -2434,6 +2488,12 @@
"node": "^10 || ^12 || >=14"
}
},
+ "node_modules/postcss-value-parser": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
+ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
+ "license": "MIT"
+ },
"node_modules/prelude-ls": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
@@ -2595,6 +2655,12 @@
"integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==",
"license": "MIT"
},
+ "node_modules/shallowequal": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz",
+ "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==",
+ "license": "MIT"
+ },
"node_modules/shebang-command": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
@@ -2622,7 +2688,6 @@
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
- "dev": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
@@ -2641,6 +2706,68 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/styled-components": {
+ "version": "6.1.19",
+ "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.1.19.tgz",
+ "integrity": "sha512-1v/e3Dl1BknC37cXMhwGomhO8AkYmN41CqyX9xhUDxry1ns3BFQy2lLDRQXJRdVVWB9OHemv/53xaStimvWyuA==",
+ "license": "MIT",
+ "dependencies": {
+ "@emotion/is-prop-valid": "1.2.2",
+ "@emotion/unitless": "0.8.1",
+ "@types/stylis": "4.2.5",
+ "css-to-react-native": "3.2.0",
+ "csstype": "3.1.3",
+ "postcss": "8.4.49",
+ "shallowequal": "1.1.0",
+ "stylis": "4.3.2",
+ "tslib": "2.6.2"
+ },
+ "engines": {
+ "node": ">= 16"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/styled-components"
+ },
+ "peerDependencies": {
+ "react": ">= 16.8.0",
+ "react-dom": ">= 16.8.0"
+ }
+ },
+ "node_modules/styled-components/node_modules/postcss": {
+ "version": "8.4.49",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz",
+ "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.7",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/stylis": {
+ "version": "4.3.2",
+ "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.2.tgz",
+ "integrity": "sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg==",
+ "license": "MIT"
+ },
"node_modules/supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
@@ -2671,6 +2798,12 @@
"url": "https://github.com/sponsors/SuperchupuDev"
}
},
+ "node_modules/tslib": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
+ "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==",
+ "license": "0BSD"
+ },
"node_modules/type-check": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
diff --git a/vite-project/package.json b/vite-project/package.json
index ca510320..85b90b29 100644
--- a/vite-project/package.json
+++ b/vite-project/package.json
@@ -12,7 +12,8 @@
"dependencies": {
"react": "^19.1.0",
"react-dom": "^19.1.0",
- "react-router-dom": "^7.7.0"
+ "react-router-dom": "^7.7.0",
+ "styled-components": "^6.1.19"
},
"devDependencies": {
"@eslint/js": "^9.25.0",
diff --git a/vite-project/src/api/products.js b/vite-project/src/api/products.js
index 1a1cd7d5..fe32ac72 100644
--- a/vite-project/src/api/products.js
+++ b/vite-project/src/api/products.js
@@ -1,12 +1,10 @@
-const BASE_URL = "https://panda-market-api.vercel.app";
-
export async function fetchProducts({
keyword = "",
page = 1,
pageSize = 10,
orderBy = "recent",
} = {}) {
- const url = new URL(`${BASE_URL}/products`);
+ const url = new URL(`${import.meta.env.VITE_API_BASE_URL}/products`);
url.searchParams.append("page", page);
url.searchParams.append("pageSize", pageSize);
url.searchParams.append("orderBy", orderBy);
diff --git a/vite-project/src/assets/ic-plus-gray.svg b/vite-project/src/assets/ic-plus-gray.svg
new file mode 100644
index 00000000..5bb9abf5
--- /dev/null
+++ b/vite-project/src/assets/ic-plus-gray.svg
@@ -0,0 +1,4 @@
+
diff --git a/vite-project/src/assets/ic-xmark-fill.svg b/vite-project/src/assets/ic-xmark-fill.svg
new file mode 100644
index 00000000..87a00547
--- /dev/null
+++ b/vite-project/src/assets/ic-xmark-fill.svg
@@ -0,0 +1,5 @@
+
diff --git a/vite-project/src/components/Button.css b/vite-project/src/components/Button.css
deleted file mode 100644
index 35f0f0c2..00000000
--- a/vite-project/src/components/Button.css
+++ /dev/null
@@ -1,13 +0,0 @@
-@import url("../palette.css");
-
-.Button {
- background-color: var(--color-primary-100);
- padding: 8px 23px;
- color: var(--color-cool-gray-100);
- font-size: 16px;
- font-weight: 600;
- line-height: 26px;
- border-radius: 8px;
- border: none;
- cursor: pointer;
-}
diff --git a/vite-project/src/components/Button.jsx b/vite-project/src/components/Button.jsx
index 9f9facb4..4019d336 100644
--- a/vite-project/src/components/Button.jsx
+++ b/vite-project/src/components/Button.jsx
@@ -1,11 +1,20 @@
-import "./Button.css";
+import styled from "styled-components";
-function Button({ title, onClick }) {
- return (
-
- );
+const StyledButton = styled.button`
+ background-color: ${({ disabled }) =>
+ disabled ? "var(--color-cool-gray-400)" : "var(--color-primary-100)"};
+ padding: 8px 23px;
+ color: var(--color-cool-gray-100);
+ font-size: 16px;
+ font-weight: 600;
+ line-height: 26px;
+ border-radius: 8px;
+ border: none;
+ cursor: ${({ disabled }) => (disabled ? "default" : "pointer")};
+`;
+
+function Button({ children, ...props }) {
+ return
{title}
-{priceString}
-