Compare commits

...

7 Commits

Author SHA1 Message Date
9231bd0d4a fix: set the text based on the context
All checks were successful
Build and Deploy Angular App / build (push) Successful in 2m32s
2024-10-09 03:11:09 +03:00
0bbed93df2 refactor: remove unused methods 2024-10-09 03:10:32 +03:00
2b09086902 refactor: adapting token storage to the API 2024-10-09 03:10:11 +03:00
9209b31db2 feat: add more info to error 2024-10-07 03:25:13 +03:00
3ca6f56fec feat: replace custom notify to ngx-toastr
#https://github.com/scttcper/ngx-toastr
2024-10-07 01:17:49 +03:00
eded639cc3 build: update ref 2024-10-07 01:16:11 +03:00
844d91de7d fix: add correct font 2024-10-07 01:15:13 +03:00
11 changed files with 482 additions and 429 deletions

313
package-lock.json generated
View File

@ -1,34 +1,35 @@
{
"name": "frontend",
"version": "1.0.0-b7",
"version": "1.0.0-b8",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "frontend",
"version": "1.0.0-b7",
"version": "1.0.0-b8",
"dependencies": {
"@angular/animations": "^18.2.6",
"@angular/cdk": "~18.2.6",
"@angular/cdk-experimental": "^18.2.6",
"@angular/common": "^18.2.6",
"@angular/compiler": "^18.2.6",
"@angular/core": "^18.2.6",
"@angular/forms": "^18.2.6",
"@angular/material": "~18.2.6",
"@angular/platform-browser": "^18.2.6",
"@angular/platform-browser-dynamic": "^18.2.6",
"@angular/router": "^18.2.6",
"@angular/animations": "^18.2.7",
"@angular/cdk": "~18.2.7",
"@angular/cdk-experimental": "^18.2.7",
"@angular/common": "^18.2.7",
"@angular/compiler": "^18.2.7",
"@angular/core": "^18.2.7",
"@angular/forms": "^18.2.7",
"@angular/material": "~18.2.7",
"@angular/platform-browser": "^18.2.7",
"@angular/platform-browser-dynamic": "^18.2.7",
"@angular/router": "^18.2.7",
"@dhutaryan/ngx-mat-timepicker": "^18.0.2",
"@progress/kendo-date-math": "^1.5.13",
"ngx-toastr": "^19.0.0",
"rxjs": "~7.8.1",
"tslib": "^2.7.0",
"zone.js": "^0.14.10"
},
"devDependencies": {
"@angular-devkit/build-angular": "^18.2.6",
"@angular/cli": "^18.2.6",
"@angular/compiler-cli": "^18.2.6",
"@angular-devkit/build-angular": "^18.2.7",
"@angular/cli": "^18.2.7",
"@angular/compiler-cli": "^18.2.7",
"@types/jasmine": "~5.1.4",
"jasmine-core": "~5.3.0",
"karma": "~6.4.4",
@ -53,13 +54,13 @@
}
},
"node_modules/@angular-devkit/architect": {
"version": "0.1802.6",
"resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1802.6.tgz",
"integrity": "sha512-oF7cPFdTLxeuvXkK/opSdIxZ1E4LrBbmuytQ/nCoAGOaKBWdqvwagRZ6jVhaI0Gwu48rkcV7Zhesg/ESNnROdw==",
"version": "0.1802.7",
"resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1802.7.tgz",
"integrity": "sha512-kpcgXnepEXcoxDTbqbGj7Hg1WJLWj1HLR3/FKmC5TbpBf1xiLxiqfkQNwz3BbE/W9JWMLdrXr3GI9O3O2gWPLg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@angular-devkit/core": "18.2.6",
"@angular-devkit/core": "18.2.7",
"rxjs": "7.8.1"
},
"engines": {
@ -69,17 +70,17 @@
}
},
"node_modules/@angular-devkit/build-angular": {
"version": "18.2.6",
"resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-18.2.6.tgz",
"integrity": "sha512-u12cJZttgs5j7gICHWSmcaTCu0EFXEzKqI8nkYCwq2MtuJlAXiMQSXYuEP9OU3Go4vMAPtQh2kShyOWCX5b4EQ==",
"version": "18.2.7",
"resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-18.2.7.tgz",
"integrity": "sha512-u8PriYdgddK7k+OS/pOFPD1v4Iu5bztUJZXZVcGeXBZFFdnGFFzKmQw9mfcyGvTMJp2ABgBuuJT0YqYgNfAhzw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@ampproject/remapping": "2.3.0",
"@angular-devkit/architect": "0.1802.6",
"@angular-devkit/build-webpack": "0.1802.6",
"@angular-devkit/core": "18.2.6",
"@angular/build": "18.2.6",
"@angular-devkit/architect": "0.1802.7",
"@angular-devkit/build-webpack": "0.1802.7",
"@angular-devkit/core": "18.2.7",
"@angular/build": "18.2.7",
"@babel/core": "7.25.2",
"@babel/generator": "7.25.0",
"@babel/helper-annotate-as-pure": "7.24.7",
@ -90,7 +91,7 @@
"@babel/preset-env": "7.25.3",
"@babel/runtime": "7.25.0",
"@discoveryjs/json-ext": "0.6.1",
"@ngtools/webpack": "18.2.6",
"@ngtools/webpack": "18.2.7",
"@vitejs/plugin-basic-ssl": "1.1.0",
"ansi-colors": "4.1.3",
"autoprefixer": "10.4.20",
@ -240,13 +241,13 @@
"license": "0BSD"
},
"node_modules/@angular-devkit/build-webpack": {
"version": "0.1802.6",
"resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1802.6.tgz",
"integrity": "sha512-JMLcXFaitJplwZMKkqhbYirINCRD6eOPZuIGaIOVynXYGWgvJkLT9t5C2wm9HqSLtp1K7NcYG2Y7PtTVR4krnQ==",
"version": "0.1802.7",
"resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1802.7.tgz",
"integrity": "sha512-VrtbrhZ+dht3f0GjtfRLRGRN4XHN/W+/bA9DqckdxVS6SydsrCWNHonvEPmOs4jJmGIGXIu6tUBMcWleTao2sg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@angular-devkit/architect": "0.1802.6",
"@angular-devkit/architect": "0.1802.7",
"rxjs": "7.8.1"
},
"engines": {
@ -260,9 +261,9 @@
}
},
"node_modules/@angular-devkit/core": {
"version": "18.2.6",
"resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-18.2.6.tgz",
"integrity": "sha512-la4CFvs5PcRWSkQ/H7TB5cPZirFVA9GoWk5LzIk8si6VjWBJRm8b3keKJoC9LlNeABRUIR5z0ocYkyQQUhdMfg==",
"version": "18.2.7",
"resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-18.2.7.tgz",
"integrity": "sha512-1ZTi4A6tEC2bkJ/puCIdIPYhesnlCVOMSDJL/lZAd0hC6X22T4pwu0AEvue7mcP5NbXpQDiBaXOZ3MmCA8PwOA==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -306,13 +307,13 @@
}
},
"node_modules/@angular-devkit/schematics": {
"version": "18.2.6",
"resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-18.2.6.tgz",
"integrity": "sha512-uIttrQ2cQ2PWAFFVPeCoNR8xvs7tPJ2i8gzqsIwYdge107xDC6u9CqfgmBqPDSFpWj+IiC2Jwcm8Z4HYKU4+7A==",
"version": "18.2.7",
"resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-18.2.7.tgz",
"integrity": "sha512-j7198lpkOXMG+Gyfln/5aDgBZV7m4pWMzHFhkO3+w3cbCNUN1TVZW0SyJcF+CYaxANzTbuumfvpsYc/fTeAGLw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@angular-devkit/core": "18.2.6",
"@angular-devkit/core": "18.2.7",
"jsonc-parser": "3.3.1",
"magic-string": "0.30.11",
"ora": "5.4.1",
@ -325,9 +326,9 @@
}
},
"node_modules/@angular/animations": {
"version": "18.2.6",
"resolved": "https://registry.npmjs.org/@angular/animations/-/animations-18.2.6.tgz",
"integrity": "sha512-vy9wy+Q9beiRxkEO8wNxFQ63AqAujGvk8AUHepxxIT7QNNc512TNKz8uH+feWDPO38Dm2obwYQHMGzs3WO7pUA==",
"version": "18.2.7",
"resolved": "https://registry.npmjs.org/@angular/animations/-/animations-18.2.7.tgz",
"integrity": "sha512-5B7qD1K+kKOf9lgJT4VNMft3IK2BnRHjN1S6l38ywzQ/nxpmCG7f+qKAAU6CpCywhNUBeXW0hVXTMuMNPVOcQQ==",
"license": "MIT",
"dependencies": {
"tslib": "^2.3.0"
@ -336,18 +337,18 @@
"node": "^18.19.1 || ^20.11.1 || >=22.0.0"
},
"peerDependencies": {
"@angular/core": "18.2.6"
"@angular/core": "18.2.7"
}
},
"node_modules/@angular/build": {
"version": "18.2.6",
"resolved": "https://registry.npmjs.org/@angular/build/-/build-18.2.6.tgz",
"integrity": "sha512-TQzX6Mi7uXFvmz7+OVl4Za7WawYPcx+B5Ewm6IY/DdMyB9P/Z4tbKb1LO+ynWUXYwm7avXo6XQQ4m5ArDY5F/A==",
"version": "18.2.7",
"resolved": "https://registry.npmjs.org/@angular/build/-/build-18.2.7.tgz",
"integrity": "sha512-oq6JsVxLP9/w9F2IjKroJwPB9CdlMblu2Xhfq/qQZRSUuM8Ppt1svr2FBTo1HrLIbosqukkVcSSdmKYDneo+cg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@ampproject/remapping": "2.3.0",
"@angular-devkit/architect": "0.1802.6",
"@angular-devkit/architect": "0.1802.7",
"@babel/core": "7.25.2",
"@babel/helper-annotate-as-pure": "7.24.7",
"@babel/helper-split-export-declaration": "7.24.7",
@ -427,9 +428,9 @@
}
},
"node_modules/@angular/cdk": {
"version": "18.2.6",
"resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-18.2.6.tgz",
"integrity": "sha512-Gfq/iv4zhlKYpdQkDaBRwxI71NHNUHM1Cs1XhnZ0/oFct5HXvSv1RHRGTKqBJLLACaAPzZKXJ/UglLoyO5CNiQ==",
"version": "18.2.7",
"resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-18.2.7.tgz",
"integrity": "sha512-Dfl37WBLeEUURQrDeuMcOgX2bkQJ+BGMOlr1qsFXzUWHH+qgYW2YwO1rbna/rjxyeFzc2Sy569dYRzNPqMewzg==",
"license": "MIT",
"dependencies": {
"tslib": "^2.3.0"
@ -444,31 +445,31 @@
}
},
"node_modules/@angular/cdk-experimental": {
"version": "18.2.6",
"resolved": "https://registry.npmjs.org/@angular/cdk-experimental/-/cdk-experimental-18.2.6.tgz",
"integrity": "sha512-EXrDNqxmodxMNEiqGkNfRmAuZ9S0x4Xhegve1lZkAvWfk81aUjz18B36nbWaQ7zUJJvRFT3tvI/ZLApjo+wBMQ==",
"version": "18.2.7",
"resolved": "https://registry.npmjs.org/@angular/cdk-experimental/-/cdk-experimental-18.2.7.tgz",
"integrity": "sha512-ugWpenAfED8RsCafuy7Gz28cTGwp/fNsiwLm9h4Hj7iWP5KNfNUWdocFBnegDXyiiqyNxml4DxH5XZjqFrqCyQ==",
"license": "MIT",
"dependencies": {
"tslib": "^2.3.0"
},
"peerDependencies": {
"@angular/cdk": "18.2.6",
"@angular/cdk": "18.2.7",
"@angular/core": "^18.0.0 || ^19.0.0"
}
},
"node_modules/@angular/cli": {
"version": "18.2.6",
"resolved": "https://registry.npmjs.org/@angular/cli/-/cli-18.2.6.tgz",
"integrity": "sha512-tdXsnV/w+Rgu8q0zFsLU5L9ImTVqrTol1vppHaQkJ/vuoHy+s8ZEbBqhVrO/ffosNb2xseUybGYvqMS4zkNQjg==",
"version": "18.2.7",
"resolved": "https://registry.npmjs.org/@angular/cli/-/cli-18.2.7.tgz",
"integrity": "sha512-KoWgSvhRsU05A2m6B7jw1kdpyoS+Ce5GGLW6xcnX7VF2AckW54vYd/8ZkgpzQrKfvIpVblYd4KJGizKoaLZ5jA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@angular-devkit/architect": "0.1802.6",
"@angular-devkit/core": "18.2.6",
"@angular-devkit/schematics": "18.2.6",
"@angular-devkit/architect": "0.1802.7",
"@angular-devkit/core": "18.2.7",
"@angular-devkit/schematics": "18.2.7",
"@inquirer/prompts": "5.3.8",
"@listr2/prompt-adapter-inquirer": "2.0.15",
"@schematics/angular": "18.2.6",
"@schematics/angular": "18.2.7",
"@yarnpkg/lockfile": "1.1.0",
"ini": "4.1.3",
"jsonc-parser": "3.3.1",
@ -491,9 +492,9 @@
}
},
"node_modules/@angular/common": {
"version": "18.2.6",
"resolved": "https://registry.npmjs.org/@angular/common/-/common-18.2.6.tgz",
"integrity": "sha512-89793ow+wrI1c7C6kyMbnweLNIZHzXthosxAEjipRZGBrqBYjvTtkE45Fl+5yBa3JO7bAhyGkUnEoyvWtZIAEA==",
"version": "18.2.7",
"resolved": "https://registry.npmjs.org/@angular/common/-/common-18.2.7.tgz",
"integrity": "sha512-5vDBmBR2JcIxHVEDunKXNU+T+OvTGiHZTSo35GFOHJxKFgX5g6+0tJBZunK04oBZGbJQUmp3pg2kMvuKKjZnkQ==",
"license": "MIT",
"dependencies": {
"tslib": "^2.3.0"
@ -502,14 +503,14 @@
"node": "^18.19.1 || ^20.11.1 || >=22.0.0"
},
"peerDependencies": {
"@angular/core": "18.2.6",
"@angular/core": "18.2.7",
"rxjs": "^6.5.3 || ^7.4.0"
}
},
"node_modules/@angular/compiler": {
"version": "18.2.6",
"resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-18.2.6.tgz",
"integrity": "sha512-3tX2/Qw+bZ8XzKitviH8jzNGyY0uohhehhBB57OJOCc+yr4ojy/7SYFnun1lSsRnDztdCE461641X4iQLCQ94w==",
"version": "18.2.7",
"resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-18.2.7.tgz",
"integrity": "sha512-XemlYyRGnu/HrICtXwTPmGtyOrI8BhbGg/HMiJ7sVx40AeEIX0uyDgnu9Gc5OjmtDqZZ8Qftg1sQAxaCVjLb1w==",
"license": "MIT",
"dependencies": {
"tslib": "^2.3.0"
@ -518,7 +519,7 @@
"node": "^18.19.1 || ^20.11.1 || >=22.0.0"
},
"peerDependencies": {
"@angular/core": "18.2.6"
"@angular/core": "18.2.7"
},
"peerDependenciesMeta": {
"@angular/core": {
@ -527,9 +528,9 @@
}
},
"node_modules/@angular/compiler-cli": {
"version": "18.2.6",
"resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-18.2.6.tgz",
"integrity": "sha512-b5x9STfjNiNM/S0D+CnqRP9UOxPtSz1+RlCH5WdOMiW/p8j5p6dBix8YYgTe6Wg3OD7eItD2pnFQKgF/dWiopA==",
"version": "18.2.7",
"resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-18.2.7.tgz",
"integrity": "sha512-U7cveObj+rrXH5EC8egAhATCeAAcOceEQDTVIOWmBa0qMR4hOMjtI2XUS2QRuI1Q+fQZ2hVEOW95WVLvEMsANA==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -551,14 +552,14 @@
"node": "^18.19.1 || ^20.11.1 || >=22.0.0"
},
"peerDependencies": {
"@angular/compiler": "18.2.6",
"@angular/compiler": "18.2.7",
"typescript": ">=5.4 <5.6"
}
},
"node_modules/@angular/core": {
"version": "18.2.6",
"resolved": "https://registry.npmjs.org/@angular/core/-/core-18.2.6.tgz",
"integrity": "sha512-PjFad2j4YBwLVTw+0Te8CJCa/tV0W8caTHG8aOjj3ObdL6ihGI+FKnwerLc9RVzDFd14BOO4C6/+LbOQAh3Ltw==",
"version": "18.2.7",
"resolved": "https://registry.npmjs.org/@angular/core/-/core-18.2.7.tgz",
"integrity": "sha512-hLOxgxLiyWm9iVHBsUsJfx1hDsXWZnfJBlr+N7cev53f0CDoPfbshqq6KV+JFqXFDguzR9dKHm1ewT1jK3e6Tw==",
"license": "MIT",
"dependencies": {
"tslib": "^2.3.0"
@ -572,9 +573,9 @@
}
},
"node_modules/@angular/forms": {
"version": "18.2.6",
"resolved": "https://registry.npmjs.org/@angular/forms/-/forms-18.2.6.tgz",
"integrity": "sha512-quGkUqTxlBaLB8C/RnpfFG57fdmNF5RQ+368N89Ma++2lpIsVAHaGZZn4yOyo3wNYaM2jBxNqaYxOzZNUl5Tig==",
"version": "18.2.7",
"resolved": "https://registry.npmjs.org/@angular/forms/-/forms-18.2.7.tgz",
"integrity": "sha512-WO3c9/OA7ekBnDBgmvi5TlHshOt5S4NREIP+/VVyuRgg28BwUWyO/Nqh19nguE1UNNRt6OMLkT6NSV2ewhcXUg==",
"license": "MIT",
"dependencies": {
"tslib": "^2.3.0"
@ -583,23 +584,23 @@
"node": "^18.19.1 || ^20.11.1 || >=22.0.0"
},
"peerDependencies": {
"@angular/common": "18.2.6",
"@angular/core": "18.2.6",
"@angular/platform-browser": "18.2.6",
"@angular/common": "18.2.7",
"@angular/core": "18.2.7",
"@angular/platform-browser": "18.2.7",
"rxjs": "^6.5.3 || ^7.4.0"
}
},
"node_modules/@angular/material": {
"version": "18.2.6",
"resolved": "https://registry.npmjs.org/@angular/material/-/material-18.2.6.tgz",
"integrity": "sha512-ObxC/vomSb9QF3vIztuiInQzws+D6u09Dhfx6uNFjtyICqxEFpF7+Qx7QVDWrsuXOgxZTKgacK8f46iV8hWUfg==",
"version": "18.2.7",
"resolved": "https://registry.npmjs.org/@angular/material/-/material-18.2.7.tgz",
"integrity": "sha512-mgPj2TCIrsngmu3iNnoaPc6su7uPv+NPCv9HaiKhTx4QGae8EW+RvUxEZJvh4Qaym1fJTi3hjnVeWvQDLQt4CA==",
"license": "MIT",
"dependencies": {
"tslib": "^2.3.0"
},
"peerDependencies": {
"@angular/animations": "^18.0.0 || ^19.0.0",
"@angular/cdk": "18.2.6",
"@angular/cdk": "18.2.7",
"@angular/common": "^18.0.0 || ^19.0.0",
"@angular/core": "^18.0.0 || ^19.0.0",
"@angular/forms": "^18.0.0 || ^19.0.0",
@ -608,9 +609,9 @@
}
},
"node_modules/@angular/platform-browser": {
"version": "18.2.6",
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-18.2.6.tgz",
"integrity": "sha512-RA8UMiYNLga+QMwpKcDw1357gYPfPyY/rmLeezMak//BbsENFYQOJ4Z6DBOBNiPlHxmBsUJMGaKdlpQhfCROyQ==",
"version": "18.2.7",
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-18.2.7.tgz",
"integrity": "sha512-xgj2DH/isFrMZ73dJJm89NRnWBI3AHtugQrZbIapkKBdEt/C1o4SR2W2cV4mPb9o+ELnWurfrxFt9o/q2vnVLw==",
"license": "MIT",
"dependencies": {
"tslib": "^2.3.0"
@ -619,9 +620,9 @@
"node": "^18.19.1 || ^20.11.1 || >=22.0.0"
},
"peerDependencies": {
"@angular/animations": "18.2.6",
"@angular/common": "18.2.6",
"@angular/core": "18.2.6"
"@angular/animations": "18.2.7",
"@angular/common": "18.2.7",
"@angular/core": "18.2.7"
},
"peerDependenciesMeta": {
"@angular/animations": {
@ -630,9 +631,9 @@
}
},
"node_modules/@angular/platform-browser-dynamic": {
"version": "18.2.6",
"resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-18.2.6.tgz",
"integrity": "sha512-kGBU3FNc+DF9r33hwHZqiWoZgQbCDdEIucU0NCLCIg0Hw6/Q9Hr2ndjxQI+WynCPg0JeBn34jpouvpeJer3YDQ==",
"version": "18.2.7",
"resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-18.2.7.tgz",
"integrity": "sha512-BDldzUKjnUjo0NW5gHjBY6CeJP1bWVfF1h/T3idyYG+F4Lxlb3aykRgLWXg4srNLY1KqE7XOYUmgc5cV613bgw==",
"license": "MIT",
"dependencies": {
"tslib": "^2.3.0"
@ -641,16 +642,16 @@
"node": "^18.19.1 || ^20.11.1 || >=22.0.0"
},
"peerDependencies": {
"@angular/common": "18.2.6",
"@angular/compiler": "18.2.6",
"@angular/core": "18.2.6",
"@angular/platform-browser": "18.2.6"
"@angular/common": "18.2.7",
"@angular/compiler": "18.2.7",
"@angular/core": "18.2.7",
"@angular/platform-browser": "18.2.7"
}
},
"node_modules/@angular/router": {
"version": "18.2.6",
"resolved": "https://registry.npmjs.org/@angular/router/-/router-18.2.6.tgz",
"integrity": "sha512-t57Sqja8unHhZlPr+4CWnQacuox2M4p2pMHps+31wt337qH6mKf4jqDmK0dE/MFdRyKjT2a2E/2NwtxXxcWNuw==",
"version": "18.2.7",
"resolved": "https://registry.npmjs.org/@angular/router/-/router-18.2.7.tgz",
"integrity": "sha512-TXE8Aw63hDp3PEaNu4B1DMNvlS0uCzs36o/OSCCmewmLnzyJygkgi4jeEj20FsWPAQOUj5g5tnCYgxz1IRrCUg==",
"license": "MIT",
"dependencies": {
"tslib": "^2.3.0"
@ -659,9 +660,9 @@
"node": "^18.19.1 || ^20.11.1 || >=22.0.0"
},
"peerDependencies": {
"@angular/common": "18.2.6",
"@angular/core": "18.2.6",
"@angular/platform-browser": "18.2.6",
"@angular/common": "18.2.7",
"@angular/core": "18.2.7",
"@angular/platform-browser": "18.2.7",
"rxjs": "^6.5.3 || ^7.4.0"
}
},
@ -3407,9 +3408,9 @@
}
},
"node_modules/@jsonjoy.com/util": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@jsonjoy.com/util/-/util-1.3.0.tgz",
"integrity": "sha512-Cebt4Vk7k1xHy87kHY7KSPLT77A7Ev7IfOblyLZhtYEhrdQ6fX4EoLq3xOQ3O/DRMEh2ok5nyC180E+ABS8Wmw==",
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@jsonjoy.com/util/-/util-1.5.0.tgz",
"integrity": "sha512-ojoNsrIuPI9g6o8UxhraZQSyF2ByJanAY4cTFbc8Mf2AXEF4aQRGY1dJxyJpuyav8r9FGflEt/Ff3u5Nt6YMPA==",
"dev": true,
"license": "Apache-2.0",
"engines": {
@ -3615,9 +3616,9 @@
]
},
"node_modules/@ngtools/webpack": {
"version": "18.2.6",
"resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-18.2.6.tgz",
"integrity": "sha512-7HwOPE1EOgcHnpt4brSiT8G2CcXB50G0+CbCBaKGy4LYCG3Y3mrlzF5Fup9HvMJ6Tzqd62RqzpKKYBiGUT7hxg==",
"version": "18.2.7",
"resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-18.2.7.tgz",
"integrity": "sha512-BmnFxss6zGobGyq9Mi7736golbK8RLgF+zYCQZ+4/OfMMA1jKVoELnyJqNyAx+DQn3m1qKVBjtGEL7pTNpPzOw==",
"dev": true,
"license": "MIT",
"engines": {
@ -4181,14 +4182,14 @@
]
},
"node_modules/@schematics/angular": {
"version": "18.2.6",
"resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-18.2.6.tgz",
"integrity": "sha512-Y988EoOEQDLEyHu3414T6AeVUyx21AexBHQNbUNQkK8cxlxyB6m1eH1cx6vFgLRFUTsLVv+C6Ln/ICNTfLcG4A==",
"version": "18.2.7",
"resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-18.2.7.tgz",
"integrity": "sha512-WOBzO11qstznHbC9tZXQf6/8+PqmaRI6QYcdTspqXNh9q9nNglvi43Xn4tSIpEhW8aSHea9hgWZV8sG+i/4W9Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"@angular-devkit/core": "18.2.6",
"@angular-devkit/schematics": "18.2.6",
"@angular-devkit/core": "18.2.7",
"@angular-devkit/schematics": "18.2.7",
"jsonc-parser": "3.3.1"
},
"engines": {
@ -4382,13 +4383,15 @@
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz",
"integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==",
"dev": true
"dev": true,
"license": "MIT"
},
"node_modules/@types/cors": {
"version": "2.8.17",
"resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz",
"integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/node": "*"
}
@ -5135,6 +5138,7 @@
"resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
"integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==",
"dev": true,
"license": "MIT",
"engines": {
"node": "^4.5.0 || >= 5.9"
}
@ -5922,6 +5926,7 @@
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
"integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
@ -6009,6 +6014,7 @@
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
"dev": true,
"license": "MIT",
"dependencies": {
"object-assign": "^4",
"vary": "^1"
@ -6572,10 +6578,11 @@
}
},
"node_modules/engine.io": {
"version": "6.5.4",
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.4.tgz",
"integrity": "sha512-KdVSDKhVKyOi+r5uEabrDLZw2qXStVvCsEB/LN3mw4WFi6Gx50jTyuxYVCwAAC0U46FdnzP/ScKRBTXb/NiEOg==",
"version": "6.6.1",
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.1.tgz",
"integrity": "sha512-NEpDCw9hrvBW+hVEOK4T7v0jFJ++KgtPl4jKFwsZVfG1XhS0dCrSb3VMb9gPAd7VAdW52VT1EnaNiU2vM8C0og==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/cookie": "^0.4.1",
"@types/cors": "^2.8.12",
@ -6586,17 +6593,18 @@
"cors": "~2.8.5",
"debug": "~4.3.1",
"engine.io-parser": "~5.2.1",
"ws": "~8.11.0"
"ws": "~8.17.1"
},
"engines": {
"node": ">=10.2.0"
}
},
"node_modules/engine.io-parser": {
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.2.tgz",
"integrity": "sha512-RcyUFKA93/CXH20l4SoVvzZfrSDMOTUS3bWVpTt2FuFP+XYrL8i8oonHP7WInRyVHXh0n/ORtoeiE1os+8qkSw==",
"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"
}
@ -9433,12 +9441,13 @@
}
},
"node_modules/micromatch": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
"integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
"dev": true,
"license": "MIT",
"dependencies": {
"braces": "^3.0.2",
"braces": "^3.0.3",
"picomatch": "^2.3.1"
},
"engines": {
@ -9896,6 +9905,20 @@
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
"dev": true
},
"node_modules/ngx-toastr": {
"version": "19.0.0",
"resolved": "https://registry.npmjs.org/ngx-toastr/-/ngx-toastr-19.0.0.tgz",
"integrity": "sha512-6pTnktwwWD+kx342wuMOWB4+bkyX9221pAgGz3SHOJH0/MI9erLucS8PeeJDFwbUYyh75nQ6AzVtolgHxi52dQ==",
"license": "MIT",
"dependencies": {
"tslib": "^2.3.0"
},
"peerDependencies": {
"@angular/common": ">=16.0.0-0",
"@angular/core": ">=16.0.0-0",
"@angular/platform-browser": ">=16.0.0-0"
}
},
"node_modules/nice-napi": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz",
@ -10251,6 +10274,7 @@
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
@ -11973,16 +11997,17 @@
}
},
"node_modules/socket.io": {
"version": "4.7.5",
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.5.tgz",
"integrity": "sha512-DmeAkF6cwM9jSfmp6Dr/5/mfMwb5Z5qRrSXLpo3Fq5SqyU8CMF15jIN4ZhfSwu35ksM1qmHZDQ/DK5XTccSTvA==",
"version": "4.8.0",
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.0.tgz",
"integrity": "sha512-8U6BEgGjQOfGz3HHTYaC/L1GaxDCJ/KM0XTkJly0EhZ5U/du9uNEZy4ZgYzEzIqlx2CMm25CrCqr1ck899eLNA==",
"dev": true,
"license": "MIT",
"dependencies": {
"accepts": "~1.3.4",
"base64id": "~2.0.0",
"cors": "~2.8.5",
"debug": "~4.3.2",
"engine.io": "~6.5.2",
"engine.io": "~6.6.0",
"socket.io-adapter": "~2.5.2",
"socket.io-parser": "~4.2.4"
},
@ -11991,13 +12016,14 @@
}
},
"node_modules/socket.io-adapter": {
"version": "2.5.4",
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.4.tgz",
"integrity": "sha512-wDNHGXGewWAjQPt3pyeYBtpWSq9cLE5UW1ZUPL/2eGK9jtse/FpXib7epSTsz0Q0m+6sg6Y4KtcFTlah1bdOVg==",
"version": "2.5.5",
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz",
"integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==",
"dev": true,
"license": "MIT",
"dependencies": {
"debug": "~4.3.4",
"ws": "~8.11.0"
"ws": "~8.17.1"
}
},
"node_modules/socket.io-parser": {
@ -13624,9 +13650,9 @@
}
},
"node_modules/webpack-dev-server/node_modules/http-proxy-middleware": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz",
"integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==",
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.7.tgz",
"integrity": "sha512-fgVY8AV7qU7z/MmXJ/rxwbrtQH4jBQ9m7kp3llF0liB7glmFeVZFBepQb32T3y8n8k2+AEYuMPCpinYW+/CuRA==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -13955,16 +13981,17 @@
"dev": true
},
"node_modules/ws": {
"version": "8.11.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz",
"integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==",
"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"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": "^5.0.2"
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {

View File

@ -1,6 +1,6 @@
{
"name": "frontend",
"version": "1.0.0-b7",
"version": "1.0.0-b8",
"scripts": {
"ng": "ng",
"start": "ng serve",
@ -10,27 +10,28 @@
},
"private": true,
"dependencies": {
"@angular/animations": "^18.2.6",
"@angular/cdk": "~18.2.6",
"@angular/cdk-experimental": "^18.2.6",
"@angular/common": "^18.2.6",
"@angular/compiler": "^18.2.6",
"@angular/core": "^18.2.6",
"@angular/forms": "^18.2.6",
"@angular/material": "~18.2.6",
"@angular/platform-browser": "^18.2.6",
"@angular/platform-browser-dynamic": "^18.2.6",
"@angular/router": "^18.2.6",
"@angular/animations": "^18.2.7",
"@angular/cdk": "~18.2.7",
"@angular/cdk-experimental": "^18.2.7",
"@angular/common": "^18.2.7",
"@angular/compiler": "^18.2.7",
"@angular/core": "^18.2.7",
"@angular/forms": "^18.2.7",
"@angular/material": "~18.2.7",
"@angular/platform-browser": "^18.2.7",
"@angular/platform-browser-dynamic": "^18.2.7",
"@angular/router": "^18.2.7",
"@dhutaryan/ngx-mat-timepicker": "^18.0.2",
"@progress/kendo-date-math": "^1.5.13",
"ngx-toastr": "^19.0.0",
"rxjs": "~7.8.1",
"tslib": "^2.7.0",
"zone.js": "^0.14.10"
},
"devDependencies": {
"@angular-devkit/build-angular": "^18.2.6",
"@angular/cli": "^18.2.6",
"@angular/compiler-cli": "^18.2.6",
"@angular-devkit/build-angular": "^18.2.7",
"@angular/cli": "^18.2.7",
"@angular/compiler-cli": "^18.2.7",
"@types/jasmine": "~5.1.4",
"jasmine-core": "~5.3.0",
"karma": "~6.4.4",

View File

@ -64,36 +64,4 @@ export class RequestBuilder {
needAuth: false
};
}
public getEndpoint(): string {
return this.endpoint;
}
public getQueryParams(): Record<string, string | number | boolean | Array<any> | null> | null {
return this.queryParams;
}
public getHttpHeaders(): HttpHeaders {
return this.httpHeaders;
}
public getData(): any {
return this.data;
}
public getSilenceMode(): boolean {
return this.silenceMode;
}
public static getStandardRequestData(): RequestData {
return {
endpoint: '',
queryParams: null,
httpHeaders: new HttpHeaders(),
data: null,
silenceMode: false,
withCredentials: false,
needAuth: false
};
}
}

View File

@ -1,35 +1,21 @@
import {catchError, distinctUntilChanged, filter, first, mergeMap, Observable, retryWhen, switchMap, timer} from "rxjs";
import {
BehaviorSubject,
catchError,
distinctUntilChanged,
filter,
first,
Observable,
of,
ReplaySubject,
switchMap
} from "rxjs";
import {HttpClient, HttpErrorResponse} from "@angular/common/http";
import {NotifyColor, OpenNotifyService} from "@service/open-notify.service";
import {environment} from "@environment";
import {Router} from "@angular/router";
import {Injectable} from "@angular/core";
import {RequestBuilder, RequestData} from "@api/RequestBuilder";
import {TokenRefreshService} from "@service/token-refresh.service";
import {AuthToken} from "@service/auth.service";
export function retryWithInterval<T>(): (source: Observable<T>) => Observable<T> {
return (source: Observable<T>) =>
source.pipe(
retryWhen((errors: Observable<any>) =>
errors.pipe(
mergeMap((error, index) => {
if (index < (environment.maxRetry < 0 ? Infinity : environment.maxRetry - 1) && !error.status.toString().startsWith('4') && !error.status.toString().startsWith('5')) {
console.log(`Retrying after ${environment.retryDelay}ms...`);
return timer(environment.retryDelay);
} else {
if (error.status.toString().startsWith('4'))
console.error(`Server returned a client code error`);
else
console.error(`Exceeded maximum retries (${environment.maxRetry})`);
throw error;
}
})
)
)
);
}
import {ToastrService} from "ngx-toastr";
import {AuthRoles} from "@model/AuthRoles";
export enum AvailableVersion {
v1
@ -37,16 +23,16 @@ export enum AvailableVersion {
@Injectable()
export default abstract class ApiService {
constructor(private http: HttpClient, private notify: OpenNotifyService, private router: Router, protected tokenRefreshService: TokenRefreshService) {
constructor(private http: HttpClient, private notify: ToastrService, private router: Router) {
}
private apiUrl = environment.apiUrl;
private static isRefreshingToken: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
private static refreshTokenSubject: ReplaySubject<any> = new ReplaySubject(1);
protected abstract basePath: string;
protected abstract version: AvailableVersion;
public static readonly tokenKey = 'auth_token';
private static addQuery(endpoint: string, queryParams?: Record<string, string | number | boolean | Array<any> | null> | null): string {
const url = new URL(endpoint);
@ -73,7 +59,7 @@ export default abstract class ApiService {
return ApiService.addQuery(ApiService.combineUrls(this.apiUrl, AvailableVersion[this.version], this.basePath, request.endpoint), request.queryParams);
}
private sendHttpRequest<Type>(method: 'get' | 'post' | 'delete' | 'put', request: RequestData): Observable<Type> {
private sendHttpRequest<Type>(method: 'get' | 'post' | 'delete' | 'put', request: RequestData, secondTry: boolean = false): Observable<Type> {
const doneEndpoint = this.combinedUrl(request);
return this.http.request<Type>(method, doneEndpoint, {
@ -81,36 +67,59 @@ export default abstract class ApiService {
headers: request.httpHeaders,
body: request.data
}).pipe(
retryWithInterval<Type>(),
catchError(error => {
if (!secondTry && error.status === 401)
return this.handle401Error().pipe(
switchMap(() => this.sendHttpRequest<Type>(method, request, true))
);
else {
if (!request.silenceMode)
this.handleError(error);
throw error;
}
})
);
}
private refreshToken(): Observable<AuthRoles> {
return this.http.get<AuthRoles>(ApiService.combineUrls(this.apiUrl, AvailableVersion[AvailableVersion.v1], 'Auth', 'ReLogin'), {
withCredentials: true
});
}
private handle401Error(): Observable<any> {
if (ApiService.isRefreshingToken.value)
return ApiService.refreshTokenSubject.asObservable();
ApiService.isRefreshingToken.next(true);
return this.refreshToken().pipe(
switchMap(_ => {
ApiService.isRefreshingToken.next(false);
ApiService.refreshTokenSubject.next(null);
return of(null);
}),
catchError(err => {
ApiService.isRefreshingToken.next(false);
ApiService.refreshTokenSubject.error(err);
ApiService.refreshTokenSubject = new ReplaySubject(1);
throw err;
})
);
}
private makeHttpRequest<Type>(method: 'get' | 'post' | 'delete' | 'put', request: RequestData): Observable<Type> {
if (request.needAuth)
return this.tokenRefreshService.getTokenRefreshing$().pipe(
if (request.needAuth) {
return ApiService.isRefreshingToken.pipe(
distinctUntilChanged(),
filter(isRefreshing => !isRefreshing),
first(),
switchMap(() => {
const token = localStorage.getItem(ApiService.tokenKey);
if (token) {
const authToken = AuthToken.httpHeader((JSON.parse(token) as AuthToken));
authToken.keys().forEach(key => request.httpHeaders = request.httpHeaders.append(key, authToken.get(key) ?? ''));
}
return this.sendHttpRequest<Type>(method, request);
})
switchMap(() => this.sendHttpRequest<Type>(method, request))
);
} else {
return this.sendHttpRequest<Type>(method, request);
}
}
private getRequest(request: RequestData | string | null): RequestData {
if (request === null)
@ -143,6 +152,7 @@ export default abstract class ApiService {
public addAuth(request: RequestData) {
request.needAuth = true;
request.withCredentials = true;
return this;
}
@ -153,41 +163,45 @@ export default abstract class ApiService {
return;
}
let message: string;
let title: string;
let message: string | undefined = undefined;
if (error.error instanceof ErrorEvent) {
message = `Произошла ошибка: ${error.error.message}`;
title = `Произошла ошибка: ${error.error.message}`;
} else {
switch (error.status) {
case 0:
message = 'Неизвестная ошибка. Пожалуйста, попробуйте позже.';
title = 'Неизвестная ошибка. Пожалуйста, попробуйте позже.';
break;
case 400:
message = 'Ошибка запроса. Пожалуйста, проверьте отправленные данные.';
title = 'Ошибка запроса. Пожалуйста, проверьте отправленные данные.';
break;
case 401:
this.router.navigate(['/login/']).then();
message = 'Ошибка авторизации. Пожалуйста, выполните вход с правильными учетными данными.';
title = 'Ошибка авторизации. Пожалуйста, выполните вход с правильными учетными данными.';
break;
case 403:
message = 'Отказано в доступе. У вас нет разрешения на выполнение этого действия.';
title = 'Отказано в доступе. У вас нет разрешения на выполнение этого действия.';
break;
case 404:
message = 'Запрашиваемый ресурс не найден.';
title = 'Запрашиваемый ресурс не найден.';
break;
case 500:
message = 'Внутренняя ошибка сервера. Пожалуйста, попробуйте позже.';
title = 'Внутренняя ошибка сервера. Пожалуйста, попробуйте позже.';
break;
case 503:
message = 'Сервер на обслуживании. Пожалуйста, попробуйте позже.';
title = 'Сервер на обслуживании. Пожалуйста, попробуйте позже.';
break;
default:
message = `Сервер вернул код ошибки: ${error.status}`;
title = `Сервер вернул код ошибки: ${error.status}`;
break;
}
if (error.error?.Error) {
message += ` ${error.error.Error}`;
}
}
this.notify.open(message, NotifyColor.Danger);
if (error.error?.Error)
message = error.error.Error;
else if (error.error instanceof String)
message = error.error.toString();
else
message = error.error.statusMessage;
}
this.notify.error(message == '' ? undefined : message, title);
}
}

View File

@ -1,10 +1,8 @@
import {Injectable} from "@angular/core";
import ApiService, {AvailableVersion} from "@api/api.service";
import {LoginRequest} from "@api/v1/loginRequest";
import {TokenResponse} from "@api/v1/tokenResponse";
import {catchError, of, tap} from "rxjs";
import {catchError, of} from "rxjs";
import {AuthRoles} from "@model/AuthRoles";
import {AuthService, AvailableAuthenticationProvider} from "@service/auth.service";
@Injectable()
export default class AuthApiService extends ApiService {
@ -18,13 +16,16 @@ export default class AuthApiService extends ApiService {
.setWithCredentials()
.build;
return this.post<TokenResponse>(request)
.pipe(
tap(response => {
AuthService.setToken(response, AvailableAuthenticationProvider.Bearer, this.combinedUrl(this.createRequestBuilder().setEndpoint('ReLogin').build));
this.tokenRefreshService.setRefreshTokenExpireMs(response.expiresIn);
})
);
return this.post<AuthRoles>(request);
}
public reLogin(){
let request = this.createRequestBuilder()
.setEndpoint('ReLogin')
.setWithCredentials()
.build;
return this.get<AuthRoles>(request);
}
public logout() {
@ -33,13 +34,7 @@ export default class AuthApiService extends ApiService {
.setEndpoint('Logout')
.build;
return this.addAuth(request)
.get(request)
.pipe(
tap(_ => {
localStorage.removeItem(ApiService.tokenKey);
})
);
return this.addAuth(request).get(request);
}
public getRole(isSilence: boolean = true) {

View File

@ -4,7 +4,23 @@ import {provideRouter} from '@angular/router';
import {routes} from './app.routes';
import {provideAnimationsAsync} from '@angular/platform-browser/animations/async';
import {provideHttpClient} from "@angular/common/http";
import {provideToastr} from "ngx-toastr";
export const appConfig: ApplicationConfig = {
providers: [provideRouter(routes), provideAnimationsAsync(), provideHttpClient()]
providers: [
provideRouter(routes),
provideAnimationsAsync(),
provideHttpClient(),
provideToastr({
timeOut: 5000,
extendedTimeOut: 2000,
positionClass: "toast-top-right",
progressBar: true,
progressAnimation: "decreasing",
newestOnTop: true,
tapToDismiss: true,
disableTimeOut: false,
autoDismiss: true,
maxOpened: 5
})]
};

View File

@ -6,7 +6,9 @@
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap" rel="stylesheet">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
</head>
<body class="mat-typography">

View File

@ -82,7 +82,7 @@ export class LoginComponent {
})
.pipe(catchError(error => {
this.loaderActive = false;
this.errorText = error.error;
this.errorText = error.error instanceof String ? error.error : error.statusText;
this.loginButtonIsDisable = true;
throw error;
}))

View File

@ -1,83 +0,0 @@
import {Injectable} from '@angular/core';
import {HttpClient, HttpHeaders} from "@angular/common/http";
import {map, Observable, throwError} from "rxjs";
import {TokenResponse} from "@api/v1/tokenResponse";
import ApiService from "@api/api.service";
export enum AvailableAuthenticationProvider {
Bearer
}
export class AuthToken {
accessToken: string;
expiresIn: Date;
authProvider: AvailableAuthenticationProvider;
endpoint: string;
constructor(accessToken: string, expiresIn: Date, authProvider: AvailableAuthenticationProvider, refreshEndpoint: string) {
this.accessToken = accessToken;
this.expiresIn = expiresIn;
this.authProvider = authProvider;
this.endpoint = refreshEndpoint;
}
public static httpHeader(token: AuthToken): HttpHeaders {
let header = new HttpHeaders();
if (token.authProvider === AvailableAuthenticationProvider.Bearer)
header = header.set('Authorization', `Bearer ${token.accessToken}`);
return header;
}
}
@Injectable({
providedIn: 'root',
})
export class AuthService {
constructor(private http: HttpClient) {
}
public static setToken(token: TokenResponse, provider: AvailableAuthenticationProvider, refreshEndpoint: string) {
localStorage.setItem(ApiService.tokenKey, JSON.stringify(
new AuthToken(token.accessToken, token.expiresIn, provider, refreshEndpoint)
));
}
public static get tokenExpiresIn(): Date {
const token = localStorage.getItem(ApiService.tokenKey);
if (!token)
return new Date();
const result = new Date((JSON.parse(token) as AuthToken).expiresIn);
return result <= new Date() ? new Date() : result;
}
public refreshToken(): Observable<Date> {
const token = localStorage.getItem(ApiService.tokenKey);
if (!token)
return throwError(() => new Error("Token is not found"));
const authToken = JSON.parse(token) as AuthToken;
switch (authToken.authProvider) {
case AvailableAuthenticationProvider.Bearer:
return this.http.get<TokenResponse>(authToken.endpoint, {withCredentials: true})
.pipe(
map(response => {
const newExpireDate = new Date(response.expiresIn);
const oldExpireDate = new Date(authToken.expiresIn);
if (newExpireDate.getTime() !== oldExpireDate.getTime()) {
AuthService.setToken(response, AvailableAuthenticationProvider.Bearer, authToken.endpoint);
return newExpireDate;
}
return newExpireDate;
})
);
}
}
}

View File

@ -1,59 +0,0 @@
import {Injectable} from '@angular/core';
import {MatSnackBar} from '@angular/material/snack-bar';
import {NotificationComponent} from "@component/common/notification/notification.component";
export enum NotifyColor {
Basic,
Warn,
Danger,
Success
}
@Injectable({
providedIn: 'root'
})
export class OpenNotifyService {
constructor(private _snackBar: MatSnackBar) {
}
colorClass: string = '';
set setColorClass(color: NotifyColor) {
switch (color) {
case NotifyColor.Warn: {
this.colorClass = 'yellow-snackbar';
}
break;
case NotifyColor.Danger: {
this.colorClass = 'red-snackbar';
}
break;
case NotifyColor.Success: {
this.colorClass = 'green-snackbar';
}
break;
default: {
this.colorClass = 'snackbar';
}
break;
}
}
open(message: string, color: NotifyColor = NotifyColor.Basic, duration: number = 5000) {
this.setColorClass = color;
this._snackBar.openFromComponent(NotificationComponent, {
data: {
message: message,
duration: duration,
className: this.colorClass,
color: color === NotifyColor.Danger ? "accent" : "primary"
},
duration: duration,
verticalPosition: 'top',
horizontalPosition: 'center',
panelClass: [this.colorClass]
})
;
}
}

View File

@ -10,22 +10,194 @@ body {
font-family: Roboto, "Helvetica Neue", sans-serif;
}
.green-snackbar {
--mdc-snackbar-container-color: #8CBA51;
color: black;
/* based on angular-toastr css https://github.com/Foxandxss/angular-toastr/blob/cb508fe6801d6b288d3afc525bb40fee1b101650/dist/angular-toastr.css */
/* position */
.toast-center-center {
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.toast-top-center {
top: 0;
right: 0;
width: 100%;
}
.toast-bottom-center {
bottom: 0;
right: 0;
width: 100%;
}
.toast-top-full-width {
top: 0;
right: 0;
width: 100%;
}
.toast-bottom-full-width {
bottom: 0;
right: 0;
width: 100%;
}
.toast-top-left {
top: 12px;
left: 12px;
}
.toast-top-right {
top: 12px;
right: 12px;
}
.toast-bottom-right {
right: 12px;
bottom: 12px;
}
.toast-bottom-left {
bottom: 12px;
left: 12px;
}
.red-snackbar {
--mdc-snackbar-container-color: #E20338;
color: white;
/* toast styles */
.toast-title {
font-weight: bold;
}
.yellow-snackbar {
--mdc-snackbar-container-color: #FFD600;
color: black;
.toast-message {
word-wrap: break-word;
}
.toast-message a,
.toast-message label {
color: #FFFFFF;
}
.toast-message a:hover {
color: #CCCCCC;
text-decoration: none;
}
.toast-close-button {
position: relative;
right: -0.3em;
top: -0.3em;
float: right;
font-size: 20px;
font-weight: bold;
color: #FFFFFF;
text-shadow: 0 1px 0 #ffffff;
/* opacity: 0.8; */
}
.toast-close-button:hover,
.toast-close-button:focus {
color: #000000;
text-decoration: none;
cursor: pointer;
opacity: 0.4;
}
/*Additional properties for button version
iOS requires the button element instead of an anchor tag.
If you want the anchor version, it requires `href="#"`.*/
button.toast-close-button {
padding: 0;
cursor: pointer;
background: transparent;
border: 0;
}
.toast-container {
pointer-events: none;
position: fixed;
z-index: 999999;
}
.toast-container * {
box-sizing: border-box;
}
.toast-container .ngx-toastr {
position: relative;
overflow: hidden;
margin: 0 0 6px;
padding: 15px 15px 15px 50px;
width: 300px;
border-radius: 3px 3px 3px 3px;
background-position: 15px center;
background-repeat: no-repeat;
background-size: 24px;
box-shadow: 0 0 12px #999999;
color: #FFFFFF;
}
.toast-container .ngx-toastr:hover {
box-shadow: 0 0 12px #000000;
opacity: 1;
cursor: pointer;
}
/* https://github.com/FortAwesome/Font-Awesome-Pro/blob/master/advanced-options/raw-svg/regular/info-circle.svg */
.toast-info {
background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHZpZXdCb3g9JzAgMCA1MTIgNTEyJyB3aWR0aD0nNTEyJyBoZWlnaHQ9JzUxMic+PHBhdGggZmlsbD0ncmdiKDI1NSwyNTUsMjU1KScgZD0nTTI1NiA4QzExOS4wNDMgOCA4IDExOS4wODMgOCAyNTZjMCAxMzYuOTk3IDExMS4wNDMgMjQ4IDI0OCAyNDhzMjQ4LTExMS4wMDMgMjQ4LTI0OEM1MDQgMTE5LjA4MyAzOTIuOTU3IDggMjU2IDh6bTAgMTEwYzIzLjE5NiAwIDQyIDE4LjgwNCA0MiA0MnMtMTguODA0IDQyLTQyIDQyLTQyLTE4LjgwNC00Mi00MiAxOC44MDQtNDIgNDItNDJ6bTU2IDI1NGMwIDYuNjI3LTUuMzczIDEyLTEyIDEyaC04OGMtNi42MjcgMC0xMi01LjM3My0xMi0xMnYtMjRjMC02LjYyNyA1LjM3My0xMiAxMi0xMmgxMnYtNjRoLTEyYy02LjYyNyAwLTEyLTUuMzczLTEyLTEydi0yNGMwLTYuNjI3IDUuMzczLTEyIDEyLTEyaDY0YzYuNjI3IDAgMTIgNS4zNzMgMTIgMTJ2MTAwaDEyYzYuNjI3IDAgMTIgNS4zNzMgMTIgMTJ2MjR6Jy8+PC9zdmc+");
}
/* https://github.com/FortAwesome/Font-Awesome-Pro/blob/master/advanced-options/raw-svg/regular/times-circle.svg */
.toast-error {
background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHZpZXdCb3g9JzAgMCA1MTIgNTEyJyB3aWR0aD0nNTEyJyBoZWlnaHQ9JzUxMic+PHBhdGggZmlsbD0ncmdiKDI1NSwyNTUsMjU1KScgZD0nTTI1NiA4QzExOSA4IDggMTE5IDggMjU2czExMSAyNDggMjQ4IDI0OCAyNDgtMTExIDI0OC0yNDhTMzkzIDggMjU2IDh6bTEyMS42IDMxMy4xYzQuNyA0LjcgNC43IDEyLjMgMCAxN0wzMzggMzc3LjZjLTQuNyA0LjctMTIuMyA0LjctMTcgMEwyNTYgMzEybC02NS4xIDY1LjZjLTQuNyA0LjctMTIuMyA0LjctMTcgMEwxMzQuNCAzMzhjLTQuNy00LjctNC43LTEyLjMgMC0xN2w2NS42LTY1LTY1LjYtNjUuMWMtNC43LTQuNy00LjctMTIuMyAwLTE3bDM5LjYtMzkuNmM0LjctNC43IDEyLjMtNC43IDE3IDBsNjUgNjUuNyA2NS4xLTY1LjZjNC43LTQuNyAxMi4zLTQuNyAxNyAwbDM5LjYgMzkuNmM0LjcgNC43IDQuNyAxMi4zIDAgMTdMMzEyIDI1Nmw2NS42IDY1LjF6Jy8+PC9zdmc+");
}
/* https://github.com/FortAwesome/Font-Awesome-Pro/blob/master/advanced-options/raw-svg/regular/check.svg */
.toast-success {
background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHZpZXdCb3g9JzAgMCA1MTIgNTEyJyB3aWR0aD0nNTEyJyBoZWlnaHQ9JzUxMic+PHBhdGggZmlsbD0ncmdiKDI1NSwyNTUsMjU1KScgZD0nTTE3My44OTggNDM5LjQwNGwtMTY2LjQtMTY2LjRjLTkuOTk3LTkuOTk3LTkuOTk3LTI2LjIwNiAwLTM2LjIwNGwzNi4yMDMtMzYuMjA0YzkuOTk3LTkuOTk4IDI2LjIwNy05Ljk5OCAzNi4yMDQgMEwxOTIgMzEyLjY5IDQzMi4wOTUgNzIuNTk2YzkuOTk3LTkuOTk3IDI2LjIwNy05Ljk5NyAzNi4yMDQgMGwzNi4yMDMgMzYuMjA0YzkuOTk3IDkuOTk3IDkuOTk3IDI2LjIwNiAwIDM2LjIwNGwtMjk0LjQgMjk0LjQwMWMtOS45OTggOS45OTctMjYuMjA3IDkuOTk3LTM2LjIwNC0uMDAxeicvPjwvc3ZnPg==");
}
/* https://github.com/FortAwesome/Font-Awesome-Pro/blob/master/advanced-options/raw-svg/regular/exclamation-triangle.svg */
.toast-warning {
background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHZpZXdCb3g9JzAgMCA1NzYgNTEyJyB3aWR0aD0nNTc2JyBoZWlnaHQ9JzUxMic+PHBhdGggZmlsbD0ncmdiKDI1NSwyNTUsMjU1KScgZD0nTTU2OS41MTcgNDQwLjAxM0M1ODcuOTc1IDQ3Mi4wMDcgNTY0LjgwNiA1MTIgNTI3Ljk0IDUxMkg0OC4wNTRjLTM2LjkzNyAwLTU5Ljk5OS00MC4wNTUtNDEuNTc3LTcxLjk4N0wyNDYuNDIzIDIzLjk4NWMxOC40NjctMzIuMDA5IDY0LjcyLTMxLjk1MSA4My4xNTQgMGwyMzkuOTQgNDE2LjAyOHpNMjg4IDM1NGMtMjUuNDA1IDAtNDYgMjAuNTk1LTQ2IDQ2czIwLjU5NSA0NiA0NiA0NiA0Ni0yMC41OTUgNDYtNDYtMjAuNTk1LTQ2LTQ2LTQ2em0tNDMuNjczLTE2NS4zNDZsNy40MTggMTM2Yy4zNDcgNi4zNjQgNS42MDkgMTEuMzQ2IDExLjk4MiAxMS4zNDZoNDguNTQ2YzYuMzczIDAgMTEuNjM1LTQuOTgyIDExLjk4Mi0xMS4zNDZsNy40MTgtMTM2Yy4zNzUtNi44NzQtNS4wOTgtMTIuNjU0LTExLjk4Mi0xMi42NTRoLTYzLjM4M2MtNi44ODQgMC0xMi4zNTYgNS43OC0xMS45ODEgMTIuNjU0eicvPjwvc3ZnPg==");
}
.toast-container.toast-top-center .ngx-toastr,
.toast-container.toast-bottom-center .ngx-toastr {
width: 300px;
margin-left: auto;
margin-right: auto;
}
.toast-container.toast-top-full-width .ngx-toastr,
.toast-container.toast-bottom-full-width .ngx-toastr {
width: 96%;
margin-left: auto;
margin-right: auto;
}
.ngx-toastr {
background-color: #030303;
pointer-events: auto;
}
.toast-success {
background-color: #51A351;
}
.toast-error {
background-color: #BD362F;
}
.toast-info {
background-color: #2F96B4;
}
.toast-warning {
background-color: #F89406;
}
.toast-progress {
position: absolute;
left: 0;
bottom: 0;
height: 4px;
background-color: #000000;
opacity: 0.4;
}
/* Responsive Design */
@media all and (max-width: 240px) {
.toast-container .ngx-toastr.div {
padding: 8px 8px 8px 50px;
width: 11em;
}
.toast-container .toast-close-button {
right: -0.2em;
top: -0.2em;
}
}
@media all and (min-width: 241px) and (max-width: 480px) {
.toast-container .ngx-toastr.div {
padding: 8px 8px 8px 50px;
width: 18em;
}
.toast-container .toast-close-button {
right: -0.2em;
top: -0.2em;
}
}
@media all and (min-width: 481px) and (max-width: 768px) {
.toast-container .ngx-toastr.div {
padding: 15px 15px 15px 50px;
width: 25em;
}
.snackbar {
--mdc-snackbar-container-color: inherit;
color: inherit;
}