diff --git a/app/annotation-service/src/main/kotlin/com/group6/annotationservice/AnnotationServiceApplication.kt b/app/annotation-service/src/main/kotlin/com/group6/annotationservice/AnnotationServiceApplication.kt index 7369e1a4..07b73789 100644 --- a/app/annotation-service/src/main/kotlin/com/group6/annotationservice/AnnotationServiceApplication.kt +++ b/app/annotation-service/src/main/kotlin/com/group6/annotationservice/AnnotationServiceApplication.kt @@ -13,7 +13,7 @@ class AnnotationServiceApplication { return object : WebMvcConfigurer { override fun addCorsMappings(registry: CorsRegistry) { registry.addMapping("/**") - .allowedOrigins("http://localhost:3000", "http://game-lounge.com") + .allowedOrigins("http://localhost:3000", "http://game-lounge.com", "http://167.99.242.175") .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") .allowCredentials(true) .allowedHeaders("*") diff --git a/app/annotation-service/src/main/kotlin/com/group6/annotationservice/controller/AnnotationController.kt b/app/annotation-service/src/main/kotlin/com/group6/annotationservice/controller/AnnotationController.kt index 42f9c2b2..9c8de8b3 100644 --- a/app/annotation-service/src/main/kotlin/com/group6/annotationservice/controller/AnnotationController.kt +++ b/app/annotation-service/src/main/kotlin/com/group6/annotationservice/controller/AnnotationController.kt @@ -15,13 +15,13 @@ class AnnotationController( private val annotationService: AnnotationService ) { @PostMapping("parse-selector") - @CrossOrigin(origins = ["http://localhost:3000", "http://game-lounge.com"]) + @CrossOrigin(origins = ["http://localhost:3000", "http://game-lounge.com", "http://167.99.242.175"]) fun parseSelector(@RequestBody selector: SelectorDto): ResponseEntity { return ResponseEntity.ok("test") } @PostMapping("/create") - @CrossOrigin(origins = ["http://localhost:3000", "http://game-lounge.com"]) + @CrossOrigin(origins = ["http://localhost:3000", "http://game-lounge.com", "http://167.99.242.175"]) fun createAnnotation(@RequestBody annotationDto: AnnotationDto): ResponseEntity { val responseAnnotation = DtoConverter @@ -30,7 +30,7 @@ class AnnotationController( } @GetMapping("/{id}") - @CrossOrigin(origins = ["http://localhost:3000", "http://game-lounge.com"]) + @CrossOrigin(origins = ["http://localhost:3000", "http://game-lounge.com", "http://167.99.242.175"]) fun getAnnotation(@PathVariable id: String): ResponseEntity { val annotation = annotationService.getAnnotation(id) ?: return ResponseEntity(HttpStatus.NOT_FOUND) @@ -40,7 +40,7 @@ class AnnotationController( } @PostMapping("/get-annotations-by-target") - @CrossOrigin(origins = ["http://localhost:3000", "http://game-lounge.com"]) + @CrossOrigin(origins = ["http://localhost:3000", "http://game-lounge.com", "http://167.99.242.175"]) fun getAnnotationsByTarget(@RequestBody request: GetAnnotationsByTargetIdRequest): ResponseEntity> { val annotations = annotationService.getAnnotationsByTarget(request.targetId) val responseAnnotationList = annotations.map { DtoConverter.convertAnnotationToAnnotationDto(it) } @@ -48,7 +48,7 @@ class AnnotationController( } @DeleteMapping("/{id}") - @CrossOrigin(origins = ["http://localhost:3000", "http://game-lounge.com"]) + @CrossOrigin(origins = ["http://localhost:3000", "http://game-lounge.com", "http://167.99.242.175"]) fun deleteAnnotation(@PathVariable id: String): ResponseEntity { return if (annotationService.deleteAnnotation(id)) { ResponseEntity(HttpStatus.NO_CONTENT) diff --git a/app/backend/src/main/kotlin/com/gamelounge/backend/model/request/CreateEditingRequest.kt b/app/backend/src/main/kotlin/com/gamelounge/backend/model/request/CreateEditingRequest.kt index e9fc1eb5..351601d9 100644 --- a/app/backend/src/main/kotlin/com/gamelounge/backend/model/request/CreateEditingRequest.kt +++ b/app/backend/src/main/kotlin/com/gamelounge/backend/model/request/CreateEditingRequest.kt @@ -11,10 +11,10 @@ class CreateEditingRequest( val description: String, val genres: List, val platforms: List, - val playerNumber: NumberOfPlayers, + val playerNumber: String, val releaseYear: Int, - val universe: UniverseInfo, - val mechanics: GameMechanics, + val universe: String, + val mechanics: String, val playtime: String, var totalRating: Int, var countRating: Int, diff --git a/app/backend/src/main/kotlin/com/gamelounge/backend/service/GameService.kt b/app/backend/src/main/kotlin/com/gamelounge/backend/service/GameService.kt index 2c599182..6e494c1a 100644 --- a/app/backend/src/main/kotlin/com/gamelounge/backend/service/GameService.kt +++ b/app/backend/src/main/kotlin/com/gamelounge/backend/service/GameService.kt @@ -1,7 +1,6 @@ package com.gamelounge.backend.service -import com.gamelounge.backend.constant.GameGenre -import com.gamelounge.backend.constant.GamePlatform +import com.gamelounge.backend.constant.* import com.gamelounge.backend.entity.* import com.gamelounge.backend.exception.* import com.gamelounge.backend.middleware.SessionAuth @@ -86,17 +85,19 @@ class GameService( // Convert platform strings to GamePlatform enum values val platforms = editedGame.platforms.map { GamePlatform.valueOf(it) }.toMutableSet() - + val playernumber = NumberOfPlayers.valueOf(editedGame.playerNumber) + var universe = UniverseInfo.valueOf(editedGame.universe) + var gamemechanics = GameMechanics.valueOf(editedGame.mechanics) val requestEditingGame = RequestedEditingGame( gameId = gameId, title = editedGame.title, description = editedGame.description, genres = genres, platforms = platforms, - playerNumber = editedGame.playerNumber, + playerNumber = playernumber, releaseYear = editedGame.releaseYear, - universe = editedGame.universe, - mechanics = editedGame.mechanics, + universe = universe, + mechanics = gamemechanics, playtime = editedGame.playtime, ) //editedGameRepository.save(requestEditingGame) // save to get gameId for image name diff --git a/app/frontend/package-lock.json b/app/frontend/package-lock.json index 93d6d4d1..e9961da0 100644 --- a/app/frontend/package-lock.json +++ b/app/frontend/package-lock.json @@ -8,10 +8,10 @@ "name": "game-lounge-frontend", "version": "0.1.0", "dependencies": { - "@emotion/react": "^11.11.1", + "@emotion/react": "^11.11.3", "@emotion/styled": "^11.11.0", "@mui/icons-material": "^5.14.15", - "@mui/material": "^5.14.15", + "@mui/material": "^5.15.1", "@nextui-org/react": "^2.2.9", "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", @@ -30,6 +30,7 @@ "react-scripts": "5.0.1", "react-transition-group": "^4.4.5", "styled-components": "^6.1.0", + "uuid": "^9.0.1", "web-vitals": "^2.1.4" }, "devDependencies": { @@ -2051,9 +2052,9 @@ "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==" }, "node_modules/@babel/runtime": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.2.tgz", - "integrity": "sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.6.tgz", + "integrity": "sha512-zHd0eUrf5GZoOWVCXp6koAKQTfZV07eit6bGPmJgnZdnSAvvZee6zniW2XMF7Cmc4ISOOnPy3QaSiIJGJkVEDQ==", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -2439,14 +2440,14 @@ "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==" }, "node_modules/@emotion/react": { - "version": "11.11.1", - "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.1.tgz", - "integrity": "sha512-5mlW1DquU5HaxjLkfkGN1GA/fvVGdyHURRiX/0FHl2cfIfRxSOfmxEH5YS43edp0OldZrZ+dkBKbngxcNCdZvA==", + "version": "11.11.3", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.3.tgz", + "integrity": "sha512-Cnn0kuq4DoONOMcnoVsTOR8E+AdnKFf//6kUWc4LCdnxj31pZWn7rIULd6Y7/Js1PiPHzn7SKCM9vB/jBni8eA==", "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.11.0", "@emotion/cache": "^11.11.0", - "@emotion/serialize": "^1.1.2", + "@emotion/serialize": "^1.1.3", "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1", "@emotion/utils": "^1.2.1", "@emotion/weak-memoize": "^0.3.1", @@ -2462,9 +2463,9 @@ } }, "node_modules/@emotion/serialize": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.2.tgz", - "integrity": "sha512-zR6a/fkFP4EAcCMQtLOhIgpprZOwNmCldtpaISpvz348+DP4Mz8ZoKaGGCQpbzepNIUWbq4w6hNZkwDyKoS+HA==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.3.tgz", + "integrity": "sha512-iD4D6QVZFDhcbH0RAG1uVu1CwVLMWUkCvAqqlewO/rxf8+87yIBAlt4+AxMiiKPLs5hFc0owNk/sLLAOROw3cA==", "dependencies": { "@emotion/hash": "^0.9.1", "@emotion/memoize": "^0.8.1", @@ -2576,9 +2577,9 @@ } }, "node_modules/@floating-ui/core": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.5.0.tgz", - "integrity": "sha512-kK1h4m36DQ0UHGj5Ah4db7R0rHemTqqO0QLvUqi1/mUUp3LuAWbWxdxSIf/XsnH9VS6rRVPLJCncjRzUvyCLXg==", + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.5.2.tgz", + "integrity": "sha512-Ii3MrfY/GAIN3OhXNzpCKaLxHQfJF9qvwq/kEJYdqDxeIHa01K8sldugal6TmeeXl+WMvhv9cnVzUTaFFJF09A==", "dependencies": { "@floating-ui/utils": "^0.1.3" } @@ -2593,9 +2594,9 @@ } }, "node_modules/@floating-ui/react-dom": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.2.tgz", - "integrity": "sha512-5qhlDvjaLmAst/rKb3VdlCinwTF4EYMiVxuuc/HVUjs46W0zgtbMmAZ1UTsDrRTxRmUEzl92mOtWbeeXL26lSQ==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.4.tgz", + "integrity": "sha512-CF8k2rgKeh/49UrnIBs4BdxPUV6vize/Db1d/YbCLyp9GiVZ0BEwf5AiDSxJRCr6yOkGqTFHtmrULxkEfYZ7dQ==", "dependencies": { "@floating-ui/dom": "^1.5.1" }, @@ -3580,14 +3581,14 @@ "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==" }, "node_modules/@mui/base": { - "version": "5.0.0-beta.21", - "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.21.tgz", - "integrity": "sha512-eTKWx3WV/nwmRUK4z4K1MzlMyWCsi3WJ3RtV4DiXZeRh4qd4JCyp1Zzzi8Wv9xM4dEBmqQntFoei716PzwmFfA==", - "dependencies": { - "@babel/runtime": "^7.23.2", - "@floating-ui/react-dom": "^2.0.2", - "@mui/types": "^7.2.7", - "@mui/utils": "^5.14.15", + "version": "5.0.0-beta.28", + "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.28.tgz", + "integrity": "sha512-KIoSc5sUFceeCaZTq5MQBapFzhHqMo4kj+4azWaCAjorduhcRQtN+BCgVHmo+gvEjix74bUfxwTqGifnu2fNTg==", + "dependencies": { + "@babel/runtime": "^7.23.5", + "@floating-ui/react-dom": "^2.0.4", + "@mui/types": "^7.2.11", + "@mui/utils": "^5.15.1", "@popperjs/core": "^2.11.8", "clsx": "^2.0.0", "prop-types": "^15.8.1" @@ -3597,7 +3598,7 @@ }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/mui" + "url": "https://opencollective.com/mui-org" }, "peerDependencies": { "@types/react": "^17.0.0 || ^18.0.0", @@ -3611,12 +3612,12 @@ } }, "node_modules/@mui/core-downloads-tracker": { - "version": "5.14.15", - "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.14.15.tgz", - "integrity": "sha512-ZCDzBWtCKjAYAlKKM3PA/jG/3uVIDT9ZitOtVixIVmTCQyc5jSV1qhJX8+qIGz4RQZ9KLzPWO2tXd0O5hvzouQ==", + "version": "5.15.1", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.15.1.tgz", + "integrity": "sha512-y/nUEsWHyBzaKYp9zLtqJKrLod/zMNEWpMj488FuQY9QTmqBiyUhI2uh7PVaLqLewXRtdmG6JV0b6T5exyuYRw==", "funding": { "type": "opencollective", - "url": "https://opencollective.com/mui" + "url": "https://opencollective.com/mui-org" } }, "node_modules/@mui/icons-material": { @@ -3645,17 +3646,17 @@ } }, "node_modules/@mui/material": { - "version": "5.14.15", - "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.14.15.tgz", - "integrity": "sha512-Gq65rHjvLzkxmhG8bvag851Oqsmru7qkUb/cCI2xu7dQzmY345f9xJRJi72sRGjhaqHXWeRKw/yIwp/7oQoeXg==", - "dependencies": { - "@babel/runtime": "^7.23.2", - "@mui/base": "5.0.0-beta.21", - "@mui/core-downloads-tracker": "^5.14.15", - "@mui/system": "^5.14.15", - "@mui/types": "^7.2.7", - "@mui/utils": "^5.14.15", - "@types/react-transition-group": "^4.4.7", + "version": "5.15.1", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.15.1.tgz", + "integrity": "sha512-WA5DVyvacxDakVyAhNqu/rRT28ppuuUFFw1bLpmRzrCJ4uw/zLTATcd4WB3YbB+7MdZNEGG/SJNWTDLEIyn3xQ==", + "dependencies": { + "@babel/runtime": "^7.23.5", + "@mui/base": "5.0.0-beta.28", + "@mui/core-downloads-tracker": "^5.15.1", + "@mui/system": "^5.15.1", + "@mui/types": "^7.2.11", + "@mui/utils": "^5.15.1", + "@types/react-transition-group": "^4.4.10", "clsx": "^2.0.0", "csstype": "^3.1.2", "prop-types": "^15.8.1", @@ -3667,7 +3668,7 @@ }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/mui" + "url": "https://opencollective.com/mui-org" }, "peerDependencies": { "@emotion/react": "^11.5.0", @@ -3689,12 +3690,12 @@ } }, "node_modules/@mui/private-theming": { - "version": "5.14.15", - "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.14.15.tgz", - "integrity": "sha512-V2Xh+Tu6A07NoSpup0P9m29GwvNMYl5DegsGWqlOTJyAV7cuuVjmVPqxgvL8xBng4R85xqIQJRMjtYYktoPNuQ==", + "version": "5.15.1", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.15.1.tgz", + "integrity": "sha512-wTbzuy5KjSvCPE9UVJktWHJ0b/tD5biavY9wvF+OpYDLPpdXK52vc1hTDxSbdkHIFMkJExzrwO9GvpVAHZBnFQ==", "dependencies": { - "@babel/runtime": "^7.23.2", - "@mui/utils": "^5.14.15", + "@babel/runtime": "^7.23.5", + "@mui/utils": "^5.15.1", "prop-types": "^15.8.1" }, "engines": { @@ -3702,7 +3703,7 @@ }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/mui" + "url": "https://opencollective.com/mui-org" }, "peerDependencies": { "@types/react": "^17.0.0 || ^18.0.0", @@ -3715,11 +3716,11 @@ } }, "node_modules/@mui/styled-engine": { - "version": "5.14.15", - "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.14.15.tgz", - "integrity": "sha512-mbOjRf867BysNpexe5Z/P8s3bWzDPNowmKhi7gtNDP/LPEeqAfiDSuC4WPTXmtvse1dCl30Nl755OLUYuoi7Mw==", + "version": "5.15.1", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.15.1.tgz", + "integrity": "sha512-7WDZTJLqGexWDjqE9oAgjU8ak6hEtUw2yQU7SIYID5kLVO2Nj/Wi/KicbLsXnTsJNvSqePIlUIWTBSXwWJCPZw==", "dependencies": { - "@babel/runtime": "^7.23.2", + "@babel/runtime": "^7.23.5", "@emotion/cache": "^11.11.0", "csstype": "^3.1.2", "prop-types": "^15.8.1" @@ -3729,7 +3730,7 @@ }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/mui" + "url": "https://opencollective.com/mui-org" }, "peerDependencies": { "@emotion/react": "^11.4.1", @@ -3746,15 +3747,15 @@ } }, "node_modules/@mui/system": { - "version": "5.14.15", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.14.15.tgz", - "integrity": "sha512-zr0Gdk1RgKiEk+tCMB900LaOpEC8NaGvxtkmMdL/CXgkqQZSVZOt2PQsxJWaw7kE4YVkIe4VukFVc43qcq9u3w==", - "dependencies": { - "@babel/runtime": "^7.23.2", - "@mui/private-theming": "^5.14.15", - "@mui/styled-engine": "^5.14.15", - "@mui/types": "^7.2.7", - "@mui/utils": "^5.14.15", + "version": "5.15.1", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.15.1.tgz", + "integrity": "sha512-LAnP0ls69rqW9eBgI29phIx/lppv+WDGI7b3EJN7VZIqw0RezA0GD7NRpV12BgEYJABEii6z5Q9B5tg7dsX0Iw==", + "dependencies": { + "@babel/runtime": "^7.23.5", + "@mui/private-theming": "^5.15.1", + "@mui/styled-engine": "^5.15.1", + "@mui/types": "^7.2.11", + "@mui/utils": "^5.15.1", "clsx": "^2.0.0", "csstype": "^3.1.2", "prop-types": "^15.8.1" @@ -3764,7 +3765,7 @@ }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/mui" + "url": "https://opencollective.com/mui-org" }, "peerDependencies": { "@emotion/react": "^11.5.0", @@ -3785,9 +3786,9 @@ } }, "node_modules/@mui/types": { - "version": "7.2.7", - "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.7.tgz", - "integrity": "sha512-sofpWmcBqOlTzRbr1cLQuUDKaUYVZTw8ENQrtL39TECRNENEzwgnNPh6WMfqMZlMvf1Aj9DLg74XPjnLr0izUQ==", + "version": "7.2.11", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.11.tgz", + "integrity": "sha512-KWe/QTEsFFlFSH+qRYf3zoFEj3z67s+qAuSnMMg+gFwbxG7P96Hm6g300inQL1Wy///gSRb8juX7Wafvp93m3w==", "peerDependencies": { "@types/react": "^17.0.0 || ^18.0.0" }, @@ -3798,12 +3799,12 @@ } }, "node_modules/@mui/utils": { - "version": "5.14.15", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.14.15.tgz", - "integrity": "sha512-QBfHovAvTa0J1jXuYDaXGk+Yyp7+Fm8GSqx6nK2JbezGqzCFfirNdop/+bL9Flh/OQ/64PeXcW4HGDdOge+n3A==", + "version": "5.15.1", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.15.1.tgz", + "integrity": "sha512-V1/d0E3Bju5YdB59HJf2G0tnHrFEvWLN+f8hAXp9+JSNy/LC2zKyqUfPPahflR6qsI681P8G9r4mEZte/SrrYA==", "dependencies": { - "@babel/runtime": "^7.23.2", - "@types/prop-types": "^15.7.8", + "@babel/runtime": "^7.23.5", + "@types/prop-types": "^15.7.11", "prop-types": "^15.8.1", "react-is": "^18.2.0" }, @@ -3812,7 +3813,7 @@ }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/mui" + "url": "https://opencollective.com/mui-org" }, "peerDependencies": { "@types/react": "^17.0.0 || ^18.0.0", @@ -6965,9 +6966,9 @@ "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==" }, "node_modules/@types/prop-types": { - "version": "15.7.9", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.9.tgz", - "integrity": "sha512-n1yyPsugYNSmHgxDFjicaI2+gCNjsBck8UX9kuofAKlc0h1bL+20oSF72KeNaW2DUlesbEVCFgyV2dPGTiY42g==" + "version": "15.7.11", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz", + "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==" }, "node_modules/@types/q": { "version": "1.5.7", @@ -7003,9 +7004,9 @@ } }, "node_modules/@types/react-transition-group": { - "version": "4.4.8", - "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.8.tgz", - "integrity": "sha512-QmQ22q+Pb+HQSn04NL3HtrqHwYMf4h3QKArOy5F8U5nEVMaihBs3SR10WiOM1iwPz5jIo8x/u11al+iEGZZrvg==", + "version": "4.4.10", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.10.tgz", + "integrity": "sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q==", "dependencies": { "@types/react": "*" } @@ -19628,6 +19629,14 @@ "websocket-driver": "^0.7.4" } }, + "node_modules/sockjs/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/source-list-map": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", @@ -21208,9 +21217,13 @@ } }, "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], "bin": { "uuid": "dist/bin/uuid" } diff --git a/app/frontend/package.json b/app/frontend/package.json index 8b60aedd..cb59b2c9 100644 --- a/app/frontend/package.json +++ b/app/frontend/package.json @@ -3,10 +3,10 @@ "version": "0.1.0", "private": true, "dependencies": { - "@emotion/react": "^11.11.1", + "@emotion/react": "^11.11.3", "@emotion/styled": "^11.11.0", "@mui/icons-material": "^5.14.15", - "@mui/material": "^5.14.15", + "@mui/material": "^5.15.1", "@nextui-org/react": "^2.2.9", "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", @@ -25,6 +25,7 @@ "react-scripts": "5.0.1", "react-transition-group": "^4.4.5", "styled-components": "^6.1.0", + "uuid": "^9.0.1", "web-vitals": "^2.1.4" }, "scripts": { diff --git a/app/frontend/src/components/CommentCard.js b/app/frontend/src/components/CommentCard.js index a7134ae1..9dba4318 100644 --- a/app/frontend/src/components/CommentCard.js +++ b/app/frontend/src/components/CommentCard.js @@ -1,29 +1,31 @@ import React from 'react' import userlogo from '../user.jpg' import ReportIcon from '@mui/icons-material/Report' -import DeleteIcon from '@mui/icons-material/Delete'; +import DeleteIcon from '@mui/icons-material/Delete' import { deleteComment } from '../services/commentService' const CommentCard = ({ comment, onUpvote, onDownvote, currentUser }) => { const isCurrentUserCreator = comment.creatorUser.username === currentUser.username const handleDeleteComment = async (commentId) => { - try { - await deleteComment(commentId) - window.location.reload() - } catch (error) { - console.error('Error deleting post:', error) - } - } + try { + await deleteComment(commentId) + window.location.reload() + } catch (error) { + console.error('Error deleting post:', error) + } + } return (

{comment.content}

- {isCurrentUserCreator ? : ( + {isCurrentUserCreator ? ( + + ) : ( @@ -59,4 +61,4 @@ const CommentCard = ({ comment, onUpvote, onDownvote, currentUser }) => {
) } -export default CommentCard \ No newline at end of file +export default CommentCard diff --git a/app/frontend/src/components/PostCard.js b/app/frontend/src/components/PostCard.js index 12f8da62..f44ab7a3 100644 --- a/app/frontend/src/components/PostCard.js +++ b/app/frontend/src/components/PostCard.js @@ -1,16 +1,124 @@ -import React from 'react' +import React, { useState, useRef, useEffect } from 'react' import ReportIcon from '@mui/icons-material/Report' import EditPost from '../pages/ForumPage/EditPost' import { deletePost } from '../services/postService' import { useNavigate } from 'react-router-dom' import { useParams } from 'react-router-dom' -import DeleteIcon from '@mui/icons-material/Delete'; +import TextWithAnnotations from '../components/TextWithAnnotation' +import CloseIcon from '@mui/icons-material/Close' +import { createAnnotation, getAnnotationsByTarget } from '../services/AnnotationService' +import { v4 as uuidv4 } from 'uuid' +import DeleteIcon from '@mui/icons-material/Delete' const PostCard = ({ post, currentUser, onUpvote, onDownvote }) => { - const isCurrentUserCreator = currentUser && post.creatorUser.username === currentUser.username ? true : false + const isCurrentUserCreator = currentUser && post.creatorUser.username === currentUser.username const navigate = useNavigate() const params = useParams() + const [annotations, setAnnotations] = useState([]) + const [showAnnotationButton, setShowAnnotationButton] = useState(false) + const [selectionRange, setSelectionRange] = useState({ startIndex: 0, endIndex: 0, value: '' }) + const [annotationText, setAnnotationText] = useState('') + const [showAnnotationPopup, setShowAnnotationPopup] = useState(false) + const postContentRef = useRef() + + useEffect(() => { + const loadAnnotations = async () => { + try { + const response = await getAnnotationsByTarget(`http://167.99.242.175:8080/post/${post.postId}`) + + const transformedAnnotations = response.data.map((annotation) => ({ + startIndex: annotation.target.selector.start, + endIndex: annotation.target.selector.end, + value: annotation.body[0].value + })) + + setAnnotations(transformedAnnotations) + } catch (error) { + console.error('Error loading annotations:', error) + } + } + + if (post) { + loadAnnotations() + } + }, [post]) + + const handleTextSelect = () => { + const selection = window.getSelection() + if (selection.rangeCount === 0) return + + const range = selection.getRangeAt(0) + const preSelectionRange = range.cloneRange() + preSelectionRange.selectNodeContents(postContentRef.current) + preSelectionRange.setEnd(range.startContainer, range.startOffset) + const startIndex = preSelectionRange.toString().length + + const endIndex = startIndex + range.toString().length + + if (range.toString().length > 0 && postContentRef.current.contains(range.startContainer)) { + setSelectionRange({ startIndex, endIndex, value: range.toString() }) + setShowAnnotationButton(true) + } else { + setShowAnnotationButton(false) + } + } + + const handleAddAnnotationClick = () => { + setShowAnnotationButton(false) + setShowAnnotationPopup(true) + } + + const handleClosePopup = () => { + setShowAnnotationPopup(false) + setAnnotationText('') + } + + const handleSubmitAnnotation = async () => { + const annotationUuid = uuidv4() // Generate UUID + const username = localStorage.getItem('username') // Fetch username from local storage + + const annotationData = { + context: 'http://www.w3.org/ns/anno.jsonld', + id: annotationUuid, // Use generated UUID + type: 'Annotation', + motivation: ['commenting', 'annotating'], + creator: `http://167.99.242.175:8080/user/${username}`, // Use fetched username + created: new Date().toISOString(), + body: [ + { + id: annotationUuid, // Use same UUID for body ID + type: 'TextualBody', + value: annotationText, + format: 'text/plain', + language: 'en', + purpose: 'commenting' + } + ], + target: { + id: `http://167.99.242.175:8080/post/${post.postId}`, + format: 'text/html', + language: 'en', + selector: { + type: 'TextPositionSelector', + start: selectionRange.startIndex, + end: selectionRange.endIndex + } + } + } + + try { + await createAnnotation(annotationData) + window.location.reload() + // setAnnotations((prevAnnotations) => [...prevAnnotations, annotationData]) + } catch (error) { + console.error('Error creating annotation:', error) + } finally { + setShowAnnotationPopup(false) + setAnnotationText('') + } + } + const handleDeletePost = async (postId) => { try { await deletePost(postId) @@ -21,7 +129,7 @@ const PostCard = ({ post, currentUser, onUpvote, onDownvote }) => { } return ( -
+
{isCurrentUserCreator && ( <> @@ -32,7 +140,7 @@ const PostCard = ({ post, currentUser, onUpvote, onDownvote }) => { className='btn btn-sm bg-neutral-200 border-none hover:bg-neutral-300' title='Delete Post' > - + )} @@ -44,61 +152,104 @@ const PostCard = ({ post, currentUser, onUpvote, onDownvote }) => { )}
-

- +

+ {post.title}

-

{post.content}

-
- {/* {post.tags.map((tag) => ( - #{tag} - ))} */} +
+
-
-
-
-
- User -
-
-
- - {post.creatorUser.username} - -

{new Date(post.creationDate).toLocaleDateString()}

-
+
+ + {showAnnotationButton && ( +
+ +
+ )} + + {showAnnotationPopup && ( +
+
+
-
-
- {post.tags.map((tag, index) => ( - - #{tag.name} - - ))} -
-
- {post.category}
- +
+ Selected Text: {selectionRange.value}
-
- -

{post.upvotes}

- -

{post.downvotes}

+ )} + +
+
+
+
+ User +
+
+
+ + {post.creatorUser.username} + +

{new Date(post.creationDate).toLocaleDateString()}

+
+
+
+
+ {post.tags.map((tag, index) => ( + + #{tag.name} + + ))} +
+
+ {post.category} +
+ +
+
+ +

{post.upvotes}

+ +

{post.downvotes}

+
- ); -}; + ) +} -export default PostCard; \ No newline at end of file +export default PostCard diff --git a/app/frontend/src/components/TextWithAnnotation.js b/app/frontend/src/components/TextWithAnnotation.js new file mode 100644 index 00000000..4b49d018 --- /dev/null +++ b/app/frontend/src/components/TextWithAnnotation.js @@ -0,0 +1,30 @@ +import React from 'react' +import Tooltip from '@mui/material/Tooltip' + +const TextWithAnnotation = ({ text, annotations }) => { + const parts = [] + let lastIndex = 0 + + const sortedAnnotations = annotations.sort((a, b) => a.startIndex - b.startIndex) + + sortedAnnotations.forEach((annotation, index) => { + const { startIndex, endIndex, value } = annotation + console.log('--->', annotation, index) + + parts.push(text.substring(lastIndex, startIndex)) + + parts.push( + + {text.substring(startIndex, endIndex)} + + ) + + lastIndex = endIndex + }) + + parts.push(text.substring(lastIndex)) + + return
{parts}
+} + +export default TextWithAnnotation diff --git a/app/frontend/src/components/navbar/Navbar.js b/app/frontend/src/components/navbar/Navbar.js index 3e6c2817..af382846 100644 --- a/app/frontend/src/components/navbar/Navbar.js +++ b/app/frontend/src/components/navbar/Navbar.js @@ -20,7 +20,7 @@ import axios from 'axios' import { useNavigate } from 'react-router-dom' import logo from '../../gamelounge.png' import { getAllSearch } from '../../services/searchService.js' -import {getUserInfoBySessionId} from "../../services/userService"; +import { getUserInfoBySessionId } from "../../services/userService"; const Navbarx = () => { const api_url = process.env.REACT_APP_API_URL const navigate = useNavigate() @@ -96,7 +96,7 @@ const Navbarx = () => { useEffect(() => { const isAdmin = JSON.parse(localStorage.getItem('isAdmin')) if (isAdmin) { - setIsAdmin(true) + setIsAdmin(true) } }, []) const [searchResults, setSearchResults] = useState([]) @@ -124,19 +124,33 @@ const Navbarx = () => { // Trigger search when the user stops typing for 300 milliseconds } - useEffect(() => { - const fetchUserInfo = async () => { - try { - const response = await getUserInfoBySessionId(); - const userData = response.data; - setCurrentUser(userData); - console.log('Current User:', userData); - } catch (error) { - console.error('Error fetching user info:', error); - } - }; - fetchUserInfo(); - }, []); + useEffect(() => { + const fetchUserInfo = async () => { + try { + const response = await getUserInfoBySessionId(); + const userData = response.data; + setCurrentUser(userData); + console.log('Current User:', userData); + } catch (error) { + console.error('Error fetching user info:', error); + } + }; + fetchUserInfo(); + }, []); + + useEffect(() => { + const fetchUserInfo = async () => { + try { + const response = await getUserInfoBySessionId(); + const userData = response.data; + setCurrentUser(userData); + console.log('Current User:', userData); + } catch (error) { + console.error('Error fetching user info:', error); + } + }; + fetchUserInfo(); + }, []); return ( @@ -168,7 +182,7 @@ const Navbarx = () => { {isAdmin && ( - Admin Panel + Admin Panel )} diff --git a/app/frontend/src/pages/GameForum/EditGame.js b/app/frontend/src/pages/GameForum/EditGame.js new file mode 100644 index 00000000..097383f5 --- /dev/null +++ b/app/frontend/src/pages/GameForum/EditGame.js @@ -0,0 +1,328 @@ +import React, { useState } from 'react' +import { + Dialog, + DialogTitle, + DialogContent, + TextField, + DialogActions, + Button, + IconButton, + MenuItem, + OutlinedInput, + FormControl, + Select, + InputLabel, + Checkbox, + ListItemText +} from '@mui/material' +import CloseIcon from '@mui/icons-material/Close' +import { useForm } from 'react-hook-form' +import axios from 'axios' +import EditIcon from '@mui/icons-material/Edit' + +export default function EditGame(props) { + const api_url = process.env.REACT_APP_API_URL + const { handleSubmit } = useForm() + const [open, setOpen] = useState(false) + const [gameData, setGameData] = useState({ + title: props.game.title, + description: props.game.description, + genres: props.game.genre, + platforms: props.game.platform, + playerNumber: props.game.playerNumber, + releaseYear: props.game.releaseYear, + universe: props.game.universe, + mechanics: props.game.mechanics, + playtime: props.game.playtime, + image: null + }) + const [selectedImage, setSelectedImage] = useState(null) + + const handleImageChange = async (e) => { + const file = e.target.files[0] + const reader = new FileReader() + reader.onloadend = () => { + const arrayBuffer = reader.result + const byteArray = new Uint8Array(arrayBuffer) + setSelectedImage(byteArray) + setSelectedImage(new Blob([byteArray])) + } + reader.readAsArrayBuffer(file) + } + + const handleUniverseInfoChange = (event) => { + setSelectedUniverseInfo(event.target.value) + } + const handleUploadButtonClick = () => { + // Programmatically trigger the file input + document.getElementById('imageInput').click() + } + + const predefinedGenres = [ + 'RGP', + 'Strategy', + 'Shooter', + 'Sports', + 'Fighting', + 'MOBA', + 'Action', + 'Adventure', + 'Simulation', + 'Horror', + 'Empty' + ] + const predefinedPlatforms = ['XBOX', 'Computer', 'PS', 'Onboard', 'Mobile', 'Empty'] + const predefinedPlayerNumber = ['Single', 'Teams', 'Multiplayer', 'MMO', 'Empty'] + const predefinedUniverseInfo = [ + 'Medieval', + 'Fantasy', + 'SciFi', + 'Cyberpunk', + 'Historical', + 'Contemporary', + 'PostApocalyptic', + 'AlternateReality', + 'Empty' + ] + const predefinedGameMechanics = ['TurnBased', 'ChangeBased', 'RealTime', 'Empty'] + + //turn string into array + const [selectedGenres, setSelectedGenres] = useState(props.game.genres) + const [selectedPlatforms, setSelectedPlatforms] = useState(props.game.platforms) + const [selectedGameMechanics, setSelectedGameMechanics] = useState(props.game.mechanics) + const [selectedPlayerNumber, setSelectedPlayerNumber] = useState(props.game.playerNumber) + const [selectedUniverseInfo, setSelectedUniverseInfo] = useState(props.game.universe) + const handleClickOpen = () => { + setOpen(true) + } + + const handleClose = () => { + setOpen(false) + clearData() + } + + const onSubmit = async () => { + const postData = { + ...gameData, + + genres: selectedGenres, + platforms: selectedPlatforms, + playerNumber: selectedPlayerNumber, + universe: selectedUniverseInfo, + image: selectedImage + } + delete postData.characters + const { title, description, genres, platforms, playerNumber, releaseYear, universe, mechanics, playtime, image } = postData + const gameId = props.game.gameId + const request = { gameId, title, description, genres, platforms, playerNumber, releaseYear, universe, mechanics, playtime, image } + const formDataToSend = new FormData() + + formDataToSend.append('request', new Blob([JSON.stringify(request)], { type: 'application/json' })) + if (selectedImage) { + const file = new File([selectedImage], 'image.png', { type: 'image/png' }) + formDataToSend.append('image', file) + } + try { + axios.defaults.withCredentials = true + const response = await axios.post(`${api_url}/game/${props.game.gameId}`, formDataToSend, { + headers: { + 'Content-Type': 'multipart/form-data' + } + }) + + if (response.status === 201) { + console.log('Game created successfully!') + } + } catch (error) { + console.log(request) + console.log(error) + } + } + + const clearData = () => { + setGameData({ + title: '', + description: '', + genre: [], + platform: [], + playerNumber: '', + releaseYear: 0, + universe: '', + mechanics: '', + playtime: '', + image: null, + characters: [] + }) + setSelectedGenres([]) + setSelectedPlatforms([]) + } + + const handleChange = (property, value) => { + setGameData((prevData) => ({ ...prevData, [property]: value })) + } + const handlePlayerNumberChange = (event) => { + setSelectedPlayerNumber(event.target.value) + } + + const handleGameMechanicsChange = (event) => { + setSelectedGameMechanics(event.target.value) + } + const handleGenreChange = (event) => { + setSelectedGenres(event.target.value) + } + + const handlePlatformChange = (event) => { + setSelectedPlatforms(event.target.value) + } + + const handleAddCharacter = () => { + setGameData((prevData) => ({ + ...prevData, + characters: [...prevData.characters, { name: ``, description: `` }] + })) + } + + return ( +
+ + + + + Edit Game + + + + + + + {Object.entries(gameData).map(([property, value]) => ( +
+ {property === 'description' ? ( + handleChange(property, event.target.value)} + /> + ) : property === 'genres' ? ( + + Genres + + + ) : property === 'platforms' ? ( + + Platforms + + + ) : property === 'image' ? ( +
+ + + {selectedImage &&
Selected Image: {selectedImage.name}
} +
+ ) : property === 'mechanics' ? ( + + Game Mechanics + + + ) : property === 'universe' ? ( + + Universe Info + + + ) : property === 'playerNumber' ? ( + + Number of Players + + + ) : ( + handleChange(property, event.target.value)} + /> + )} +
+ ))} +
+ + + + +
+
+ ) +} diff --git a/app/frontend/src/pages/GamePage/GamePage.js b/app/frontend/src/pages/GamePage/GamePage.js index 1f3f6797..9bea3f27 100644 --- a/app/frontend/src/pages/GamePage/GamePage.js +++ b/app/frontend/src/pages/GamePage/GamePage.js @@ -1,46 +1,46 @@ -import React, { useState, useEffect } from 'react'; -import 'tailwindcss/tailwind.css'; -import { useParams, useNavigate } from 'react-router-dom'; -import Navbarx from '../../components/navbar/Navbar'; -import { getGame, rateGame } from '../../services/gameService'; +import React, { useState, useEffect } from 'react' +import 'tailwindcss/tailwind.css' +import { useParams, useNavigate } from 'react-router-dom' +import Navbarx from '../../components/navbar/Navbar' +import { getGame, rateGame } from '../../services/gameService' // import { getAllGames } from '../../services/gameService'; - +import EditGame from '../GameForum/EditGame' const GamePage = () => { - const navigate = useNavigate(); - const { gameId } = useParams(); + const navigate = useNavigate() + const { gameId } = useParams() - const [game, setGame] = useState(null); - const [rating, setRating] = useState(null); - const [hasRated, setHasRated] = useState(false); - const [similarGames, setSimilarGames] = useState([]); + const [game, setGame] = useState(null) + const [rating, setRating] = useState(null) + const [hasRated, setHasRated] = useState(false) + const [similarGames, setSimilarGames] = useState([]) useEffect(() => { const fetchGame = async () => { try { - const response = await getGame(gameId); - setGame(response.data); - setSimilarGames(response.data.similarGames); + const response = await getGame(gameId) + setGame(response.data) + setSimilarGames(response.data.similarGames) } catch (error) { - console.error(error); + console.error(error) } - }; - fetchGame(); - }, [gameId]); + } + fetchGame() + }, [gameId]) const handleRatingChange = (selectedRating) => { - setRating(selectedRating); - }; + setRating(selectedRating) + } const handleRateGame = async () => { if (!hasRated && rating !== null) { try { - await rateGame(gameId, rating); - setHasRated(true); + await rateGame(gameId, rating) + setHasRated(true) } catch (error) { - console.error('Error rating game:', error); + console.error('Error rating game:', error) } } - }; + } return ( <> @@ -50,8 +50,11 @@ const GamePage = () => {
-

Page created on: {new Date(game?.creationDate).toLocaleDateString()}

+

+ Page created on: {new Date(game?.creationDate).toLocaleDateString()} +

+ {game && }
@@ -65,7 +68,9 @@ const GamePage = () => {

Description

{game?.description}


-

Release Year: {game?.releaseYear}

+

+ Release Year: {game?.releaseYear} +

@@ -110,11 +115,15 @@ const GamePage = () => {

Mechanics: {game?.mechanics}

Universe : {game?.universe}

Playtime: {game?.playtime}

-

Player number : {game?.playerNumber}

+

+ Player number : {game?.playerNumber} +

Platform:

{game?.platforms.map((platform, index) => ( -

{platform}

+

+ {platform} +

))}
{/*

Game Details:

*/} @@ -131,10 +140,7 @@ const GamePage = () => {

Games You May Like:

{similarGames.map((game) => (
  • -
  • @@ -150,7 +156,7 @@ const GamePage = () => {
    - ); -}; + ) +} -export default GamePage; +export default GamePage diff --git a/app/frontend/src/services/AnnotationService.js b/app/frontend/src/services/AnnotationService.js new file mode 100644 index 00000000..680dbb16 --- /dev/null +++ b/app/frontend/src/services/AnnotationService.js @@ -0,0 +1,24 @@ +import axios from 'axios' + +const axiosInstance = axios.create({ + // baseURL: process.env.REACT_APP_API_URL + baseURL: 'http://167.99.242.175:8081' +}) + +axiosInstance.defaults.withCredentials = true + +export const createAnnotation = (annotationData) => { + return axiosInstance.post('/annotation/create', annotationData, { + withCredentials: true + }) +} + +export const getAnnotationsByTarget = (targetId) => { + return axiosInstance.post( + '/annotation/get-annotations-by-target', + { targetId }, + { + withCredentials: true + } + ) +} diff --git a/app/frontend/src/services/userService.js b/app/frontend/src/services/userService.js index ffcaddaa..3d86fb3e 100644 --- a/app/frontend/src/services/userService.js +++ b/app/frontend/src/services/userService.js @@ -70,14 +70,13 @@ export const getFollowings = (sessionId) => { } export const followUser = (sessionId, userId) => { - return axiosInstance.put(`/user/follow-user/${userId}`, { - headers: { Cookie: `SESSIONID=${sessionId}` } - }); -}; + return axiosInstance.put(`/user/follow-user/${userId}`, { + headers: { Cookie: `SESSIONID=${sessionId}` } + }) +} export const unfollowUser = (sessionId, userId) => { - return axiosInstance.put(`/user/unfollow-user/${userId}`, { - headers: { Cookie: `SESSIONID=${sessionId}` } - }); -}; - + return axiosInstance.put(`/user/unfollow-user/${userId}`, { + headers: { Cookie: `SESSIONID=${sessionId}` } + }) +}