diff --git a/frontend/angular.json b/frontend/angular.json index b3f0208..5367aa1 100755 --- a/frontend/angular.json +++ b/frontend/angular.json @@ -56,7 +56,13 @@ "development": { "optimization": false, "extractLicenses": false, - "sourceMap": true + "sourceMap": true, + "fileReplacements": [ + { + "replace": "src/environments/environment.ts", + "with": "src/environments/environment.development.ts" + } + ] } }, "defaultConfiguration": "production" diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index f2993df..6eb0dc6 100755 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -62,10 +62,10 @@ importers: devDependencies: '@angular-devkit/build-angular': specifier: ^19.1.7 - version: 19.1.8(@angular/compiler-cli@19.1.7(@angular/compiler@19.1.7(@angular/core@19.1.7(rxjs@7.8.2)(zone.js@0.15.0)))(typescript@5.7.3))(@angular/compiler@19.1.7(@angular/core@19.1.7(rxjs@7.8.2)(zone.js@0.15.0)))(@types/node@22.13.5)(chokidar@4.0.3)(jiti@2.4.2)(karma@6.4.4)(lightningcss@1.29.1)(tailwindcss@4.0.9)(typescript@5.7.3)(vite@6.0.11(@types/node@22.13.5)(jiti@2.4.2)(less@4.2.1)(lightningcss@1.29.1)(sass@1.83.1)(terser@5.37.0)) + version: 19.1.8(@angular/compiler-cli@19.1.7(@angular/compiler@19.1.7(@angular/core@19.1.7(rxjs@7.8.2)(zone.js@0.15.0)))(typescript@5.7.3))(@angular/compiler@19.1.7(@angular/core@19.1.7(rxjs@7.8.2)(zone.js@0.15.0)))(@types/node@22.13.14)(chokidar@4.0.3)(jiti@2.4.2)(karma@6.4.4)(lightningcss@1.29.1)(tailwindcss@4.0.9)(typescript@5.7.3)(vite@6.0.11(@types/node@22.13.14)(jiti@2.4.2)(less@4.2.1)(lightningcss@1.29.1)(sass@1.83.1)(terser@5.37.0)) '@angular/cli': specifier: ^19.1.7 - version: 19.1.8(@types/node@22.13.5)(chokidar@4.0.3) + version: 19.1.8(@types/node@22.13.14)(chokidar@4.0.3) '@angular/compiler-cli': specifier: ^19.1.0 version: 19.1.7(@angular/compiler@19.1.7(@angular/core@19.1.7(rxjs@7.8.2)(zone.js@0.15.0)))(typescript@5.7.3) @@ -1720,8 +1720,8 @@ packages: '@types/node-forge@1.3.11': resolution: {integrity: sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==} - '@types/node@22.13.5': - resolution: {integrity: sha512-+lTU0PxZXn0Dr1NBtC7Y8cR21AJr87dLLU953CWA6pMxxv/UDc7jYAY90upcrie1nRcD6XNG5HOYEDtgW5TxAg==} + '@types/node@22.13.14': + resolution: {integrity: sha512-Zs/Ollc1SJ8nKUAgc7ivOEdIBM8JAKgrqqUYi2J997JuKO7/tpQC+WCetQ1sypiKCQWHdvdg9wBNpUPEWZae7w==} '@types/qs@6.9.18': resolution: {integrity: sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA==} @@ -4443,13 +4443,13 @@ snapshots: transitivePeerDependencies: - chokidar - '@angular-devkit/build-angular@19.1.8(@angular/compiler-cli@19.1.7(@angular/compiler@19.1.7(@angular/core@19.1.7(rxjs@7.8.2)(zone.js@0.15.0)))(typescript@5.7.3))(@angular/compiler@19.1.7(@angular/core@19.1.7(rxjs@7.8.2)(zone.js@0.15.0)))(@types/node@22.13.5)(chokidar@4.0.3)(jiti@2.4.2)(karma@6.4.4)(lightningcss@1.29.1)(tailwindcss@4.0.9)(typescript@5.7.3)(vite@6.0.11(@types/node@22.13.5)(jiti@2.4.2)(less@4.2.1)(lightningcss@1.29.1)(sass@1.83.1)(terser@5.37.0))': + '@angular-devkit/build-angular@19.1.8(@angular/compiler-cli@19.1.7(@angular/compiler@19.1.7(@angular/core@19.1.7(rxjs@7.8.2)(zone.js@0.15.0)))(typescript@5.7.3))(@angular/compiler@19.1.7(@angular/core@19.1.7(rxjs@7.8.2)(zone.js@0.15.0)))(@types/node@22.13.14)(chokidar@4.0.3)(jiti@2.4.2)(karma@6.4.4)(lightningcss@1.29.1)(tailwindcss@4.0.9)(typescript@5.7.3)(vite@6.0.11(@types/node@22.13.14)(jiti@2.4.2)(less@4.2.1)(lightningcss@1.29.1)(sass@1.83.1)(terser@5.37.0))': dependencies: '@ampproject/remapping': 2.3.0 '@angular-devkit/architect': 0.1901.8(chokidar@4.0.3) '@angular-devkit/build-webpack': 0.1901.8(chokidar@4.0.3)(webpack-dev-server@5.2.0(webpack@5.97.1))(webpack@5.97.1(esbuild@0.24.2)) '@angular-devkit/core': 19.1.8(chokidar@4.0.3) - '@angular/build': 19.1.8(@angular/compiler-cli@19.1.7(@angular/compiler@19.1.7(@angular/core@19.1.7(rxjs@7.8.2)(zone.js@0.15.0)))(typescript@5.7.3))(@angular/compiler@19.1.7(@angular/core@19.1.7(rxjs@7.8.2)(zone.js@0.15.0)))(@types/node@22.13.5)(chokidar@4.0.3)(jiti@2.4.2)(less@4.2.1)(lightningcss@1.29.1)(postcss@8.4.49)(tailwindcss@4.0.9)(terser@5.37.0)(typescript@5.7.3) + '@angular/build': 19.1.8(@angular/compiler-cli@19.1.7(@angular/compiler@19.1.7(@angular/core@19.1.7(rxjs@7.8.2)(zone.js@0.15.0)))(typescript@5.7.3))(@angular/compiler@19.1.7(@angular/core@19.1.7(rxjs@7.8.2)(zone.js@0.15.0)))(@types/node@22.13.14)(chokidar@4.0.3)(jiti@2.4.2)(less@4.2.1)(lightningcss@1.29.1)(postcss@8.4.49)(tailwindcss@4.0.9)(terser@5.37.0)(typescript@5.7.3) '@angular/compiler-cli': 19.1.7(@angular/compiler@19.1.7(@angular/core@19.1.7(rxjs@7.8.2)(zone.js@0.15.0)))(typescript@5.7.3) '@babel/core': 7.26.0 '@babel/generator': 7.26.3 @@ -4462,7 +4462,7 @@ snapshots: '@babel/runtime': 7.26.0 '@discoveryjs/json-ext': 0.6.3 '@ngtools/webpack': 19.1.8(@angular/compiler-cli@19.1.7(@angular/compiler@19.1.7(@angular/core@19.1.7(rxjs@7.8.2)(zone.js@0.15.0)))(typescript@5.7.3))(typescript@5.7.3)(webpack@5.97.1(esbuild@0.24.2)) - '@vitejs/plugin-basic-ssl': 1.2.0(vite@6.0.11(@types/node@22.13.5)(jiti@2.4.2)(less@4.2.1)(lightningcss@1.29.1)(sass@1.83.1)(terser@5.37.0)) + '@vitejs/plugin-basic-ssl': 1.2.0(vite@6.0.11(@types/node@22.13.14)(jiti@2.4.2)(less@4.2.1)(lightningcss@1.29.1)(sass@1.83.1)(terser@5.37.0)) ansi-colors: 4.1.3 autoprefixer: 10.4.20(postcss@8.4.49) babel-loader: 9.2.1(@babel/core@7.26.0)(webpack@5.97.1(esbuild@0.24.2)) @@ -4564,7 +4564,7 @@ snapshots: '@angular/core': 19.1.7(rxjs@7.8.2)(zone.js@0.15.0) tslib: 2.8.1 - '@angular/build@19.1.8(@angular/compiler-cli@19.1.7(@angular/compiler@19.1.7(@angular/core@19.1.7(rxjs@7.8.2)(zone.js@0.15.0)))(typescript@5.7.3))(@angular/compiler@19.1.7(@angular/core@19.1.7(rxjs@7.8.2)(zone.js@0.15.0)))(@types/node@22.13.5)(chokidar@4.0.3)(jiti@2.4.2)(less@4.2.1)(lightningcss@1.29.1)(postcss@8.4.49)(tailwindcss@4.0.9)(terser@5.37.0)(typescript@5.7.3)': + '@angular/build@19.1.8(@angular/compiler-cli@19.1.7(@angular/compiler@19.1.7(@angular/core@19.1.7(rxjs@7.8.2)(zone.js@0.15.0)))(typescript@5.7.3))(@angular/compiler@19.1.7(@angular/core@19.1.7(rxjs@7.8.2)(zone.js@0.15.0)))(@types/node@22.13.14)(chokidar@4.0.3)(jiti@2.4.2)(less@4.2.1)(lightningcss@1.29.1)(postcss@8.4.49)(tailwindcss@4.0.9)(terser@5.37.0)(typescript@5.7.3)': dependencies: '@ampproject/remapping': 2.3.0 '@angular-devkit/architect': 0.1901.8(chokidar@4.0.3) @@ -4575,8 +4575,8 @@ snapshots: '@babel/helper-annotate-as-pure': 7.25.9 '@babel/helper-split-export-declaration': 7.24.7 '@babel/plugin-syntax-import-attributes': 7.26.0(@babel/core@7.26.0) - '@inquirer/confirm': 5.1.1(@types/node@22.13.5) - '@vitejs/plugin-basic-ssl': 1.2.0(vite@6.0.11(@types/node@22.13.5)(jiti@2.4.2)(less@4.2.1)(lightningcss@1.29.1)(sass@1.83.1)(terser@5.37.0)) + '@inquirer/confirm': 5.1.1(@types/node@22.13.14) + '@vitejs/plugin-basic-ssl': 1.2.0(vite@6.0.11(@types/node@22.13.14)(jiti@2.4.2)(less@4.2.1)(lightningcss@1.29.1)(sass@1.83.1)(terser@5.37.0)) beasties: 0.2.0 browserslist: 4.24.4 esbuild: 0.24.2 @@ -4593,7 +4593,7 @@ snapshots: sass: 1.83.1 semver: 7.6.3 typescript: 5.7.3 - vite: 6.0.11(@types/node@22.13.5)(jiti@2.4.2)(less@4.2.1)(lightningcss@1.29.1)(sass@1.83.1)(terser@5.37.0) + vite: 6.0.11(@types/node@22.13.14)(jiti@2.4.2)(less@4.2.1)(lightningcss@1.29.1)(sass@1.83.1)(terser@5.37.0) watchpack: 2.4.2 optionalDependencies: less: 4.2.1 @@ -4622,13 +4622,13 @@ snapshots: optionalDependencies: parse5: 7.2.1 - '@angular/cli@19.1.8(@types/node@22.13.5)(chokidar@4.0.3)': + '@angular/cli@19.1.8(@types/node@22.13.14)(chokidar@4.0.3)': dependencies: '@angular-devkit/architect': 0.1901.8(chokidar@4.0.3) '@angular-devkit/core': 19.1.8(chokidar@4.0.3) '@angular-devkit/schematics': 19.1.8(chokidar@4.0.3) - '@inquirer/prompts': 7.2.1(@types/node@22.13.5) - '@listr2/prompt-adapter-inquirer': 2.0.18(@inquirer/prompts@7.2.1(@types/node@22.13.5)) + '@inquirer/prompts': 7.2.1(@types/node@22.13.14) + '@listr2/prompt-adapter-inquirer': 2.0.18(@inquirer/prompts@7.2.1(@types/node@22.13.14)) '@schematics/angular': 19.1.8(chokidar@4.0.3) '@yarnpkg/lockfile': 1.1.0 ini: 5.0.0 @@ -5493,33 +5493,33 @@ snapshots: '@esbuild/win32-x64@0.24.2': optional: true - '@inquirer/checkbox@4.1.2(@types/node@22.13.5)': + '@inquirer/checkbox@4.1.2(@types/node@22.13.14)': dependencies: - '@inquirer/core': 10.1.7(@types/node@22.13.5) + '@inquirer/core': 10.1.7(@types/node@22.13.14) '@inquirer/figures': 1.0.10 - '@inquirer/type': 3.0.4(@types/node@22.13.5) + '@inquirer/type': 3.0.4(@types/node@22.13.14) ansi-escapes: 4.3.2 yoctocolors-cjs: 2.1.2 optionalDependencies: - '@types/node': 22.13.5 + '@types/node': 22.13.14 - '@inquirer/confirm@5.1.1(@types/node@22.13.5)': + '@inquirer/confirm@5.1.1(@types/node@22.13.14)': dependencies: - '@inquirer/core': 10.1.7(@types/node@22.13.5) - '@inquirer/type': 3.0.4(@types/node@22.13.5) - '@types/node': 22.13.5 + '@inquirer/core': 10.1.7(@types/node@22.13.14) + '@inquirer/type': 3.0.4(@types/node@22.13.14) + '@types/node': 22.13.14 - '@inquirer/confirm@5.1.6(@types/node@22.13.5)': + '@inquirer/confirm@5.1.6(@types/node@22.13.14)': dependencies: - '@inquirer/core': 10.1.7(@types/node@22.13.5) - '@inquirer/type': 3.0.4(@types/node@22.13.5) + '@inquirer/core': 10.1.7(@types/node@22.13.14) + '@inquirer/type': 3.0.4(@types/node@22.13.14) optionalDependencies: - '@types/node': 22.13.5 + '@types/node': 22.13.14 - '@inquirer/core@10.1.7(@types/node@22.13.5)': + '@inquirer/core@10.1.7(@types/node@22.13.14)': dependencies: '@inquirer/figures': 1.0.10 - '@inquirer/type': 3.0.4(@types/node@22.13.5) + '@inquirer/type': 3.0.4(@types/node@22.13.14) ansi-escapes: 4.3.2 cli-width: 4.1.0 mute-stream: 2.0.0 @@ -5527,96 +5527,96 @@ snapshots: wrap-ansi: 6.2.0 yoctocolors-cjs: 2.1.2 optionalDependencies: - '@types/node': 22.13.5 + '@types/node': 22.13.14 - '@inquirer/editor@4.2.7(@types/node@22.13.5)': + '@inquirer/editor@4.2.7(@types/node@22.13.14)': dependencies: - '@inquirer/core': 10.1.7(@types/node@22.13.5) - '@inquirer/type': 3.0.4(@types/node@22.13.5) + '@inquirer/core': 10.1.7(@types/node@22.13.14) + '@inquirer/type': 3.0.4(@types/node@22.13.14) external-editor: 3.1.0 optionalDependencies: - '@types/node': 22.13.5 + '@types/node': 22.13.14 - '@inquirer/expand@4.0.9(@types/node@22.13.5)': + '@inquirer/expand@4.0.9(@types/node@22.13.14)': dependencies: - '@inquirer/core': 10.1.7(@types/node@22.13.5) - '@inquirer/type': 3.0.4(@types/node@22.13.5) + '@inquirer/core': 10.1.7(@types/node@22.13.14) + '@inquirer/type': 3.0.4(@types/node@22.13.14) yoctocolors-cjs: 2.1.2 optionalDependencies: - '@types/node': 22.13.5 + '@types/node': 22.13.14 '@inquirer/figures@1.0.10': {} - '@inquirer/input@4.1.6(@types/node@22.13.5)': + '@inquirer/input@4.1.6(@types/node@22.13.14)': dependencies: - '@inquirer/core': 10.1.7(@types/node@22.13.5) - '@inquirer/type': 3.0.4(@types/node@22.13.5) + '@inquirer/core': 10.1.7(@types/node@22.13.14) + '@inquirer/type': 3.0.4(@types/node@22.13.14) optionalDependencies: - '@types/node': 22.13.5 + '@types/node': 22.13.14 - '@inquirer/number@3.0.9(@types/node@22.13.5)': + '@inquirer/number@3.0.9(@types/node@22.13.14)': dependencies: - '@inquirer/core': 10.1.7(@types/node@22.13.5) - '@inquirer/type': 3.0.4(@types/node@22.13.5) + '@inquirer/core': 10.1.7(@types/node@22.13.14) + '@inquirer/type': 3.0.4(@types/node@22.13.14) optionalDependencies: - '@types/node': 22.13.5 + '@types/node': 22.13.14 - '@inquirer/password@4.0.9(@types/node@22.13.5)': + '@inquirer/password@4.0.9(@types/node@22.13.14)': dependencies: - '@inquirer/core': 10.1.7(@types/node@22.13.5) - '@inquirer/type': 3.0.4(@types/node@22.13.5) + '@inquirer/core': 10.1.7(@types/node@22.13.14) + '@inquirer/type': 3.0.4(@types/node@22.13.14) ansi-escapes: 4.3.2 optionalDependencies: - '@types/node': 22.13.5 + '@types/node': 22.13.14 - '@inquirer/prompts@7.2.1(@types/node@22.13.5)': + '@inquirer/prompts@7.2.1(@types/node@22.13.14)': dependencies: - '@inquirer/checkbox': 4.1.2(@types/node@22.13.5) - '@inquirer/confirm': 5.1.6(@types/node@22.13.5) - '@inquirer/editor': 4.2.7(@types/node@22.13.5) - '@inquirer/expand': 4.0.9(@types/node@22.13.5) - '@inquirer/input': 4.1.6(@types/node@22.13.5) - '@inquirer/number': 3.0.9(@types/node@22.13.5) - '@inquirer/password': 4.0.9(@types/node@22.13.5) - '@inquirer/rawlist': 4.0.9(@types/node@22.13.5) - '@inquirer/search': 3.0.9(@types/node@22.13.5) - '@inquirer/select': 4.0.9(@types/node@22.13.5) - '@types/node': 22.13.5 + '@inquirer/checkbox': 4.1.2(@types/node@22.13.14) + '@inquirer/confirm': 5.1.6(@types/node@22.13.14) + '@inquirer/editor': 4.2.7(@types/node@22.13.14) + '@inquirer/expand': 4.0.9(@types/node@22.13.14) + '@inquirer/input': 4.1.6(@types/node@22.13.14) + '@inquirer/number': 3.0.9(@types/node@22.13.14) + '@inquirer/password': 4.0.9(@types/node@22.13.14) + '@inquirer/rawlist': 4.0.9(@types/node@22.13.14) + '@inquirer/search': 3.0.9(@types/node@22.13.14) + '@inquirer/select': 4.0.9(@types/node@22.13.14) + '@types/node': 22.13.14 - '@inquirer/rawlist@4.0.9(@types/node@22.13.5)': + '@inquirer/rawlist@4.0.9(@types/node@22.13.14)': dependencies: - '@inquirer/core': 10.1.7(@types/node@22.13.5) - '@inquirer/type': 3.0.4(@types/node@22.13.5) + '@inquirer/core': 10.1.7(@types/node@22.13.14) + '@inquirer/type': 3.0.4(@types/node@22.13.14) yoctocolors-cjs: 2.1.2 optionalDependencies: - '@types/node': 22.13.5 + '@types/node': 22.13.14 - '@inquirer/search@3.0.9(@types/node@22.13.5)': + '@inquirer/search@3.0.9(@types/node@22.13.14)': dependencies: - '@inquirer/core': 10.1.7(@types/node@22.13.5) + '@inquirer/core': 10.1.7(@types/node@22.13.14) '@inquirer/figures': 1.0.10 - '@inquirer/type': 3.0.4(@types/node@22.13.5) + '@inquirer/type': 3.0.4(@types/node@22.13.14) yoctocolors-cjs: 2.1.2 optionalDependencies: - '@types/node': 22.13.5 + '@types/node': 22.13.14 - '@inquirer/select@4.0.9(@types/node@22.13.5)': + '@inquirer/select@4.0.9(@types/node@22.13.14)': dependencies: - '@inquirer/core': 10.1.7(@types/node@22.13.5) + '@inquirer/core': 10.1.7(@types/node@22.13.14) '@inquirer/figures': 1.0.10 - '@inquirer/type': 3.0.4(@types/node@22.13.5) + '@inquirer/type': 3.0.4(@types/node@22.13.14) ansi-escapes: 4.3.2 yoctocolors-cjs: 2.1.2 optionalDependencies: - '@types/node': 22.13.5 + '@types/node': 22.13.14 '@inquirer/type@1.5.5': dependencies: mute-stream: 1.0.0 - '@inquirer/type@3.0.4(@types/node@22.13.5)': + '@inquirer/type@3.0.4(@types/node@22.13.14)': optionalDependencies: - '@types/node': 22.13.5 + '@types/node': 22.13.14 '@isaacs/cliui@8.0.2': dependencies: @@ -5673,9 +5673,9 @@ snapshots: '@leichtgewicht/ip-codec@2.0.5': {} - '@listr2/prompt-adapter-inquirer@2.0.18(@inquirer/prompts@7.2.1(@types/node@22.13.5))': + '@listr2/prompt-adapter-inquirer@2.0.18(@inquirer/prompts@7.2.1(@types/node@22.13.14))': dependencies: - '@inquirer/prompts': 7.2.1(@types/node@22.13.5) + '@inquirer/prompts': 7.2.1(@types/node@22.13.14) '@inquirer/type': 1.5.5 '@lmdb/lmdb-darwin-arm64@3.2.2': @@ -6096,24 +6096,24 @@ snapshots: '@types/body-parser@1.19.5': dependencies: '@types/connect': 3.4.38 - '@types/node': 22.13.5 + '@types/node': 22.13.14 '@types/bonjour@3.5.13': dependencies: - '@types/node': 22.13.5 + '@types/node': 22.13.14 '@types/connect-history-api-fallback@1.5.4': dependencies: '@types/express-serve-static-core': 5.0.6 - '@types/node': 22.13.5 + '@types/node': 22.13.14 '@types/connect@3.4.38': dependencies: - '@types/node': 22.13.5 + '@types/node': 22.13.14 '@types/cors@2.8.17': dependencies: - '@types/node': 22.13.5 + '@types/node': 22.13.14 '@types/eslint-scope@3.7.7': dependencies: @@ -6129,14 +6129,14 @@ snapshots: '@types/express-serve-static-core@4.19.6': dependencies: - '@types/node': 22.13.5 + '@types/node': 22.13.14 '@types/qs': 6.9.18 '@types/range-parser': 1.2.7 '@types/send': 0.17.4 '@types/express-serve-static-core@5.0.6': dependencies: - '@types/node': 22.13.5 + '@types/node': 22.13.14 '@types/qs': 6.9.18 '@types/range-parser': 1.2.7 '@types/send': 0.17.4 @@ -6152,7 +6152,7 @@ snapshots: '@types/http-proxy@1.17.16': dependencies: - '@types/node': 22.13.5 + '@types/node': 22.13.14 '@types/jasmine@5.1.7': {} @@ -6162,9 +6162,9 @@ snapshots: '@types/node-forge@1.3.11': dependencies: - '@types/node': 22.13.5 + '@types/node': 22.13.14 - '@types/node@22.13.5': + '@types/node@22.13.14': dependencies: undici-types: 6.20.0 @@ -6177,7 +6177,7 @@ snapshots: '@types/send@0.17.4': dependencies: '@types/mime': 1.3.5 - '@types/node': 22.13.5 + '@types/node': 22.13.14 '@types/serve-index@1.9.4': dependencies: @@ -6186,20 +6186,20 @@ snapshots: '@types/serve-static@1.15.7': dependencies: '@types/http-errors': 2.0.4 - '@types/node': 22.13.5 + '@types/node': 22.13.14 '@types/send': 0.17.4 '@types/sockjs@0.3.36': dependencies: - '@types/node': 22.13.5 + '@types/node': 22.13.14 '@types/ws@8.5.14': dependencies: - '@types/node': 22.13.5 + '@types/node': 22.13.14 - '@vitejs/plugin-basic-ssl@1.2.0(vite@6.0.11(@types/node@22.13.5)(jiti@2.4.2)(less@4.2.1)(lightningcss@1.29.1)(sass@1.83.1)(terser@5.37.0))': + '@vitejs/plugin-basic-ssl@1.2.0(vite@6.0.11(@types/node@22.13.14)(jiti@2.4.2)(less@4.2.1)(lightningcss@1.29.1)(sass@1.83.1)(terser@5.37.0))': dependencies: - vite: 6.0.11(@types/node@22.13.5)(jiti@2.4.2)(less@4.2.1)(lightningcss@1.29.1)(sass@1.83.1)(terser@5.37.0) + vite: 6.0.11(@types/node@22.13.14)(jiti@2.4.2)(less@4.2.1)(lightningcss@1.29.1)(sass@1.83.1)(terser@5.37.0) '@webassemblyjs/ast@1.14.1': dependencies: @@ -6828,7 +6828,7 @@ snapshots: engine.io@6.6.4: dependencies: '@types/cors': 2.8.17 - '@types/node': 22.13.5 + '@types/node': 22.13.14 accepts: 1.3.8 base64id: 2.0.0 cookie: 0.7.2 @@ -7440,7 +7440,7 @@ snapshots: jest-worker@27.5.1: dependencies: - '@types/node': 22.13.5 + '@types/node': 22.13.14 merge-stream: 2.0.0 supports-color: 8.1.1 @@ -8832,13 +8832,13 @@ snapshots: vary@1.1.2: {} - vite@6.0.11(@types/node@22.13.5)(jiti@2.4.2)(less@4.2.1)(lightningcss@1.29.1)(sass@1.83.1)(terser@5.37.0): + vite@6.0.11(@types/node@22.13.14)(jiti@2.4.2)(less@4.2.1)(lightningcss@1.29.1)(sass@1.83.1)(terser@5.37.0): dependencies: esbuild: 0.24.2 postcss: 8.5.3 rollup: 4.30.1 optionalDependencies: - '@types/node': 22.13.5 + '@types/node': 22.13.14 fsevents: 2.3.3 jiti: 2.4.2 less: 4.2.1 diff --git a/frontend/src/app/game/game.component.html b/frontend/src/app/game/game.component.html index 65ec7a9..4275489 100755 --- a/frontend/src/app/game/game.component.html +++ b/frontend/src/app/game/game.component.html @@ -55,7 +55,7 @@

Hint

- @for(card of hint; track card.id) { + @for(card of gameService.hint; track card.id) { 0) return; - this.hint = await this.gameService.showHint(); + await this.gameService.showHint(); } } diff --git a/frontend/src/environments/environment.development.ts b/frontend/src/environments/environment.development.ts new file mode 100644 index 0000000..f274e5e --- /dev/null +++ b/frontend/src/environments/environment.development.ts @@ -0,0 +1 @@ +export const environment = {}; diff --git a/frontend/src/environments/environment.ts b/frontend/src/environments/environment.ts new file mode 100644 index 0000000..3baf987 --- /dev/null +++ b/frontend/src/environments/environment.ts @@ -0,0 +1,6 @@ +export const environment = { + production: false, + apiUrl: 'http://localhost:5224/api/v1/Games', + authUrl: 'http://localhost:5224/api/v1/Auth', + authTokenKey: 'auth_token', +}; \ No newline at end of file diff --git a/frontend/src/service/auth/auth.interceptor.ts b/frontend/src/service/auth/auth.interceptor.ts index 1213e72..7ec2134 100644 --- a/frontend/src/service/auth/auth.interceptor.ts +++ b/frontend/src/service/auth/auth.interceptor.ts @@ -1,15 +1,17 @@ -import { HttpInterceptorFn, HttpRequest } from '@angular/common/http'; import { inject } from '@angular/core'; +import { HttpInterceptorFn } from '@angular/common/http'; + import { AuthService } from './auth.service'; export const authInterceptor: HttpInterceptorFn = (req, next) => { - const authService = inject(AuthService); + const authService: AuthService = inject(AuthService); const token = authService.getToken(); if (token) { const cloned = req.clone({ headers: req.headers.set('Authorization', `Bearer ${token}`) }); + return next(cloned); } diff --git a/frontend/src/service/auth/auth.service.ts b/frontend/src/service/auth/auth.service.ts index 443306e..ff4f9de 100644 --- a/frontend/src/service/auth/auth.service.ts +++ b/frontend/src/service/auth/auth.service.ts @@ -1,37 +1,33 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; +import { Router } from '@angular/router'; import { HttpClient } from '@angular/common/http'; import { BehaviorSubject, Observable, tap } from 'rxjs'; -import { Router } from '@angular/router'; -interface LoginResponse { - token: string; -} +import { environment } from '../../environments/environment'; -@Injectable({ - providedIn: 'root' -}) +import { Credentials, LoginResponse } from './auth.types'; + +@Injectable({ providedIn: 'root' }) export class AuthService { - private API_URL = 'http://localhost:5224/api/v1/Auth'; - private tokenKey = 'auth_token'; + private http: HttpClient = inject(HttpClient); + private router: Router = inject(Router); + private isAuthenticatedSubject = new BehaviorSubject(this.hasToken()); - public redirectUrl: string | null = null; - constructor(private http: HttpClient, private router: Router) {} - - login(username: string, password: string): Observable { - return this.http.post(`${this.API_URL}/login`, { username, password }) + login(credentials: Credentials): Observable { + return this.http.post(`${environment.authUrl}/login`, credentials) .pipe( tap(response => { - localStorage.setItem(this.tokenKey, response.token); + localStorage.setItem(environment.authTokenKey, response.token); this.isAuthenticatedSubject.next(true); }) ); } logout(): void { - localStorage.removeItem(this.tokenKey); + localStorage.removeItem(environment.authTokenKey); this.isAuthenticatedSubject.next(false); - this.router.navigate(['/login']); + this.router.navigate([ '/login' ]); } isAuthenticated(): Observable { @@ -39,10 +35,10 @@ export class AuthService { } getToken(): string | null { - return localStorage.getItem(this.tokenKey); + return localStorage.getItem(environment.authTokenKey); } private hasToken(): boolean { - return !!localStorage.getItem(this.tokenKey); + return !!localStorage.getItem(environment.authTokenKey); } } \ No newline at end of file diff --git a/frontend/src/service/auth/auth.types.ts b/frontend/src/service/auth/auth.types.ts new file mode 100644 index 0000000..cc64429 --- /dev/null +++ b/frontend/src/service/auth/auth.types.ts @@ -0,0 +1,8 @@ +export type Credentials = { + username: string; + password: string; +} + +export type LoginResponse = { + token: string; +} \ No newline at end of file diff --git a/frontend/src/service/game/game.service.ts b/frontend/src/service/game/game.service.ts index daf2fd9..cc31512 100755 --- a/frontend/src/service/game/game.service.ts +++ b/frontend/src/service/game/game.service.ts @@ -1,37 +1,22 @@ import { Injectable, inject } from '@angular/core'; import { HttpClient } from '@angular/common/http'; -import { Card, toCard } from '../../app/models/card'; import { lastValueFrom } from 'rxjs'; -interface GameResponse { - deck: number[]; - hand: number[]; - found: number[]; - startedAt: Date; - finishedAt?: Date; - fails: number; - hints: number; - id: number; -} +import { Card, toCard } from '../../app/models/card'; +import { environment } from '../../environments/environment'; -interface SetCheckResponse { - isSet: boolean; - newState: GameResponse; -} - -type HintResponse = number[]; +import { GameResponse, SetCheckResponse, HintResponse } from './game.types'; @Injectable({ providedIn: 'root' }) export class GameService { private http = inject(HttpClient); - - private API_URL = 'http://localhost:5224/api/v1/Games'; - public deck: Card[] = []; - public hand: Card[] = []; - public selectedCards: Card[] = []; - public foundSets: Card[][] = []; - public possibleSets: Card[][] = []; + public deck: Card[] = []; + public hand: Card[] = []; + public hint: Card[] = []; + private selectedCards: Card[] = []; + public foundSets: Card[][] = []; + public possibleSets: Card[][] = []; public fails: number = 0; public hints: number = 0; @@ -40,9 +25,12 @@ export class GameService { public startDate: Date = new Date(); public finishedAt?: Date; - constructor() {} - - public updateState(game: GameResponse): void { + private updateState(game: GameResponse): void { + // Clear hint if set is found + if ((this.deck.length + this.hand.length) !== (game.deck.length + game.hand.length)) { + this.hint.length = 0; + } + this.gameId = game.id; this.fails = game.fails; this.hints = game.hints; @@ -68,9 +56,9 @@ export class GameService { // Fetch game data from the server, use lastValueFrom to convert Observable to Promise let game: GameResponse; if (gameId) { - game = await lastValueFrom(this.http.get(`${this.API_URL}/${gameId}`)) + game = await lastValueFrom(this.http.get(`${environment.apiUrl}/${gameId}`)) } else { - game = await lastValueFrom(this.http.post(this.API_URL, {})); + game = await lastValueFrom(this.http.post(environment.apiUrl, {})); } // Update the game state with the fetched data @@ -79,17 +67,17 @@ export class GameService { return game.id; } - public async getSetsInHand(): Promise { + private async getSetsInHand(): Promise { const sets = await lastValueFrom( - this.http.get(`${this.API_URL}/SetsInHand/${this.gameId}`) + this.http.get(`${environment.apiUrl}/SetsInHand/${this.gameId}`) ); this.possibleSets = sets.map(set => set.map(cardIndex => this.hand[cardIndex])); } - public async checkSet(cards: number[]): Promise { + private async checkSet(cards: number[]): Promise { const response = await lastValueFrom( - this.http.post(`${this.API_URL}/CheckSet/${this.gameId}`, cards) + this.http.post(`${environment.apiUrl}/CheckSet/${this.gameId}`, cards) ); this.updateState(response.newState); @@ -116,14 +104,14 @@ export class GameService { } } - public async showHint(): Promise { - const hint = await lastValueFrom(this.http.get(`${this.API_URL}/Hint/${this.gameId}`)); + public async showHint(): Promise { + const hint = await lastValueFrom(this.http.get(`${environment.apiUrl}/Hint/${this.gameId}`)); this.hints++; - return hint.map((indexOfCard: number) => this.hand[indexOfCard]); + this.hint = hint.map((indexOfCard: number) => this.hand[indexOfCard]); } public deleteGame(): void { - this.http.delete(`${this.API_URL}/${this.gameId}`).subscribe(); + this.http.delete(`${environment.apiUrl}/${this.gameId}`).subscribe(); } } \ No newline at end of file diff --git a/frontend/src/service/game/game.types.ts b/frontend/src/service/game/game.types.ts new file mode 100644 index 0000000..f487b43 --- /dev/null +++ b/frontend/src/service/game/game.types.ts @@ -0,0 +1,17 @@ +export type GameResponse = { + deck: number[]; + hand: number[]; + found: number[]; + startedAt: Date; + finishedAt?: Date; + fails: number; + hints: number; + id: number; +} + +export type SetCheckResponse = { + isSet: boolean; + newState: GameResponse; +} + +export type HintResponse = number[]; diff --git a/frontend/src/service/user/user-data.service.ts b/frontend/src/service/user/user-data.service.ts index 483d322..284a3c0 100644 --- a/frontend/src/service/user/user-data.service.ts +++ b/frontend/src/service/user/user-data.service.ts @@ -1,40 +1,28 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { lastValueFrom } from 'rxjs'; + import { AuthService } from '../auth/auth.service'; +import { environment } from '../../environments/environment'; -@Injectable({ - providedIn: 'root' -}) +import { Credentials } from '../auth/auth.types'; +import { GameResponse } from '../game/game.types'; + +@Injectable({ providedIn: 'root' }) export class UserDataService { - private GAMES_API_URL = 'http://localhost:5224/api/v1/Games'; + private http: HttpClient = inject(HttpClient); + private authService: AuthService = inject(AuthService); - constructor( - private http: HttpClient, - private authService: AuthService - ) {} - - public async login(credentials: { username: string, password: string }): Promise { - try { - await lastValueFrom(this.authService.login(credentials.username, credentials.password)); - } catch (error) { - console.error('Login error:', error); - throw error; - } + public async login(credentials: Credentials): Promise { + await lastValueFrom(this.authService.login(credentials)); } - public async getGames(): Promise { - try { - const data = await lastValueFrom(this.http.get(this.GAMES_API_URL)); - - const sortedGames = data - .sort((a: any, b: any) => new Date(a.startedAt).getTime() - new Date(b.startedAt).getTime()) - .reverse(); - - return sortedGames; - } catch (error) { - console.error('Error getting games:', error); - throw error; - } + public async getGames(): Promise { + const games = await lastValueFrom(this.http.get(environment.apiUrl)); + + return games + .sort((a: GameResponse, b: GameResponse) => + new Date(a.startedAt).getTime() - new Date(b.startedAt).getTime() + ).reverse(); } } \ No newline at end of file diff --git a/frontend/webpack.config.ts b/frontend/webpack.config.ts new file mode 100644 index 0000000..e0aedf1 --- /dev/null +++ b/frontend/webpack.config.ts @@ -0,0 +1,6 @@ +import { EnvironmentPlugin } from 'webpack'; +const Dotenv = require('dotenv-webpack'); + +module.exports = { + plugins: [new Dotenv()], +}; \ No newline at end of file