diff --git a/.env b/.env new file mode 100644 index 0000000..bdd0971 --- /dev/null +++ b/.env @@ -0,0 +1 @@ +REACT_APP_SERVER_URL=http://localhost:8080 \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index a9052ab..4c5c5e1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,15 +8,25 @@ "name": "front", "version": "0.1.0", "dependencies": { + "@stomp/stompjs": "^7.0.0", "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", + "axios": "^1.7.7", + "date-fns": "^4.1.0", + "qs": "^6.13.0", "react": "^18.3.1", "react-dom": "^18.3.1", "react-icons": "^5.3.0", + "react-modal": "^3.16.1", "react-router-dom": "^6.26.0", "react-scripts": "5.0.1", + "sockjs-client": "^1.6.1", + "stompjs": "^2.3.3", "web-vitals": "^2.1.4" + }, + "devDependencies": { + "@babel/plugin-proposal-private-property-in-object": "^7.21.11" } }, "node_modules/@adobe/css-tools": { @@ -629,9 +639,18 @@ } }, "node_modules/@babel/plugin-proposal-private-property-in-object": { - "version": "7.21.0-placeholder-for-preset-env.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", - "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "version": "7.21.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.11.tgz", + "integrity": "sha512-0QZ8qP/3RLDVBwBFoWAwCtgcDZJVwA5LUJRZU8x2YFfKNuFq161wK3cuGrALu5yiPu+vzwTAg/sMWVNeWeNyaw==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-property-in-object instead.", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-create-class-features-plugin": "^7.21.0", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + }, "engines": { "node": ">=6.9.0" }, @@ -1890,6 +1909,18 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/preset-env/node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/preset-env/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -3411,6 +3442,11 @@ "@sinonjs/commons": "^1.7.0" } }, + "node_modules/@stomp/stompjs": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@stomp/stompjs/-/stompjs-7.0.0.tgz", + "integrity": "sha512-fGdq4wPDnSV/KyOsjq4P+zLc8MFWC3lMmP5FBgLWKPJTYcuCbAIrnRGjB7q2jHZdYCOD5vxLuFoKIYLy5/u8Pw==" + }, "node_modules/@surma/rollup-plugin-off-main-thread": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz", @@ -5394,6 +5430,29 @@ "node": ">=4" } }, + "node_modules/axios": { + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", + "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/axios/node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/axobject-query": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.1.1.tgz", @@ -5789,6 +5848,20 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "node_modules/body-parser/node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/bonjour-service": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.2.1.tgz", @@ -5872,6 +5945,19 @@ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" }, + "node_modules/bufferutil": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.8.tgz", + "integrity": "sha512-4T53u4PdgsXqKaIctwF8ifXlRTTmEPJ8iEPWFdGZvcf7sbwYo6FKFEX9eNNAnzFZ7EzJAQ3CJeOtCRA4rDp7Pw==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, "node_modules/builtin-modules": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", @@ -6710,6 +6796,19 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" }, + "node_modules/d": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.2.tgz", + "integrity": "sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==", + "optional": true, + "dependencies": { + "es5-ext": "^0.10.64", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.12" + } + }, "node_modules/damerau-levenshtein": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", @@ -6776,6 +6875,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/date-fns": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz", + "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, "node_modules/debug": { "version": "4.3.6", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", @@ -7411,6 +7519,46 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/es5-ext": { + "version": "0.10.64", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", + "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "esniff": "^2.0.1", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "optional": true, + "dependencies": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/es6-symbol": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.4.tgz", + "integrity": "sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==", + "optional": true, + "dependencies": { + "d": "^1.0.2", + "ext": "^1.7.0" + }, + "engines": { + "node": ">=0.12" + } + }, "node_modules/escalade": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", @@ -8059,6 +8207,21 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/esniff": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", + "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", + "optional": true, + "dependencies": { + "d": "^1.0.1", + "es5-ext": "^0.10.62", + "event-emitter": "^0.3.5", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/espree": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", @@ -8138,6 +8301,16 @@ "node": ">= 0.6" } }, + "node_modules/event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", + "optional": true, + "dependencies": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, "node_modules/eventemitter3": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", @@ -8151,6 +8324,14 @@ "node": ">=0.8.x" } }, + "node_modules/eventsource": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-2.0.2.tgz", + "integrity": "sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA==", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -8173,6 +8354,11 @@ "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, + "node_modules/exenv": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/exenv/-/exenv-1.2.2.tgz", + "integrity": "sha512-Z+ktTxTwv9ILfgKCk32OX3n/doe+OcLTRtqK9pcL+JsP3J1/VW8Uvl4ZjLlKqeW4rzK4oesDOGMEMRIZqtP4Iw==" + }, "node_modules/exit": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", @@ -8249,6 +8435,29 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "node_modules/express/node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ext": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", + "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", + "optional": true, + "dependencies": { + "type": "^2.7.2" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -12716,6 +12925,12 @@ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" }, + "node_modules/next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", + "optional": true + }, "node_modules/no-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", @@ -12733,6 +12948,17 @@ "node": ">= 6.13.0" } }, + "node_modules/node-gyp-build": { + "version": "4.8.2", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.2.tgz", + "integrity": "sha512-IRUxE4BVsHWXkV/SFOut4qTlagw2aM8T5/vnTsmrHJvVoKueJHRc/JaFND7QDDc61kLYUJ6qlZM3sqTSyx2dTw==", + "optional": true, + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -14618,6 +14844,11 @@ "node": ">= 0.10" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/psl": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", @@ -14642,11 +14873,11 @@ } }, "node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", "dependencies": { - "side-channel": "^1.0.4" + "side-channel": "^1.0.6" }, "engines": { "node": ">=0.6" @@ -14963,6 +15194,10 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.3.0.tgz", "integrity": "sha512-DnUk8aFbTyQPSkCfF8dbX6kQjXA9DktMeJqfjrg6cK9vwQVMxmcA3BfP4QoiztVmEHtwlTgLFsPuH2NskKT6eg==", +<<<<<<< HEAD + "license": "MIT", +======= +>>>>>>> develop#1 "peerDependencies": { "react": "*" } @@ -14972,6 +15207,30 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" }, + "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==" + }, + "node_modules/react-modal": { + "version": "3.16.1", + "resolved": "https://registry.npmjs.org/react-modal/-/react-modal-3.16.1.tgz", + "integrity": "sha512-VStHgI3BVcGo7OXczvnJN7yT2TWHJPDXZWyI/a0ssFNhGZWsPmB8cF0z33ewDXq4VfYMO1vXgiv/g8Nj9NDyWg==", + "license": "MIT", + "dependencies": { + "exenv": "^1.2.0", + "prop-types": "^15.7.2", + "react-lifecycles-compat": "^3.0.0", + "warning": "^4.0.3" + }, + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "react": "^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18", + "react-dom": "^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18" + } + }, "node_modules/react-refresh": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", @@ -15951,6 +16210,32 @@ "websocket-driver": "^0.7.4" } }, + "node_modules/sockjs-client": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.6.1.tgz", + "integrity": "sha512-2g0tjOR+fRs0amxENLi/q5TiJTqY+WXFOzb5UwXndlK6TO3U/mirZznpx6w34HVMoc3g7cY24yC/ZMIYnDlfkw==", + "dependencies": { + "debug": "^3.2.7", + "eventsource": "^2.0.2", + "faye-websocket": "^0.11.4", + "inherits": "^2.0.4", + "url-parse": "^1.5.10" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://tidelift.com/funding/github/npm/sockjs-client" + } + }, + "node_modules/sockjs-client/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dependencies": { + "ms": "^2.1.1" + } + }, "node_modules/source-list-map": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", @@ -16179,6 +16464,14 @@ "node": ">= 0.8" } }, + "node_modules/stompjs": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/stompjs/-/stompjs-2.3.3.tgz", + "integrity": "sha512-5l/Ogz0DTFW7TrpHF0LAETGqM/so8UxNJvYZjJKqcX31EVprSQgnGkO80tZctPC/lFBDUrSFiTG3xd0R27XAIA==", + "optionalDependencies": { + "websocket": "latest" + } + }, "node_modules/stop-iteration-iterator": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz", @@ -17009,6 +17302,12 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, + "node_modules/type": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.3.tgz", + "integrity": "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==", + "optional": true + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -17288,6 +17587,19 @@ "requires-port": "^1.0.0" } }, + "node_modules/utf-8-validate": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz", + "integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -17382,6 +17694,14 @@ "makeerror": "1.0.12" } }, + "node_modules/warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, "node_modules/watchpack": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", @@ -17641,6 +17961,23 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/websocket": { + "version": "1.0.35", + "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.35.tgz", + "integrity": "sha512-/REy6amwPZl44DDzvRCkaI1q1bIiQB0mEFQLUrhz3z2EK91cp3n72rAjUlrTP0zV22HJIUOVHQGPxhFRjxjt+Q==", + "optional": true, + "dependencies": { + "bufferutil": "^4.0.1", + "debug": "^2.2.0", + "es5-ext": "^0.10.63", + "typedarray-to-buffer": "^3.1.5", + "utf-8-validate": "^5.0.2", + "yaeti": "^0.0.6" + }, + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/websocket-driver": { "version": "0.7.4", "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", @@ -17662,6 +17999,21 @@ "node": ">=0.8.0" } }, + "node_modules/websocket/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "optional": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/websocket/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "optional": true + }, "node_modules/whatwg-encoding": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", @@ -18244,6 +18596,15 @@ "node": ">=10" } }, + "node_modules/yaeti": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", + "integrity": "sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug==", + "optional": true, + "engines": { + "node": ">=0.10.32" + } + }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", diff --git a/package.json b/package.json index 524bab3..ff295e1 100644 --- a/package.json +++ b/package.json @@ -3,14 +3,21 @@ "version": "0.1.0", "private": true, "dependencies": { + "@stomp/stompjs": "^7.0.0", "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", + "axios": "^1.7.7", + "date-fns": "^4.1.0", + "qs": "^6.13.0", "react": "^18.3.1", "react-dom": "^18.3.1", "react-icons": "^5.3.0", + "react-modal": "^3.16.1", "react-router-dom": "^6.26.0", "react-scripts": "5.0.1", + "sockjs-client": "^1.6.1", + "stompjs": "^2.3.3", "web-vitals": "^2.1.4" }, "scripts": { @@ -36,5 +43,8 @@ "last 1 firefox version", "last 1 safari version" ] + }, + "devDependencies": { + "@babel/plugin-proposal-private-property-in-object": "^7.21.11" } -} +} \ No newline at end of file diff --git a/public/asset/ChatRoomPic1.png b/public/asset/ChatRoomPic1.png new file mode 100644 index 0000000..84b1404 Binary files /dev/null and b/public/asset/ChatRoomPic1.png differ diff --git a/public/asset/ChatRoomPic2.png b/public/asset/ChatRoomPic2.png new file mode 100644 index 0000000..5f325b0 Binary files /dev/null and b/public/asset/ChatRoomPic2.png differ diff --git a/public/asset/ChatRoomPic3.png b/public/asset/ChatRoomPic3.png new file mode 100644 index 0000000..6d3f4e8 Binary files /dev/null and b/public/asset/ChatRoomPic3.png differ diff --git a/public/asset/ChatRoomPic4.png b/public/asset/ChatRoomPic4.png new file mode 100644 index 0000000..1052ef9 Binary files /dev/null and b/public/asset/ChatRoomPic4.png differ diff --git a/public/asset/ChatRoomPic5.png b/public/asset/ChatRoomPic5.png new file mode 100644 index 0000000..45622aa Binary files /dev/null and b/public/asset/ChatRoomPic5.png differ diff --git a/public/asset/ChatRoomPic6.png b/public/asset/ChatRoomPic6.png new file mode 100644 index 0000000..26459e8 Binary files /dev/null and b/public/asset/ChatRoomPic6.png differ diff --git a/public/asset/my.png b/public/asset/my.png new file mode 100644 index 0000000..8c43fa2 Binary files /dev/null and b/public/asset/my.png differ diff --git a/public/asset/play.png b/public/asset/play.png new file mode 100644 index 0000000..a266e74 Binary files /dev/null and b/public/asset/play.png differ diff --git a/public/asset/talk.png b/public/asset/talk.png new file mode 100644 index 0000000..6867801 Binary files /dev/null and b/public/asset/talk.png differ diff --git a/src/App.jsx b/src/App.jsx index db658a0..762db0e 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,7 +1,16 @@ -import { BrowserRouter as Router, Routes, Route, Navigate } from "react-router-dom"; +import { BrowserRouter as Router, Routes, Route } from "react-router-dom"; import "./App.css"; import Main from "./page/Main/Main"; +import Login from "./page/Login/Login"; +import Login1 from "./page/Login/Login1"; +import Login2 from "./page/Login/Login2"; +import Login3 from "./page/Login/Login3"; +import Login4 from "./page/Login/Login4"; +import Login5 from "./page/Login/Login5"; +import Login6 from "./page/Login/Login6"; +import Login7 from "./page/Login/Login7"; import Mypage from "./page/Mypage/Mypage" +import Mbti from "./page/Mbti/Mbti"; import Mbti1 from "./page/Mbti/Mbti1"; import Mbti2 from "./page/Mbti/Mbti2"; import Mbti3 from "./page/Mbti/Mbti3"; @@ -30,58 +39,70 @@ import INFP from "./page/Mbti/Mbti_result/INFP"; import INFJ from "./page/Mbti/Mbti_result/INFJ"; import INTP from "./page/Mbti/Mbti_result/INTP"; import INTJ from "./page/Mbti/Mbti_result/INTJ"; - +import ChatStartPage from "./page/Chat/ChatStartPage" +import ChatRoom from "./page/Chat/ChatRoom/ChatRoom" +import MakeChatRoom from "./page/Chat/MakeChatRoom/MakeChatRoom"; import { MbtiProvider } from "./context/MbitContext"; -import Estj from "./page/Mbti/Mbti_result/ESTJ"; -import Entp from "./page/Mbti/Mbti_result/ENTP"; -import Intp from "./page/Mbti/Mbti_result/INTP"; -import Infj from "./page/Mbti/Mbti_result/INFJ"; -import Istp from "./page/Mbti/Mbti_result/ISTP"; +import { UserProvider } from "./context/UserContext"; function App() { - + return ( + + + + + + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> - - - - - - } /> - }/> - }/> - }/> - }/> - }/> - }/> - }/> - }/> - }/> - }/> - }/> - }/> - }/> - }/> - }/> - }/> - }/> - }/> - }/> - }/> - }/> - }/> - }/> - }/> - }/> - }/> - }/> - }/> - }/> - - - + } /> + {/* } /> */} + } /> + } /> + + + + ); } -export default App; +export default App; \ No newline at end of file diff --git a/src/api/api.js b/src/api/api.js new file mode 100644 index 0000000..a47498b --- /dev/null +++ b/src/api/api.js @@ -0,0 +1,19 @@ +import axios from 'axios'; + +const api = axios.create({ + baseURL: `${process.env.REACT_APP_SERVER_URL}`, + withCredentials: true,//쿠키를 자동으로 포함 +}); + +// 요청 인터셉터를 추가하여 모든 요청에 Authorization 헤더를 추가 +api.interceptors.request.use(config => { + const token = localStorage.getItem('token'); + if (token) { + config.headers.Authorization = `Bearer ${token}`; + } + return config; +}, error => { + return Promise.reject(error); +}) + +export default api; \ No newline at end of file diff --git a/src/api/chatRoom/fetchAnotherInformation.jsx b/src/api/chatRoom/fetchAnotherInformation.jsx new file mode 100644 index 0000000..104b235 --- /dev/null +++ b/src/api/chatRoom/fetchAnotherInformation.jsx @@ -0,0 +1,13 @@ +import api from '../api'; + +export const fetchAnotherInformation = async (nickname, chatroomId) => { + try { + const response = await api.get(`/user/info/${nickname}/${chatroomId}`); + if (response.status === 200) { + return response.data; + } + } catch (error) { + console.error('Error fetching user info:', error); + throw error; + } +}; \ No newline at end of file diff --git a/src/api/chatRoom/fetchChatRoomFetch.jsx b/src/api/chatRoom/fetchChatRoomFetch.jsx new file mode 100644 index 0000000..f2acd5d --- /dev/null +++ b/src/api/chatRoom/fetchChatRoomFetch.jsx @@ -0,0 +1,17 @@ +import api from '../api'; + +export const fetchChatRoomFetch = async () => { + try { + const response = await api.get(`/api/chat-rooms`); + if (response.status === 200) { + return { + rooms: response.data.rooms, + possibleEnterNumber: response.data.possibleEnterNumber, + gender: response.data.gender + }; + } + } catch (error) { + console.error('Error fetching chat rooms:', error); + throw error; + } +}; \ No newline at end of file diff --git a/src/api/chatRoom/fetchChatroomInformation.jsx b/src/api/chatRoom/fetchChatroomInformation.jsx new file mode 100644 index 0000000..9c506f0 --- /dev/null +++ b/src/api/chatRoom/fetchChatroomInformation.jsx @@ -0,0 +1,24 @@ +import api from '../api'; +import { parseISO, format, differenceInDays } from 'date-fns'; + +export const fetchChatroomInformation = async (roomId) => { + try { + const response = await api.get(`/api/chatroom/${roomId}`); + const { room, possibleEnterNumber, remainingTime } = response.data; + + const endDate = parseISO(remainingTime); + const formattedDate = format(endDate, 'yyyy년 MM월 dd일'); + const today = new Date(); + const daysLeft = differenceInDays(endDate, today); + + return { + room, + possibleEnterNumber, + remainingTime: formattedDate, + dDay: daysLeft, + }; + } catch (error) { + console.error("Error fetching room data:", error); + throw error; + } +}; \ No newline at end of file diff --git a/src/api/chatRoom/fetchMessages.jsx b/src/api/chatRoom/fetchMessages.jsx new file mode 100644 index 0000000..c53b510 --- /dev/null +++ b/src/api/chatRoom/fetchMessages.jsx @@ -0,0 +1,11 @@ +import api from '../api'; + +export const fetchMessages = async (chatroomId) => { + try { + const response = await api.get(`api/chatroom/${chatroomId}/messages`); + return response.data; + } catch (error) { + console.error("Failed to load chat room data", error); + throw error; + } +}; \ No newline at end of file diff --git a/src/api/chatRoom/fetchRoomParticipants.jsx b/src/api/chatRoom/fetchRoomParticipants.jsx new file mode 100644 index 0000000..091ac08 --- /dev/null +++ b/src/api/chatRoom/fetchRoomParticipants.jsx @@ -0,0 +1,11 @@ +import api from '../api'; // api 인스턴스를 import하세요. 경로는 실제 구조에 맞게 조정해야 합니다. + +export const fetchRoomParticipants = async (roomId) => { + try { + const response = await api.get(`/api/chatroom/participants/${roomId}`); + return response.data.members; + } catch (error) { + console.error(`Error fetching room details for roomId: ${roomId}`, error); + return []; + } +}; \ No newline at end of file diff --git a/src/api/chatRoom/leaveChatRoom.jsx b/src/api/chatRoom/leaveChatRoom.jsx new file mode 100644 index 0000000..e985a7a --- /dev/null +++ b/src/api/chatRoom/leaveChatRoom.jsx @@ -0,0 +1,10 @@ +import api from '../api'; + +export const leaveChatRoom = async (roomId) => { + try { + await api.delete(`/api/chatroom/${roomId}`); + } catch (error) { + console.error("채팅방 나가기 API 요청 실패:", error); + throw error; + } +}; \ No newline at end of file diff --git a/src/api/chatRoom/makeChatRoom.jsx b/src/api/chatRoom/makeChatRoom.jsx new file mode 100644 index 0000000..248a700 --- /dev/null +++ b/src/api/chatRoom/makeChatRoom.jsx @@ -0,0 +1,13 @@ +import api from '../api'; + +export const makeChatRoom = async (roomData) => { + try { + const response = await api.post('api/chatroom', roomData); + if (response.status !== 200) { + throw new Error('채팅방 생성 실패'); + } + return response.data; + } catch (error) { + throw new Error('채팅방 생성 중 오류가 발생했습니다.'); + } +}; \ No newline at end of file diff --git a/src/api/chatRoom/roomStart.jsx b/src/api/chatRoom/roomStart.jsx new file mode 100644 index 0000000..476b7e8 --- /dev/null +++ b/src/api/chatRoom/roomStart.jsx @@ -0,0 +1,11 @@ +import api from '../api'; + +export const roomStart = async (roomId) => { + try { + const response = await api.post(`/api/chatroom/start/${roomId}`); + return response.data.success; // 서버에서 성공 여부를 반환한다고 가정 + } catch (error) { + console.error('Error starting the room:', error); + return false; + } +}; \ No newline at end of file diff --git a/src/api/chatRoom/socket/socketService.js b/src/api/chatRoom/socket/socketService.js new file mode 100644 index 0000000..4fd6a6c --- /dev/null +++ b/src/api/chatRoom/socket/socketService.js @@ -0,0 +1,68 @@ +import { Stomp } from '@stomp/stompjs'; +import SockJS from 'sockjs-client'; + +let stompClient = null; + + +//떠날때 +export const sendLeaveMessage = (socket, roomId) => { + const leaveMessage = { + type: 'LEAVE', + chatroomId: roomId, + }; + socket.send(`/pub/chat.leave/${roomId}`, {}, JSON.stringify(leaveMessage)); +}; + +export const unsubscribeFromRoom = (socket, roomId) => { + socket.unsubscribe(`/sub/${roomId}`); +}; + + + + + + +//입장 및 나감 +export const connectWebSocket = (chatroomId, onMessageReceived) => { + const socket = new SockJS('/ws'); + stompClient = Stomp.over(socket); + + stompClient.connect({}, (frame) => { + console.log('Connected: ' + frame); + stompClient.subscribe(`/sub/${chatroomId}`, (message) => { + const newMessage = JSON.parse(message.body); + onMessageReceived(newMessage); + }); + + const joinMessage = { + content: 'User has joined the chat', + type: 'JOIN', + }; + stompClient.send(`/pub/chat.join/${chatroomId}`, {}, JSON.stringify(joinMessage)); + localStorage.setItem(`chatroom_${chatroomId}_connected`, 'true'); + }, (error) => { + console.error('Connection error:', error); + localStorage.removeItem(`chatroom_${chatroomId}_connected`); + }); + + return stompClient; +}; + +export const disconnectWebSocket = () => { + if (stompClient) { + stompClient.disconnect(() => { + console.log('Disconnected'); + }); + } +}; + +//전송할때 +export const sendMessageHandler = (chatroomId, message) => { + if (stompClient && stompClient.connected) { + stompClient.send(`/pub/chat.send/${chatroomId}`, {}, JSON.stringify(message)); + } else { + console.error("WebSocket is not connected"); + } +}; + + diff --git a/src/api/deleteUser.jsx b/src/api/deleteUser.jsx new file mode 100644 index 0000000..2135baa --- /dev/null +++ b/src/api/deleteUser.jsx @@ -0,0 +1,14 @@ +import api from './api'; + +export const deleteUser = async () => { + try { + const response = await api.delete('/api/user/delete'); + if (response.status === 200) { + return { success: true }; + } else { + return { success: false, error: 'Failed to delete user account' }; + } + } catch (error) { + return { success: false, error: error.message }; + } +}; \ No newline at end of file diff --git a/src/api/getIsNickExist.jsx b/src/api/getIsNickExist.jsx new file mode 100644 index 0000000..b723f81 --- /dev/null +++ b/src/api/getIsNickExist.jsx @@ -0,0 +1,18 @@ +import axios from 'axios'; + +const BASE_URL = 'http://localhost:8080'; + +/** + * @param {string} nickname - 닉네임 + * @returns {Promise} 서버 응답 데이터 + */ +export const getIsNickExist = async (nickname) => { + try { + const response = await axios.get(`${BASE_URL}/api/users/isExist/${nickname}`); + console.log(response.data); + return response; // 전체 응답 객체를 반환 + } catch (error) { + console.error('Error fetching nickname data:', error); + throw error; // 에러를 호출자에게 전달 + } +}; diff --git a/src/api/getUser.jsx b/src/api/getUser.jsx new file mode 100644 index 0000000..a88ea5d --- /dev/null +++ b/src/api/getUser.jsx @@ -0,0 +1,27 @@ +import axios from 'axios'; +const BASE_URL ='http://localhost:8080'; + + +export const getUser = async (token) => { + console.log("전송 데이터",token); + try { + const response = await axios.get(`${BASE_URL}/api/user/info`,{ + headers: { + 'Authorization': `Bearer ${token}` + } + }); + console.log(response.data); + return response.data; + } catch (error) { + if (error.response) { + const { status, data } = error.response; + console.error('Error response:', status, data); + } else if (error.request) { + console.error('No response received:', error.request); + } else { + console.error('Error setting up request:', error.message); + } + throw error; + } + }; + \ No newline at end of file diff --git a/src/api/postLogin.jsx b/src/api/postLogin.jsx new file mode 100644 index 0000000..73ce5be --- /dev/null +++ b/src/api/postLogin.jsx @@ -0,0 +1,33 @@ +import axios from 'axios'; +import qs from 'qs'; + +const BASE_URL = 'http://localhost:8080'; + +/** + * 인증 이메일 전송 + * @param {Object} userData - 로그인 데이터 + */ + +export const postLogin = async (loginData) => { + console.log("전송 데이터", loginData); + try { + const response = await axios.post(`${BASE_URL}/api/auth/login`, qs.stringify(loginData), { + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + withCredentials: true, + }); + console.log(response); + return response; + } catch (error) { + if (error.response) { + const { status, data } = error.response; + console.error('Error response:', status, data); + } else if (error.request) { + console.error('No response received:', error.request); + } else { + console.error('Error setting up request:', error.message); + } + throw error; + } +}; diff --git a/src/api/postMail.jsx b/src/api/postMail.jsx new file mode 100644 index 0000000..a419ef3 --- /dev/null +++ b/src/api/postMail.jsx @@ -0,0 +1,27 @@ +import axios from 'axios'; +const BASE_URL ='http://localhost:8080'; + +/** + * 인증이메일 전송 + * @param {string} email - 일기 내용 + */ + +export const postMail = async (email) => { + console.log("전송 데이터",email); + try { + const response = await axios.post(`${BASE_URL}/api/auth/sign-up/email?email=${email}`, email ); + console.log(response.data); + return response.data; + } catch (error) { + if (error.response) { + const { status, data } = error.response; + console.error('Error response:', status, data); + } else if (error.request) { + console.error('No response received:', error.request); + } else { + console.error('Error setting up request:', error.message); + } + throw error; + } + }; + \ No newline at end of file diff --git a/src/api/postMailCheck.jsx b/src/api/postMailCheck.jsx new file mode 100644 index 0000000..06565f5 --- /dev/null +++ b/src/api/postMailCheck.jsx @@ -0,0 +1,33 @@ +import axios from 'axios'; +const BASE_URL ='http://localhost:8080'; + +/** + * 인증이메일 확인 + * @param {string} email + * @param {string} key + */ + +export const postMailCheck = async (email,key) => { + console.log("전송 데이터",email,key); + try { + const response = await axios.post(`${BASE_URL}/api/auth/sign-up/check`, + { + email:email, + key:key + } + ); + console.log(response.data); + return response.data; + } catch (error) { + if (error.response) { + const { status, data } = error.response; + console.error('Error response:', status, data); + } else if (error.request) { + console.error('No response received:', error.request); + } else { + console.error('Error setting up request:', error.message); + } + throw error; + } + }; + \ No newline at end of file diff --git a/src/api/postUser.jsx b/src/api/postUser.jsx new file mode 100644 index 0000000..c4d3d08 --- /dev/null +++ b/src/api/postUser.jsx @@ -0,0 +1,27 @@ +import axios from 'axios'; +const BASE_URL ='http://localhost:8080'; + +/** + * 인증이메일 전송 + * @param {Object} userData - 일기 내용 + */ + +export const postUser = async (userData) => { + console.log("전송 데이터",userData); + try { + const response = await axios.post(`${BASE_URL}/api/auth/sign-up`, userData ); + console.log(response.data); + return response.data; + } catch (error) { + if (error.response) { + const { status, data } = error.response; + console.error('Error response:', status, data); + } else if (error.request) { + console.error('No response received:', error.request); + } else { + console.error('Error setting up request:', error.message); + } + throw error; + } + }; + \ No newline at end of file diff --git a/src/api/putUser.jsx b/src/api/putUser.jsx new file mode 100644 index 0000000..c2369a0 --- /dev/null +++ b/src/api/putUser.jsx @@ -0,0 +1,26 @@ +import axios from 'axios'; + +const BASE_URL = 'http://localhost:8080'; + +export const putUser = async (token, userData) => { + console.log("전송 데이터", userData, token); + try { + const response = await axios.put(`${BASE_URL}/api/user/update`, userData, { + headers: { + 'Authorization': `Bearer ${token}`, + } + }); + console.log(response.data); + return response.data; + } catch (error) { + if (error.response) { + const { status, data } = error.response; + console.error('Error response:', status, data); + } else if (error.request) { + console.error('No response received:', error.request); + } else { + console.error('Error setting up request:', error.message); + } + throw error; + } +}; diff --git a/src/asset/ChatRoomPic1.png b/src/asset/ChatRoomPic1.png new file mode 100644 index 0000000..84b1404 Binary files /dev/null and b/src/asset/ChatRoomPic1.png differ diff --git a/src/asset/ChatRoomPic2.png b/src/asset/ChatRoomPic2.png new file mode 100644 index 0000000..5f325b0 Binary files /dev/null and b/src/asset/ChatRoomPic2.png differ diff --git a/src/asset/ChatRoomPic3.png b/src/asset/ChatRoomPic3.png new file mode 100644 index 0000000..6d3f4e8 Binary files /dev/null and b/src/asset/ChatRoomPic3.png differ diff --git a/src/asset/ChatRoomPic4.png b/src/asset/ChatRoomPic4.png new file mode 100644 index 0000000..1052ef9 Binary files /dev/null and b/src/asset/ChatRoomPic4.png differ diff --git a/src/asset/ChatRoomPic5.png b/src/asset/ChatRoomPic5.png new file mode 100644 index 0000000..45622aa Binary files /dev/null and b/src/asset/ChatRoomPic5.png differ diff --git a/src/asset/ChatRoomPic6.png b/src/asset/ChatRoomPic6.png new file mode 100644 index 0000000..26459e8 Binary files /dev/null and b/src/asset/ChatRoomPic6.png differ diff --git a/src/asset/ENFJ.png b/src/asset/ENFJ.png new file mode 100644 index 0000000..9550355 Binary files /dev/null and b/src/asset/ENFJ.png differ diff --git a/src/asset/ENFP.png b/src/asset/ENFP.png new file mode 100644 index 0000000..cc625a6 Binary files /dev/null and b/src/asset/ENFP.png differ diff --git a/src/asset/ENTJ.png b/src/asset/ENTJ.png new file mode 100644 index 0000000..5c67411 Binary files /dev/null and b/src/asset/ENTJ.png differ diff --git a/src/asset/ENTP.png b/src/asset/ENTP.png new file mode 100644 index 0000000..62be652 Binary files /dev/null and b/src/asset/ENTP.png differ diff --git a/src/asset/ESFJ.png b/src/asset/ESFJ.png new file mode 100644 index 0000000..f73a1ac Binary files /dev/null and b/src/asset/ESFJ.png differ diff --git a/src/asset/ESFP.png b/src/asset/ESFP.png new file mode 100644 index 0000000..e7e01fb Binary files /dev/null and b/src/asset/ESFP.png differ diff --git a/src/asset/ESTJ.png b/src/asset/ESTJ.png new file mode 100644 index 0000000..9517507 Binary files /dev/null and b/src/asset/ESTJ.png differ diff --git a/src/asset/ESTP.png b/src/asset/ESTP.png new file mode 100644 index 0000000..3d2d91f Binary files /dev/null and b/src/asset/ESTP.png differ diff --git a/src/asset/INFJ.png b/src/asset/INFJ.png new file mode 100644 index 0000000..ec5d4a3 Binary files /dev/null and b/src/asset/INFJ.png differ diff --git a/src/asset/INFP.png b/src/asset/INFP.png new file mode 100644 index 0000000..5d3bebc Binary files /dev/null and b/src/asset/INFP.png differ diff --git a/src/asset/INTJ.png b/src/asset/INTJ.png new file mode 100644 index 0000000..359838f Binary files /dev/null and b/src/asset/INTJ.png differ diff --git a/src/asset/INTP.png b/src/asset/INTP.png new file mode 100644 index 0000000..0bd0b13 Binary files /dev/null and b/src/asset/INTP.png differ diff --git a/src/asset/ISFJ.png b/src/asset/ISFJ.png new file mode 100644 index 0000000..3592aa0 Binary files /dev/null and b/src/asset/ISFJ.png differ diff --git a/src/asset/ISFP.png b/src/asset/ISFP.png new file mode 100644 index 0000000..0eea141 Binary files /dev/null and b/src/asset/ISFP.png differ diff --git a/src/asset/ISTJ.png b/src/asset/ISTJ.png new file mode 100644 index 0000000..6dde274 Binary files /dev/null and b/src/asset/ISTJ.png differ diff --git a/src/asset/ISTP.png b/src/asset/ISTP.png new file mode 100644 index 0000000..0e9b8e3 Binary files /dev/null and b/src/asset/ISTP.png differ diff --git a/src/asset/boy.png b/src/asset/boy.png new file mode 100644 index 0000000..46f6293 Binary files /dev/null and b/src/asset/boy.png differ diff --git a/src/asset/girl.png b/src/asset/girl.png new file mode 100644 index 0000000..2bfaebd Binary files /dev/null and b/src/asset/girl.png differ diff --git a/src/asset/loading.gif b/src/asset/loading.gif new file mode 100644 index 0000000..11225d7 Binary files /dev/null and b/src/asset/loading.gif differ diff --git a/src/asset/logo.png b/src/asset/logo.png new file mode 100644 index 0000000..9133004 Binary files /dev/null and b/src/asset/logo.png differ diff --git a/src/asset/main.png b/src/asset/main.png new file mode 100644 index 0000000..861fa4c Binary files /dev/null and b/src/asset/main.png differ diff --git a/src/asset/sublogo.png b/src/asset/sublogo.png new file mode 100644 index 0000000..d80554f Binary files /dev/null and b/src/asset/sublogo.png differ diff --git "a/src/asset/\355\232\203\353\266\210\354\235\264 \355\205\214\354\212\244\355\212\270.png" "b/src/asset/\355\232\203\353\266\210\354\235\264 \355\205\214\354\212\244\355\212\270.png" new file mode 100644 index 0000000..248e9a5 Binary files /dev/null and "b/src/asset/\355\232\203\353\266\210\354\235\264 \355\205\214\354\212\244\355\212\270.png" differ diff --git a/src/context/MbitContext.js b/src/context/MbitContext.js index 7a71088..33d67ae 100644 --- a/src/context/MbitContext.js +++ b/src/context/MbitContext.js @@ -4,31 +4,27 @@ export const MbtiContext = createContext(); export const MbtiProvider = ({ children }) => { const [Mbti, setMbti] = useState({ - e:0, - s:0, - f:0, - p:0, - selectList:{ - "1":false, - "2":false, - "3":false, - "4":false, - "5":false, - "6":false, - "7":false, - "8":false, - "9":false, - "10":false, - "11":false, - "12":false + selectList: { + "1": false, + "2": false, + "3": false, + "4": false, + "5": false, + "6": false, + "7": false, + "8": false, + "9": false, + "10": false, + "11": false, + "12": false } - - + + }); - const updateMbti = (newMbti)=>{ - setMbti((prevMbti)=>{ - return{ + const updateMbti = (newMbti) => { + setMbti((prevMbti) => { + return { ...prevMbti, ...newMbti }; @@ -37,23 +33,19 @@ export const MbtiProvider = ({ children }) => { const resetMbti = () => { setMbti({ - e:0, - s:0, - f:0, - p:0, - selectList:{ - "1":false, - "2":false, - "3":false, - "4":false, - "5":false, - "6":false, - "7":false, - "8":false, - "9":false, - "10":false, - "11":false, - "12":false + selectList: { + "1": false, + "2": false, + "3": false, + "4": false, + "5": false, + "6": false, + "7": false, + "8": false, + "9": false, + "10": false, + "11": false, + "12": false } }); }; diff --git a/src/context/UserContext.js b/src/context/UserContext.js new file mode 100644 index 0000000..11810e2 --- /dev/null +++ b/src/context/UserContext.js @@ -0,0 +1,46 @@ +import React, { createContext, useState } from 'react'; + +export const UserContext = createContext(); + +export const UserProvider = ({ children }) => { + const [User, setUser] = useState({ + schoolEmail:"", + password:"", + nickname:"", + gender:"", + studentId:"", + college:"", + department:"", + birthday:"" + + + }); + + const updateUser = (newUser)=>{ + setUser((prevUser)=>{ + return{ + ...prevUser, + ...newUser + }; + }); + }; + + const resetUser = () => { + setUser({ + schoolEmail:"", + password:"", + nickname:"", + gender:"", + studentId:"", + college:"", + department:"", + birthday:"" + + }); + }; + return ( + + {children} + + ); +} \ No newline at end of file diff --git a/src/index.js b/src/index.js index f037b25..18347fa 100644 --- a/src/index.js +++ b/src/index.js @@ -1,11 +1,12 @@ import React from 'react'; import ReactDOM from 'react-dom/client'; import App from './App'; + + const root = ReactDOM.createRoot(document.getElementById('root')); root.render( -); - +); \ No newline at end of file diff --git a/src/page/Chat/ChatPage.css b/src/page/Chat/ChatPage.css new file mode 100644 index 0000000..dc943a0 --- /dev/null +++ b/src/page/Chat/ChatPage.css @@ -0,0 +1,508 @@ +@import url("https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.8/dist/web/static/pretendard.css"); + +@keyframes spin { + from { + transform: rotate(0deg); + } + + to { + transform: rotate(360deg); + } +} + + + +.chatStartPage { + + height: 812px; + width: 375px; + margin: 0 auto; + + + .margin-container { + margin: 0 16px; + height: 100%; + } + + .logo { + width: 43px; + height: 20px; + margin-top: 50px; + } + + .title-name { + display: flex; + flex-direction: row; + } + + .one-click { + border-bottom: 2px solid #FF7898; + } + + .two-click { + border-bottom: none; + } + + + .enter { + /* 참여중인 채팅방 */ + margin-top: 10px; + width: 50%; + height: 20px; + text-align: center; + font-family: 'Pretendard'; + font-style: normal; + font-weight: 600; + font-size: 16px; + line-height: 18px; + /* identical to box height, or 112% */ + color: #000000; + } + + .no-enter { + /* 참여중인 채팅방 */ + text-align: center; + margin-top: 10px; + + width: 50%; + font-family: 'Pretendard'; + font-style: normal; + font-weight: 600; + font-size: 16px; + line-height: 18px; + /* identical to box height, or 112% */ + color: #000000; + } + + .title { + height: 50px; + display: flex; + flex-direction: column; + justify-content: space-between; + + } + + .title-span { + /* 최대 0개방까지 참여 가능합니다 */ + + + + font-family: 'Pretendard'; + font-style: normal; + font-weight: 500; + font-size: 10px; + line-height: 12px; + + + color: #696969; + } + + .chatRoomLists { + overflow-y: scroll; + scrollbar-width: none; + /* Firefox */ + -ms-overflow-style: none; + /* Internet Explorer 10+ */ + margin: 20px 0 0 0; + display: flex; + height: 575px; + justify-content: center; + } + + .chatRoomList { + /* Rectangle 3470027 */ + margin-bottom: 20px; + box-sizing: border-box; + width: 343px; + background: #FFFFFF; + } + + .chatRoomList-up { + border-width: 1px 1px 0px 1px; + border-style: solid; + border-color: #EAEAEA; + border-radius: 12px 12px 0px 0px; + height: 80px; + padding-bottom: 10px; + cursor: pointer; + } + + .room-title { + /* 채팅방 메인 타이틀 */ + + position: relative; + width: 104px; + height: 16px; + top: -40px; + left: 60px; + font-family: 'Pretendard'; + font-style: normal; + font-weight: 600; + font-size: 14px; + line-height: 16px; + /* identical to box height, or 114% */ + display: flex; + align-items: center; + + color: #000000; + } + + .bigCircle { + /* Ellipse 1447 */ + + position: relative; + width: 40px; + height: 40px; + left: 10px; + top: 5px; + border-radius: 50%; + background: #BDBDBD; + + } + + .bigCircle-pink { + background-color: #FF7898; + } + + .pink-text { + color: #FF7898; + } + + .bigCircle-name { + /* 참여 가능 */ + color: #FFFFFF; + position: relative; + width: 25px; + height: 28px; + left: 8px; + top: 7px; + text-align: center; + font-family: 'Pretendard'; + font-style: normal; + font-weight: 500; + font-size: 14px; + line-height: 14px; + /* or 100% */ + display: flex; + align-items: center; + } + + .gender-number { + + position: relative; + width: 50px; + height: 12px; + left: 180px; + top: -70px; + + font-family: 'Pretendard'; + font-style: normal; + font-weight: 400; + font-size: 10px; + line-height: 12px; + /* identical to box height, or 120% */ + display: flex; + align-items: center; + + color: #777777; + } + + + + .room-subTitle { + /* 채팅방 한 줄 소개, 서브 타이틀 */ + margin-top: 0; + position: relative; + width: 250px; + height: 14px; + left: 60px; + top: -60px; + + font-family: 'Pretendard'; + font-style: normal; + font-weight: 500; + font-size: 12px; + line-height: 14px; + word-wrap: break-word; + color: #5E5E5E; + } + + .members { + cursor: pointer; + /* Rectangle 3470028 */ + display: flex; + justify-content: center; + box-sizing: border-box; + flex-direction: column; + + + min-height: 34px; + + background: #F5F5F5; + + + border-width: 1px 1px 0px 1px; + border-style: solid; + border-color: #EAEAEA; + border-radius: 0px 0px 12px 12px; + } + + .members-see { + justify-content: center; + font-family: 'Pretendard'; + font-style: normal; + font-weight: 500; + font-size: 12px; + line-height: 14px; + /* identical to box height, or 117% */ + display: flex; + align-items: center; + + color: #000000; + } + + .expanded { + padding-top: 10px; + } + + .members-list { + max-height: 0; + /* 기본적으로 숨김 상태 */ + overflow: hidden; + } + + .members-list.expanded { + max-height: 500px; + /* 충분히 큰 값으로 설정, 필요에 따라 조정 가능 */ + padding: 10px 0; + /* 확장되면서 패딩 추가 */ + } + + .members-list.collapsed { + max-height: 0; + padding: 0; + } + + .members-container { + display: flex; + align-items: flex-start; + max-height: 120px; + overflow-y: scroll; + scrollbar-width: none; + /* Firefox */ + -ms-overflow-style: none; + /* Internet Explorer 10+ */ + } + + .members-section { + flex: 1; + padding: 0 10px; + word-wrap: break-word; + word-break: break-all; + overflow-wrap: break-word + } + + + .major { + + + font-family: 'Pretendard'; + font-style: normal; + font-weight: 400; + font-size: 12px; + line-height: 14px; + /* identical to box height, or 117% */ + display: flex; + align-items: center; + + color: #777777; + } + + .nickName { + + + font-family: 'Pretendard'; + font-style: normal; + font-weight: 500; + font-size: 12px; + line-height: 14px; + /* identical to box height, or 117% */ + display: flex; + align-items: center; + + color: #3D3D3D; + } + + + .members-section ul { + list-style: none; + padding: 0; + margin: 0; + } + + .members-section li { + margin-bottom: 5px; + } + + .gender-text { + margin-bottom: 5px; + font-family: 'Pretendard'; + font-style: normal; + font-weight: 500; + font-size: 10px; + line-height: 12px; + /* identical to box height, or 120% */ + display: flex; + align-items: center; + + color: #777777; + } + + .smallCircle { + width: 5px; + height: 5px; + position: relative; + top: 5px; + left: 320px; + } + + + + .gender-contatiner { + /* 남녀현황 */ + position: relative; + width: 40px; + height: 27px; + left: 290px; + top: -75px; + } + + .gender-number-information { + /* 남 */ + font-family: 'Pretendard'; + font-style: normal; + font-weight: 400; + font-size: 12px; + line-height: 14px; + /* identical to box height, or 117% */ + display: flex; + align-items: center; + + color: #3D3D3D; + } + + .make-chatRoom-button { + + position: relative; + width: 343px; + height: 50px; + left: calc(50% - 343px / 2); + background: #FF7898; + border-radius: 5px; + border: none; + cursor: pointer; + } + + .make-chatRoom-button.disabled { + background-color: #D9D9D9; + cursor: not-allowed; + } + + .make-chatRoom-button span { + justify-content: center; + /* 채팅방 만들기 */ + font-family: 'Pretendard'; + font-style: normal; + font-weight: 700; + font-size: 18px; + line-height: 20px; + /* identical to box height, or 111% */ + display: flex; + align-items: center; + text-align: center; + + color: #FFFFFF; + } + + .refresh-indicator { + position: relative; + justify-content: center; + width: 100%; + text-align: center; + margin-bottom: 20px; + } + + .refresh-indicator.first { + transform: translateY(0); + } + + + .refresh-icon { + animation: spin 1s linear infinite; + } + + + .user-list { + display: flex; + flex-direction: row; + } + + .user-list img { + width: 18px; + height: 18px; + border-radius: 50%; + margin-left: 3px; + margin-right: 5px; + } + + .user-container { + display: flex; + flex-direction: column; + } + + .startButton-container { + display: flex; + justify-content: center; + align-items: center; + margin-top: 20px; + } + + .startButton { + width: 282px; + height: 40px; + color: #FFFFFF; + } + + .startButton button { + width: 100%; + height: 100%; + border-radius: 5px; + border: 1px solid #bebebe; + background-color: #BDBDBD; + border-radius: 38px; + } + + .startButton button.active { + border: none; + background-color: #FF7898; + /* 활성화되었을 때의 배경색 */ + color: white; + + /* 텍스트 색상 */ + cursor: pointer; + /* 커서 스타일 */ + } + + .startButton span { + font-family: 'Pretendard'; + font-style: normal; + font-weight: 700; + font-size: 16px; + line-height: 18px; + + color: #FFFFFF; + /* identical to box height, or 111% */ + } + + +} \ No newline at end of file diff --git a/src/page/Chat/ChatRoom/ChatRoom.css b/src/page/Chat/ChatRoom/ChatRoom.css new file mode 100644 index 0000000..21ebb5b --- /dev/null +++ b/src/page/Chat/ChatRoom/ChatRoom.css @@ -0,0 +1,405 @@ +.chatRoom { + + height: 812px; + width: 375px; + margin: 0 auto; + + .margin-container { + display: flex; + flex-direction: column; + height: 100%; + margin: 0 16px; + } + + .title { + width: 100%; + height: 46px; + margin-top: 50px; + background: #ffffff; + display: flex; + text-align: center; + justify-content: space-between; + } + + .title-name { + /* 채팅방 이름 */ + + padding-top: 15px; + width: 100%; + height: 18px; + + cursor: pointer; + font-family: 'Pretendard'; + font-style: normal; + font-weight: 600; + font-size: 16px; + line-height: 18px; + /* identical to box height, or 112% */ + display: flex; + align-items: center; + + color: #3D3D3D; + } + + + .bar { + width: 343px; + height: 6px; + + + + background-color: #FAFAFA; + + } + + + .dialog-icon-container { + margin-top: 15px; + display: flex; + justify-content: center; + text-align: center; + cursor: pointer; + } + + /*원이 3개이상*/ + .profile-icon-0 { + width: 20px; + height: 20px; + background-size: cover; + background-position: center; + border-radius: 50%; + position: relative; + background-color: #A0A0A0; + left: 37px; + top: 0; + z-index: 0; + } + + .profile-icon-1 { + width: 20px; + height: 20px; + background-size: cover; + background-position: center; + border-radius: 50%; + position: relative; + background-color: #BBBBBB; + left: 27px; + top: 0; + z-index: 1; + } + + .profile-icon-2 { + width: 20px; + height: 20px; + background-size: cover; + background-position: center; + border-radius: 50%; + position: relative; + background-color: #F5F5F5; + left: 17px; + top: 0; + z-index: 2; + } + + /*원이 2개*/ + .profile-icon-0.member-length-2 { + width: 20px; + height: 20px; + background-size: cover; + background-position: center; + border-radius: 50%; + position: relative; + background-color: #A0A0A0; + left: 10px; + top: 0; + z-index: 0; + } + + .profile-icon-1.member-length-2 { + width: 20px; + height: 20px; + background-size: cover; + background-position: center; + border-radius: 50%; + position: relative; + background-color: #BBBBBB; + left: 0px; + top: 0; + z-index: 1; + } + + /*원이 1개*/ + .profile-icon-0.member-length-1 { + width: 20px; + height: 20px; + background-size: cover; + background-position: center; + border-radius: 50%; + position: relative; + background-color: #A0A0A0; + left: 0px; + top: 0; + z-index: 0; + } + + + .extra-profile-count { + + /* +2 */ + + position: relative; + width: 17px; + height: 16px; + left: 2px; + top: 2px; + + font-family: 'Pretendard'; + font-style: normal; + font-weight: 500; + font-size: 10px; + line-height: 16px; + /* identical to box height, or 114% */ + display: flex; + align-items: center; + text-align: center; + + color: #606060; + z-index: 3; + + } + + + + .text-bar { + position: fixed; + width: 343px; + height: 40px; + bottom: 20px; + + display: flex; + flex-direction: row; + justify-content: space-between; + } + + .text-container { + /* Rectangle 3470037 */ + width: 299px; + height: 40px; + background: #F5F5F5; + border-radius: 42px; + display: flex; + text-align: center; + align-items: center; + } + + .send-container { + width: 40px; + height: 40px; + justify-content: center; + display: flex; + align-items: center; + border-radius: 50%; + background: #F5F5F5; + } + + .svg-click { + cursor: pointer; + } + + .text { + /* Text... */ + border: none; + + /* 기본 배경 제거 */ + background: none; + + /* 기본 그림자 제거 */ + box-shadow: none; + + /* 기본 외곽선(focus 시 생기는 테두리) 제거 */ + outline: none; + + /* 기본 여백 제거 */ + padding: 0; + margin: 0; + /* 텍스트 선택 시 색상 초기화 (선택 사항) */ + user-select: text; + + /* 모양을 네이티브 스타일로 초기화 (iOS, Android 등) */ + appearance: none; + + margin-left: 20px; + margin-right: 20px; + width: 100%; + height: 20px; + font-family: 'Pretendard'; + font-style: normal; + font-weight: 400; + font-size: 14px; + line-height: 20px; + border-radius: 42px; + color: #000000; + z-index: 1000; + } + + + + .chat-container { + gap: 10px; + display: flex; + flex-direction: column; + margin-top: 10px; + max-height: 639px; + overflow-y: scroll; + scrollbar-width: none; + /* Firefox */ + -ms-overflow-style: none; + /* Internet Explorer 10+ */ + + } + + .message-group { + display: flex; + flex-direction: row; + align-items: flex-start; + margin-bottom: 5px; + } + + .profile-icon { + /* Ellipse 1457 */ + + width: 30px; + height: 30px; + border-radius: 50%; + /* 임시 */ + background: #eee; + } + + .message-box { + width: 100%; + display: flex; + flex-direction: column; + } + + .message-box.noSent { + padding-left: 5px; + } + + .message { + background-color: #f1f1f1; + padding: 10px 20px; + border-radius: 42px; + max-width: 60%; + + font-family: 'Pretendard'; + font-style: normal; + font-weight: 400; + font-size: 14px; + line-height: 20px; + /* identical to box height, or 143% */ + display: flex; + align-items: center; + + color: #3D3D3D; + + + /* Inside auto layout */ + flex: none; + order: 0; + flex-grow: 0; + + } + + .message-group.sent { + margin-top: 13px; + transform: scaleX(-1); + } + + + .message.sent { + background-color: #FF7898; + transform: scaleX(-1); + + font-family: 'Pretendard'; + font-style: normal; + font-weight: 400; + font-size: 14px; + line-height: 20px; + /* identical to box height, or 143% */ + display: flex; + align-items: center; + + color: #FFFFFF; + + + /* Inside auto layout */ + flex: none; + order: 0; + flex-grow: 0; + } + + .message-time.sent { + transform: scaleX(-1); + } + + .message-time { + font-size: 10px; + color: gray; + margin-top: auto; + position: relative; + left: 5px; + + } + + .message-time-box { + display: flex; + flex-direction: row; + } + + .chat-nickName { + /* 참여자닉네임1 */ + + + font-family: 'Pretendard'; + font-style: normal; + font-weight: 400; + font-size: 10px; + line-height: 12px; + /* identical to box height, or 120% */ + display: flex; + align-items: center; + padding-left: 7px; + color: #777777; + margin-bottom: 5px; + } + + .message-group.noSent { + .no-see { + width: 30px; + height: 30px; + } + } + + .message-box.no-see-box.noSent { + position: relative; + top: -5px + } + + + .message-group.same-user { + margin-top: -15px; + /* 간격을 줄이기 위해 음수 마진 사용 */ + } + + .no-see.same-user { + display: none; + /* 같은 사용자의 연속 메시지에서는 빈 공간 숨기기 */ + } + + .message-box.same-user { + margin-top: 5px; + /* 같은 사용자의 연속 메시지 사이의 간격 */ + } + + +} \ No newline at end of file diff --git a/src/page/Chat/ChatRoom/ChatRoom.jsx b/src/page/Chat/ChatRoom/ChatRoom.jsx new file mode 100644 index 0000000..0852e5f --- /dev/null +++ b/src/page/Chat/ChatRoom/ChatRoom.jsx @@ -0,0 +1,273 @@ +import React, { useState, useRef, useEffect, useCallback } from "react"; +import './ChatRoom.css' +import { useParams } from 'react-router-dom'; +import ChatRoomInformation from "./ChatRoomInformation/ChatRoomInformation"; +import { fetchMessages } from "../../../api/chatRoom/fetchMessages"; + + +import { connectWebSocket, disconnectWebSocket, sendMessageHandler } from "../../../api/chatRoom/socket/socketService"; + +function ChatRoom() { + + + + const [selectChatRoom, setSelectChatRoom] = useState({ + title: "프로그래밍 기초", + members: [ + { gender: "여자", major: "Physics", studentId: "20210003", nickname: "Alice", profileImage: "../../../../asset/ChatRoomPic3.png" }, + { gender: "남자", major: "Mathematicasdfasdfdassdfsssadfsdf", studentId: "20210004", nickname: "Bob", profileImage: "../../../../asset/ChatRoomPic4.png" }, + { gender: "남자", major: "Mathematicasdfasdfdassdfsssadfsdf", studentId: "20210004", nickname: "키키", profileImage: "../../../../asset/ChatRoomPic2.png" }, + { gender: "남자", major: "Mathematicasdfasdfdassdfsssadfsdf", studentId: "20210004", nickname: "한준서", profileImage: "../../../../asset/ChatRoomPic1.png" } + ], + //시작여부 + hasStarted: true, + }) + + // 현재 유저 + const [selectUser, setSelectUser] = useState({ + nickname: "Alice", + profileImage: "../../../../asset/ChatRoomPic3.png" + }) + + + const chatContainerRef = useRef(null); + const [inputValue, setInputValue] = useState(''); // 입력된 메시지 저장 + + const [messages, setMessages] = useState([ + { nickname: "Alice", text: "안녕하세요, 모두들! 오늘 회의 시작해볼까요? 지난 주에 논의했던 프로젝트 진행 상황에 대해 이야기 나누면 좋을 것 같아요.", time: "13:57", profileImage: "../../../../asset/ChatRoomPic3.png" }, + { nickname: "Alice", text: "각자 맡은 부분에 대해 간단히 공유해주시면 감사하겠습니다. 특히 어려움을 겪고 있는 부분이 있다면 함께 해결방안을 모색해보면 좋겠어요.", time: "13:57", profileImage: "../../../../asset/ChatRoomPic3.png" }, + { nickname: "Bob", text: "네, 좋습니다. 먼저 지난 주 진행 상황부터 공유해볼까요? 저는 프론트엔드 부분을 맡아 작업 중인데, 반응형 디자인 구현에 약간의 어려움을 겪고 있습니다.", time: "13:58", profileImage: "../../../../asset/ChatRoomPic4.png" }, + { nickname: "키키", text: "저는 데이터베이스 설계를 완료했습니다. ERD 공유드릴게요. 테이블 간의 관계를 최적화하는 데 시간이 좀 걸렸지만, 효율적인 구조를 만들었다고 생각합니다.", time: "13:59", profileImage: "../../../../asset/ChatRoomPic2.png" }, + { nickname: "한준서", text: "와, 키키님 빠르네요! 저는 UI 디자인 초안을 만들었어요. 사용자 경험을 최우선으로 고려했고, 색상 팔레트와 아이콘 세트도 선정했습니다. 피드백 주시면 감사하겠습니다.", time: "14:00", profileImage: "../../../../asset/ChatRoomPic1.png" }, + { nickname: "Alice", text: "모두 열심히 하고 계시네요. 저는 API 문서 작성 중입니다. RESTful 원칙을 따르면서도 우리 프로젝트에 최적화된 엔드포인트를 설계하고 있어요.", time: "14:01", profileImage: "../../../../asset/ChatRoomPic3.png" }, + { nickname: "Bob", text: "좋습니다. 그럼 이번 주 목표를 정해볼까요? 저는 반응형 디자인 문제를 해결하고, 주요 페이지의 레이아웃을 완성하는 것이 목표입니다.", time: "14:02", profileImage: "../../../../asset/ChatRoomPic4.png" }, + { nickname: "키키", text: "네, 저는 이번 주에 백엔드 코딩을 시작하려고 합니다. 주요 API 엔드포인트 구현과 데이터베이스 연동을 목표로 하고 있어요.", time: "14:03", profileImage: "../../../../asset/ChatRoomPic2.png" }, + { nickname: "한준서", text: "저는 UI 디자인 피드백 받고 수정할 예정이에요. 그리고 나서 프로토타입 제작을 시작해 사용자 테스트를 준비하려고 합니다.", time: "14:04", profileImage: "../../../../asset/ChatRoomPic1.png" }, + { nickname: "Alice", text: "API 개발도 이번 주부터 본격적으로 시작하겠습니다. 인증 시스템 구현과 주요 데이터 처리 로직 작성이 이번 주 목표예요.", time: "14:05", profileImage: "../../../../asset/ChatRoomPic3.png" }, + { nickname: "Bob", text: "다들 계획이 구체적이네요. 좋습니다! 서로 진행 상황을 공유하면서 협업하면 더 효율적으로 프로젝트를 진행할 수 있을 것 같아요.", time: "14:06", profileImage: "../../../../asset/ChatRoomPic4.png" }, + { nickname: "키키", text: "그러고 보니 다음 주 중간 발표 준비도 해야 할 것 같아요. 지금까지의 진행 상황을 정리하고 앞으로의 계획도 간략하게 준비해야겠네요.", time: "14:07", profileImage: "../../../../asset/ChatRoomPic2.png" }, + { nickname: "한준서", text: "맞아요. 발표 자료는 제가 만들어볼게요. 각자 맡은 부분에 대한 간단한 요약을 주시면 그걸 바탕으로 발표 자료를 구성하겠습니다.", time: "14:08", profileImage: "../../../../asset/ChatRoomPic1.png" }, + { nickname: "Alice", text: "좋은 생각이에요. 모두 수고 많으셨습니다. 다음 회의는 금요일에 하는 걸로 할까요? 그때까지 각자 목표한 바를 진행하고, 중간 발표 준비도 함께 점검해보면 좋겠어요.", time: "14:09", profileImage: "../../../../asset/ChatRoomPic3.png" }, + ]); + // 메시지 리스트 저장 + const stompClientRef = useRef(null); + const { chatroomId } = useParams(); + const [chatRoomInformation, setChatRoomInformation] = useState(false); + const reconnectIntervalRef = useRef(null); + + const handleMessageReceived = useCallback((newMessage) => { + setMessages((prevMessages) => [...prevMessages, newMessage]); + }, []); + + + + useEffect(() => { + const fetchDataMsg = async () => { + //메시지 받아오는 부분 + try { + const data = await fetchMessages(chatroomId); + setMessages(data.messages); + setSelectChatRoom(data.chatRoom); + setSelectUser(data.user); + } catch (error) { + console.error("Failed to load chat room data", error); + } + }; + + fetchDataMsg(); + + const storedConnectionStatus = localStorage.getItem(`chatroom_${chatroomId}_connected`); + + if (storedConnectionStatus !== 'true') { + stompClientRef.current = connectWebSocket(chatroomId, handleMessageReceived); + } + + // Set up periodic connection check + reconnectIntervalRef.current = setInterval(() => { + if (stompClientRef.current && !stompClientRef.current.connected) { + console.log('Connection lost. Attempting to reconnect...'); + stompClientRef.current = connectWebSocket(chatroomId, handleMessageReceived); + } + }, 30000); // Check every 30 seconds + + return () => { + disconnectWebSocket(); + clearInterval(reconnectIntervalRef.current); + }; + }, [chatroomId, handleMessageReceived]);; + + + const sendMessage = () => { + if (inputValue.trim()) { + const newMessage = { + nickname: selectUser.nickname, + text: inputValue, + time: new Date().toLocaleTimeString(), + profileImage: selectUser.profileImage + }; + + sendMessageHandler(chatroomId, newMessage); + setInputValue(''); + } + }; + + + + const maxVisibleProfiles = 3; + const memberLength = selectChatRoom.members.length; + const members = selectChatRoom.members.slice(0, maxVisibleProfiles); + const extraProfilesCount = memberLength - maxVisibleProfiles; + + const memberLengthClassName = `member-length-${memberLength}`; + + + + // 컴포넌트가 처음 렌더링되거나 messages가 업데이트될 때 스크롤을 아래로 이동 + useEffect(() => { + if (chatContainerRef.current) { + chatContainerRef.current.scrollTop = chatContainerRef.current.scrollHeight; + } + }, [messages]); // messages가 변경될 때마다 스크롤을 업데이트 + + + + + const goChatRoomInformation = () => { + setChatRoomInformation(true) + } + + + + // Enter 키를 누르면 메시지 전송 + const handleKeyPress = (event) => { + if (event.key === 'Enter') { + sendMessage(); + } + }; + + + const formatMessages = (messages) => { + return messages.map((message, index) => { + // 현재 유저를 받는 로직 추가 해야됨 + const isCurrentUser = message.nickname === "Alice"; + + // 프로필 이미지와 닉네임을 표시할지 여부 결정 + const showProfile = !isCurrentUser && + (index === 0 || messages[index - 1].nickname !== message.nickname || messages[index - 1].time !== message.time); + + // 시간 표시할지 여부 결정 + const isLastMessage = index === messages.length - 1; + const isNextnicknameDifferent = !isLastMessage && messages[index + 1].nickname !== message.nickname; + const isNextTimeDifferent = !isLastMessage && messages[index + 1].time !== message.time; + const showTime = isLastMessage || isNextnicknameDifferent || isNextTimeDifferent; + + const isPreviousMessageSameUser = index > 0 && messages[index - 1].nickname === message.nickname; + + return ( +
+ {showProfile ? + {`${message.nickname}'s :
+ } +
+ {showProfile && ({message.nickname})} +
+
+ {message.text} +
+ {showTime && ( +
{message.time}
+ )} +
+
+
+ ); + }); + }; + + + if (chatRoomInformation) { + return ( + setChatRoomInformation(value)} + /> + ) + } + else { + return ( +
+
+
+
goChatRoomInformation()}> + {selectChatRoom.title} + + + +
+
goChatRoomInformation()}> + {members.map((member, index) => ( +
0) ? '' : `url(${member.profileImage})` + }} + /> + ))} + {extraProfilesCount > 0 && ( +
+ +{extraProfilesCount} +
+ )} + +
+
+
+ + +
+ {formatMessages(messages)} +
+ + +
+
+ {selectChatRoom.hasStarted ? setInputValue(e.target.value)} // 입력된 값 업데이트 + onKeyDown={handleKeyPress} // Enter 키 처리 + /> : } + +
+ {selectChatRoom.hasStarted && inputValue.trim() ? ( +
+ + + +
+ ) : ( +
+ + + +
+ )} + + +
+
+
+ ) + } +} + +export default ChatRoom; \ No newline at end of file diff --git a/src/page/Chat/ChatRoom/ChatRoomInformation/ChatRoomInformation.css b/src/page/Chat/ChatRoom/ChatRoomInformation/ChatRoomInformation.css new file mode 100644 index 0000000..2da054d --- /dev/null +++ b/src/page/Chat/ChatRoom/ChatRoomInformation/ChatRoomInformation.css @@ -0,0 +1,276 @@ +.chatRoomInformation { + height: 812px; + width: 375px; + + margin: 0 auto; + + .margin-container { + display: flex; + flex-direction: column; + height: 100%; + margin: 0 16px; + } + + .title { + width: 100%; + height: 46px; + margin-top: 50px; + background: #ffffff; + display: flex; + text-align: center; + + } + + .title-name { + /* 채팅방 이름 */ + + padding-top: 15px; + width: 90px; + height: 18px; + + font-family: 'Pretendard'; + font-style: normal; + font-weight: 600; + font-size: 16px; + line-height: 18px; + /* identical to box height, or 112% */ + display: flex; + align-items: center; + + color: #3D3D3D; + } + + + .chat-room-header { + display: flex; + flex-direction: column; + align-items: center; + margin-bottom: 50px; + margin-top: 10px + } + + .chat-room-image { + border-radius: 50%; + margin-bottom: 10px; + + /* 채팅방 대표사진 */ + width: 60px; + height: 60px; + background-color: #3D3D3D; + + } + + .chat-room-name { + + + font-family: 'Pretendard'; + font-style: normal; + font-weight: 600; + font-size: 16px; + line-height: 18px; + + color: #3D3D3D; + } + + + + .chat-room-actions { + width: 300px; + margin: 0 auto; + display: flex; + justify-content: space-evenly; + margin-bottom: 20px; + } + + .action-item { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 5px; + width: 84px; + height: 84px; + background-color: #FAFAFA; + border-radius: 20%; + transition: all 0.3s ease; + } + + .action-item img { + width: 30px; + height: 30px; + } + + .action-item span { + + font-family: 'Pretendard'; + font-style: normal; + font-weight: 500; + font-size: 12px; + line-height: 14px; + + color: #000000; + } + + .chat-room-timer { + + margin: 0 auto; + width: 272px; + height: 40px; + + + background: #FAFAFA; + border-radius: 10px; + margin-bottom: 20px; + display: flex; + justify-content: center; + align-items: center; + } + + .chat-room-timer span { + + + + font-family: 'Pretendard'; + font-style: normal; + font-weight: 500; + font-size: 12px; + line-height: 14px; + /* identical to box height, or 117% */ + + + color: #000000; + } + + .participant-list { + margin-top: 20px; + display: flex; + flex-direction: column; + gap: 5px; + } + + .participant-list-text { + + font-family: 'Pretendard'; + font-style: normal; + font-weight: 600; + font-size: 14px; + line-height: 18px; + + color: #3D3D3D; + margin-bottom: 10px; + } + + .gender { + + font-family: 'Pretendard'; + font-style: normal; + font-weight: 500; + font-size: 12px; + line-height: 12px; + color: #777777; + margin-bottom: 10px; + } + + + .participant-group { + max-height: 150px; + overflow-y: scroll; + scrollbar-width: none; + /* Firefox */ + -ms-overflow-style: none; + } + + .participant-group ul { + padding-left: 0px; + } + + + + .bar { + width: 343px; + height: 6px; + background-color: #FAFAFA; + + } + + + .major { + + + font-family: 'Pretendard'; + font-style: normal; + font-weight: 400; + font-size: 12px; + line-height: 14px; + /* identical to box height, or 117% */ + display: flex; + align-items: center; + + color: #777777; + } + + .nickName { + + + font-family: 'Pretendard'; + font-style: normal; + font-weight: 500; + font-size: 12px; + line-height: 14px; + /* identical to box height, or 117% */ + display: flex; + align-items: center; + + color: #3D3D3D; + } + + .user-list { + display: flex; + flex-direction: row; + margin-bottom: 5px; + } + + .user-list img { + width: 18px; + height: 18px; + border-radius: 50%; + margin-left: 3px; + margin-right: 5px; + } + + .user-container { + display: flex; + flex-direction: column; + } + + + + + + + .action-item svg { + fill: none; + transition: all 0.3s ease; + } + + .action-item span { + color: #3D3D3D; + transition: all 0.3s ease; + } + + .action-item.active { + background-color: #FF7898; + /* 핑크 배경 */ + } + + .action-item.active svg { + fill: #fff; + /* 아이콘 하얀색 */ + } + + .action-item.active span { + color: #fff; + /* 글자 하얀색 */ + } + +} \ No newline at end of file diff --git a/src/page/Chat/ChatRoom/ChatRoomInformation/ChatRoomInformation.jsx b/src/page/Chat/ChatRoom/ChatRoomInformation/ChatRoomInformation.jsx new file mode 100644 index 0000000..cf25182 --- /dev/null +++ b/src/page/Chat/ChatRoom/ChatRoomInformation/ChatRoomInformation.jsx @@ -0,0 +1,205 @@ +import React, { useState, useEffect } from "react"; +import './ChatRoomInformation.css' +import ExitDialog from '../../Dialog/ExitDialog' +import UserIntroduceDialog from '../../Dialog/UserintroduceDialog' +import { fetchChatroomInformation } from '../../../../api/chatRoom/fetchChatroomInformation' + +function ChatRoomInformation({ roomId, socket, chatRoomInformation }) { + const [exitDialog, setExitDialog] = useState(false); + + const goChatRoom = () => { + chatRoomInformation(false); + }; + + const [activeItemPromise, setActiveItemPromise] = useState(null); + const [activeItemAlarm, setActiveItemAlarm] = useState(null); + const [activeItemExit, setActiveItemExit] = useState(null); + const [userIntroduceDialog, setUserIntroduceDialog] = useState(false); + const [clickUser, setClickUser] = useState(); + + const handleClick = (item) => { + if (item === "promise") { + if (activeItemPromise) { + setActiveItemPromise(false); + } else { + setActiveItemPromise(true); + } + } else if (item === "alarm") { + if (activeItemAlarm) { + setActiveItemAlarm(false); + } else { + setActiveItemAlarm(true); + } + } else if (item === "exit") { + if (activeItemExit) { + setActiveItemExit(false); + } else { + setActiveItemExit(true); + } + } + + }; + + + const [room, setRoom] = useState({ + title: "프로그래밍 기초", + members: [ + { gender: "여자", major: "Physics", studentId: "20210003", nickname: "Alice", profileImage: "../../../../asset/ChatRoomPic3.png" }, + { gender: "여자", major: "Mathematicasdfasdfdassdfsssadfsdf", studentId: "20210004", nickname: "Bob", profileImage: "../../../../asset/ChatRoomPic4.png" }, + { gender: "남자", major: "Mathematicasdfasdfdassdfsssadfsdf", studentId: "20210004", nickname: "Bob", profileImage: "../../../../asset/ChatRoomPic4.png" }, + { gender: "남자", major: "Mathematicasdfasdfdassdfsssadfsdf", studentId: "20210004", nickname: "Bob", profileImage: "../../../../asset/ChatRoomPic4.png" } + ], + profileImage: '../../../../asset/ChatRoomPic1.png', + }) + + const [possibleEnterNumber, setPossibleEnterNumber] = useState(3); + const [remainingTime, setRemainingTime] = useState(''); + const [dDay, setDDay] = useState(0); + + useEffect(() => { + const fetchRoomData = async () => { + try { + const data = await fetchChatroomInformation(roomId); + setRoom(data.room); + setPossibleEnterNumber(data.possibleEnterNumber); + setRemainingTime(data.remainingTime); + setDDay(data.dDay); + } catch (error) { + console.error("Error fetching room data:", error); + } + }; + + fetchRoomData(); + }, [roomId]); + + + + + const dialogOpen = (nickname) => { + setUserIntroduceDialog(true); + setClickUser(nickname); + } + + + + + return ( +
+ setUserIntroduceDialog(false)} roomId={roomId} /> + setExitDialog(false)} possibleEnterNumber={possibleEnterNumber} roomId={roomId} socket={socket} /> +
+
+
goChatRoom()}> + + + +
+
+ {/* 이미지와 채팅방 이름 */} +
+ Chat Room + {room.title} +
+ + {/* 버튼들 */} +
+ {/* 약속 잡기 */} +
handleClick('promise')} + > + + + + + + + + 약속 잡기 +
+ + {/* 알림 끄기 */} +
handleClick('alarm')} + > + + + + + + {activeItemAlarm ? <> + + 알림 키기 : 알림 끄기 + } + +
+ + {/* 나가기 */} +
{ handleClick('exit'); setExitDialog(true) }} + > + + + + + 나가기 +
+
+ + + {/* 채팅방 종료 시간 */} +
+ + {remainingTime}에 채팅방이 종료됩니다 D-{dDay} + +
+ +
+ + {/* 참여자 목록 */} +
+ 참여자 목록 +
+
남자
+
    + {room.members.filter(member => member.gender === "남자").map((member, index) => ( +
  • dialogOpen(member.nickname)}> + {`${member.nickname}'s +
    + {member.major}{member.studentId[0] + member.studentId[1]} {member.nickname} +
    +
  • + ))} +
+
+
+
여자
+
    + {room.members.filter(member => member.gender === "여자").map((member, index) => ( +
  • dialogOpen(member.nickname)}> + {`${member.nickname}'s +
    + {member.major}{member.studentId[0] + member.studentId[1]} {member.nickname} +
    +
  • + ))} +
+
+
+ +
+
+ ); +} + +export default ChatRoomInformation; \ No newline at end of file diff --git a/src/page/Chat/ChatStartPage.jsx b/src/page/Chat/ChatStartPage.jsx new file mode 100644 index 0000000..661dafd --- /dev/null +++ b/src/page/Chat/ChatStartPage.jsx @@ -0,0 +1,220 @@ +import React, { useState, useEffect } from "react"; +import './ChatPage.css' +import EnterCheckPage from './EnterCheckPage' +import NoEnterCheckPage from './NoEnterCheckPage' +import { useNavigate } from 'react-router-dom'; +import api from '../../api/api' +import logo from '../../asset/logo.png' +import { fetchChatRoomFetch } from '../../api/chatRoom/fetchChatRoomFetch' + +function ChatStartPage() { + + const navigate = useNavigate(); + const [loading, setLoading] = useState(true); + + const [isEnterCheck, setIsEnterClick] = useState(false); + //유저의 정보를 가져와 성별,입장참여 횟수를 가져온다 + const [possibleEnterNumber, setPossibleEnterNumber] = useState(3); + const [gender, setGender] = useState("남자"); + + + const handleNavigation = (destination) => { + switch (destination) { + case 'play': + navigate('/mbti'); + break; + case 'talk': + window.location.reload(); // 페이지 새로고침 (필요에 따라 이 부분을 변경) + break; + case 'my': + navigate('/myPage'); + break; + default: + break; + } + }; + + + // 채팅방 정보를 저장하는 state(예시 백에서 받아와야함) + //나중에 역순으로 할거? + const [chatRooms, setChatRooms] = useState([ + { + id: 1, + //제목 길이 20자 제한 + title: "채팅방 메인 타이틀아아아아f", + //서브제목 길이 30자 제한 + subTitle: "dddddddddddddddddddddddddddddddddddddddddddddddddd", + + //방아이디로 조회한다. + + //나중에 서버로 부터 바는 member이기 때문에 삭제! + members: [ + { gender: "남자", major: "Computer Science", studentId: "20210001", nickname: "John", profileImage: "../../../../asset/ChatRoomPic1.png" }, + { gender: "여자", major: "Design", studentId: "20210002", nickname: "Jane", profileImage: "../../../../asset/ChatRoomPic2.png" } + ], + maxMembers: 2, + //입장체크 + enterCheck: false, + host: "asdfasdf1234346", + maleCount: 1, + femaleCount: 1, + profileImage: '../../../../asset/ChatRoomPic1.png', + hasStarted: false + }, + { + id: 2, + title: "프로그래밍 기초", + subTitle: "프로그래밍의 기초를 배워봅시다", + members: [ + { gender: "여자", major: "Physics", studentId: "20210003", nickname: "Alice", profileImage: "../../../../asset/ChatRoomPic3.png" }, + { gender: "여자", major: "Mathematicasdfasdfdassdfsssadfsdf", studentId: "20210004", nickname: "Bob", profileImage: "../../../../asset/ChatRoomPic4.png" }, + { gender: "남자", major: "Mathematicasdfasdfdassdfsssadfsdf", studentId: "20210004", nickname: "Bob", profileImage: "../../../../asset/ChatRoomPic4.png" }, + { gender: "남자", major: "Mathematicasdfasdfdassdfsssadfsdf", studentId: "20210004", nickname: "Bob", profileImage: "../../../../asset/ChatRoomPic4.png" } + ], + maxMembers: 4, + enterCheck: true, + host: "asdfasdf1234346", + maleCount: 2, + femaleCount: 2, + profileImage: '../../../../asset/ChatRoomPic1.png', + hasStarted: false + }, + { + id: 3, + title: "프로그래밍 기초", + subTitle: "프로그래밍의 기초를 배워봅시다", + members: [ + { gender: "여자", major: "Physics", studentId: "20210003", nickname: "Alice", profileImage: "../../../../asset/ChatRoomPic4.png" }, + { gender: "남자", major: "Mathematics", studentId: "20210004", nickname: "Bob", profileImage: "../../../../asset/ChatRoomPic5.png" }, + { gender: "남자", major: "asd", studentId: "20210004", nickname: "Bob", profileImage: "../../../../asset/ChatRoomPic6.png" }, + + ], + maxMembers: 6, + enterCheck: false, + host: "asdfasdf1234346", + maleCount: 2, + femaleCount: 1, + profileImage: '../../../../asset/ChatRoomPic1.png', + hasStarted: false + }, + { + id: 4, + title: "프로그래밍 기초", + subTitle: "프로그래밍의 기초를 배워봅시다", + //사진경로 절대경로로 수정 + members: [ + { gender: "여자", major: "Physics", studentId: "20210003", nickname: "Alice", profileImage: "../../../../asset/ChatRoomPic3.png" }, + { gender: "남자", major: "Mathematics", studentId: "20210004", nickname: "Bob", profileImage: "../../../../asset/ChatRoomPic2.png" }, + { gender: "여자", major: "Mathematics", studentId: "20210004", nickname: "Bob", profileImage: "../../../../asset/ChatRoomPic6.png" }, + { gender: "남자", major: "Mathematics", studentId: "20210004", nickname: "Bob", profileImage: "../../../../asset/ChatRoomPic1.png" }, + { gender: "여자", major: "asd", studentId: "20210004", nickname: "한준서", profileImage: "../../../../asset/ChatRoomPic5.png" } + ], + maxMembers: 6, + enterCheck: false, + host: "asdfasdf1234346", + maleCount: 2, + femaleCount: 3, + profileImage: '../../../../asset/ChatRoomPic1.png', + hasStarted: false + } + ]); + + + useEffect(() => { + const fetchChatRooms = async () => { + try { + const data = await fetchChatRoomFetch(); + setChatRooms(data.rooms); + setPossibleEnterNumber(data.possibleEnterNumber); + setGender(data.gender); + } catch (error) { + console.error('Error fetching chat rooms:', error); + } finally { + setLoading(false); + } + }; + + fetchChatRooms(); + }, []); + + + + + + + + + const makeChatRoom = () => { + //챗룸 만들기로 이동 + navigate('/makeChatRoom') + } + + + + if (loading) return ( +
+ + + +
+ ); + + return ( +
+
+
+ +
+
+ setIsEnterClick(true)} >참여중 + setIsEnterClick(false)}>모집중 +
+ {isEnterCheck ? 최대 {possibleEnterNumber}개 방까지 참여 가능합니다 : } + +
+ +
+ {isEnterCheck ? ( + setIsEnterClick(value)} + /> + ) : ( + setIsEnterClick(value)} + /> + )} +
+ + + +
+ +
+ ); +} + +export default ChatStartPage; \ No newline at end of file diff --git a/src/page/Chat/Dialog/ExitDialog.jsx b/src/page/Chat/Dialog/ExitDialog.jsx new file mode 100644 index 0000000..503f1ce --- /dev/null +++ b/src/page/Chat/Dialog/ExitDialog.jsx @@ -0,0 +1,57 @@ +import React from "react"; +import { useNavigate } from 'react-router-dom'; +import './dialog.css' +import { leaveChatRoom } from '../../../api/chatRoom/leaveChatRoom'; +import { sendLeaveMessage, unsubscribeFromRoom } from '../../../api/chatRoom/socket/socketService'; + +function ExitDialog({ isOpen, onClose, possibleEnterNumber, roomId, socket }) { + const navigate = useNavigate(); + if (!isOpen) return null; + + const exitRoom = async () => { + try { + await leaveChatRoom(roomId); + sendLeaveMessage(socket, roomId); + unsubscribeFromRoom(socket, roomId); + + navigate('/chatStartPage'); + onClose(); + } catch (error) { + console.error("채팅방 나가기 실패:", error); + } + } + + + return ( +
+
+
+
+ + + + + + +
+

정말로 채팅방을 나가시겠어요?

+

+ 현재 나의 채팅방 입장 참여 횟수 {possibleEnterNumber}개 +

+
+ + + +
+ +
+
+
+ ); +} + +export default ExitDialog; \ No newline at end of file diff --git a/src/page/Chat/Dialog/LeaveDialog.jsx b/src/page/Chat/Dialog/LeaveDialog.jsx new file mode 100644 index 0000000..0fba53d --- /dev/null +++ b/src/page/Chat/Dialog/LeaveDialog.jsx @@ -0,0 +1,60 @@ +import React from "react"; +import { useNavigate } from 'react-router-dom'; +import './dialog.css' +import { deleteUser } from '../../../api/deleteUser'; + +function LeaveDialog({ isOpen, onClose }) { + const navigate = useNavigate(); + if (!isOpen) return null; + + const exitRoom = async () => { + try { + const result = await deleteUser(); + if (result.success) { + console.log('User account deleted successfully'); + // You can add a success message or redirect the user here + } else { + console.error('Failed to delete user account'); + // You can add an error message or handle the error here + } + } catch (error) { + console.error('Error deleting user account:', error); + // You can add an error message or handle the error here + } + //chatStartPage로 이동 + navigate('/'); + onClose(); + } + + return ( +
+
+
+
+ + + + + + +
+

정말로 탈퇴하시겠어요?

+

+

+
+ + + +
+ +
+
+
+ ); +} + +export default LeaveDialog; \ No newline at end of file diff --git a/src/page/Chat/Dialog/MaxChanceDialog.jsx b/src/page/Chat/Dialog/MaxChanceDialog.jsx new file mode 100644 index 0000000..76f9b54 --- /dev/null +++ b/src/page/Chat/Dialog/MaxChanceDialog.jsx @@ -0,0 +1,33 @@ +import React, { useState } from "react"; +import './dialog.css' + +function MaxChanceDialog({ isOpen, onClose, possibleEnterNumber }) { + if (!isOpen) return null; + + return ( +
+
+
+
+ + + + + + +
+

최대 참여 가능한 채팅방 수가 초과되었습니다

+

+ {/* 로직구현 */} + 현재 나의 채팅방 입장 잔여 횟수 {possibleEnterNumber}개 +

+ +
+
+
+ ); +} + +export default MaxChanceDialog; \ No newline at end of file diff --git a/src/page/Chat/Dialog/MaxMemberDialog.jsx b/src/page/Chat/Dialog/MaxMemberDialog.jsx new file mode 100644 index 0000000..92c0323 --- /dev/null +++ b/src/page/Chat/Dialog/MaxMemberDialog.jsx @@ -0,0 +1,31 @@ +import React, { useState } from "react"; + +function MaxMemberDialog({ isOpen, onClose }) { + if (!isOpen) return null; + + return ( +
+
+
+
+ + + + + + +
+

채팅방 인원이 마감되었습니다

+

+ +

+ +
+
+
+ ); +} + +export default MaxMemberDialog; \ No newline at end of file diff --git a/src/page/Chat/Dialog/OkayDialog.jsx b/src/page/Chat/Dialog/OkayDialog.jsx new file mode 100644 index 0000000..40e4d6b --- /dev/null +++ b/src/page/Chat/Dialog/OkayDialog.jsx @@ -0,0 +1,77 @@ +import React, { useState } from "react"; +import { useNavigate } from 'react-router-dom'; +import './dialog.css' + +function OkayDialog({ isOpen, onClose, selectChatRoom, possibleEnterNumber }) { + const navigate = useNavigate(); + + if (!isOpen) return null; + + + const maxVisibleProfiles = 3; + const memberLength = selectChatRoom.members.length; + const members = selectChatRoom.members.slice(0, maxVisibleProfiles); + const extraProfilesCount = memberLength - maxVisibleProfiles; + + const memberLengthClassName = `member-length-${memberLength}`; + + + + const goChatRoom = () => { + navigate(`/chat/2`); // 원하는 경로로 이동 + // navigate(`/chat/${selectChatRoom.id}`); // 원하는 경로로 이동 + //소켓으로 업그레이드 하고 방으로 이동 + onClose(); + } + + return ( +
+
+
+

채팅방 메인 타이틀에 입장하시겠습니까?

+
+ {members.map((member, index) => ( +
0) ? '' : `url(${member.profileImage})` + }} + /> + ))} + {extraProfilesCount > 0 && ( +
+ +{extraProfilesCount} +
+ )} + +
+
+

+ 원활한 서비스 이용을 위해 + 동시에 최대 3개의 채팅방을 이용할 수 있습니다 +

+

+ 현재 나의 채팅방 입장 참여 횟수 {possibleEnterNumber}개 +

+
+ + + +
+ + + +
+ +
+
+
+ ); +} + +export default OkayDialog; \ No newline at end of file diff --git a/src/page/Chat/Dialog/UserintroduceDialog.jsx b/src/page/Chat/Dialog/UserintroduceDialog.jsx new file mode 100644 index 0000000..4d12959 --- /dev/null +++ b/src/page/Chat/Dialog/UserintroduceDialog.jsx @@ -0,0 +1,78 @@ +import React, { useState, useEffect } from "react"; +import { fetchAnotherInformation } from '../../../api/chatRoom/fetchAnotherInformation'; + +function UserIntroduceDialog({ isOpen, onClose, nickname, roomId }) { + //유저의 정보를 받아오는 useEffect 사용 + const [profile, setProfile] = useState({ + profileImage: "", + nickname: "아아", + department: "컴공", + studentId: "202001485", + shortIntroduce: "안녕하세요", + mbti: "INFJ", + height: "180", + drinking: "소주3병마심", + smoking: "1갑" + }); + + + useEffect(() => { + const fetchIntroduceItems = async () => { + try { + const userData = await fetchAnotherInformation(nickname, roomId); + setProfile(userData); + } catch (error) { + console.error('Error fetching user info:', error); + } + }; + fetchIntroduceItems(); + }, [nickname, roomId]); + + + + + + + if (!isOpen) return null; + + + + return ( +
+
+
+
+ Profile +
+
{profile.nickname}
+

{profile.department} {profile.studentId.slice(1, 3)}학번

+
+
{profile.shortIntroduce}
+
+
+
+ + {/* 유저를통한 로직추가 */} +
· MBTI
{profile.mbti}
+
· 키
{profile.height}
+
· 음주
{profile.drinking}
+
· 흡연
{profile.smoking}
+
+
+
+ +
+
+
+
+ ); +} + +export default UserIntroduceDialog; \ No newline at end of file diff --git a/src/page/Chat/Dialog/dialog.css b/src/page/Chat/Dialog/dialog.css new file mode 100644 index 0000000..3e3e3fd --- /dev/null +++ b/src/page/Chat/Dialog/dialog.css @@ -0,0 +1,483 @@ +.dialog { + + position: absolute; + height: 100%; + width: 100%; + top: 0px; + background: rgba(0, 0, 0, 0.1); + z-index: 1000; + left: 0px; + + .dialog-backdrop { + + + display: flex; + justify-content: center; + align-items: center; + + + z-index: 1000; + /* 앞으로 오게 하기 위해 z-index 값 추가 */ + + /* Group 816134 */ + + position: absolute; + width: 343px; + height: 330px; + left: calc(50% - 343px/2); + top: 199px; + } + + .dialog-container { + background-color: white; + border-radius: 20px; + width: 343px; + height: 330px; + text-align: center; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); + display: flex; + flex-direction: column; + + + } + + .dialog-icon { + margin-top: 32px; + margin-bottom: 32px; + } + + .dialog-title { + /* 최대 참여 가능 채팅방 수가 초과되었습니다 */ + font-family: 'Pretendard'; + font-style: normal; + font-weight: 600; + font-size: 16px; + line-height: 18px; + margin-bottom: 0; + /* identical to box height, or 112% */ + + + color: #3D3D3D; + } + + .dialog-message { + margin-top: 3px; + font-family: 'Pretendard'; + font-style: normal; + font-weight: 500; + font-size: 12px; + line-height: 14px; + /* identical to box height, or 117% */ + + + color: #3D3D3D; + } + + .highlight-text { + color: #FF7898; + font-weight: bold; + } + + + + .dialog-button { + background-color: #F5F5F5; + font-family: 'Pretendard'; + font-style: normal; + font-weight: 600; + font-size: 18px; + line-height: 20px; + color: #3D3D3D; + border: none; + border-radius: 14px; + padding: 10px 20px; + font-size: 16px; + cursor: pointer; + margin: 10px 31px 0 31px; + } + + .dialog-button:hover { + background-color: #FF7898; + } + + + .dialog-okay-title { + /* 에 입장하시겠습니까? */ + font-family: 'Pretendard'; + font-style: normal; + font-weight: 500; + font-size: 16px; + line-height: 18px; + /* identical to box height, or 112% */ + margin-top: 30px; + align-items: center; + color: #3D3D3D; + } + + .button-container { + display: flex; + flex-direction: row; + justify-content: space-evenly; + } + + .dialog-button-cancle { + /* Rectangle 3470049 */ + width: 136px; + height: 44px; + border: none; + background: #F5F5F5; + border-radius: 14px; + font-family: 'Pretendard'; + font-style: normal; + font-weight: 600; + font-size: 18px; + line-height: 20px; + color: #3D3D3D; + } + + .dialog-button-pink { + /* Rectangle 3470049 */ + font-family: 'Pretendard'; + font-style: normal; + font-weight: 600; + font-size: 18px; + line-height: 20px; + color: #FFFFFF; + width: 136px; + height: 44px; + border: none; + background: #FF7898; + border-radius: 14px; + } + + + .dialog-icon-container { + margin-top: 15px; + display: flex; + justify-content: center; + text-align: center; + } + + /*원이 3개이상*/ + .profile-icon-0 { + width: 96px; + height: 96px; + background-size: cover; + background-position: center; + border-radius: 50%; + position: relative; + background-color: #A0A0A0; + left: 60px; + top: 0; + z-index: 0; + } + + .profile-icon-1 { + width: 96px; + height: 96px; + background-size: cover; + background-position: center; + border-radius: 50%; + position: relative; + background-color: #BBBBBB; + left: 0; + top: 0; + z-index: 1; + } + + .profile-icon-2 { + width: 96px; + height: 96px; + background-size: cover; + background-position: center; + border-radius: 50%; + position: relative; + background-color: #F5F5F5; + left: -60px; + top: 0; + z-index: 2; + } + + /*원이 2개*/ + .profile-icon-0.member-length-2 { + width: 96px; + height: 96px; + background-size: cover; + background-position: center; + border-radius: 50%; + position: relative; + background-color: #A0A0A0; + left: 30px; + top: 0; + z-index: 0; + } + + .profile-icon-1.member-length-2 { + width: 96px; + height: 96px; + background-size: cover; + background-position: center; + border-radius: 50%; + position: relative; + background-color: #BBBBBB; + left: -30px; + top: 0; + z-index: 1; + } + + /*원이 1개*/ + .profile-icon-0.member-length-1 { + width: 96px; + height: 96px; + background-size: cover; + background-position: center; + border-radius: 50%; + position: relative; + background-color: #A0A0A0; + left: 0px; + top: 0; + z-index: 0; + } + + + .extra-profile-count { + + /* +2 */ + + position: absolute; + width: 17px; + height: 16px; + left: 200px; + top: 118px; + + font-family: 'Pretendard'; + font-style: normal; + font-weight: 500; + font-size: 14px; + line-height: 16px; + /* identical to box height, or 114% */ + display: flex; + align-items: center; + text-align: center; + + color: #606060; + z-index: 3; + + } + + .okayDialog-container { + display: flex; + flex-direction: column; + margin-left: 47px; + } + + .dialog-message-2 { + /* 원활한 서비스 이용을 위해 동시에 최대 3개의 채팅방을 이용할 수 있습니다 */ + margin-top: 15px; + font-family: 'Pretendard'; + font-style: normal; + font-weight: 500; + font-size: 12px; + line-height: 18px; + /* or 150% */ + margin-bottom: 0px; + color: #777777; + + display: flex; + justify-content: start; + flex-direction: column; + text-align: left; + } + + .dialog-message.okay { + margin-top: 0px; + margin-bottom: 16px; + text-align: left; + } +} + + +.userIntroduceDialog { + position: absolute; + height: 100%; + width: 100%; + top: 0px; + background: rgba(0, 0, 0, 0.1); + z-index: 1000; + left: 0px; + display: flex; + justify-content: center; + + .dialog-backdrop { + display: flex; + justify-content: center; + align-items: center; + z-index: 1000; + position: absolute; + width: 375px; + height: 558px; + top: 254px; + } + + .dialog-container { + background-color: white; + border-radius: 20px; + width: 100%; + height: 100%; + text-align: center; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); + display: flex; + flex-direction: column; + + + } + + + .dialog-title { + /* 최대 참여 가능 채팅방 수가 초과되었습니다 */ + font-family: 'Pretendard'; + font-style: normal; + font-weight: 600; + font-size: 16px; + line-height: 18px; + margin-bottom: 0; + /* identical to box height, or 112% */ + margin-top: 10px; + + color: #3D3D3D; + } + + .dialog-message { + margin-top: 3px; + font-family: 'Pretendard'; + font-style: normal; + font-weight: 500; + font-size: 14px; + color: #777777; + } + + + .button-container { + margin-top: 30px; + display: flex; + flex-direction: row; + justify-content: space-evenly; + } + + + + + .dialog-button-cancle { + /* Rectangle 3470049 */ + width: 136px; + height: 44px; + border: none; + background-color: #F5F5F5; + border-radius: 14px; + font-family: 'Pretendard'; + font-style: normal; + font-weight: 600; + font-size: 18px; + line-height: 20px; + cursor: pointer; + color: #3D3D3D; + } + + + .dialog-button-cancle:hover { + background-color: #FF7898; + color: #F5F5F5; + } + + .profile-image-container { + margin-top: 50px; + } + + .profile-image-container img { + width: 128px; + height: 128px; + border-radius: 50%; + } + + .introduction-container { + margin: 10px auto; + margin-bottom: 30px; + width: 343px; + height: 46px; + background: #F5F5F5; + border-radius: 10px; + align-items: center; + display: flex; + } + + .introduction { + /* 한 줄 소개 안녕하세요~~~~ */ + width: 100%; + height: 16px; + padding: 0 20px; + text-align: left; + font-family: 'Pretendard'; + font-style: normal; + font-weight: 400; + font-size: 14px; + line-height: 16px; + + + color: #3D3D3D; + } + + .additional-info-container { + margin: 0 20px; + padding: 0 10px; + background: #F5F5F5; + border-radius: 10px; + + } + + .additional-info { + width: 100%; + height: 100%; + text-align: left; + display: flex; + flex-direction: column; + padding: 10px; + gap: 7px; + } + + .additional-info div { + display: flex; + flex-direction: row; + } + + .additional-info .first { + + width: 45px; + height: 16px; + + font-family: 'Pretendard'; + font-style: normal; + font-weight: 400; + font-size: 14px; + line-height: 16px; + + + color: #3D3D3D; + } + + .additional-info .second { + + width: 269px; + height: 16px; + margin-left: 10px; + + font-family: 'Pretendard'; + font-style: normal; + font-weight: 500; + font-size: 14px; + line-height: 16px; + + + color: #3D3D3D; + } + +} \ No newline at end of file diff --git a/src/page/Chat/EnterCheckPage.jsx b/src/page/Chat/EnterCheckPage.jsx new file mode 100644 index 0000000..901e748 --- /dev/null +++ b/src/page/Chat/EnterCheckPage.jsx @@ -0,0 +1,254 @@ +import React, { useState, useEffect } from "react"; +import { useNavigate } from 'react-router-dom'; +import './ChatPage.css' +import { roomStart } from '../../api/chatRoom/roomStart' +import { fetchRoomParticipants } from '../../api/chatRoom/fetchRoomParticipants'; + +function EnterCheckPage(props) { + const { chatRooms, isEnterCheck } = props; + const [expandedRoomIds, setExpandedRoomIds] = useState([]); + const [updatedChatRooms, setUpdatedChatRooms] = useState( + chatRooms.filter(room => room.enterCheck === true) + ); + + const navigate = useNavigate(); + + //새로고침 + const [startY, setStartY] = useState(0); + const [translateY, setTranslateY] = useState(0); + const [isRefreshing, setIsRefreshing] = useState(false); + + const threshold = 80; // 새로고침을 트리거하는 기준 거리 + + // 새로고침 함수 + const handleRefresh = () => { + setIsRefreshing(true); + setTimeout(() => { + setIsRefreshing(false); + isEnterCheck(true); // enterCheck를 true로 설정 + }, 1500); + }; + + // 터치 시작 + const handleTouchStart = (event) => { + setStartY(event.touches[0].clientY); + }; + + // 터치 중일 때 + const handleTouchMove = (event) => { + const currentY = event.touches[0].clientY; + const distance = currentY - startY; + + + // 스크롤이 최상단일 때만 동작 + if (window.scrollY === 0 && distance > 0) { + setTranslateY(distance * 0.3); // 끌린 만큼 translateY 값을 설정 + } + + // 기준 거리를 넘었을 때 새로고침 + if (distance > threshold) { + handleRefresh(); + } + }; + + // 터치 끝 + const handleTouchEnd = () => { + setTranslateY(0); // 터치가 끝나면 원래 위치로 돌아가기 + }; + + + + //토글확장 관리 + const toggleExpand = (roomId) => { + setExpandedRoomIds(prevState => + prevState.includes(roomId) + ? prevState.filter(id => id !== roomId) + : [...prevState, roomId] + ); + console.log(expandedRoomIds) + }; + + + + const goChatRoom = (roomId) => { + navigate('/chat/2'); + //navigate(`/chat/${roomId}`); // 원하는 경로로 이동 + } + + // 수정된 handleButtonClick 함수 + const handleButtonClick = async (event, room) => { + if (room.members.length < room.maxMembers) { + event.stopPropagation(); // 이벤트 버블링을 막음 + } else { + event.stopPropagation(); + const started = await roomStart(room.id); + if (started) { + goChatRoom(room.id); + } else { + // 시작 실패 시 에러 처리 (예: 알림 표시) + console.error('Failed to start the room'); + } + } + }; + + + + + useEffect(() => { + if (expandedRoomIds.length > 0) { + const updateMembers = async () => { + const updatedRooms = await Promise.all( + updatedChatRooms.map(async (room) => { + if (room.id === expandedRoomIds[expandedRoomIds.length - 1]) { + const updatedMembers = await fetchRoomParticipants(room.id); + return { + + ...room, + members: updatedMembers, + maleCount: updatedMembers.filter(member => member.gender === "남자").length, + femaleCount: updatedMembers.filter(member => member.gender === "여자").length + }; + } + return room; + }) + ); + + setUpdatedChatRooms(updatedRooms); + }; + + updateMembers(); + } + }, [expandedRoomIds, isEnterCheck, chatRooms]); + + // // expandedRoomIds가 변경될 때마다 서버에서 members를 가져와 업데이트하는 useEffect + // useEffect(() => { + // if (expandedRoomIds.length > 0) { + // const fetchRoomDetails = async (roomId) => { + // try { + // const response = await api.get(`/api/chatroom/participants/${roomId}`); + // return response.data.members; // 서버로부터 members를 받아옴 + // } catch (error) { + // console.error(`Error fetching room details for roomId: ${roomId}`, error); + // return []; + // } + // }; + + // const updateMembers = async () => { + // const updatedRooms = await Promise.all( + // chatRooms.map(async (room) => { + // if (room.id === expandedRoomIds[expandedRoomIds.length - 1]) { + // const updatedMembers = await fetchRoomDetails(room.id); + // return { ...room, members: updatedMembers }; + // } + // console.log(updatedChatRooms); + // return room; + // }) + // ); + + // setUpdatedChatRooms(updatedRooms); + // }; + + // updateMembers(); + // } + // }, [expandedRoomIds,isEnterCheck]); // expandedRoomIds가 변경될 때 reload될 때 마다 + + + return ( +
+ {isRefreshing &&
} + {updatedChatRooms + .map(room => { + const maleCount = room.maleCount; + const femaleCount = room.femaleCount; + + const isExpanded = expandedRoomIds.includes(room.id); // 현재 방이 확장되었는지 확인 + return ( +
+
+
+ + + +
+ + + {/* /사진수정/ */} + profileImage +

{room.title}

+ 남: {maleCount} 여: {femaleCount} +

{room.subTitle}

+
+
toggleExpand(room.id)}> + + 참여인원 보기 + {!isExpanded ? + + : + + + } + + + {/* 확장된 상태일 때만 멤버 리스트를 렌더링 */} +
+
+
+

남자

+
    + {room.members.filter(member => member.gender === "남자").map((member, index) => ( +
  • + +
    + {member.major}{member.studentId[0] + member.studentId[1]} {member.nickname} +
    +
  • + ))} +
+
+
+

여자

+
    + {room.members.filter(member => member.gender === "여자").map((member, index) => ( +
  • + +
    + {member.major}{member.studentId[0] + member.studentId[1]} {member.nickname} +
    +
  • + ))} +
+
+
+
+
+ +
+
+
+
+
+ ); + })} +
+ ) +} + + +export default EnterCheckPage; \ No newline at end of file diff --git a/src/page/Chat/MakeChatRoom/MakeChatRoom.css b/src/page/Chat/MakeChatRoom/MakeChatRoom.css new file mode 100644 index 0000000..bf09277 --- /dev/null +++ b/src/page/Chat/MakeChatRoom/MakeChatRoom.css @@ -0,0 +1,255 @@ +.makeChatRoom { + + height: 812px; + width: 375px; + margin: 0 auto; + + .margin-container { + display: flex; + flex-direction: column; + height: 100%; + margin: 0 10px; + } + + .title { + width: 100%; + height: 46px; + margin-top: 50px; + background: #ffffff; + display: flex; + text-align: center; + margin-left: 6px; + justify-content: space-between; + } + + .title-name { + /* 채팅방 이름 */ + + padding-top: 15px; + width: 120px; + height: 18px; + + cursor: pointer; + font-family: 'Pretendard'; + font-style: normal; + font-weight: 600; + font-size: 16px; + line-height: 18px; + /* identical to box height, or 112% */ + display: flex; + align-items: center; + + color: #3D3D3D; + + + } + + .small-title { + /* 채팅방 정보 */ + margin-top: 20px; + width: 73px; + height: 18px; + + font-family: 'Pretendard'; + font-style: normal; + font-weight: 600; + font-size: 16px; + line-height: 18px; + + color: #3D3D3D; + + } + + .top { + width: 100%; + display: flex; + flex-direction: row; + gap: 20px; + height: 80px; + margin-bottom: 20px; + justify-content: space-between; + + } + + + .chat-room-info { + display: flex; + flex-direction: column; + align-items: center; + margin-top: 5px; + } + + + + .top img { + border: 1px solid #ccc; + border-radius: 50%; + width: 80px; + height: 100%; + /* background-color: none; */ + } + + + .edit-avatar { + border: 1px solid #ccc; + border-radius: 50%; + position: relative; + width: 20px; + height: 20px; + left: -110px; + top: 80px; + display: flex; + justify-content: center; + align-items: center; + background: #F5F5F5; + } + + .chat-room-name { + + + width: 230px; + background: #F5F5F5; + border-radius: 10px; + margin: auto 0px; + height: 50px; + border: 1px solid #ccc; + padding: 0 10px; + font-family: 'Pretendard'; + font-style: normal; + font-weight: 400; + font-size: 14px; + line-height: 16px; + color: #777777; + } + + .chat-room-description { + + width: 335px; + height: 100px; + + background: #F5F5F5; + border-radius: 10px; + + border: 1px solid #ccc; + border-radius: 10px; + font-size: 14px; + + + + font-family: 'Pretendard'; + font-style: normal; + font-weight: 400; + font-size: 14px; + line-height: 16px; + padding: 10px; + color: #777777; + } + + .members-settings { + margin-top: 50px; + margin-bottom: 20px; + } + + .small-title2 { + width: 73px; + height: 18px; + + font-family: 'Pretendard'; + font-style: normal; + font-weight: 600; + font-size: 16px; + line-height: 18px; + margin-bottom: 30px; + color: #3D3D3D; + } + + .gender-settings { + margin-left: 10px; + display: flex; + flex-direction: column; + gap: 15px; + } + + + .gender-settings div { + display: flex; + flex-direction: row; + align-items: center; + gap: 10px; + } + + .gender-settings span { + + width: 25px; + height: 16px; + + font-family: 'Pretendard'; + font-style: normal; + font-weight: 500; + font-size: 14px; + line-height: 16px; + + + color: #3D3D3D; + } + + + + .count-button { + + padding: 5px 15px; + border-radius: 20px; + border: 1px solid #ccc; + background-color: #fff; + cursor: pointer; + } + + .count-button.active { + background-color: #FF7898; + border-color: #ccc; + color: #F5F5F5; + } + + + + .note { + margin-top: 50px; + font-size: 12px; + color: #888; + margin-bottom: 20px; + } + + .create-room-button { + /* Rectangle 13 */ + margin: 5px auto; + font-family: 'Pretendard'; + font-style: normal; + font-weight: 700; + font-size: 18px; + line-height: 20px; + color: #FFFFFF; + background: #FF7898; + width: 343px; + height: 50px; + background: #FF7898; + border-radius: 5px; + border: none; + } + + .create-room-button:hover { + background-color: #FF7898; + } + + .error { + margin-left: 7px; + font-family: 'Pretendard'; + font-style: normal; + font-weight: 400; + font-size: 12px; + line-height: 14px; + display: flex; + align-items: center; + color: #FF3131; + } + +} \ No newline at end of file diff --git a/src/page/Chat/MakeChatRoom/MakeChatRoom.jsx b/src/page/Chat/MakeChatRoom/MakeChatRoom.jsx new file mode 100644 index 0000000..609dd7a --- /dev/null +++ b/src/page/Chat/MakeChatRoom/MakeChatRoom.jsx @@ -0,0 +1,224 @@ +import React, { useState, useRef } from "react"; +import './MakeChatRoom.css' +import { useNavigate } from 'react-router-dom'; +import Pic1 from '../../../asset/ChatRoomPic1.png' +import Pic2 from '../../../asset/ChatRoomPic2.png' +import Pic3 from '../../../asset/ChatRoomPic3.png' +import Pic4 from '../../../asset/ChatRoomPic4.png' +import Pic5 from '../../../asset/ChatRoomPic5.png' +import Pic6 from '../../../asset/ChatRoomPic6.png' +import { makeChatRoom } from '../../../api/chatRoom/makeChatRoom'; + + + +function MakeChatRoom() { + + const profileImages = [ + Pic1, Pic2, Pic3, Pic4, Pic5, Pic6 + ] + const getRandomImage = () => { + return profileImages[Math.floor(Math.random() * profileImages.length)]; + }; + + const navigate = useNavigate(); + + const [count, setCount] = useState(3); + const [roomTitle, setRoomTitle] = useState(''); + const [subTitle, setSubTitle] = useState('');//30자 이내로 제한 + const [profileImage, setProfileImage] = useState(getRandomImage()); + const [error, setError] = useState(''); + + const handleNavigation = (destination) => { + switch (destination) { + case 'play': + window.location.reload(); + break; + case 'talk': + navigate('/ChatStartPage'); // 페이지 새로고침 (필요에 따라 이 부분을 변경) + break; + case 'my': + navigate('/myPage'); + break; + default: + break; + } + }; + + const fileInputRef = useRef(null); + + const handleImageClick = () => { + fileInputRef.current.click(); + }; + + const handleImageChange = (event) => { + const file = event.target.files[0]; + if (file) { + const reader = new FileReader(); + reader.onloadend = () => { + setProfileImage(reader.result); + }; + reader.readAsDataURL(file); + } + }; + + + const handleTitleChange = (e) => { + if (e.target.value.length <= 20) { + setRoomTitle(e.target.value); + } + }; + + const handleSubTitleChange = (e) => { + if (e.target.value.length <= 50) { + setSubTitle(e.target.value); + } + }; + + const makeRoom = async () => { + if (roomTitle === '' || subTitle === '') { + setError('모든 필드를 입력해 주세요.'); + return; + } + + try { + const numberOfMembers = count * 2; + const roomData = { + title: roomTitle, + subTitle: subTitle, + profileImage: profileImage, + maxMembers: numberOfMembers, + maleCount: count, + femaleCount: count + }; + + await makeChatRoom(roomData); + console.log("Room Created"); + navigate('/ChatStartPage'); + setError(''); + } catch (error) { + setError(error.message); + } + }; + + + + + return ( + <> +
+
+
+
navigate('/ChatStartPage')} > + + + + + 채팅방 만들기 +
+ +
+ +
채팅방 정보
+ +
+
+ + + +
+
+ + +
+