diff --git a/package-lock.json b/package-lock.json index d592406..044f8b6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "streetview", "version": "0.1.0", "dependencies": { + "@react-oauth/google": "^0.12.1", "@reduxjs/toolkit": "^2.0.1", "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", @@ -17,12 +18,15 @@ "@types/react": "^18.2.67", "@types/react-dom": "^18.2.22", "axios": "^1.6.3", + "cross-env": "^7.0.3", "dotenv": "^16.3.1", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-kakao-maps-sdk": "^1.1.26", "react-redux": "^9.0.4", "react-router-dom": "^6.22.3", "react-scripts": "5.0.1", + "styled-components": "^6.1.8", "typescript": "^4.9.5", "web-vitals": "^2.1.4", "workbox-background-sync": "^6.6.0", @@ -37,6 +41,10 @@ "workbox-routing": "^6.6.0", "workbox-strategies": "^6.6.0", "workbox-streams": "^6.6.0" + }, + "devDependencies": { + "dotenv-cli": "^7.4.1", + "tsconfig-paths-webpack-plugin": "^4.1.0" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -2305,6 +2313,24 @@ "postcss-selector-parser": "^6.0.10" } }, + "node_modules/@emotion/is-prop-valid": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.1.tgz", + "integrity": "sha512-61Mf7Ufx4aDxx1xlDeOm8aFFigGHE4z+0sKCa+IHCeZKiyP9RLD0Mmx7m8b9/Cf37f7NAvQOOJAbQQGVr5uERw==", + "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==" + }, + "node_modules/@emotion/unitless": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", + "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==" + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -3355,6 +3381,15 @@ } } }, + "node_modules/@react-oauth/google": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/@react-oauth/google/-/google-0.12.1.tgz", + "integrity": "sha512-qagsy22t+7UdkYAiT5ZhfM4StXi9PPNvw0zuwNmabrWyMKddczMtBIOARflbaIj+wHiQjnMAsZmzsUYuXeyoSg==", + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, "node_modules/@reduxjs/toolkit": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.2.1.tgz", @@ -4347,6 +4382,11 @@ "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.0", + "resolved": "https://registry.npmjs.org/@types/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-n4sx2bqL0mW1tvDf/loQ+aMX7GQD3lc3fkCMC55VFNDu/vBOabO+LTIeXKM14xK0ppk5TUGcWRjiSpIlUpghKw==" + }, "node_modules/@types/testing-library__jest-dom": { "version": "5.14.9", "resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.9.tgz", @@ -5860,6 +5900,14 @@ "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==", + "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", @@ -6254,6 +6302,23 @@ "node": ">=10" } }, + "node_modules/cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "bin": { + "cross-env": "src/bin/cross-env.js", + "cross-env-shell": "src/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=10.14", + "npm": ">=6", + "yarn": ">=1" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -6292,6 +6357,14 @@ "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==", + "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", @@ -6482,6 +6555,16 @@ "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==", + "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", @@ -7096,6 +7179,30 @@ "url": "https://dotenvx.com" } }, + "node_modules/dotenv-cli": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/dotenv-cli/-/dotenv-cli-7.4.1.tgz", + "integrity": "sha512-fE1aywjRrWGxV3miaiUr3d2zC/VAiuzEGghi+QzgIA9fEf/M5hLMaRSXb4IxbUAwGmaLi0IozdZddnVU96acag==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "dotenv": "^16.3.0", + "dotenv-expand": "^10.0.0", + "minimist": "^1.2.6" + }, + "bin": { + "dotenv": "cli.js" + } + }, + "node_modules/dotenv-cli/node_modules/dotenv-expand": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-10.0.0.tgz", + "integrity": "sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A==", + "dev": true, + "engines": { + "node": ">=12" + } + }, "node_modules/dotenv-expand": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", @@ -12195,6 +12302,11 @@ "node": ">=4.0" } }, + "node_modules/kakao.maps.d.ts": { + "version": "0.1.39", + "resolved": "https://registry.npmjs.org/kakao.maps.d.ts/-/kakao.maps.d.ts-0.1.39.tgz", + "integrity": "sha512-KXENJ8hHYtjb5G+0vf8TXx/PwWW4j5ndDiQTSMvGtF7EFWu2P3N/+Zivcj9/UKn3j29Iz/sIUaA7WL8Ug3IDGQ==" + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -14968,6 +15080,19 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" }, + "node_modules/react-kakao-maps-sdk": { + "version": "1.1.26", + "resolved": "https://registry.npmjs.org/react-kakao-maps-sdk/-/react-kakao-maps-sdk-1.1.26.tgz", + "integrity": "sha512-rzQLGVPk8LmT8ffZFiPEutQxxufH/K9ffbFgCnE9OqqfvlQ94qmBtl8s5MvTtt3noOJ3W1WBTle1B/Y+Yphuug==", + "dependencies": { + "@babel/runtime": "^7.22.15", + "kakao.maps.d.ts": "^0.1.39" + }, + "peerDependencies": { + "react": "^16.8 || ^17 || ^18", + "react-dom": "^16.8 || ^17 || ^18" + } + }, "node_modules/react-redux": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.1.0.tgz", @@ -15913,6 +16038,11 @@ "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==" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -16445,6 +16575,70 @@ "webpack": "^5.0.0" } }, + "node_modules/styled-components": { + "version": "6.1.9", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.1.9.tgz", + "integrity": "sha512-aBOqs0uMsYufFXSE4q6cA6Ty1fwZuMk4BJRHfiGSna59F1otnxiDelwhN4fEwmBtIymmF0ZqXHnpSigr2ps9Cg==", + "dependencies": { + "@emotion/is-prop-valid": "1.2.1", + "@emotion/unitless": "0.8.1", + "@types/stylis": "4.2.0", + "css-to-react-native": "3.2.0", + "csstype": "3.1.2", + "postcss": "8.4.31", + "shallowequal": "1.1.0", + "stylis": "4.3.1", + "tslib": "2.5.0" + }, + "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/csstype": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", + "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" + }, + "node_modules/styled-components/node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "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" + } + ], + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/styled-components/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + }, "node_modules/stylehacks": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz", @@ -16460,6 +16654,11 @@ "postcss": "^8.2.15" } }, + "node_modules/stylis": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.1.tgz", + "integrity": "sha512-EQepAV+wMsIaGVGX1RECzgrcqRRU/0sYOHkeLsZ3fzHaHXZy4DaOOX0vOlGQdlsjkh3mFHAIlVimpwAs4dslyQ==" + }, "node_modules/sucrase": { "version": "3.35.0", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", @@ -16958,6 +17157,113 @@ "strip-bom": "^3.0.0" } }, + "node_modules/tsconfig-paths-webpack-plugin": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-4.1.0.tgz", + "integrity": "sha512-xWFISjviPydmtmgeUAuXp4N1fky+VCtfhOkDUFIv5ea7p4wuTomI4QTrXvFBX2S4jZsmyTSrStQl+E+4w+RzxA==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "enhanced-resolve": "^5.7.0", + "tsconfig-paths": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/tsconfig-paths-webpack-plugin/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/tsconfig-paths-webpack-plugin/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/tsconfig-paths-webpack-plugin/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/tsconfig-paths-webpack-plugin/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/tsconfig-paths-webpack-plugin/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/tsconfig-paths-webpack-plugin/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/tsconfig-paths-webpack-plugin/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tsconfig-paths-webpack-plugin/node_modules/tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "dev": true, + "dependencies": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/tsconfig-paths/node_modules/json5": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", diff --git a/package.json b/package.json index 91e7af1..a506908 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,7 @@ "last 1 safari version" ] }, + "proxy": "http://semtle.catholic.ac.kr:8085/", "devDependencies": { "dotenv-cli": "^7.4.1", "tsconfig-paths-webpack-plugin": "^4.1.0" diff --git a/src/Pages/MainPage/MainPage.tsx b/src/Pages/MainPage/MainPage.tsx index 32223a5..887aef1 100644 --- a/src/Pages/MainPage/MainPage.tsx +++ b/src/Pages/MainPage/MainPage.tsx @@ -1,11 +1,11 @@ -import { useState } from "react"; -import MainToggle from "../../components/Main/MainToggle/MainToggle"; -import TopSearchBar from "../../components/Main/TopSearchBar/TopSearchBar"; +import MainToggle from "src/components/Main/components/MainToggle"; +import TopSearchBar from "src/components/Main/components/TopSearchBar"; + const MainPage = () => { - const [isOpenSideBar, setIsOpenSideBar] = useState(false); - const handleIsOpenSideBar = () => { - setIsOpenSideBar((prer) => !prer); - }; + // const [isOpenSideBar, setIsOpenSideBar] = useState(false); + // const handleIsOpenSideBar = () => { + // setIsOpenSideBar((prer) => !prer); + // }; return ( <> diff --git a/src/assets/images/basic_color_img.png b/src/assets/images/basic_color_img.png new file mode 100644 index 0000000..988c20e Binary files /dev/null and b/src/assets/images/basic_color_img.png differ diff --git a/src/assets/images/basic_img.png b/src/assets/images/basic_img.png new file mode 100644 index 0000000..b97aac3 Binary files /dev/null and b/src/assets/images/basic_img.png differ diff --git a/src/assets/images/ic_arrow_right.png b/src/assets/images/ic_arrow_right.png new file mode 100644 index 0000000..2a17b1e Binary files /dev/null and b/src/assets/images/ic_arrow_right.png differ diff --git a/src/assets/images/ic_gallery.png b/src/assets/images/ic_gallery.png new file mode 100644 index 0000000..b51af75 Binary files /dev/null and b/src/assets/images/ic_gallery.png differ diff --git a/src/assets/images/ic_mypage.png b/src/assets/images/ic_mypage.png new file mode 100644 index 0000000..ba592c6 Binary files /dev/null and b/src/assets/images/ic_mypage.png differ diff --git a/src/assets/images/ic_report.png b/src/assets/images/ic_report.png new file mode 100644 index 0000000..7be34b3 Binary files /dev/null and b/src/assets/images/ic_report.png differ diff --git a/src/assets/images/ic_x.png b/src/assets/images/ic_x.png new file mode 100644 index 0000000..69de222 Binary files /dev/null and b/src/assets/images/ic_x.png differ diff --git a/src/assets/images/img_upload.png b/src/assets/images/img_upload.png new file mode 100644 index 0000000..9082183 Binary files /dev/null and b/src/assets/images/img_upload.png differ diff --git a/src/components/Atom/ImageModal/ImageModal.css b/src/components/Atom/ImageModal/ImageModal.css new file mode 100644 index 0000000..3ef4390 --- /dev/null +++ b/src/components/Atom/ImageModal/ImageModal.css @@ -0,0 +1,30 @@ +.image__modal__wrapper{ + width: 100%; + height: 100%; +} +.image__modal__content { + width: 100%; + height: 100%; + display: flex; + justify-content: center; + align-items: center; + position: relative; +} +.image__modal__content img { + object-fit: contain; +} +.image__modal__currentIndex { + margin-left: auto; +} +.image__modal__control { + position: absolute; + top: 50%; + transform: translate(-50%, -50%); + left: 50%; + width: 90%; + display: flex; + justify-content: space-between; +} +.image__modal__control div { + cursor: pointer; +} diff --git a/src/components/Atom/ImageModal/ImageModal.tsx b/src/components/Atom/ImageModal/ImageModal.tsx new file mode 100644 index 0000000..60eaa23 --- /dev/null +++ b/src/components/Atom/ImageModal/ImageModal.tsx @@ -0,0 +1,52 @@ +import { useState } from "react"; +import { IMAGES } from "src/constants/images"; +import { Arrow } from "../arrow/Arrow"; +import "./ImageModal.css"; +interface ImageModalProps { + imageList?: string[]; +} +export const ImageModal = ({ imageList = [IMAGES.basicImg] }: ImageModalProps) => { + const [currentIndex, setCurrentIndex] = useState(0); + const [startX, setStartX] = useState(0); + + const handleTouchStart = (e: React.TouchEvent) => { + setStartX(e.touches[0].pageX); + }; + + const handleSwipe = (endX: number) => { + const diffX = startX - endX; + if (diffX > 50) { + handlePrevNext(-1); + } else if (diffX < -50) { + handlePrevNext(1); + } + }; + + const handleTouchMove = (e: React.TouchEvent) => { + handleSwipe(e.touches[0].pageX); + }; + const handlePrevNext = (delta: number) => { + const newIndex = currentIndex + delta; + if (newIndex >= 0 && newIndex < imageList.length) setCurrentIndex(newIndex); + }; + + return ( +
+
+ contentImg +
+ handlePrevNext(-1)} isLeft={true} /> + handlePrevNext(1)} /> +
+
+
+ ); +}; diff --git a/src/components/Atom/arrow/Arrow.tsx b/src/components/Atom/arrow/Arrow.tsx new file mode 100644 index 0000000..f9461bd --- /dev/null +++ b/src/components/Atom/arrow/Arrow.tsx @@ -0,0 +1,27 @@ +import { IMAGES } from "src/constants/images"; + +interface arrowProps { + onClick: () => void; + width?: string; + height?: string; + isLeft?: boolean; +} + +export const Arrow = ({ + onClick, + width = "0.875rem", + height = "1.64063rem", + isLeft = false, +}: arrowProps) => { + const style: React.CSSProperties = { + transform: isLeft ? "rotate(180deg)" : "", + width: width, + height: height, + cursor: "pointer", + }; + return ( +
+ arrowLeft +
+ ); +}; diff --git a/src/components/Atom/backgroundImg/BackgroundImg.tsx b/src/components/Atom/backgroundImg/BackgroundImg.tsx new file mode 100644 index 0000000..a6764a3 --- /dev/null +++ b/src/components/Atom/backgroundImg/BackgroundImg.tsx @@ -0,0 +1,29 @@ +import { ReactNode } from "react"; + +export interface BackgroundImgProps { + children?: ReactNode; + img: string; + className?: string; + onClick?: React.MouseEventHandler | undefined; +} + +export function BackgroundImg({ + children, + className = "", + img, + onClick, +}: BackgroundImgProps) { + console.log("iiiiii",img); + return ( +
+ {children} +
+ ); +} diff --git a/src/components/Atom/commentBox/CommentBox.css b/src/components/Atom/commentBox/CommentBox.css new file mode 100644 index 0000000..3c8fcaa --- /dev/null +++ b/src/components/Atom/commentBox/CommentBox.css @@ -0,0 +1,19 @@ +.comment-box-wrapper { + display: flex; + align-items: center; + margin-bottom: 0.4rem; +} +.comment-box-user-info { + display: flex; + align-items: center; + margin-right: 0.5rem; +} +.comment-box-user-profile { + width: 1.875rem; + height: 1.875rem; + border-radius: 50%; +} +.comment-box-user-name { + text-align: center; + margin-left: 0.5rem; +} diff --git a/src/components/Atom/commentBox/CommentBox.tsx b/src/components/Atom/commentBox/CommentBox.tsx new file mode 100644 index 0000000..c37282d --- /dev/null +++ b/src/components/Atom/commentBox/CommentBox.tsx @@ -0,0 +1,33 @@ +import { Comment } from "src/constants/interface"; +import { BackgroundImg } from "../backgroundImg/BackgroundImg"; +import { Text } from "../text/Text"; +import "./CommentBox.css"; + +const CommentBox = ({ commentItem }: { commentItem: Comment }) => { + return ( +
+
+ {commentItem.member.picture && ( + + )} + + {commentItem.member.nickName} + {/* 익명 */} + +
+ + {commentItem.replyContent} + +
+ ); +}; +export default CommentBox; diff --git a/src/components/Atom/customModal/CustomModal.css b/src/components/Atom/customModal/CustomModal.css new file mode 100644 index 0000000..2b40810 --- /dev/null +++ b/src/components/Atom/customModal/CustomModal.css @@ -0,0 +1,50 @@ +.custom-modal-wrapper { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.5); /* 반투명 배경 */ + display: flex; + justify-content: center; + align-items: center; + z-index: 10; +} +.custom-modal-box { + width: 15.875rem; + height: 8.1875rem; + border-radius: 1.25rem; + background: #fff; + flex-direction: column; + display: flex; + align-items: center; +} +.custom-modal-title { + margin-top: 1rem; + margin-bottom: 0.5rem; +} + +.custom-modal-box-buttom { + margin-top: auto; + display: flex; + align-items: center; + width: 100%; +} +.custom-modal-box-yes { + background: #ea4335; + height: 2.5625rem; + width: 50%; + border-radius: 0 0 0 1.25rem; + display: flex; + align-items: center; + justify-content: center; +} +.custom-modal-box-no { + background: #fff; + height: 2.5625rem; + width: 50%; + display: flex; + align-items: center; + justify-content: center; + border-radius: 0 0 1.25rem 0; +} diff --git a/src/components/Atom/customModal/CustomModal.tsx b/src/components/Atom/customModal/CustomModal.tsx new file mode 100644 index 0000000..07ea294 --- /dev/null +++ b/src/components/Atom/customModal/CustomModal.tsx @@ -0,0 +1,40 @@ +import { Text } from "../text/Text"; +import "./CustomModal.css"; +interface CoustomModalProps { + title: string; + content: string; + onClick: () => void; + onClose: () => void; +} +const CustomModal = ({ + title, + content, + onClick, + onClose, +}: CoustomModalProps) => { + return ( +
+
+ + {title} + + + {content} + +
+
+ + 예 + +
+
+ + 아니요 + +
+
+
+
+ ); +}; +export default CustomModal; diff --git a/src/components/Atom/customModal/logoutModal/LogoutModal.css b/src/components/Atom/customModal/logoutModal/LogoutModal.css new file mode 100644 index 0000000..e69de29 diff --git a/src/components/Atom/customModal/logoutModal/LogoutModal.tsx b/src/components/Atom/customModal/logoutModal/LogoutModal.tsx new file mode 100644 index 0000000..777a6fb --- /dev/null +++ b/src/components/Atom/customModal/logoutModal/LogoutModal.tsx @@ -0,0 +1,32 @@ +import { useDispatch } from "react-redux"; +import { useNavigate } from "react-router-dom"; +import { logout } from "src/store/auth/authAction"; +import CustomModal from "../CustomModal"; +import "./LogoutModal.css"; + +interface logoutProp { + onClose: () => void; +} +const LogoutModal = ({ onClose }: logoutProp) => { + const navigate = useNavigate(); + const dispatch = useDispatch(); + const handleLogout = () => { + // localStorage.removeItem("token"); + localStorage.clear(); + dispatch(logout()); + navigate("/"); + onClose(); + window.location.reload(); + }; + return ( + <> + + + ); +}; +export default LogoutModal; diff --git a/src/components/Atom/mainToggleIcon/MainToggleIcon.css b/src/components/Atom/mainToggleIcon/MainToggleIcon.css new file mode 100644 index 0000000..ecd8c46 --- /dev/null +++ b/src/components/Atom/mainToggleIcon/MainToggleIcon.css @@ -0,0 +1,13 @@ +.google-login-button { + margin-left: 0.56rem; + margin-right: 1rem; + width: 70%; + border-radius: 2.5rem; + box-shadow: 0px 0px 1px 0px rgba(0, 0, 0, 0.08), + 0px 0px 1px 0px rgba(0, 0, 0, 0.17); +} +.menu-circle-img-style { + width: 1.26563rem; + height: 1.26563rem; + cursor: pointer; +} diff --git a/src/components/Atom/mainToggleIcon/MainToggleIcon.tsx b/src/components/Atom/mainToggleIcon/MainToggleIcon.tsx new file mode 100644 index 0000000..18742bf --- /dev/null +++ b/src/components/Atom/mainToggleIcon/MainToggleIcon.tsx @@ -0,0 +1,15 @@ +import "./MainToggleIcon.css"; +interface MainToggleIconProps { + onClick?: () => void; + imgSrc: string; + clasName?: string; +} +const MainToggleIcon = ({ + imgSrc, + onClick, + clasName = "menu-circle-img-style", +}: MainToggleIconProps) => { + return Icon; +}; + +export default MainToggleIcon; diff --git a/src/components/Atom/mappingImgItem/MappingImgItem.css b/src/components/Atom/mappingImgItem/MappingImgItem.css new file mode 100644 index 0000000..de2e5a3 --- /dev/null +++ b/src/components/Atom/mappingImgItem/MappingImgItem.css @@ -0,0 +1,16 @@ +.mapping__img__item__img { + position: relative; +} +.mapping__img__item__img img { + width: 15.125rem; + height: 15.125rem; + object-fit: cover; + border-radius: 0.625rem; +} +.mapping__img__item__remove img { + width: 1rem; + height: 1rem; + position: absolute; + top: 0.2rem; + right: 0.2rem; +} diff --git a/src/components/Atom/mappingImgItem/MappingImgItem.tsx b/src/components/Atom/mappingImgItem/MappingImgItem.tsx new file mode 100644 index 0000000..e137494 --- /dev/null +++ b/src/components/Atom/mappingImgItem/MappingImgItem.tsx @@ -0,0 +1,25 @@ +import { IMAGES } from "src/constants/images"; +import "./MappingImgItem.css"; + +interface MappingImgItemProps { + imgUrl: string; + index: number; + onClick: (index: number) => void; +} +export const MappingImgItem = ({ + imgUrl, + index, + onClick, +}: MappingImgItemProps) => { + return ( +
+ {`mapping__img__item__img__${index}`} +
onClick(index)} + > + X +
+
+ ); +}; diff --git a/src/components/Atom/text/Text.tsx b/src/components/Atom/text/Text.tsx new file mode 100644 index 0000000..060693a --- /dev/null +++ b/src/components/Atom/text/Text.tsx @@ -0,0 +1,32 @@ +import React, { ReactNode } from "react"; + +export interface TextProps { + children?: ReactNode; + color?: string; + fontSize?: string; + fontWeight?: string; + className?: string; + onClick?: any; +} + +export function Text({ + children, + color = "#FFF", + fontSize = "0.75rem", + fontWeight = "700", + className = "", + onClick, +}: TextProps) { + const textStyle: React.CSSProperties = { + color: color, + fontSize: fontSize, + fontWeight: fontWeight, + fontFamily: "Plus Jakarta Sans", + }; + + return ( + + {children} + + ); +} diff --git a/src/components/Atom/topSideToggle/TopSideToggle.css b/src/components/Atom/topSideToggle/TopSideToggle.css new file mode 100644 index 0000000..62d88e0 --- /dev/null +++ b/src/components/Atom/topSideToggle/TopSideToggle.css @@ -0,0 +1,19 @@ +.top-side-toggle-wrapper { + display: flex; + flex-direction: column; + } + .top-side-toggle-icon { + display: flex; + justify-content: flex-start; + align-items: center; + width: 6.75rem; + height: 2.5rem; + border-radius: 1.25rem; + margin-bottom: 1.06rem; + cursor: pointer; + } + .top-side-toggle-icon-text { + text-align: center; + margin-left: 0.4rem; + } + \ No newline at end of file diff --git a/src/components/Atom/topSideToggle/TopSideToggle.tsx b/src/components/Atom/topSideToggle/TopSideToggle.tsx new file mode 100644 index 0000000..800b9c3 --- /dev/null +++ b/src/components/Atom/topSideToggle/TopSideToggle.tsx @@ -0,0 +1,117 @@ +import { useEffect, useState } from "react"; +import { useLocation, useNavigate } from "react-router-dom"; +import PagePath from "src/constants/PagePath"; +import LogoutModal from "../customModal/logoutModal/LogoutModal"; +import { Text } from "../text/Text"; +import WriteModal from "../writeModal/WriteModal"; +import "./TopSideToggle.css"; +import { MypageArr } from "./constants"; + +const TopSideToggle = () => { + const location = useLocation(); + const [selectedIcon, setSelectedIcon] = useState(location.pathname); + useEffect(() => { + setSelectedIcon(location.pathname); + }, [location.pathname]); //주소 변경마다 업데이트 + const navigate = useNavigate(); + const [openLogoutModal, setOpenLogoutModal] = useState(false); + const [openStreetModal, setOpenStreetModal] = useState(false); + const logoutClick = () => { + setOpenLogoutModal(true); + }; + const handlelogoutClose = () => { + setOpenLogoutModal(false); + }; + const handleOpenFetchStreet = () => { + setOpenStreetModal(true); + }; + const handleCloseFetchStreet = () => { + setOpenStreetModal(false); + }; + type TopSideToggleIconProp = { + path: string; + img: string; + alt: string; + height: number; + width: number; + }; + const TopSideToggleIcon = ({ + path, + img, + alt, + height, + width, + }: TopSideToggleIconProp) => { + const topSideToggleIconWrapperStyle: React.CSSProperties = { + background: selectedIcon.startsWith(path) ? "#5356FF" : "#89BBF1", + }; + const topSideToggleIconImgStyle: React.CSSProperties = { + width: `${width}rem`, + height: `${height}rem`, + marginLeft: "0.9rem", + }; + const handleOnClick = () => { + switch (path) { + case PagePath.Mypage: + navigate(`/${path}`); + break; + case PagePath.logout: + logoutClick(); + break; + case PagePath.Request: + handleOpenFetchStreet(); + break; + default: + console.error("Unhandled path: ", path); + } + }; + + return ( +
+ {alt} + + {alt} + +
+ ); + }; + + return ( + <> +
+ {MypageArr.map((item, index) => ( + + ))} + {openLogoutModal && } + {openStreetModal && ( + + )} +
+ + ); +}; + +export default TopSideToggle; diff --git a/src/components/Atom/topSideToggle/constants.ts b/src/components/Atom/topSideToggle/constants.ts new file mode 100644 index 0000000..31bfd66 --- /dev/null +++ b/src/components/Atom/topSideToggle/constants.ts @@ -0,0 +1,26 @@ +import PagePath from "src/constants/PagePath"; +import { IMAGES } from "src/constants/images"; + +export const MypageArr = [ + { + path: PagePath.Request, + img: IMAGES.addLocation, + alt: "거리요청", + height: 1.4375, + width: 1.15006, + }, + { + path: PagePath.Mypage, + img: IMAGES.mypageIcon, + alt: "마이페이지", + height: 1.1875, + width: 1.1875, + }, + { + path: PagePath.logout, + img: IMAGES.myRemveWhite, + alt: "로그아웃", + height: 1.0625, + width: 1.0625, + }, +]; diff --git a/src/components/Atom/writeModal/WriteModal.css b/src/components/Atom/writeModal/WriteModal.css new file mode 100644 index 0000000..09034f9 --- /dev/null +++ b/src/components/Atom/writeModal/WriteModal.css @@ -0,0 +1,109 @@ +.write-modal-wrapper { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.5); /* 반투명 배경 */ + display: flex; + justify-content: center; + align-items: center; + z-index: 10; +} + +.write-modal-box { + width: 28.875rem; + height: 37.4375rem; + border-radius: 1.25rem; + background: #fff; + padding: 1.2rem; +} +.write-modal-top { + display: flex; + align-items: center; + width: 100%; +} +.write-modal-top span { + margin-left: auto; +} +.write-modal-top img { + width: 0.90781rem; + height: 0.90056rem; + margin-left: auto; +} + +.write-modal-img-input-wrapper { + margin-top: 0.8rem; + + display: flex; + flex-direction: column; + align-items: center; +} +.write-modal-img-input-box { + width: 100%; + display: flex; + align-items: center; + justify-content: center; + overflow-x: auto; + flex-wrap: nowrap; +} +.write-modal-maping-img { + display: flex; + margin: 0 0.6rem; +} +.write-modal-img-input-box::-webkit-scrollbar { + display: none; +} +.write-modal-img-input img { + width: 15.125rem; + height: 15.125rem; + border-radius: 0.625rem; + background: #dff5ff; +} + +.write-modal-summit-button { + width: 5.3125rem; + height: 1.875rem; + flex-shrink: 0; + border-radius: 0.625rem; + background: #dff5ff; + display: flex; + align-items: center; + justify-content: center; + margin-top: 1rem; + cursor: pointer; +} +.write-modal-content { + margin-bottom: 0.75rem; + width: 100%; + display: flex; + justify-content: flex-start; + flex-direction: column; + border-bottom: 1px solid #000; +} +.write-modal-content input, +textarea { + margin-top: 0.44rem; + width: 100%; + height: 100%; + outline: none; + border: none; + + &::focus { + outline: none; + border: none; + } + &::placeholder { + color: #d9d9d9; + font-family: "Plus Jakarta Sans"; + font-size: 0.625rem; + font-style: normal; + font-weight: 700; + line-height: normal; + } +} +.write-modal-tag-list { + width: 100%; + display: flex; + justify-content: flex-start; +} diff --git a/src/components/Atom/writeModal/WriteModal.tsx b/src/components/Atom/writeModal/WriteModal.tsx new file mode 100644 index 0000000..3d74060 --- /dev/null +++ b/src/components/Atom/writeModal/WriteModal.tsx @@ -0,0 +1,191 @@ +import React, { FC, useState } from "react"; +import { IMAGES } from "src/constants/images"; +import { Coordinates, Tag } from "src/constants/interface"; +import { useFetchReview } from "src/hook/useFetchReview"; +import useFetchStreet from "src/hook/useFetchStreet"; +import { MappingImgItem } from "../mappingImgItem/MappingImgItem"; +import { Text } from "../text/Text"; +import "./WriteModal.css"; + +interface WriteModalProps { + writeTypeProp: string; + streetDatas?: Coordinates; + onClose: () => void; +} + +const WriteModal: FC = ({ + writeTypeProp, + streetDatas, + onClose, +}) => { + const writeType = writeTypeProp === "REVIEW" ? "리뷰" : "거리"; + const { writeReview } = useFetchReview(); + const { writeStreet } = useFetchStreet(); + const [uploadTitle, setUpladTitle] = useState(""); + const [uploadContent, setUpladContent] = useState(""); + const [tag, setTag] = useState(""); + const [uploadTagList, setUpladTagList] = useState([]); + const [uploadImages, setUploadImages] = useState<{ + imageFiles: File[]; + imageUrls: string[]; + }>({ imageFiles: [], imageUrls: [] }); + const handleFetchData = async () => { + if (writeTypeProp === "REVIEW") { + if (streetDatas === undefined) { + console.log("streetData = undifind"); + return; + } + try { + await writeReview( + { lat: streetDatas.lat, lng: streetDatas.lng }, + uploadContent, + uploadImages.imageFiles + ); + console.log("리뷰 작성 성공"); + } catch (error) { + console.error("리뷰 작성 실패:", error); + } + } else if (writeTypeProp === "STREET") { + try { + await writeStreet(uploadTitle, uploadTagList, uploadImages.imageFiles); + console.log("거리 작성 성공"); + } catch (error) { + console.error("거리 작성 실패:", error); + } + } + onClose(); + }; + const handleImageInputChange = (e: React.ChangeEvent) => { + if (!e.target.files) return; // a + const files = e.target.files; + const fileArray = Array.from(files); //b + if (uploadImages.imageFiles.length + fileArray.length <= 3) { + const newImages = Array.from(files, (file) => URL.createObjectURL(file)); + setUploadImages({ + imageFiles: [...uploadImages.imageFiles, ...fileArray], + imageUrls: [...uploadImages.imageUrls, ...newImages], + }); + } else { + alert("이미지는 최대 3장까지 첨부할 수 있습니다."); + } + }; + const handleRemoveImage = (id: number) => { + setUploadImages({ + imageFiles: uploadImages.imageFiles.filter((_, index) => index !== id), + imageUrls: uploadImages.imageUrls.filter((_, index) => index !== id), + }); + }; + return ( +
+
+
+ + {writeType} 작성 + + x +
+
+ <> +
+
+ {uploadImages.imageUrls.map((url, index) => ( + handleRemoveImage(index)} + /> + ))} +
+
+ + +
+
+ + <> + {writeTypeProp === "STREET" && ( +
+ + 제목 + + setUpladTitle(e.target.value)} + /> +
+ )} + {writeTypeProp === "REVIEW" && ( +
+ + 내용 + +