From d8ed8ce2cec5a2f94527562e0746b3bbb9ae19dd Mon Sep 17 00:00:00 2001 From: blugee Date: Mon, 30 Jun 2025 22:08:02 +0530 Subject: [PATCH] add login with google and change --- front-update.py | 2 +- nginx.conf | 2 +- package-lock.json | 546 +++++++++++++----- package.json | 5 +- proxy.conf.json | 3 +- src/app/app-routing.module.ts | 8 + src/app/app.module.ts | 43 +- src/app/auth/apple-config.ts | 7 + src/app/auth/msal-config.ts | 19 + src/app/icons/icon-subset.ts | 9 +- src/app/providers/login_checker.ts | 53 +- src/app/providers/mikrowizard/data.ts | 38 +- src/app/providers/mikrowizard/provider.ts | 4 +- .../views/dashboard/dashboard.component.html | 456 ++++++++------- .../views/dashboard/dashboard.component.scss | 443 ++++++++++++++ .../views/dashboard/dashboard.component.ts | 274 +++++++-- .../views/pages/login/login.component.html | 132 +++-- .../views/pages/login/login.component.scss | 38 ++ src/app/views/pages/login/login.component.ts | 107 +++- src/app/views/pages/pages-routing.module.ts | 8 + src/app/views/pages/pages.module.ts | 3 +- .../views/pages/signup/signup.component.html | 115 ++++ .../views/pages/signup/signup.component.scss | 38 ++ .../views/pages/signup/signup.component.ts | 61 ++ src/assets/Pause GIF Image.gif | Bin 0 -> 45012 bytes 25 files changed, 1946 insertions(+), 468 deletions(-) create mode 100644 src/app/auth/apple-config.ts create mode 100644 src/app/auth/msal-config.ts create mode 100644 src/app/views/dashboard/dashboard.component.scss create mode 100644 src/app/views/pages/signup/signup.component.html create mode 100644 src/app/views/pages/signup/signup.component.scss create mode 100644 src/app/views/pages/signup/signup.component.ts create mode 100644 src/assets/Pause GIF Image.gif diff --git a/front-update.py b/front-update.py index 97eba8b..c8239e2 100644 --- a/front-update.py +++ b/front-update.py @@ -23,7 +23,7 @@ logging.basicConfig(level=logging.INFO) log = logging.getLogger("updater") log.setLevel(logging.INFO) -API_URL="http://host.docker.internal:8181" +API_URL="http://192.168.1.26:8181" Config_File="/conf/server-conf.json" Version_File="/usr/share/nginx/html/version.json" # Example usage diff --git a/nginx.conf b/nginx.conf index 418d1e1..e5519ef 100644 --- a/nginx.conf +++ b/nginx.conf @@ -18,7 +18,7 @@ server { try_files $uri $uri/ /index.html =404; } location /api { - proxy_pass http://host.docker.internal:8181; + proxy_pass http://192.168.1.26:8181; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $realip_remote_addr; diff --git a/package-lock.json b/package-lock.json index 2ad6060..30655bb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,13 +1,13 @@ { - "name": "coreui-free-angular-admin-template", - "version": "4.5.27", + "name": "MikroWizard", + "version": "1.0.1", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "coreui-free-angular-admin-template", - "version": "4.5.27", - "license": "MIT", + "name": "MikroWizard", + "version": "1.0.1", + "license": "AGPL", "dependencies": { "@angular/animations": "^17.3.5", "@angular/cdk": "^16.2.9", @@ -20,6 +20,8 @@ "@angular/platform-browser": "^17.3.5", "@angular/platform-browser-dynamic": "^17.3.5", "@angular/router": "^17.3.5", + "@azure/msal-angular": "^4.0.12", + "@azure/msal-browser": "^4.12.0", "@coreui/angular": "~4.5.27", "@coreui/angular-chartjs": "~4.5.27", "@coreui/chartjs": "^3.1.2", @@ -28,25 +30,26 @@ "@coreui/icons-angular": "~4.5.27", "@coreui/utils": "^2.0.2", "@easyfonts/font-awesome-v6": "^6.0.6", - "@fortawesome/angular-fontawesome": "^0.13.0", - "@fortawesome/fontawesome-svg-core": "^6.4.2", - "@fortawesome/free-brands-svg-icons": "^6.4.2", - "@fortawesome/free-regular-svg-icons": "^6.4.2", - "@fortawesome/free-solid-svg-icons": "^6.4.2", + "@fortawesome/angular-fontawesome": "^0.15.0", "@generic-ui/fabric": "^0.19.0", "@generic-ui/hermes": "^0.19.0", "@generic-ui/ngx-grid": "^0.19.0", + "apple-signin-auth": "^2.0.0", "chart.js": "^3.9.1", "date-fns": "^3.6.0", "date-fns-jalali": "^3.6.0-0", "date-fns-tz": "^3.1.3", + "diff-match-patch-ts": "^0.6.0", "font-awesome": "^4.7.0", "install": "^0.13.0", "lodash-es": "^4.17.21", "mat-progress-buttons": "^9.3.1", "ngx-cron-editor": "^0.8.1", "ngx-date-fns": "^11.0.0", + "ngx-diff": "^9.0.0", + "ngx-highlight-js": "^18.0.0", "ngx-highlightjs": "^12.0.0", + "ngx-infinite-scroll": "^18.0.0", "ngx-mat-select-search": "^7.0.6", "ngx-material-date-fns-adapter": "^18.0.0", "ngx-scrollbar": "^13.0.3", @@ -754,6 +757,37 @@ "rxjs": "^6.5.3 || ^7.4.0" } }, + "node_modules/@azure/msal-angular": { + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/@azure/msal-angular/-/msal-angular-4.0.12.tgz", + "integrity": "sha512-J6wV84d0hcKjiH/VSCMDcigYiJ9bcrMuM3uFrfEATVn7ANziKq+skp9buwcr0s6e60Z3R7Juxb8wOnI1HemXGw==", + "dependencies": { + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@azure/msal-browser": "^4.12.0", + "rxjs": "^7.0.0" + } + }, + "node_modules/@azure/msal-browser": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-4.12.0.tgz", + "integrity": "sha512-WD1lmVWchg7wn1mI7Tr4v7QPyTwK+8Nuyje3jRpOFENLRLEBsdK8VVdTw3C+TypZmYn4cOAdj3zREnuFXgvfIA==", + "dependencies": { + "@azure/msal-common": "15.6.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@azure/msal-common": { + "version": "15.6.0", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-15.6.0.tgz", + "integrity": "sha512-EotmBz42apYGjqiIV9rDUdptaMptpTn4TdGf3JfjLvFvinSe9BJ6ywU92K9ky+t/b0ghbeTSe9RfqlgLh8f2jA==", + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/@babel/code-frame": { "version": "7.24.2", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", @@ -2972,15 +3006,15 @@ } }, "node_modules/@fortawesome/angular-fontawesome": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@fortawesome/angular-fontawesome/-/angular-fontawesome-0.13.0.tgz", - "integrity": "sha512-gzSPRdveOXNO7NIiMgTyB46aiHG0i98KinnAEqHXi8qzraM/kCcHn/0y3f4MhemX6kftwsFli0IU8RyHmtXlSQ==", + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@fortawesome/angular-fontawesome/-/angular-fontawesome-0.15.0.tgz", + "integrity": "sha512-oxmJDYGNSym5ycFR0LX4ZOPAU+wWmMAznYpkm5DNAtWWkhMLcrZl15eZQmVIEE+qruQ7JiVrg3tpo8bEkFlDgw==", "dependencies": { - "tslib": "^2.4.1" + "@fortawesome/fontawesome-svg-core": "^6.5.2", + "tslib": "^2.6.2" }, "peerDependencies": { - "@angular/core": "^16.0.0", - "@fortawesome/fontawesome-svg-core": "~1.2.27 || ~1.3.0-beta2 || ^6.1.0" + "@angular/core": "^18.0.0" } }, "node_modules/@fortawesome/fontawesome-common-types": { @@ -3004,42 +3038,6 @@ "node": ">=6" } }, - "node_modules/@fortawesome/free-brands-svg-icons": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-6.5.2.tgz", - "integrity": "sha512-zi5FNYdmKLnEc0jc0uuHH17kz/hfYTg4Uei0wMGzcoCL/4d3WM3u1VMc0iGGa31HuhV5i7ZK8ZlTCQrHqRHSGQ==", - "hasInstallScript": true, - "dependencies": { - "@fortawesome/fontawesome-common-types": "6.5.2" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@fortawesome/free-regular-svg-icons": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-6.5.2.tgz", - "integrity": "sha512-iabw/f5f8Uy2nTRtJ13XZTS1O5+t+anvlamJ3zJGLEVE2pKsAWhPv2lq01uQlfgCX7VaveT3EVs515cCN9jRbw==", - "hasInstallScript": true, - "dependencies": { - "@fortawesome/fontawesome-common-types": "6.5.2" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@fortawesome/free-solid-svg-icons": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.5.2.tgz", - "integrity": "sha512-QWFZYXFE7O1Gr1dTIp+D6UcFUF0qElOnZptpi7PBUMylJh+vFmIedVe1Ir6RM1t2tEQLLSV1k7bR4o92M+uqlw==", - "hasInstallScript": true, - "dependencies": { - "@fortawesome/fontawesome-common-types": "6.5.2" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/@generic-ui/fabric": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@generic-ui/fabric/-/fabric-0.19.1.tgz", @@ -5390,6 +5388,19 @@ "node": ">= 8" } }, + "node_modules/apple-signin-auth": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/apple-signin-auth/-/apple-signin-auth-2.0.0.tgz", + "integrity": "sha512-/O5gvAby7OU2K7baYQCzY0e0tCiHzhFkzt9L2v3bMd2I2w0ckCZ/4hdVUOrbolRGMppME+Zo8TAKPVru8aYnzg==", + "license": "MIT", + "dependencies": { + "jsonwebtoken": "^9.0.0", + "node-rsa": "^1.1.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -5405,6 +5416,15 @@ "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", "dev": true }, + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "license": "MIT", + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, "node_modules/autoprefixer": { "version": "10.4.18", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.18.tgz", @@ -5770,6 +5790,12 @@ "ieee754": "^1.1.13" } }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -6735,6 +6761,11 @@ "integrity": "sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA==", "dev": true }, + "node_modules/diff-match-patch-ts": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/diff-match-patch-ts/-/diff-match-patch-ts-0.6.0.tgz", + "integrity": "sha512-U0uPIJ+wJqgaBoVw2MFSFpGIk7q3mJJ+/sehbxDZFv4Gx6a1GOmrsSLmxVDDrGtRL4Q9de084aa5lVpCHn+eUw==" + }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -6832,6 +6863,15 @@ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "dev": true }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -8824,6 +8864,49 @@ "node >= 0.2.0" ] }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "license": "MIT", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jwa": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", + "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "license": "MIT", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, "node_modules/karma": { "version": "6.4.3", "resolved": "https://registry.npmjs.org/karma/-/karma-6.4.3.tgz", @@ -9178,6 +9261,48 @@ "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", "dev": true }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "license": "MIT" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "license": "MIT" + }, "node_modules/log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -9728,8 +9853,7 @@ "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/multicast-dns": { "version": "7.2.5", @@ -9757,6 +9881,7 @@ "version": "3.3.7", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "dev": true, "funding": [ { "type": "github", @@ -9846,6 +9971,28 @@ "date-fns": ">=3" } }, + "node_modules/ngx-diff": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/ngx-diff/-/ngx-diff-9.1.0.tgz", + "integrity": "sha512-hD54tOsQLNU6ltbS9gV4wDrnZrMfxjF2AlxnUrp+/n2+T/nLK2bQMiiQT2IEe6CXZ1dl/doKD1qGoQnX0mSoIw==", + "dependencies": { + "tslib": "^2.0.0" + }, + "peerDependencies": { + "@angular/common": ">=18.0.0", + "@angular/core": ">=18.0.0", + "diff-match-patch-ts": ">=0.5.0" + } + }, + "node_modules/ngx-highlight-js": { + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/ngx-highlight-js/-/ngx-highlight-js-18.0.0.tgz", + "integrity": "sha512-r/LSijb5Ju95ZGm89+4e905kFWkDt1XdGCg2prR1wleTE77uROscjZMht0D40WSRbxmOg+J2zw9OOZvsWSLeeg==", + "dependencies": { + "highlight.js": "^11.9.0", + "tslib": "^2.3.0" + } + }, "node_modules/ngx-highlightjs": { "version": "12.0.0", "resolved": "https://registry.npmjs.org/ngx-highlightjs/-/ngx-highlightjs-12.0.0.tgz", @@ -9859,6 +10006,18 @@ "@angular/core": ">=17.0.0" } }, + "node_modules/ngx-infinite-scroll": { + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/ngx-infinite-scroll/-/ngx-infinite-scroll-18.0.0.tgz", + "integrity": "sha512-D183TDwpsd9Zl56UmItsl3RzHdN25srAISfg6lc3A8mEKkEgOq0s7ZzRAYcx8DHsAkMgtZqjIPEvMifD3DOB/g==", + "dependencies": { + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/common": ">=18.0.0 <19.0.0", + "@angular/core": ">=18.0.0 <19.0.0" + } + }, "node_modules/ngx-mat-select-search": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/ngx-mat-select-search/-/ngx-mat-select-search-7.0.6.tgz", @@ -10030,6 +10189,15 @@ "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", "dev": true }, + "node_modules/node-rsa": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/node-rsa/-/node-rsa-1.1.1.tgz", + "integrity": "sha512-Jd4cvbJMryN21r5HgxQOpMEqv+ooke/korixNNK3mGqfGJmy0M77WDDzo/05969+OkMy3XW1UuZsSmW9KQm7Fw==", + "license": "MIT", + "dependencies": { + "asn1": "^0.2.4" + } + }, "node_modules/nopt": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.0.tgz", @@ -10663,7 +10831,8 @@ "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true }, "node_modules/picomatch": { "version": "2.3.1", @@ -10715,6 +10884,7 @@ "version": "8.4.35", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz", "integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==", + "dev": true, "funding": [ { "type": "opencollective", @@ -11446,7 +11616,6 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, "funding": [ { "type": "github", @@ -11465,8 +11634,7 @@ "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "node_modules/safevalues": { "version": "0.3.4", @@ -11579,7 +11747,6 @@ "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, "dependencies": { "lru-cache": "^6.0.0" }, @@ -11594,7 +11761,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, "dependencies": { "yallist": "^4.0.0" }, @@ -11605,8 +11771,7 @@ "node_modules/semver/node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/send": { "version": "0.18.0", @@ -11997,6 +12162,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -14207,6 +14373,27 @@ "tslib": "^2.3.0" } }, + "@azure/msal-angular": { + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/@azure/msal-angular/-/msal-angular-4.0.12.tgz", + "integrity": "sha512-J6wV84d0hcKjiH/VSCMDcigYiJ9bcrMuM3uFrfEATVn7ANziKq+skp9buwcr0s6e60Z3R7Juxb8wOnI1HemXGw==", + "requires": { + "tslib": "^2.3.0" + } + }, + "@azure/msal-browser": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-4.12.0.tgz", + "integrity": "sha512-WD1lmVWchg7wn1mI7Tr4v7QPyTwK+8Nuyje3jRpOFENLRLEBsdK8VVdTw3C+TypZmYn4cOAdj3zREnuFXgvfIA==", + "requires": { + "@azure/msal-common": "15.6.0" + } + }, + "@azure/msal-common": { + "version": "15.6.0", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-15.6.0.tgz", + "integrity": "sha512-EotmBz42apYGjqiIV9rDUdptaMptpTn4TdGf3JfjLvFvinSe9BJ6ywU92K9ky+t/b0ghbeTSe9RfqlgLh8f2jA==" + }, "@babel/code-frame": { "version": "7.24.2", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", @@ -14580,8 +14767,7 @@ "version": "7.21.0-placeholder-for-preset-env.2", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", - "dev": true, - "requires": {} + "dev": true }, "@babel/plugin-syntax-async-generators": { "version": "7.8.4", @@ -15659,11 +15845,12 @@ "optional": true }, "@fortawesome/angular-fontawesome": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@fortawesome/angular-fontawesome/-/angular-fontawesome-0.13.0.tgz", - "integrity": "sha512-gzSPRdveOXNO7NIiMgTyB46aiHG0i98KinnAEqHXi8qzraM/kCcHn/0y3f4MhemX6kftwsFli0IU8RyHmtXlSQ==", + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@fortawesome/angular-fontawesome/-/angular-fontawesome-0.15.0.tgz", + "integrity": "sha512-oxmJDYGNSym5ycFR0LX4ZOPAU+wWmMAznYpkm5DNAtWWkhMLcrZl15eZQmVIEE+qruQ7JiVrg3tpo8bEkFlDgw==", "requires": { - "tslib": "^2.4.1" + "@fortawesome/fontawesome-svg-core": "^6.5.2", + "tslib": "^2.6.2" } }, "@fortawesome/fontawesome-common-types": { @@ -15679,30 +15866,6 @@ "@fortawesome/fontawesome-common-types": "6.5.2" } }, - "@fortawesome/free-brands-svg-icons": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-6.5.2.tgz", - "integrity": "sha512-zi5FNYdmKLnEc0jc0uuHH17kz/hfYTg4Uei0wMGzcoCL/4d3WM3u1VMc0iGGa31HuhV5i7ZK8ZlTCQrHqRHSGQ==", - "requires": { - "@fortawesome/fontawesome-common-types": "6.5.2" - } - }, - "@fortawesome/free-regular-svg-icons": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-6.5.2.tgz", - "integrity": "sha512-iabw/f5f8Uy2nTRtJ13XZTS1O5+t+anvlamJ3zJGLEVE2pKsAWhPv2lq01uQlfgCX7VaveT3EVs515cCN9jRbw==", - "requires": { - "@fortawesome/fontawesome-common-types": "6.5.2" - } - }, - "@fortawesome/free-solid-svg-icons": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.5.2.tgz", - "integrity": "sha512-QWFZYXFE7O1Gr1dTIp+D6UcFUF0qElOnZptpi7PBUMylJh+vFmIedVe1Ir6RM1t2tEQLLSV1k7bR4o92M+uqlw==", - "requires": { - "@fortawesome/fontawesome-common-types": "6.5.2" - } - }, "@generic-ui/fabric": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@generic-ui/fabric/-/fabric-0.19.1.tgz", @@ -16676,8 +16839,7 @@ "version": "17.3.5", "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-17.3.5.tgz", "integrity": "sha512-0heI0yHUckdGI8uywu/wkp24KR/tdYMKYJOaYIU+9JydyN1zJRpbR7x0thddl7+k/zu2ZGbfFdv1779Ecw/xdA==", - "dev": true, - "requires": {} + "dev": true }, "@nodelib/fs.scandir": { "version": "2.1.5", @@ -17380,8 +17542,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/@vitejs/plugin-basic-ssl/-/plugin-basic-ssl-1.1.0.tgz", "integrity": "sha512-wO4Dk/rm8u7RNhOf95ZzcEmC9rYOncYgvq4z3duaJrCgjN8BxAnDVyndanfcJZ0O6XZzHz6Q0hTimxTg8Y9g/A==", - "dev": true, - "requires": {} + "dev": true }, "@webassemblyjs/ast": { "version": "1.12.1", @@ -17573,8 +17734,7 @@ "version": "1.9.0", "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", - "dev": true, - "requires": {} + "dev": true }, "adjust-sourcemap-loader": { "version": "4.0.0", @@ -17694,6 +17854,15 @@ "picomatch": "^2.0.4" } }, + "apple-signin-auth": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/apple-signin-auth/-/apple-signin-auth-2.0.0.tgz", + "integrity": "sha512-/O5gvAby7OU2K7baYQCzY0e0tCiHzhFkzt9L2v3bMd2I2w0ckCZ/4hdVUOrbolRGMppME+Zo8TAKPVru8aYnzg==", + "requires": { + "jsonwebtoken": "^9.0.0", + "node-rsa": "^1.1.1" + } + }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -17709,6 +17878,14 @@ "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", "dev": true }, + "asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "requires": { + "safer-buffer": "~2.1.0" + } + }, "autoprefixer": { "version": "10.4.18", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.18.tgz", @@ -17961,6 +18138,11 @@ "ieee754": "^1.1.13" } }, + "buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, "buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -18596,8 +18778,7 @@ "date-fns-tz": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/date-fns-tz/-/date-fns-tz-3.1.3.tgz", - "integrity": "sha512-ZfbMu+nbzW0mEzC8VZrLiSWvUIaI3aRHeq33mTe7Y38UctKukgqPR4nTDwcwS4d64Gf8GghnVsroBuMY3eiTeA==", - "requires": {} + "integrity": "sha512-ZfbMu+nbzW0mEzC8VZrLiSWvUIaI3aRHeq33mTe7Y38UctKukgqPR4nTDwcwS4d64Gf8GghnVsroBuMY3eiTeA==" }, "date-format": { "version": "4.0.14", @@ -18673,6 +18854,11 @@ "integrity": "sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA==", "dev": true }, + "diff-match-patch-ts": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/diff-match-patch-ts/-/diff-match-patch-ts-0.6.0.tgz", + "integrity": "sha512-U0uPIJ+wJqgaBoVw2MFSFpGIk7q3mJJ+/sehbxDZFv4Gx6a1GOmrsSLmxVDDrGtRL4Q9de084aa5lVpCHn+eUw==" + }, "dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -18746,6 +18932,14 @@ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "dev": true }, + "ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "requires": { + "safe-buffer": "^5.0.1" + } + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -18826,8 +19020,7 @@ "version": "8.11.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", - "dev": true, - "requires": {} + "dev": true } } }, @@ -19677,8 +19870,7 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", - "dev": true, - "requires": {} + "dev": true }, "ieee754": { "version": "1.2.1", @@ -20226,6 +20418,42 @@ "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", "dev": true }, + "jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "requires": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + } + }, + "jwa": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", + "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", + "requires": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "requires": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, "karma": { "version": "6.4.3", "resolved": "https://registry.npmjs.org/karma/-/karma-6.4.3.tgz", @@ -20374,8 +20602,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-2.1.0.tgz", "integrity": "sha512-sPQE1+nlsn6Hwb5t+HHwyy0A1FNCVKuL1192b+XNauMYWThz2kweiBVW1DqloRpVvZIJkIoHVB7XRpK78n1xbQ==", - "dev": true, - "requires": {} + "dev": true }, "karma-source-map-support": { "version": "1.4.0", @@ -20497,6 +20724,41 @@ "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", "dev": true }, + "lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, + "lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, + "lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, "log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -20917,8 +21179,7 @@ "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "multicast-dns": { "version": "7.2.5", @@ -20939,7 +21200,8 @@ "nanoid": { "version": "3.3.7", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==" + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "dev": true }, "needle": { "version": "3.3.1", @@ -20992,6 +21254,23 @@ "tslib": "^2.3.0" } }, + "ngx-diff": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/ngx-diff/-/ngx-diff-9.1.0.tgz", + "integrity": "sha512-hD54tOsQLNU6ltbS9gV4wDrnZrMfxjF2AlxnUrp+/n2+T/nLK2bQMiiQT2IEe6CXZ1dl/doKD1qGoQnX0mSoIw==", + "requires": { + "tslib": "^2.0.0" + } + }, + "ngx-highlight-js": { + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/ngx-highlight-js/-/ngx-highlight-js-18.0.0.tgz", + "integrity": "sha512-r/LSijb5Ju95ZGm89+4e905kFWkDt1XdGCg2prR1wleTE77uROscjZMht0D40WSRbxmOg+J2zw9OOZvsWSLeeg==", + "requires": { + "highlight.js": "^11.9.0", + "tslib": "^2.3.0" + } + }, "ngx-highlightjs": { "version": "12.0.0", "resolved": "https://registry.npmjs.org/ngx-highlightjs/-/ngx-highlightjs-12.0.0.tgz", @@ -21001,6 +21280,14 @@ "tslib": "^2.3.0" } }, + "ngx-infinite-scroll": { + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/ngx-infinite-scroll/-/ngx-infinite-scroll-18.0.0.tgz", + "integrity": "sha512-D183TDwpsd9Zl56UmItsl3RzHdN25srAISfg6lc3A8mEKkEgOq0s7ZzRAYcx8DHsAkMgtZqjIPEvMifD3DOB/g==", + "requires": { + "tslib": "^2.3.0" + } + }, "ngx-mat-select-search": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/ngx-mat-select-search/-/ngx-mat-select-search-7.0.6.tgz", @@ -21121,6 +21408,14 @@ "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", "dev": true }, + "node-rsa": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/node-rsa/-/node-rsa-1.1.1.tgz", + "integrity": "sha512-Jd4cvbJMryN21r5HgxQOpMEqv+ooke/korixNNK3mGqfGJmy0M77WDDzo/05969+OkMy3XW1UuZsSmW9KQm7Fw==", + "requires": { + "asn1": "^0.2.4" + } + }, "nopt": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.0.tgz", @@ -21585,7 +21880,8 @@ "picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true }, "picomatch": { "version": "2.3.1", @@ -21622,6 +21918,7 @@ "version": "8.4.35", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz", "integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==", + "dev": true, "requires": { "nanoid": "^3.3.7", "picocolors": "^1.0.0", @@ -21657,8 +21954,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz", "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==", - "dev": true, - "requires": {} + "dev": true }, "postcss-modules-local-by-default": { "version": "4.0.5", @@ -22137,14 +22433,12 @@ "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" }, "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "safevalues": { "version": "0.3.4", @@ -22210,7 +22504,6 @@ "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, "requires": { "lru-cache": "^6.0.0" }, @@ -22219,7 +22512,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, "requires": { "yallist": "^4.0.0" } @@ -22227,8 +22519,7 @@ "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" } } }, @@ -22490,8 +22781,7 @@ "version": "8.11.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", - "dev": true, - "requires": {} + "dev": true } } }, @@ -22546,7 +22836,8 @@ "source-map-js": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", - "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==" + "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "dev": true }, "source-map-loader": { "version": "5.0.0", @@ -22849,8 +23140,7 @@ "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "requires": {} + "dev": true }, "json-schema-traverse": { "version": "0.4.1", @@ -23399,8 +23689,7 @@ "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "requires": {} + "dev": true }, "json-schema-traverse": { "version": "0.4.1", @@ -23637,8 +23926,7 @@ "version": "8.16.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", - "dev": true, - "requires": {} + "dev": true }, "y18n": { "version": "5.0.8", diff --git a/package.json b/package.json index a390bf6..fa0f231 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ }, "scripts": { "ng": "ng", - "start": "ng serve --proxy-config proxy.conf.json --host 0.0.0.0", + "start": "ng serve --host 0.0.0.0 --port 4200 --disable-host-check", "startnp": "ng serve --proxy-config=./proxy.conf.ts --host 0.0.0.0", "build": "ng build", "watch": "ng build --watch --configuration development", @@ -30,6 +30,8 @@ "@angular/platform-browser": "^17.3.5", "@angular/platform-browser-dynamic": "^17.3.5", "@angular/router": "^17.3.5", + "@azure/msal-angular": "^4.0.12", + "@azure/msal-browser": "^4.12.0", "@coreui/angular": "~4.5.27", "@coreui/angular-chartjs": "~4.5.27", "@coreui/chartjs": "^3.1.2", @@ -42,6 +44,7 @@ "@generic-ui/fabric": "^0.19.0", "@generic-ui/hermes": "^0.19.0", "@generic-ui/ngx-grid": "^0.19.0", + "apple-signin-auth": "^2.0.0", "chart.js": "^3.9.1", "date-fns": "^3.6.0", "date-fns-jalali": "^3.6.0-0", diff --git a/proxy.conf.json b/proxy.conf.json index b6b8647..fbff922 100644 --- a/proxy.conf.json +++ b/proxy.conf.json @@ -1,8 +1,9 @@ { "/api": { - "target": "http://172.17.0.1:8181/", + "target": "http://192.168.1.26:3000/", "secure": false, "changeOrigin": true, + "logLevel": "debug", "pathRewrite": {"^/api" : ""} } } \ No newline at end of file diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 3c4186a..c41f77c 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -5,6 +5,7 @@ import { DefaultLayoutComponent } from './containers'; import { Page404Component } from './views/pages/page404/page404.component'; import { Page500Component } from './views/pages/page500/page500.component'; import { LoginComponent } from './views/pages/login/login.component'; +import { SignupComponent } from './views/pages/signup/signup.component'; const routes: Routes = [ { @@ -132,6 +133,13 @@ const routes: Routes = [ title: 'Login Page' } }, + { + path: 'signup', + component: SignupComponent, + data: { + title: 'Signup Page' + } + }, {path: '**', redirectTo: 'dashboard'} ]; diff --git a/src/app/app.module.ts b/src/app/app.module.ts index bb74798..dfebcf2 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -1,8 +1,12 @@ -import { NgModule ,APP_INITIALIZER} from '@angular/core'; +import { NgModule, APP_INITIALIZER } from '@angular/core'; import { HashLocationStrategy, LocationStrategy, PathLocationStrategy } from '@angular/common'; import { BrowserModule, Title } from '@angular/platform-browser'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { ReactiveFormsModule,FormsModule } from '@angular/forms'; +import { ReactiveFormsModule, FormsModule } from '@angular/forms'; +import { MsalModule, MsalService, MSAL_INSTANCE, MsalGuard, MsalInterceptor, MsalGuardConfiguration, MsalInterceptorConfiguration } from '@azure/msal-angular'; +import { IPublicClientApplication, PublicClientApplication, InteractionType } from '@azure/msal-browser'; +import { msalConfig } from './auth/msal-config'; +import { HTTP_INTERCEPTORS } from '@angular/common/http'; import { NgScrollbarModule } from 'ngx-scrollbar'; import { HttpClientModule } from '@angular/common/http'; @@ -54,12 +58,36 @@ const APP_CONTAINERS = [ export function loginStatusProviderFactory(provider: loginChecker) { return () => provider.load(); } + +export function MSALInstanceFactory(): IPublicClientApplication { + const msalInstance = new PublicClientApplication(msalConfig); + msalInstance.initialize().catch(error => { + console.error('MSAL initialization failed:', error); + }); + return msalInstance; +} + +const guardConfig: MsalGuardConfiguration = { + interactionType: InteractionType.Popup, + authRequest: { + scopes: ['User.Read', 'profile', 'email', 'openid'] + } +}; + +const interceptorConfig: MsalInterceptorConfiguration = { + interactionType: InteractionType.Popup, + protectedResourceMap: new Map([ + ['https://graph.microsoft.com/v1.0/me', ['User.Read']] + ]) +}; + @NgModule({ declarations: [AppComponent, ...APP_CONTAINERS], imports: [ BrowserModule, BrowserAnimationsModule, AppRoutingModule, + MsalModule.forRoot(MSALInstanceFactory(), guardConfig, interceptorConfig), AvatarModule, BreadcrumbModule, FooterModule, @@ -94,6 +122,17 @@ export function loginStatusProviderFactory(provider: loginChecker) { provide: LocationStrategy, useClass: HashLocationStrategy }, + { + provide: MSAL_INSTANCE, + useFactory: MSALInstanceFactory + }, + { + provide: HTTP_INTERCEPTORS, + useClass: MsalInterceptor, + multi: true + }, + MsalService, + MsalGuard, MikroWizardProvider, dataProvider, loginChecker, diff --git a/src/app/auth/apple-config.ts b/src/app/auth/apple-config.ts new file mode 100644 index 0000000..e7b7493 --- /dev/null +++ b/src/app/auth/apple-config.ts @@ -0,0 +1,7 @@ +export const appleConfig = { + clientId: 'ca.circleprotect.cnode', // Your Apple Service ID + redirectURI: "https://30c8-2402-a00-162-359c-1499-994a-62e-7eda.ngrok-free.app/auth/apple/callback", + scope: 'name email', + state: 'apple-signin', + usePopup: true +}; \ No newline at end of file diff --git a/src/app/auth/msal-config.ts b/src/app/auth/msal-config.ts new file mode 100644 index 0000000..4d4eeb6 --- /dev/null +++ b/src/app/auth/msal-config.ts @@ -0,0 +1,19 @@ +import { Configuration, PopupRequest } from "@azure/msal-browser"; + +// MSAL configuration +export const msalConfig: Configuration = { + auth: { + clientId: "fdbc76a1-2553-4efb-871f-56afdd3dd894", // Replace with your Azure AD application client ID + authority: "https://login.microsoftonline.com/common", + redirectUri: window.location.origin, + }, + cache: { + cacheLocation: "sessionStorage", + storeAuthStateInCookie: false, + } +}; + +// Add here scopes for id token to be used at MS Identity Platform endpoints. +export const loginRequest: PopupRequest = { + scopes: ["User.Read", "profile", "email", "openid"] +}; \ No newline at end of file diff --git a/src/app/icons/icon-subset.ts b/src/app/icons/icon-subset.ts index 5407905..167fb98 100644 --- a/src/app/icons/icon-subset.ts +++ b/src/app/icons/icon-subset.ts @@ -89,7 +89,8 @@ import { cilUserFemale, cilUserFollow, cilUserUnfollow, - cilExitToApp + cilExitToApp, + cilBuilding } from '@coreui/icons'; export const iconSubset = { @@ -183,7 +184,8 @@ export const iconSubset = { cilUserFemale, cilUserFollow, cilUserUnfollow, - cilExitToApp + cilExitToApp, + cilBuilding }; export enum IconSubset { @@ -277,5 +279,6 @@ export enum IconSubset { cilUserFemale = 'cilUserFemale', cilUserFollow = 'cilUserFollow', cilUserUnfollow = 'cilUserUnfollow', - cilExitToApp = 'cilExitToApp' + cilExitToApp = 'cilExitToApp', + cilBuilding = 'cilBuilding' } diff --git a/src/app/providers/login_checker.ts b/src/app/providers/login_checker.ts index 36becec..d3e8b63 100644 --- a/src/app/providers/login_checker.ts +++ b/src/app/providers/login_checker.ts @@ -1,28 +1,47 @@ import { Injectable } from '@angular/core'; +import { Router, NavigationEnd } from '@angular/router'; import { dataProvider } from './mikrowizard/data'; @Injectable() export class loginChecker { - private logged_in: boolean = false; - private pinging: boolean = false; + private is_logged_in: boolean = false; + private public_routes = ['/login', '/signup']; - constructor(private data_provider: dataProvider) { + constructor( + private router: Router, + private data_provider: dataProvider + ) { + this.router.events.subscribe((event) => { + if (event instanceof NavigationEnd) { + const currentRoute = event.url; + if (!this.is_logged_in && !this.public_routes.includes(currentRoute)) { + this.router.navigate(['/login']); + } + } + }); } - public isLoggedIn(): boolean { - return this.logged_in; + + isLoggedIn() { + return this.is_logged_in; } + + setStatus(status: boolean) { + this.is_logged_in = status; + } + load() { - var _self = this; - return this.data_provider.isLoggedIn().then(result => { - _self.logged_in = result; - }).catch(err => { - _self.logged_in = false; - }) - } - setStatus(status: boolean): void { - this.logged_in = status; - } - setPinging(ping: boolean): void { - this.pinging = ping; + return new Promise((resolve, reject) => { + this.data_provider.getSessionInfo().then(res => { + if ('uid' in res && res['uid']) { + this.is_logged_in = true; + } else { + this.is_logged_in = false; + } + resolve(true); + }).catch(err => { + this.is_logged_in = false; + resolve(true); + }); + }); } } \ No newline at end of file diff --git a/src/app/providers/mikrowizard/data.ts b/src/app/providers/mikrowizard/data.ts index ba6c8a4..8c77fbc 100644 --- a/src/app/providers/mikrowizard/data.ts +++ b/src/app/providers/mikrowizard/data.ts @@ -1,5 +1,5 @@ - import { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; // import { MikroWizardrpcProvider } from '../MikroWizardrpc/MikroWizardrpc'; import { MikroWizardProvider } from './provider'; @@ -10,13 +10,15 @@ import { User } from './user'; export class dataProvider { // public serverUrl: string = "/api"; - public serverUrl: string = ""; + public serverUrl: string = "http://192.168.1.26:3000"; private db: string = "NothingImportant"; + private apiUrl: string = "/api"; constructor( // private http: HTTP, // public MikroWizardRPC: MikroWizardrpcProvider, public MikroWizardRPC: MikroWizardProvider, + private http: HttpClient ) { this.MikroWizardRPC.init({ MikroWizard_server: this.serverUrl @@ -628,4 +630,36 @@ export class dataProvider { getFullUrl(url: any) { return this.serverUrl + url; } + + signup(username: string, organization: string, email: string, password: string) { + var data = { + 'username': username, + 'organization': organization, + 'email': email, + 'password': password + } + return this.MikroWizardRPC.sendJsonRequest("/api/signup", data); + } + + async loginWithOffice365(tokenClaims: any): Promise { + try { + const data = { + tokenClaims: tokenClaims + }; + return this.MikroWizardRPC.sendJsonRequest("/api/auth/office365", data); + } catch (error) { + throw error; + } + } + + async loginWithApple(appleResponse: any): Promise { + try { + const data = { + appleResponse: appleResponse + }; + return this.MikroWizardRPC.sendJsonRequest("/api/auth/apple", data); + } catch (error) { + throw error; + } + } } diff --git a/src/app/providers/mikrowizard/provider.ts b/src/app/providers/mikrowizard/provider.ts index ac7deb4..6542332 100644 --- a/src/app/providers/mikrowizard/provider.ts +++ b/src/app/providers/mikrowizard/provider.ts @@ -124,7 +124,7 @@ export class MikroWizardProvider { let headers = { "Content-Type": "application/json", }; - return this.http.post(url, params, + return this.http.post(this.MikroWizard_server + url, params, {headers:this.headers,withCredentials:true} ).toPromise() .then(this.handleRequestErrors) @@ -145,7 +145,7 @@ export class MikroWizardProvider { let headers = { "Content-Type": "application/x-www-form-urlencoded", }; - return this.http.get( url, + return this.http.get( this.MikroWizard_server + url, { responseType: 'json' } ).toPromise() } diff --git a/src/app/views/dashboard/dashboard.component.html b/src/app/views/dashboard/dashboard.component.html index af6b2e9..2556092 100644 --- a/src/app/views/dashboard/dashboard.component.html +++ b/src/app/views/dashboard/dashboard.component.html @@ -1,224 +1,260 @@ - - - - Past 24 Hour Statics - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +
+
+

Dashboard

+
+ + +
+
+
+ + +
+ + +
+
+ + + + Past 24 Hour Statistics + + + + - -
-
Total users
-
{{stats['Users']}}
-
+ + + + + + - -
-
Total Devices
-
{{stats['Devices']}}
-
+ + + + + + - -
-
Total Events
-
{{stats['Events']}}
-
+ + + + + + - -
-
Total Auth Logs
-
{{stats['Auth']}}
-
+ + + + + + - -
-
Total Acc Logs
-
{{stats['Acc']}}
-
+ + + + + +
- - -
- - + + +
+
- - - - -

Total Devices Traffic

-
- -
- - - + +
+
+ + + +

Total Devices Traffic

+ +
+ + + + + + + + + + + + + + + +
+
+
- - - - - - - -
- -
-
- - -
-
- - - -
-

Version and Serial information

- -
-
-
- Serial: {{ stats['serial'] }} - Copy -
- Not Registred - License Validation failed -
-
-
- Serial: {{ stats['serial'] }} - Copy -
- Unable connect to server/Check server internet connection -
-
-
- Serial: {{ stats['serial'] }} - Copy -
- Registred - License Type : {{stats['license']}} - Manual update - Auto update -
-
- Your Mikroman version : {{stats['version']}} - - - - - Your Mikrofront version : {{front_version}} - - - - -
-

License User name is not set in settings read more!

-

Serial number not submitedread more!

- - -
-
- - - - - - - {{slide.title}} -
-
{{slide.title}}
-

+ +
+
+ + + +
{{widget.title}}
+ +
+ + +
+
+ +

{{widget.data?.usage || 0}}%

+

CPU Usage

- - - - - - - - +
+ + +
+
+
+ {{widget.data?.usage || 0}}% +
+
+

Memory Usage: {{widget.data?.used || 0}}GB / {{widget.data?.total || 0}}GB

+
+ + +
+
+
+ {{widget.data?.usage || 0}}% +
+
+

Disk Usage: {{widget.data?.used || 0}}GB / {{widget.data?.total || 0}}GB

+
+ + +
+
+
+

{{widget.data?.download || 0}}

+ Download +
+
+

{{widget.data?.upload || 0}}

+ Upload +
+
+
+
+
+
+
+ +
+
+ + + +
{{view.name}}
+ +
+ +

Custom view content will be loaded here...

+
+ +
+
+
+
+
+ +
+ + + + +
ADD WIZARD
+ +
+ +
+
+ + +
+
+ + +
+
+
+ + + + +
+ + + + +
ADD VIEW
+ +
+ +
+
+ + +
+
+ + +
+
+
+ + + + +
+ +
Please Confirm Mikroman Update
@@ -246,9 +282,7 @@
- - + + \ No newline at end of file diff --git a/src/app/views/dashboard/dashboard.component.scss b/src/app/views/dashboard/dashboard.component.scss new file mode 100644 index 0000000..257b694 --- /dev/null +++ b/src/app/views/dashboard/dashboard.component.scss @@ -0,0 +1,443 @@ +// Dashboard Header Styles +.dashboard-header { + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + color: white; + padding: 25px; + border-radius: 12px; + margin-bottom: 25px; + box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4); + + h2 { + margin: 0; + font-weight: 600; + text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + } +} + +.dashboard-controls { + .dropdown-toggle { + background: rgba(255, 255, 255, 0.2); + border: 2px solid rgba(255, 255, 255, 0.3); + color: white; + border-radius: 25px; + padding: 12px 24px; + font-weight: 500; + transition: all 0.3s ease; + backdrop-filter: blur(10px); + + &:hover { + background: rgba(255, 255, 255, 0.3); + border-color: rgba(255, 255, 255, 0.5); + transform: translateY(-2px); + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2); + } + + i { + margin-right: 8px; + } + } + + .dropdown-menu { + border: none; + border-radius: 12px; + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15); + padding: 8px 0; + margin-top: 8px; + + .dropdown-item { + padding: 12px 20px; + font-weight: 500; + transition: all 0.2s ease; + + &:hover { + background: #f8f9fa; + color: #495057; + } + + i { + margin-right: 10px; + width: 16px; + } + + &.text-danger:hover { + background: #dc3545; + color: white; + } + } + } +} + +// Dashboard Grid Layout +.dashboard-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(350px, 1fr)); + grid-gap: 20px; + grid-auto-rows: minmax(250px, auto); + margin-bottom: 20px; + + @media (max-width: 768px) { + grid-template-columns: 1fr; + grid-gap: 15px; + } +} + +// Widget Styles +.dashboard-widget { + position: relative; + transition: all 0.3s ease; + + &:hover { + transform: translateY(-2px); + } +} + +.widget-container { + position: relative; + height: 100%; + transition: all 0.3s ease; + + .card { + border: none; + border-radius: 12px; + box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1); + transition: all 0.3s ease; + overflow: hidden; + + &:hover { + box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15); + } + + .card-header { + background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%); + border-bottom: 1px solid #dee2e6; + padding: 15px 20px; + font-weight: 600; + color: #495057; + } + + .card-body { + padding: 20px; + } + } +} + +.widget-remove-btn { + position: absolute; + top: 15px; + right: 15px; + z-index: 10; + border-radius: 50%; + width: 32px; + height: 32px; + padding: 0; + display: flex; + align-items: center; + justify-content: center; + border: none; + background: #dc3545; + color: white; + font-size: 14px; + opacity: 0; + transition: all 0.3s ease; + transform: scale(0.8); + + &:hover { + background: #c82333; + transform: scale(1.1); + } +} + +.widget-drag-handle { + cursor: move; + color: #6c757d; + transition: color 0.2s ease; + + &:hover { + color: #495057; + } +} + +// Remove Mode Styles +.remove-mode { + .widget-container { + border: 2px dashed #dc3545; + border-radius: 12px; + background: rgba(220, 53, 69, 0.05); + + &:hover { + background: rgba(220, 53, 69, 0.1); + } + } + + .widget-remove-btn { + opacity: 1; + transform: scale(1); + } +} + +// Custom Widget Styles +.cpu-widget, .memory-widget, .disk-widget, .network-widget { + .progress { + border-radius: 8px; + overflow: hidden; + + .progress-bar { + transition: width 0.6s ease; + } + } + + canvas { + max-width: 100%; + height: auto; + } + + h3 { + color: #495057; + font-weight: 700; + margin: 0; + } + + .text-muted { + color: #6c757d !important; + font-size: 0.9rem; + } +} + +.network-widget { + .row > div { + padding: 15px; + border-radius: 8px; + margin: 5px; + background: #f8f9fa; + transition: all 0.3s ease; + + &:hover { + background: #e9ecef; + transform: translateY(-2px); + } + + h4 { + margin: 0; + font-weight: 600; + } + } +} + +// Modal Styles +.modal-content { + border: none; + border-radius: 12px; + overflow: hidden; + box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3); +} + +.modal-header { + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + color: white; + border-bottom: none; + padding: 20px 25px; + + h5 { + margin: 0; + font-weight: 600; + } + + .btn-close { + filter: brightness(0) invert(1); + opacity: 0.8; + + &:hover { + opacity: 1; + } + } +} + +.modal-body { + padding: 25px; + + .form-label { + font-weight: 600; + color: #495057; + margin-bottom: 8px; + } + + .form-control, .form-select { + border-radius: 8px; + border: 2px solid #e9ecef; + padding: 12px 15px; + transition: all 0.3s ease; + + &:focus { + border-color: #667eea; + box-shadow: 0 0 0 0.2rem rgba(102, 126, 234, 0.25); + } + } +} + +.modal-footer { + border-top: 1px solid #e9ecef; + padding: 20px 25px; + + .btn { + border-radius: 8px; + padding: 10px 20px; + font-weight: 500; + transition: all 0.3s ease; + + &.btn-primary { + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + border: none; + + &:hover:not(:disabled) { + transform: translateY(-2px); + box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4); + } + } + + &.btn-secondary { + border: 2px solid #6c757d; + color: #6c757d; + background: transparent; + + &:hover { + background: #6c757d; + color: white; + } + } + } +} + +// Stats Widget Specific Styles +.stats-widget { + .card-footer { + background: #f8f9fa; + border-top: 1px solid #dee2e6; + padding: 20px; + + .border-start { + border-left-width: 4px !important; + transition: all 0.3s ease; + + &:hover { + transform: translateX(5px); + } + } + } +} + +// Traffic Chart Widget +.traffic-widget { + .btn-group { + .btn { + border-radius: 6px; + font-size: 0.875rem; + padding: 8px 12px; + transition: all 0.3s ease; + + &.active { + background: #667eea; + border-color: #667eea; + } + } + } +} + +// Responsive Design +@media (max-width: 1200px) { + .dashboard-grid { + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + } +} + +@media (max-width: 768px) { + .dashboard-header { + padding: 20px; + text-align: center; + + .d-flex { + flex-direction: column; + gap: 15px; + } + } + + .dashboard-grid { + grid-template-columns: 1fr; + grid-gap: 15px; + } + + .widget-container .card { + .card-header { + padding: 12px 15px; + } + + .card-body { + padding: 15px; + } + } +} + +// Animation Keyframes +@keyframes slideInUp { + from { + opacity: 0; + transform: translateY(30px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes pulse { + 0% { + transform: scale(1); + } + 50% { + transform: scale(1.05); + } + 100% { + transform: scale(1); + } +} + +// Apply animations +.dashboard-widget { + animation: slideInUp 0.5s ease-out; +} + +.widget-remove-btn:hover { + animation: pulse 0.3s ease-in-out; +} + +// Loading States +.loading-widget { + .card-body { + display: flex; + align-items: center; + justify-content: center; + min-height: 200px; + + .spinner-border { + color: #667eea; + } + } +} + +// Success/Error States +.widget-success { + .card { + border-left: 4px solid #28a745; + } +} + +.widget-warning { + .card { + border-left: 4px solid #ffc107; + } +} + +.widget-error { + .card { + border-left: 4px solid #dc3545; + } +} \ No newline at end of file diff --git a/src/app/views/dashboard/dashboard.component.ts b/src/app/views/dashboard/dashboard.component.ts index f5cc1a7..d756aa8 100644 --- a/src/app/views/dashboard/dashboard.component.ts +++ b/src/app/views/dashboard/dashboard.component.ts @@ -1,14 +1,43 @@ import { Component, OnInit } from "@angular/core"; -import { UntypedFormControl, UntypedFormGroup } from "@angular/forms"; +import { UntypedFormControl, UntypedFormGroup, FormBuilder, Validators } from "@angular/forms"; import { dataProvider } from "../../providers/mikrowizard/data"; import { loginChecker } from "../../providers/login_checker"; import { Router } from "@angular/router"; import { formatInTimeZone } from "date-fns-tz"; +interface DashboardWidget { + enabled: boolean; + position: { + col: string; + row: string; + }; +} + +interface CustomWidget { + id: string; + type: string; + title: string; + position: { + col: string; + row: string; + }; + data?: any; +} + +interface CustomView { + id: string; + name: string; + description?: string; + position: { + col: string; + row: string; + }; +} + @Component({ templateUrl: "dashboard.component.html", + styleUrls: ["./dashboard.component.scss"] }) - export class DashboardComponent implements OnInit { public uid: number; public uname: string; @@ -16,11 +45,29 @@ export class DashboardComponent implements OnInit { public copy_msg: any = false; public ConfirmModalVisible: boolean = false; public action: string = ""; - front_version=require('../../../../package.json').version; + public front_version = require('../../../../package.json').version; + + // Dashboard customization properties + public isRemoveMode: boolean = false; + public wizardModalVisible: boolean = false; + public viewModalVisible: boolean = false; + public wizardForm: UntypedFormGroup; + public viewForm: UntypedFormGroup; + + // Widget management + public widgets: { [key: string]: DashboardWidget } = { + stats: { enabled: true, position: { col: '1 / -1', row: '1' } }, + traffic: { enabled: true, position: { col: '1 / -1', row: '2' } } + }; + + public customWidgets: CustomWidget[] = []; + public customViews: CustomView[] = []; + constructor( private data_provider: dataProvider, private router: Router, - private login_checker: loginChecker + private login_checker: loginChecker, + private fb: FormBuilder ) { var _self = this; if (!this.login_checker.isLoggedIn()) { @@ -28,23 +75,33 @@ export class DashboardComponent implements OnInit { _self.router.navigate(["login"]); }, 100); } + this.data_provider.getSessionInfo().then((res) => { _self.uid = res.uid; _self.uname = res.name; _self.tz = res.tz; - const userId = _self.uid; }); - //get datagrid data - function isNotEmpty(value: any): boolean { - return value !== undefined && value !== null && value !== ""; - } + + // Initialize forms + this.wizardForm = this.fb.group({ + wizardType: ['', Validators.required], + wizardTitle: ['', Validators.required] + }); + + this.viewForm = this.fb.group({ + viewName: ['', Validators.required], + viewDescription: [''] + }); + + // Load saved dashboard configuration + this.loadDashboardConfig(); } public trafficRadioGroup = new UntypedFormGroup({ trafficRadio: new UntypedFormControl("5m"), }); + public chart_data: any = {}; - public Chartoptions = { responsive: true, plugins: { @@ -55,7 +112,6 @@ export class DashboardComponent implements OnInit { let label = context.dataset.label || ""; var res = context.parsed.y; let unitIndex = 0; - // if (res>8) res /=8; while (res >= 1024 && unitIndex < units.length - 1) { res /= 1024; unitIndex++; @@ -63,19 +119,14 @@ export class DashboardComponent implements OnInit { switch (context.dataset.unit) { case "rx": return "rx/s :" + res.toFixed(3) + " " + units[unitIndex]; - break; case "tx": return "tx/s :" + res.toFixed(3) + " " + units[unitIndex]; - break; case "rxp": return "rxp/s :" + context.parsed.y; - break; case "txp": return "txp/s :" + context.parsed.y; - break; default: return context.parsed.y; - break; } }, }, @@ -161,6 +212,7 @@ export class DashboardComponent implements OnInit { }, }, }; + public options: any; public delta: string = "5m"; public stats: any = false; @@ -169,8 +221,159 @@ export class DashboardComponent implements OnInit { this.options = this.Chartoptions; this.initStats(); this.initTrafficChart(); + this.startDataRefresh(); } + // Dashboard customization methods + showWizardModal(): void { + this.wizardModalVisible = true; + this.wizardForm.reset(); + } + + showViewModal(): void { + this.viewModalVisible = true; + this.viewForm.reset(); + } + + toggleRemoveMode(): void { + this.isRemoveMode = !this.isRemoveMode; + } + + commitWizard(): void { + if (this.wizardForm.valid) { + const formValue = this.wizardForm.value; + const newWidget: CustomWidget = { + id: this.generateId(), + type: formValue.wizardType, + title: formValue.wizardTitle, + position: this.getNextAvailablePosition(), + data: this.getInitialWidgetData(formValue.wizardType) + }; + + this.customWidgets.push(newWidget); + this.saveDashboardConfig(); + this.wizardModalVisible = false; + this.startWidgetDataRefresh(newWidget); + } + } + + commitView(): void { + if (this.viewForm.valid) { + const formValue = this.viewForm.value; + const newView: CustomView = { + id: this.generateId(), + name: formValue.viewName, + description: formValue.viewDescription, + position: this.getNextAvailablePosition() + }; + + this.customViews.push(newView); + this.saveDashboardConfig(); + this.viewModalVisible = false; + } + } + + removeWidget(widgetType: string): void { + this.widgets[widgetType].enabled = false; + this.saveDashboardConfig(); + } + + removeCustomWidget(index: number): void { + this.customWidgets.splice(index, 1); + this.saveDashboardConfig(); + } + + removeCustomView(index: number): void { + this.customViews.splice(index, 1); + this.saveDashboardConfig(); + } + + // Helper methods + private generateId(): string { + return 'widget_' + Math.random().toString(36).substr(2, 9); + } + + private getNextAvailablePosition(): { col: string; row: string } { + const totalWidgets = Object.keys(this.widgets).filter(key => this.widgets[key].enabled).length + + this.customWidgets.length + this.customViews.length; + const row = Math.floor(totalWidgets / 2) + 3; // Start after default widgets + const col = (totalWidgets % 2) === 0 ? '1 / 2' : '2 / 3'; + return { col, row: row.toString() }; + } + + private getInitialWidgetData(type: string): any { + switch (type) { + case 'cpu': + return { usage: 0 }; + case 'memory': + return { usage: 0, used: 0, total: 0 }; + case 'disk': + return { usage: 0, used: 0, total: 0 }; + case 'network': + return { download: '0 MB/s', upload: '0 MB/s' }; + default: + return {}; + } + } + + private loadDashboardConfig(): void { + const saved = localStorage.getItem('dashboard_config'); + if (saved) { + try { + const config = JSON.parse(saved); + this.widgets = { ...this.widgets, ...config.widgets }; + this.customWidgets = config.customWidgets || []; + this.customViews = config.customViews || []; + } catch (e) { + console.error('Error loading dashboard config:', e); + } + } + } + + private saveDashboardConfig(): void { + const config = { + widgets: this.widgets, + customWidgets: this.customWidgets, + customViews: this.customViews + }; + localStorage.setItem('dashboard_config', JSON.stringify(config)); + } + + private startDataRefresh(): void { + // Refresh widget data every 5 seconds + setInterval(() => { + this.customWidgets.forEach(widget => this.updateWidgetData(widget)); + }, 5000); + } + + private startWidgetDataRefresh(widget: CustomWidget): void { + this.updateWidgetData(widget); + } + + private updateWidgetData(widget: CustomWidget): void { + // Simulate data updates - in real implementation, fetch from API + switch (widget.type) { + case 'cpu': + widget.data.usage = Math.floor(Math.random() * 100); + break; + case 'memory': + widget.data.usage = Math.floor(Math.random() * 100); + widget.data.used = (Math.random() * 16).toFixed(1); + widget.data.total = 16; + break; + case 'disk': + widget.data.usage = Math.floor(Math.random() * 100); + widget.data.used = (Math.random() * 500).toFixed(1); + widget.data.total = 500; + break; + case 'network': + widget.data.download = (Math.random() * 100).toFixed(1) + ' MB/s'; + widget.data.upload = (Math.random() * 50).toFixed(1) + ' MB/s'; + break; + } + } + + // Original methods initTrafficChart(): void { var _self = this; this.data_provider.dashboard_traffic(this.delta).then((res) => { @@ -184,44 +387,45 @@ export class DashboardComponent implements OnInit { _self.chart_data = { datasets: res["data"]["datasets"], labels: labels }; }); } - initStats() { + + initStats(): void { var _self = this; - this.data_provider.dashboard_stats(true,this.front_version).then((res) => { + this.data_provider.dashboard_stats(true, this.front_version).then((res) => { _self.stats = res; }); } - copy_this() { - //show text copy to clipboard for 3 seconds + copy_this(): void { this.copy_msg = true; setTimeout(() => { this.copy_msg = false; }, 3000); } - // Traffic Chart setTrafficPeriod(value: string): void { this.trafficRadioGroup.setValue({ trafficRadio: value }); this.delta = value; this.initTrafficChart(); } - showConfirmModal(action: string) { + + showConfirmModal(action: string): void { this.action = action; - this.ConfirmModalVisible = true + this.ConfirmModalVisible = true; } - ConfirmAction() { + + ConfirmAction(): void { var _self = this; - this.data_provider.apply_update(this.action).then((res) => { - if (res["status"]=='success') { - if (_self.action=='update_mikroman') { - _self.stats['update_inprogress']=true; - } - if (_self.action=='update_mikrofront') { - _self.stats['front_update_inprogress']=true; - } - _self.action=""; - _self.ConfirmModalVisible = false; + this.data_provider.apply_update(this.action).then((res) => { + if (res["status"] == 'success') { + if (_self.action == 'update_mikroman') { + _self.stats['update_inprogress'] = true; } - }); + if (_self.action == 'update_mikrofront') { + _self.stats['front_update_inprogress'] = true; + } + _self.action = ""; + _self.ConfirmModalVisible = false; + } + }); } } diff --git a/src/app/views/pages/login/login.component.html b/src/app/views/pages/login/login.component.html index 77a0218..ef20267 100644 --- a/src/app/views/pages/login/login.component.html +++ b/src/app/views/pages/login/login.component.html @@ -1,62 +1,74 @@
- - - - - - - - - - - -
-

Login

-

Sign In to your account

- - - - - - - - - - - - - - - - - - - {{error_msg}} - - - - - -
-
-
+ + + + + + + + + + + +
+

Sign in

+

Don't have an account?

+ + + + + + + + + + + + + + + + + + + {{error_msg}} + + + + + +

or continue with

+
+ + + + + + +
+
+
+
-
-
-
-
-
+ +
+
+ + \ No newline at end of file diff --git a/src/app/views/pages/login/login.component.scss b/src/app/views/pages/login/login.component.scss index e69de29..41b0c81 100644 --- a/src/app/views/pages/login/login.component.scss +++ b/src/app/views/pages/login/login.component.scss @@ -0,0 +1,38 @@ +$primary-color: #303c54; +$secondary-color: #ffffff; +$border-color: #000000; +$hover-bg-color: #f8f9fa; +$signin-bg-color: #635DFF; +$signin-hover-color: #4a46d1; + +.btn-social { + color: $primary-color; + background-color: $secondary-color; + border: 1px solid $border-color; + + &:hover { + background-color: $hover-bg-color; + } +} + +.signup-link { + color: $primary-color; + text-decoration: none; + font-weight: bold; +} + +.heading-text { + font-size: 1.5rem; + font-weight: bold; + color: $primary-color; +} + +.btn-signin { + background-color: $signin-bg-color; + color: $secondary-color; + font-weight: bold; + + &:hover { + background-color: $signin-hover-color; + } +} diff --git a/src/app/views/pages/login/login.component.ts b/src/app/views/pages/login/login.component.ts index b3910e7..2929e51 100644 --- a/src/app/views/pages/login/login.component.ts +++ b/src/app/views/pages/login/login.component.ts @@ -1,15 +1,25 @@ -import { Component } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; import { Router } from '@angular/router'; import { dataProvider } from '../../../providers/mikrowizard/data'; import { loginChecker } from '../../../providers/login_checker'; import { Validators, FormControl, FormGroup} from '@angular/forms'; +import { MsalService } from '@azure/msal-angular'; +import { loginRequest } from '../../../auth/msal-config'; +import { appleConfig } from '../../../auth/apple-config'; +import appleSignin from 'apple-signin-auth'; + +declare global { + interface Window { + AppleID: any; + } +} @Component({ selector: 'app-login', templateUrl: './login.component.html', styleUrls: ['./login.component.scss'] }) -export class LoginComponent { +export class LoginComponent implements OnInit { public loginForm: FormGroup; public forgotForm: FormGroup; public error_msg: string = ""; @@ -24,10 +34,21 @@ export class LoginComponent { private router: Router, private data_provider: dataProvider, private login_checker: loginChecker, + private msalService: MsalService ) { this.createForm(); }; + ngOnInit() { + // Check if user is already logged in with MSAL + if (this.msalService.instance.getActiveAccount()) { + this.handleMsalLogin(); + } + + // Initialize Apple Sign In + this.initializeAppleSignIn(); + } + createForm() { this.loginForm = new FormGroup({ username: new FormControl(''), @@ -64,4 +85,86 @@ export class LoginComponent { }); } + loginWithOffice365() { + this.msalService.loginPopup(loginRequest) + .subscribe({ + next: (result) => { + this.handleMsalLogin(); + }, + error: (error) => { + this.error_msg = "Error during Office 365 login: " + error.message; + console.error('MSAL login error:', error); + } + }); + } + + private handleMsalLogin() { + const account = this.msalService.instance.getActiveAccount(); + if (account) { + this.data_provider.loginWithOffice365(account.idTokenClaims) + .then(res => { + if ('uid' in res && res['uid']) { + this.error_msg = ""; + this.login_checker.setStatus(true); + this.router.navigate(['/'], { replaceUrl: true }); + } else { + this.error_msg = 'Error: Problem with Office 365 login'; + } + }) + .catch(err => { + this.error_msg = "Connection with backend broken!"; + console.error('Backend error:', err); + }); + } + } + + private initializeAppleSignIn() { + // Load Apple Sign In script + const script = document.createElement('script'); + script.src = 'https://appleid.cdn-apple.com/appleauth/static/jsapi/appleid/1/en_US/appleid.auth.js'; + script.onload = () => { + window.AppleID.auth.init({ + clientId: appleConfig.clientId, + scope: appleConfig.scope, + redirectURI: appleConfig.redirectURI, + state: appleConfig.state, + usePopup: appleConfig.usePopup + }); + }; + document.body.appendChild(script); + console.log(appleConfig); + } + + + + + loginWithApple() { + window.AppleID.auth.signIn() + .then((response: any) => { + // Handle successful sign in + this.handleAppleLogin(response); + }) + .catch((error: any) => { + this.error_msg = "Error during Apple login: " + error.message; + console.error('Apple login error:', error); + }); + } + + private handleAppleLogin(response: any) { + console.log(JSON.stringify(response)); + this.data_provider.loginWithApple(response) + .then(res => { + if ('uid' in res && res['uid']) { + this.error_msg = ""; + this.login_checker.setStatus(true); + this.router.navigate(['/'], { replaceUrl: true }); + } else { + this.error_msg = 'Error: Problem with Apple login'; + } + }) + .catch(err => { + this.error_msg = "Connection with backend broken!"; + console.error('Backend error:', err); + }); + } } diff --git a/src/app/views/pages/pages-routing.module.ts b/src/app/views/pages/pages-routing.module.ts index b72022b..8cf6d83 100644 --- a/src/app/views/pages/pages-routing.module.ts +++ b/src/app/views/pages/pages-routing.module.ts @@ -3,6 +3,7 @@ import { RouterModule, Routes } from '@angular/router'; import { Page404Component } from './page404/page404.component'; import { Page500Component } from './page500/page500.component'; import { LoginComponent } from './login/login.component'; +import { SignupComponent } from './signup/signup.component'; const routes: Routes = [ { @@ -26,6 +27,13 @@ const routes: Routes = [ title: 'Login Page' } }, + { + path: 'signup', + component: SignupComponent, + data: { + title: 'Signup Page' + } + }, ]; @NgModule({ diff --git a/src/app/views/pages/pages.module.ts b/src/app/views/pages/pages.module.ts index 6c3250f..9778d57 100644 --- a/src/app/views/pages/pages.module.ts +++ b/src/app/views/pages/pages.module.ts @@ -3,16 +3,17 @@ import { CommonModule } from '@angular/common'; import { PagesRoutingModule } from './pages-routing.module'; import { LoginComponent } from './login/login.component'; +import { SignupComponent } from './signup/signup.component'; import { Page404Component } from './page404/page404.component'; import { Page500Component } from './page500/page500.component'; import { ButtonModule, CardModule, FormModule, GridModule } from '@coreui/angular'; import { IconModule } from '@coreui/icons-angular'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; - @NgModule({ declarations: [ LoginComponent, + SignupComponent, Page404Component, Page500Component ], diff --git a/src/app/views/pages/signup/signup.component.html b/src/app/views/pages/signup/signup.component.html new file mode 100644 index 0000000..1e6fb51 --- /dev/null +++ b/src/app/views/pages/signup/signup.component.html @@ -0,0 +1,115 @@ +
+ + + + + + + + + + + +
+

Create your account

+

Already have an account?

+ + + + + + + +
+ Username is required +
+ + + + + + + +
+ Organization field is required +
+ + + + + + + +
+ Email is + required + Please enter a valid + email +
+ + + + + + + +
+ Password is + required + Password must be at + least 6 characters +
+ + + + + + + +
+ Please confirm your password +
+ + {{error_msg}} + {{success_msg}} + + + + + +

or continue with

+
+ + + + + + +
+
+
+
+
+
+
+
\ No newline at end of file diff --git a/src/app/views/pages/signup/signup.component.scss b/src/app/views/pages/signup/signup.component.scss new file mode 100644 index 0000000..a028390 --- /dev/null +++ b/src/app/views/pages/signup/signup.component.scss @@ -0,0 +1,38 @@ +$primary-color: #303c54; +$secondary-color: #ffffff; +$border-color: #000000; +$hover-bg-color: #f8f9fa; +$signin-bg-color: #635DFF; +$signin-hover-color: #4a46d1; + +.signup-link { + color: $primary-color; + text-decoration: none; + font-weight: bold; +} + +.heading-text { + font-size: 1.5rem; + font-weight: bold; + color: $primary-color; +} + +.btn-signin { + background-color: $signin-bg-color; + color: $secondary-color; + font-weight: bold; + + &:hover { + background-color: $signin-hover-color; + } +} + +.btn-social { + color: $primary-color; + background-color: $secondary-color; + border: 1px solid $border-color; + + &:hover { + background-color: $hover-bg-color; + } +} \ No newline at end of file diff --git a/src/app/views/pages/signup/signup.component.ts b/src/app/views/pages/signup/signup.component.ts new file mode 100644 index 0000000..3b1c8df --- /dev/null +++ b/src/app/views/pages/signup/signup.component.ts @@ -0,0 +1,61 @@ +import { Component } from '@angular/core'; +import { Router } from '@angular/router'; +import { dataProvider } from '../../../providers/mikrowizard/data'; +import { Validators, FormControl, FormGroup } from '@angular/forms'; + +@Component({ + selector: 'app-signup', + templateUrl: './signup.component.html', + styleUrls: ['./signup.component.scss'] +}) +export class SignupComponent { + public signupForm: FormGroup; + public error_msg: string = ""; + public success_msg: string = ""; + public submitted = false; + + constructor( + private router: Router, + private data_provider: dataProvider, + ) { + this.createForm(); + } + + createForm() { + this.signupForm = new FormGroup({ + username: new FormControl('', [Validators.required]), + organization: new FormControl('', [Validators.required]), + email: new FormControl('', [Validators.required, Validators.email]), + password: new FormControl('', [Validators.required, Validators.minLength(6)]), + confirmPassword: new FormControl('', [Validators.required]) + }); + } + + onClickSubmit() { + this.submitted = true; + if (this.signupForm.invalid) { + return; + } + + const formData = this.signupForm.value; + if (formData.password !== formData.confirmPassword) { + this.error_msg = "Passwords do not match"; + return; + } + + this.data_provider.signup(formData.username, formData.organization, formData.email, formData.password) + .then(res => { + if (res['status'] === 'success') { + this.success_msg = "Registration successful! Please login."; + setTimeout(() => { + this.router.navigate(['/login']); + }, 2000); + } else { + this.error_msg = res['err'] || 'Registration failed'; + } + }) + .catch(err => { + this.error_msg = "Connection with backend broken!"; + }); + } +} \ No newline at end of file diff --git a/src/assets/Pause GIF Image.gif b/src/assets/Pause GIF Image.gif new file mode 100644 index 0000000000000000000000000000000000000000..b2ddfa11b89ea70eaca7bf9e4c26463543521830 GIT binary patch literal 45012 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 Wk=LBun(duc&5y@}c