Compare commits

..

8 Commits

22 changed files with 593 additions and 312 deletions

262
package-lock.json generated
View File

@ -8,26 +8,26 @@
"name": "frontend",
"version": "1.0.0-a0",
"dependencies": {
"@angular/animations": "^18.0.1",
"@angular/cdk": "~18.0.1",
"@angular/cdk-experimental": "^18.0.1",
"@angular/common": "^18.0.1",
"@angular/compiler": "^18.0.1",
"@angular/core": "^18.0.1",
"@angular/forms": "^18.0.1",
"@angular/material": "~18.0.1",
"@angular/platform-browser": "^18.0.1",
"@angular/platform-browser-dynamic": "^18.0.1",
"@angular/router": "^18.0.1",
"@angular/animations": "^18.0.2",
"@angular/cdk": "~18.0.2",
"@angular/cdk-experimental": "^18.0.2",
"@angular/common": "^18.0.2",
"@angular/compiler": "^18.0.2",
"@angular/core": "^18.0.2",
"@angular/forms": "^18.0.2",
"@angular/material": "~18.0.2",
"@angular/platform-browser": "^18.0.2",
"@angular/platform-browser-dynamic": "^18.0.2",
"@angular/router": "^18.0.2",
"@progress/kendo-date-math": "^1.5.13",
"rxjs": "~7.8.1",
"tslib": "^2.6.2",
"tslib": "^2.6.3",
"zone.js": "~0.14.6"
},
"devDependencies": {
"@angular-devkit/build-angular": "^18.0.2",
"@angular/cli": "^18.0.2",
"@angular/compiler-cli": "^18.0.1",
"@angular-devkit/build-angular": "^18.0.3",
"@angular/cli": "^18.0.3",
"@angular/compiler-cli": "^18.0.2",
"@types/jasmine": "~5.1.4",
"jasmine-core": "~5.1.2",
"karma": "~6.4.3",
@ -52,13 +52,13 @@
}
},
"node_modules/@angular-devkit/architect": {
"version": "0.1800.2",
"resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1800.2.tgz",
"integrity": "sha512-PX7lCTAqWe9C40+fie+DAc8vhpGA+JgZKWWrMHUTV/iZx8RXx2X4xGQsqYu36p4i3MSfQdbn+0xLWGmjScPVOQ==",
"version": "0.1800.3",
"resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1800.3.tgz",
"integrity": "sha512-ZoQuvCN/Ft4XJ+/XouYFKGoyEYTfZ8I5yI1M4t19lkRb3MwpQribWcZu4PP+SNnS6/9qnW7guxiQGS+CVlqnDg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@angular-devkit/core": "18.0.2",
"@angular-devkit/core": "18.0.3",
"rxjs": "7.8.1"
},
"engines": {
@ -68,17 +68,17 @@
}
},
"node_modules/@angular-devkit/build-angular": {
"version": "18.0.2",
"resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-18.0.2.tgz",
"integrity": "sha512-cQkTx7XaIPj6+DXo6wZmO4iY0hOOfPDnSN/+m84XpBW0tuPGxH7Z9B6wV+Uwcpm9HGPqzRA7VZyPsqbK860b0Q==",
"version": "18.0.3",
"resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-18.0.3.tgz",
"integrity": "sha512-TTYPtQPqpI7V5H44oBqpPCYjwycWplOfhx/rjxDcrdGITYJF18rzwJs6mFx2QMBZl+99YYhxDajRCq05UDRQrw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@ampproject/remapping": "2.3.0",
"@angular-devkit/architect": "0.1800.2",
"@angular-devkit/build-webpack": "0.1800.2",
"@angular-devkit/core": "18.0.2",
"@angular/build": "18.0.2",
"@angular-devkit/architect": "0.1800.3",
"@angular-devkit/build-webpack": "0.1800.3",
"@angular-devkit/core": "18.0.3",
"@angular/build": "18.0.3",
"@babel/core": "7.24.5",
"@babel/generator": "7.24.5",
"@babel/helper-annotate-as-pure": "7.22.5",
@ -89,7 +89,7 @@
"@babel/preset-env": "7.24.5",
"@babel/runtime": "7.24.5",
"@discoveryjs/json-ext": "0.5.7",
"@ngtools/webpack": "18.0.2",
"@ngtools/webpack": "18.0.3",
"@vitejs/plugin-basic-ssl": "1.1.0",
"ansi-colors": "4.1.3",
"autoprefixer": "10.4.19",
@ -255,14 +255,21 @@
"node": ">=10"
}
},
"node_modules/@angular-devkit/build-angular/node_modules/tslib": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==",
"dev": true,
"license": "0BSD"
},
"node_modules/@angular-devkit/build-webpack": {
"version": "0.1800.2",
"resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1800.2.tgz",
"integrity": "sha512-CbTURBhZWzx+5KewS2Nkqy2rwBTFgDCvUwONGWuy1K68+85vOWUKqjkfvriHA+JkWN03w7FzWEtTfcOg0EzYkw==",
"version": "0.1800.3",
"resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1800.3.tgz",
"integrity": "sha512-qasDZI28gNsYTOWwJHoFZlVAyw47qlCXbPEma0VDCukZe5XX8RoZnN5ZA9nC8xpqKQ5pzJnPk7rAqa0dsEt9Xg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@angular-devkit/architect": "0.1800.2",
"@angular-devkit/architect": "0.1800.3",
"rxjs": "7.8.1"
},
"engines": {
@ -276,9 +283,9 @@
}
},
"node_modules/@angular-devkit/core": {
"version": "18.0.2",
"resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-18.0.2.tgz",
"integrity": "sha512-QXcEdfmODc0rKblBerk30yw70fypIkFm6gQBLJgsshpwc+TMA+fuMLcPQebOTzKLtD2tNUkk/7SrWPQIGqeXaA==",
"version": "18.0.3",
"resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-18.0.3.tgz",
"integrity": "sha512-nTs1KbNSVCVooPdDaeTh1YbggNVaqexbQjXNIvJJzRB8qPkWNPxm0pQeFjU7kWUBg2+aBXN4/CNwU1YHwxfiSQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -322,13 +329,13 @@
}
},
"node_modules/@angular-devkit/schematics": {
"version": "18.0.2",
"resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-18.0.2.tgz",
"integrity": "sha512-G9yGcoB67sH0eRNWoiQWNn2KwiI7sDasVscYPGKf1yo7JRiXmzX/LpfKRPsZTl+Bs0FItnwDInsqgMisK89/6g==",
"version": "18.0.3",
"resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-18.0.3.tgz",
"integrity": "sha512-utKGk9KHTvLsxpga3aaGJ7HDggMhZtBOHFb8phFK/GXazaeEGDvm7Sin2it2uw/i9xvu79RQ/IrWvebhanwU1g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@angular-devkit/core": "18.0.2",
"@angular-devkit/core": "18.0.3",
"jsonc-parser": "3.2.1",
"magic-string": "0.30.10",
"ora": "5.4.1",
@ -341,29 +348,29 @@
}
},
"node_modules/@angular/animations": {
"version": "18.0.1",
"resolved": "https://registry.npmjs.org/@angular/animations/-/animations-18.0.1.tgz",
"integrity": "sha512-QAY/oxfuFY2Bjr3foniWlLAiddXHu8879lZvXHt1NVOsiav+vD15IEEQsnuQbJPy/EHEnAlUh9UptB4zQIBp/Q==",
"version": "18.0.2",
"resolved": "https://registry.npmjs.org/@angular/animations/-/animations-18.0.2.tgz",
"integrity": "sha512-WhsotLl74UlRZZE9R7X3BXeNm1YOD1hUMOuGCa20pvUZ8X6ayz5c8B7tc/BZ0ua/9UkyZzbdMTEDi8JPDPPTew==",
"license": "MIT",
"dependencies": {
"tslib": "^2.3.0"
},
"engines": {
"node": "^18.13.0 || >=20.9.0"
"node": "^18.19.1 || ^20.11.1 || >=22.0.0"
},
"peerDependencies": {
"@angular/core": "18.0.1"
"@angular/core": "18.0.2"
}
},
"node_modules/@angular/build": {
"version": "18.0.2",
"resolved": "https://registry.npmjs.org/@angular/build/-/build-18.0.2.tgz",
"integrity": "sha512-iPPHdAJ3LiR8t/+39xjvrqMWcTmRrfphzKxXoIVDcswQjVQIk00EYuxinC6EVa7dSKDl1thk1MeCNZ9DIjaAvQ==",
"version": "18.0.3",
"resolved": "https://registry.npmjs.org/@angular/build/-/build-18.0.3.tgz",
"integrity": "sha512-AvyySRuNkmnMxKcoPs6NuddLaCVUWnoGnPmqqd1YY3mT/yxShorIZyrJ1loxzveIcgFJ65qDPGPhIfK7KxysYQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@ampproject/remapping": "2.3.0",
"@angular-devkit/architect": "0.1800.2",
"@angular-devkit/architect": "0.1800.3",
"@babel/core": "7.24.5",
"@babel/helper-annotate-as-pure": "7.22.5",
"@babel/helper-split-export-declaration": "7.24.5",
@ -486,9 +493,9 @@
}
},
"node_modules/@angular/cdk": {
"version": "18.0.1",
"resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-18.0.1.tgz",
"integrity": "sha512-2fCqX1sz5cM+LncO6ak4EU2ZBm8MWitv5V53go3Iz5dOVOdrvysBt8smEkWZ4nvEKkFYHEPpQo0YlxEWbuTEmA==",
"version": "18.0.2",
"resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-18.0.2.tgz",
"integrity": "sha512-KQTfi17PV/DYg8UcsoF9Jxfz+3nCPImm5eY1Mq7wGK8qUOlw9Y8fZ3eNU9ZH4hdg2FcKXJrAJqSA7I2nqdSX7w==",
"license": "MIT",
"dependencies": {
"tslib": "^2.3.0"
@ -503,29 +510,29 @@
}
},
"node_modules/@angular/cdk-experimental": {
"version": "18.0.1",
"resolved": "https://registry.npmjs.org/@angular/cdk-experimental/-/cdk-experimental-18.0.1.tgz",
"integrity": "sha512-Wr3RLxCJ9wKNGktEuAKNAxfcamIGxkO9z26CBzvDQ29MYMJqmdG9XY7U9ue4NDq8mNsUxCmtAHUg7zoysBi3ZQ==",
"version": "18.0.2",
"resolved": "https://registry.npmjs.org/@angular/cdk-experimental/-/cdk-experimental-18.0.2.tgz",
"integrity": "sha512-dgMCV7v2XHiu7Gj0CtJYgu10M0bYlpi3+fYwM2rX3mEG3/I8jgnUX/CnLP9BjQbrhgK5MrsLP7zGRlCAqB3VaQ==",
"license": "MIT",
"dependencies": {
"tslib": "^2.3.0"
},
"peerDependencies": {
"@angular/cdk": "18.0.1",
"@angular/cdk": "18.0.2",
"@angular/core": "^18.0.0 || ^19.0.0"
}
},
"node_modules/@angular/cli": {
"version": "18.0.2",
"resolved": "https://registry.npmjs.org/@angular/cli/-/cli-18.0.2.tgz",
"integrity": "sha512-shrxMD1bcWWh7WpBN3KTV+Lt8E62gURSUFhs6kdGLepMDif8LPAv45+hpt8SBU9VfQuL6AHa4cW8uDL9BKGlYA==",
"version": "18.0.3",
"resolved": "https://registry.npmjs.org/@angular/cli/-/cli-18.0.3.tgz",
"integrity": "sha512-1i51QeLwLpIdBbwOANSLFAuqXOGRpvSHCuZo1SojkvZ1COZ5jJZoCaKRZzCFsA/16gv/jcTEfBGpoAXLC2lRog==",
"dev": true,
"license": "MIT",
"dependencies": {
"@angular-devkit/architect": "0.1800.2",
"@angular-devkit/core": "18.0.2",
"@angular-devkit/schematics": "18.0.2",
"@schematics/angular": "18.0.2",
"@angular-devkit/architect": "0.1800.3",
"@angular-devkit/core": "18.0.3",
"@angular-devkit/schematics": "18.0.3",
"@schematics/angular": "18.0.3",
"@yarnpkg/lockfile": "1.1.0",
"ansi-colors": "4.1.3",
"ini": "4.1.2",
@ -562,34 +569,34 @@
}
},
"node_modules/@angular/common": {
"version": "18.0.1",
"resolved": "https://registry.npmjs.org/@angular/common/-/common-18.0.1.tgz",
"integrity": "sha512-iADQC5m4fvk+VNXEoU1KR93b0eG218/GuNdzUNVJHcjxdFxPshKk5fiaGSosUCxXPRQOxDKzmS9EDang87E/Ew==",
"version": "18.0.2",
"resolved": "https://registry.npmjs.org/@angular/common/-/common-18.0.2.tgz",
"integrity": "sha512-7CK5sFptUFWE3ZrKl8MjgoKjYKC20SN089F4xQIYtP2qM3IoJH/X7qa+5Eidk4PovS1SuCJHrB5AoREWwtWJHA==",
"license": "MIT",
"dependencies": {
"tslib": "^2.3.0"
},
"engines": {
"node": "^18.13.0 || >=20.9.0"
"node": "^18.19.1 || ^20.11.1 || >=22.0.0"
},
"peerDependencies": {
"@angular/core": "18.0.1",
"@angular/core": "18.0.2",
"rxjs": "^6.5.3 || ^7.4.0"
}
},
"node_modules/@angular/compiler": {
"version": "18.0.1",
"resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-18.0.1.tgz",
"integrity": "sha512-zyG/ifCtN0drAuwz0oV6LtzTiDREsM1Ay7eJW9wTvp3NCv06goHLtHXX12eFfZQWJViBv924lyRDSWdZN7r3GQ==",
"version": "18.0.2",
"resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-18.0.2.tgz",
"integrity": "sha512-9PKi++yKq3SvsTteZAhAJsiueEWzl64SpIBwooWRaSav6Jfl9Y2+b46SXTdAIuNdcGNW7ZeDZbPK3RPtxsROrA==",
"license": "MIT",
"dependencies": {
"tslib": "^2.3.0"
},
"engines": {
"node": "^18.13.0 || >=20.9.0"
"node": "^18.19.1 || ^20.11.1 || >=22.0.0"
},
"peerDependencies": {
"@angular/core": "18.0.1"
"@angular/core": "18.0.2"
},
"peerDependenciesMeta": {
"@angular/core": {
@ -598,9 +605,9 @@
}
},
"node_modules/@angular/compiler-cli": {
"version": "18.0.1",
"resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-18.0.1.tgz",
"integrity": "sha512-Aoz70+/o8R2lG2EGDAYbj6yu2B7kqa/9loYEwG0fECJTtXoRBP+bEGpUxMmxOb59tMDnbIhBHmNPPEQVTXvgSQ==",
"version": "18.0.2",
"resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-18.0.2.tgz",
"integrity": "sha512-+HIYJ0WIAg75mS30KzCN9gO2SeZXF4A8CeKOwBKhIvlq9kkaTpgmpDkVx814e9z3OeIqCEUn10qebJTwZgtZDA==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -619,23 +626,23 @@
"ngcc": "bundles/ngcc/index.js"
},
"engines": {
"node": "^18.13.0 || >=20.9.0"
"node": "^18.19.1 || ^20.11.1 || >=22.0.0"
},
"peerDependencies": {
"@angular/compiler": "18.0.1",
"@angular/compiler": "18.0.2",
"typescript": ">=5.4 <5.5"
}
},
"node_modules/@angular/core": {
"version": "18.0.1",
"resolved": "https://registry.npmjs.org/@angular/core/-/core-18.0.1.tgz",
"integrity": "sha512-Db1livvugoLdLsWww5IqUS5v+yUN7/5Rj0trZv9BgxIuoNtoipfLqKHwZWpumH3yI5Ucu+UH9zZ1mlGyF0Kexw==",
"version": "18.0.2",
"resolved": "https://registry.npmjs.org/@angular/core/-/core-18.0.2.tgz",
"integrity": "sha512-5VtFaYz97X9sQpxRuFUBKu2gqgFVU/Obgk/Q8ZRw+TBhFnU4e9NFod7dtAJH9scCOWZYnU07+aeI6ChiRSKrZw==",
"license": "MIT",
"dependencies": {
"tslib": "^2.3.0"
},
"engines": {
"node": "^18.13.0 || >=20.9.0"
"node": "^18.19.1 || ^20.11.1 || >=22.0.0"
},
"peerDependencies": {
"rxjs": "^6.5.3 || ^7.4.0",
@ -643,27 +650,27 @@
}
},
"node_modules/@angular/forms": {
"version": "18.0.1",
"resolved": "https://registry.npmjs.org/@angular/forms/-/forms-18.0.1.tgz",
"integrity": "sha512-j1nUzwnZHO/BRXK0joQbAV10JWxeRVKmPzIaDulY2o28Er1jVKyw2T8EwI+xSvBbAqyJyaAd+ysWUhm3FfH+GA==",
"version": "18.0.2",
"resolved": "https://registry.npmjs.org/@angular/forms/-/forms-18.0.2.tgz",
"integrity": "sha512-AGuQVav7wbX6pRhjeE6c45dlWnhb+93ZHHBRT02Wg1PRyrgmebpoLAtiUmAR/YhR45zD6Q9o7fg/076+bnIcdQ==",
"license": "MIT",
"dependencies": {
"tslib": "^2.3.0"
},
"engines": {
"node": "^18.13.0 || >=20.9.0"
"node": "^18.19.1 || ^20.11.1 || >=22.0.0"
},
"peerDependencies": {
"@angular/common": "18.0.1",
"@angular/core": "18.0.1",
"@angular/platform-browser": "18.0.1",
"@angular/common": "18.0.2",
"@angular/core": "18.0.2",
"@angular/platform-browser": "18.0.2",
"rxjs": "^6.5.3 || ^7.4.0"
}
},
"node_modules/@angular/material": {
"version": "18.0.1",
"resolved": "https://registry.npmjs.org/@angular/material/-/material-18.0.1.tgz",
"integrity": "sha512-y8OaESXw32P74Jh2FEr3n7QjqjTlo2Jf+XdgOvp5dd1yxpJ20vnK7ZCEQqCpxdxGAzXqR+2DccKk9tebB9egZw==",
"version": "18.0.2",
"resolved": "https://registry.npmjs.org/@angular/material/-/material-18.0.2.tgz",
"integrity": "sha512-bns6X6HAonBnj+I0QkbqfD4u5ehs8HZP3ateb19ZbfhVLJEu0MB/AJfeM3cinPrQnTMdYBjIDTtIyQWh//EFiw==",
"license": "MIT",
"dependencies": {
"@material/animation": "15.0.0-canary.7f224ddd4.0",
@ -718,7 +725,7 @@
},
"peerDependencies": {
"@angular/animations": "^18.0.0 || ^19.0.0",
"@angular/cdk": "18.0.1",
"@angular/cdk": "18.0.2",
"@angular/common": "^18.0.0 || ^19.0.0",
"@angular/core": "^18.0.0 || ^19.0.0",
"@angular/forms": "^18.0.0 || ^19.0.0",
@ -727,20 +734,20 @@
}
},
"node_modules/@angular/platform-browser": {
"version": "18.0.1",
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-18.0.1.tgz",
"integrity": "sha512-rQUsOxZxiwSPvyHdne60IKIGsvFoVc1rO4mDyXU+9sCCLmPKHzNyEzp7vybTZeiqa3k6v3sV/bfHWwrRzmvenw==",
"version": "18.0.2",
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-18.0.2.tgz",
"integrity": "sha512-IdNyRMFtM5GCvueNFrXmwA1C5LUMi6aSccdA1fpsTFAGDleeT5oiKU82iIcVmdj+Kse233KQFU0HFyijy4W2/Q==",
"license": "MIT",
"dependencies": {
"tslib": "^2.3.0"
},
"engines": {
"node": "^18.13.0 || >=20.9.0"
"node": "^18.19.1 || ^20.11.1 || >=22.0.0"
},
"peerDependencies": {
"@angular/animations": "18.0.1",
"@angular/common": "18.0.1",
"@angular/core": "18.0.1"
"@angular/animations": "18.0.2",
"@angular/common": "18.0.2",
"@angular/core": "18.0.2"
},
"peerDependenciesMeta": {
"@angular/animations": {
@ -749,38 +756,38 @@
}
},
"node_modules/@angular/platform-browser-dynamic": {
"version": "18.0.1",
"resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-18.0.1.tgz",
"integrity": "sha512-lzjq7HjigGxO5oh5Sw0Vxa3mAVidYHpHFQr46/OSl9T5jLpStcjEqK0xcfQz9bf2hV+0qFfMqmd2k0XQl7feqg==",
"version": "18.0.2",
"resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-18.0.2.tgz",
"integrity": "sha512-wQlw3TgUEs5uZRT6mPIKFHCgOBDE4joar9b/0bjZv5SOUvJNkED+roNlRKxjQDIagOMAUlcD3OnynlNr6le6YA==",
"license": "MIT",
"dependencies": {
"tslib": "^2.3.0"
},
"engines": {
"node": "^18.13.0 || >=20.9.0"
"node": "^18.19.1 || ^20.11.1 || >=22.0.0"
},
"peerDependencies": {
"@angular/common": "18.0.1",
"@angular/compiler": "18.0.1",
"@angular/core": "18.0.1",
"@angular/platform-browser": "18.0.1"
"@angular/common": "18.0.2",
"@angular/compiler": "18.0.2",
"@angular/core": "18.0.2",
"@angular/platform-browser": "18.0.2"
}
},
"node_modules/@angular/router": {
"version": "18.0.1",
"resolved": "https://registry.npmjs.org/@angular/router/-/router-18.0.1.tgz",
"integrity": "sha512-PapdvfATjRZI0cJ/RH8n/ixHDHa4HIBaOMwhgU73InU9t6NIhBXg6aRECYV2qGt7NtpLYSHmG5Z1Ws86rm5Tyw==",
"version": "18.0.2",
"resolved": "https://registry.npmjs.org/@angular/router/-/router-18.0.2.tgz",
"integrity": "sha512-eIualVChd3bMA8GjKfAKL9wv7zKWx85Cu3b1qhUxrG3XyT40X1ud2GRHBKCuklUITcAR8HjUKnWuOjUDkhTT4Q==",
"license": "MIT",
"dependencies": {
"tslib": "^2.3.0"
},
"engines": {
"node": "^18.13.0 || >=20.9.0"
"node": "^18.19.1 || ^20.11.1 || >=22.0.0"
},
"peerDependencies": {
"@angular/common": "18.0.1",
"@angular/core": "18.0.1",
"@angular/platform-browser": "18.0.1",
"@angular/common": "18.0.2",
"@angular/core": "18.0.2",
"@angular/platform-browser": "18.0.2",
"rxjs": "^6.5.3 || ^7.4.0"
}
},
@ -4129,9 +4136,9 @@
]
},
"node_modules/@ngtools/webpack": {
"version": "18.0.2",
"resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-18.0.2.tgz",
"integrity": "sha512-I+ZNFGBnykUWBwGPCXy6m9R2fIX/ovnAUHylvThYd/M+FUfc+Z/3DpKEUBYIOLVCLNZR5nuK0t9QLlazYhWFgg==",
"version": "18.0.3",
"resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-18.0.3.tgz",
"integrity": "sha512-wYskgAomDgyBJ8fsP+jfb0rt1t5OpNx4EXEzZo37Nxb04P5CkW+9yQ/xuhPMF8hO/dfKL1k/BKAKUeOOUQmAIA==",
"dev": true,
"license": "MIT",
"engines": {
@ -4692,14 +4699,14 @@
]
},
"node_modules/@schematics/angular": {
"version": "18.0.2",
"resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-18.0.2.tgz",
"integrity": "sha512-qkJs1oxHtneJ6QxDKpxNyneXGDM9SKVj+Bgi8xUAU3FEzpsYmE/aW3MfwYHOZl0pDBO8c2raqLvlyl3dGP6/Gg==",
"version": "18.0.3",
"resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-18.0.3.tgz",
"integrity": "sha512-ApiDJRmcl5Kc5862Ay9RWy96c8hlkf8ELjiBj+SQCAObXTne0NJH2596ckYTkqIRI9yC/8tfolDMJih5i1jwOA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@angular-devkit/core": "18.0.2",
"@angular-devkit/schematics": "18.0.2",
"@angular-devkit/core": "18.0.3",
"@angular-devkit/schematics": "18.0.3",
"jsonc-parser": "3.2.1"
},
"engines": {
@ -4931,9 +4938,9 @@
}
},
"node_modules/@types/express-serve-static-core": {
"version": "4.19.2",
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.2.tgz",
"integrity": "sha512-dPSEQElyVJ97BuGduAqQjpBocZWAs0GR94z+ptL7JXQJeJdHw2WBG3EWdFrK36b8Q6j8P4cXOMhgUoi0IIfIsg==",
"version": "4.19.3",
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.3.tgz",
"integrity": "sha512-KOzM7MhcBFlmnlr/fzISFF5vGWVSvN6fTd4T+ExOt08bA/dA5kpSzY52nMsI1KDFmUREpJelPYyuslLRSjjgCg==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -12748,9 +12755,10 @@
}
},
"node_modules/tslib": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
"version": "2.6.3",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz",
"integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==",
"license": "0BSD"
},
"node_modules/tuf-js": {
"version": "2.2.1",

View File

@ -10,26 +10,26 @@
},
"private": true,
"dependencies": {
"@angular/animations": "^18.0.1",
"@angular/cdk": "~18.0.1",
"@angular/cdk-experimental": "^18.0.1",
"@angular/common": "^18.0.1",
"@angular/compiler": "^18.0.1",
"@angular/core": "^18.0.1",
"@angular/forms": "^18.0.1",
"@angular/material": "~18.0.1",
"@angular/platform-browser": "^18.0.1",
"@angular/platform-browser-dynamic": "^18.0.1",
"@angular/router": "^18.0.1",
"@angular/animations": "^18.0.2",
"@angular/cdk": "~18.0.2",
"@angular/cdk-experimental": "^18.0.2",
"@angular/common": "^18.0.2",
"@angular/compiler": "^18.0.2",
"@angular/core": "^18.0.2",
"@angular/forms": "^18.0.2",
"@angular/material": "~18.0.2",
"@angular/platform-browser": "^18.0.2",
"@angular/platform-browser-dynamic": "^18.0.2",
"@angular/router": "^18.0.2",
"@progress/kendo-date-math": "^1.5.13",
"rxjs": "~7.8.1",
"tslib": "^2.6.2",
"tslib": "^2.6.3",
"zone.js": "~0.14.6"
},
"devDependencies": {
"@angular-devkit/build-angular": "^18.0.2",
"@angular/cli": "^18.0.2",
"@angular/compiler-cli": "^18.0.1",
"@angular-devkit/build-angular": "^18.0.3",
"@angular/cli": "^18.0.3",
"@angular/compiler-cli": "^18.0.2",
"@types/jasmine": "~5.1.4",
"jasmine-core": "~5.1.2",
"karma": "~6.4.3",

158
src/api/api.service.ts Normal file
View File

@ -0,0 +1,158 @@
import {catchError, mergeMap, Observable, retryWhen, timer} from "rxjs";
import {HttpClient, HttpErrorResponse} from "@angular/common/http";
import {NotifyColor, OpenNotifyService} from "@service/open-notify.service";
import {environment} from "@/config/environment";
import {Router} from "@angular/router";
import {Injectable} from "@angular/core";
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;
}
})
)
)
);
}
/*
@Injectable({
providedIn: 'root'
})
*/
export enum AvailableVersion {
v1
}
@Injectable()
export default abstract class ApiService {
constructor(private http: HttpClient, private notify: OpenNotifyService, private router: Router) {
}
private urlApi = environment.apiUrl;
protected abstract basePath: string;
protected abstract version: AvailableVersion;
private static addQuery(endpoint: string, queryParams?: Record<string, string | number | boolean | Array<any> | null> | null): string {
const url = new URL(endpoint);
if (queryParams) {
Object.keys(queryParams).forEach(key => {
const value = queryParams[key];
if (value !== null && value !== undefined) {
if (typeof(value) === typeof(Array)) {
(value as Array<any>).forEach(x => url.searchParams.append(key, x.toString()));
}
else
url.searchParams.append(key, value.toString());
}
});
}
return url.href;
}
private static combineUrls(...parts: string[]): string {
let test = parts.map(part => part.replace(/(^\/+|\/+$)/g, '')).join('/');
console.log(test);
return test;
}
public get<Type>(endpoint: string = '', queryParams: Record<string, string | number | boolean | Array<any> | null> | null = null): Observable<Type> {
return this.http.get<Type>(ApiService.addQuery(ApiService.combineUrls(this.urlApi, AvailableVersion[this.version], this.basePath, endpoint), queryParams), {withCredentials: true}).pipe(
retryWithInterval<Type>(),
catchError(error => {
this.handleError(error);
throw error;
})
);
}
public post<Type>(endpoint: string, data: any, queryParams: Record<string, string | number | boolean | Array<any> | null> | null = null): Observable<Type> {
return this.http.post<Type>(ApiService.addQuery(ApiService.combineUrls(this.urlApi, AvailableVersion[this.version], this.basePath, endpoint), queryParams), data, {withCredentials: true}).pipe(
retryWithInterval<Type>(),
catchError(error => {
this.handleError(error);
throw error;
})
);
}
public put<Type>(endpoint: string, data: any, queryParams: Record<string, string | number | boolean | Array<any> | null> | null = null): Observable<Type> {
return this.http.put<Type>(ApiService.addQuery(ApiService.combineUrls(this.urlApi, AvailableVersion[this.version], this.basePath, endpoint), queryParams), data, {withCredentials: true}).pipe(
retryWithInterval<Type>(),
catchError(error => {
this.handleError(error);
throw error;
})
);
}
public delete<Type>(endpoint: string): Observable<Type> {
return this.http.delete<Type>(ApiService.combineUrls(this.urlApi, AvailableVersion[this.version], this.basePath, endpoint), {withCredentials: true}).pipe(
retryWithInterval<Type>(),
catchError(error => {
this.handleError(error);
throw error;
})
);
}
private handleError(error: HttpErrorResponse): void {
// todo: change to Retry-After condition
if (error.error.toString().includes("setup")) {
this.router.navigate(['/setup/']);
return;
}
let message: string;
if (error.error instanceof ErrorEvent) {
message = `Произошла ошибка: ${error.error.message}`;
} else {
switch (error.status) {
case 0:
message = 'Неизвестная ошибка. Пожалуйста, попробуйте позже.';
break;
case 400:
message = 'Ошибка запроса. Пожалуйста, проверьте отправленные данные.';
break;
case 401:
message = 'Ошибка авторизации. Пожалуйста, выполните вход с правильными учетными данными.';
break;
case 403:
message = 'Отказано в доступе. У вас нет разрешения на выполнение этого действия.';
break;
case 404:
message = 'Запрашиваемый ресурс не найден.';
break;
case 500:
message = 'Внутренняя ошибка сервера. Пожалуйста, попробуйте позже.';
break;
case 503:
message = 'Сервер на обслуживании. Пожалуйста, попробуйте позже.';
break;
default:
message = `Сервер вернул код ошибки: ${error.status}`;
break;
}
if (error.error?.Error) {
message += ` ${error.error.Error}`;
}
}
this.notify.open(message, NotifyColor.Danger);
}
}

View File

@ -0,0 +1,18 @@
import {Injectable} from "@angular/core";
import ApiService, {AvailableVersion} from "@api/api.service";
import {CampusBasicInfoResponse} from "@api/v1/campusBasicInfoResponse";
import {CampusDetailsResponse} from "@api/v1/campusDetailsResponse";
@Injectable()
export class CampusService extends ApiService {
protected basePath = 'Campus/';
protected version = AvailableVersion.v1;
public getCampus() {
return this.get<CampusBasicInfoResponse[]>();
}
public getById(id: number) {
return this.get<CampusDetailsResponse>(id.toString());
}
}

View File

@ -0,0 +1,17 @@
import {Injectable} from "@angular/core";
import ApiService, {AvailableVersion} from "@api/api.service";
import {DisciplineResponse} from "@api/v1/disciplineResponse";
@Injectable()
export class DisciplineService extends ApiService {
protected basePath = 'Discipline/';
protected version = AvailableVersion.v1;
public getDisciplines(page: number | null = null, pageSize: number | null = null) {
return this.get<DisciplineResponse[]>('', {page: page, pageSize: pageSize});
}
public getById(id: number) {
return this.get<DisciplineResponse>(id.toString());
}
}

View File

@ -0,0 +1,19 @@
import {Injectable} from "@angular/core";
import ApiService, {AvailableVersion} from "@api/api.service";
import {FacultyResponse} from "@api/v1/facultyResponse";
import {FacultyDetailsResponse} from "@api/v1/facultyDetailsResponse";
@Injectable()
export class FacultyService extends ApiService {
protected basePath = 'Faculty/';
protected version = AvailableVersion.v1;
public getFaculties(page: number | null = null, pageSize: number | null = null) {
return this.get<FacultyResponse[]>('', {page: page, pageSize: pageSize});
}
public getById(id: number) {
return this.get<FacultyDetailsResponse>(id.toString());
}
}

View File

@ -0,0 +1,22 @@
import {Injectable} from "@angular/core";
import ApiService, {AvailableVersion} from "@api/api.service";
import {GroupResponse} from "@api/v1/groupResponse";
import {GroupDetailsResponse} from "@api/v1/groupDetailsResponse";
@Injectable()
export class GroupService extends ApiService {
protected basePath = 'Group/';
protected version = AvailableVersion.v1;
public getGroups(page: number | null = null, pageSize: number | null = null) {
return this.get<GroupResponse[]>('', {page: page, pageSize: pageSize});
}
public getById(id: number) {
return this.get<GroupDetailsResponse>(id.toString());
}
public getByFaculty(id: number) {
return this.get<GroupResponse[]>('GetByFaculty/' + id.toString());
}
}

View File

@ -0,0 +1,22 @@
import {Injectable} from "@angular/core";
import ApiService, {AvailableVersion} from "@api/api.service";
import {LectureHallResponse} from "@api/v1/lectureHallResponse";
import {LectureHallDetailsResponse} from "@api/v1/lectureHallDetailsResponse";
@Injectable()
export class LectureHallService extends ApiService {
protected basePath = 'LectureHall/';
protected version = AvailableVersion.v1;
public getLectureHalls() {
return this.get<LectureHallResponse[]>();
}
public getById(id: number) {
return this.get<LectureHallDetailsResponse>(id.toString());
}
public getByCampus(id: number) {
return this.get<LectureHallResponse[]>('GetByCampus/' + id.toString());
}
}

View File

@ -0,0 +1,17 @@
import {Injectable} from "@angular/core";
import ApiService, {AvailableVersion} from "@api/api.service";
import {ProfessorResponse} from "@api/v1/professorResponse";
@Injectable()
export class ProfessorService extends ApiService {
protected basePath = 'Professor/';
protected version = AvailableVersion.v1;
public getProfessors(page: number | null = null, pageSize: number | null = null) {
return this.get<ProfessorResponse[]>('', {page: page, pageSize: pageSize});
}
public getById(id: number) {
return this.get<ProfessorResponse>(id.toString());
}
}

View File

@ -0,0 +1,44 @@
import {Injectable} from "@angular/core";
import ApiService, {AvailableVersion} from "@api/api.service";
import {DateOnly} from "@model/DateOnly";
import {PeriodTimes} from "@model/pairPeriodTime";
import {ScheduleRequest} from "@api/v1/scheduleRequest";
import {ScheduleResponse} from "@api/v1/scheduleResponse";
import {GroupScheduleResponse} from "@api/v1/groupScheduleResponse";
import {ProfessorScheduleResponse} from "@api/v1/professorScheduleResponse";
import {LectureHallScheduleResponse} from "@api/v1/lectureHallScheduleResponse";
import {map} from "rxjs";
@Injectable()
export class ScheduleService extends ApiService {
protected basePath = 'Schedule/';
protected version = AvailableVersion.v1;
public startTerm() {
return this.get<string>('StartTerm').pipe(map(date => new DateOnly(date)));
}
public pairPeriod() {
return this.get<PeriodTimes>('PairPeriod');
}
public postSchedule(data: ScheduleRequest) {
return this.post<ScheduleResponse[]>('', data);
}
public getByGroup(id : number, isEven: boolean | null = null, disciplines: Array<number> | null = null, professors: Array<number> | null = null, lectureHalls: Array<number> | null = null) {
return this.get<GroupScheduleResponse>('GetByGroup' + id.toString(), {isEven: isEven, disciplines: disciplines, professors: professors, lectureHalls: lectureHalls});
}
public getByProfessor(id : number, isEven: boolean | null = null, disciplines: Array<number> | null = null, groups: Array<number> | null = null, lectureHalls: Array<number> | null = null) {
return this.get<ProfessorScheduleResponse>('GetByProfessor' + id.toString(), {isEven: isEven, disciplines: disciplines, groups: groups, lectureHalls: lectureHalls});
}
public getByLectureHall(id : number, isEven: boolean | null = null, disciplines: Array<number> | null = null, groups: Array<number> | null = null, professors: Array<number> | null = null) {
return this.get<LectureHallScheduleResponse>('GetByLectureHall' + id.toString(), {isEven: isEven, disciplines: disciplines, groups: groups, professors: professors});
}
public getByDiscipline(id : number, isEven: boolean | null = null, groups: Array<number> | null = null, professors: Array<number> | null = null, lectureHalls: Array<number> | null = null) {
return this.get<GroupScheduleResponse>('GetByDiscipline' + id.toString(), {isEven: isEven, groups: groups, professors: professors, lectureHalls: lectureHalls});
}
}

View File

@ -0,0 +1,58 @@
import {Injectable} from "@angular/core";
import ApiService, {AvailableVersion} from "@api/api.service";
import {DatabaseRequest} from "@api/v1/databaseRequest";
import {CacheRequest} from "@api/v1/cacheRequest";
import {CreateUserRequest} from "@api/v1/createUserRequest";
import {LoggingRequest} from "@api/v1/loggingRequest";
import {EmailRequest} from "@api/v1/emailRequest";
import {ScheduleConfigurationRequest} from "@api/v1/scheduleConfigurationRequest";
@Injectable()
export default class SetupService extends ApiService {
protected basePath = 'Setup/';
protected version = AvailableVersion.v1;
public checkToken(token: string) {
return this.get<boolean>('CheckToken', {token: token});
}
public setPsql(data: DatabaseRequest) {
return this.post<boolean>('SetPsql', data);
}
public setMysql(data: DatabaseRequest) {
return this.post<boolean>('SetMysql', data);
}
public setSqlite(path: string | null = null) {
return this.post<boolean>('SetSqlite', null, {path: path});
}
public setRedis(data: CacheRequest) {
return this.post<boolean>('SetRedis', data);
}
public setMemcached() {
return this.post<boolean>('SetMemcached', null);
}
public createAdmin(data: CreateUserRequest) {
return this.post<boolean>('CreateAdmin', data);
}
public setLogging(data: LoggingRequest | null = null) {
return this.post<boolean>('SetLogging', data);
}
public setEmail(data: EmailRequest | null = null) {
return this.post<boolean>('SetEmail', data);
}
public setSchedule(data: ScheduleConfigurationRequest) {
return this.post<boolean>('SetSchedule', data);
}
public submit() {
return this.post<boolean>('Submit', null);
}
}

View File

@ -49,7 +49,12 @@
<!-- Discipline -->
<div class="mat-body-1">{{ elementData["discipline"] }}</div>
<!-- Type of Occupation -->
<div class="mat-body">({{ elementData["typeOfOccupation"] }})</div>
@for (typeOfOccupation of elementData["typeOfOccupations"]; track $index) {
@if ($index !== 0) {
<br/>
}
<div class="mat-body">({{typeOfOccupation}})</div>
}
<!-- Professors -->
@if (checkAvailableData(elementData["professors"])) {

View File

@ -4,8 +4,8 @@ import {MatIcon} from "@angular/material/icon";
import {DatePipe} from "@angular/common";
import {addDays} from "@progress/kendo-date-math";
import {MatDivider} from "@angular/material/divider";
import {Schedule} from "@page/schedule/schedule.component";
import {DataSpinnerComponent} from "@component/common/data-spinner/data-spinner.component";
import {ScheduleResponse} from "@api/v1/scheduleResponse";
interface TableData {
pairNumber: number;
@ -34,14 +34,14 @@ export class TableComponent implements OnChanges {
protected tableDataSource: MatTableDataSource<TableData> = new MatTableDataSource<TableData>([]);
protected daysOfWeek: string[] = ['Понедельник', 'Вторник', 'Среда', 'Четверг', 'Пятница', 'Суббота'];
protected displayedColumns: string[] = ['pairNumber'];
protected dataSource: Schedule[] = [];
protected dataSource: ScheduleResponse[] = [];
protected isOneGroup: boolean = false;
@Input() currentWeek!: number;
@Input() startWeek!: Date;
@Input() isLoad: boolean = false;
@Input() set data(schedule: Schedule[]) {
@Input() set data(schedule: ScheduleResponse[]) {
this.dataSource = schedule;
this.convertData();
this.isOneGroup = schedule.every((item, _, array) => item.group === array[0].group);
@ -69,9 +69,13 @@ export class TableComponent implements OnChanges {
for (let k: number = 1; k < 7; k++) {
convertedData.data[k.toString()] = this.dataSource.filter(x =>
x.pairNumber === i
&& x.dayOfWeek === k
&& x.isEven === (this.currentWeek % 2 === 0));
x.pairNumber === i &&
x.dayOfWeek === k &&
x.isEven === (this.currentWeek % 2 === 0) &&
(
(x.isExcludedWeeks && (!x.weeks || !x.weeks.includes(this.currentWeek))) ||
(!x.isExcludedWeeks && (!x.weeks || x.weeks.includes(this.currentWeek)))
));
}
tableData.push(convertedData);
@ -81,8 +85,8 @@ export class TableComponent implements OnChanges {
this.isLoad = false;
}
protected checkAvailableData(data: any[]): boolean {
return data && data.length !== 0 && data.every(x => x !== null);
protected checkAvailableData(data: any[] | any): boolean {
return data && data.length !== 0 && (!Array.isArray(data) || data.every(x => x !== null));
}
protected readonly addDays = addDays;

View File

@ -4,9 +4,9 @@ import {MatChipListboxChange, MatChipsModule} from '@angular/material/chips';
import {FormControl, ReactiveFormsModule} from "@angular/forms";
import {AsyncPipe} from "@angular/common";
import {map, Observable, of} from "rxjs";
import {FacultyResponse} from "@model/facultyResponse";
import {GroupResponse} from "@model/groupResponse";
import {LoadingIndicatorComponent} from "@component/common/loading-indicator/loading-indicator.component";
import {GroupResponse} from "@api/v1/groupResponse";
import {FacultyResponse} from "@api/v1/facultyResponse";
@Component({
selector: 'app-group',

View File

@ -3,10 +3,10 @@ import {AsyncPipe} from "@angular/common";
import {MatAccordion, MatExpansionModule, MatExpansionPanel} from "@angular/material/expansion";
import {MatChipListboxChange, MatChipsModule} from "@angular/material/chips";
import {Observable, of} from "rxjs";
import {CampusBasicInfoResponse} from "@model/campusBasicInfoResponse";
import {FormControl, ReactiveFormsModule} from "@angular/forms";
import {LectureHallResponse} from "@model/lectureHallResponse";
import {LoadingIndicatorComponent} from "@component/common/loading-indicator/loading-indicator.component";
import {CampusBasicInfoResponse} from "@api/v1/campusBasicInfoResponse";
import {LectureHallResponse} from "@api/v1/lectureHallResponse";
@Component({
selector: 'app-lecture-hall',

View File

@ -43,6 +43,7 @@ export interface SelectData {
templateUrl: './other.component.html',
styleUrl: './other.component.css'
})
export class OtherComponent {
private _searchQuery: string = '';
protected filteredData: BehaviorSubject<SelectData[]> = new BehaviorSubject<SelectData[]>([]);

View File

@ -4,8 +4,8 @@ import {FormControl, ReactiveFormsModule} from "@angular/forms";
import {MatAutocompleteModule, MatAutocompleteSelectedEvent} from "@angular/material/autocomplete";
import {AsyncPipe} from "@angular/common";
import {map, Observable, startWith} from "rxjs";
import {ProfessorResponse} from "@model/professorResponse";
import {LoadingIndicatorComponent} from "@component/common/loading-indicator/loading-indicator.component";
import {ProfessorResponse} from "@api/v1/professorResponse";
@Component({
selector: 'app-professor',

View File

@ -1,26 +1,26 @@
import {Component, EventEmitter, Output, ViewChild} from '@angular/core';
import {HttpClientModule} from "@angular/common/http";
import {ApiService} from '@service/api.service';
import {OtherComponent} from "@component/schedule/tabs/other/other.component";
import {MatTab, MatTabChangeEvent, MatTabGroup} from "@angular/material/tabs";
import {
ProfessorComponent
} from "@component/schedule/tabs/professor/professor.component";
import {GroupComponent} from "@component/schedule/tabs/group/group.component";
import {
LectureHallComponent
} from "@component/schedule/tabs/lecture-hall/lecture-hall.component";
import {ProfessorResponse} from "@model/professorResponse";
import {catchError, map, Observable, of, switchMap, tap} from "rxjs";
import {GroupResponse} from "@model/groupResponse";
import {FacultyResponse} from "@model/facultyResponse";
import {CampusBasicInfoResponse} from "@model/campusBasicInfoResponse";
import {LectureHallResponse} from "@model/lectureHallResponse";
import {ReactiveFormsModule} from "@angular/forms";
import {AsyncPipe, NgIf} from "@angular/common";
import {DisciplineResponse} from "@model/disciplineResponse";
import {MatButton} from "@angular/material/button";
import {DataSpinnerComponent} from "@component/common/data-spinner/data-spinner.component";
import {ProfessorResponse} from "@api/v1/professorResponse";
import {FacultyResponse} from "@api/v1/facultyResponse";
import {GroupResponse} from "@api/v1/groupResponse";
import {CampusBasicInfoResponse} from "@api/v1/campusBasicInfoResponse";
import {LectureHallResponse} from "@api/v1/lectureHallResponse";
import {DisciplineService} from "@api/v1/discipline.service";
import {GroupComponent} from "@component/schedule/tabs/group/group.component";
import {ProfessorComponent} from "@component/schedule/tabs/professor/professor.component";
import {LectureHallComponent} from "@component/schedule/tabs/lecture-hall/lecture-hall.component";
import {FacultyService} from "@api/v1/faculty.service";
import {GroupService} from "@api/v1/group.service";
import {ProfessorService} from "@api/v1/professor.service";
import {CampusService} from "@api/v1/campus.service";
import {LectureHallService} from "@api/v1/lectureHall.service";
@Component({
selector: 'app-tabs',
@ -30,17 +30,18 @@ import {DataSpinnerComponent} from "@component/common/data-spinner/data-spinner.
OtherComponent,
MatTabGroup,
MatTab,
ProfessorComponent,
GroupComponent,
LectureHallComponent,
ReactiveFormsModule,
AsyncPipe,
NgIf,
MatButton,
DataSpinnerComponent
DataSpinnerComponent,
GroupComponent,
ProfessorComponent,
LectureHallComponent
],
templateUrl: './tabs.component.html',
styleUrl: './tabs.component.css'
styleUrl: './tabs.component.css',
providers: [FacultyService, GroupService, ProfessorService, CampusService, LectureHallService, DisciplineService ]
})
export class TabsComponent {
@ -66,12 +67,17 @@ export class TabsComponent {
@Output() lectureHallSelected: EventEmitter<number> = new EventEmitter<number>();
@Output() professorSelected: EventEmitter<number> = new EventEmitter<number>();
constructor(private api: ApiService) {
constructor(
private facultyApi: FacultyService,
private groupApi: GroupService,
private professorApi: ProfessorService,
private campusApi: CampusService,
private lectureHallApi: LectureHallService,
private disciplineApi: DisciplineService) {
this.facultyLoad().then();
}
protected async chooseTabs(event: MatTabChangeEvent) {
console.log(event);
switch (event.index) {
case 0:
await this.facultyLoad();
@ -92,7 +98,7 @@ export class TabsComponent {
if (this.facultiesLoaded === null) this.facultiesLoaded = false;
if (this.facultiesLoaded) return;
this.faculties = this.api.get<FacultyResponse[]>("Faculty/Get").pipe(
this.faculties = this.facultyApi.getFaculties().pipe(
tap(() => {
this.facultiesLoaded = true;
}),
@ -109,7 +115,7 @@ export class TabsComponent {
if (this.groupLoaded)
this.groups = this.groupsData.pipe(map(data => data.filter(x => x.facultyId === id)));
else
this.groups = this.api.get<GroupResponse[]>("Group/GetByFaculty/" + id).pipe(
this.groups = this.groupApi.getByFaculty(id).pipe(
tap(() => {
this.groupLoaded = false;
}),
@ -125,7 +131,7 @@ export class TabsComponent {
if (this.professorsLoaded) return;
this.api.get<ProfessorResponse[]>("Professor/Get").pipe(
this.professorApi.getProfessors().pipe(
catchError((error) => {
this.professorsLoaded = null;
throw error;
@ -147,7 +153,7 @@ export class TabsComponent {
if (this.campusesLoaded) return;
this.campuses = this.api.get<CampusBasicInfoResponse[]>("Campus/Get").pipe(
this.campuses = this.campusApi.getCampus().pipe(
tap(() => {
this.campusesLoaded = true;
}),
@ -164,7 +170,7 @@ export class TabsComponent {
if (this.lectureHallsLoaded)
this.lectureHalls = this.lectureHallsData.pipe(map(data => data.filter(x => x.campusId === id)));
else
this.lectureHalls = this.api.get<LectureHallResponse[]>("LectureHall/GetByCampus/" + id).pipe(
this.lectureHalls = this.lectureHallApi.getByCampus(id).pipe(
tap(() => {
this.lectureHallsLoaded = false;
}),
@ -180,7 +186,7 @@ export class TabsComponent {
await this.campusLoad();
if (!this.lectureHallsLoaded) {
this.lectureHallsData = this.api.get<LectureHallResponse[]>("LectureHall/Get");
this.lectureHallsData = this.lectureHallApi.getLectureHalls();
this.lectureHallsData.pipe(
switchMap(lectureHalls => this.campuses.pipe(
map(campuses => {
@ -205,7 +211,7 @@ export class TabsComponent {
protected async loadDisciplines() {
if (!this.disciplinesLoaded) {
this.api.get<DisciplineResponse[]>("Discipline/Get").pipe(
this.disciplineApi.getDisciplines().pipe(
catchError((error) => {
this.disciplinesLoaded = null;
throw error;
@ -226,7 +232,7 @@ export class TabsComponent {
await this.facultyLoad();
if (!this.groupLoaded) {
this.groupsData = this.api.get<GroupResponse[]>("Group/Get");
this.groupsData = this.groupApi.getGroups();
this.groupsData.pipe(
switchMap(groups => this.faculties.pipe(
map(campuses => {
@ -267,11 +273,4 @@ export class TabsComponent {
@ViewChild('lecture') lectureHallEx!: OtherComponent;
@ViewChild('group') groupEx!: OtherComponent;
@ViewChild('professor') professorEx!: OtherComponent;
onClickNagmi() {
console.log('huy = ' + this.disciplineEx.selectedIds);
console.log('huy2 = ' + this.lectureHallEx.selectedIds);
console.log('huy3 = ' + this.groupEx.selectedIds);
console.log('huy3 = ' + this.professorEx.selectedIds);
}
}

View File

@ -1,6 +1,6 @@
export const environment = {
apiUrl: 'http://localhost:5269/api/v1/',
apiUrl: 'http://localhost:5269/api/',
production: false,
maxRetry: 30,
retryDelay: 15000
maxRetry: 3,
retryDelay: 1500
}

View File

@ -1,110 +0,0 @@
import {Injectable} from '@angular/core';
import {catchError, mergeMap, Observable, retryWhen, throwError, timer} from "rxjs";
import {HttpClient} from "@angular/common/http";
import {NotifyColor, OpenNotifyService} from "@service/open-notify.service";
import {environment} from "@/config/environment";
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')) {
console.log(`Retrying after ${environment.retryDelay}ms...`);
return timer(environment.retryDelay);
} else {
console.error(`Exceeded maximum retries (${environment.maxRetry})`);
return throwError(error);
}
})
)
)
);
}
@Injectable({
providedIn: 'root'
})
export class ApiService {
constructor(private http: HttpClient, private notify: OpenNotifyService) {
}
private urlApi = environment.apiUrl;
get<Type>(endpoint: string): Observable<Type> {
return this.http.get<Type>(this.urlApi + endpoint).pipe(
retryWithInterval<Type>(),
catchError(error => {
this.handleError(error);
return throwError(error);
})
);
}
post<Type>(endpoint: string, data: any): Observable<Type> {
return this.http.post<Type>(this.urlApi + endpoint, data).pipe(
retryWithInterval<Type>(),
catchError(error => {
this.handleError(error);
return throwError(error);
})
);
}
put<Type>(endpoint: string, data: any): Observable<Type> {
return this.http.put<Type>(this.urlApi + endpoint, data).pipe(
retryWithInterval<Type>(),
catchError(error => {
this.handleError(error);
return throwError(error);
})
);
}
delete<Type>(endpoint: string): Observable<Type> {
return this.http.delete<Type>(this.urlApi + endpoint).pipe(
retryWithInterval<Type>(),
catchError(error => {
this.handleError(error);
return throwError(error);
})
);
}
private handleError(error: any): void {
let message: string;
if (error.error instanceof ErrorEvent) {
message = `Произошла ошибка: ${error.error.message}`;
} else {
switch (error.status) {
case 0:
message = 'Неизвестная ошибка. Пожалуйста, попробуйте позже.';
break;
case 400:
message = 'Ошибка запроса. Пожалуйста, проверьте отправленные данные.';
break;
case 401:
message = 'Ошибка авторизации. Пожалуйста, выполните вход с правильными учетными данными.';
break;
case 403:
message = 'Отказано в доступе. У вас нет разрешения на выполнение этого действия.';
break;
case 404:
message = 'Запрашиваемый ресурс не найден.';
break;
case 500:
message = 'Внутренняя ошибка сервера. Пожалуйста, попробуйте позже.';
break;
default:
message = `Сервер вернул код ошибки: ${error.status}`;
break;
}
if (error.error?.Error) {
message += ` ${error.error.Error}`;
}
}
this.notify.open(message, NotifyColor.Danger);
}
}

View File

@ -9,8 +9,6 @@
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
import {PairPeriodTime} from "@model/pairPeriodTime";
/**
* Represents a request to configure the schedule settings.
*/
@ -23,8 +21,4 @@ export interface ScheduleConfigurationRequest {
* Gets or sets the start date of the term.
*/
startTerm: string;
/**
* Gets or sets the pair period times, keyed by pair number.
*/
pairPeriod: { [key: string]: PairPeriodTime; };
}

View File

@ -29,7 +29,12 @@
"./src/pages/*"
],
"@model/*": [
"./src/shared/*"
"./src/shared/structs/*"
],
"@api/*": [
"./src/shared/responses/*",
"./src/shared/requests/*",
"./src/api/*"
],
"@/*": [
"./src/*"