chore(frontend): Finish services

This commit is contained in:
2025-03-31 08:32:17 +00:00
parent b646bde79a
commit d50847ce5b
13 changed files with 207 additions and 189 deletions

View File

@@ -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"

194
frontend/pnpm-lock.yaml generated
View File

@@ -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

View File

@@ -55,7 +55,7 @@
<button class="red" (click)="showHint()">hint</button>
<div class="flex flex-row h-[40px] items-center">
<p class="font-bold mr-5 flex">Hint</p>
@for(card of hint; track card.id) {
@for(card of gameService.hint; track card.id) {
<game-card
[card]="card"
[selected]="false"

View File

@@ -51,6 +51,6 @@ export class GameComponent {
async showHint() {
// if (this.hint.length > 0) return;
this.hint = await this.gameService.showHint();
await this.gameService.showHint();
}
}

View File

@@ -0,0 +1 @@
export const environment = {};

View File

@@ -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',
};

View File

@@ -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);
}

View File

@@ -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<boolean>(this.hasToken());
public redirectUrl: string | null = null;
constructor(private http: HttpClient, private router: Router) {}
login(username: string, password: string): Observable<LoginResponse> {
return this.http.post<LoginResponse>(`${this.API_URL}/login`, { username, password })
login(credentials: Credentials): Observable<LoginResponse> {
return this.http.post<LoginResponse>(`${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<boolean> {
@@ -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);
}
}

View File

@@ -0,0 +1,8 @@
export type Credentials = {
username: string;
password: string;
}
export type LoginResponse = {
token: string;
}

View File

@@ -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<GameResponse>(`${this.API_URL}/${gameId}`))
game = await lastValueFrom(this.http.get<GameResponse>(`${environment.apiUrl}/${gameId}`))
} else {
game = await lastValueFrom(this.http.post<GameResponse>(this.API_URL, {}));
game = await lastValueFrom(this.http.post<GameResponse>(environment.apiUrl, {}));
}
// Update the game state with the fetched data
@@ -79,17 +67,17 @@ export class GameService {
return game.id;
}
public async getSetsInHand(): Promise<void> {
private async getSetsInHand(): Promise<void> {
const sets = await lastValueFrom(
this.http.get<number[][]>(`${this.API_URL}/SetsInHand/${this.gameId}`)
this.http.get<number[][]>(`${environment.apiUrl}/SetsInHand/${this.gameId}`)
);
this.possibleSets = sets.map(set => set.map(cardIndex => this.hand[cardIndex]));
}
public async checkSet(cards: number[]): Promise<boolean> {
private async checkSet(cards: number[]): Promise<boolean> {
const response = await lastValueFrom(
this.http.post<SetCheckResponse>(`${this.API_URL}/CheckSet/${this.gameId}`, cards)
this.http.post<SetCheckResponse>(`${environment.apiUrl}/CheckSet/${this.gameId}`, cards)
);
this.updateState(response.newState);
@@ -116,14 +104,14 @@ export class GameService {
}
}
public async showHint(): Promise<Card[]> {
const hint = await lastValueFrom(this.http.get<HintResponse>(`${this.API_URL}/Hint/${this.gameId}`));
public async showHint(): Promise<void> {
const hint = await lastValueFrom(this.http.get<HintResponse>(`${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<void>(`${this.API_URL}/${this.gameId}`).subscribe();
this.http.delete<void>(`${environment.apiUrl}/${this.gameId}`).subscribe();
}
}

View File

@@ -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[];

View File

@@ -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<void> {
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<void> {
await lastValueFrom(this.authService.login(credentials));
}
public async getGames(): Promise<any[]> {
try {
const data = await lastValueFrom(this.http.get<any[]>(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<GameResponse[]> {
const games = await lastValueFrom(this.http.get<GameResponse[]>(environment.apiUrl));
return games
.sort((a: GameResponse, b: GameResponse) =>
new Date(a.startedAt).getTime() - new Date(b.startedAt).getTime()
).reverse();
}
}

View File

@@ -0,0 +1,6 @@
import { EnvironmentPlugin } from 'webpack';
const Dotenv = require('dotenv-webpack');
module.exports = {
plugins: [new Dotenv()],
};