From e061b9d1c0fce1ce3e27e0ec4931689a715203c2 Mon Sep 17 00:00:00 2001 From: ExostFlash Date: Tue, 3 Jun 2025 13:06:28 +0200 Subject: [PATCH] Fonction de pute --- .../restControllers/MatchRestController.kt | 2 +- FencerJudgeFront/package-lock.json | 508 +++++++++++++++++- FencerJudgeFront/package.json | 5 + .../src/app/components/home/home.component.ts | 86 ++- .../matches-add/matches-add.component.ts | 109 ++-- .../match/matches-id/matches-id.component.ts | 32 +- .../match/matches/matches.component.ts | 31 +- .../src/app/interfaces/models/api.model.ts | 5 + .../src/app/services/api/api.service.spec.ts | 16 + .../src/app/services/api/api.service.ts | 56 ++ .../app/services/matches/matches.service.ts | 111 +--- .../src/app/services/player/player.service.ts | 37 +- .../app/services/referee/referee.service.ts | 41 +- db.lock.db | 6 + db.mv.db | Bin 32768 -> 36864 bytes 15 files changed, 805 insertions(+), 240 deletions(-) create mode 100644 FencerJudgeFront/src/app/interfaces/models/api.model.ts create mode 100644 FencerJudgeFront/src/app/services/api/api.service.spec.ts create mode 100644 FencerJudgeFront/src/app/services/api/api.service.ts create mode 100644 db.lock.db diff --git a/FencerJudgeBack/src/main/kotlin/fr/teamflash/fencerjudgeback/restControllers/MatchRestController.kt b/FencerJudgeBack/src/main/kotlin/fr/teamflash/fencerjudgeback/restControllers/MatchRestController.kt index ab2e7de..9d526ac 100644 --- a/FencerJudgeBack/src/main/kotlin/fr/teamflash/fencerjudgeback/restControllers/MatchRestController.kt +++ b/FencerJudgeBack/src/main/kotlin/fr/teamflash/fencerjudgeback/restControllers/MatchRestController.kt @@ -17,7 +17,7 @@ class MatchRestController(private val matchService: MatchService) { } // Lister tous les matchs - @GetMapping("/") + @GetMapping("") fun getAll(): ResponseEntity?> { return ResponseEntity.ok(matchService.getAll()) } diff --git a/FencerJudgeFront/package-lock.json b/FencerJudgeFront/package-lock.json index 00f2a33..766f3cc 100644 --- a/FencerJudgeFront/package-lock.json +++ b/FencerJudgeFront/package-lock.json @@ -16,10 +16,14 @@ "@angular/platform-browser": "^19.1.0", "@angular/platform-browser-dynamic": "^19.1.0", "@angular/router": "^19.1.0", + "@stomp/stompjs": "^7.1.1", + "@types/stompjs": "^2.3.9", "boostrap": "^2.0.0", "bootstrap": "^5.3.5", "bootstrap-icons": "^1.11.3", "rxjs": "~7.8.0", + "socket.io-client": "^4.8.1", + "sockjs-client": "^1.6.1", "tslib": "^2.3.0", "zone.js": "~0.15.0" }, @@ -28,6 +32,7 @@ "@angular/cli": "^19.1.4", "@angular/compiler-cli": "^19.1.0", "@types/jasmine": "~5.1.0", + "@types/sockjs-client": "^1.5.4", "jasmine-core": "~5.5.0", "karma": "~6.4.0", "karma-chrome-launcher": "~3.2.0", @@ -4726,6 +4731,20 @@ "linux" ] }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.41.1.tgz", + "integrity": "sha512-LdIUOb3gvfmpkgFZuccNa2uYiqtgZAz3PTzjuM5bH3nvuy9ty6RGc/Q0+HDFrHrizJGVpjnTZ1yS5TNNjFlklw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true + }, "node_modules/@rollup/rollup-linux-s390x-gnu": { "version": "4.34.8", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.34.8.tgz", @@ -4924,9 +4943,13 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", - "dev": true, "license": "MIT" }, + "node_modules/@stomp/stompjs": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@stomp/stompjs/-/stompjs-7.1.1.tgz", + "integrity": "sha512-chcDs6YkAnKp1FqzwhGvh3i7v0+/ytzqWdKYw6XzINEKAzke/iD00dNgFPWSZEqktHOK+C1gSzXhLkLbARIaZw==" + }, "node_modules/@tufjs/canonical-json": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@tufjs/canonical-json/-/canonical-json-2.0.0.tgz", @@ -5139,7 +5162,6 @@ "version": "22.14.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.0.tgz", "integrity": "sha512-Kmpl+z84ILoG+3T/zQFyAJsU6EPTmOCj8/2+83fSN6djd6I4o7uOuGIH6vq3PrjY5BGitSbFuMN18j3iknubbA==", - "dev": true, "license": "MIT", "dependencies": { "undici-types": "~6.21.0" @@ -5219,6 +5241,20 @@ "@types/node": "*" } }, + "node_modules/@types/sockjs-client": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@types/sockjs-client/-/sockjs-client-1.5.4.tgz", + "integrity": "sha512-zk+uFZeWyvJ5ZFkLIwoGA/DfJ+pYzcZ8eH4H/EILCm2OBZyHH6Hkdna1/UWL/CFruh5wj6ES7g75SvUB0VsH5w==", + "dev": true + }, + "node_modules/@types/stompjs": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/@types/stompjs/-/stompjs-2.3.9.tgz", + "integrity": "sha512-fu/GgkRdxwyEJ+JeUsGhDxGwmZQi+xeNElradGQ4ehWiG2z/o89gsi5Y7Gv0KC6VK1v78Cjh8zj3VF+RvqCGSA==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/ws": { "version": "8.18.1", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", @@ -7266,11 +7302,38 @@ "node": ">=10.2.0" } }, + "node_modules/engine.io-client": { + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.3.tgz", + "integrity": "sha512-T0iLjnyNWahNyv/lcjS2y4oE358tVS/SYQNxYXGAJ9/GLgH4VCvOQ/mhTjqU88mLZCQgiG8RIegFHYCdVC+j5w==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1", + "xmlhttprequest-ssl": "~2.1.1" + } + }, + "node_modules/engine.io-client/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/engine.io-parser": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", - "dev": true, "license": "MIT", "engines": { "node": ">=10.0.0" @@ -7586,6 +7649,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/exponential-backoff": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.2.tgz", @@ -7796,7 +7867,6 @@ "version": "0.11.4", "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", - "dev": true, "license": "Apache-2.0", "dependencies": { "websocket-driver": ">=0.5.1" @@ -7806,11 +7876,10 @@ } }, "node_modules/fdir": { - "version": "6.4.3", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.3.tgz", - "integrity": "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==", + "version": "6.4.5", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.5.tgz", + "integrity": "sha512-4BG7puHpVsIYxZUbiUE3RqGloLaSSwzYie5jvasC4LWuBWzZawynvYouhjbQKw2JuIGYdm0DzIxl8iVidKlUEw==", "dev": true, - "license": "MIT", "peerDependencies": { "picomatch": "^3 || ^4" }, @@ -8440,7 +8509,6 @@ "version": "0.5.10", "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.10.tgz", "integrity": "sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==", - "dev": true, "license": "MIT" }, "node_modules/http-proxy": { @@ -8674,7 +8742,6 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true, "license": "ISC" }, "node_modules/ini": { @@ -10416,7 +10483,6 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, "license": "MIT" }, "node_modules/msgpackr": { @@ -11652,6 +11718,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -11864,7 +11935,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "dev": true, "license": "MIT" }, "node_modules/resolve": { @@ -12091,7 +12161,6 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, "funding": [ { "type": "github", @@ -12717,11 +12786,40 @@ } } }, + "node_modules/socket.io-client": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.1.tgz", + "integrity": "sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.2", + "engine.io-client": "~6.6.1", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-client/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/socket.io-parser": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", - "dev": true, "license": "MIT", "dependencies": { "@socket.io/component-emitter": "~3.1.0", @@ -12735,7 +12833,6 @@ "version": "4.3.7", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -12779,6 +12876,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/socks": { "version": "2.8.4", "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.4.tgz", @@ -13343,13 +13466,12 @@ "license": "MIT" }, "node_modules/tinyglobby": { - "version": "0.2.12", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.12.tgz", - "integrity": "sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==", + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", + "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", "dev": true, - "license": "MIT", "dependencies": { - "fdir": "^6.4.3", + "fdir": "^6.4.4", "picomatch": "^4.0.2" }, "engines": { @@ -13522,7 +13644,6 @@ "version": "6.21.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true, "license": "MIT" }, "node_modules/unicode-canonical-property-names-ecmascript": { @@ -13659,6 +13780,15 @@ "browserslist": ">= 4.21.0" } }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -13718,16 +13848,18 @@ } }, "node_modules/vite": { - "version": "6.2.6", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.6.tgz", - "integrity": "sha512-9xpjNl3kR4rVDZgPNdTL0/c6ao4km69a/2ihNQbcANz8RuCOK3hQBmLSJf3bRKVQjVMda+YvizNE8AwvogcPbw==", + "version": "6.3.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", + "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==", "dev": true, - "license": "MIT", "peer": true, "dependencies": { "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", "postcss": "^8.5.3", - "rollup": "^4.30.1" + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" }, "bin": { "vite": "bin/vite.js" @@ -13790,6 +13922,279 @@ } } }, + "node_modules/vite/node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.41.1.tgz", + "integrity": "sha512-NELNvyEWZ6R9QMkiytB4/L4zSEaBC03KIXEghptLGLZWJ6VPrL63ooZQCOnlx36aQPGhzuOMwDerC1Eb2VmrLw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "peer": true + }, + "node_modules/vite/node_modules/@rollup/rollup-android-arm64": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.41.1.tgz", + "integrity": "sha512-DXdQe1BJ6TK47ukAoZLehRHhfKnKg9BjnQYUu9gzhI8Mwa1d2fzxA1aw2JixHVl403bwp1+/o/NhhHtxWJBgEA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "peer": true + }, + "node_modules/vite/node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.41.1.tgz", + "integrity": "sha512-5afxvwszzdulsU2w8JKWwY8/sJOLPzf0e1bFuvcW5h9zsEg+RQAojdW0ux2zyYAz7R8HvvzKCjLNJhVq965U7w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "peer": true + }, + "node_modules/vite/node_modules/@rollup/rollup-darwin-x64": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.41.1.tgz", + "integrity": "sha512-egpJACny8QOdHNNMZKf8xY0Is6gIMz+tuqXlusxquWu3F833DcMwmGM7WlvCO9sB3OsPjdC4U0wHw5FabzCGZg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "peer": true + }, + "node_modules/vite/node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.41.1.tgz", + "integrity": "sha512-DBVMZH5vbjgRk3r0OzgjS38z+atlupJ7xfKIDJdZZL6sM6wjfDNo64aowcLPKIx7LMQi8vybB56uh1Ftck/Atg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "peer": true + }, + "node_modules/vite/node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.41.1.tgz", + "integrity": "sha512-3FkydeohozEskBxNWEIbPfOE0aqQgB6ttTkJ159uWOFn42VLyfAiyD9UK5mhu+ItWzft60DycIN1Xdgiy8o/SA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "peer": true + }, + "node_modules/vite/node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.41.1.tgz", + "integrity": "sha512-wC53ZNDgt0pqx5xCAgNunkTzFE8GTgdZ9EwYGVcg+jEjJdZGtq9xPjDnFgfFozQI/Xm1mh+D9YlYtl+ueswNEg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true + }, + "node_modules/vite/node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.41.1.tgz", + "integrity": "sha512-jwKCca1gbZkZLhLRtsrka5N8sFAaxrGz/7wRJ8Wwvq3jug7toO21vWlViihG85ei7uJTpzbXZRcORotE+xyrLA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true + }, + "node_modules/vite/node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.41.1.tgz", + "integrity": "sha512-g0UBcNknsmmNQ8V2d/zD2P7WWfJKU0F1nu0k5pW4rvdb+BIqMm8ToluW/eeRmxCared5dD76lS04uL4UaNgpNA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true + }, + "node_modules/vite/node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.41.1.tgz", + "integrity": "sha512-XZpeGB5TKEZWzIrj7sXr+BEaSgo/ma/kCgrZgL0oo5qdB1JlTzIYQKel/RmhT6vMAvOdM2teYlAaOGJpJ9lahg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true + }, + "node_modules/vite/node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.41.1.tgz", + "integrity": "sha512-bkCfDJ4qzWfFRCNt5RVV4DOw6KEgFTUZi2r2RuYhGWC8WhCA8lCAJhDeAmrM/fdiAH54m0mA0Vk2FGRPyzI+tw==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true + }, + "node_modules/vite/node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.41.1.tgz", + "integrity": "sha512-3mr3Xm+gvMX+/8EKogIZSIEF0WUu0HL9di+YWlJpO8CQBnoLAEL/roTCxuLncEdgcfJcvA4UMOf+2dnjl4Ut1A==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true + }, + "node_modules/vite/node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.41.1.tgz", + "integrity": "sha512-3rwCIh6MQ1LGrvKJitQjZFuQnT2wxfU+ivhNBzmxXTXPllewOF7JR1s2vMX/tWtUYFgphygxjqMl76q4aMotGw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true + }, + "node_modules/vite/node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.41.1.tgz", + "integrity": "sha512-oIE6M8WC9ma6xYqjvPhzZYk6NbobIURvP/lEbh7FWplcMO6gn7MM2yHKA1eC/GvYwzNKK/1LYgqzdkZ8YFxR8g==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true + }, + "node_modules/vite/node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.41.1.tgz", + "integrity": "sha512-cWBOvayNvA+SyeQMp79BHPK8ws6sHSsYnK5zDcsC3Hsxr1dgTABKjMnMslPq1DvZIp6uO7kIWhiGwaTdR4Og9A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true + }, + "node_modules/vite/node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.41.1.tgz", + "integrity": "sha512-y5CbN44M+pUCdGDlZFzGGBSKCA4A/J2ZH4edTYSSxFg7ce1Xt3GtydbVKWLlzL+INfFIZAEg1ZV6hh9+QQf9YQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true + }, + "node_modules/vite/node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.41.1.tgz", + "integrity": "sha512-lZkCxIrjlJlMt1dLO/FbpZbzt6J/A8p4DnqzSa4PWqPEUUUnzXLeki/iyPLfV0BmHItlYgHUqJe+3KiyydmiNQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "peer": true + }, + "node_modules/vite/node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.41.1.tgz", + "integrity": "sha512-+psFT9+pIh2iuGsxFYYa/LhS5MFKmuivRsx9iPJWNSGbh2XVEjk90fmpUEjCnILPEPJnikAU6SFDiEUyOv90Pg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "peer": true + }, + "node_modules/vite/node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.41.1.tgz", + "integrity": "sha512-Wq2zpapRYLfi4aKxf2Xff0tN+7slj2d4R87WEzqw7ZLsVvO5zwYCIuEGSZYiK41+GlwUo1HiR+GdkLEJnCKTCw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "peer": true + }, + "node_modules/vite/node_modules/@types/estree": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", + "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", + "dev": true, + "peer": true + }, "node_modules/vite/node_modules/postcss": { "version": "8.5.3", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", @@ -13820,6 +14225,46 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/vite/node_modules/rollup": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.41.1.tgz", + "integrity": "sha512-cPmwD3FnFv8rKMBc1MxWCwVQFxwf1JEmSX3iQXrRVVG15zerAIXRjMFVWnd5Q5QvgKF7Aj+5ykXFhUl+QGnyOw==", + "dev": true, + "peer": true, + "dependencies": { + "@types/estree": "1.0.7" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.41.1", + "@rollup/rollup-android-arm64": "4.41.1", + "@rollup/rollup-darwin-arm64": "4.41.1", + "@rollup/rollup-darwin-x64": "4.41.1", + "@rollup/rollup-freebsd-arm64": "4.41.1", + "@rollup/rollup-freebsd-x64": "4.41.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.41.1", + "@rollup/rollup-linux-arm-musleabihf": "4.41.1", + "@rollup/rollup-linux-arm64-gnu": "4.41.1", + "@rollup/rollup-linux-arm64-musl": "4.41.1", + "@rollup/rollup-linux-loongarch64-gnu": "4.41.1", + "@rollup/rollup-linux-powerpc64le-gnu": "4.41.1", + "@rollup/rollup-linux-riscv64-gnu": "4.41.1", + "@rollup/rollup-linux-riscv64-musl": "4.41.1", + "@rollup/rollup-linux-s390x-gnu": "4.41.1", + "@rollup/rollup-linux-x64-gnu": "4.41.1", + "@rollup/rollup-linux-x64-musl": "4.41.1", + "@rollup/rollup-win32-arm64-msvc": "4.41.1", + "@rollup/rollup-win32-ia32-msvc": "4.41.1", + "@rollup/rollup-win32-x64-msvc": "4.41.1", + "fsevents": "~2.3.2" + } + }, "node_modules/void-elements": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", @@ -14175,7 +14620,6 @@ "version": "0.7.4", "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", - "dev": true, "license": "Apache-2.0", "dependencies": { "http-parser-js": ">=0.5.1", @@ -14190,7 +14634,6 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", - "dev": true, "license": "Apache-2.0", "engines": { "node": ">=0.8.0" @@ -14371,7 +14814,6 @@ "version": "8.17.1", "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=10.0.0" @@ -14389,6 +14831,14 @@ } } }, + "node_modules/xmlhttprequest-ssl": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz", + "integrity": "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", diff --git a/FencerJudgeFront/package.json b/FencerJudgeFront/package.json index aecac98..ad9b812 100644 --- a/FencerJudgeFront/package.json +++ b/FencerJudgeFront/package.json @@ -18,10 +18,14 @@ "@angular/platform-browser": "^19.1.0", "@angular/platform-browser-dynamic": "^19.1.0", "@angular/router": "^19.1.0", + "@stomp/stompjs": "^7.1.1", + "@types/stompjs": "^2.3.9", "boostrap": "^2.0.0", "bootstrap": "^5.3.5", "bootstrap-icons": "^1.11.3", "rxjs": "~7.8.0", + "socket.io-client": "^4.8.1", + "sockjs-client": "^1.6.1", "tslib": "^2.3.0", "zone.js": "~0.15.0" }, @@ -30,6 +34,7 @@ "@angular/cli": "^19.1.4", "@angular/compiler-cli": "^19.1.0", "@types/jasmine": "~5.1.0", + "@types/sockjs-client": "^1.5.4", "jasmine-core": "~5.5.0", "karma": "~6.4.0", "karma-chrome-launcher": "~3.2.0", diff --git a/FencerJudgeFront/src/app/components/home/home.component.ts b/FencerJudgeFront/src/app/components/home/home.component.ts index c6a3439..d91de8e 100644 --- a/FencerJudgeFront/src/app/components/home/home.component.ts +++ b/FencerJudgeFront/src/app/components/home/home.component.ts @@ -1,4 +1,4 @@ -import { Component } from '@angular/core'; +import { Component, OnDestroy, OnInit } from '@angular/core'; import { MatchesService } from '@services/matches/matches.service'; import { Matches, MatchState } from '@interfaces/matches'; @@ -7,7 +7,10 @@ import { PlayerService } from '@services/player/player.service'; import { Player } from '@interfaces/player'; import { RefereeService } from '@services/referee/referee.service'; -import { Referee, RefereeLevel } from '@interfaces/referee'; +import { Referee } from '@interfaces/referee'; + +import { ApiService } from '@services/api/api.service'; +import { ApiResponse } from '@interfaces/models/api.model'; @Component({ selector: 'app-home', @@ -15,7 +18,7 @@ import { Referee, RefereeLevel } from '@interfaces/referee'; templateUrl: './home.component.html', styleUrl: './home.component.css', }) -export class HomeComponent { +export class HomeComponent implements OnInit, OnDestroy { latestMatch?: Matches; player1?: Player; player2?: Player; @@ -31,7 +34,7 @@ export class HomeComponent { ) {} ngOnInit(): void { - this.getLatestMatch(); + this.getLatestMatchFromAPI(); } ngOnDestroy(): void { @@ -40,26 +43,35 @@ export class HomeComponent { } } - private getLatestMatch(): void { + private getLatestMatchFromAPI(): void { this.matchesService.getMatches().subscribe((matches) => { - if (matches.length > 0) { - this.latestMatch = matches[matches.length - 1]; + if (matches && matches.length > 0) { + // Trier par date si nĂ©cessaire : + const sorted = matches.sort( + (a, b) => new Date(b.date).getTime() - new Date(a.date).getTime() + ); + this.latestMatch = sorted[0]; + console.log( - '[HomeComponent] 🏁 Dernier match ajoutĂ© :', + '[HomeComponent] 🏁 Dernier match rĂ©cupĂ©rĂ© :', this.latestMatch ); - // RĂ©cupĂ©ration des donnĂ©es liĂ©es this.loadRelatedEntities(this.latestMatch); if (this.latestMatch.state === MatchState.NOT_STARTED) { - this.updateTimeUntilMatch(); // initial - this.intervalId = setInterval(() => { - this.updateTimeUntilMatch(); - }, 60000); // chaque minute + console.log(this.latestMatch.state); + } + + if (this.latestMatch.state === MatchState.NOT_STARTED) { + this.updateTimeUntilMatch(); + this.intervalId = setInterval( + () => this.updateTimeUntilMatch(), + 60000 + ); } } else { - console.warn('[HomeComponent] Aucun match disponible'); + console.warn('[HomeComponent] Aucun match trouvĂ© via l’API'); } }); } @@ -91,16 +103,25 @@ export class HomeComponent { } private loadRelatedEntities(match: Matches): void { - this.playerService.getPlayerById(match.player1ID).subscribe((p1) => { - this.player1 = p1!; + this.playerService.getPlayerById(match.player1ID).subscribe({ + next: (response) => { + this.player1 = response.data; + }, + error: () => console.warn('Erreur chargement player1'), }); - this.playerService.getPlayerById(match.player2ID).subscribe((p2) => { - this.player2 = p2!; + this.playerService.getPlayerById(match.player2ID).subscribe({ + next: (response) => { + this.player2 = response.data; + }, + error: () => console.warn('Erreur chargement player2'), }); - this.refereeService.getRefereeById(match.refereeID).subscribe((ref) => { - this.referee = ref!; + this.refereeService.getRefereeById(match.refereeID).subscribe({ + next: (response) => { + this.referee = response.data; + }, + error: () => console.warn('Erreur chargement referee'), }); } @@ -131,29 +152,6 @@ export class HomeComponent { } getTimeUntilMatch(): string | null { - if ( - !this.latestMatch || - this.latestMatch.state !== MatchState.NOT_STARTED - ) { - return null; - } - - const now = new Date(); - const matchDate = new Date(this.latestMatch.date); - const diffMs = matchDate.getTime() - now.getTime(); - - if (diffMs <= 0) return null; - - const totalMinutes = Math.floor(diffMs / (1000 * 60)); - const days = Math.floor(totalMinutes / (60 * 24)); - const hours = Math.floor((totalMinutes % (60 * 24)) / 60); - const minutes = totalMinutes % 60; - - let result = ''; - if (days > 0) result += `${days}j `; - if (hours > 0 || days > 0) result += `${hours}h `; - result += `${minutes}min`; - - return result.trim(); + return this.timeUntilMatch; } } diff --git a/FencerJudgeFront/src/app/components/match/matches-add/matches-add.component.ts b/FencerJudgeFront/src/app/components/match/matches-add/matches-add.component.ts index 77a8a94..b18b2e5 100644 --- a/FencerJudgeFront/src/app/components/match/matches-add/matches-add.component.ts +++ b/FencerJudgeFront/src/app/components/match/matches-add/matches-add.component.ts @@ -1,6 +1,5 @@ import { Component } from '@angular/core'; import { Router } from '@angular/router'; -import { FormsModule } from '@angular/forms'; import { MatchesService } from '@services/matches/matches.service'; import { MatchState } from '@interfaces/matches'; @@ -8,13 +7,13 @@ import { MatchState } from '@interfaces/matches'; import { PlayerService } from '@services/player/player.service'; import { RefereeService } from '@services/referee/referee.service'; -import { Referee, RefereeLevel } from '@interfaces/referee'; +import { RefereeLevel } from '@interfaces/referee'; @Component({ selector: 'app-matches-add', standalone: false, templateUrl: './matches-add.component.html', - styleUrl: './matches-add.component.css', + styleUrls: ['./matches-add.component.css'], // tableau attendu }) export class MatchesAddComponent { formData = { @@ -53,45 +52,83 @@ export class MatchesAddComponent { this.formData.player1Name, this.formData.player1Club ) - .subscribe((player1) => { - this.formData['player1Id'] = player1.id; + .subscribe({ + next: (response) => { + if (response && response.data) { + this.formData.player1Id = response.data.id; - this.playerService - .getOrCreateByName( - this.formData.player2FirstName, - this.formData.player2Name, - this.formData.player2Club - ) - .subscribe((player2) => { - this.formData['player2Id'] = player2.id; - - this.refereeService + this.playerService .getOrCreateByName( - this.formData.refereeFirstName, - this.formData.refereeName, - this.formData.refereelevel + this.formData.player2FirstName, + this.formData.player2Name, + this.formData.player2Club ) - .subscribe((referee) => { - this.formData['refereeId'] = referee.id; + .subscribe({ + next: (resp2) => { + if (resp2 && resp2.data) { + this.formData.player2Id = resp2.data.id; - const matchPayload = { - refereeID: referee.id, - player1ID: player1.id, - score1: 0, - player2ID: player2.id, - score2: 0, - country: this.formData.country, - city: this.formData.city, - weapon: this.formData.weapon, - date: new Date(this.formData.date), - state: MatchState.NOT_STARTED, - }; + this.refereeService + .getOrCreateByName( + this.formData.refereeFirstName, + this.formData.refereeName, + this.formData.refereelevel + ) + .subscribe({ + next: (respRef) => { + if (respRef && respRef.data) { + this.formData.refereeId = respRef.data.id; - this.matchService.create(matchPayload).subscribe(() => { - this.router.navigate(['/matches']); - }); + const matchPayload = { + refereeID: respRef.data.id, + player1ID: response.data.id, + score1: 0, + player2ID: resp2.data.id, + score2: 0, + country: this.formData.country, + city: this.formData.city, + weapon: this.formData.weapon, + date: new Date(this.formData.date), + state: MatchState.NOT_STARTED, + }; + + this.matchService.create(matchPayload).subscribe({ + next: () => { + this.router.navigate(['/matches']); + }, + error: () => { + console.error( + 'Erreur lors de la crĂ©ation du match' + ); + }, + }); + } else { + console.warn( + 'Erreur lors du chargement du referee' + ); + } + }, + error: () => { + console.warn( + "Erreur lors de l'appel getOrCreateReferee" + ); + }, + }); + } else { + console.warn('Erreur lors du chargement du player 2'); + } + }, + error: () => { + console.warn("Erreur lors de l'appel getOrCreatePlayer 2"); + }, }); - }); + } else { + console.warn('Erreur lors du chargement du player 1'); + } + }, + error: () => { + console.warn("Erreur lors de l'appel getOrCreatePlayer 1"); + }, }); } } diff --git a/FencerJudgeFront/src/app/components/match/matches-id/matches-id.component.ts b/FencerJudgeFront/src/app/components/match/matches-id/matches-id.component.ts index c0ff55a..483aaa1 100644 --- a/FencerJudgeFront/src/app/components/match/matches-id/matches-id.component.ts +++ b/FencerJudgeFront/src/app/components/match/matches-id/matches-id.component.ts @@ -36,20 +36,29 @@ export class MatchesIdComponent implements OnInit { ngOnInit(): void { const id = Number(this.route.snapshot.paramMap.get('id')); - this.matchService.getMatchById(id).subscribe((match) => { + this.matchService.getMatchById(id).subscribe((response) => { + const match = response; if (match) { this.match = match; - this.playerService - .getPlayerById(match.player1ID) - .subscribe((p1) => (this.player1 = p1!)); - this.playerService - .getPlayerById(match.player2ID) - .subscribe((p2) => (this.player2 = p2!)); + this.playerService.getPlayerById(match.player1ID).subscribe((res1) => { + this.player1 = res1.data; + }); + + this.playerService.getPlayerById(match.player2ID).subscribe((res2) => { + this.player2 = res2.data; + }); this.refereeService .getRefereeById(match.refereeID) - .subscribe((ref) => (this.referee = ref!)); + .subscribe((refResponse) => { + if (refResponse && refResponse.data) { + this.referee = refResponse.data; + } else { + console.warn('Erreur ou referee non trouvĂ©'); + this.referee = undefined; + } + }); } }); } @@ -114,20 +123,18 @@ export class MatchesIdComponent implements OnInit { incrementScore(player: 1 | 2): void { if (!this.match) return; - // IncrĂ©mente le score du joueur if (player === 1) { this.match.score1 += 1; } else { this.match.score2 += 1; } - // Change l'Ă©tat en fonction des scores if ( this.match.score1 === 1 && this.match.score2 === 0 && this.match.state === MatchState.NOT_STARTED ) { - this.match.state = MatchState.ONGOING; // passage Ă  l'Ă©tat 1 (en cours) + this.match.state = MatchState.ONGOING; } else if ( this.match.score2 === 1 && this.match.score1 === 0 && @@ -136,9 +143,8 @@ export class MatchesIdComponent implements OnInit { this.match.state = MatchState.ONGOING; } - // Si l'un des scores arrive Ă  15, on considĂšre le match terminĂ© if (this.match.score1 >= 15 || this.match.score2 >= 15) { - this.match.state = MatchState.OVER; // passage Ă  l'Ă©tat 2 (terminĂ©) + this.match.state = MatchState.OVER; } this.updateScores(); diff --git a/FencerJudgeFront/src/app/components/match/matches/matches.component.ts b/FencerJudgeFront/src/app/components/match/matches/matches.component.ts index a1cf3dc..8e34052 100644 --- a/FencerJudgeFront/src/app/components/match/matches/matches.component.ts +++ b/FencerJudgeFront/src/app/components/match/matches/matches.component.ts @@ -25,13 +25,34 @@ export class MatchesComponent { ) {} ngOnInit(): void { - this.playerService.getPlayers().subscribe((players) => { - // construire la map ID -> joueur - players.forEach((player) => this.playersMap.set(player.id, player)); + this.playerService.getPlayers().subscribe({ + next: (response) => { + if (response && response.data) { + response.data.forEach((player) => + this.playersMap.set(player.id, player) + ); + } else { + console.warn( + '[MatchesComponent] Erreur lors du chargement des joueurs' + ); + } + }, + error: () => + console.warn("[MatchesComponent] Erreur lors de l'appel getPlayers"), }); - this.matchesService.getMatches().subscribe((data) => { - this.matches = data; + this.matchesService.getMatches().subscribe({ + next: (response) => { + if (response) { + this.matches = response; + } else { + console.warn( + '[MatchesComponent] Erreur lors du chargement des matchs' + ); + } + }, + error: () => + console.warn("[MatchesComponent] Erreur lors de l'appel getMatches"), }); } diff --git a/FencerJudgeFront/src/app/interfaces/models/api.model.ts b/FencerJudgeFront/src/app/interfaces/models/api.model.ts new file mode 100644 index 0000000..ace0066 --- /dev/null +++ b/FencerJudgeFront/src/app/interfaces/models/api.model.ts @@ -0,0 +1,5 @@ +export interface ApiResponse { + data: T; + status?: string; + message?: string; +} diff --git a/FencerJudgeFront/src/app/services/api/api.service.spec.ts b/FencerJudgeFront/src/app/services/api/api.service.spec.ts new file mode 100644 index 0000000..c0310ae --- /dev/null +++ b/FencerJudgeFront/src/app/services/api/api.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { ApiService } from './api.service'; + +describe('ApiService', () => { + let service: ApiService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(ApiService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/FencerJudgeFront/src/app/services/api/api.service.ts b/FencerJudgeFront/src/app/services/api/api.service.ts new file mode 100644 index 0000000..af70f6b --- /dev/null +++ b/FencerJudgeFront/src/app/services/api/api.service.ts @@ -0,0 +1,56 @@ +import { Injectable } from '@angular/core'; +import { HttpClient, HttpErrorResponse } from '@angular/common/http'; +import { Observable, throwError } from 'rxjs'; +import { catchError } from 'rxjs/operators'; +import { ApiResponse } from '@interfaces/models/api.model'; // Assure-toi que ce fichier et cette interface existent + +@Injectable({ + providedIn: 'root', +}) +export class ApiService { + private baseUrl = 'http://localhost:8080'; + + constructor(private http: HttpClient) {} + + get(endpoint: string): Observable { + console.log( + `ApiService: Making GET request to ${this.baseUrl}/${endpoint}` + ); + return this.http + .get(`${this.baseUrl}/${endpoint}`) + .pipe(catchError(this.handleError)); + } + + post(endpoint: string, body: any): Observable> { + return this.http + .post>(`${this.baseUrl}/${endpoint}`, body) + .pipe(catchError(this.handleError)); + } + + put(endpoint: string, body: any): Observable> { + return this.http + .put>(`${this.baseUrl}/${endpoint}`, body) + .pipe(catchError(this.handleError)); + } + + delete(endpoint: string): Observable> { + return this.http + .delete>(`${this.baseUrl}/${endpoint}`) + .pipe(catchError(this.handleError)); + } + + private handleError(error: HttpErrorResponse): Observable { + let errorMessage = 'Une erreur est survenue'; + + if (error.error instanceof ErrorEvent) { + // Erreur cĂŽtĂ© client ou rĂ©seau + errorMessage = `Erreur cĂŽtĂ© client : ${error.error.message}`; + } else { + // Erreur cĂŽtĂ© serveur + errorMessage = `Erreur cĂŽtĂ© serveur : ${error.status} - ${error.message}`; + } + + console.error('ApiService: Error occurred:', errorMessage); // Debug + return throwError(() => new Error(errorMessage)); + } +} diff --git a/FencerJudgeFront/src/app/services/matches/matches.service.ts b/FencerJudgeFront/src/app/services/matches/matches.service.ts index bbffd36..fbd909f 100644 --- a/FencerJudgeFront/src/app/services/matches/matches.service.ts +++ b/FencerJudgeFront/src/app/services/matches/matches.service.ts @@ -1,107 +1,42 @@ import { Injectable } from '@angular/core'; -import { Observable, of } from 'rxjs'; +import { Observable } from 'rxjs'; import { Matches, MatchState } from '@interfaces/matches'; +import { ApiService } from '@services/api/api.service'; // Assure-toi que le chemin est bon +import { ApiResponse } from '@interfaces/models/api.model'; // Assure-toi que cette interface existe bien @Injectable({ providedIn: 'root' }) export class MatchesService { - private matches: Matches[] = [ - { - id: 1, - refereeID: 10, - player1ID: 1, - score1: 15, - player2ID: 2, - score2: 13, - country: 'France', - city: 'Paris', - weapon: 'Fleuret', - date: new Date('2025-06-10T14:00:00'), - state: MatchState.OVER, - }, - { - id: 2, - refereeID: 11, - player1ID: 3, - score1: 5, - player2ID: 4, - score2: 7, - country: 'France', - city: 'Lyon', - weapon: 'ÉpĂ©e', - date: new Date('2025-06-15T10:00:00'), - state: MatchState.ONGOING, - }, - { - id: 3, - refereeID: 12, - player1ID: 5, - score1: 0, - player2ID: 6, - score2: 0, - country: 'Belgique', - city: 'Bruxelles', - weapon: 'Sabre', - date: new Date('2025-06-20T16:30:00'), - state: MatchState.NOT_STARTED, - }, - ]; - private nextMatchId = Math.max(...this.matches.map((m) => m.id)) + 1; + private readonly endpoint = 'matches'; - constructor() { - console.log('[MatchesService] Initial matches loaded:', this.matches); + constructor(private api: ApiService) { + console.log('[MatchesService] Connected to API'); } getMatches(): Observable { - console.log('[MatchesService] Fetching all matches'); - return of(this.matches); + console.log('[MatchesService] GET all matches'); + return this.api.get(this.endpoint); } - getMatchById(id: number): Observable { - const match = this.matches.find((m) => m.id === id); - console.log(`[MatchesService] Fetching match ID: ${id}`, match); - return of(match); + getMatchById(id: number): Observable { + console.log(`[MatchesService] GET match ID: ${id}`); + return this.api.get(`${this.endpoint}/${id}`); } - // PrĂ©paration future pour WebSocket - connectToMatchUpdatesWebSocket(): void { - console.log( - '[MatchesService] WebSocket connection placeholder initialized' - ); - // ici tu pourrais plus tard faire : this.socket = new WebSocket('ws://...') etc. + create(match: Omit): Observable> { + console.log('[MatchesService] POST new match', match); + return this.api.post(this.endpoint, match); } - updateMatch(id: number, updatedData: Partial): void { - const match = this.matches.find((m) => m.id === id); - if (match) { - Object.assign(match, updatedData); - console.log(`[MatchesService] Match ${id} mis Ă  jour :`, match); - } else { - console.warn(`[MatchesService] Match ${id} introuvable`); - } + updateMatch( + id: number, + updatedData: Partial + ): Observable> { + console.log(`[MatchesService] PUT update match ID: ${id}`, updatedData); + return this.api.put(`${this.endpoint}/${id}`, updatedData); } - create(match: Omit): Observable { - const newMatch: Matches = { - ...match, - id: this.nextMatchId++, - }; - - this.matches.push(newMatch); - console.log('[MatchesService] Match created:', newMatch); - return of(newMatch); - } - - deleteMatch(id: number): Observable { - const index = this.matches.findIndex((m) => m.id === id); - - if (index !== -1) { - const deletedMatch = this.matches.splice(index, 1)[0]; - console.log(`[MatchesService] Match ${id} supprimĂ© :`, deletedMatch); - - return of(true); - } else { - console.warn(`[MatchesService] Match ${id} introuvable pour suppression`); - return of(false); - } + deleteMatch(id: number): Observable> { + console.log(`[MatchesService] DELETE match ID: ${id}`); + return this.api.delete(`${this.endpoint}/${id}`); } } diff --git a/FencerJudgeFront/src/app/services/player/player.service.ts b/FencerJudgeFront/src/app/services/player/player.service.ts index c5f3a1b..d98f2ad 100644 --- a/FencerJudgeFront/src/app/services/player/player.service.ts +++ b/FencerJudgeFront/src/app/services/player/player.service.ts @@ -1,6 +1,7 @@ import { Injectable } from '@angular/core'; import { Observable, of } from 'rxjs'; import { Player } from '@interfaces/player'; +import { ApiResponse } from '@interfaces/models/api.model'; @Injectable({ providedIn: 'root' }) export class PlayerService { @@ -17,22 +18,30 @@ export class PlayerService { console.log('[PlayerService] Initial players loaded:', this.players); } - getPlayers(): Observable { - console.log('[PlayerService] Fetching all players'); - return of(this.players); + getPlayers(): Observable> { + return of({ + success: true, + data: this.players, + message: 'Liste des joueurs rĂ©cupĂ©rĂ©e avec succĂšs.', + }); } - getPlayerById(id: number): Observable { + getPlayerById(id: number): Observable> { const player = this.players.find((p) => p.id === id); - console.log(`[PlayerService] Fetching player ID: ${id}`, player); - return of(player); + return of({ + success: !!player, + data: player, + message: player + ? `Joueur avec ID ${id} trouvĂ©.` + : `Aucun joueur trouvĂ© avec ID ${id}.`, + }); } getOrCreateByName( firstName: string, name: string, club: string = 'Inconnu' - ): Observable { + ): Observable> { const existing = this.players.find( (p) => p.firstName.toLowerCase() === firstName.toLowerCase() && @@ -40,8 +49,11 @@ export class PlayerService { ); if (existing) { - console.log('[PlayerService] Joueur trouvĂ© :', existing); - return of(existing); + return of({ + success: true, + data: existing, + message: 'Joueur existant trouvĂ©.', + }); } const newPlayer: Player = { @@ -55,7 +67,10 @@ export class PlayerService { }; this.players.push(newPlayer); - console.log('[PlayerService] Nouveau joueur créé :', newPlayer); - return of(newPlayer); + return of({ + success: true, + data: newPlayer, + message: 'Nouveau joueur créé.', + }); } } diff --git a/FencerJudgeFront/src/app/services/referee/referee.service.ts b/FencerJudgeFront/src/app/services/referee/referee.service.ts index 8d26a06..71e8362 100644 --- a/FencerJudgeFront/src/app/services/referee/referee.service.ts +++ b/FencerJudgeFront/src/app/services/referee/referee.service.ts @@ -1,6 +1,7 @@ import { Injectable } from '@angular/core'; import { Observable, of } from 'rxjs'; import { Referee, RefereeLevel } from '@interfaces/referee'; +import { ApiResponse } from '@interfaces/models/api.model'; @Injectable({ providedIn: 'root' }) export class RefereeService { @@ -29,22 +30,30 @@ export class RefereeService { console.log('[RefereeService] Initial referees loaded:', this.referees); } - getReferees(): Observable { - console.log('[RefereeService] Fetching all referees'); - return of(this.referees); + getReferees(): Observable> { + return of({ + success: true, + data: this.referees, + message: 'Liste des arbitres rĂ©cupĂ©rĂ©e avec succĂšs.', + }); } - getRefereeById(id: number): Observable { + getRefereeById(id: number): Observable> { const ref = this.referees.find((r) => r.id === id); - console.log(`[RefereeService] Fetching referee ID: ${id}`, ref); - return of(ref); + return of({ + success: !!ref, + data: ref, + message: ref + ? `Arbitre avec ID ${id} trouvĂ©.` + : `Aucun arbitre trouvĂ© avec ID ${id}.`, + }); } getOrCreateByName( firstName: string, name: string, level: RefereeLevel - ): Observable { + ): Observable> { const existing = this.referees.find( (r) => r.firstName.toLowerCase() === firstName.toLowerCase() && @@ -52,11 +61,14 @@ export class RefereeService { ); if (existing) { - console.log('[RefereeService] Arbitre trouvĂ© :', existing); - return of(existing); + return of({ + success: true, + data: existing, + message: 'Arbitre existant trouvĂ©.', + }); } - const newPlayer: Referee = { + const newRef: Referee = { id: this.referees.length > 0 ? Math.max(...this.referees.map((r) => r.id)) + 1 @@ -66,8 +78,11 @@ export class RefereeService { level, }; - this.referees.push(newPlayer); - console.log('[RefereeService] Nouveau arbitre créé :', newPlayer); - return of(newPlayer); + this.referees.push(newRef); + return of({ + success: true, + data: newRef, + message: 'Nouvel arbitre créé.', + }); } } diff --git a/db.lock.db b/db.lock.db new file mode 100644 index 0000000..528aa42 --- /dev/null +++ b/db.lock.db @@ -0,0 +1,6 @@ +#FileLock +#Tue Jun 03 12:13:33 CEST 2025 +hostName=ExostFlash +id=1973548313a73b09e15c7ad67f0a2a90c7d955c292d +method=file +server=192.167.2.100\:58591 diff --git a/db.mv.db b/db.mv.db index 13d536ba314fcc2675be1669753f4b8bbb2700ce..da25e4f66976a1bc8a3f40396c60b5f80b442685 100644 GIT binary patch literal 36864 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 Z`@g^cXZ!EaPP(`M*EMkee@y%T{{WeYKN$c3 literal 32768 zcmeHP%WvDr8K)F0QT#~ay!O4zXp=a0wF=4MO9I+Nmg!iHtav3lj~1vgB!^jdWyzK0 zY}`w?*hT+<0?nbvqAAcr554rz!(xj?FTHdR{acEnm-ge3lqktk=)`#-J3g2kavtCO zW;psDznPVSl(iZy`_qEvU+bGcCk0UugslC^Zu8TE$!Oeo0wc7-Dh*J>r8WIu8 z%UV9`w%S{`Q$X1Vq`kA*YI^zI2I<(Jkaj`WRo8Y9n&j9YLx3T`5MT%}1Q-Gg0fqoW zfFZyTUU5ffZ)|zeMy|(+mNI07HNwzz|>vFa#I^3;~7!Lx3T`5O^^N1VMf) z+aOJF1#IK{WT&8KxA4P)Bp?B9whKtjwp*=EK_WKr0t7`vik$6iZjl1AZR7w8pd0+R zvQ6^PqXTqWc0tsM%OBPsa0n*q6N_B(ESDBVThSb>r$vIW?ut@c5^ULau$7iX!6JFV zPD`>VW6LFJ3F|h}^Hv%OI&!h2r4f<{5mg0l^2kw$kVd*B$hNDdWn||Cs=!22X6owepsdRMv@OA6GG#zcmKx|Al^B7&sJX;+ptLC)(R zw%Ye|pGdi_2Rofso8%lTSKVziZsW!-S;8G`?rjs6=f&WY9T*TFv`mC!T|VQ`3Emo-`l=KwoKDbr=;`rB?Cw#l=R;su7p8Nz*15y;1upPLWm8 z!8A9=rQe(WaUwO#C6Y%bQb&)ak55jWIC*OJ^cn5~fBA0RF!@r=D4NE`;(EEV#7juZ z@R^w}6aG_vy>8U_@-kn&W%9<|a^0-+nR+q9*NYXCpCedP1O$tw;|eO#;OZ zyP}50*DA$(MokgGnZxC+>h{d~q{nVg@833Vg(go4kwZ$5Q9L`Qr9pnkAF)M3V0^9fox z*lb$Ld=Hv#?%#Cq_0}`7+D7^AFjtJ*Mg`U_!@Oe{RbJqW)g>NvCsvOCaHbd_S2sRh zH>#zf6>tMyz^ZH(YbJlEY_9MkU-QVFW79Y3aY9>xWIW)Rl(CK{&&cZzX z_6hT}3)vHw??ah|!HoWRy;vzPmrF&nJbDQX@&;pLmg(X-IX`jwKnvsYz&!4Cm`>!0 zGccV#JCx~k(5bwf0aavf=2yvuWP;;zUvo#+*8J;N-_q;E)l*Yb)6;KoT;i|O*N-er zMRf>HEU)4A=FSh7+Za@nsee4a_W7eP-{eFoHF@=;Wb)>6gY33RM_Q@uwVGkBe?qR` z{brU!a%$?*Tr#op&GGGT<_U^dmmH@qB~B(aUV~bkx>D(Qm8Fiy%r_aU)v2+$%5`kD z35?v|PX2NNMs9k>SNhJLJI|%2#}*C7BsDWOm)}6@M9e;&jG6D$*hWLqW@FZNI#z9G zV%ByxR&D2EFO6FwMR3=38&S^X_}^f52bJaH%)O7Egp9U+T?q!f+v+ zZ^f+b?O3(Fv%1*ceMlM&vQt?kjr*HF+a*)~dVKBcum1Qhr>LoEo=+x&>GSTm8pBNQ z#T4)Tag}vD`@sRk<72jbAy&(o_$_nXk+){gA5FZ?rNBLvlaA8=*uU8H8KMa!`xnK( zsPvUW|NHkEz1QjgJpFGUZQ?DG+nA;ourEDxcPdPP(b>HR!}A<{-r6Qj6V~}1a2UW2 zm>s`p2U`u26J}@@KOt(-S16j$Px|@D);5^9(98*%-fcRqO6z`3>`zMlNknBXY-nDY zg5MSjcIyO=je>4vKP7vG&{LItfE$I-TctYSc`sL>R%DT7@=Trg*V7ERPc$zEp@lh;8=b|rM zFmFazrbhap4{|fu~r>HZ~uS zHR?6?oW&1e>lk>Ay@#ui81NrEy5~QZG{=ehk5xUp+uYrOjb))b*-sWb;pDY&&%>-z z*Ro@gY|FNHw?VzyD~RszLotJ0Y~aH79NM9n@{pL?7%8cjTa=`{6!CJa&OxNqsFbQJ zMWhsZQtAPi`93KD+5s?K*oHR(OX5K(NJw;|Qfkscq|~XDmZV3eL|rU#ASLdTQXG;} z9wQ|kaY4fQ=!k2|L8Qb~N?Ea^Qi=(2I*3Qe*b(J0;Wed(2_&mJ3KJr76%0!K=Nz~2 z2vmpbT;ke~IBxQD?#S{N{?$|Qy|ky|!xdC~$bX%i_|sXqa$IW1q*wNjA;1t|2rvW~ z0?&%TK>R%tNbl~VhtQ9$Ba99!k{+#RGHBl!_=)#4ITe1^!LsUw|6TZ@_jC^Ug^egi zn$uIx96$W;|6)T#JlfPV-h1)?2NUoEJ{3#34Ygb=ys=+vbra(bf?N@${8d$V z!KR5w`HHB!zYPr>D^SW+DrJwc(=#2k9(yOW9Yi}em1oHIvU;DlMKy7c))P_1b)jo8 z*nw>Q#@B%m1dAtFWBLpRtKVPeD>* z`TsW<|DWw6c-B1>&w;tl`2V>>*hTSD`2Rm){D0UP!1(_@g_ZIDBRd7cZ3?j%+X}P) z8ULU0|ND0zF#iA8jZv?>{r`o}|Gx^PAdej6T|IITT>JR{2IK#a*u3zn^Z&nO{C|Io z%Bb;s)%pMbW&Hm!JB@mq9bPy7{~5;r4=MJH{~w&cL7Dj9Y5srW?`QvVj62q)NOkE? zPAvM<9|l<_KHGmkmWj{yr)R{3ERzAc*vQjjqHKR~9uF~2B80Mi87FR!NbdxUlYVB= zFyqAIvW*aPB&+QJVmj2{4TQHL#)BA}@IiGZT+Ck}(6z7`c5j^-IcH?o(4 z@&6hB|5*vFtUN=2A;1t|2s{-6LHs|Azjx#RBohC3Wvr`MeVVBGlgfnge|LcM?8X1l z5dWWUjP%r(=$1oW?uGwdKm70O6-Ycx$^yjylRr)V0*>t3N4X72{)B0{FI>y*GSf+* zi~Icf{cHxGnGT0G_LT8qX1cEm??W;f(qu_*;*l=lB%*=qg11T3ukU~wIDE?szh>wE zMGmZdZO{K>@qhS{VDbOp2rCx0!KFH$#EdI~V|BG=F f;>+UyzwG#bVE>2qch~-Rd-lH~$d0Z(C;R{ZX(yPK