diff --git a/ex.css b/ex.css new file mode 100644 index 0000000..e64d2c4 --- /dev/null +++ b/ex.css @@ -0,0 +1,4 @@ +p { + justify-content: space-between; + flex-direction: column; +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 3a0df99..cde0503 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,9 @@ "cra-template": "1.2.0", "react": "^18.3.1", "react-dom": "^18.3.1", + "react-router-dom": "^7.1.5", "react-scripts": "5.0.1", + "styled-components": "^6.1.15", "web-vitals": "^4.2.4" } }, @@ -2208,6 +2210,27 @@ "postcss-selector-parser": "^6.0.10" } }, + "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/@eslint-community/eslint-utils": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", @@ -3303,6 +3326,12 @@ "@types/node": "*" } }, + "node_modules/@types/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", + "license": "MIT" + }, "node_modules/@types/eslint": { "version": "8.56.12", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.12.tgz", @@ -3525,6 +3554,12 @@ "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==" }, + "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/@types/trusted-types": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", @@ -4947,6 +4982,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-api": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", @@ -5446,6 +5490,15 @@ "postcss": "^8.4" } }, + "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-declaration-sorter": { "version": "6.4.1", "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.4.1.tgz", @@ -5587,6 +5640,17 @@ "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==" }, + "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/css-tree": { "version": "1.0.0-alpha.37", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", @@ -5774,6 +5838,12 @@ "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==" }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT" + }, "node_modules/damerau-levenshtein": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", @@ -12720,6 +12790,55 @@ "node": ">=0.10.0" } }, + "node_modules/react-router": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.1.5.tgz", + "integrity": "sha512-8BUF+hZEU4/z/JD201yK6S+UYhsf58bzYIDq2NS1iGpwxSXDu7F+DeGSkIXMFBuHZB21FSiCzEcUb18cQNdRkA==", + "license": "MIT", + "dependencies": { + "@types/cookie": "^0.6.0", + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0", + "turbo-stream": "2.4.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/react-router-dom": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.1.5.tgz", + "integrity": "sha512-/4f9+up0Qv92D3bB8iN5P1s3oHAepSGa9h5k6tpTFlixTTskJZwKGhJ6vRJ277tLD1zuaZTt95hyGWV1Z37csQ==", + "license": "MIT", + "dependencies": { + "react-router": "7.1.5" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, + "node_modules/react-router/node_modules/cookie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", + "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/react-scripts": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz", @@ -13556,6 +13675,12 @@ "node": ">= 0.8.0" } }, + "node_modules/set-cookie-parser": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", + "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", + "license": "MIT" + }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", @@ -13604,6 +13729,12 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, + "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", @@ -14207,6 +14338,68 @@ "webpack": "^5.0.0" } }, + "node_modules/styled-components": { + "version": "6.1.15", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.1.15.tgz", + "integrity": "sha512-PpOTEztW87Ua2xbmLa7yssjNyUF9vE7wdldRfn1I2E6RTkqknkBYpj771OxM/xrvRGinLy2oysa7GOd7NcZZIA==", + "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/styled-components/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/stylehacks": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz", @@ -14222,6 +14415,12 @@ "postcss": "^8.2.15" } }, + "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/sucrase": { "version": "3.35.0", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", @@ -14809,6 +15008,12 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, + "node_modules/turbo-stream": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/turbo-stream/-/turbo-stream-2.4.0.tgz", + "integrity": "sha512-FHncC10WpBd2eOmGwpmQsWLDoK4cqsA/UT/GqNoaKOQnT8uzhtCbg3EoUDMvqpOSAI0S26mr0rkjzbOO6S3v1g==", + "license": "ISC" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", diff --git a/package.json b/package.json index f70e9ed..88fb4db 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,9 @@ "cra-template": "1.2.0", "react": "^18.3.1", "react-dom": "^18.3.1", + "react-router-dom": "^7.1.5", "react-scripts": "5.0.1", + "styled-components": "^6.1.15", "web-vitals": "^4.2.4" }, "scripts": { diff --git a/src/App.js b/src/App.js index d24509b..7e692ee 100644 --- a/src/App.js +++ b/src/App.js @@ -1,9 +1,13 @@ -import './App.css'; +import React, { useState } from 'react'; +import AllList from './pages/AllList.jsx'; +import Complete from './pages/Complete.jsx'; -function App() { +const App = () => { return ( -
+ <> + + ); -} +}; export default App; diff --git a/src/components/AllList/AddPopup.jsx b/src/components/AllList/AddPopup.jsx new file mode 100644 index 0000000..26f8b14 --- /dev/null +++ b/src/components/AllList/AddPopup.jsx @@ -0,0 +1,106 @@ +import React, { useState } from 'react'; +import styled from 'styled-components'; +import cancel from '../../images/CancelBtn.png' + + + +const Popup = styled.div ` + width: 336px; + height: 297px; + border-radius: 15px; + /*position: relative;*/ + position: absolute; + top: 180px; + left: 28px; + background-color: #fff; + z-index: 1000; /* 팝업이 앞에 보이도록 z-index 설정 */ +`; + +const Content = styled.form ` +display: flex; + flex-direction: column; /* 세로로 쌓이도록 설정 */ + margin: 12px 0 0 20px; +`; + +const CancelBtn = styled.img ` + position: absolute; + top: 12px; + right: 12px; + cursor: pointer; /* 클릭 가능하게 커서 변경 */ +`; + +const CategorySelect = styled.select ` + width: 158px; + height: 35px; + border-radius: 20px; + border: 1px solid #000; + font-size: 15px; + padding: 9px 14px; + margin-bottom: 13px; +`; + +const DifficultySelect = styled(CategorySelect) ` + +`; + +const Title = styled.p ` + margin: 0; +font-size: 15px; + margin-bottom: 8px; + margin-left: 3px; + +`; + +const MissionText = styled.textarea ` +width: 287px; + height: 99px; + border: 1px solid #000; + border-radius: 3px; + resize: none; /* 크기 조절 불가능하게 설정 */ +`; + +const Submit = styled.button ` + width: 104px; + height: 32px; + border-radius: 20px; + border: none; + background-color: #5AB2FF; + color: #fff; + font-size: 18px; + padding: 0 5px; + position: absolute; + bottom: 12px; + right: 22px; +`; + +const AddPopup = ({ onClose }) => { + + return ( + <> + + + + + + + + + + + + + + + + + 미션 내용 + + 추가하기 + + + + + ); +}; + +export default AddPopup; diff --git a/src/components/AllList/DeletePopup.jsx b/src/components/AllList/DeletePopup.jsx new file mode 100644 index 0000000..14c5709 --- /dev/null +++ b/src/components/AllList/DeletePopup.jsx @@ -0,0 +1,77 @@ + +import React, { useState } from 'react'; +import styled from 'styled-components'; + + + +const Popup = styled.div ` + width: 336px; + height: 142px; + border-radius: 15px; + /*position: relative;*/ + position: absolute; + top: 180px; + left: 28px; + background-color: #fff; + z-index: 1000; /* 팝업이 앞에 보이도록 z-index 설정 */ + display: flex; + justify-content: center; + align-items: center; + `; + +const Content = styled.form ` + display: flex; + flex-direction: column; +`; + +const CancelBtn = styled.img ` + position: absolute; + top: 12px; + right: 12px; + cursor: pointer; /* 클릭 가능하게 커서 변경 */ +`; + +const Warning = styled.p ` + font-size: 20px; +`; + +const ButtonWrap = styled.div ` + display: flex; + +`; + +const Yes = styled.button ` + width: 104px; + height: 32px; + border-radius: 20px; + border: none; + background-color: #fff; + border: 1px solid #000; + color: #000; + font-size: 18px; + padding: 0 5px; + +`; + +const No = styled(Yes) ` +margin-left: 22px; +`; + +const DeletePopup = ({ onClose }) => { + + return ( + <> + + + 미션을 삭제하시겠습니까? + + + 아니오 + + + + + ); +}; + +export default DeletePopup; diff --git a/src/components/AllList/EditPopup.jsx b/src/components/AllList/EditPopup.jsx new file mode 100644 index 0000000..0ce4b54 --- /dev/null +++ b/src/components/AllList/EditPopup.jsx @@ -0,0 +1,104 @@ +import React, { useState } from 'react'; +import styled from 'styled-components'; +import cancel from '../../images/CancelBtn.png' + +const Popup = styled.div ` + width: 336px; + height: 297px; + border-radius: 15px; + /*position: relative;*/ + position: absolute; + top: 180px; + left: 28px; + background-color: #fff; + z-index: 1000; /* 팝업이 앞에 보이도록 z-index 설정 */ +`; + +const Content = styled.form ` +display: flex; + flex-direction: column; /* 세로로 쌓이도록 설정 */ + margin: 12px 0 0 20px; +`; + +const CancelBtn = styled.img ` + position: absolute; + top: 12px; + right: 12px; + cursor: pointer; /* 클릭 가능하게 커서 변경 */ +`; + +const CategorySelect = styled.select ` + width: 158px; + height: 35px; + border-radius: 20px; + border: 1px solid #000; + font-size: 15px; + padding: 9px 14px; + margin-bottom: 13px; +`; + +const DifficultySelect = styled(CategorySelect) ` + +`; + +const Title = styled.p ` + margin: 0; +font-size: 15px; + margin-bottom: 8px; + margin-left: 3px; + +`; + +const MissionText = styled.textarea ` +width: 287px; + height: 99px; + border: 1px solid #000; + border-radius: 3px; + resize: none; /* 크기 조절 불가능하게 설정 */ +`; + +const Submit = styled.button ` + width: 104px; + height: 32px; + border-radius: 20px; + border: none; + background-color: #5AB2FF; + color: #fff; + font-size: 18px; + padding: 0 5px; + position: absolute; + bottom: 12px; + right: 22px; +`; + +const EditPopup = ({ onClose }) => { + + return ( + <> + + + + + + + + + + + + + + + + + 미션 내용 + + 추가하기 + + + + + ); +}; + +export default EditPopup; diff --git a/src/components/AllList/FuncPopup.jsx b/src/components/AllList/FuncPopup.jsx new file mode 100644 index 0000000..6a972a4 --- /dev/null +++ b/src/components/AllList/FuncPopup.jsx @@ -0,0 +1,64 @@ +import React, { useState } from 'react'; +import styled from 'styled-components'; +import cancel from '../../images/CancelBtn.png' + + + +const Popup = styled.div ` + width: 336px; + height: 142px; + border-radius: 15px; + /*position: relative;*/ + position: absolute; + top: 180px; + left: 28px; + background-color: #fff; + z-index: 1000; /* 팝업이 앞에 보이도록 z-index 설정 */ + display: flex; + justify-content: center; + align-items: center; + `; + +const Content = styled.form ` + display: flex; +`; + +const CancelBtn = styled.img ` + position: absolute; + top: 12px; + right: 12px; + cursor: pointer; /* 클릭 가능하게 커서 변경 */ +`; + +const Edit = styled.button ` + width: 104px; + height: 32px; + border-radius: 20px; + border: none; + background-color: #5AB2FF; + color: #fff; + font-size: 18px; + padding: 0 5px; + +`; + +const Delete = styled(Edit) ` +margin-left: 22px; +`; + +const FuncPopup = ({ onClose }) => { + + return ( + <> + + + 수정하기 + 삭제하기 + + + + + ); +}; + +export default FuncPopup; diff --git a/src/components/AllList/Mission.jsx b/src/components/AllList/Mission.jsx new file mode 100644 index 0000000..28a152d --- /dev/null +++ b/src/components/AllList/Mission.jsx @@ -0,0 +1,76 @@ +import React from 'react'; +import styled from 'styled-components'; + +const missionColors = { + ing: '#FFEA63', // 진행중 + yet: '#FFF9D0', // 미완료 + done: '#D9D9D9', // 완료 +}; + + +const MissionDiv = styled.div` + width: 340px; + height: 45px; + background-color: ${({ type }) => missionColors[type] || '#E5E5E5'}; // 상태에 따라 배경색 변경 + margin-bottom: 10px; + padding: 7px 0px 7px 8px; + `; + +const MissionDetail = styled.div ` + display: flex; +`; + +const MissionCategory = styled.p` + background-color: ${({ color }) => color}; + border-radius: 5px; + font-size: 10px; + color: #000; + margin: 0; + width: 52px; + height: 13px; + display: flex; + justify-content: center; + align-items: center; + margin-right: 3px; +`; + +const MissionDifficulty = styled.p` + width: 17px; + height: 12px; + background-color: #FFF9D0; + font-size: 10px; + color: #fff; + margin: 0; + border-radius: 5px; + display: flex; + align-items: center; + justify-content: center; +`; + +const MissionText = styled.p` + flex-grow: 1; + font-size: 14px; + margin: 0; +`; + +const Mission = ({ type, category, text }) => { + const categoryData = { + 자기계발: '#FFF1F1', + 공부: '#FFE4CC', + 건강: '#E8FFE3', + 취미: '#E9F2FF', + 기타: '#F2E9FF', + }; + + return ( + + + {category} + + + {text} + + ); +}; + +export default Mission; diff --git a/src/components/Challenge/CardDetail.jsx b/src/components/Challenge/CardDetail.jsx new file mode 100644 index 0000000..54e4a13 --- /dev/null +++ b/src/components/Challenge/CardDetail.jsx @@ -0,0 +1,155 @@ +import React, { useState } from 'react'; +import styled from 'styled-components'; +import cancel from '../../images/CancelBtn.png' + + + +const Popup = styled.div ` + width: 226px; + height: 292px; + border-radius: 15px; + /*position: relative;*/ + position: absolute; + top: 200px; + left: 75px; + box-shadow: 0px 2px 3px rgba(0, 0, 0, 0.3); + background-color: ${({ color }) => color}; + z-index: 1000; /* 팝업이 앞에 보이도록 z-index 설정 */ +`; + +const Content = styled.form ` +display: flex; + flex-direction: column; /* 세로로 쌓이도록 설정 */ + margin: 25px 0 0 15px; +`; + +const CancelBtn = styled.img ` + position: absolute; + top: 12px; + right: 12px; + cursor: pointer; /* 클릭 가능하게 커서 변경 */ +`; + +const MissionText = styled.h3 ` + margin: 0; + font-size: 15px; + margin-bottom: 8px; + font-weight: bold; + +`; + +const MissionDetail = styled.div ` + display: flex; + margin-bottom: 8px; + align-items: center; +`; + +const MissionCategory = styled.p` + border-radius: 5px; + font-size: 10px; + color: #000; + margin: 0; + display: flex; + justify-content: center; + align-items: center; +`; + +const MissionDifficulty = styled.p` + width: 17px; + height: 12px; + background-color: #FFF9D0; + font-size: 10px; + color: #000; + margin: 0; + border-radius: 5px; + display: flex; + align-items: center; + justify-content: center; + margin-left: 5px; +`; + +const SubTitle = styled.p` + margin: 0; + font-size: 12px; + margin-bottom: 8px; + font-weight: bold; +`; + +const RateWrap = styled.div ` + display: flex; + margin-bottom: 8px; + +`; + +const RateYellow = styled.div ` + width: 147px; + height: 12px; + border-radius: 20px; + background-color: #FFFDF0; +`; + +const RateBlue = styled.div ` + width: ${({ rate }) => rate}%; + height: 12px; + border-radius: 20px; + background-color: #A0DEFF; +`; + +const RateText = styled.p ` + margin: 0; + font-size: 10px; + margin-left: 9px; +`; + +const Memo = styled.div ` + width: 195px; + height: 112px; + border-radius: 15px; + background-color: #FBFBFB; + display: flex; + justify-content: center; + padding-top: 11px; +`; + +const MemoText = styled.p ` + font-size: 10px; + width: 156px; + height: 84px; + overflow: hidden; +`; + + +const CardDetail = ({ onClose, category, rate }) => { + const categoryData = { + 자기계발: '#FFF1F1', + 공부: '#FFE4CC', + 건강: '#E8FFE3', + 취미: '#E9F2FF', + 기타: '#F2E9FF', + }; + return ( + <> + + + 미션 내용 + + {category} + + + 성취율 + + + + + {rate}% + + 메모 + 어쩌구저쩌구 + + + + + ); +}; + +export default CardDetail; diff --git a/src/components/Challenge/Cards.jsx b/src/components/Challenge/Cards.jsx new file mode 100644 index 0000000..b63c0e1 --- /dev/null +++ b/src/components/Challenge/Cards.jsx @@ -0,0 +1,95 @@ +import React from 'react'; +import styled from 'styled-components'; +import star from '../../images/Star.png'; +import noStar from '../../images/NoStar.png'; + +const categoryColors = { + develope: '#FFF1F1', // 자기계발 카테고리 색상 + study: '#FFE4CC', // 공부 카테고리 색상 + health: '#E8FFE3', // 건강 카테고리 색상 + hobby: '#E9F2FF', // 취미 카테고리 색상 + others: '#F2E9FF', // 기타 카테고리 색상 + }; + +const Card = styled.div ` + width: 115px; + height: 140px; + border-radius: 15px; + background-color: ${({ category }) => categoryColors[category] || '#E5E5E5'}; // 카테고리에 따라 배경색 변경 + display: flex; + flex-direction: column; + align-items:center; + box-shadow: 0px 2px 3px rgba(0, 0, 0, 0.3); + margin-bottom: 36px; + + &:hover { + background-color: ${({ category }) => categoryColors[category] ? darkenColor(categoryColors[category], 0.1) : '#ccc'}; + } + `; + +const CardContent = styled.div ` + width: 100px; + padding: 18px 0 15px 0; + border-bottom: 1px solid #B0B0B0; + display: flex; + flex-direction: column; + align-items:center; +`; + +const Mission = styled.p ` + font-size: 15px; + margin: 0; +`; + +const MissionNum = styled.p ` + font-size: 40px; + margin: 0; +`; + +const StarWrap = styled.div ` + width: 90px; + display: flex; + justify-content: space-between; + margin-top: 3px; +`; + +const Star = styled.img ` +`; + +// 색상을 어둡게 만드는 함수 (옵션) +const darkenColor = (color, percentage) => { + const colorCode = color.slice(1); + const num = parseInt(colorCode, 16); + const amt = Math.round(2.55 * percentage * 100); + const R = (num >> 16) + amt; + const G = ((num >> 8) & 0x00ff) + amt; + const B = (num & 0x0000ff) + amt; + return `#${( + 0x1000000 + + (R < 255 ? (R < 1 ? 0 : R) : 255) * 0x10000 + + (G < 255 ? (G < 1 ? 0 : G) : 255) * 0x100 + + (B < 255 ? (B < 1 ? 0 : B) : 255) + ) + .toString(16) + .slice(1)}`; + }; + +const Cards = ({category, number}) => { + return ( + <> + + + mission + {number} + + + + + + + + + ); +}; + +export default Cards; diff --git a/src/components/Footer.jsx b/src/components/Footer.jsx new file mode 100644 index 0000000..2f5b0be --- /dev/null +++ b/src/components/Footer.jsx @@ -0,0 +1,66 @@ +import React from 'react'; +import styled from 'styled-components'; + +const FooterDiv = styled.div` + width: 365px; + height: 102px; + background-color: black; + display: flex; + flex-direction: column; + justify-content: center; + padding-left: 28px; + position: absolute; + top: 750px; + `; + +const AppTitle = styled.h3 ` + font-size: 15px; + font-weight: bold; + color: #ffffff; + margin: 0; + margin-bottom: 5px; +`; + +const Team = styled.p ` + font-size: 12px; + color: #ffffff; + margin: 0; + margin-bottom: 3px; +`; + +const MemberDiv = styled.div ` + display: flex; +`; + +const Member = styled(Team) ` + font-size: 10px; + margin-right: 9px; +`; + +const Position = styled(Member) ` + font-weight: bold; + margin-right: 5px; +`; + +const Club = styled.p ` + margin: 0; + font-size: 12px; + color: #ffffff; + margin-top: 10px; +`; + +const Footer = () => { + + return ( + <> + + 작심3일 + N3W + 백엔드김미주 심수빈 정민주프론트엔드 박민주 박소이 + DSWU CORNER + + + ); +}; + +export default Footer; diff --git a/src/components/Header.jsx b/src/components/Header.jsx new file mode 100644 index 0000000..8d9751f --- /dev/null +++ b/src/components/Header.jsx @@ -0,0 +1,54 @@ +import React, { useState } from 'react'; +import styled from 'styled-components'; +import logoimg from '../images/Logo.png'; +import hamburger from '../images/hamburger.png'; +import Sidebar from './Sidebar.jsx'; + +const HeaderDiv = styled.div` + width: 393px; + height: 66px; + border: 1.5px solid #B5B5B5; + display: flex; + align-items: center; +`; + +const Logo = styled.img` + height: 29.38px; + margin-left: 24.48px; +`; + +const HamburgerBtn = styled.button` + background: none; + border: none; + margin-left: auto; + margin-right: 24.48px; +`; + +const HamburgerImg = styled.img` + width: 24px; + height: 24px; +`; + +const Header = () => { + // useState를 컴포넌트 함수 내에 선언 + const [isOpen, setIsOpen] = useState(false); + + // 사이드바를 열고 닫는 함수 + const toggleSide = () => { + setIsOpen(!isOpen); + }; + + return ( + <> + + + + + + + + + ); +}; + +export default Header; diff --git a/src/components/Sidebar.jsx b/src/components/Sidebar.jsx new file mode 100644 index 0000000..ce0126b --- /dev/null +++ b/src/components/Sidebar.jsx @@ -0,0 +1,103 @@ +import React, { useRef, useEffect } from 'react'; +import styled from 'styled-components'; +import Complete from '../images/Complete.png'; +import AllList from '../images/AllList.png'; +import Challenge from '../images/Challenge.png'; + + +// styled-components를 사용한 스타일링 + +const Display = styled.div` + width: 393px; + height: 852px; + position: relative; + border: 1px solid #ccc; /* 확인을 위해 경계선 추가 */ +`; + +const SideBarWrap = styled.div` + z-index: 5; + padding: 12px; + border-radius: 15px 0 0 15px; + background-color: #FFFFFF80; + height: 100%; + width: 200px; + right: -200px; /* 닫혔을 때는 화면 밖에 위치 */ + top: 0; + position: absolute; + transition: 0.5s ease; + + &.open { + right: 15px; /* 열렸을 때는 화면 안으로 이동 */ + transition: 0.5s ease; + } +`; + + +const MenuWrap = styled.ul ` + list-style: none; + padding: 0; + margin-top: 100px; +`; + +const MenuImg = styled.img ` + width: 24px; + heigiht: 24px; +`; + +const Menu = styled.li` + display: flex; + padding-top: 20px; + padding-bottom: 15px; + border-bottom: 1.5px solid rgb(231, 231, 231); + + &:last-child { + border: none; + } +`; + +const MenuText = styled.p ` + font-size: 15px; + margin: 0; + margin-left: 24px; +`; + +const ExitMenu = styled.span` + position: absolute; + bottom: 26px; + font-size: 0.8rem; +`; + +function Sidebar({ isOpen, setIsOpen }) { + const outside = useRef(null); + + // 사이드바 외부 클릭 시 닫히는 로직 + const handlerOutside = (e) => { + if (outside.current && !outside.current.contains(e.target)) { + toggleSide(); + } + }; + + useEffect(() => { + document.addEventListener('mousedown', handlerOutside); + return () => { + document.removeEventListener('mousedown', handlerOutside); + }; + }, []); // 한 번만 실행되도록 설정 + + // 사이드바를 닫는 함수 + const toggleSide = () => { + setIsOpen(false); + }; + + return ( + + + 성취 미션 + 챌린지 + 전체 미션 목록 + + + ); +} + +export default Sidebar; \ No newline at end of file diff --git a/src/images/AllList.png b/src/images/AllList.png new file mode 100644 index 0000000..aedf5e3 Binary files /dev/null and b/src/images/AllList.png differ diff --git a/src/images/CancelBtn.png b/src/images/CancelBtn.png new file mode 100644 index 0000000..8eaf092 Binary files /dev/null and b/src/images/CancelBtn.png differ diff --git a/src/images/Challenge.png b/src/images/Challenge.png new file mode 100644 index 0000000..4be13c9 Binary files /dev/null and b/src/images/Challenge.png differ diff --git a/src/images/Complete.png b/src/images/Complete.png new file mode 100644 index 0000000..e30fafc Binary files /dev/null and b/src/images/Complete.png differ diff --git a/src/images/Logo.png b/src/images/Logo.png new file mode 100644 index 0000000..85db456 Binary files /dev/null and b/src/images/Logo.png differ diff --git a/src/images/Logout.png b/src/images/Logout.png new file mode 100644 index 0000000..089e887 Binary files /dev/null and b/src/images/Logout.png differ diff --git a/src/images/NoStar.png b/src/images/NoStar.png new file mode 100644 index 0000000..bd99617 Binary files /dev/null and b/src/images/NoStar.png differ diff --git a/src/images/PlusBtn.png b/src/images/PlusBtn.png new file mode 100644 index 0000000..a7d29b3 Binary files /dev/null and b/src/images/PlusBtn.png differ diff --git a/src/images/Star.png b/src/images/Star.png new file mode 100644 index 0000000..e71c96d Binary files /dev/null and b/src/images/Star.png differ diff --git a/src/images/hamburger.png b/src/images/hamburger.png new file mode 100644 index 0000000..9f5b206 Binary files /dev/null and b/src/images/hamburger.png differ diff --git a/src/pages/AllList.jsx b/src/pages/AllList.jsx new file mode 100644 index 0000000..69657db --- /dev/null +++ b/src/pages/AllList.jsx @@ -0,0 +1,129 @@ +import React, { useState } from 'react'; +import Header from '../components/Header.jsx'; +import Footer from '../components/Footer.jsx'; +import PlusBtn from '../images/PlusBtn.png'; +import Mission from '../components/AllList/Mission.jsx'; +import styled from 'styled-components'; +import AddPopup from '../components/AllList/AddPopup.jsx'; +import FuncPopup from '../components/Challenge/CardDetail.jsx'; +import DeletePopup from '../components/AllList/DeletePopup.jsx'; +import CardDetail from '../components/Challenge/CardDetail.jsx'; + +const Display = styled.div ` + width: 393px; + height: 852px; + border: 1px solid #000; +`; + +const Content = styled.div ` + display: flex; + align-items: center; + flex-direction: column; /* 세로로 쌓이도록 설정 */ + margin-top: 26px; +`; + +const FuncDiv = styled.div ` + display: flex; + width: 340px; + margin-bottom: 20px; + justify-content: space-between; +`; + +const AddMission = styled.div ` + width: 51px; + height: 35px; + border-radius: 20px; + background-color: #CAF4FF; + display: flex; + justify-content: center; + align-items: center; +`; + +const Plus = styled.img ` +`; + +const CategorySelect = styled.select ` + width: 158px; + height: 35px; + border-radius: 20px; + border: 1px solid #000; + font-size: 15px; + padding: 9px 14px; +`; + +const MissionWrap = styled.div` + width: 393px; + height: 599px; + display: flex; + flex-direction: column; /* 세로로 쌓이도록 설정 */ + align-items: center; + overflow-y: scroll; /* 세로 스크롤 */ + + /* 스크롤바 숨기기 */ + &::-webkit-scrollbar { + display: none; + } + -ms-overflow-style: none; + scrollbar-width: none; +`; + +const AllList = () => { + + const [showPopup, setShowPopup] = useState(false); // 팝업 상태 관리 + + const handleAddMission = () => { + setShowPopup(true); // 플러스 버튼 클릭 시 팝업 열기 + }; + + const handleClosePopup = () => { + setShowPopup(false); // 팝업 닫기 + }; + + return ( + <> + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {showPopup && } {/* 팝업 표시 */} + +