diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..1588f65 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,24 @@ +--- +name: ๐Ÿ› Bug Report +about: ๋ฒ„๊ทธ๋ฅผ ๋ณด๊ณ ํ•ฉ๋‹ˆ๋‹ค +title: "[Bug] " +labels: bug +assignees: '' +--- + +## ๐Ÿž ๋ฒ„๊ทธ ์„ค๋ช… +- ๋ฒ„๊ทธ์— ๋Œ€ํ•ด ๊ฐ„๋‹จํžˆ ์„ค๋ช…ํ•ด์ฃผ์„ธ์š”. + +## โœ… ์žฌํ˜„ ๋ฐฉ๋ฒ• +- ๋ฒ„๊ทธ๋ฅผ ์žฌํ˜„ํ•˜๋ ค๋ฉด ๋‹ค์Œ ๋‹จ๊ณ„๋ฅผ ๋”ฐ๋ฅด์„ธ์š”: + 1. [์˜ˆ: ํŽ˜์ด์ง€ ์ด๋™ ๊ฒฝ๋กœ] + 2. [์˜ˆ: ํŠน์ • ๋ฒ„ํŠผ ํด๋ฆญ] + 3. [์˜ˆ: ๊ธฐ๋Œ€๋˜๋Š” ๊ฒฐ๊ณผ] + +## ๐Ÿ–ฅ ํ™˜๊ฒฝ ์ •๋ณด +- OS: [Windows, macOS, Linux ๋“ฑ] +- Browser: [Chrome, Firefox ๋“ฑ] +- Version: [์•ฑ/๋ธŒ๋ผ์šฐ์ € ๋ฒ„์ „] + +## ๐Ÿ“ธ ์Šคํฌ๋ฆฐ์ƒท +- ๋ฒ„๊ทธ๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” ์Šคํฌ๋ฆฐ์ƒท์„ ์ถ”๊ฐ€ํ•ด์ฃผ์„ธ์š” (ํ•„์š”ํ•œ ๊ฒฝ์šฐ). diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..9880a67 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,16 @@ +--- +name: โœจ Feature Request +about: ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์„ ์š”์ฒญํ•ฉ๋‹ˆ๋‹ค +title: "[Feature] " +labels: enhancement +assignees: '' +--- + +## ๐ŸŒŸ ๊ธฐ๋Šฅ ์„ค๋ช… +- ์ถ”๊ฐ€ํ•˜๊ณ  ์‹ถ์€ ๊ธฐ๋Šฅ์ด๋‚˜ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ๊ฐ„๋žตํžˆ ์„ค๋ช…ํ•ด์ฃผ์„ธ์š”. + +## ๐Ÿค” ์ด์œ  +- ์ด ๊ธฐ๋Šฅ์ด ์™œ ํ•„์š”ํ•œ์ง€, ์–ด๋–ค ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š”์ง€ ์„ค๋ช…ํ•ด์ฃผ์„ธ์š”. + +## ๐Ÿ“‹ ์ถ”๊ฐ€ ์ •๋ณด +- ๊ธฐ๋Šฅ๊ณผ ๊ด€๋ จ๋œ ์ฐธ๊ณ  ์ž๋ฃŒ๋‚˜ ์ถ”๊ฐ€ ์ •๋ณด๋ฅผ ํฌํ•จํ•ด์ฃผ์„ธ์š”. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..3d75a26 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,34 @@ +## ๐Ÿ“‹ ์š”์•ฝ +- ์ด Pull Request์˜ ๋ชฉ์ ๊ณผ ์ฃผ์š” ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ๊ฐ„๋žตํžˆ ์„ค๋ช…ํ•ด์ฃผ์„ธ์š”. + +## ๐Ÿ›  ๋ณ€๊ฒฝ ์‚ฌํ•ญ +- ์ด๋ฒˆ Pull Request์—์„œ ์ž‘์—…ํ•œ ์ฃผ์š” ๋ณ€๊ฒฝ ์‚ฌํ•ญ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค: + - [x] ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ ์ถ”๊ฐ€ ๋˜๋Š” ๋ฒ„๊ทธ ์ˆ˜์ • + - [x] ์ฝ”๋“œ ๋ฆฌํŒฉํ† ๋ง ๋˜๋Š” ์ตœ์ ํ™” + - [x] ๋ฌธ์„œ ์—…๋ฐ์ดํŠธ + +## ๐Ÿ”— ๊ด€๋ จ ์ด์Šˆ +- ์ด PR๋กœ ํ•ด๊ฒฐ๋˜๋Š” ์ด์Šˆ: #[์ด์Šˆ ๋ฒˆํ˜ธ] +- ๊ด€๋ จ๋œ ์ด์Šˆ: #[์ด์Šˆ ๋ฒˆํ˜ธ] + +## ๐Ÿ“ธ ์Šคํฌ๋ฆฐ์ƒท ๋˜๋Š” GIF (ํ•ด๋‹น๋˜๋Š” ๊ฒฝ์šฐ) +- UI ๋ณ€๊ฒฝ์ด ์žˆ๋Š” ๊ฒฝ์šฐ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ๋ณด์—ฌ์ฃผ๋Š” ์Šคํฌ๋ฆฐ์ƒท์ด๋‚˜ GIF๋ฅผ ์ถ”๊ฐ€ํ•ด์ฃผ์„ธ์š”. + +## โœ… ์ฒดํฌ๋ฆฌ์ŠคํŠธ +- [ ] ์ฝ”๋“œ๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ๋™์ž‘ํ•˜๋Š”์ง€ ํ…Œ์ŠคํŠธํ–ˆ์Šต๋‹ˆ๋‹ค. +- [ ] ๊ด€๋ จ ๋ฌธ์„œ๋ฅผ ์ž‘์„ฑํ•˜๊ฑฐ๋‚˜ ์—…๋ฐ์ดํŠธํ–ˆ์Šต๋‹ˆ๋‹ค. (ํ•ด๋‹น๋˜๋Š” ๊ฒฝ์šฐ) +- [ ] ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ๋ฐ˜์˜ํ•œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค. +- [ ] ๋ชจ๋“  ํ…Œ์ŠคํŠธ๊ฐ€ ์„ฑ๊ณต์ ์œผ๋กœ ํ†ต๊ณผํ–ˆ์Šต๋‹ˆ๋‹ค. + +## ๐Ÿ›ก ํ…Œ์ŠคํŠธ ๋ฐฉ๋ฒ• +- ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•œ ํ…Œ์ŠคํŠธ ๋ฐฉ๋ฒ•์„ ๋‹จ๊ณ„๋ณ„๋กœ ์ž‘์„ฑํ•ด์ฃผ์„ธ์š”: + 1. ๋ธŒ๋žœ์น˜๋ฅผ ๋กœ์ปฌ๋กœ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค. + 2. ์•„๋ž˜ ๋ช…๋ น์–ด๋ฅผ ์‚ฌ์šฉํ•ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋˜๋Š” ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค: + ``` + # ์˜ˆ์‹œ ๋ช…๋ น์–ด + ./run_tests.sh + ``` + 3. ์˜ˆ์ƒ ๋™์ž‘์ด๋‚˜ ์ถœ๋ ฅ ๊ฒฐ๊ณผ๋ฅผ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. + +## ๐Ÿ“š ์ถ”๊ฐ€ ์ฐธ๊ณ  ์‚ฌํ•ญ +- ์ด PR๊ณผ ๊ด€๋ จํ•ด ๋ฆฌ๋ทฐ์–ด๊ฐ€ ์•Œ์•„์•ผ ํ•  ์ถ”๊ฐ€ ๋‚ด์šฉ์ด ์žˆ๋‹ค๋ฉด ์ž‘์„ฑํ•ด์ฃผ์„ธ์š”. diff --git a/package-lock.json b/package-lock.json index 47d6e6b..e7809f9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,18 @@ { - "name": "vite-project", + "name": "refrigerator-front", "version": "0.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "vite-project", + "name": "refrigerator-front", "version": "0.0.0", "dependencies": { "@reduxjs/toolkit": "^2.5.0", "axios": "^1.7.9", + "bootstrap": "^5.3.3", "react": "^18.3.1", + "react-bootstrap": "^2.10.7", "react-dom": "^18.3.1", "react-redux": "^9.2.0", "react-router-dom": "^6.28.0" @@ -266,6 +268,18 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/runtime": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", + "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", + "license": "MIT", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/template": { "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", @@ -991,6 +1005,31 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@react-aria/ssr": { + "version": "3.9.7", + "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.7.tgz", + "integrity": "sha512-GQygZaGlmYjmYM+tiNBA5C6acmiDWF52Nqd40bBp0Znk4M4hP+LTmI0lpI1BuKMw45T8RIhrAsICIfKwZvi2Gg==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0" + }, + "engines": { + "node": ">= 12" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, "node_modules/@reduxjs/toolkit": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.5.0.tgz", @@ -1024,6 +1063,60 @@ "node": ">=14.0.0" } }, + "node_modules/@restart/hooks": { + "version": "0.4.16", + "resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.4.16.tgz", + "integrity": "sha512-f7aCv7c+nU/3mF7NWLtVVr0Ra80RqsO89hO72r+Y/nvQr5+q0UFGkocElTH6MJApvReVh6JHUFYn2cw1WdHF3w==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.3" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@restart/ui": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@restart/ui/-/ui-1.9.2.tgz", + "integrity": "sha512-MWWqJqSyqUWWPBOOiRQrX57CBc/9CoYONg7sE+uag72GCAuYrHGU5c49vU5s4BUSBgiKNY6rL7TULqGDrouUaA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.26.0", + "@popperjs/core": "^2.11.8", + "@react-aria/ssr": "^3.5.0", + "@restart/hooks": "^0.5.0", + "@types/warning": "^3.0.3", + "dequal": "^2.0.3", + "dom-helpers": "^5.2.0", + "uncontrollable": "^8.0.4", + "warning": "^4.0.3" + }, + "peerDependencies": { + "react": ">=16.14.0", + "react-dom": ">=16.14.0" + } + }, + "node_modules/@restart/ui/node_modules/@restart/hooks": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.5.0.tgz", + "integrity": "sha512-wS+h6IusJCPjTkmOOrRZxIPICD/mtFA3PRZviutoM23/b7akyDGfZF/WS+nIFk27u7JDhPE2+0GBdZxjSqHZkg==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.3" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@restart/ui/node_modules/uncontrollable": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-8.0.4.tgz", + "integrity": "sha512-ulRWYWHvscPFc0QQXvyJjY6LIXU56f0h8pQFvhxiKk5V1fcI8gp9Ht9leVAhrVjzqMw0BgjspBINx9r6oyJUvQ==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.14.0" + } + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.28.1", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.28.1.tgz", @@ -1290,6 +1383,15 @@ "win32" ] }, + "node_modules/@swc/helpers": { + "version": "0.5.15", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", + "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -1353,14 +1455,12 @@ "version": "15.7.14", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==", - "devOptional": true, "license": "MIT" }, "node_modules/@types/react": { "version": "18.3.18", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.18.tgz", "integrity": "sha512-t4yC+vtgnkYjNSKlFx1jkAhH8LgTo2N/7Qvi83kdEaUtMDiwpbLAktKDaAMlRcJ5eSxZkH74eEGt1ky31d7kfQ==", - "devOptional": true, "license": "MIT", "dependencies": { "@types/prop-types": "*", @@ -1377,12 +1477,27 @@ "@types/react": "^18.0.0" } }, + "node_modules/@types/react-transition-group": { + "version": "4.4.12", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz", + "integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*" + } + }, "node_modules/@types/use-sync-external-store": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz", "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==", "license": "MIT" }, + "node_modules/@types/warning": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.3.tgz", + "integrity": "sha512-D1XC7WK8K+zZEveUPY+cf4+kgauk8N4eHr/XIHXGlGYkHLud6hK9lYfZk1ry1TNh798cZUCgb6MqGEG8DkJt6Q==", + "license": "MIT" + }, "node_modules/@vitejs/plugin-react": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.4.tgz", @@ -1642,6 +1757,25 @@ "dev": true, "license": "MIT" }, + "node_modules/bootstrap": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.3.tgz", + "integrity": "sha512-8HLCdWgyoMguSO9o+aH+iuZ+aht+mzW0u3HIMzVu7Srrpv7EBBxTnrFlSCskwdY1+EOFQSm7uMJhNQHkdPcmjg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/twbs" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + } + ], + "license": "MIT", + "peerDependencies": { + "@popperjs/core": "^2.11.8" + } + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -1784,6 +1918,12 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/classnames": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==", + "license": "MIT" + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -1849,7 +1989,6 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "devOptional": true, "license": "MIT" }, "node_modules/data-view-buffer": { @@ -1976,6 +2115,15 @@ "node": ">=0.4.0" } }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/doctrine": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", @@ -1989,6 +2137,16 @@ "node": ">=0.10.0" } }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -2887,6 +3045,15 @@ "node": ">= 0.4" } }, + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, "node_modules/is-array-buffer": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", @@ -3532,7 +3699,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -3785,7 +3951,6 @@ "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dev": true, "license": "MIT", "dependencies": { "loose-envify": "^1.4.0", @@ -3793,6 +3958,19 @@ "react-is": "^16.13.1" } }, + "node_modules/prop-types-extra": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/prop-types-extra/-/prop-types-extra-1.1.1.tgz", + "integrity": "sha512-59+AHNnHYCdiC+vMwY52WmvP5dM3QLeoumYuEyceQDi9aEhtwN9zIQ2ZNo25sMyXnbh32h+P1ezDsUpUH3JAew==", + "license": "MIT", + "dependencies": { + "react-is": "^16.3.2", + "warning": "^4.0.0" + }, + "peerDependencies": { + "react": ">=0.14.0" + } + }, "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", @@ -3821,6 +3999,37 @@ "node": ">=0.10.0" } }, + "node_modules/react-bootstrap": { + "version": "2.10.7", + "resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-2.10.7.tgz", + "integrity": "sha512-w6mWb3uytB5A18S2oTZpYghcOUK30neMBBvZ/bEfA+WIF2dF4OGqjzoFVMpVXBjtyf92gkmRToHlddiMAVhQqQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.24.7", + "@restart/hooks": "^0.4.9", + "@restart/ui": "^1.9.2", + "@types/prop-types": "^15.7.12", + "@types/react-transition-group": "^4.4.6", + "classnames": "^2.3.2", + "dom-helpers": "^5.2.1", + "invariant": "^2.2.4", + "prop-types": "^15.8.1", + "prop-types-extra": "^1.1.0", + "react-transition-group": "^4.4.5", + "uncontrollable": "^7.2.1", + "warning": "^4.0.3" + }, + "peerDependencies": { + "@types/react": ">=16.14.8", + "react": ">=16.14.0", + "react-dom": ">=16.14.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/react-dom": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", @@ -3838,7 +4047,12 @@ "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true, + "license": "MIT" + }, + "node_modules/react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==", "license": "MIT" }, "node_modules/react-redux": { @@ -3906,6 +4120,22 @@ "react-dom": ">=16.8" } }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "license": "BSD-3-Clause", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, "node_modules/redux": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", @@ -3944,6 +4174,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "license": "MIT" + }, "node_modules/regexp.prototype.flags": { "version": "1.5.3", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.3.tgz", @@ -4373,6 +4609,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -4483,6 +4725,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/uncontrollable": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-7.2.1.tgz", + "integrity": "sha512-svtcfoTADIB0nT9nltgjujTi7BzVmwjZClOmskKu/E8FW9BXzg9os8OLr4f8Dlnk0rYWJIWr4wv9eKUXiQvQwQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.6.3", + "@types/react": ">=16.9.11", + "invariant": "^2.2.4", + "react-lifecycles-compat": "^3.0.4" + }, + "peerDependencies": { + "react": ">=15.0.0" + } + }, "node_modules/update-browserslist-db": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", @@ -4605,6 +4862,15 @@ } } }, + "node_modules/warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/package.json b/package.json index 259d0a5..1612935 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,9 @@ "dependencies": { "@reduxjs/toolkit": "^2.5.0", "axios": "^1.7.9", + "bootstrap": "^5.3.3", "react": "^18.3.1", + "react-bootstrap": "^2.10.7", "react-dom": "^18.3.1", "react-redux": "^9.2.0", "react-router-dom": "^6.28.0" diff --git a/src/App.jsx b/src/App.jsx index 7e46023..a237256 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,10 +1,30 @@ +import { BrowserRouter, Routes, Route } from "react-router-dom" +import Layout from "./pages/layouts/Layout" +import Ingredient from "./pages/ingredient/Ingregdient" +import Recipe from "./pages/recipe/Recipe" +import RecipeDetail from "./pages/recipe/RecipeDetail" +import {AddRecipe} from "./pages/recipe/AddRecipe" +import Join from "./pages/user/Join" + function App() { + return ( + + + }> + + }/> + {/* } /> */} + + + } /> + } /> + }/> + + } /> + - return ( -

- test -

- ) +
+
+ ) } - -export default App +export default App \ No newline at end of file diff --git a/src/assets/css/Ingredient/ingredient/Ingredient.module.css b/src/assets/css/Ingredient/ingredient/Ingredient.module.css new file mode 100644 index 0000000..547c8d9 --- /dev/null +++ b/src/assets/css/Ingredient/ingredient/Ingredient.module.css @@ -0,0 +1,5 @@ +/* Ingredient.module.css */ +.userIngredient { + border: 3px solid black; + background-color: lightgreen; +} diff --git a/src/assets/css/Ingredient/userIngredient/UsersIngredient.module.css b/src/assets/css/Ingredient/userIngredient/UsersIngredient.module.css new file mode 100644 index 0000000..991e216 --- /dev/null +++ b/src/assets/css/Ingredient/userIngredient/UsersIngredient.module.css @@ -0,0 +1,86 @@ +.container { + padding: 1rem; + background-color: #f0fff0; /* ์—ฐํ•œ ๋…น์ƒ‰ ๋ฐฐ๊ฒฝ */ + text-align: center; +} + +.cardContainer { + display: flex; + flex-wrap: wrap; /* ์ค„๋ฐ”๊ฟˆ ํ—ˆ์šฉ */ + justify-content: flex-start; /* ์นด๋“œ๋“ค์„ ์™ผ์ชฝ ์ •๋ ฌ */ + gap: 1rem; /* ์นด๋“œ ๊ฐ„๊ฒฉ */ +} + +.header { + display: flex; + flex-direction: column; /* ์ˆ˜์ง์œผ๋กœ ๋ฐฐ์น˜ */ + align-items: center; /* ์ˆ˜ํ‰ ๊ฐ€์šด๋ฐ ์ •๋ ฌ */ + justify-content: center; /* ์ˆ˜์ง ๊ฐ€์šด๋ฐ ์ •๋ ฌ */ + margin-bottom: 1rem; /* ์ œ๋ชฉ๊ณผ ์นด๋“œ ๊ฐ„๊ฒฉ ์ถ”๊ฐ€ */ + padding: 0 1rem; +} + +.header h1 { + font-size: 1.8rem; + color: #333; + margin: 0 0 0.5rem; /* ์•„๋ž˜ ๊ฐ„๊ฒฉ ์ถ”๊ฐ€ */ +} + +.filterDropdown { + padding: 0.5rem 1rem; + font-size: 1rem; + border: 1px solid #ddd; /* ๋“œ๋กญ๋‹ค์šด ํ…Œ๋‘๋ฆฌ */ + border-radius: 5px; /* ๋ชจ์„œ๋ฆฌ ๋‘ฅ๊ธ€๊ฒŒ */ + cursor: pointer; + background-color: #ffffff; /* ๋“œ๋กญ๋‹ค์šด ๋ฐฐ๊ฒฝ */ + color: #333; /* ํ…์ŠคํŠธ ์ƒ‰์ƒ */ + transition: border-color 0.3s ease; +} + +.filterDropdown:focus { + outline: none; + border-color: #28a745; /* ์ดˆ๋ก์ƒ‰ ํ…Œ๋‘๋ฆฌ */ +} + +.filterDropdown:hover { + border-color: #28a745; /* ํ˜ธ๋ฒ„ ์‹œ ํ…Œ๋‘๋ฆฌ ์ƒ‰์ƒ */ +} + +.filters { + display: flex; + gap: 1rem; /* ํ•„ํ„ฐ ๊ฐ„ ๊ฐ„๊ฒฉ */ + align-items: center; +} + +.filterDropdown { + padding: 0.5rem 1rem; + font-size: 1rem; + border: 1px solid #ddd; + border-radius: 5px; + cursor: pointer; + background-color: #ffffff; + color: #333; + transition: border-color 0.3s ease; +} + +.favoriteButton { + padding: 0.5rem 1rem; + font-size: 1rem; + border: 1px solid #ddd; + border-radius: 5px; + cursor: pointer; + background-color: #ffffff; + color: #333; + transition: background-color 0.3s ease, border-color 0.3s ease; +} + +.favoriteButton.active { + background-color: #28a745; /* ํ™œ์„ฑํ™” ์ƒํƒœ */ + color: white; /* ํ…์ŠคํŠธ ์ƒ‰์ƒ */ + border-color: #28a745; +} + +.favoriteButton:hover { + background-color: #218838; /* Hover ์‹œ ์–ด๋‘์šด ์ดˆ๋ก์ƒ‰ */ + color: white; +} \ No newline at end of file diff --git a/src/assets/css/Ingredient/userIngredient/UsersIngredientItem.module.css b/src/assets/css/Ingredient/userIngredient/UsersIngredientItem.module.css new file mode 100644 index 0000000..5b02d62 --- /dev/null +++ b/src/assets/css/Ingredient/userIngredient/UsersIngredientItem.module.css @@ -0,0 +1,102 @@ +.card { + width: 260px; /* ๊ฐ€๋กœ ๊ธธ์ด */ + height: auto; /* ์„ธ๋กœ ๊ธธ์ด ์ž๋™ ์กฐ์ • */ + background-color: #ffffff; /* ์นด๋“œ ๋ฐฐ๊ฒฝ */ + padding: 0.5rem; /* ๋‚ด๋ถ€ ์—ฌ๋ฐฑ ์ตœ์†Œํ™” */ + border: 1px solid #ddd; + border-radius: 10px; + display: flex; + flex-direction: column; + justify-content: space-between; /* ์ƒ๋‹จ, ์ค‘๋‹จ, ํ•˜๋‹จ ์˜์—ญ ๋ถ„๋ฆฌ */ + text-align: left; + position: relative; /* ์ „์ฒด ์นด๋“œ ๋‚ด๋ถ€ ๋ฐฐ์น˜ */ + transition: all 0.3s ease; +} + +.card img { + border-radius: 10px; + height: 120px; /* ์ด๋ฏธ์ง€ ๋†’์ด */ + width: 100%; + object-fit: cover; /* ์ด๋ฏธ์ง€ ๋น„์œจ ์œ ์ง€ */ + margin-bottom: 0; /* ์ด๋ฏธ์ง€์™€ ํ…์ŠคํŠธ ๊ฐ„๊ฒฉ ์ œ๊ฑฐ */ +} + +/* ์ฆ๊ฒจ์ฐพ๊ธฐ ๋ฒ„ํŠผ๊ณผ ์ˆ˜๋Ÿ‰ ์˜์—ญ */ +.favoriteAndQuantity { + display: flex; + justify-content: space-between; /* ์ขŒ์šฐ๋กœ ๋ฐฐ์น˜ */ + align-items: center; + margin-top: -30px; /* ์ด๋ฏธ์ง€์™€ ๊ฒน์น˜๋„๋ก ์กฐ์ • */ + padding: 0; /* ์ขŒ์šฐ ์—ฌ๋ฐฑ ์ œ๊ฑฐ */ +} + +.favoriteButton { + display: flex; /* Flexbox๋กœ ๋‚ด๋ถ€ ์ •๋ ฌ */ + align-items: center; /* ์„ธ๋กœ ๊ฐ€์šด๋ฐ ์ •๋ ฌ */ + justify-content: center; /* ๊ฐ€๋กœ ๊ฐ€์šด๋ฐ ์ •๋ ฌ */ + background-color: rgba(207, 205, 205, 0.7); /* ๋ฐ˜ํˆฌ๋ช… ๋ฐฐ๊ฒฝ */ + border: none; + font-size: 0.9rem; /* ๋ณ„ ํฌ๊ธฐ ์กฐ์ • */ + cursor: pointer; + border-radius: 50%; /* ๋ฒ„ํŠผ์„ ๋™๊ทธ๋ž—๊ฒŒ */ + width: 1.5rem; /* ๋ฒ„ํŠผ ๋„ˆ๋น„ */ + height: 1.5rem; /* ๋ฒ„ํŠผ ๋†’์ด */ + padding: 0; /* ๋‚ด๋ถ€ ์—ฌ๋ฐฑ ์ œ๊ฑฐ */ + margin-top: 2px; +} + +.favoriteButton:hover { + background-color: rgb(66, 55, 55)/* Hover ์‹œ ์ƒ‰์ƒ ๋ณ€๊ฒฝ */ +} + + + +/* ์ˆ˜๋Ÿ‰ ํ‘œ์‹œ */ +.quantity { + background-color: rgba(207, 205, 205, 0.7); /* ๋ฐ˜ํˆฌ๋ช… ๋ฐฐ๊ฒฝ */ + color: black; + padding: 0.2rem 0.4rem; /* ์—ฌ๋ฐฑ ์กฐ์ • */ + border-radius: 5px; + font-size: 0.85rem; /* ํ…์ŠคํŠธ ํฌ๊ธฐ ์กฐ์ • */ +} + +/* ์นด๋“œ ํ…์ŠคํŠธ ์„น์…˜ */ +.details { + font-size: 0.85rem; + color: #666; + margin-top: 0.1rem; /* ํ…์ŠคํŠธ ๊ฐ„๊ฒฉ ์ตœ์†Œํ™” */ + line-height: 1.4; +} + +.details strong { + color: #333; + font-weight: bold; +} + +.title { + font-size: 1rem; + font-weight: bold; + margin: 0.2rem 0; /* ์ด๋ฏธ์ง€์™€ ์ด๋ฆ„ ๊ฐ„๊ฒฉ ์ตœ์†Œํ™” */ + text-align: center; +} + +/* ์‚ฌ์šฉํ•˜๊ธฐ ๋ฒ„ํŠผ */ +.useButton { + align-self: flex-end; /* ์˜ค๋ฅธ์ชฝ ์ •๋ ฌ */ + background-color: rgba(207, 205, 205, 1); + color: black; + font-weight: 500; + border: none; + padding: 0.5rem 1rem; + border-radius: 5px; + cursor: pointer; + font-size: 0.85rem; + position: absolute; /* ์˜ค๋ฅธ์ชฝ ํ•˜๋‹จ ๊ณ ์ • */ + bottom: 10px; + right: 10px; + z-index: 5; /* ๋‹ค๋ฅธ ์š”์†Œ์™€ ๊ฒน์น˜์ง€ ์•Š๋„๋ก */ +} + +.useButton:hover { + background-color: rgb(179, 178, 178); +} diff --git a/src/assets/css/default.css b/src/assets/css/default.css deleted file mode 100644 index e69de29..0000000 diff --git a/src/assets/css/main.module.css b/src/assets/css/main.module.css new file mode 100644 index 0000000..21aa0a2 --- /dev/null +++ b/src/assets/css/main.module.css @@ -0,0 +1,25 @@ +@font-face { + font-family: 'Pretendard-Regular'; + src: url('https://fastly.jsdelivr.net/gh/Project-Noonnu/noonfonts_2107@1.1/Pretendard-Regular.woff') format('woff'); + font-weight: 400; + font-style: normal; +} + +* { + font-family: Pretendard-Regular; + margin: 0; + padding: 0; + box-sizing: border-box; +} + +.mainContainer { + display: flex; + flex-direction: column; /* ์ƒํ•˜ ๋ ˆ์ด์•„์›ƒ ์ •๋ ฌ */ + min-height: 1200px; /* ํ™”๋ฉด ์ „์ฒด ๋†’์ด */ + background-color: #f9f9f9; +} + +main { + flex: 1; /* ๋ฉ”์ธ ์ฝ˜ํ…์ธ ๊ฐ€ ๋‚จ์€ ๊ณต๊ฐ„์„ ์ฐจ์ง€ */ + padding: 1rem; +} \ No newline at end of file diff --git a/src/assets/css/main/footer.module.css b/src/assets/css/main/footer.module.css new file mode 100644 index 0000000..4da668b --- /dev/null +++ b/src/assets/css/main/footer.module.css @@ -0,0 +1,75 @@ +.footer { + display: flex; + justify-content: space-between; /* ์ขŒ์šฐ๋กœ ์˜์—ญ ๋‚˜๋ˆ” */ + align-items: center; + background-color: #e0f7ff; /* ์—ฐํ•œ ํŒŒ๋ž€์ƒ‰ ๋ฐฐ๊ฒฝ */ + color: #333; + padding: 2rem; +} + +.leftSection { + display: flex; + flex-direction: column; + align-items: flex-start; /* ์™ผ์ชฝ ์ •๋ ฌ */ + gap: 1rem; +} + +.logo { + font-size: 2.2rem; + font-weight: bold; + margin: 0; +} + +.logoReci { + color: black; +} + +.logoPick { + color: #007bff; /* ํŒŒ๋ž€์ƒ‰ */ +} + +.socialIcons { + display: flex; + gap: 1rem; +} + +.icon { + font-size: 1.1rem; + text-decoration: none; + color: black; + transition: transform 0.3s ease, color 0.3s ease; +} + +.icon:hover { + transform: scale(1.2); /* ์•„์ด์ฝ˜ ํ™•๋Œ€ */ + color: #007bff; +} + +.rightSection { + display: flex; + gap: 3rem; /* ๊ฐ ๋งํฌ ์„น์…˜ ๊ฐ„ ๊ฐ„๊ฒฉ */ + text-align: left; +} + +.linksGroup { + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.linksGroup h3 { + font-size: 1.2rem; + font-weight: bold; + margin-bottom: 0.5rem; +} + +.linksGroup a { + text-decoration: none; + color: #333; + font-size: 1rem; + transition: color 0.3s ease; +} + +.linksGroup a:hover { + color: #007bff; +} diff --git a/src/assets/css/main/header.module.css b/src/assets/css/main/header.module.css new file mode 100644 index 0000000..7115f26 --- /dev/null +++ b/src/assets/css/main/header.module.css @@ -0,0 +1,44 @@ +.header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 1rem 2rem; + color: black; + box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.1); + background-color: white; + margin-bottom: 15px; + position: sticky; /* ์Šคํฌ๋กค ์‹œ ์ƒ๋‹จ ๊ณ ์ • */ + top: 0; + z-index: 1000; /* ๋‹ค๋ฅธ ์š”์†Œ ์œ„๋กœ */ + padding: 0.8rem 2rem; +} + +.logo h1 { + font-size: 1.8rem; + font-weight: bolder; + margin: 0; +} +.logoReci { + color: black; /* Reci์˜ ๊ธฐ๋ณธ ์ƒ‰์ƒ */ +} + +.logoPick { + color: #3FA2F6; /* Pick์˜ ์ดˆ๋ก์ƒ‰ */ + font-weight: 900; /* ๊ฐ•์กฐ */ +} + +.nav { + display: flex; + gap: 1.5rem; +} + +.nav a { + color: black; + text-decoration: none; + font-size: 1rem; + transition: color 0.3s ease; +} + +.nav a:hover { + color: rgb(61, 60, 60); +} diff --git a/src/assets/css/recipe/recipe.css b/src/assets/css/recipe/recipe.css new file mode 100644 index 0000000..8a3bd0a --- /dev/null +++ b/src/assets/css/recipe/recipe.css @@ -0,0 +1,4 @@ +.recipe-wrapper { + display: flex; + gap: 20px; +} \ No newline at end of file diff --git a/src/assets/css/user/Join.css b/src/assets/css/user/Join.css new file mode 100644 index 0000000..be56060 --- /dev/null +++ b/src/assets/css/user/Join.css @@ -0,0 +1,71 @@ +.join-container { + width: 460px; + margin: 0 auto; +} + +.join-container h2 { + font-size: 30px; + font-weight: bold; + text-align: center; + margin: 0 0 16px 0; +} + +.join-container p { + text-align: center; + color: #666; + margin: 0 0 32px 0; + font-size: 16px; +} + +.form-label { + display: block; + font-size: 15px; + margin: 0 0 8px 0; +} + +.form-control { + width: 100%; + height: 48px; + padding: 0 16px; + border: 1px solid #DDE2E5; + border-radius: 5px; + background: #F8F9FA; + font-size: 14px; + box-sizing: border-box; + margin-bottom: 8px; +} + +.form-control:focus { + border-color: #3FA2F6; + background: #fff; + outline: none; +} + +.invalid-feedback { + display: block; + color: #FF3B3B; + font-size: 14px; + margin: 0 0 16px 0; +} + +.btn-primary { + width: 100%; + height: 48px; + background: #3FA2F6; + border: none; + border-radius: 5px; + color: white; + font-size: 16px; + cursor: pointer; + box-sizing: border-box; + margin-top: 5px; +} + +.btn-primary:hover { + background: #3691E0; +} + +.btn-primary:disabled { + background: #A5D3FB; + cursor: not-allowed; +} \ No newline at end of file diff --git a/src/assets/image/noimage.jpg b/src/assets/image/noimage.jpg new file mode 100644 index 0000000..110d770 Binary files /dev/null and b/src/assets/image/noimage.jpg differ diff --git a/src/assets/image/recipeimage.png b/src/assets/image/recipeimage.png new file mode 100644 index 0000000..55c5d9d Binary files /dev/null and b/src/assets/image/recipeimage.png differ diff --git a/src/components/ingredient/userIngredient/UsersIngredient.jsx b/src/components/ingredient/userIngredient/UsersIngredient.jsx new file mode 100644 index 0000000..8de30f9 --- /dev/null +++ b/src/components/ingredient/userIngredient/UsersIngredient.jsx @@ -0,0 +1,86 @@ +import { useEffect, useState } from "react"; +import { getUsersIngredient } from "../../../sources/api/IngredientAPI"; +import UsersIngredientItem from "./UsersIngredientItem"; +import style from '../../../assets/css/Ingredient/userIngredient/UsersIngredient.module.css'; + +function UserIngredient() { + const [usersIngredientList, setUserIngredientList] = useState([]); // ์ „์ฒด ์žฌ๋ฃŒ ๋ชฉ๋ก + const [filteredIngredients, setFilteredIngredients] = useState([]); // ํ•„ํ„ฐ๋ง๋œ ์žฌ๋ฃŒ ๋ชฉ๋ก + const [category, setCategory] = useState("์ „์ฒด"); // ์„ ํƒ๋œ ์นดํ…Œ๊ณ ๋ฆฌ + const [showFavoritesOnly, setShowFavoritesOnly] = useState(false); // ์ฆ๊ฒจ์ฐพ๊ธฐ ํ•„ํ„ฐ ์ƒํƒœ + + const categories = [ + "์ „์ฒด", "์ฑ„์†Œ", "๊ณผ์ผ", "์œก๋ฅ˜", "ํ•ด์‚ฐ๋ฌผ", "์œ ์ œํ’ˆ", + "๊ณ„๋ž€", "์žฅ๋ฅ˜", "์†Œ์Šค", "์Œ๋ฃŒ", "๊ณก๋ฌผ", "๊ฒฌ๊ณผ๋ฅ˜", + "๋ฐ˜์ฐฌ", "์™„์ œํ’ˆ" + ]; + + useEffect(() => { + const fetchIngredients = async () => { + try { + const data = await getUsersIngredient(); // ์„œ๋ฒ„ ๋ฐ์ดํ„ฐ ํ˜ธ์ถœ + setUserIngredientList(data); // ์ „์ฒด ๋ฐ์ดํ„ฐ ์„ค์ • + setFilteredIngredients(data); // ์ดˆ๊ธฐ ํ•„ํ„ฐ๋ง ๋ฐ์ดํ„ฐ ์„ค์ • + } catch (err) { + console.error(err); + } + }; + + fetchIngredients(); // ๋น„๋™๊ธฐ ์ž‘์—… ํ˜ธ์ถœ + }, []); + + useEffect(() => { + let filtered = usersIngredientList; + + // ์นดํ…Œ๊ณ ๋ฆฌ ํ•„ํ„ฐ๋ง + if (category !== "์ „์ฒด") { + filtered = filtered.filter( + (ingredient) => ingredient.ingredientCategory === category + ); + } + + // ์ฆ๊ฒจ์ฐพ๊ธฐ ํ•„ํ„ฐ๋ง + if (showFavoritesOnly) { + filtered = filtered.filter((ingredient) => ingredient.bookmarked === true); + } + + setFilteredIngredients(filtered); + }, [category, showFavoritesOnly, usersIngredientList]); + + return ( +
+
+

๋‚ด ๋ƒ‰์žฅ๊ณ 

+
+ + +
+
+
+ {filteredIngredients.map((ingredient) => ( + + ))} +
+
+ ); +} + +export default UserIngredient; diff --git a/src/components/ingredient/userIngredient/UsersIngredientItem.jsx b/src/components/ingredient/userIngredient/UsersIngredientItem.jsx new file mode 100644 index 0000000..6a6e8bb --- /dev/null +++ b/src/components/ingredient/userIngredient/UsersIngredientItem.jsx @@ -0,0 +1,62 @@ +import Button from 'react-bootstrap/Button'; +import Card from 'react-bootstrap/Card'; +import style from '../../../assets/css/Ingredient/userIngredient/UsersIngredientItem.module.css'; +import React, { useState } from 'react'; +import { updateIngredientBookmark } from '../../../sources/api/IngredientAPI'; + +function UsersIngredientItem({ userIngredient }) { + const [isBookmarked, setIsBookmarked] = useState(userIngredient.bookmarked || false); + + const toggleBookmark = () => { + const newBookmarkState = !isBookmarked; + setIsBookmarked(newBookmarkState); // ์ฆ๊ฒจ์ฐพ๊ธฐ ์ƒํƒœ ์—…๋ฐ์ดํŠธ + + + // ์„œ๋ฒ„๋กœ ์ƒํƒœ ์—…๋ฐ์ดํŠธ (ํ•„์š”์‹œ) + updateIngredientBookmark(1, isBookmarked, userIngredient) + userIngredient.bookmarked = !userIngredient.bookmarked + }; + + console.log(userIngredient); + return ( + +
+ {userIngredient.ingredientName} +
+ +
+ ์ˆ˜๋Ÿ‰: {userIngredient.ingredientAmount || 0} +
+
+
+ + + {userIngredient.ingredientName || '์žฌ๋ฃŒ๋ช…'} + + +
์œ ํ†ต๊ธฐํ•œ: {userIngredient.expirationDate || '์•Œ ์ˆ˜ ์—†์Œ'}
+
๋‚จ์€ ์ผ์ˆ˜: {userIngredient.remainExpirationDate || 0}
+
๋ณด๊ด€ ๋ฐฉ๋ฒ•: {userIngredient.ingredientStorage || '๋ณด๊ด€ ๋ฐฉ๋ฒ• ๋ฏธ์ •'}
+
+ +
+
+ ); +} + +export default UsersIngredientItem; diff --git a/src/components/main/Footer.jsx b/src/components/main/Footer.jsx new file mode 100644 index 0000000..a556024 --- /dev/null +++ b/src/components/main/Footer.jsx @@ -0,0 +1,38 @@ +import React from "react"; +import style from '../../assets/css/main/Footer.module.css'; + +function Footer() { + return ( + + ); +} + +export default Footer; diff --git a/src/components/main/Header.jsx b/src/components/main/Header.jsx new file mode 100644 index 0000000..3099bd4 --- /dev/null +++ b/src/components/main/Header.jsx @@ -0,0 +1,22 @@ +import React from "react"; +import style from '../../assets/css/main/header.module.css'; + +function Header() { + return ( +
+
+

+ Reci + Pick +

+
+ +
+ ); +} + +export default Header; diff --git a/src/components/recipe/recipeItem.jsx b/src/components/recipe/recipeItem.jsx new file mode 100644 index 0000000..fd68ffc --- /dev/null +++ b/src/components/recipe/recipeItem.jsx @@ -0,0 +1,17 @@ +import defaultRecipeImg from "../../assets/image/recipeimage.png" + +const RecipeItem = ({ recipe }) => { + const imgSrc = recipe.recipeSource.length!==0?recipe.recipeSource[0].recipeSourceSave:defaultRecipeImg + return ( +
+ Recipe Image +

์ด๋ฆ„:{recipe.recipeName}

+

๋‚ด์šฉ:{recipe.recipeContent}

+

์กฐ๋ฆฌ์‹œ๊ฐ„:{recipe.recipeCookingTime}(๋‹จ์œ„)

+

๋‚œ์ด๋„:{recipe.recipeDifficulty}

+

์กฐํšŒ์ˆ˜:{recipe.recipeViews}

+
+ ); +}; + +export default RecipeItem \ No newline at end of file diff --git a/src/components/user/JoinForm.jsx b/src/components/user/JoinForm.jsx new file mode 100644 index 0000000..6902263 --- /dev/null +++ b/src/components/user/JoinForm.jsx @@ -0,0 +1,209 @@ +import { useState } from 'react' +import { Form, Button } from 'react-bootstrap' +import { userApi } from '../../sources/api/UserAPI' +import { useNavigate } from 'react-router-dom' +import '../../assets/css/user/Join.css' + +const JoinForm = () => { + const navigate = useNavigate() + const [formData, setFormData] = useState({ + userId: '', + userPw: '', + userPwConfirm: '', + userEmail: '', + userNickname: '' + }) + + const [errors, setErrors] = useState({}) + const [isSubmitting, setIsSubmitting] = useState(false) + + const handleChange = (e) => { + const { name, value } = e.target + setFormData(prev => ({ + ...prev, + [name]: value + })) + // ์ž…๋ ฅ ์‹œ ํ•ด๋‹น ํ•„๋“œ์˜ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€ ์ œ๊ฑฐ + if (errors[name]) { + setErrors(prev => ({ + ...prev, + [name]: '' + })) + } + } + + const validateForm = () => { + const newErrors = {} + + // ์•„์ด๋”” ๊ฒ€์ฆ + if (!formData.userId) { + newErrors.userId = '์•„์ด๋””๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.' + } else if (formData.userId.length < 4) { + newErrors.userId = '์•„์ด๋””๋Š” 4์ž ์ด์ƒ์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.' + } + + // ๋น„๋ฐ€๋ฒˆํ˜ธ ๊ฒ€์ฆ + if (!formData.userPw) { + newErrors.userPw = '๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.' + } else if (formData.userPw.length < 8) { + newErrors.userPw = '๋น„๋ฐ€๋ฒˆํ˜ธ๋Š” 8์ž ์ด์ƒ์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.' + } + + // ๋น„๋ฐ€๋ฒˆํ˜ธ ํ™•์ธ + if (formData.userPw !== formData.userPwConfirm) { + newErrors.userPwConfirm = '๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ์ผ์น˜ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.' + } + + // ์ด๋ฉ”์ผ ๊ฒ€์ฆ + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/ + if (!formData.userEmail) { + newErrors.userEmail = '์ด๋ฉ”์ผ์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.' + } else if (!emailRegex.test(formData.userEmail)) { + newErrors.userEmail = '์˜ฌ๋ฐ”๋ฅธ ์ด๋ฉ”์ผ ํ˜•์‹์ด ์•„๋‹™๋‹ˆ๋‹ค.' + } + + // ๋‹‰๋„ค์ž„ ๊ฒ€์ฆ + if (!formData.userNickname) { + newErrors.userNickname = '๋‹‰๋„ค์ž„์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.' + } else if (formData.userNickname.length < 2) { + newErrors.userNickname = '๋‹‰๋„ค์ž„์€ 2์ž ์ด์ƒ์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.' + } + + setErrors(newErrors) + return Object.keys(newErrors).length === 0 // ๋ชจ๋“  ์ž…๋ ฅ์ด ์œ ํšจํ•œ ๊ฒฝ์šฐ ํผ ์ œ์ถœ ์ง„ํ–‰ํ–‰ + } + + const handleSubmit = async (e) => { + e.preventDefault() + setIsSubmitting(true) + + try { + const isValid = validateForm() + if (!isValid) { + setIsSubmitting(false) + return + } + + const { userPwConfirm, ...submitData } = formData + await userApi.signup(submitData) + alert('ํšŒ์›๊ฐ€์ž…์ด ์™„๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค!') + navigate('/login') + } catch (error) { + console.log('Error response:', error.response?.data) + if (error.response?.status === 409) { + // ์„œ๋ฒ„์—์„œ ๋ฐ›์€ ๋ฌธ์ž์—ด์„ ๋ฐฐ์—ด๋กœ ๋ถ„๋ฆฌ + const errorMessages = error.response.data.split(', ') + + // ๊ฐ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๋ฅผ ํ•ด๋‹นํ•˜๋Š” ํ•„๋“œ์— ๋งคํ•‘ + const fieldErrors = {} + errorMessages.forEach(message => { + if (message.includes('์•„์ด๋””')) fieldErrors.userId = message + if (message.includes('์ด๋ฉ”์ผ')) fieldErrors.userEmail = message + if (message.includes('๋‹‰๋„ค์ž„')) fieldErrors.userNickname = message + }) + + setErrors(prev => ({ + ...prev, + ...fieldErrors + })) + } else { + alert('ํšŒ์›๊ฐ€์ž… ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.') + } + } finally { + setIsSubmitting(false) + } + } + + return ( +
+ + ์•„์ด๋”” + + + {errors.userId} + + + + + ๋น„๋ฐ€๋ฒˆํ˜ธ + + + {errors.userPw} + + + + + ๋น„๋ฐ€๋ฒˆํ˜ธ ํ™•์ธ + + + {errors.userPwConfirm} + + + + + ์ด๋ฉ”์ผ + + + {errors.userEmail} + + + + + ๋‹‰๋„ค์ž„ + + + {errors.userNickname} + + + + +
+ ) +} + +export default JoinForm \ No newline at end of file diff --git a/src/main.jsx b/src/main.jsx index 19ad620..b67a5e1 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -1,8 +1,11 @@ import { createRoot } from 'react-dom/client' import App from './App.jsx' +import './assets/css/main.module.css' +import style from './assets/css/main.module.css' + createRoot(document.getElementById('root')).render( - <> +
- +
) diff --git a/src/pages/ingredient/Ingregdient.jsx b/src/pages/ingredient/Ingregdient.jsx new file mode 100644 index 0000000..3d4e7a1 --- /dev/null +++ b/src/pages/ingredient/Ingregdient.jsx @@ -0,0 +1,17 @@ +import UsersIngredient from "../../components/ingredient/userIngredient/UsersIngredient" +import style from '../../assets/css/ingredient/ingredient/Ingredient.module.css' + + +function Ingredient() { + console.log(style); + return ( + <> +

์žฌ๋ฃŒ ํŽ˜์ด์ง€

+
+ +
+ + ) +} + +export default Ingredient \ No newline at end of file diff --git a/src/pages/layouts/Layout.jsx b/src/pages/layouts/Layout.jsx new file mode 100644 index 0000000..5f3618d --- /dev/null +++ b/src/pages/layouts/Layout.jsx @@ -0,0 +1,15 @@ +import {Outlet} from 'react-router-dom' +import Header from '../../components/main/Header' +import Footer from '../../components/main/Footer' + +function Layout() { + return( +
+
+ +
+ ) +} + +export default Layout \ No newline at end of file diff --git a/src/pages/recipe/Recipe.jsx b/src/pages/recipe/Recipe.jsx new file mode 100644 index 0000000..be1ac53 --- /dev/null +++ b/src/pages/recipe/Recipe.jsx @@ -0,0 +1,45 @@ +import { useEffect, useState,useCallback } from "react" +import { getRecipeList } from "../../sources/api/recipeAPI.jsx"; +import {Route, useNavigate} from "react-router-dom"; +import RecipeItem from "../../components/recipe/RecipeItem.jsx" +import "../../assets/css/recipe/recipe.css" + +function recipe() { + const [recipeList, setRecipeList] = useState([]) + const navigate = useNavigate(); + + useEffect(() => { + const fetchRecipes = async () => { + try { + const data = await getRecipeList(); + console.log("recipeList",data); // ๊ฐ€์ ธ์˜จ ๋ฐ์ดํ„ฐ ํ™•์ธ + setRecipeList(data); // ์ƒํƒœ ์—…๋ฐ์ดํŠธ + } catch (err) { + console.error(err); + } + }; + + fetchRecipes(); // ๋น„๋™๊ธฐ ์ž‘์—… ํ˜ธ์ถœ + }, []); + + const handleAdd = useCallback(() => { + navigate("/recipe/create"); + },[navigate]) + + return ( + <> +

๋ ˆ์‹œํ”ผ ํŽ˜์ด์ง€

+
+ {recipeList.map((recipe) => ( + + ))} +
+ + + ) +} +export default recipe \ No newline at end of file diff --git a/src/pages/recipe/RecipeDetail.jsx b/src/pages/recipe/RecipeDetail.jsx new file mode 100644 index 0000000..56646b1 --- /dev/null +++ b/src/pages/recipe/RecipeDetail.jsx @@ -0,0 +1,59 @@ +import { useParams } from 'react-router-dom' +import { useEffect, useState,useCallback } from "react" +import { getRecipeDetail } from "../../sources/api/recipeAPI.jsx"; +import defaultRecipeImg from "../../assets/image/recipeimage.png" + + +function RecipeDetail() { + const { recipePk } = useParams() + const [recipeDetail, setRecipeDetail] = useState({}) + useEffect(() => { + const fetchRecipes = async () => { + try { + const data = await getRecipeDetail(recipePk); + console.log("recipeDetail",data); + setRecipeDetail(data); + } catch (err) { + console.error(err); + } + }; + fetchRecipes(); + }, [recipePk]); + + const recipeImage = + recipeDetail.recipeSource && recipeDetail.recipeSource.length > 0 + ? recipeDetail.recipeSource[0] + : defaultRecipeImg; + + return ( + <> +

๋ ˆ์‹œํ”ผ ์ƒ์„ธ์กฐํšŒ ํŽ˜์ด์ง€

+
+ ๋ ˆ์‹œํ”ผ ์ด๋ฏธ์ง€ +

์ด๋ฆ„:{recipeDetail.recipeName}

+

๋‚ด์šฉ:{recipeDetail.recipeContent}

+

์กฐ๋ฆฌ์‹œ๊ฐ„:{recipeDetail.recipeCookingTime}(๋‹จ์œ„)

+

๋‚œ์ด๋„:{recipeDetail.recipeDifficulty}

+

์กฐํšŒ์ˆ˜:{recipeDetail.recipeViews}

+

์žฌ๋ฃŒ

+ {recipeDetail.ingredients && recipeDetail.ingredients.map(ingredient => (

{ingredient.ingredientName}

))} +

์š”๋ฆฌ๊ณผ์ •

+ {recipeDetail.recipeStep && + recipeDetail.recipeStep.map((step, index) => ( +
+

Step {index + 1}: {step.recipeStepContent}

+ {step.recipeStepSource && ( + {`Step + )} +
+ ))} + +
+ + ) +} + +export default RecipeDetail \ No newline at end of file diff --git a/src/pages/recipe/addRecipe.jsx b/src/pages/recipe/addRecipe.jsx new file mode 100644 index 0000000..d0988ba --- /dev/null +++ b/src/pages/recipe/addRecipe.jsx @@ -0,0 +1,318 @@ + +import { useState } from 'react'; +import { createRecipe } from "../../sources/api/recipeAPI.jsx"; + +const initialState = { + recipeName: '', + recipeCookingTime: 0, + recipeDifficulty: 0, + recipeContent: '', + recipeSteps: [], + recipeCategoryPk: 0, + userPk: 0, +}; + +export const AddRecipe = () => { + + const [request, setRequest] = useState({ ...initialState }); + + const [recipeSources, setRecipeSources] = useState([]); + + const [recipeStepSources, setRecipeStepSources] = useState([]); + + const [result, setResult] = useState(null); + + const defaultUrl = 'https://picsum.photos/200/300'; + const handleClickAdd = () => { + + const formData = new FormData(); + + // request ๊ธฐ๋ณธ ๊ธ€์€ ๊ทธ๋ƒฅ ์ถ”๊ฐ€ + formData.append("request",new Blob([JSON.stringify(request)],{type:"application/json"})); + + // recipeSources ํŒŒ์ผ ๋ฐฐ์—ด ์ถ”๊ฐ€ + recipeSources.forEach(file => { + formData.append(`recipeSources`, file); // ๊ฐ™์€ ์ด๋ฆ„์œผ๋กœ ์„œ๋ฒ„์— ์ „๋‹ฌ + }); + + // recipeStepSources ์ถ”๊ฐ€ + recipeStepSources.forEach((files, stepIndex) => { + if(files) { + files.forEach(file => { + formData.append(`recipeStepSources`, file); + }) + } + }); + + createRecipe(formData) + .then(res => { + console.log(res); + setResult(res); + + // ์‚ฌ์šฉ ํ›„ ์ดˆ๊ธฐํ™” + setRequest({ ...initialState }); + setRecipeSources([]); + setRecipeStepSources([]); + }) + .catch(err => { + console.log(err); + }); + }; + + // ํŒŒ์ผ ์—…๋กœ๋“œ ์ฒ˜๋ฆฌ + const handleRecipeSourcesChange = (e) => { + setRecipeSources(Array.from(e.target.files)); // ์—ฌ๋Ÿฌ ํŒŒ์ผ์„ ๋ฐฐ์—ด๋กœ ์ถ”๊ฐ€ + console.log(Array.from(e.target.files)) + }; + + + const handleInputChange = (e) => { + const { name, value } = e.target; + setRequest(prevState => ({ + ...prevState, + [name]: value, + })); + }; + + const addStep = (index) => { + setRequest((prevState) => { + const updatedSteps = [...prevState.recipeSteps]; + const newStep = { + recipeStepOrder: index + 2, + recipeStepContent: '', + }; + + updatedSteps.splice(index + 1, 0, newStep); // ํด๋ฆญ๋œ ์Šคํ… ์•„๋ž˜ ์‚ฝ์ž… + + // ์ „์ฒด ์ˆœ์„œ ์—…๋ฐ์ดํŠธ + const updatedOrderedSteps = updatedSteps.map((step, idx) => ({ + ...step, + recipeStepOrder: idx + 1, // ์ˆœ์„œ ์žฌ์ •๋ ฌ + })); + + return { + ...prevState, + recipeSteps: updatedOrderedSteps, + }; + }); + + // recipeStepSources์—๋„ ๋นˆ ๊ฐ’ ์ถ”๊ฐ€ + setRecipeStepSources((prevSources) => { + const updatedSources = [...prevSources]; + updatedSources.splice(index + 1, 0, null); // ํด๋ฆญ๋œ ์Šคํ… ์•„๋ž˜์— ๋นˆ ๊ฐ’ ์‚ฝ์ž… + return updatedSources; + }); + + }; + + const removeStep = (index) => { + setRequest((prevState) => { + const updatedSteps = [...prevState.recipeSteps]; + updatedSteps.splice(index, 1); // ํ•ด๋‹น ์ธ๋ฑ์Šค ์Šคํ… ์‚ญ์ œ + + // ์ˆœ์„œ ์žฌ์ •๋ ฌ + const updatedOrderedSteps = updatedSteps.map((step, idx) => ({ + ...step, + recipeStepOrder: idx + 1, + })); + + return { + ...prevState, + recipeSteps: updatedOrderedSteps, + }; + }); + + // recipeStepSources์—์„œ๋„ ํ•ด๋‹น ์ธ๋ฑ์Šค์˜ ์ด๋ฏธ์ง€ ์‚ญ์ œ + setRecipeStepSources((prevSources) => { + const updatedSources = [...prevSources]; + updatedSources.splice(index, 1); // ํ•ด๋‹น ์ธ๋ฑ์Šค ์‚ญ์ œ + return updatedSources; + }); + }; + + const handleStepChange = (e, index) => { + const { name, value } = e.target; + setRequest(prevState => { + const updatedSteps = [...prevState.recipeSteps]; + updatedSteps[index] = { ...updatedSteps[index], [name]: value }; + return { + ...prevState, + recipeSteps: updatedSteps, + }; + }); + }; + + const handleRecipeStepSourcesChange = (e, index) => { + const files = Array.from(e.target.files||[]); // ์—…๋กœ๋“œ๋œ ํŒŒ์ผ ๋ฆฌ์ŠคํŠธ + + setRecipeStepSources((prevSources) => { + const updatedSources = [...prevSources]; + updatedSources[index] = files.length > 0 ? files : null; // ํŒŒ์ผ์ด ์žˆ์œผ๋ฉด ์—…๋ฐ์ดํŠธ, ์—†์œผ๋ฉด null + return updatedSources; + }); + }; + + const printRecipeStepSources = () => { + console.log(recipeStepSources); + } + return ( +
+
+

๋ ˆ์‹œํ”ผ ์‚ฌ์ง„ : + + + +

+
+
+

๋ ˆ์‹œํ”ผ ์ด๋ฆ„ : + +

+
+ + +
+

์กฐ๋ฆฌ ์‹œ๊ฐ„ : + +

+
+ +
+

๋ ˆ์‹œํ”ผ ๊ฐ„๋‹จํ•œ ๋‚ด์šฉ : + +

+
+
+

์นดํ…Œ๊ณ ๋ฆฌ : + +

+
+
+

์œ ์ € ๋ฒˆํ˜ธ: + +

+
+ + + {/* ๋‹จ๊ณ„๋ณ„ ์ž…๋ ฅ */} + + + {request.recipeSteps.map((step, index) => ( +
+

์Šคํ… {index + 1} : + handleStepChange(e, index)} + placeholder={`์Šคํ… ${index + 1}`} + /> +

+ {/* ์ˆจ๊ฒจ์ง„ ํŒŒ์ผ ์„ ํƒ ์ž…๋ ฅ */} + handleRecipeStepSourcesChange(e, index)} + style={{display: 'none'}} // ์ˆจ๊ธฐ๊ธฐ + /> + + + + + {/* ๋ผ๋ฒจ์„ ํด๋ฆญํ•˜๋ฉด ํŒŒ์ผ ์ฐฝ ์—ด๊ธฐ */} + + +
+

+ +
+ ))} + + + + +
+ ); +}; \ No newline at end of file diff --git a/src/pages/user/Join.jsx b/src/pages/user/Join.jsx new file mode 100644 index 0000000..813d53b --- /dev/null +++ b/src/pages/user/Join.jsx @@ -0,0 +1,15 @@ +import JoinForm from '../../components/user/JoinForm' +import { Container } from "react-bootstrap" +import '../../assets/css/user/Join.css' + +const Join = () => { + return ( + +

ํšŒ์›๊ฐ€์ž…

+

ReciPick์˜ ํšŒ์›์ด ๋˜์–ด ๋‚˜๋งŒ์„ ์œ„ํ•œ ๋งž์ถค ๋ ˆ์‹œํ”ผ๋ฅผ ๋งŒ๋‚˜๋ณด์„ธ์š”.

+ +
+ ) +} + +export default Join \ No newline at end of file diff --git a/src/sources/api/IngredientAPI.js b/src/sources/api/IngredientAPI.js new file mode 100644 index 0000000..4fb5435 --- /dev/null +++ b/src/sources/api/IngredientAPI.js @@ -0,0 +1,55 @@ +import axios from "axios"; + +const BASE_URL = 'http://localhost:8080/ingredient' + +export async function getUsersIngredient() { + try { + const response = await axios({ + url: `${BASE_URL}`, + method: 'get', + params: { + userPk: 1, + }, + }); + return response.data; // ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ˜ํ™˜ + } catch (err) { + console.error(err); + throw err; // ํ˜ธ์ถœ์ž์—๊ฒŒ ์—๋Ÿฌ ์ „๋‹ฌ + } +} + +export function updateIngredientBookmark(userPk, isBookmarked, ingredient) { + if (!isBookmarked) { + axios({ + url: `${BASE_URL}/bookmark/regist`, + method: 'post', + data: { + userPk: userPk, + ingredientMyRefrigeratorPk: ingredient.ingredientMyRefrigeratorPk + } + }) + .then((res) => { + console.log(res); + console.log("์ถ”๊ฐ€๋จ"); + }) + .catch((err) => { + console.log(err); + }) + } else { + axios({ + url: `${BASE_URL}/bookmark/delete`, + method: 'delete', + data: { + userPk: userPk, + ingredientBookmarkPk: ingredient.ingredientBookmarkPk + } + }) + .then((res) => { + console.log(res); + console.log("์‚ญ์ œ๋จ"); + }) + .catch((err) => { + console.log(err); + }) + } +} \ No newline at end of file diff --git a/src/sources/api/UserAPI.js b/src/sources/api/UserAPI.js new file mode 100644 index 0000000..9cdf908 --- /dev/null +++ b/src/sources/api/UserAPI.js @@ -0,0 +1,15 @@ +import axios from 'axios' + +const BASE_URL = 'http://localhost:8080' + +export const userApi = { + // ํšŒ์›๊ฐ€์ž… + signup: async (userData) => { + try { + const response = await axios.post(`${BASE_URL}/auth/join`, userData) + return response.data + } catch (error) { + throw error + } + }, +} \ No newline at end of file diff --git a/src/sources/api/recipeAPI.jsx b/src/sources/api/recipeAPI.jsx new file mode 100644 index 0000000..2d68640 --- /dev/null +++ b/src/sources/api/recipeAPI.jsx @@ -0,0 +1,24 @@ +import axios from "axios"; + +export const API_URL_HOST = "http://localhost:8080"; + +const prefix = `${API_URL_HOST}/recipe`; + +export const getRecipeDetail = async (recipePk) => { + const url = `${prefix}/${recipePk}` + const res = await axios.get(url) + return res.data +} + +export const getRecipeList = async () => { + const url = `${prefix}`; + const res = await axios.get(url); + return res.data; +} + +export const createRecipe = async (recipe) => { + console.log(recipe); + const url = `${prefix}`; + const res = await axios.post(url, recipe); + return res.data; +} \ No newline at end of file