ajout des sources
This commit is contained in:
11
angular.json
11
angular.json
@@ -27,9 +27,15 @@
|
|||||||
"src/assets"
|
"src/assets"
|
||||||
],
|
],
|
||||||
"styles": [
|
"styles": [
|
||||||
"src/styles.css"
|
"src/styles.css",
|
||||||
|
"node_modules/bootstrap/dist/css/bootstrap.min.css",
|
||||||
|
"node_modules/ngx-toastr/toastr.css",
|
||||||
|
"node_modules/@angular/material/prebuilt-themes/deeppurple-amber.css"
|
||||||
],
|
],
|
||||||
"scripts": []
|
"scripts": [
|
||||||
|
"node_modules/bootstrap/dist/js/bootstrap.min.js",
|
||||||
|
"node_modules/jquery/dist/jquery.js"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"configurations": {
|
"configurations": {
|
||||||
"production": {
|
"production": {
|
||||||
@@ -94,6 +100,7 @@
|
|||||||
"src/assets"
|
"src/assets"
|
||||||
],
|
],
|
||||||
"styles": [
|
"styles": [
|
||||||
|
"./node_modules/@angular/material/prebuilt-themes/deeppurple-amber.css",
|
||||||
"src/styles.css"
|
"src/styles.css"
|
||||||
],
|
],
|
||||||
"scripts": []
|
"scripts": []
|
||||||
|
|||||||
285
package-lock.json
generated
285
package-lock.json
generated
@@ -9,13 +9,25 @@
|
|||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/animations": "~13.3.0",
|
"@angular/animations": "~13.3.0",
|
||||||
|
"@angular/cdk": "^13.3.6",
|
||||||
"@angular/common": "~13.3.0",
|
"@angular/common": "~13.3.0",
|
||||||
"@angular/compiler": "~13.3.0",
|
"@angular/compiler": "~13.3.0",
|
||||||
"@angular/core": "~13.3.0",
|
"@angular/core": "~13.3.0",
|
||||||
|
"@angular/flex-layout": "^13.0.0-beta.38",
|
||||||
"@angular/forms": "~13.3.0",
|
"@angular/forms": "~13.3.0",
|
||||||
|
"@angular/material": "^13.3.6",
|
||||||
"@angular/platform-browser": "~13.3.0",
|
"@angular/platform-browser": "~13.3.0",
|
||||||
"@angular/platform-browser-dynamic": "~13.3.0",
|
"@angular/platform-browser-dynamic": "~13.3.0",
|
||||||
"@angular/router": "~13.3.0",
|
"@angular/router": "~13.3.0",
|
||||||
|
"@fortawesome/angular-fontawesome": "^0.10.2",
|
||||||
|
"@fortawesome/fontawesome-svg-core": "^6.1.1",
|
||||||
|
"@fortawesome/free-solid-svg-icons": "^6.1.1",
|
||||||
|
"bootstrap": "^5.1.3",
|
||||||
|
"font-awesome": "^4.7.0",
|
||||||
|
"jquery": "^3.6.0",
|
||||||
|
"ngx-cookie-service": "^13.2.0",
|
||||||
|
"ngx-toastr": "^14.3.0",
|
||||||
|
"popper.js": "^1.16.1",
|
||||||
"rxjs": "~7.5.0",
|
"rxjs": "~7.5.0",
|
||||||
"tslib": "^2.3.0",
|
"tslib": "^2.3.0",
|
||||||
"zone.js": "~0.11.4"
|
"zone.js": "~0.11.4"
|
||||||
@@ -347,6 +359,28 @@
|
|||||||
"@angular/core": "13.3.5"
|
"@angular/core": "13.3.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@angular/cdk": {
|
||||||
|
"version": "13.3.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-13.3.6.tgz",
|
||||||
|
"integrity": "sha512-sPwEGCHARxuUMzPLRiiN51GQP0P39ev+eL06NcyYXZ3AxyZIH5N/PWmGKf7EDOXIof4ata13jsIeW38mFe+wvg==",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.3.0"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"parse5": "^5.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@angular/common": "^13.0.0 || ^14.0.0-0",
|
||||||
|
"@angular/core": "^13.0.0 || ^14.0.0-0",
|
||||||
|
"rxjs": "^6.5.3 || ^7.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@angular/cdk/node_modules/parse5": {
|
||||||
|
"version": "5.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz",
|
||||||
|
"integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
"node_modules/@angular/cli": {
|
"node_modules/@angular/cli": {
|
||||||
"version": "13.3.4",
|
"version": "13.3.4",
|
||||||
"resolved": "https://registry.npmjs.org/@angular/cli/-/cli-13.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/@angular/cli/-/cli-13.3.4.tgz",
|
||||||
@@ -532,6 +566,21 @@
|
|||||||
"zone.js": "~0.11.4"
|
"zone.js": "~0.11.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@angular/flex-layout": {
|
||||||
|
"version": "13.0.0-beta.38",
|
||||||
|
"resolved": "https://registry.npmjs.org/@angular/flex-layout/-/flex-layout-13.0.0-beta.38.tgz",
|
||||||
|
"integrity": "sha512-kcWb7CcoHbvw7fjo/knizWVmSSmvaTnr8v1ML6zOdxu1PK9UPPOcOS8RTm6fy61zoC2LABivP1/6Z2jF5XfpdQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.3.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@angular/cdk": "^13.0.0",
|
||||||
|
"@angular/common": "^13.0.0",
|
||||||
|
"@angular/core": "^13.0.0",
|
||||||
|
"@angular/platform-browser": "^13.0.0",
|
||||||
|
"rxjs": "^6.5.3 || ^7.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@angular/forms": {
|
"node_modules/@angular/forms": {
|
||||||
"version": "13.3.5",
|
"version": "13.3.5",
|
||||||
"resolved": "https://registry.npmjs.org/@angular/forms/-/forms-13.3.5.tgz",
|
"resolved": "https://registry.npmjs.org/@angular/forms/-/forms-13.3.5.tgz",
|
||||||
@@ -549,6 +598,23 @@
|
|||||||
"rxjs": "^6.5.3 || ^7.4.0"
|
"rxjs": "^6.5.3 || ^7.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@angular/material": {
|
||||||
|
"version": "13.3.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@angular/material/-/material-13.3.6.tgz",
|
||||||
|
"integrity": "sha512-V+3Fs9JK+7KlcdJG/Oa/IQuLHC8WYzuL8ffH1Q06ANSzGxSjsAHs4FZQpKUpVBoL3E+p9Yz+zkKe91k5L62VoQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.3.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@angular/animations": "^13.0.0 || ^14.0.0-0",
|
||||||
|
"@angular/cdk": "13.3.6",
|
||||||
|
"@angular/common": "^13.0.0 || ^14.0.0-0",
|
||||||
|
"@angular/core": "^13.0.0 || ^14.0.0-0",
|
||||||
|
"@angular/forms": "^13.0.0 || ^14.0.0-0",
|
||||||
|
"@angular/platform-browser": "^13.0.0 || ^14.0.0-0",
|
||||||
|
"rxjs": "^6.5.3 || ^7.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@angular/platform-browser": {
|
"node_modules/@angular/platform-browser": {
|
||||||
"version": "13.3.5",
|
"version": "13.3.5",
|
||||||
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-13.3.5.tgz",
|
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-13.3.5.tgz",
|
||||||
@@ -2283,6 +2349,50 @@
|
|||||||
"node": ">=10.0.0"
|
"node": ">=10.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@fortawesome/angular-fontawesome": {
|
||||||
|
"version": "0.10.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@fortawesome/angular-fontawesome/-/angular-fontawesome-0.10.2.tgz",
|
||||||
|
"integrity": "sha512-VxsCAo2lK74KwD236AKAhGpiethfz9yqCViIG2iRAZqgNmuZ6ihwumjbLW32n6hV4fFvCqLcHmpngoEl3TNiOg==",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.3.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@fortawesome/fontawesome-svg-core": "~1.2.27 || ~1.3.0-beta2 || ^6.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@fortawesome/fontawesome-common-types": {
|
||||||
|
"version": "6.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.1.1.tgz",
|
||||||
|
"integrity": "sha512-wVn5WJPirFTnzN6tR95abCx+ocH+3IFLXAgyavnf9hUmN0CfWoDjPT/BAWsUVwSlYYVBeCLJxaqi7ZGe4uSjBA==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@fortawesome/fontawesome-svg-core": {
|
||||||
|
"version": "6.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.1.1.tgz",
|
||||||
|
"integrity": "sha512-NCg0w2YIp81f4V6cMGD9iomfsIj7GWrqmsa0ZsPh59G7PKiGN1KymZNxmF00ssuAlo/VZmpK6xazsGOwzKYUMg==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@fortawesome/fontawesome-common-types": "6.1.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@fortawesome/free-solid-svg-icons": {
|
||||||
|
"version": "6.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.1.1.tgz",
|
||||||
|
"integrity": "sha512-0/5exxavOhI/D4Ovm2r3vxNojGZioPwmFrKg0ZUH69Q68uFhFPs6+dhAToh6VEQBntxPRYPuT5Cg1tpNa9JUPg==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@fortawesome/fontawesome-common-types": "6.1.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@gar/promisify": {
|
"node_modules/@gar/promisify": {
|
||||||
"version": "1.1.3",
|
"version": "1.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz",
|
||||||
@@ -2509,6 +2619,16 @@
|
|||||||
"read-package-json-fast": "^2.0.1"
|
"read-package-json-fast": "^2.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@popperjs/core": {
|
||||||
|
"version": "2.11.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.5.tgz",
|
||||||
|
"integrity": "sha512-9X2obfABZuDVLCgPK9aX0a/x4jaOEweTTWE2+9sr0Qqqevj2Uv5XorvusThmc9XGYpS9yI+fhh8RTafBtGposw==",
|
||||||
|
"peer": true,
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/popperjs"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@schematics/angular": {
|
"node_modules/@schematics/angular": {
|
||||||
"version": "13.3.4",
|
"version": "13.3.4",
|
||||||
"resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-13.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-13.3.4.tgz",
|
||||||
@@ -3480,6 +3600,18 @@
|
|||||||
"integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=",
|
"integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/bootstrap": {
|
||||||
|
"version": "5.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.1.3.tgz",
|
||||||
|
"integrity": "sha512-fcQztozJ8jToQWXxVuEyXWW+dSo8AiXWKwiSSrKWsRB/Qt+Ewwza+JWoLKiTuQLaEPhdNAJ7+Dosc9DOIqNy7Q==",
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/bootstrap"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@popperjs/core": "^2.10.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/brace-expansion": {
|
"node_modules/brace-expansion": {
|
||||||
"version": "1.1.11",
|
"version": "1.1.11",
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||||
@@ -5704,6 +5836,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/font-awesome": {
|
||||||
|
"version": "4.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/font-awesome/-/font-awesome-4.7.0.tgz",
|
||||||
|
"integrity": "sha1-j6jPBBGhoxr9B7BtKQK7n8gVoTM=",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/forwarded": {
|
"node_modules/forwarded": {
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
||||||
@@ -6902,6 +7042,11 @@
|
|||||||
"url": "https://github.com/chalk/supports-color?sponsor=1"
|
"url": "https://github.com/chalk/supports-color?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/jquery": {
|
||||||
|
"version": "3.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.0.tgz",
|
||||||
|
"integrity": "sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw=="
|
||||||
|
},
|
||||||
"node_modules/js-tokens": {
|
"node_modules/js-tokens": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||||
@@ -7922,6 +8067,31 @@
|
|||||||
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
|
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/ngx-cookie-service": {
|
||||||
|
"version": "13.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ngx-cookie-service/-/ngx-cookie-service-13.2.0.tgz",
|
||||||
|
"integrity": "sha512-WxuLrZROWf59DfPPstPsrS18nxtPvT+uJ4AEjFs57NqtTfYdRQXhVJ02fZ4WP4VPElI8o6qndNL7gi9tkEdg4Q==",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@angular/common": "^13.0.0",
|
||||||
|
"@angular/core": "^13.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/ngx-toastr": {
|
||||||
|
"version": "14.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ngx-toastr/-/ngx-toastr-14.3.0.tgz",
|
||||||
|
"integrity": "sha512-d8j/sOr60w5U7rGlcKQ0Ff4u+m2NzhqU5ZdJXn7QW3aR3Zf/rY7/Fd14BmUindTOWVr2NeTYcQXCjLpir0ldpA==",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.3.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@angular/common": ">=12.0.0-0",
|
||||||
|
"@angular/core": ">=12.0.0-0",
|
||||||
|
"@angular/platform-browser": ">=12.0.0-0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/nice-napi": {
|
"node_modules/nice-napi": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz",
|
||||||
@@ -8880,6 +9050,16 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/popper.js": {
|
||||||
|
"version": "1.16.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz",
|
||||||
|
"integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==",
|
||||||
|
"deprecated": "You can find the new Popper v2 at @popperjs/core, this package is dedicated to the legacy v1",
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/popperjs"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/portfinder": {
|
"node_modules/portfinder": {
|
||||||
"version": "1.0.28",
|
"version": "1.0.28",
|
||||||
"resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz",
|
"resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz",
|
||||||
@@ -11886,6 +12066,23 @@
|
|||||||
"tslib": "^2.3.0"
|
"tslib": "^2.3.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@angular/cdk": {
|
||||||
|
"version": "13.3.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-13.3.6.tgz",
|
||||||
|
"integrity": "sha512-sPwEGCHARxuUMzPLRiiN51GQP0P39ev+eL06NcyYXZ3AxyZIH5N/PWmGKf7EDOXIof4ata13jsIeW38mFe+wvg==",
|
||||||
|
"requires": {
|
||||||
|
"parse5": "^5.0.0",
|
||||||
|
"tslib": "^2.3.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"parse5": {
|
||||||
|
"version": "5.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz",
|
||||||
|
"integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==",
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"@angular/cli": {
|
"@angular/cli": {
|
||||||
"version": "13.3.4",
|
"version": "13.3.4",
|
||||||
"resolved": "https://registry.npmjs.org/@angular/cli/-/cli-13.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/@angular/cli/-/cli-13.3.4.tgz",
|
||||||
@@ -12018,6 +12215,14 @@
|
|||||||
"tslib": "^2.3.0"
|
"tslib": "^2.3.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@angular/flex-layout": {
|
||||||
|
"version": "13.0.0-beta.38",
|
||||||
|
"resolved": "https://registry.npmjs.org/@angular/flex-layout/-/flex-layout-13.0.0-beta.38.tgz",
|
||||||
|
"integrity": "sha512-kcWb7CcoHbvw7fjo/knizWVmSSmvaTnr8v1ML6zOdxu1PK9UPPOcOS8RTm6fy61zoC2LABivP1/6Z2jF5XfpdQ==",
|
||||||
|
"requires": {
|
||||||
|
"tslib": "^2.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@angular/forms": {
|
"@angular/forms": {
|
||||||
"version": "13.3.5",
|
"version": "13.3.5",
|
||||||
"resolved": "https://registry.npmjs.org/@angular/forms/-/forms-13.3.5.tgz",
|
"resolved": "https://registry.npmjs.org/@angular/forms/-/forms-13.3.5.tgz",
|
||||||
@@ -12026,6 +12231,14 @@
|
|||||||
"tslib": "^2.3.0"
|
"tslib": "^2.3.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@angular/material": {
|
||||||
|
"version": "13.3.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@angular/material/-/material-13.3.6.tgz",
|
||||||
|
"integrity": "sha512-V+3Fs9JK+7KlcdJG/Oa/IQuLHC8WYzuL8ffH1Q06ANSzGxSjsAHs4FZQpKUpVBoL3E+p9Yz+zkKe91k5L62VoQ==",
|
||||||
|
"requires": {
|
||||||
|
"tslib": "^2.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@angular/platform-browser": {
|
"@angular/platform-browser": {
|
||||||
"version": "13.3.5",
|
"version": "13.3.5",
|
||||||
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-13.3.5.tgz",
|
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-13.3.5.tgz",
|
||||||
@@ -13229,6 +13442,35 @@
|
|||||||
"integrity": "sha512-ws57AidsDvREKrZKYffXddNkyaF14iHNHm8VQnZH6t99E8gczjNN0GpvcGny0imC80yQ0tHz1xVUKk/KFQSUyA==",
|
"integrity": "sha512-ws57AidsDvREKrZKYffXddNkyaF14iHNHm8VQnZH6t99E8gczjNN0GpvcGny0imC80yQ0tHz1xVUKk/KFQSUyA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"@fortawesome/angular-fontawesome": {
|
||||||
|
"version": "0.10.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@fortawesome/angular-fontawesome/-/angular-fontawesome-0.10.2.tgz",
|
||||||
|
"integrity": "sha512-VxsCAo2lK74KwD236AKAhGpiethfz9yqCViIG2iRAZqgNmuZ6ihwumjbLW32n6hV4fFvCqLcHmpngoEl3TNiOg==",
|
||||||
|
"requires": {
|
||||||
|
"tslib": "^2.3.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@fortawesome/fontawesome-common-types": {
|
||||||
|
"version": "6.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.1.1.tgz",
|
||||||
|
"integrity": "sha512-wVn5WJPirFTnzN6tR95abCx+ocH+3IFLXAgyavnf9hUmN0CfWoDjPT/BAWsUVwSlYYVBeCLJxaqi7ZGe4uSjBA=="
|
||||||
|
},
|
||||||
|
"@fortawesome/fontawesome-svg-core": {
|
||||||
|
"version": "6.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.1.1.tgz",
|
||||||
|
"integrity": "sha512-NCg0w2YIp81f4V6cMGD9iomfsIj7GWrqmsa0ZsPh59G7PKiGN1KymZNxmF00ssuAlo/VZmpK6xazsGOwzKYUMg==",
|
||||||
|
"requires": {
|
||||||
|
"@fortawesome/fontawesome-common-types": "6.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@fortawesome/free-solid-svg-icons": {
|
||||||
|
"version": "6.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.1.1.tgz",
|
||||||
|
"integrity": "sha512-0/5exxavOhI/D4Ovm2r3vxNojGZioPwmFrKg0ZUH69Q68uFhFPs6+dhAToh6VEQBntxPRYPuT5Cg1tpNa9JUPg==",
|
||||||
|
"requires": {
|
||||||
|
"@fortawesome/fontawesome-common-types": "6.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@gar/promisify": {
|
"@gar/promisify": {
|
||||||
"version": "1.1.3",
|
"version": "1.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz",
|
||||||
@@ -13409,6 +13651,12 @@
|
|||||||
"read-package-json-fast": "^2.0.1"
|
"read-package-json-fast": "^2.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@popperjs/core": {
|
||||||
|
"version": "2.11.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.5.tgz",
|
||||||
|
"integrity": "sha512-9X2obfABZuDVLCgPK9aX0a/x4jaOEweTTWE2+9sr0Qqqevj2Uv5XorvusThmc9XGYpS9yI+fhh8RTafBtGposw==",
|
||||||
|
"peer": true
|
||||||
|
},
|
||||||
"@schematics/angular": {
|
"@schematics/angular": {
|
||||||
"version": "13.3.4",
|
"version": "13.3.4",
|
||||||
"resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-13.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-13.3.4.tgz",
|
||||||
@@ -14225,6 +14473,12 @@
|
|||||||
"integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=",
|
"integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"bootstrap": {
|
||||||
|
"version": "5.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.1.3.tgz",
|
||||||
|
"integrity": "sha512-fcQztozJ8jToQWXxVuEyXWW+dSo8AiXWKwiSSrKWsRB/Qt+Ewwza+JWoLKiTuQLaEPhdNAJ7+Dosc9DOIqNy7Q==",
|
||||||
|
"requires": {}
|
||||||
|
},
|
||||||
"brace-expansion": {
|
"brace-expansion": {
|
||||||
"version": "1.1.11",
|
"version": "1.1.11",
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||||
@@ -15803,6 +16057,11 @@
|
|||||||
"integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==",
|
"integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"font-awesome": {
|
||||||
|
"version": "4.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/font-awesome/-/font-awesome-4.7.0.tgz",
|
||||||
|
"integrity": "sha1-j6jPBBGhoxr9B7BtKQK7n8gVoTM="
|
||||||
|
},
|
||||||
"forwarded": {
|
"forwarded": {
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
||||||
@@ -16691,6 +16950,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"jquery": {
|
||||||
|
"version": "3.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.0.tgz",
|
||||||
|
"integrity": "sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw=="
|
||||||
|
},
|
||||||
"js-tokens": {
|
"js-tokens": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||||
@@ -17459,6 +17723,22 @@
|
|||||||
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
|
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"ngx-cookie-service": {
|
||||||
|
"version": "13.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ngx-cookie-service/-/ngx-cookie-service-13.2.0.tgz",
|
||||||
|
"integrity": "sha512-WxuLrZROWf59DfPPstPsrS18nxtPvT+uJ4AEjFs57NqtTfYdRQXhVJ02fZ4WP4VPElI8o6qndNL7gi9tkEdg4Q==",
|
||||||
|
"requires": {
|
||||||
|
"tslib": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ngx-toastr": {
|
||||||
|
"version": "14.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ngx-toastr/-/ngx-toastr-14.3.0.tgz",
|
||||||
|
"integrity": "sha512-d8j/sOr60w5U7rGlcKQ0Ff4u+m2NzhqU5ZdJXn7QW3aR3Zf/rY7/Fd14BmUindTOWVr2NeTYcQXCjLpir0ldpA==",
|
||||||
|
"requires": {
|
||||||
|
"tslib": "^2.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"nice-napi": {
|
"nice-napi": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz",
|
||||||
@@ -18189,6 +18469,11 @@
|
|||||||
"find-up": "^4.0.0"
|
"find-up": "^4.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"popper.js": {
|
||||||
|
"version": "1.16.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz",
|
||||||
|
"integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ=="
|
||||||
|
},
|
||||||
"portfinder": {
|
"portfinder": {
|
||||||
"version": "1.0.28",
|
"version": "1.0.28",
|
||||||
"resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz",
|
"resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz",
|
||||||
|
|||||||
12
package.json
12
package.json
@@ -11,13 +11,25 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/animations": "~13.3.0",
|
"@angular/animations": "~13.3.0",
|
||||||
|
"@angular/cdk": "^13.3.6",
|
||||||
"@angular/common": "~13.3.0",
|
"@angular/common": "~13.3.0",
|
||||||
"@angular/compiler": "~13.3.0",
|
"@angular/compiler": "~13.3.0",
|
||||||
"@angular/core": "~13.3.0",
|
"@angular/core": "~13.3.0",
|
||||||
|
"@angular/flex-layout": "^13.0.0-beta.38",
|
||||||
"@angular/forms": "~13.3.0",
|
"@angular/forms": "~13.3.0",
|
||||||
|
"@angular/material": "^13.3.6",
|
||||||
"@angular/platform-browser": "~13.3.0",
|
"@angular/platform-browser": "~13.3.0",
|
||||||
"@angular/platform-browser-dynamic": "~13.3.0",
|
"@angular/platform-browser-dynamic": "~13.3.0",
|
||||||
"@angular/router": "~13.3.0",
|
"@angular/router": "~13.3.0",
|
||||||
|
"@fortawesome/angular-fontawesome": "^0.10.2",
|
||||||
|
"@fortawesome/fontawesome-svg-core": "^6.1.1",
|
||||||
|
"@fortawesome/free-solid-svg-icons": "^6.1.1",
|
||||||
|
"bootstrap": "^5.1.3",
|
||||||
|
"font-awesome": "^4.7.0",
|
||||||
|
"jquery": "^3.6.0",
|
||||||
|
"ngx-cookie-service": "^13.2.0",
|
||||||
|
"ngx-toastr": "^14.3.0",
|
||||||
|
"popper.js": "^1.16.1",
|
||||||
"rxjs": "~7.5.0",
|
"rxjs": "~7.5.0",
|
||||||
"tslib": "^2.3.0",
|
"tslib": "^2.3.0",
|
||||||
"zone.js": "~0.11.4"
|
"zone.js": "~0.11.4"
|
||||||
|
|||||||
@@ -1,10 +1,47 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { RouterModule, Routes } from '@angular/router';
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
|
import { HomeComponent } from './components/home/home.component';
|
||||||
|
import { LoginComponent } from './components/login/login.component';
|
||||||
|
import { UserEditComponent } from './components/workspace/user-edit/user-edit.component';
|
||||||
|
import { WorkspaceComponent } from './components/workspace/workspace.component';
|
||||||
|
import { UsersListComponent } from './components/workspace/users-list/users-list.component';
|
||||||
|
import { UserItemComponent } from './components/workspace/users-list/user-item/user-item.component';
|
||||||
|
import { MessagesComponent } from './components/workspace/messages/messages.component';
|
||||||
|
import { ProfileService } from './services/profile/profile.service';
|
||||||
|
|
||||||
const routes: Routes = [];
|
const routes: Routes = [
|
||||||
|
{
|
||||||
|
path:"login",
|
||||||
|
component:LoginComponent
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path:"",
|
||||||
|
component:HomeComponent,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path:"edit/:id",
|
||||||
|
component:UserEditComponent
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path:"utilisateurs",
|
||||||
|
component:UsersListComponent
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path:"messages",
|
||||||
|
component:MessagesComponent
|
||||||
|
}
|
||||||
|
],
|
||||||
|
canActivate: [ProfileService]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path:'**',
|
||||||
|
redirectTo:'/'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [RouterModule.forRoot(routes)],
|
imports: [RouterModule.forRoot(routes)],
|
||||||
exports: [RouterModule]
|
exports: [RouterModule]
|
||||||
})
|
})
|
||||||
|
|
||||||
export class AppRoutingModule { }
|
export class AppRoutingModule { }
|
||||||
|
|||||||
@@ -1,484 +1 @@
|
|||||||
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->
|
|
||||||
<!-- * * * * * * * * * * * The content below * * * * * * * * * * * -->
|
|
||||||
<!-- * * * * * * * * * * is only a placeholder * * * * * * * * * * -->
|
|
||||||
<!-- * * * * * * * * * * and can be replaced. * * * * * * * * * * * -->
|
|
||||||
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->
|
|
||||||
<!-- * * * * * * * * * Delete the template below * * * * * * * * * * -->
|
|
||||||
<!-- * * * * * * * to get started with your project! * * * * * * * * -->
|
|
||||||
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->
|
|
||||||
|
|
||||||
<style>
|
|
||||||
:host {
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
|
|
||||||
font-size: 14px;
|
|
||||||
color: #333;
|
|
||||||
box-sizing: border-box;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
-moz-osx-font-smoothing: grayscale;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1,
|
|
||||||
h2,
|
|
||||||
h3,
|
|
||||||
h4,
|
|
||||||
h5,
|
|
||||||
h6 {
|
|
||||||
margin: 8px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.spacer {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toolbar {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
height: 60px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
background-color: #1976d2;
|
|
||||||
color: white;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toolbar img {
|
|
||||||
margin: 0 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toolbar #twitter-logo {
|
|
||||||
height: 40px;
|
|
||||||
margin: 0 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toolbar #youtube-logo {
|
|
||||||
height: 40px;
|
|
||||||
margin: 0 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toolbar #twitter-logo:hover,
|
|
||||||
.toolbar #youtube-logo:hover {
|
|
||||||
opacity: 0.8;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
display: flex;
|
|
||||||
margin: 82px auto 32px;
|
|
||||||
padding: 0 16px;
|
|
||||||
max-width: 960px;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
svg.material-icons {
|
|
||||||
height: 24px;
|
|
||||||
width: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
svg.material-icons:not(:last-child) {
|
|
||||||
margin-right: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card svg.material-icons path {
|
|
||||||
fill: #888;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-container {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
justify-content: center;
|
|
||||||
margin-top: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card {
|
|
||||||
all: unset;
|
|
||||||
border-radius: 4px;
|
|
||||||
border: 1px solid #eee;
|
|
||||||
background-color: #fafafa;
|
|
||||||
height: 40px;
|
|
||||||
width: 200px;
|
|
||||||
margin: 0 8px 16px;
|
|
||||||
padding: 16px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
transition: all 0.2s ease-in-out;
|
|
||||||
line-height: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-container .card:not(:last-child) {
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card.card-small {
|
|
||||||
height: 16px;
|
|
||||||
width: 168px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-container .card:not(.highlight-card) {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-container .card:not(.highlight-card):hover {
|
|
||||||
transform: translateY(-3px);
|
|
||||||
box-shadow: 0 4px 17px rgba(0, 0, 0, 0.35);
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-container .card:not(.highlight-card):hover .material-icons path {
|
|
||||||
fill: rgb(105, 103, 103);
|
|
||||||
}
|
|
||||||
|
|
||||||
.card.highlight-card {
|
|
||||||
background-color: #1976d2;
|
|
||||||
color: white;
|
|
||||||
font-weight: 600;
|
|
||||||
border: none;
|
|
||||||
width: auto;
|
|
||||||
min-width: 30%;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card.card.highlight-card span {
|
|
||||||
margin-left: 60px;
|
|
||||||
}
|
|
||||||
|
|
||||||
svg#rocket {
|
|
||||||
width: 80px;
|
|
||||||
position: absolute;
|
|
||||||
left: -10px;
|
|
||||||
top: -24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
svg#rocket-smoke {
|
|
||||||
height: calc(100vh - 95px);
|
|
||||||
position: absolute;
|
|
||||||
top: 10px;
|
|
||||||
right: 180px;
|
|
||||||
z-index: -10;
|
|
||||||
}
|
|
||||||
|
|
||||||
a,
|
|
||||||
a:visited,
|
|
||||||
a:hover {
|
|
||||||
color: #1976d2;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover {
|
|
||||||
color: #125699;
|
|
||||||
}
|
|
||||||
|
|
||||||
.terminal {
|
|
||||||
position: relative;
|
|
||||||
width: 80%;
|
|
||||||
max-width: 600px;
|
|
||||||
border-radius: 6px;
|
|
||||||
padding-top: 45px;
|
|
||||||
margin-top: 8px;
|
|
||||||
overflow: hidden;
|
|
||||||
background-color: rgb(15, 15, 16);
|
|
||||||
}
|
|
||||||
|
|
||||||
.terminal::before {
|
|
||||||
content: "\2022 \2022 \2022";
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
height: 4px;
|
|
||||||
background: rgb(58, 58, 58);
|
|
||||||
color: #c2c3c4;
|
|
||||||
width: 100%;
|
|
||||||
font-size: 2rem;
|
|
||||||
line-height: 0;
|
|
||||||
padding: 14px 0;
|
|
||||||
text-indent: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.terminal pre {
|
|
||||||
font-family: SFMono-Regular,Consolas,Liberation Mono,Menlo,monospace;
|
|
||||||
color: white;
|
|
||||||
padding: 0 1rem 1rem;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.circle-link {
|
|
||||||
height: 40px;
|
|
||||||
width: 40px;
|
|
||||||
border-radius: 40px;
|
|
||||||
margin: 8px;
|
|
||||||
background-color: white;
|
|
||||||
border: 1px solid #eeeeee;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
cursor: pointer;
|
|
||||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);
|
|
||||||
transition: 1s ease-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
.circle-link:hover {
|
|
||||||
transform: translateY(-0.25rem);
|
|
||||||
box-shadow: 0px 3px 15px rgba(0, 0, 0, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
footer {
|
|
||||||
margin-top: 8px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
line-height: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
footer a {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.github-star-badge {
|
|
||||||
color: #24292e;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
font-size: 12px;
|
|
||||||
padding: 3px 10px;
|
|
||||||
border: 1px solid rgba(27,31,35,.2);
|
|
||||||
border-radius: 3px;
|
|
||||||
background-image: linear-gradient(-180deg,#fafbfc,#eff3f6 90%);
|
|
||||||
margin-left: 4px;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.github-star-badge:hover {
|
|
||||||
background-image: linear-gradient(-180deg,#f0f3f6,#e6ebf1 90%);
|
|
||||||
border-color: rgba(27,31,35,.35);
|
|
||||||
background-position: -.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.github-star-badge .material-icons {
|
|
||||||
height: 16px;
|
|
||||||
width: 16px;
|
|
||||||
margin-right: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
svg#clouds {
|
|
||||||
position: fixed;
|
|
||||||
bottom: -160px;
|
|
||||||
left: -230px;
|
|
||||||
z-index: -10;
|
|
||||||
width: 1920px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Responsive Styles */
|
|
||||||
@media screen and (max-width: 767px) {
|
|
||||||
.card-container > *:not(.circle-link) ,
|
|
||||||
.terminal {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card:not(.highlight-card) {
|
|
||||||
height: 16px;
|
|
||||||
margin: 8px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card.highlight-card span {
|
|
||||||
margin-left: 72px;
|
|
||||||
}
|
|
||||||
|
|
||||||
svg#rocket-smoke {
|
|
||||||
right: 120px;
|
|
||||||
transform: rotate(-5deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: 575px) {
|
|
||||||
svg#rocket-smoke {
|
|
||||||
display: none;
|
|
||||||
visibility: hidden;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<!-- Toolbar -->
|
|
||||||
<div class="toolbar" role="banner">
|
|
||||||
<img
|
|
||||||
width="40"
|
|
||||||
alt="Angular Logo"
|
|
||||||
src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTAgMjUwIj4KICAgIDxwYXRoIGZpbGw9IiNERDAwMzEiIGQ9Ik0xMjUgMzBMMzEuOSA2My4ybDE0LjIgMTIzLjFMMTI1IDIzMGw3OC45LTQzLjcgMTQuMi0xMjMuMXoiIC8+CiAgICA8cGF0aCBmaWxsPSIjQzMwMDJGIiBkPSJNMTI1IDMwdjIyLjItLjFWMjMwbDc4LjktNDMuNyAxNC4yLTEyMy4xTDEyNSAzMHoiIC8+CiAgICA8cGF0aCAgZmlsbD0iI0ZGRkZGRiIgZD0iTTEyNSA1Mi4xTDY2LjggMTgyLjZoMjEuN2wxMS43LTI5LjJoNDkuNGwxMS43IDI5LjJIMTgzTDEyNSA1Mi4xem0xNyA4My4zaC0zNGwxNy00MC45IDE3IDQwLjl6IiAvPgogIDwvc3ZnPg=="
|
|
||||||
/>
|
|
||||||
<span>Welcome</span>
|
|
||||||
<div class="spacer"></div>
|
|
||||||
<a aria-label="Angular on twitter" target="_blank" rel="noopener" href="https://twitter.com/angular" title="Twitter">
|
|
||||||
<svg id="twitter-logo" height="24" data-name="Logo" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 400">
|
|
||||||
<rect width="400" height="400" fill="none"/>
|
|
||||||
<path d="M153.62,301.59c94.34,0,145.94-78.16,145.94-145.94,0-2.22,0-4.43-.15-6.63A104.36,104.36,0,0,0,325,122.47a102.38,102.38,0,0,1-29.46,8.07,51.47,51.47,0,0,0,22.55-28.37,102.79,102.79,0,0,1-32.57,12.45,51.34,51.34,0,0,0-87.41,46.78A145.62,145.62,0,0,1,92.4,107.81a51.33,51.33,0,0,0,15.88,68.47A50.91,50.91,0,0,1,85,169.86c0,.21,0,.43,0,.65a51.31,51.31,0,0,0,41.15,50.28,51.21,51.21,0,0,1-23.16.88,51.35,51.35,0,0,0,47.92,35.62,102.92,102.92,0,0,1-63.7,22A104.41,104.41,0,0,1,75,278.55a145.21,145.21,0,0,0,78.62,23" fill="#fff"/>
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
<a aria-label="Angular on YouTube" target="_blank" rel="noopener" href="https://youtube.com/angular" title="YouTube">
|
|
||||||
<svg id="youtube-logo" height="24" width="24" data-name="Logo" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#fff">
|
|
||||||
<path d="M0 0h24v24H0V0z" fill="none"/>
|
|
||||||
<path d="M21.58 7.19c-.23-.86-.91-1.54-1.77-1.77C18.25 5 12 5 12 5s-6.25 0-7.81.42c-.86.23-1.54.91-1.77 1.77C2 8.75 2 12 2 12s0 3.25.42 4.81c.23.86.91 1.54 1.77 1.77C5.75 19 12 19 12 19s6.25 0 7.81-.42c.86-.23 1.54-.91 1.77-1.77C22 15.25 22 12 22 12s0-3.25-.42-4.81zM10 15V9l5.2 3-5.2 3z"/>
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="content" role="main">
|
|
||||||
|
|
||||||
<!-- Highlight Card -->
|
|
||||||
<div class="card highlight-card card-small">
|
|
||||||
|
|
||||||
<svg id="rocket" xmlns="http://www.w3.org/2000/svg" width="101.678" height="101.678" viewBox="0 0 101.678 101.678">
|
|
||||||
<title>Rocket Ship</title>
|
|
||||||
<g id="Group_83" data-name="Group 83" transform="translate(-141 -696)">
|
|
||||||
<circle id="Ellipse_8" data-name="Ellipse 8" cx="50.839" cy="50.839" r="50.839" transform="translate(141 696)" fill="#dd0031"/>
|
|
||||||
<g id="Group_47" data-name="Group 47" transform="translate(165.185 720.185)">
|
|
||||||
<path id="Path_33" data-name="Path 33" d="M3.4,42.615a3.084,3.084,0,0,0,3.553,3.553,21.419,21.419,0,0,0,12.215-6.107L9.511,30.4A21.419,21.419,0,0,0,3.4,42.615Z" transform="translate(0.371 3.363)" fill="#fff"/>
|
|
||||||
<path id="Path_34" data-name="Path 34" d="M53.3,3.221A3.09,3.09,0,0,0,50.081,0,48.227,48.227,0,0,0,18.322,13.437c-6-1.666-14.991-1.221-18.322,7.218A33.892,33.892,0,0,1,9.439,25.1l-.333.666a3.013,3.013,0,0,0,.555,3.553L23.985,43.641a2.9,2.9,0,0,0,3.553.555l.666-.333A33.892,33.892,0,0,1,32.647,53.3c8.55-3.664,8.884-12.326,7.218-18.322A48.227,48.227,0,0,0,53.3,3.221ZM34.424,9.772a6.439,6.439,0,1,1,9.106,9.106,6.368,6.368,0,0,1-9.106,0A6.467,6.467,0,0,1,34.424,9.772Z" transform="translate(0 0.005)" fill="#fff"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
|
||||||
<span>{{ title }} app is running!</span>
|
|
||||||
|
|
||||||
<svg id="rocket-smoke" xmlns="http://www.w3.org/2000/svg" width="516.119" height="1083.632" viewBox="0 0 516.119 1083.632">
|
|
||||||
<title>Rocket Ship Smoke</title>
|
|
||||||
<path id="Path_40" data-name="Path 40" d="M644.6,141S143.02,215.537,147.049,870.207s342.774,201.755,342.774,201.755S404.659,847.213,388.815,762.2c-27.116-145.51-11.551-384.124,271.9-609.1C671.15,139.365,644.6,141,644.6,141Z" transform="translate(-147.025 -140.939)" fill="#f5f5f5"/>
|
|
||||||
</svg>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Resources -->
|
|
||||||
<h2>Resources</h2>
|
|
||||||
<p>Here are some links to help you get started:</p>
|
|
||||||
|
|
||||||
<div class="card-container">
|
|
||||||
<a class="card" target="_blank" rel="noopener" href="https://angular.io/tutorial">
|
|
||||||
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M5 13.18v4L12 21l7-3.82v-4L12 17l-7-3.82zM12 3L1 9l11 6 9-4.91V17h2V9L12 3z"/></svg>
|
|
||||||
<span>Learn Angular</span>
|
|
||||||
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/></svg> </a>
|
|
||||||
|
|
||||||
<a class="card" target="_blank" rel="noopener" href="https://angular.io/cli">
|
|
||||||
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M9.4 16.6L4.8 12l4.6-4.6L8 6l-6 6 6 6 1.4-1.4zm5.2 0l4.6-4.6-4.6-4.6L16 6l6 6-6 6-1.4-1.4z"/></svg>
|
|
||||||
<span>CLI Documentation</span>
|
|
||||||
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/></svg>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a class="card" target="_blank" rel="noopener" href="https://material.angular.io">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" style="margin-right: 8px" width="21.813" height="23.453" viewBox="0 0 21.813 23.453"><path d="M4099.584,972.736h0l-10.882,3.9,1.637,14.4,9.245,5.153,9.245-5.153,1.686-14.4Z" transform="translate(-4088.702 -972.736)" fill="#808080"/><path d="M4181.516,972.736v23.453l9.245-5.153,1.686-14.4Z" transform="translate(-4170.633 -972.736)" fill="#808080"/><path d="M4137.529,1076.127l-7.7-3.723,4.417-2.721,7.753,3.723Z" transform="translate(-4125.003 -1058.315)" fill="#ffe0b2"/><path d="M4137.529,1051.705l-7.7-3.723,4.417-2.721,7.753,3.723Z" transform="translate(-4125.003 -1036.757)" fill="#fff3e0"/><path d="M4137.529,1027.283l-7.7-3.723,4.417-2.721,7.753,3.723Z" transform="translate(-4125.003 -1015.199)" fill="#fff"/></svg>
|
|
||||||
<span>Angular Material</span>
|
|
||||||
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/></svg>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a class="card" target="_blank" rel="noopener" href="https://blog.angular.io/">
|
|
||||||
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M13.5.67s.74 2.65.74 4.8c0 2.06-1.35 3.73-3.41 3.73-2.07 0-3.63-1.67-3.63-3.73l.03-.36C5.21 7.51 4 10.62 4 14c0 4.42 3.58 8 8 8s8-3.58 8-8C20 8.61 17.41 3.8 13.5.67zM11.71 19c-1.78 0-3.22-1.4-3.22-3.14 0-1.62 1.05-2.76 2.81-3.12 1.77-.36 3.6-1.21 4.62-2.58.39 1.29.59 2.65.59 4.04 0 2.65-2.15 4.8-4.8 4.8z"/></svg>
|
|
||||||
<span>Angular Blog</span>
|
|
||||||
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/></svg>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a class="card" target="_blank" rel="noopener" href="https://angular.io/devtools/">
|
|
||||||
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><g><rect fill="none" height="24" width="24"/></g><g><g><path d="M14.73,13.31C15.52,12.24,16,10.93,16,9.5C16,5.91,13.09,3,9.5,3S3,5.91,3,9.5C3,13.09,5.91,16,9.5,16 c1.43,0,2.74-0.48,3.81-1.27L19.59,21L21,19.59L14.73,13.31z M9.5,14C7.01,14,5,11.99,5,9.5S7.01,5,9.5,5S14,7.01,14,9.5 S11.99,14,9.5,14z"/><polygon points="10.29,8.44 9.5,6 8.71,8.44 6.25,8.44 8.26,10.03 7.49,12.5 9.5,10.97 11.51,12.5 10.74,10.03 12.75,8.44"/></g></g></svg>
|
|
||||||
<span>Angular DevTools</span>
|
|
||||||
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/></svg>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Next Steps -->
|
|
||||||
<h2>Next Steps</h2>
|
|
||||||
<p>What do you want to do next with your app?</p>
|
|
||||||
|
|
||||||
<input type="hidden" #selection>
|
|
||||||
|
|
||||||
<div class="card-container">
|
|
||||||
<button class="card card-small" (click)="selection.value = 'component'" tabindex="0">
|
|
||||||
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg>
|
|
||||||
<span>New Component</span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button class="card card-small" (click)="selection.value = 'material'" tabindex="0">
|
|
||||||
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg>
|
|
||||||
<span>Angular Material</span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button class="card card-small" (click)="selection.value = 'pwa'" tabindex="0">
|
|
||||||
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg>
|
|
||||||
<span>Add PWA Support</span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button class="card card-small" (click)="selection.value = 'dependency'" tabindex="0">
|
|
||||||
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg>
|
|
||||||
<span>Add Dependency</span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button class="card card-small" (click)="selection.value = 'test'" tabindex="0">
|
|
||||||
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg>
|
|
||||||
<span>Run and Watch Tests</span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button class="card card-small" (click)="selection.value = 'build'" tabindex="0">
|
|
||||||
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg>
|
|
||||||
<span>Build for Production</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Terminal -->
|
|
||||||
<div class="terminal" [ngSwitch]="selection.value">
|
|
||||||
<pre *ngSwitchDefault>ng generate component xyz</pre>
|
|
||||||
<pre *ngSwitchCase="'material'">ng add @angular/material</pre>
|
|
||||||
<pre *ngSwitchCase="'pwa'">ng add @angular/pwa</pre>
|
|
||||||
<pre *ngSwitchCase="'dependency'">ng add _____</pre>
|
|
||||||
<pre *ngSwitchCase="'test'">ng test</pre>
|
|
||||||
<pre *ngSwitchCase="'build'">ng build</pre>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Links -->
|
|
||||||
<div class="card-container">
|
|
||||||
<a class="circle-link" title="Find a Local Meetup" href="https://www.meetup.com/find/?keywords=angular" target="_blank" rel="noopener">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24.607" height="23.447" viewBox="0 0 24.607 23.447">
|
|
||||||
<title>Meetup Logo</title>
|
|
||||||
<path id="logo--mSwarm" d="M21.221,14.95A4.393,4.393,0,0,1,17.6,19.281a4.452,4.452,0,0,1-.8.069c-.09,0-.125.035-.154.117a2.939,2.939,0,0,1-2.506,2.091,2.868,2.868,0,0,1-2.248-.624.168.168,0,0,0-.245-.005,3.926,3.926,0,0,1-2.589.741,4.015,4.015,0,0,1-3.7-3.347,2.7,2.7,0,0,1-.043-.38c0-.106-.042-.146-.143-.166a3.524,3.524,0,0,1-1.516-.69A3.623,3.623,0,0,1,2.23,14.557a3.66,3.66,0,0,1,1.077-3.085.138.138,0,0,0,.026-.2,3.348,3.348,0,0,1-.451-1.821,3.46,3.46,0,0,1,2.749-3.28.44.44,0,0,0,.355-.281,5.072,5.072,0,0,1,3.863-3,5.028,5.028,0,0,1,3.555.666.31.31,0,0,0,.271.03A4.5,4.5,0,0,1,18.3,4.7a4.4,4.4,0,0,1,1.334,2.751,3.658,3.658,0,0,1,.022.706.131.131,0,0,0,.1.157,2.432,2.432,0,0,1,1.574,1.645,2.464,2.464,0,0,1-.7,2.616c-.065.064-.051.1-.014.166A4.321,4.321,0,0,1,21.221,14.95ZM13.4,14.607a2.09,2.09,0,0,0,1.409,1.982,4.7,4.7,0,0,0,1.275.221,1.807,1.807,0,0,0,.9-.151.542.542,0,0,0,.321-.545.558.558,0,0,0-.359-.534,1.2,1.2,0,0,0-.254-.078c-.262-.047-.526-.086-.787-.138a.674.674,0,0,1-.617-.75,3.394,3.394,0,0,1,.218-1.109c.217-.658.509-1.286.79-1.918a15.609,15.609,0,0,0,.745-1.86,1.95,1.95,0,0,0,.06-1.073,1.286,1.286,0,0,0-1.051-1.033,1.977,1.977,0,0,0-1.521.2.339.339,0,0,1-.446-.042c-.1-.092-.2-.189-.307-.284a1.214,1.214,0,0,0-1.643-.061,7.563,7.563,0,0,1-.614.512A.588.588,0,0,1,10.883,8c-.215-.115-.437-.215-.659-.316a2.153,2.153,0,0,0-.695-.248A2.091,2.091,0,0,0,7.541,8.562a9.915,9.915,0,0,0-.405.986c-.559,1.545-1.015,3.123-1.487,4.7a1.528,1.528,0,0,0,.634,1.777,1.755,1.755,0,0,0,1.5.211,1.35,1.35,0,0,0,.824-.858c.543-1.281,1.032-2.584,1.55-3.875.142-.355.28-.712.432-1.064a.548.548,0,0,1,.851-.24.622.622,0,0,1,.185.539,2.161,2.161,0,0,1-.181.621c-.337.852-.68,1.7-1.018,2.552a2.564,2.564,0,0,0-.173.528.624.624,0,0,0,.333.71,1.073,1.073,0,0,0,.814.034,1.22,1.22,0,0,0,.657-.655q.758-1.488,1.511-2.978.35-.687.709-1.37a1.073,1.073,0,0,1,.357-.434.43.43,0,0,1,.463-.016.373.373,0,0,1,.153.387.7.7,0,0,1-.057.236c-.065.157-.127.316-.2.469-.42.883-.846,1.763-1.262,2.648A2.463,2.463,0,0,0,13.4,14.607Zm5.888,6.508a1.09,1.09,0,0,0-2.179.006,1.09,1.09,0,0,0,2.179-.006ZM1.028,12.139a1.038,1.038,0,1,0,.01-2.075,1.038,1.038,0,0,0-.01,2.075ZM13.782.528a1.027,1.027,0,1,0-.011,2.055A1.027,1.027,0,0,0,13.782.528ZM22.21,6.95a.882.882,0,0,0-1.763.011A.882.882,0,0,0,22.21,6.95ZM4.153,4.439a.785.785,0,1,0,.787-.78A.766.766,0,0,0,4.153,4.439Zm8.221,18.22a.676.676,0,1,0-.677.666A.671.671,0,0,0,12.374,22.658ZM22.872,12.2a.674.674,0,0,0-.665.665.656.656,0,0,0,.655.643.634.634,0,0,0,.655-.644A.654.654,0,0,0,22.872,12.2ZM7.171-.123A.546.546,0,0,0,6.613.43a.553.553,0,1,0,1.106,0A.539.539,0,0,0,7.171-.123ZM24.119,9.234a.507.507,0,0,0-.493.488.494.494,0,0,0,.494.494.48.48,0,0,0,.487-.483A.491.491,0,0,0,24.119,9.234Zm-19.454,9.7a.5.5,0,0,0-.488-.488.491.491,0,0,0-.487.5.483.483,0,0,0,.491.479A.49.49,0,0,0,4.665,18.936Z" transform="translate(0 0.123)" fill="#f64060"/>
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a class="circle-link" title="Join the Conversation on Discord" href="https://discord.gg/angular" target="_blank" rel="noopener">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="26" height="26" viewBox="0 0 245 240">
|
|
||||||
<title>Discord Logo</title>
|
|
||||||
<path d="M104.4 103.9c-5.7 0-10.2 5-10.2 11.1s4.6 11.1 10.2 11.1c5.7 0 10.2-5 10.2-11.1.1-6.1-4.5-11.1-10.2-11.1zM140.9 103.9c-5.7 0-10.2 5-10.2 11.1s4.6 11.1 10.2 11.1c5.7 0 10.2-5 10.2-11.1s-4.5-11.1-10.2-11.1z"/>
|
|
||||||
<path d="M189.5 20h-134C44.2 20 35 29.2 35 40.6v135.2c0 11.4 9.2 20.6 20.5 20.6h113.4l-5.3-18.5 12.8 11.9 12.1 11.2 21.5 19V40.6c0-11.4-9.2-20.6-20.5-20.6zm-38.6 130.6s-3.6-4.3-6.6-8.1c13.1-3.7 18.1-11.9 18.1-11.9-4.1 2.7-8 4.6-11.5 5.9-5 2.1-9.8 3.5-14.5 4.3-9.6 1.8-18.4 1.3-25.9-.1-5.7-1.1-10.6-2.7-14.7-4.3-2.3-.9-4.8-2-7.3-3.4-.3-.2-.6-.3-.9-.5-.2-.1-.3-.2-.4-.3-1.8-1-2.8-1.7-2.8-1.7s4.8 8 17.5 11.8c-3 3.8-6.7 8.3-6.7 8.3-22.1-.7-30.5-15.2-30.5-15.2 0-32.2 14.4-58.3 14.4-58.3 14.4-10.8 28.1-10.5 28.1-10.5l1 1.2c-18 5.2-26.3 13.1-26.3 13.1s2.2-1.2 5.9-2.9c10.7-4.7 19.2-6 22.7-6.3.6-.1 1.1-.2 1.7-.2 6.1-.8 13-1 20.2-.2 9.5 1.1 19.7 3.9 30.1 9.6 0 0-7.9-7.5-24.9-12.7l1.4-1.6s13.7-.3 28.1 10.5c0 0 14.4 26.1 14.4 58.3 0 0-8.5 14.5-30.6 15.2z"/>
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Footer -->
|
|
||||||
<footer>
|
|
||||||
Love Angular?
|
|
||||||
<a href="https://github.com/angular/angular" target="_blank" rel="noopener"> Give our repo a star.
|
|
||||||
<div class="github-star-badge">
|
|
||||||
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M0 0h24v24H0z" fill="none"/><path d="M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z"/></svg>
|
|
||||||
Star
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
<a href="https://github.com/angular/angular" target="_blank" rel="noopener">
|
|
||||||
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z" fill="#1976d2"/><path d="M0 0h24v24H0z" fill="none"/></svg>
|
|
||||||
</a>
|
|
||||||
</footer>
|
|
||||||
|
|
||||||
<svg id="clouds" xmlns="http://www.w3.org/2000/svg" width="2611.084" height="485.677" viewBox="0 0 2611.084 485.677">
|
|
||||||
<title>Gray Clouds Background</title>
|
|
||||||
<path id="Path_39" data-name="Path 39" d="M2379.709,863.793c10-93-77-171-168-149-52-114-225-105-264,15-75,3-140,59-152,133-30,2.83-66.725,9.829-93.5,26.25-26.771-16.421-63.5-23.42-93.5-26.25-12-74-77-130-152-133-39-120-212-129-264-15-54.084-13.075-106.753,9.173-138.488,48.9-31.734-39.726-84.4-61.974-138.487-48.9-52-114-225-105-264,15a162.027,162.027,0,0,0-103.147,43.044c-30.633-45.365-87.1-72.091-145.206-58.044-52-114-225-105-264,15-75,3-140,59-152,133-53,5-127,23-130,83-2,42,35,72,70,86,49,20,106,18,157,5a165.625,165.625,0,0,0,120,0c47,94,178,113,251,33,61.112,8.015,113.854-5.72,150.492-29.764a165.62,165.62,0,0,0,110.861-3.236c47,94,178,113,251,33,31.385,4.116,60.563,2.495,86.487-3.311,25.924,5.806,55.1,7.427,86.488,3.311,73,80,204,61,251-33a165.625,165.625,0,0,0,120,0c51,13,108,15,157-5a147.188,147.188,0,0,0,33.5-18.694,147.217,147.217,0,0,0,33.5,18.694c49,20,106,18,157,5a165.625,165.625,0,0,0,120,0c47,94,178,113,251,33C2446.709,1093.793,2554.709,922.793,2379.709,863.793Z" transform="translate(142.69 -634.312)" fill="#eee"/>
|
|
||||||
</svg>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->
|
|
||||||
<!-- * * * * * * * * * * * The content above * * * * * * * * * * * -->
|
|
||||||
<!-- * * * * * * * * * * is only a placeholder * * * * * * * * * * -->
|
|
||||||
<!-- * * * * * * * * * * and can be replaced. * * * * * * * * * * * -->
|
|
||||||
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->
|
|
||||||
<!-- * * * * * * * * * * End of Placeholder * * * * * * * * * * * -->
|
|
||||||
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->
|
|
||||||
|
|
||||||
<router-outlet></router-outlet>
|
<router-outlet></router-outlet>
|
||||||
|
|||||||
@@ -1,18 +1,92 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
|
import { HttpClientModule, HttpClientXsrfModule, HTTP_INTERCEPTORS } from '@angular/common/http';
|
||||||
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||||
|
import { CookieService } from 'ngx-cookie-service';
|
||||||
|
import { RouterModule } from '@angular/router';
|
||||||
|
|
||||||
|
import { MatToolbarModule } from '@angular/material/toolbar';
|
||||||
|
import { MatSidenavModule } from '@angular/material/sidenav';
|
||||||
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
|
import { MatDividerModule } from '@angular/material/divider';
|
||||||
|
import { MatDialogModule } from '@angular/material/dialog';
|
||||||
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||||
|
import { MatInputModule } from '@angular/material/input';
|
||||||
|
import { MatCardModule } from '@angular/material/card';
|
||||||
|
import { MatPaginatorModule } from '@angular/material/paginator';
|
||||||
|
import { MatSortModule } from '@angular/material/sort';
|
||||||
|
import { MatTableModule } from "@angular/material/table";
|
||||||
|
import { MatSelectModule } from '@angular/material/select';
|
||||||
|
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||||
|
|
||||||
import { AppRoutingModule } from './app-routing.module';
|
import { AppRoutingModule } from './app-routing.module';
|
||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component';
|
||||||
|
import { LoginComponent } from './components/login/login.component';
|
||||||
|
import { HomeComponent } from './components/home/home.component';
|
||||||
|
import { HttpXsrfInterceptorService } from './interceptors/http-xsrf-interceptor/http-xsrf-interceptor.service';
|
||||||
|
import { NavbarComponent } from './components/navbar/navbar.component';
|
||||||
|
import { WorkspaceComponent } from './components/workspace/workspace.component';
|
||||||
|
import { UserEditComponent } from './components/workspace/user-edit/user-edit.component';
|
||||||
|
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
|
import { FlexLayoutModule } from '@angular/flex-layout';
|
||||||
|
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
|
||||||
|
|
||||||
|
import { ToastrModule } from 'ngx-toastr';
|
||||||
|
import { UsersListComponent } from './components/workspace/users-list/users-list.component';
|
||||||
|
import { UserItemComponent } from './components/workspace/users-list/user-item/user-item.component';
|
||||||
|
import { ConfirmDialogComponent } from './components/dialog/confirm-dialog/confirm-dialog.component';
|
||||||
|
import { AddUserDialogComponent } from './components/dialog/add-user-dialog/add-user-dialog.component';
|
||||||
|
import { MessagesComponent } from './components/workspace/messages/messages.component';
|
||||||
|
import { ChatboxComponent } from './components/workspace/messages/chatbox/chatbox.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
AppComponent
|
AppComponent,
|
||||||
],
|
LoginComponent,
|
||||||
imports: [
|
HomeComponent,
|
||||||
BrowserModule,
|
NavbarComponent,
|
||||||
AppRoutingModule
|
WorkspaceComponent,
|
||||||
],
|
UserEditComponent,
|
||||||
providers: [],
|
UsersListComponent,
|
||||||
bootstrap: [AppComponent]
|
UserItemComponent,
|
||||||
|
ConfirmDialogComponent,
|
||||||
|
AddUserDialogComponent,
|
||||||
|
MessagesComponent,
|
||||||
|
ChatboxComponent,
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
BrowserModule,
|
||||||
|
AppRoutingModule,
|
||||||
|
HttpClientModule,
|
||||||
|
FormsModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
HttpClientXsrfModule.withOptions({
|
||||||
|
cookieName: 'csrf_access_token'
|
||||||
|
}),
|
||||||
|
BrowserAnimationsModule,
|
||||||
|
MatToolbarModule,
|
||||||
|
MatSidenavModule,
|
||||||
|
MatIconModule,
|
||||||
|
MatDividerModule,
|
||||||
|
MatDialogModule,
|
||||||
|
MatFormFieldModule,
|
||||||
|
MatInputModule,
|
||||||
|
MatButtonModule,
|
||||||
|
MatCardModule,
|
||||||
|
MatPaginatorModule,
|
||||||
|
MatSortModule,
|
||||||
|
MatTableModule,
|
||||||
|
MatSelectModule,
|
||||||
|
MatCheckboxModule,
|
||||||
|
FlexLayoutModule,
|
||||||
|
RouterModule,
|
||||||
|
FontAwesomeModule,
|
||||||
|
ToastrModule.forRoot()
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
{ provide: HTTP_INTERCEPTORS, useClass: HttpXsrfInterceptorService, multi: true }
|
||||||
|
],
|
||||||
|
bootstrap: [AppComponent]
|
||||||
})
|
})
|
||||||
export class AppModule { }
|
export class AppModule { }
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
.mat-form-field {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<h2 mat-dialog-title>Ajout d'un utilisateur</h2>
|
||||||
|
<mat-dialog-content>
|
||||||
|
<div class="container" fxLayout="column" fxLayoutAlign="space-between center">
|
||||||
|
<form [formGroup]="userFG">
|
||||||
|
<mat-form-field>
|
||||||
|
<input matInput type="text" formControlName="prenom" class="form-control" name="Prenom" placeholder="Prénom" [ngClass]="{'is-invalid':submitted && userFG.controls['prenom'].errors}">
|
||||||
|
<mat-error *ngIf="userFG.controls['prenom'].hasError('required')">Le prénom est requis !</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field>
|
||||||
|
<input matInput type="text" formControlName="nom" class="form-control" name="Nom" placeholder="Nom" [ngClass]="{'is-invalid':submitted && userFG.controls['nom'].errors}">
|
||||||
|
<mat-error *ngIf="userFG.controls['nom'].hasError('required')">Le nom est requis !</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field>
|
||||||
|
<input matInput type="text" formControlName="identifiant" class="form-control" name="Identifiant" placeholder="Identifiant" [ngClass]="{'is-invalid':submitted && userFG.controls['identifiant'].errors}">
|
||||||
|
<mat-error *ngIf="userFG.controls['identifiant'].hasError('required')">L'identifiant est requis !</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field>
|
||||||
|
<mat-select [(value)]="selected" formControlName="role" placeholder="Role">
|
||||||
|
<mat-option value="Coach">Coach</mat-option>
|
||||||
|
<mat-option value="Administrateur">Administrateur</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</mat-dialog-content>
|
||||||
|
<mat-dialog-actions align="end">
|
||||||
|
<button mat-button (click)="onAbort()">Annuler</button>
|
||||||
|
<button mat-raised-button color="primary" [disabled]="!userFG.valid" (click)="sendUser(userFG)">OK</button>
|
||||||
|
</mat-dialog-actions>
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { AddUserDialogComponent } from './add-user-dialog.component';
|
||||||
|
|
||||||
|
describe('AddUserDialogComponent', () => {
|
||||||
|
let component: AddUserDialogComponent;
|
||||||
|
let fixture: ComponentFixture<AddUserDialogComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ AddUserDialogComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(AddUserDialogComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import { Component, OnInit, Inject } from '@angular/core';
|
||||||
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||||
|
import { FormBuilder, FormGroup, Validators, ValidatorFn, ValidationErrors, AbstractControl } from '@angular/forms';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { BackendService } from '../../../services/backend.service';
|
||||||
|
import { Utilisateur } from '../../../model/utilisateur.model';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-add-user-dialog',
|
||||||
|
templateUrl: './add-user-dialog.component.html',
|
||||||
|
styleUrls: ['./add-user-dialog.component.css']
|
||||||
|
})
|
||||||
|
export class AddUserDialogComponent implements OnInit {
|
||||||
|
userFG:FormGroup;
|
||||||
|
submitted:boolean=false;
|
||||||
|
selected:string="Coach";
|
||||||
|
constructor(private fb:FormBuilder,
|
||||||
|
private bs:BackendService,
|
||||||
|
private router:Router,
|
||||||
|
@Inject(MAT_DIALOG_DATA) public data:any,
|
||||||
|
public dialogRef: MatDialogRef<AddUserDialogComponent>) { }
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.userFG=this.fb.group({
|
||||||
|
nom:["", Validators.required],
|
||||||
|
prenom:["", Validators.required],
|
||||||
|
identifiant:["", Validators.required],
|
||||||
|
role:[""],
|
||||||
|
});
|
||||||
|
|
||||||
|
this.userFG.get('role')?.setValue("Coach");
|
||||||
|
}
|
||||||
|
|
||||||
|
sendUser(user:FormGroup): void {
|
||||||
|
this.dialogRef.close(user.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
onAbort(): void {
|
||||||
|
this.dialogRef.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<h2 mat-dialog-title>{{title}}</h2>
|
||||||
|
<mat-dialog-content>
|
||||||
|
{{text}}
|
||||||
|
</mat-dialog-content>
|
||||||
|
<mat-dialog-actions align="end">
|
||||||
|
<button mat-button (click)="sendAnswer(false)">{{labelNOK}}</button>
|
||||||
|
<button mat-raised-button color="primary" (click)="sendAnswer(true)">{{labelOK}}</button>
|
||||||
|
</mat-dialog-actions>
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { ConfirmDialogComponent } from './confirm-dialog.component';
|
||||||
|
|
||||||
|
describe('ConfirmDialogComponent', () => {
|
||||||
|
let component: ConfirmDialogComponent;
|
||||||
|
let fixture: ComponentFixture<ConfirmDialogComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ ConfirmDialogComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(ConfirmDialogComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import { Component, OnInit, Inject } from '@angular/core';
|
||||||
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-confirm-dialog',
|
||||||
|
templateUrl: './confirm-dialog.component.html',
|
||||||
|
styleUrls: ['./confirm-dialog.component.css']
|
||||||
|
})
|
||||||
|
export class ConfirmDialogComponent implements OnInit {
|
||||||
|
title:string;
|
||||||
|
text:string;
|
||||||
|
labelOK: string;
|
||||||
|
labelNOK: string;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
@Inject(MAT_DIALOG_DATA) public data:any,
|
||||||
|
public dialogRef: MatDialogRef<ConfirmDialogComponent>
|
||||||
|
) {
|
||||||
|
this.title = data.title;
|
||||||
|
this.text = data.text;
|
||||||
|
this.labelOK = data.labelOK;
|
||||||
|
this.labelNOK = data.labelNOK;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
sendAnswer(val:boolean): void {
|
||||||
|
this.dialogRef.close(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
0
src/app/components/home/home.component.css
Normal file
0
src/app/components/home/home.component.css
Normal file
1
src/app/components/home/home.component.html
Normal file
1
src/app/components/home/home.component.html
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<app-navbar [userInput$]="profile$"></app-navbar>
|
||||||
25
src/app/components/home/home.component.spec.ts
Normal file
25
src/app/components/home/home.component.spec.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { HomeComponent } from './home.component';
|
||||||
|
|
||||||
|
describe('HomeComponent', () => {
|
||||||
|
let component: HomeComponent;
|
||||||
|
let fixture: ComponentFixture<HomeComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ HomeComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(HomeComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
59
src/app/components/home/home.component.ts
Normal file
59
src/app/components/home/home.component.ts
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { Utilisateur } from '../../model/utilisateur.model';
|
||||||
|
import { BackendService } from '../../services/backend.service';
|
||||||
|
import { EventDriverService } from '../../services/event.driver.service';
|
||||||
|
import { ActionsTypes, ActionEvt } from '../../state/user.state';
|
||||||
|
import { Observable, of, Subscription } from 'rxjs';
|
||||||
|
import { catchError, map, startWith } from 'rxjs/operators';
|
||||||
|
import { ToastrService } from 'ngx-toastr';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-home',
|
||||||
|
templateUrl: './home.component.html',
|
||||||
|
styleUrls: ['./home.component.css']
|
||||||
|
})
|
||||||
|
export class HomeComponent implements OnInit {
|
||||||
|
profile$:Observable<Utilisateur>|null=null;
|
||||||
|
sub:Subscription;
|
||||||
|
|
||||||
|
constructor( private bs:BackendService,
|
||||||
|
private evtDrv:EventDriverService,
|
||||||
|
private toast:ToastrService ) { }
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.sub=this.evtDrv.sourceEventSubjectObservable.subscribe(
|
||||||
|
(evt:ActionEvt) => {
|
||||||
|
this.onActionEvent(evt);
|
||||||
|
}, error => {
|
||||||
|
this.toast.error("Une erreur s'est produite ...");
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
this.profile$ = this.bs.getCurrentUser().pipe(
|
||||||
|
map(data => {
|
||||||
|
return data;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this.sub.unsubscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
onActionEvent($evt:ActionEvt): void {
|
||||||
|
console.log("[HOME] ", $evt.type);
|
||||||
|
switch($evt.type) {
|
||||||
|
case ActionsTypes.UPDATE_USER: {
|
||||||
|
this.profile$ = this.bs.getUser($evt.payload.userId).pipe(
|
||||||
|
map(data => {
|
||||||
|
return data;
|
||||||
|
}));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
299
src/app/components/login/login.component.css
Normal file
299
src/app/components/login/login.component.css
Normal file
@@ -0,0 +1,299 @@
|
|||||||
|
|
||||||
|
/* BASIC */
|
||||||
|
|
||||||
|
html {
|
||||||
|
background-color: #56baed;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: "Poppins", sans-serif;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #92badd;
|
||||||
|
display:inline-block;
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
text-transform: uppercase;
|
||||||
|
display:inline-block;
|
||||||
|
margin: 40px 8px 10px 8px;
|
||||||
|
color: #cccccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 600;
|
||||||
|
text-transform: uppercase;
|
||||||
|
display:inline-block;
|
||||||
|
margin: 40px 8px 10px 8px;
|
||||||
|
color: #cccccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* STRUCTURE */
|
||||||
|
|
||||||
|
.wrapper {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
width: 100%;
|
||||||
|
min-height: 100%;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#formContent {
|
||||||
|
-webkit-border-radius: 10px 10px 10px 10px;
|
||||||
|
border-radius: 10px 10px 10px 10px;
|
||||||
|
background: #fff;
|
||||||
|
padding: 30px;
|
||||||
|
width: 90%;
|
||||||
|
max-width: 450px;
|
||||||
|
position: relative;
|
||||||
|
padding: 0px;
|
||||||
|
-webkit-box-shadow: 0 30px 60px 0 rgba(0,0,0,0.3);
|
||||||
|
box-shadow: 0 30px 60px 0 rgba(0,0,0,0.3);
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#formFooter {
|
||||||
|
background-color: #f6f6f6;
|
||||||
|
border-top: 1px solid #dce8f1;
|
||||||
|
padding: 25px;
|
||||||
|
text-align: center;
|
||||||
|
-webkit-border-radius: 0 0 10px 10px;
|
||||||
|
border-radius: 0 0 10px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* TABS */
|
||||||
|
|
||||||
|
h2.inactive {
|
||||||
|
color: #cccccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2.active {
|
||||||
|
color: #0d0d0d;
|
||||||
|
border-bottom: 2px solid #5fbae9;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* FORM TYPOGRAPHY*/
|
||||||
|
|
||||||
|
input[type=button], input[type=submit], input[type=reset] {
|
||||||
|
background-color: #56baed;
|
||||||
|
border: none;
|
||||||
|
color: white;
|
||||||
|
padding: 15px 80px;
|
||||||
|
text-align: center;
|
||||||
|
text-decoration: none;
|
||||||
|
display: inline-block;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-size: 13px;
|
||||||
|
-webkit-box-shadow: 0 10px 30px 0 rgba(95,186,233,0.4);
|
||||||
|
box-shadow: 0 10px 30px 0 rgba(95,186,233,0.4);
|
||||||
|
-webkit-border-radius: 5px 5px 5px 5px;
|
||||||
|
border-radius: 5px 5px 5px 5px;
|
||||||
|
margin: 5px 20px 40px 20px;
|
||||||
|
-webkit-transition: all 0.3s ease-in-out;
|
||||||
|
-moz-transition: all 0.3s ease-in-out;
|
||||||
|
-ms-transition: all 0.3s ease-in-out;
|
||||||
|
-o-transition: all 0.3s ease-in-out;
|
||||||
|
transition: all 0.3s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=button]:hover, input[type=submit]:hover, input[type=reset]:hover {
|
||||||
|
background-color: #39ace7;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=button]:active, input[type=submit]:active, input[type=reset]:active {
|
||||||
|
-moz-transform: scale(0.95);
|
||||||
|
-webkit-transform: scale(0.95);
|
||||||
|
-o-transform: scale(0.95);
|
||||||
|
-ms-transform: scale(0.95);
|
||||||
|
transform: scale(0.95);
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=text] {
|
||||||
|
background-color: #f6f6f6;
|
||||||
|
border: none;
|
||||||
|
color: #0d0d0d;
|
||||||
|
padding: 15px 32px;
|
||||||
|
text-align: center;
|
||||||
|
text-decoration: none;
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 16px;
|
||||||
|
margin: 5px;
|
||||||
|
width: 85%;
|
||||||
|
border: 2px solid #f6f6f6;
|
||||||
|
-webkit-transition: all 0.5s ease-in-out;
|
||||||
|
-moz-transition: all 0.5s ease-in-out;
|
||||||
|
-ms-transition: all 0.5s ease-in-out;
|
||||||
|
-o-transition: all 0.5s ease-in-out;
|
||||||
|
transition: all 0.5s ease-in-out;
|
||||||
|
-webkit-border-radius: 5px 5px 5px 5px;
|
||||||
|
border-radius: 5px 5px 5px 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=text]:focus {
|
||||||
|
background-color: #fff;
|
||||||
|
border-bottom: 2px solid #5fbae9;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=text]:placeholder {
|
||||||
|
color: #cccccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
input[type=password] {
|
||||||
|
background-color: #f6f6f6;
|
||||||
|
border: none;
|
||||||
|
color: #0d0d0d;
|
||||||
|
padding: 15px 32px;
|
||||||
|
text-align: center;
|
||||||
|
text-decoration: none;
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 16px;
|
||||||
|
margin: 5px;
|
||||||
|
width: 85%;
|
||||||
|
border: 2px solid #f6f6f6;
|
||||||
|
-webkit-transition: all 0.5s ease-in-out;
|
||||||
|
-moz-transition: all 0.5s ease-in-out;
|
||||||
|
-ms-transition: all 0.5s ease-in-out;
|
||||||
|
-o-transition: all 0.5s ease-in-out;
|
||||||
|
transition: all 0.5s ease-in-out;
|
||||||
|
-webkit-border-radius: 5px 5px 5px 5px;
|
||||||
|
border-radius: 5px 5px 5px 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=password]:focus {
|
||||||
|
background-color: #fff;
|
||||||
|
border-bottom: 2px solid #5fbae9;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=password]:placeholder {
|
||||||
|
color: #cccccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ANIMATIONS */
|
||||||
|
|
||||||
|
/* Simple CSS3 Fade-in-down Animation */
|
||||||
|
.fadeInDown {
|
||||||
|
-webkit-animation-name: fadeInDown;
|
||||||
|
animation-name: fadeInDown;
|
||||||
|
-webkit-animation-duration: 1s;
|
||||||
|
animation-duration: 1s;
|
||||||
|
-webkit-animation-fill-mode: both;
|
||||||
|
animation-fill-mode: both;
|
||||||
|
}
|
||||||
|
|
||||||
|
@-webkit-keyframes fadeInDown {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
-webkit-transform: translate3d(0, -100%, 0);
|
||||||
|
transform: translate3d(0, -100%, 0);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
-webkit-transform: none;
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeInDown {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
-webkit-transform: translate3d(0, -100%, 0);
|
||||||
|
transform: translate3d(0, -100%, 0);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
-webkit-transform: none;
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Simple CSS3 Fade-in Animation */
|
||||||
|
@-webkit-keyframes fadeIn { from { opacity:0; } to { opacity:1; } }
|
||||||
|
@-moz-keyframes fadeIn { from { opacity:0; } to { opacity:1; } }
|
||||||
|
@keyframes fadeIn { from { opacity:0; } to { opacity:1; } }
|
||||||
|
|
||||||
|
.fadeIn {
|
||||||
|
opacity:0;
|
||||||
|
-webkit-animation:fadeIn ease-in 1;
|
||||||
|
-moz-animation:fadeIn ease-in 1;
|
||||||
|
animation:fadeIn ease-in 1;
|
||||||
|
|
||||||
|
-webkit-animation-fill-mode:forwards;
|
||||||
|
-moz-animation-fill-mode:forwards;
|
||||||
|
animation-fill-mode:forwards;
|
||||||
|
|
||||||
|
-webkit-animation-duration:1s;
|
||||||
|
-moz-animation-duration:1s;
|
||||||
|
animation-duration:1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fadeIn.first {
|
||||||
|
-webkit-animation-delay: 0.4s;
|
||||||
|
-moz-animation-delay: 0.4s;
|
||||||
|
animation-delay: 0.4s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fadeIn.second {
|
||||||
|
-webkit-animation-delay: 0.6s;
|
||||||
|
-moz-animation-delay: 0.6s;
|
||||||
|
animation-delay: 0.6s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fadeIn.third {
|
||||||
|
-webkit-animation-delay: 0.8s;
|
||||||
|
-moz-animation-delay: 0.8s;
|
||||||
|
animation-delay: 0.8s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fadeIn.fourth {
|
||||||
|
-webkit-animation-delay: 1s;
|
||||||
|
-moz-animation-delay: 1s;
|
||||||
|
animation-delay: 1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Simple CSS3 Fade-in Animation */
|
||||||
|
.underlineHover:after {
|
||||||
|
display: block;
|
||||||
|
left: 0;
|
||||||
|
bottom: -10px;
|
||||||
|
width: 0;
|
||||||
|
height: 2px;
|
||||||
|
background-color: #56baed;
|
||||||
|
content: "";
|
||||||
|
transition: width 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.underlineHover:hover {
|
||||||
|
color: #0d0d0d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.underlineHover:hover:after{
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* OTHERS */
|
||||||
|
|
||||||
|
*:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#icon {
|
||||||
|
width:60%;
|
||||||
|
}
|
||||||
|
|
||||||
31
src/app/components/login/login.component.html
Normal file
31
src/app/components/login/login.component.html
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
<div class="wrapper fadeInDown">
|
||||||
|
<div id="formContent">
|
||||||
|
<!-- Tabs Titles -->
|
||||||
|
|
||||||
|
<!-- Icon -->
|
||||||
|
<div class="fadeIn first">
|
||||||
|
<h4>U10 Manager</h4>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Login Form -->
|
||||||
|
<div class="container" *ngIf="loginFG">
|
||||||
|
<form [formGroup]="loginFG">
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="text" formControlName="login" class="form-control fadeIn second" name="login" placeholder="Identifiant" [ngClass]="{'is-invalid':submitted && loginFG.controls['login'].errors}">
|
||||||
|
<div *ngIf="submitted && loginFG.controls['login'].errors" class="invalid-feedback">
|
||||||
|
<div *ngIf="loginFG.controls['login'].errors['required']">L'identifiant est requis !</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="password" formControlName="password" class="form-control fadeIn third" name="password" placeholder="Mot de passe" [ngClass]="{'is-invalid':submitted && loginFG.controls['password'].errors}">
|
||||||
|
<div *ngIf="submitted && loginFG.controls['password'].errors" class="invalid-feedback">
|
||||||
|
<div *ngIf="loginFG.controls['password'].errors['required']">Le mot de passe est requis !</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<input (click)="onLogin()" type="submit" class="fadeIn fourth" value="Log In">
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
25
src/app/components/login/login.component.spec.ts
Normal file
25
src/app/components/login/login.component.spec.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { LoginComponent } from './login.component';
|
||||||
|
|
||||||
|
describe('LoginComponent', () => {
|
||||||
|
let component: LoginComponent;
|
||||||
|
let fixture: ComponentFixture<LoginComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ LoginComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(LoginComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
42
src/app/components/login/login.component.ts
Normal file
42
src/app/components/login/login.component.ts
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { BackendService } from '../../services/backend.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-login',
|
||||||
|
templateUrl: './login.component.html',
|
||||||
|
styleUrls: ['./login.component.css']
|
||||||
|
})
|
||||||
|
export class LoginComponent implements OnInit {
|
||||||
|
loginFG?:FormGroup;
|
||||||
|
submitted:boolean=false;
|
||||||
|
|
||||||
|
constructor(private fb:FormBuilder, private bs:BackendService, private router:Router) { }
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.loginFG=this.fb.group({
|
||||||
|
login:["", Validators.required],
|
||||||
|
password:["", Validators.required],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onLogin(): void {
|
||||||
|
this.submitted=true;
|
||||||
|
if(this.loginFG?.invalid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// DEBUG
|
||||||
|
console.log(this.loginFG?.value);
|
||||||
|
// END DEBUG
|
||||||
|
this.bs.loginUser(this.loginFG?.value).subscribe(
|
||||||
|
data=>{
|
||||||
|
// DEBUG
|
||||||
|
console.log(data);
|
||||||
|
// END DEBUG
|
||||||
|
this.router.navigateByUrl("/");
|
||||||
|
}, error=>{
|
||||||
|
alert("Credentials failed, please try again !");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
153
src/app/components/navbar/navbar.component.css
Normal file
153
src/app/components/navbar/navbar.component.css
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
mat-toolbar {
|
||||||
|
background: #004a9f;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
mat-sidenav {
|
||||||
|
margin: 16px;
|
||||||
|
width: 250px;
|
||||||
|
border-right: none;
|
||||||
|
background: #002b5c;
|
||||||
|
color: white;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 16px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
/* height: calc(100vh - 130px); */
|
||||||
|
height: calc(100vh - 40px);
|
||||||
|
border-radius: 10px;
|
||||||
|
margin: 16px;
|
||||||
|
/* margin-left: 32px; */
|
||||||
|
margin-left: 32px;
|
||||||
|
|
||||||
|
padding: 16px;
|
||||||
|
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
mat-sidenav-container {
|
||||||
|
height: calc(100vh - 5px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-button {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
font-size: 1rem;
|
||||||
|
|
||||||
|
mat-icon {
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
margin-top: 16px;
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.name {
|
||||||
|
margin-top: 8px;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.designation {
|
||||||
|
margin-top: 2px;
|
||||||
|
font-size: 0.7rem;
|
||||||
|
color: lightgrey;
|
||||||
|
}
|
||||||
|
|
||||||
|
mat-divider {
|
||||||
|
margin-top: 16px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
background-color: rgba(255, 255, 255, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
mat-icon {
|
||||||
|
margin: 5px 5px 5px 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* ==========================================
|
||||||
|
* CUSTOM UTIL CLASSES
|
||||||
|
* ==========================================
|
||||||
|
*
|
||||||
|
* .vertical-nav {
|
||||||
|
* min-width: 17rem;
|
||||||
|
* width: 17rem;
|
||||||
|
* height: 100vh;
|
||||||
|
* position: fixed;
|
||||||
|
* top: 0;
|
||||||
|
* left: 0;
|
||||||
|
* box-shadow: 3px 3px 10px rgba(0, 0, 0, 0.1);
|
||||||
|
* transition: all 0.4s;
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* .page-content {
|
||||||
|
* width: calc(100% - 17rem);
|
||||||
|
* margin-left: 17rem;
|
||||||
|
* transition: all 0.4s;
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* // for toggle behavior
|
||||||
|
*
|
||||||
|
* #sidebar.active {
|
||||||
|
* margin-left: -17rem;
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* #content.active {
|
||||||
|
* width: 100%;
|
||||||
|
* margin: 0;
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* @media (max-width: 768px) {
|
||||||
|
* #sidebar {
|
||||||
|
* margin-left: -17rem;
|
||||||
|
* }
|
||||||
|
* #sidebar.active {
|
||||||
|
* margin-left: 0;
|
||||||
|
* }
|
||||||
|
* #content {
|
||||||
|
* width: 100%;
|
||||||
|
* margin: 0;
|
||||||
|
* }
|
||||||
|
* #content.active {
|
||||||
|
* margin-left: 17rem;
|
||||||
|
* width: calc(100% - 17rem);
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* /*
|
||||||
|
* *
|
||||||
|
* * ==========================================
|
||||||
|
* * FOR DEMO PURPOSE
|
||||||
|
* * ==========================================
|
||||||
|
* *
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* body {
|
||||||
|
* background: #599fd9;
|
||||||
|
* background: -webkit-linear-gradient(to right, #599fd9, #c2e59c);
|
||||||
|
* background: linear-gradient(to right, #599fd9, #c2e59c);
|
||||||
|
* min-height: 100vh;
|
||||||
|
* overflow-x: hidden;
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* .separator {
|
||||||
|
* margin: 3rem 0;
|
||||||
|
* border-bottom: 1px dashed #fff;
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* .text-uppercase {
|
||||||
|
* letter-spacing: 0.1em;
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* .text-gray {
|
||||||
|
* color: #aaa;
|
||||||
|
* }
|
||||||
|
*/
|
||||||
59
src/app/components/navbar/navbar.component.html
Normal file
59
src/app/components/navbar/navbar.component.html
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
<mat-sidenav-container>
|
||||||
|
<mat-sidenav mode="side" class="mat-elevation-z8" opened>
|
||||||
|
<img
|
||||||
|
class="avatar mat-elevation-z8"
|
||||||
|
[src]="thumbnail"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ng-container *ngIf="(userInput$ | async) as profile">
|
||||||
|
<h4 class="name">{{profile.Prenom}} {{profile.Nom}}</h4>
|
||||||
|
<p class="designation">{{profile.Role}}</p>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<mat-divider></mat-divider>
|
||||||
|
|
||||||
|
<button mat-button (click)="myAccount()" class="menu-button">
|
||||||
|
<mat-icon>face</mat-icon>
|
||||||
|
<span>Mon Compte</span>
|
||||||
|
</button>
|
||||||
|
<button mat-button (click)="logout()" class="menu-button">
|
||||||
|
<mat-icon>eject</mat-icon>
|
||||||
|
<span>Déconnexion</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div *ngIf="isAdmin">
|
||||||
|
<mat-divider></mat-divider>
|
||||||
|
<button mat-button (click)="modifUsers()" class="menu-button">
|
||||||
|
<mat-icon>supervisor_account</mat-icon>
|
||||||
|
<span>Utilisateurs</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<mat-divider></mat-divider>
|
||||||
|
|
||||||
|
<button mat-button class="menu-button" routerLink="/home">
|
||||||
|
<mat-icon>home</mat-icon>
|
||||||
|
<span>Home</span>
|
||||||
|
</button>
|
||||||
|
<button mat-button (click)="messages()" class="menu-button">
|
||||||
|
<mat-icon>message</mat-icon>
|
||||||
|
<span>Messages</span>
|
||||||
|
</button>
|
||||||
|
<button mat-button class="menu-button" routerLink="/about">
|
||||||
|
<mat-icon>info</mat-icon>
|
||||||
|
<span>About</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<mat-divider></mat-divider>
|
||||||
|
|
||||||
|
<button mat-button class="menu-button" routerLink="/help">
|
||||||
|
<mat-icon>help</mat-icon>
|
||||||
|
<span>Help</span>
|
||||||
|
</button>
|
||||||
|
</mat-sidenav>
|
||||||
|
<mat-sidenav-content>
|
||||||
|
<div class="content mat-elevation-z8">
|
||||||
|
<app-workspace></app-workspace>
|
||||||
|
</div>
|
||||||
|
</mat-sidenav-content>
|
||||||
|
</mat-sidenav-container>
|
||||||
25
src/app/components/navbar/navbar.component.spec.ts
Normal file
25
src/app/components/navbar/navbar.component.spec.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { NavbarComponent } from './navbar.component';
|
||||||
|
|
||||||
|
describe('NavbarComponent', () => {
|
||||||
|
let component: NavbarComponent;
|
||||||
|
let fixture: ComponentFixture<NavbarComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ NavbarComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(NavbarComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
89
src/app/components/navbar/navbar.component.ts
Normal file
89
src/app/components/navbar/navbar.component.ts
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
import { Component, OnInit, Input, Output, ViewChild } from '@angular/core';
|
||||||
|
import { Utilisateur } from '../../model/utilisateur.model';
|
||||||
|
import { BackendService } from '../../services/backend.service';
|
||||||
|
import { ActionsTypes, ActionEvt } from '../../state/user.state';
|
||||||
|
import { EventDriverService } from '../../services/event.driver.service';
|
||||||
|
import { Observable, Subscription } from 'rxjs';
|
||||||
|
import { catchError, map, startWith } from 'rxjs/operators';
|
||||||
|
import { Router, NavigationEnd } from '@angular/router';
|
||||||
|
import { BreakpointObserver } from '@angular/cdk/layout';
|
||||||
|
import { MatSidenav } from '@angular/material/sidenav';
|
||||||
|
import { delay, filter } from 'rxjs/operators';
|
||||||
|
import { ToastrService } from 'ngx-toastr';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-navbar',
|
||||||
|
templateUrl: './navbar.component.html',
|
||||||
|
styleUrls: ['./navbar.component.css']
|
||||||
|
})
|
||||||
|
export class NavbarComponent implements OnInit {
|
||||||
|
@Input() userInput$:Observable<Utilisateur>|null=null;
|
||||||
|
userId:number;
|
||||||
|
thumbnail:any;
|
||||||
|
isAdmin:boolean = false;
|
||||||
|
@ViewChild(MatSidenav)
|
||||||
|
sidenav!: MatSidenav;
|
||||||
|
|
||||||
|
constructor( private bs:BackendService,
|
||||||
|
private router:Router,
|
||||||
|
private evtDrv:EventDriverService,
|
||||||
|
private observer: BreakpointObserver,
|
||||||
|
private toast:ToastrService ) {}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.userInput$?.subscribe(
|
||||||
|
(observer:Utilisateur) => {
|
||||||
|
this.userId = observer['userId'];
|
||||||
|
if(observer['Role'] == "Administrateur") {
|
||||||
|
this.isAdmin = true;
|
||||||
|
}
|
||||||
|
console.log("UserID : " + this.userId);
|
||||||
|
if(this.userId != 0) {
|
||||||
|
this.bs.getPhotoUser(this.userId).subscribe((data:any) => {
|
||||||
|
this.createImageFromBlob(data);
|
||||||
|
}, error => {
|
||||||
|
console.log("Erreur !!");
|
||||||
|
this.toast.error("Erreur de récupération des infos de l'utilisateur");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, error => {
|
||||||
|
this.toast.error("Une erreur s'est produite ...");
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
createImageFromBlob(image: Blob) {
|
||||||
|
let reader = new FileReader();
|
||||||
|
reader.addEventListener("load", () => {
|
||||||
|
this.thumbnail = reader.result;
|
||||||
|
}, false);
|
||||||
|
if(image) {
|
||||||
|
reader.readAsDataURL(image);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logout(): void {
|
||||||
|
this.bs.logoutUser().subscribe(
|
||||||
|
(data:any) => {
|
||||||
|
console.log(data);
|
||||||
|
this.router.navigateByUrl("/login");
|
||||||
|
}, error => {
|
||||||
|
console.log("Erreur de deconnexion de l'utilisateur");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
myAccount(): void {
|
||||||
|
console.log("Modif user account");
|
||||||
|
this.evtDrv.publishEvent({type:ActionsTypes.EDIT_USER, payload:this.userId});
|
||||||
|
}
|
||||||
|
|
||||||
|
modifUsers(): void {
|
||||||
|
console.log("Modify users");
|
||||||
|
this.evtDrv.publishEvent({type:ActionsTypes.MODIF_USERS, payload:this.isAdmin});
|
||||||
|
}
|
||||||
|
|
||||||
|
messages(): void {
|
||||||
|
console.log("Messages");
|
||||||
|
this.evtDrv.publishEvent({type:ActionsTypes.SEND_MESSAGES, payload:this.isAdmin});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
.imessage {
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin: 0 auto 1rem;
|
||||||
|
padding: 0.5rem 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
position: relative;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-style: italic;
|
||||||
|
display: block;
|
||||||
|
margin: 1rem auto 0.25rem;
|
||||||
|
color: #002b5c
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
border-radius: 1.15rem;
|
||||||
|
line-height: 1.25;
|
||||||
|
max-width: 100%;
|
||||||
|
padding: 0.5rem .875rem;
|
||||||
|
position: relative;
|
||||||
|
word-wrap: break-word;
|
||||||
|
margin: 0.5rem 0;
|
||||||
|
width: fit-content;
|
||||||
|
}
|
||||||
|
|
||||||
|
.imessage p::before,
|
||||||
|
.imessage p::after {
|
||||||
|
bottom: -0.1rem;
|
||||||
|
content: "";
|
||||||
|
height: 1rem;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
p.from-me {
|
||||||
|
align-self: end;
|
||||||
|
background-color: #002b5c;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
p.from-me::before {
|
||||||
|
border-bottom-left-radius: 0.8rem 0.7rem;
|
||||||
|
border-right: 1rem solid #002b5c;
|
||||||
|
right: -0.35rem;
|
||||||
|
transform: translate(0, -0.1rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
p.from-me::after {
|
||||||
|
background-color: #fff;
|
||||||
|
border-bottom-left-radius: 0.5rem;
|
||||||
|
right: -40px;
|
||||||
|
transform:translate(-30px, -2px);
|
||||||
|
width: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p.from-them {
|
||||||
|
align-self: start;
|
||||||
|
background-color: #e5e5ea;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
p.from-them:before {
|
||||||
|
border-bottom-right-radius: 0.8rem 0.7rem;
|
||||||
|
border-left: 1rem solid #e5e5ea;
|
||||||
|
left: -0.35rem;
|
||||||
|
transform: translate(0, -0.1rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
p.from-them::after {
|
||||||
|
background-color: #fff;
|
||||||
|
border-bottom-right-radius: 0.5rem;
|
||||||
|
left: 20px;
|
||||||
|
transform: translate(-30px, -2px);
|
||||||
|
width: 10px;
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<div #imessage class="imessage">
|
||||||
|
<ng-container *ngIf="(messagesInput$ | async) as result">
|
||||||
|
<ng-container *ngIf="utilisateur">
|
||||||
|
<ng-container *ngFor="let msg of result">
|
||||||
|
<p *ngIf="msg.user?.userId === utilisateur.userId" class="from-me">
|
||||||
|
{{msg.Text}}
|
||||||
|
</p>
|
||||||
|
<p *ngIf="msg.user?.userId !== utilisateur.userId" class="from-them">
|
||||||
|
{{msg.Text}}
|
||||||
|
<span>{{msg.user?.Prenom}} {{msg.user?.Nom}}</span>
|
||||||
|
</p>
|
||||||
|
</ng-container>
|
||||||
|
</ng-container>
|
||||||
|
</ng-container>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { ChatboxComponent } from './chatbox.component';
|
||||||
|
|
||||||
|
describe('ChatboxComponent', () => {
|
||||||
|
let component: ChatboxComponent;
|
||||||
|
let fixture: ComponentFixture<ChatboxComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ ChatboxComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(ChatboxComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import { Component, OnInit, AfterViewInit, Input, ViewChild, ContentChild, ElementRef } from '@angular/core';
|
||||||
|
import { BackendService } from '../../../../services/backend.service';
|
||||||
|
import { EventDriverService } from '../../../../services/event.driver.service';
|
||||||
|
import { Message } from '../../../../model/message.model';
|
||||||
|
import { Utilisateur } from '../../../../model/utilisateur.model';
|
||||||
|
import { ToastrService } from 'ngx-toastr';
|
||||||
|
import { Observable, of } from 'rxjs';
|
||||||
|
import { catchError, map, startWith } from 'rxjs/operators';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-chatbox',
|
||||||
|
templateUrl: './chatbox.component.html',
|
||||||
|
styleUrls: ['./chatbox.component.css']
|
||||||
|
})
|
||||||
|
export class ChatboxComponent implements OnInit, AfterViewInit {
|
||||||
|
@Input() messagesInput$:Observable<Message[]> | null=null;
|
||||||
|
@ViewChild('imessage') myElmt: ElementRef;
|
||||||
|
utilisateur:Utilisateur;
|
||||||
|
|
||||||
|
constructor( private bs:BackendService,
|
||||||
|
private evtDrv:EventDriverService,
|
||||||
|
private toast:ToastrService ) { }
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.bs.getCurrentUser().subscribe(
|
||||||
|
(data:Utilisateur) => {
|
||||||
|
this.utilisateur = data;
|
||||||
|
}, error => {
|
||||||
|
this.toast.error('Une erreur s\'est produite');
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ngAfterViewInit(): void {
|
||||||
|
console.log("ngAfterViewInit()");
|
||||||
|
console.log(this.myElmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
ngAfterViewChecked(): void {
|
||||||
|
this.myElmt.nativeElement.scrollTop = this.myElmt.nativeElement.scrollHeight;
|
||||||
|
console.log(this.myElmt.nativeElement.scrollTop, this.myElmt.nativeElement.scrollHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
onDelete(msg:Message): void {
|
||||||
|
console.log("Delete message : ", msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
23
src/app/components/workspace/messages/messages.component.css
Normal file
23
src/app/components/workspace/messages/messages.component.css
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
.container {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
display: grid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.msg-field {
|
||||||
|
overflow-x: hidden;
|
||||||
|
overflow-y: auto;
|
||||||
|
/*height: 500px;*/
|
||||||
|
}
|
||||||
|
|
||||||
|
.msg-box {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-form-field {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*::-webkit-scrollbar { display: none; }*/
|
||||||
|
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<div class="container" fxLayout="column">
|
||||||
|
<div class="msg-field" fxFlex="85%">
|
||||||
|
<app-chatbox [messagesInput$]="messages$"></app-chatbox>
|
||||||
|
</div>
|
||||||
|
<div class="msg-box" fxFlex="15%">
|
||||||
|
<mat-form-field appearance="standard">
|
||||||
|
<mat-label>Message</mat-label>
|
||||||
|
<textarea style="resize: none;" matInput placeholder="Entrer votre message à destination des autres utilisateurs ..." [(ngModel)]="msgText" rows="4" fxFlex="97%"></textarea>
|
||||||
|
<button mat-icon-button mat-suffix (click)="onSend()">
|
||||||
|
<mat-icon>send</mat-icon>
|
||||||
|
</button>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { MessagesComponent } from './messages.component';
|
||||||
|
|
||||||
|
describe('MessagesComponent', () => {
|
||||||
|
let component: MessagesComponent;
|
||||||
|
let fixture: ComponentFixture<MessagesComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ MessagesComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(MessagesComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
48
src/app/components/workspace/messages/messages.component.ts
Normal file
48
src/app/components/workspace/messages/messages.component.ts
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { formatDate } from '@angular/common';
|
||||||
|
import { BackendService } from '../../../services/backend.service';
|
||||||
|
import { EventDriverService } from '../../../services/event.driver.service';
|
||||||
|
import { Message } from '../../../model/message.model';
|
||||||
|
import { Utilisateur } from '../../../model/utilisateur.model';
|
||||||
|
import { ToastrService } from 'ngx-toastr';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { catchError, map, startWith } from 'rxjs/operators';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-messages',
|
||||||
|
templateUrl: './messages.component.html',
|
||||||
|
styleUrls: ['./messages.component.css']
|
||||||
|
})
|
||||||
|
export class MessagesComponent implements OnInit {
|
||||||
|
msgText:string = "";
|
||||||
|
utilisateur$:Observable<Utilisateur>|null=null;
|
||||||
|
messages$:Observable<Message[]>|null=null;
|
||||||
|
|
||||||
|
constructor( private bs:BackendService,
|
||||||
|
private evtDrv:EventDriverService,
|
||||||
|
private toast:ToastrService ) { }
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
onSend():void {
|
||||||
|
let msg:Message = {Text: this.msgText, Date: formatDate(new Date(), 'yyyy-MM-dd hh:mm:ss', 'en-US')};
|
||||||
|
this.bs.sendMessage(msg).subscribe(
|
||||||
|
data => {
|
||||||
|
this.msgText = "";
|
||||||
|
this.update();
|
||||||
|
}, error => {
|
||||||
|
this.toast.error("Erreur lors de l'envoi du message");
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
update(): void {
|
||||||
|
this.messages$ = this.bs.getAllMessages().pipe(
|
||||||
|
map(datas => {
|
||||||
|
return datas;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
.container {
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
mat-form-field {
|
||||||
|
margin-left: 1.5rem;
|
||||||
|
width: calc(100% - 1.5rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
padding: 5px 50px 5px 50px;
|
||||||
|
margin: 16px 8px 16px 8px;
|
||||||
|
font-size: 18px;
|
||||||
|
text-align: center;
|
||||||
|
text-decoration: none;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
mat-card {
|
||||||
|
width: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
padding: 5px 5px 5px 5px;
|
||||||
|
margin: 10px 10px 10px 10px;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
padding: 5px 5px 5px 5px;
|
||||||
|
margin: 10px 10px 10px 10px;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 40px;
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
<div *ngIf="userFG; else err">
|
||||||
|
<div class="container" fxLayout="row" fxLayoutGap="0px">
|
||||||
|
<mat-card class="mat-elevation-z4">
|
||||||
|
<img mat-card-image [src]="thumbnail" class="avatar">
|
||||||
|
<mat-card-actions fxLayout="row" fxLayoutAlign="center">
|
||||||
|
<input type="file" accept="image/*" class="file-input" (change)="onImageSelected($event)" #fileUpload>
|
||||||
|
</mat-card-actions>
|
||||||
|
</mat-card>
|
||||||
|
<form [formGroup]="userFG">
|
||||||
|
<mat-form-field>
|
||||||
|
<input matInput type="text" formControlName="prenom" class="form-control" name="Prenom" placeholder="Prénom" [ngClass]="{'is-invalid':submitted && userFG.controls['prenom'].errors}" (change)="onInputChange($event)">
|
||||||
|
<mat-error *ngIf="userFG.controls['prenom'].hasError('required')">Le prénom est requis !</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field>
|
||||||
|
<input matInput type="text" formControlName="nom" class="form-control" name="Nom" placeholder="Nom" [ngClass]="{'is-invalid':submitted && userFG.controls['nom'].errors}" (change)="onInputChange($event)">
|
||||||
|
<mat-error *ngIf="userFG.controls['nom'].hasError('required')">Le nom est requis !</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field>
|
||||||
|
<input matInput type="text" formControlName="identifiant" class="form-control" name="Identifiant" placeholder="Identifiant" [ngClass]="{'is-invalid':submitted && userFG.controls['identifiant'].errors}" (change)="onInputChange($event)">
|
||||||
|
<mat-error *ngIf="userFG.controls['identifiant'].hasError('required')">L'identifiant est requis !</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field>
|
||||||
|
<input matInput type="password" formControlName="password" class="form-control" name="Password" placeholder="Mot de passe" [ngClass]="{'is-invalid':submitted && userFG.controls['password'].errors}" (change)="onInputChange($event)">
|
||||||
|
<mat-error *ngIf="userFG.controls['password'].hasError('required')">Le mot de passe est requis !</mat-error>
|
||||||
|
<mat-error *ngIf="userFG.controls['password'].hasError('pattern')">Le mot de passe doit avoir minimum {{minPw}} caractères et maximum {{maxPw}} avec 1 caratère minuscule, 1 majuscule et 1 chiffre</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field>
|
||||||
|
<input matInput type="password" formControlName="confirm" class="form-control" name="Confirm" placeholder="Confirmer Mot de passe" [ngClass]="{'is-invalid':submitted && userFG.controls['confirm'].errors}" (change)="onInputChange($event)">
|
||||||
|
<mat-error *ngIf="userFG.controls['confirm'].hasError('required')">La confirmation du mot de passe est requis !</mat-error>
|
||||||
|
<mat-error *ngIf="userFG.controls['confirm'].hasError('pattern')">Le mot de passe doit avoir minimum {{minPw}} caractères et maximum {{maxPw}} avec 1 caratère minuscule, 1 majuscule et 1 chiffre</mat-error>
|
||||||
|
<mat-error *ngIf="userFG.controls['confirm'].hasError('mismatch')">La confirmation doit correspondre au mot de passe</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="container" fxLayout="row" fxLayoutAlign="center">
|
||||||
|
<button mat-raised-button color="primary" [disabled]="!userFG?.valid" (click)="onModify()">Modifier</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ng-template #err>
|
||||||
|
<div class="container" *ngIf="errFlag" fxLayout="column" fxLayoutAlign="center">
|
||||||
|
<h1>403</h1>
|
||||||
|
<h4>INTERDIT</h4>
|
||||||
|
<p>Vous n'êtes pas autorisé à venir ici !!<p>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { UserEditComponent } from './user-edit.component';
|
||||||
|
|
||||||
|
describe('UserEditComponent', () => {
|
||||||
|
let component: UserEditComponent;
|
||||||
|
let fixture: ComponentFixture<UserEditComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ UserEditComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(UserEditComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
144
src/app/components/workspace/user-edit/user-edit.component.ts
Normal file
144
src/app/components/workspace/user-edit/user-edit.component.ts
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
import { Component, OnInit, EventEmitter } from '@angular/core';
|
||||||
|
import { FormBuilder, FormGroup, Validators, ValidatorFn, ValidationErrors, AbstractControl } from '@angular/forms';
|
||||||
|
import { Router, ActivatedRoute } from '@angular/router';
|
||||||
|
import { BackendService } from '../../../services/backend.service';
|
||||||
|
import { Utilisateur } from '../../../model/utilisateur.model';
|
||||||
|
import { EventDriverService } from '../../../services/event.driver.service';
|
||||||
|
import { ActionsTypes, ActionEvt } from '../../../state/user.state';
|
||||||
|
import { ToastrService } from 'ngx-toastr';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-user-edit',
|
||||||
|
templateUrl: './user-edit.component.html',
|
||||||
|
styleUrls: ['./user-edit.component.css']
|
||||||
|
})
|
||||||
|
export class UserEditComponent implements OnInit {
|
||||||
|
userFG?:FormGroup;
|
||||||
|
submitted:boolean=false;
|
||||||
|
userId:number;
|
||||||
|
minPw:number = 8;
|
||||||
|
maxPw:number = 24;
|
||||||
|
thumbnail:any;
|
||||||
|
photo:File;
|
||||||
|
user:Utilisateur;
|
||||||
|
errFlag:boolean = false;
|
||||||
|
|
||||||
|
constructor(private fb:FormBuilder,
|
||||||
|
private bs:BackendService,
|
||||||
|
private router:Router,
|
||||||
|
private evtDrv:EventDriverService,
|
||||||
|
private ar:ActivatedRoute,
|
||||||
|
private toast:ToastrService) {
|
||||||
|
this.userId=this.ar.snapshot.params['id'];
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.bs.getUser(this.userId).subscribe(
|
||||||
|
(data:Utilisateur) => {
|
||||||
|
this.user = data;
|
||||||
|
this.userFG=this.fb.group({
|
||||||
|
nom:[this.user.Nom, Validators.required],
|
||||||
|
prenom:[this.user.Prenom, Validators.required],
|
||||||
|
identifiant:[this.user.Identifiant, Validators.required],
|
||||||
|
password:["", [
|
||||||
|
Validators.required,
|
||||||
|
Validators.pattern('(?=\\D*\\d)(?=[^a-z])(?=[^A-Z]*[A-Z]).{' + this.minPw + ',' + this.maxPw + '}')
|
||||||
|
]],
|
||||||
|
confirm:["", [
|
||||||
|
Validators.required,
|
||||||
|
Validators.pattern('(?=\\D*\\d)(?=[^a-z])(?=[^A-Z]*[A-Z]).{' + this.minPw + ',' + this.maxPw + '}'),
|
||||||
|
passwordMatchValidator
|
||||||
|
]],
|
||||||
|
});
|
||||||
|
this.bs.getPhotoUser(this.userId).subscribe(
|
||||||
|
(data:any) => {
|
||||||
|
this.createImageURL(data);
|
||||||
|
}, error => {
|
||||||
|
this.toast.error('Erreur de récupération de la photo');
|
||||||
|
});
|
||||||
|
}, error => {
|
||||||
|
if(error.status == 403) {
|
||||||
|
this.errFlag = true;
|
||||||
|
} else {
|
||||||
|
this.toast.error('Erreur de récupération des informations');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
createImageURL(image:any): void {
|
||||||
|
let reader = new FileReader();
|
||||||
|
reader.addEventListener("load", () => {
|
||||||
|
this.thumbnail = reader.result;
|
||||||
|
}, false);
|
||||||
|
if(image) {
|
||||||
|
reader.readAsDataURL(image);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onImageSelected(event:any): void {
|
||||||
|
if(event.target.files[0]) {
|
||||||
|
this.photo = event.target.files[0];
|
||||||
|
this.createImageURL(this.photo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onInputChange(event:any): void {
|
||||||
|
switch(event.target.name) {
|
||||||
|
case 'Prenom':
|
||||||
|
this.user.Prenom = event.target.value;
|
||||||
|
break;
|
||||||
|
case 'Nom':
|
||||||
|
this.user.Nom = event.target.value;
|
||||||
|
break;
|
||||||
|
case 'Identifiant':
|
||||||
|
this.user.Identifiant = event.target.value;
|
||||||
|
break;
|
||||||
|
case 'Confirm':
|
||||||
|
if(this.userFG?.get('password')?.value === this.userFG?.get('confirm')?.value) {
|
||||||
|
this.user.Password = event.target.value;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
console.log("En cours de changement ...", this.user);
|
||||||
|
}
|
||||||
|
|
||||||
|
onModify(): void {
|
||||||
|
this.submitted=true;
|
||||||
|
if(this.userFG?.invalid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// DEBUG
|
||||||
|
console.log("Utilisateur modifié : ", this.userFG?.value);
|
||||||
|
// END DEBUG
|
||||||
|
const formData = new FormData();
|
||||||
|
this.bs.updateUser(this.userId, this.user).subscribe(
|
||||||
|
data => {
|
||||||
|
console.log("response : ", data);
|
||||||
|
// TODO: Envoyer nouvelle photo si elle existe
|
||||||
|
// TODO: Rafraichir les infos de la navbar
|
||||||
|
this.evtDrv.publishEvent({type:ActionsTypes.UPDATE_USER, payload:this.user});
|
||||||
|
if(this.photo) {
|
||||||
|
formData.append("photo", this.photo, this.photo.name);
|
||||||
|
this.bs.updateUserPhoto(this.userId, formData).subscribe(
|
||||||
|
data => {
|
||||||
|
this.toast.success('Mise à jour réussie');
|
||||||
|
}, error => {
|
||||||
|
this.toast.error('Erreur de mise à jour de la photo');
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.toast.success('Mise à jour réussie');
|
||||||
|
}
|
||||||
|
}, error => {
|
||||||
|
this.toast.error('Erreur de mise à jour des informations');
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const passwordMatchValidator: ValidatorFn = (fg: AbstractControl): ValidationErrors | null => {
|
||||||
|
let pt = fg.parent as FormGroup;
|
||||||
|
if(!pt) return null;
|
||||||
|
return pt.get('password')?.value === pt.get('confirm')?.value ? null : { 'mismatch' : true };
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
<tr class="mat-row" *ngIf="user">
|
||||||
|
<td class="mat-cell">{{user.Nom}}</td>
|
||||||
|
<td class="mat-cell">{{user.Prenom}}</td>
|
||||||
|
<td class="mat-cell">{{user.Identifiant}}</td>
|
||||||
|
<td class="mat-cell">{{user.Role}}</td>
|
||||||
|
<td class="mat-cell">
|
||||||
|
<mat-icon *ngIf="user.Actif">check_circle</mat-icon>
|
||||||
|
<mat-icon *ngIf="!user.Actif">remove_circle</mat-icon>
|
||||||
|
</td>
|
||||||
|
<td class="mat-cell" *ngIf="!isCur">
|
||||||
|
<button mat-raised-button color="primary" (click)="onResetPasswd()">Reset Mot de passe</button>
|
||||||
|
</td>
|
||||||
|
<td class="mat-cell" *ngIf="!isCur">
|
||||||
|
<button mat-raised-button color="primary" (click)="onActivate(user)">
|
||||||
|
<span *ngIf="user.Actif">Désactiver</span>
|
||||||
|
<span *ngIf="!user.Actif">Activer</span>
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
<td class="mat-cell" *ngIf="!isCur">
|
||||||
|
<button mat-raised-button color="primary" (click)="onDelete()">Supprimer</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { UserItemComponent } from './user-item.component';
|
||||||
|
|
||||||
|
describe('UserItemComponent', () => {
|
||||||
|
let component: UserItemComponent;
|
||||||
|
let fixture: ComponentFixture<UserItemComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ UserItemComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(UserItemComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
import { Component, OnInit, Input } from '@angular/core';
|
||||||
|
import { BackendService } from '../../../../services/backend.service';
|
||||||
|
import { Utilisateur } from '../../../../model/utilisateur.model';
|
||||||
|
import { EventDriverService } from '../../../../services/event.driver.service';
|
||||||
|
import { ActionsTypes, ActionEvt } from '../../../../state/user.state';
|
||||||
|
import { ConfirmDialogComponent } from '../../../dialog/confirm-dialog/confirm-dialog.component';
|
||||||
|
import { ToastrService } from 'ngx-toastr';
|
||||||
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-user-item',
|
||||||
|
templateUrl: './user-item.component.html',
|
||||||
|
styleUrls: ['./user-item.component.css']
|
||||||
|
})
|
||||||
|
export class UserItemComponent implements OnInit {
|
||||||
|
@Input() user:Utilisateur;
|
||||||
|
@Input() curUser:Utilisateur;
|
||||||
|
isCur:boolean = false;
|
||||||
|
isActif:boolean = false;
|
||||||
|
|
||||||
|
constructor( private evtDrv: EventDriverService,
|
||||||
|
private bs: BackendService,
|
||||||
|
public dialog: MatDialog,
|
||||||
|
private toast:ToastrService) { }
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
if(this.curUser.userId == this.user.userId) {
|
||||||
|
this.isCur = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isActif = this.user.Actif;
|
||||||
|
}
|
||||||
|
|
||||||
|
onResetPasswd(): void {
|
||||||
|
const myDialog = this.dialog.open(ConfirmDialogComponent, {
|
||||||
|
disableClose: true,
|
||||||
|
data: {
|
||||||
|
title: 'Ré-intilisation du mot de passe',
|
||||||
|
text: 'Confirmer la ré-initialisation du mot de passe pour l\'utilisateur ' + this.user.Prenom + ' ' + this.user.Nom,
|
||||||
|
labelOK: 'Confirmer',
|
||||||
|
labelNOK: 'Annuler'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
myDialog.afterClosed().subscribe(
|
||||||
|
data => {
|
||||||
|
if(data) {
|
||||||
|
this.bs.resetPasswordUser(this.user.userId).subscribe(
|
||||||
|
(data:any) => {
|
||||||
|
this.toast.success("Ré-initialisation du mot de passe réussi");
|
||||||
|
}, error => {
|
||||||
|
this.toast.error("Erreur de ré-initialisation du mot de passe");
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}, error => {
|
||||||
|
this.toast.error("Une erreur est survenue ...");
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
onActivate(user:Utilisateur): void {
|
||||||
|
var title:string;
|
||||||
|
let text:string;
|
||||||
|
if(user.Actif) {
|
||||||
|
title = 'Désactivation du compte';
|
||||||
|
text = 'Confirmer la désactivation de l\'utilisateur ' + user.Prenom + ' ' + user.Nom;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
title = 'Activation du compte';
|
||||||
|
text = 'Confirmer l\'activation de l\'utilisateur ' + user.Prenom + ' ' + user.Nom;
|
||||||
|
}
|
||||||
|
|
||||||
|
const myDialog = this.dialog.open(ConfirmDialogComponent, {
|
||||||
|
disableClose: true,
|
||||||
|
data: {
|
||||||
|
title: title,
|
||||||
|
text: text,
|
||||||
|
labelOK: 'Confirmer',
|
||||||
|
labelNOK: 'Annuler'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
myDialog.afterClosed().subscribe(
|
||||||
|
data => {
|
||||||
|
if(data) {
|
||||||
|
user.Actif = !user.Actif;
|
||||||
|
this.bs.activateUser(this.user.userId, user).subscribe(
|
||||||
|
(data:any) => {
|
||||||
|
if(user.Actif) {
|
||||||
|
this.toast.success("Activation de l'utilisateur réussi");
|
||||||
|
} else {
|
||||||
|
this.toast.success("Désactivation de l'utilisateur réussi");
|
||||||
|
}
|
||||||
|
}, error => {
|
||||||
|
this.toast.error("Erreu de désactivation de l'utilisateur");
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
onDelete(): void {
|
||||||
|
console.log("Delete User: " + this.user.userId);
|
||||||
|
let id = this.user?.userId;
|
||||||
|
const myDialog = this.dialog.open(ConfirmDialogComponent, {
|
||||||
|
disableClose: true,
|
||||||
|
data: {
|
||||||
|
title: 'Suppression du compte',
|
||||||
|
text: 'Confirmer la suppression de l\'utilisateur ' + this.user.Prenom + ' ' + this.user.Nom,
|
||||||
|
labelOK: 'Confirmer',
|
||||||
|
labelNOK: 'Annuler'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
myDialog.afterClosed().subscribe(
|
||||||
|
data => {
|
||||||
|
console.log("réponse: ", data);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
//this.bs.deleteUser(id).subscribe(
|
||||||
|
// data => {
|
||||||
|
|
||||||
|
// }, error => {
|
||||||
|
|
||||||
|
// });
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
.mat-column-Actif {
|
||||||
|
flex:0 0 10%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-column-Role {
|
||||||
|
flex:0 0 15%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-column-Nom {
|
||||||
|
flex:0 0 15%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-column-Prenom {
|
||||||
|
flex:0 0 15%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-column-Identifiant {
|
||||||
|
flex:0 0 15%;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
margin: 0.5rem;
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
<div [hidden]="!isAdmin">
|
||||||
|
<div class="mat-elevation-z4">
|
||||||
|
<mat-toolbar color="primary">
|
||||||
|
<button mat-icon-button matTooltip="Ajouter un utilisteur" (click)="onAddUser()">
|
||||||
|
<fa-icon [icon]="faUserPlus" class="fa-2x"></fa-icon>
|
||||||
|
</button>
|
||||||
|
</mat-toolbar>
|
||||||
|
<mat-table [dataSource]="dataSource" matSort>
|
||||||
|
<ng-container matColumnDef="Nom">
|
||||||
|
<mat-header-cell *matHeaderCellDef mat-sort-header>Nom</mat-header-cell>
|
||||||
|
<mat-cell *matCellDef="let element">{{element.Nom}}</mat-cell>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="Prenom">
|
||||||
|
<mat-header-cell *matHeaderCellDef mat-sort-header>Prenom</mat-header-cell>
|
||||||
|
<mat-cell *matCellDef="let element">{{element.Prenom}}</mat-cell>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="Identifiant">
|
||||||
|
<mat-header-cell *matHeaderCellDef mat-sort-header>Identifiant</mat-header-cell>
|
||||||
|
<mat-cell *matCellDef="let element">{{element.Identifiant}}</mat-cell>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="Role">
|
||||||
|
<mat-header-cell *matHeaderCellDef mat-sort-header>Role</mat-header-cell>
|
||||||
|
<mat-cell *matCellDef="let element">{{element.Role}}</mat-cell>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="Actif">
|
||||||
|
<mat-header-cell *matHeaderCellDef mat-sort-header>Actif</mat-header-cell>
|
||||||
|
<mat-cell *matCellDef="let element">
|
||||||
|
<mat-icon *ngIf="element.Actif">check_circle</mat-icon>
|
||||||
|
<mat-icon *ngIf="!element.Actif">remove_circle</mat-icon>
|
||||||
|
</mat-cell>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="Actions">
|
||||||
|
<mat-header-cell *matHeaderCellDef>Actions</mat-header-cell>
|
||||||
|
<mat-cell *matCellDef="let element">
|
||||||
|
<button mat-mini-fab color="primary" [disabled]="element.userId == curUser.userId" (click)="onActivate(element)">
|
||||||
|
<span *ngIf="element.Actif"><fa-icon [icon]="faLock"></fa-icon></span>
|
||||||
|
<span *ngIf="!element.Actif"><fa-icon [icon]='faLockOpen'></fa-icon></span>
|
||||||
|
</button>
|
||||||
|
<button mat-mini-fab color="primary" [disabled]="element.userId == curUser.userId" matTooltip="Supprimer l'utilisateur" (click)="onDelete(element)">
|
||||||
|
<fa-icon [icon]="faTrashCan"></fa-icon>
|
||||||
|
</button>
|
||||||
|
<button mat-mini-fab color="primary" [disabled]="element.userId == curUser.userId" (click)="onResetPasswd(element)">
|
||||||
|
<fa-icon matTooltip="Ré-initialisation du mot de passe" [icon]="faUserSlash"></fa-icon>
|
||||||
|
</button>
|
||||||
|
</mat-cell>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<mat-header-row *matHeaderRowDef="columnsToDisplay"></mat-header-row>
|
||||||
|
<mat-row *matRowDef="let myRowData; columns: columnsToDisplay"></mat-row>
|
||||||
|
</mat-table>
|
||||||
|
<mat-paginator [pageSizeOptions]="[5]"></mat-paginator>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { UsersListComponent } from './users-list.component';
|
||||||
|
|
||||||
|
describe('UsersListComponent', () => {
|
||||||
|
let component: UsersListComponent;
|
||||||
|
let fixture: ComponentFixture<UsersListComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ UsersListComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(UsersListComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
203
src/app/components/workspace/users-list/users-list.component.ts
Normal file
203
src/app/components/workspace/users-list/users-list.component.ts
Normal file
@@ -0,0 +1,203 @@
|
|||||||
|
import { Component, OnInit, ViewChild } from '@angular/core';
|
||||||
|
import { BackendService } from '../../../services/backend.service';
|
||||||
|
import { Utilisateur } from '../../../model/utilisateur.model';
|
||||||
|
import { EventDriverService } from '../../../services/event.driver.service';
|
||||||
|
import { ActionsTypes, ActionEvt } from '../../../state/user.state';
|
||||||
|
import { ConfirmDialogComponent } from '../../dialog/confirm-dialog/confirm-dialog.component';
|
||||||
|
import { AddUserDialogComponent } from '../../dialog/add-user-dialog/add-user-dialog.component';
|
||||||
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
|
import { ToastrService } from 'ngx-toastr';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { Observable, of, Subscription, Subject } from 'rxjs';
|
||||||
|
import { catchError, map, startWith } from 'rxjs/operators';
|
||||||
|
import { MatSort } from '@angular/material/sort';
|
||||||
|
import { MatPaginator } from '@angular/material/paginator';
|
||||||
|
import { MatTableDataSource } from '@angular/material/table';
|
||||||
|
import { faTrashCan, faUserSlash, faUserCheck, faLock, faLockOpen, faUserPlus } from '@fortawesome/free-solid-svg-icons';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-users-list',
|
||||||
|
templateUrl: './users-list.component.html',
|
||||||
|
styleUrls: ['./users-list.component.css']
|
||||||
|
})
|
||||||
|
export class UsersListComponent implements OnInit {
|
||||||
|
curUser:Utilisateur;
|
||||||
|
isAdmin:boolean = false;
|
||||||
|
dataSource: MatTableDataSource<Utilisateur> = new MatTableDataSource<Utilisateur>();
|
||||||
|
columnsToDisplay = ['Prenom', 'Nom', 'Identifiant', 'Role', 'Actif', 'Actions'];
|
||||||
|
@ViewChild(MatPaginator, { static: true }) paginator: MatPaginator;
|
||||||
|
@ViewChild(MatSort, { static: true }) sort: MatSort;
|
||||||
|
|
||||||
|
faTrashCan = faTrashCan;
|
||||||
|
faUserSlash = faUserSlash;
|
||||||
|
faUserCheck = faUserCheck;
|
||||||
|
faLock = faLock;
|
||||||
|
faLockOpen = faLockOpen;
|
||||||
|
faUserPlus = faUserPlus;
|
||||||
|
|
||||||
|
update:any = new Subject<any>();
|
||||||
|
updateObs$ = this.update.asObservable();
|
||||||
|
|
||||||
|
constructor( private bs:BackendService,
|
||||||
|
private router:Router,
|
||||||
|
private evtDrv:EventDriverService,
|
||||||
|
public dialog: MatDialog,
|
||||||
|
private toast:ToastrService) {}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.bs.getCurrentUser().subscribe(
|
||||||
|
(data:Utilisateur) => {
|
||||||
|
this.curUser = data;
|
||||||
|
if(this.curUser['Role'] == "Administrateur") {
|
||||||
|
this.isAdmin = true;
|
||||||
|
}
|
||||||
|
}, error => {
|
||||||
|
this.toast.error("Une erreur s'est produite");
|
||||||
|
});
|
||||||
|
this.updateObs$.subscribe(() => { this.refresh() });
|
||||||
|
this.bs.getAllUsers().subscribe(
|
||||||
|
(data:Utilisateur[]) => {
|
||||||
|
this.dataSource.data = data;
|
||||||
|
this.dataSource.paginator = this.paginator;
|
||||||
|
this.dataSource.sort = this.sort;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
setUpdate(): void {
|
||||||
|
this.update.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
refresh(): void {
|
||||||
|
this.bs.getAllUsers().subscribe(
|
||||||
|
(data:Utilisateur[]) => {
|
||||||
|
this.dataSource.data = data;
|
||||||
|
this.dataSource.paginator = this.paginator;
|
||||||
|
this.dataSource.sort = this.sort;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
onActivate(user:Utilisateur): void {
|
||||||
|
var title:string;
|
||||||
|
let text:string;
|
||||||
|
if(user.Actif) {
|
||||||
|
title = 'Désactivation du compte';
|
||||||
|
text = 'Confirmer la désactivation de l\'utilisateur ' + user.Prenom + ' ' + user.Nom;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
title = 'Activation du compte';
|
||||||
|
text = 'Confirmer l\'activation de l\'utilisateur ' + user.Prenom + ' ' + user.Nom;
|
||||||
|
}
|
||||||
|
|
||||||
|
const myDialog = this.dialog.open(ConfirmDialogComponent, {
|
||||||
|
disableClose: true,
|
||||||
|
data: {
|
||||||
|
title: title,
|
||||||
|
text: text,
|
||||||
|
labelOK: 'Confirmer',
|
||||||
|
labelNOK: 'Annuler'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
myDialog.afterClosed().subscribe(
|
||||||
|
data => {
|
||||||
|
if(data) {
|
||||||
|
user.Actif = !user.Actif;
|
||||||
|
this.bs.activateUser(user.userId, user).subscribe(
|
||||||
|
(data:any) => {
|
||||||
|
if(user.Actif) {
|
||||||
|
this.toast.success("Activation de l'utilisateur réussi");
|
||||||
|
} else {
|
||||||
|
this.toast.success("Désactivation de l'utilisateur réussi");
|
||||||
|
}
|
||||||
|
}, error => {
|
||||||
|
this.toast.error("Erreur de désactivation de l'utilisateur");
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
onDelete(user:Utilisateur): void {
|
||||||
|
const myDialog = this.dialog.open(ConfirmDialogComponent, {
|
||||||
|
disableClose: true,
|
||||||
|
data: {
|
||||||
|
title: 'Suppression de l\'utilisateur',
|
||||||
|
text: 'Confirmer la suppression de l\'utilisateur ' + user.Prenom + ' ' + user.Nom,
|
||||||
|
labelOK: 'Confirmer',
|
||||||
|
labelNOK: 'Annuler'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
myDialog.afterClosed().subscribe(
|
||||||
|
data => {
|
||||||
|
if(data) {
|
||||||
|
this.bs.deleteUser(user.userId).subscribe(
|
||||||
|
(data:any) => {
|
||||||
|
this.toast.success("Suppression de l'utilisateur réussi");
|
||||||
|
this.setUpdate();
|
||||||
|
}, error => {
|
||||||
|
this.toast.error("Erreur de suppression de l'utilisateur");
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
onResetPasswd(user:Utilisateur): void {
|
||||||
|
const myDialog = this.dialog.open(ConfirmDialogComponent, {
|
||||||
|
disableClose: true,
|
||||||
|
data: {
|
||||||
|
title: 'Ré-initialisation du mot de passe',
|
||||||
|
text: 'Confirmer la ré-initialisation du mot de passe pour l\'utilisateur ' + user.Prenom + ' ' + user.Nom,
|
||||||
|
labelOK: 'Confirmer',
|
||||||
|
labelNOK: 'Annuler'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
myDialog.afterClosed().subscribe(
|
||||||
|
data => {
|
||||||
|
if(data) {
|
||||||
|
this.bs.resetPasswordUser(user.userId).subscribe(
|
||||||
|
(data:any) => {
|
||||||
|
this.toast.success("Ré-initialisation du mot de passe réussi");
|
||||||
|
}, error => {
|
||||||
|
this.toast.error("Erreur de ré-initialisation du mot de passe");
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}, error => {
|
||||||
|
this.toast.error("Une erreur est survenue ...");
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
onAddUser(): void {
|
||||||
|
let user:Utilisateur = {userId:0, Nom:'', Prenom:'', Role:'', Identifiant:'', Password:'', Actif:false, Photo:''};
|
||||||
|
const myDialog = this.dialog.open(AddUserDialogComponent, {
|
||||||
|
disableClose: true,
|
||||||
|
width: '400px'
|
||||||
|
});
|
||||||
|
myDialog.afterClosed().subscribe(
|
||||||
|
data => {
|
||||||
|
if(data) {
|
||||||
|
user.Prenom = data['prenom'];
|
||||||
|
user.Nom = data['nom'];
|
||||||
|
user.Identifiant = data['identifiant'];
|
||||||
|
user.Role = data['role'];
|
||||||
|
user.Actif = true;
|
||||||
|
this.bs.saveUser(user).subscribe(
|
||||||
|
data => {
|
||||||
|
this.toast.success("Enregistrement de l'utilisateur réussi");
|
||||||
|
this.setUpdate();
|
||||||
|
}, error => {
|
||||||
|
this.toast.error("Erreur lors de l'enregistrement de l'utilisateur");
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
1
src/app/components/workspace/workspace.component.html
Normal file
1
src/app/components/workspace/workspace.component.html
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<router-outlet></router-outlet>
|
||||||
25
src/app/components/workspace/workspace.component.spec.ts
Normal file
25
src/app/components/workspace/workspace.component.spec.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { WorkspaceComponent } from './workspace.component';
|
||||||
|
|
||||||
|
describe('WorkspaceComponent', () => {
|
||||||
|
let component: WorkspaceComponent;
|
||||||
|
let fixture: ComponentFixture<WorkspaceComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ WorkspaceComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(WorkspaceComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
59
src/app/components/workspace/workspace.component.ts
Normal file
59
src/app/components/workspace/workspace.component.ts
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { Utilisateur } from '../../model/utilisateur.model';
|
||||||
|
import { ActionsTypes, ActionEvt } from '../../state/user.state';
|
||||||
|
import { EventDriverService } from '../../services/event.driver.service';
|
||||||
|
import { Observable, of, Subscription } from 'rxjs';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-workspace',
|
||||||
|
templateUrl: './workspace.component.html',
|
||||||
|
styleUrls: ['./workspace.component.css']
|
||||||
|
})
|
||||||
|
export class WorkspaceComponent implements OnInit {
|
||||||
|
sub:Subscription;
|
||||||
|
|
||||||
|
constructor( private evtDrv:EventDriverService, private router:Router ) { }
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.sub=this.evtDrv.sourceEventSubjectObservable.subscribe(
|
||||||
|
(data:ActionEvt) => {
|
||||||
|
this.onActionEvt(data);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onActionEvt($event: ActionEvt): void {
|
||||||
|
switch($event.type) {
|
||||||
|
case ActionsTypes.EDIT_USER: {
|
||||||
|
this.onEditUser($event.payload);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ActionsTypes.MODIF_USERS: {
|
||||||
|
this.onListUsers();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ActionsTypes.SEND_MESSAGES: {
|
||||||
|
this.onMessages();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onEditUser(val:number): void {
|
||||||
|
this.router.navigateByUrl("/edit/"+val);
|
||||||
|
}
|
||||||
|
|
||||||
|
onListUsers(): void {
|
||||||
|
this.router.navigateByUrl("/utilisateurs");
|
||||||
|
}
|
||||||
|
|
||||||
|
onMessages(): void {
|
||||||
|
this.router.navigateByUrl("/messages");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { HttpXsrfInterceptorService } from './http-xsrf-interceptor.service';
|
||||||
|
|
||||||
|
describe('HttpXsrfInterceptorService', () => {
|
||||||
|
let service: HttpXsrfInterceptorService;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({});
|
||||||
|
service = TestBed.inject(HttpXsrfInterceptorService);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be created', () => {
|
||||||
|
expect(service).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { HttpRequest, HttpHandler, HttpEvent, HttpXsrfTokenExtractor } from '@angular/common/http';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class HttpXsrfInterceptorService {
|
||||||
|
constructor(private tokenExtractor: HttpXsrfTokenExtractor) { }
|
||||||
|
|
||||||
|
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
||||||
|
// DEBUG
|
||||||
|
console.log("[Interceptor]", req);
|
||||||
|
// END DEBUG
|
||||||
|
const headerName = 'X-CSRF-TOKEN';
|
||||||
|
const token = this.tokenExtractor.getToken() as string;
|
||||||
|
// DEBUG
|
||||||
|
console.log('[Interceptor] token : ' + token);
|
||||||
|
// END DEBUG
|
||||||
|
|
||||||
|
if (token != null && !req.headers.has(headerName)) {
|
||||||
|
req = req.clone({
|
||||||
|
headers: req.headers.set(headerName, token)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return next.handle(req);
|
||||||
|
}
|
||||||
|
}
|
||||||
12
src/app/model/message.model.ts
Normal file
12
src/app/model/message.model.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
export interface User {
|
||||||
|
Prenom:string;
|
||||||
|
Nom:string;
|
||||||
|
userId:number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Message {
|
||||||
|
msgId?:number;
|
||||||
|
Text:string;
|
||||||
|
Date:string;
|
||||||
|
user?:User;
|
||||||
|
}
|
||||||
10
src/app/model/utilisateur.model.ts
Normal file
10
src/app/model/utilisateur.model.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
export interface Utilisateur {
|
||||||
|
userId:number;
|
||||||
|
Nom:string;
|
||||||
|
Prenom:string;
|
||||||
|
Role:string;
|
||||||
|
Identifiant:string;
|
||||||
|
Password:string;
|
||||||
|
Actif:boolean;
|
||||||
|
Photo:string;
|
||||||
|
}
|
||||||
155
src/app/services/backend.service.ts
Normal file
155
src/app/services/backend.service.ts
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { HttpClient, HttpHeaders } from '@angular/common/http';
|
||||||
|
import { environment } from '../../environments/environment';
|
||||||
|
import { Utilisateur } from '../model/utilisateur.model';
|
||||||
|
import { Message } from '../model/message.model';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
|
@Injectable({providedIn:"root"})
|
||||||
|
export class BackendService {
|
||||||
|
constructor(private http:HttpClient) {}
|
||||||
|
|
||||||
|
getAllUsers():Observable<Utilisateur[]> {
|
||||||
|
let host=environment.host;
|
||||||
|
const options = {
|
||||||
|
headers: new HttpHeaders({
|
||||||
|
'Content-Type' : 'application/json',
|
||||||
|
}),
|
||||||
|
withCredentials: true
|
||||||
|
};
|
||||||
|
return this.http.get<Utilisateur[]>(host+"/api/utilisateurs", options);
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteUser(id:number):Observable<any> {
|
||||||
|
let host=environment.host;
|
||||||
|
const options = {
|
||||||
|
headers: new HttpHeaders({
|
||||||
|
'Content-Type' : 'application/json',
|
||||||
|
}),
|
||||||
|
withCredentials: true
|
||||||
|
};
|
||||||
|
return this.http.delete<any>(host+"/api/utilisateurs/" + id, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
saveUser(val:Utilisateur):Observable<any> {
|
||||||
|
let host=environment.host;
|
||||||
|
const options = {
|
||||||
|
headers: new HttpHeaders({
|
||||||
|
'Content-Type' : 'application/json',
|
||||||
|
}),
|
||||||
|
withCredentials: true
|
||||||
|
};
|
||||||
|
return this.http.post<any>(host+"/api/utilisateurs", val, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
getUser(id:number):Observable<Utilisateur> {
|
||||||
|
let host=environment.host;
|
||||||
|
const options = {
|
||||||
|
headers: new HttpHeaders({
|
||||||
|
'Content-Type' : 'application/json',
|
||||||
|
}),
|
||||||
|
withCredentials: true
|
||||||
|
};
|
||||||
|
return this.http.get<Utilisateur>(host+"/api/utilisateurs/" + id, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateUser(id:number, val:Utilisateur):Observable<any> {
|
||||||
|
let host=environment.host;
|
||||||
|
const options = {
|
||||||
|
headers: new HttpHeaders({
|
||||||
|
'Content-Type' : 'application/json',
|
||||||
|
}),
|
||||||
|
withCredentials: true
|
||||||
|
};
|
||||||
|
return this.http.put<any>(host+"/api/utilisateurs/"+id, val, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateUserPhoto(id:number, val:FormData):Observable<any> {
|
||||||
|
let host=environment.host;
|
||||||
|
return this.http.post<any>(host+"/api/utilisateurs/" + id + "/uploadImage", val, { withCredentials: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
getPhotoUser(id:number):Observable<Blob> {
|
||||||
|
let host=environment.host;
|
||||||
|
return this.http.get(host+"/api/utilisateurs/" + id + "/photo", { withCredentials: true, responseType: 'blob'});
|
||||||
|
}
|
||||||
|
|
||||||
|
getCurrentUser():Observable<Utilisateur> {
|
||||||
|
let host=environment.host;
|
||||||
|
const options = {
|
||||||
|
headers: new HttpHeaders({
|
||||||
|
'Content-Type' : 'application/json',
|
||||||
|
}),
|
||||||
|
withCredentials: true
|
||||||
|
};
|
||||||
|
return this.http.get<Utilisateur>(host+"/api/utilisateurs/current", options);
|
||||||
|
}
|
||||||
|
|
||||||
|
resetPasswordUser(id:number):Observable<any> {
|
||||||
|
let host=environment.host;
|
||||||
|
const options = {
|
||||||
|
headers: new HttpHeaders({
|
||||||
|
'Content-Type' : 'application/json',
|
||||||
|
}),
|
||||||
|
withCredentials: true
|
||||||
|
};
|
||||||
|
return this.http.get<any>(host+"/api/utilisateurs/" + id + "/reset_password", options);
|
||||||
|
}
|
||||||
|
|
||||||
|
activateUser(id:number, val:Utilisateur):Observable<any> {
|
||||||
|
let host=environment.host;
|
||||||
|
const options = {
|
||||||
|
headers: new HttpHeaders({
|
||||||
|
'Content-Type' : 'application/json',
|
||||||
|
}),
|
||||||
|
withCredentials: true
|
||||||
|
};
|
||||||
|
return this.http.put<any>(host+"/api/utilisateurs/" + id + "/activate", val, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
loginUser(val:any) {
|
||||||
|
let host=environment.host;
|
||||||
|
const options = {
|
||||||
|
headers: new HttpHeaders({
|
||||||
|
'Content-Type' : 'application/json',
|
||||||
|
'Authorization' : 'Basic ' + btoa(val['login'] + ':' + val['password']),
|
||||||
|
}),
|
||||||
|
withCredentials: true
|
||||||
|
};
|
||||||
|
|
||||||
|
return this.http.post<any>(host+"/api/utilisateurs/login", {}, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
logoutUser():Observable<any> {
|
||||||
|
let host=environment.host;
|
||||||
|
const options = {
|
||||||
|
headers: new HttpHeaders({
|
||||||
|
'Content-Type' : 'application/json',
|
||||||
|
}),
|
||||||
|
withCredentials: true
|
||||||
|
};
|
||||||
|
return this.http.post<any>(host+"/api/utilisateurs/logout", {}, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
getAllMessages():Observable<Message[]> {
|
||||||
|
let host=environment.host;
|
||||||
|
const options = {
|
||||||
|
headers: new HttpHeaders({
|
||||||
|
'Content-Type' : 'application/json',
|
||||||
|
}),
|
||||||
|
withCredentials: true
|
||||||
|
};
|
||||||
|
return this.http.get<Message[]>(host+"/api/messages", options);
|
||||||
|
}
|
||||||
|
|
||||||
|
sendMessage(val:Message):Observable<any> {
|
||||||
|
let host=environment.host;
|
||||||
|
const options = {
|
||||||
|
headers: new HttpHeaders({
|
||||||
|
'Content-Type' : 'application/json',
|
||||||
|
}),
|
||||||
|
withCredentials: true
|
||||||
|
};
|
||||||
|
return this.http.post<any>(host+"/api/messages", val, options);
|
||||||
|
}
|
||||||
|
}
|
||||||
15
src/app/services/event.driver.service.ts
Normal file
15
src/app/services/event.driver.service.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { Subject } from 'rxjs';
|
||||||
|
import { ActionEvt } from '../state/user.state';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn:"root"
|
||||||
|
})
|
||||||
|
export class EventDriverService {
|
||||||
|
sourceEventSubject:Subject<ActionEvt>=new Subject<ActionEvt>();
|
||||||
|
sourceEventSubjectObservable=this.sourceEventSubject.asObservable();
|
||||||
|
|
||||||
|
publishEvent(event:ActionEvt) {
|
||||||
|
this.sourceEventSubject.next(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
16
src/app/services/profile/profile.service.spec.ts
Normal file
16
src/app/services/profile/profile.service.spec.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { ProfileService } from './profile.service';
|
||||||
|
|
||||||
|
describe('ProfileService', () => {
|
||||||
|
let service: ProfileService;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({});
|
||||||
|
service = TestBed.inject(ProfileService);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be created', () => {
|
||||||
|
expect(service).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
46
src/app/services/profile/profile.service.ts
Normal file
46
src/app/services/profile/profile.service.ts
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { BackendService } from '../backend.service';
|
||||||
|
import { Utilisateur } from '../../model/utilisateur.model';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class ProfileService implements CanActivate {
|
||||||
|
profile:Utilisateur;
|
||||||
|
|
||||||
|
constructor( private bs:BackendService, private router:Router ) { }
|
||||||
|
|
||||||
|
getProfile(): Observable<any> {
|
||||||
|
return new Observable((observer) => {
|
||||||
|
if(this.profile) {
|
||||||
|
console.log("Profile non null");
|
||||||
|
observer.next(this.profile);
|
||||||
|
observer.complete();
|
||||||
|
} else {
|
||||||
|
this.bs.getCurrentUser().subscribe(profile => {
|
||||||
|
this.profile = profile;
|
||||||
|
observer.next(profile);
|
||||||
|
observer.complete()
|
||||||
|
}, error => {
|
||||||
|
observer.error(error);
|
||||||
|
observer.complete();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
|
||||||
|
return new Observable((observer) => {
|
||||||
|
this.getProfile().subscribe(profile => {
|
||||||
|
observer.next(true);
|
||||||
|
observer.complete();
|
||||||
|
}, error => {
|
||||||
|
this.router.navigateByUrl("/login");
|
||||||
|
observer.next(false);
|
||||||
|
observer.complete();
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
24
src/app/state/user.state.ts
Normal file
24
src/app/state/user.state.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
export enum ActionsTypes {
|
||||||
|
EDIT_USER="[Utilisateur] Editer utilisateur",
|
||||||
|
UPDATE_USER="[Utilisateur] Mise à jour des infos de l'utiliateur",
|
||||||
|
MODIF_USERS="[Utilisateurs] Modification des utilisateurs",
|
||||||
|
SEND_MESSAGES="[Utilisateurs] Envoie de messages"
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ActionEvt {
|
||||||
|
type:ActionsTypes,
|
||||||
|
payload?:any,
|
||||||
|
errMsg?:string
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum DataStateEnum {
|
||||||
|
LOADING,
|
||||||
|
LOADED,
|
||||||
|
ERROR
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AppDataState<T> {
|
||||||
|
dataState?:DataStateEnum,
|
||||||
|
data?:T,
|
||||||
|
errorMessage?:string
|
||||||
|
}
|
||||||
@@ -3,7 +3,8 @@
|
|||||||
// The list of file replacements can be found in `angular.json`.
|
// The list of file replacements can be found in `angular.json`.
|
||||||
|
|
||||||
export const environment = {
|
export const environment = {
|
||||||
production: false
|
production: false,
|
||||||
|
host: "http://localhost:5000"
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -6,6 +6,9 @@
|
|||||||
<base href="/">
|
<base href="/">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com">
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap" rel="stylesheet">
|
||||||
|
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<app-root></app-root>
|
<app-root></app-root>
|
||||||
|
|||||||
@@ -1 +1,5 @@
|
|||||||
/* You can add global styles to this file, and also import other style files */
|
/* You can add global styles to this file, and also import other style files */
|
||||||
|
@import "~font-awesome/css/font-awesome.min.css";
|
||||||
|
|
||||||
|
html, body { height: 100%; }
|
||||||
|
body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; }
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
"noPropertyAccessFromIndexSignature": true,
|
"noPropertyAccessFromIndexSignature": true,
|
||||||
"noImplicitReturns": true,
|
"noImplicitReturns": true,
|
||||||
"noFallthroughCasesInSwitch": true,
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"strictPropertyInitialization": false,
|
||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
"declaration": false,
|
"declaration": false,
|
||||||
"downlevelIteration": true,
|
"downlevelIteration": true,
|
||||||
@@ -20,7 +21,8 @@
|
|||||||
"module": "es2020",
|
"module": "es2020",
|
||||||
"lib": [
|
"lib": [
|
||||||
"es2020",
|
"es2020",
|
||||||
"dom"
|
"dom",
|
||||||
|
"dom.iterable"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"angularCompilerOptions": {
|
"angularCompilerOptions": {
|
||||||
|
|||||||
Reference in New Issue
Block a user