mirror of
https://github.com/MikroWizard/mikrofront.git
synced 2025-07-23 20:34:33 +02:00
add login with google and change
This commit is contained in:
parent
10d4cff4a4
commit
d8ed8ce2ce
25 changed files with 1946 additions and 468 deletions
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
546
package-lock.json
generated
546
package-lock.json
generated
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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" : ""}
|
||||
}
|
||||
}
|
|
@ -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'}
|
||||
];
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
7
src/app/auth/apple-config.ts
Normal file
7
src/app/auth/apple-config.ts
Normal file
|
@ -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
|
||||
};
|
19
src/app/auth/msal-config.ts
Normal file
19
src/app/auth/msal-config.ts
Normal file
|
@ -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"]
|
||||
};
|
|
@ -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'
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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<any> {
|
||||
try {
|
||||
const data = {
|
||||
tokenClaims: tokenClaims
|
||||
};
|
||||
return this.MikroWizardRPC.sendJsonRequest("/api/auth/office365", data);
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async loginWithApple(appleResponse: any): Promise<any> {
|
||||
try {
|
||||
const data = {
|
||||
appleResponse: appleResponse
|
||||
};
|
||||
return this.MikroWizardRPC.sendJsonRequest("/api/auth/apple", data);
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -1,224 +1,260 @@
|
|||
<c-row *ngIf="stats">
|
||||
<c-col xs>
|
||||
<c-card *ngIf="stats" class="mb-1">
|
||||
<c-card-header>Past 24 Hour Statics</c-card-header>
|
||||
<c-card-body>
|
||||
<c-row>
|
||||
<c-col md="12" xl="12" xs="12">
|
||||
<c-row>
|
||||
<c-col class="mb-sm-1 mb-0">
|
||||
<c-widget-stat-f [title]="'Failed Logins'" class="mb-1" color="danger" padding
|
||||
value="{{stats['FailedLogins']}}">
|
||||
<ng-template cTemplateId="widgetIconTemplate">
|
||||
<i style="font-size: 2em;" class="fa-solid fa-person-circle-exclamation"></i>
|
||||
</ng-template>
|
||||
</c-widget-stat-f>
|
||||
</c-col>
|
||||
<c-col class="mb-sm-1 mb-0">
|
||||
<c-widget-stat-f [title]="'Success Logins'" class="mb-1" color="success" padding
|
||||
value="{{stats['SuccessfulLogins']}}">
|
||||
<ng-template cTemplateId="widgetIconTemplate">
|
||||
<i style="font-size: 2em;" class="fa-solid fa-arrow-right-to-bracket"></i>
|
||||
</ng-template>
|
||||
</c-widget-stat-f>
|
||||
</c-col>
|
||||
<c-col class="mb-sm-1 mb-0">
|
||||
<c-widget-stat-f [title]="'Critical Events'" class="mb-1" color="danger" padding
|
||||
value="{{stats['Critical']}}">
|
||||
<ng-template cTemplateId="widgetIconTemplate">
|
||||
<i style="font-size: 2em;" class="fa-solid fa-skull-crossbones"></i>
|
||||
</ng-template>
|
||||
</c-widget-stat-f>
|
||||
</c-col>
|
||||
<c-col class="mb-sm-1 mb-0">
|
||||
<c-widget-stat-f [title]="'Warning Events'" class="mb-1" color="warning" padding
|
||||
value="{{stats['Warning']}}">
|
||||
<ng-template cTemplateId="widgetIconTemplate">
|
||||
<i style="font-size: 2em;" class="fa-solid fa-triangle-exclamation"></i>
|
||||
</ng-template>
|
||||
</c-widget-stat-f>
|
||||
</c-col>
|
||||
<c-col class="mb-sm-1 mb-0">
|
||||
<c-widget-stat-f [title]="'Info Events'" class="mb-1" color="info" padding value="{{stats['Info']}}">
|
||||
<ng-template cTemplateId="widgetIconTemplate">
|
||||
<i style="font-size: 2em;" class="fa-solid fa-circle-info"></i>
|
||||
</ng-template>
|
||||
</c-widget-stat-f>
|
||||
</c-col>
|
||||
</c-row>
|
||||
</c-col>
|
||||
</c-row>
|
||||
</c-card-body>
|
||||
<c-card-footer class="pb-0">
|
||||
<c-col xs>
|
||||
<c-row>
|
||||
<!-- Dashboard Header with Customization Controls -->
|
||||
<div class="dashboard-header mb-3">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<h2>Dashboard</h2>
|
||||
<div class="dashboard-controls">
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-primary dropdown-toggle" type="button" id="dashboardActions" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<i class="fa-solid fa-plus"></i> Customize Dashboard
|
||||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="dashboardActions">
|
||||
<li><a class="dropdown-item" href="#" (click)="showWizardModal()"><i class="fa-solid fa-magic"></i> ADD WIZARD</a></li>
|
||||
<li><a class="dropdown-item" href="#" (click)="showViewModal()"><i class="fa-solid fa-eye"></i> ADD VIEW</a></li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li><a class="dropdown-item text-danger" href="#" (click)="toggleRemoveMode()"><i class="fa-solid fa-trash"></i> REMOVE</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<button *ngIf="isRemoveMode" class="btn btn-secondary ms-2" (click)="toggleRemoveMode()">
|
||||
<i class="fa-solid fa-times"></i> Cancel Remove
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Dashboard Grid Container -->
|
||||
<div class="dashboard-grid" [class.remove-mode]="isRemoveMode">
|
||||
|
||||
<!-- Default Stats Widget -->
|
||||
<div class="dashboard-widget" *ngIf="widgets['stats'].enabled" [style.grid-column]="widgets['stats'].position.col" [style.grid-row]="widgets['stats'].position.row">
|
||||
<div class="widget-container">
|
||||
<button *ngIf="isRemoveMode" class="btn btn-danger btn-sm widget-remove-btn" (click)="removeWidget('stats')">
|
||||
<i class="fa-solid fa-times"></i>
|
||||
</button>
|
||||
<c-card class="h-100">
|
||||
<c-card-header class="d-flex justify-content-between align-items-center">
|
||||
<span>Past 24 Hour Statistics</span>
|
||||
<i class="fa-solid fa-grip-vertical widget-drag-handle"></i>
|
||||
</c-card-header>
|
||||
<c-card-body>
|
||||
<c-row *ngIf="stats">
|
||||
<c-col md="12" xl="12" xs="12">
|
||||
<c-row>
|
||||
<c-col class="mb-0 pb-0">
|
||||
<div class="border-start border-start-4 border-start-info pt-1 px-3 mb-1">
|
||||
<div class="text-medium-emphasis small">Total users</div>
|
||||
<div class="fs-6 fw-semibold">{{stats['Users']}}</div>
|
||||
</div>
|
||||
<c-col class="mb-sm-1 mb-0">
|
||||
<c-widget-stat-f [title]="'Failed Logins'" class="mb-1" color="danger" padding
|
||||
value="{{stats['FailedLogins']}}">
|
||||
<ng-template cTemplateId="widgetIconTemplate">
|
||||
<i style="font-size: 2em;" class="fa-solid fa-person-circle-exclamation"></i>
|
||||
</ng-template>
|
||||
</c-widget-stat-f>
|
||||
</c-col>
|
||||
<c-col class="mb-0 pb-0">
|
||||
<div class="border-start border-start-4 border-start-warning pt-1 px-3 mb-1">
|
||||
<div class="text-medium-emphasis small">Total Devices</div>
|
||||
<div class="fs-6 fw-semibold">{{stats['Devices']}}</div>
|
||||
</div>
|
||||
<c-col class="mb-sm-1 mb-0">
|
||||
<c-widget-stat-f [title]="'Success Logins'" class="mb-1" color="success" padding
|
||||
value="{{stats['SuccessfulLogins']}}">
|
||||
<ng-template cTemplateId="widgetIconTemplate">
|
||||
<i style="font-size: 2em;" class="fa-solid fa-arrow-right-to-bracket"></i>
|
||||
</ng-template>
|
||||
</c-widget-stat-f>
|
||||
</c-col>
|
||||
<c-col class="mb-0 pb-0">
|
||||
<div class="border-start border-start-4 border-start-success pt-1 px-3 mb-1">
|
||||
<div class="text-medium-emphasis small">Total Events</div>
|
||||
<div class="fs-6 fw-semibold">{{stats['Events']}}</div>
|
||||
</div>
|
||||
<c-col class="mb-sm-1 mb-0">
|
||||
<c-widget-stat-f [title]="'Critical Events'" class="mb-1" color="danger" padding
|
||||
value="{{stats['Critical']}}">
|
||||
<ng-template cTemplateId="widgetIconTemplate">
|
||||
<i style="font-size: 2em;" class="fa-solid fa-skull-crossbones"></i>
|
||||
</ng-template>
|
||||
</c-widget-stat-f>
|
||||
</c-col>
|
||||
<c-col class="mb-0 pb-0">
|
||||
<div class="border-start border-start-4 border-start-success pt-1 px-3 mb-1">
|
||||
<div class="text-medium-emphasis small">Total Auth Logs</div>
|
||||
<div class="fs-6 fw-semibold">{{stats['Auth']}}</div>
|
||||
</div>
|
||||
<c-col class="mb-sm-1 mb-0">
|
||||
<c-widget-stat-f [title]="'Warning Events'" class="mb-1" color="warning" padding
|
||||
value="{{stats['Warning']}}">
|
||||
<ng-template cTemplateId="widgetIconTemplate">
|
||||
<i style="font-size: 2em;" class="fa-solid fa-triangle-exclamation"></i>
|
||||
</ng-template>
|
||||
</c-widget-stat-f>
|
||||
</c-col>
|
||||
<c-col class="mb-0 pb-0">
|
||||
<div class="border-start border-start-4 border-start-success pt-1 px-3 mb-1">
|
||||
<div class="text-medium-emphasis small">Total Acc Logs</div>
|
||||
<div class="fs-6 fw-semibold">{{stats['Acc']}}</div>
|
||||
</div>
|
||||
<c-col class="mb-sm-1 mb-0">
|
||||
<c-widget-stat-f [title]="'Info Events'" class="mb-1" color="info" padding value="{{stats['Info']}}">
|
||||
<ng-template cTemplateId="widgetIconTemplate">
|
||||
<i style="font-size: 2em;" class="fa-solid fa-circle-info"></i>
|
||||
</ng-template>
|
||||
</c-widget-stat-f>
|
||||
</c-col>
|
||||
</c-row>
|
||||
</c-col>
|
||||
</c-row>
|
||||
</c-col>
|
||||
</c-card-footer>
|
||||
</c-card>
|
||||
</c-col>
|
||||
</c-row>
|
||||
</c-card-body>
|
||||
</c-card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<c-card class="mb-1">
|
||||
<c-card-body>
|
||||
<c-row>
|
||||
<c-col sm="5">
|
||||
<h4 class="card-title mb-0" id="traffic">Total Devices Traffic</h4>
|
||||
</c-col>
|
||||
<c-col class="d-none d-md-block" sm="7">
|
||||
<form [formGroup]="trafficRadioGroup">
|
||||
<c-button-group class="float-end me-3" role="group">
|
||||
<input class="btn-check" formControlName="trafficRadio" type="radio" value="5m" />
|
||||
<label (click)="setTrafficPeriod('5m')" cButton cFormCheckLabel color="secondary" variant="outline">5
|
||||
Minues</label>
|
||||
<!-- Traffic Chart Widget -->
|
||||
<div class="dashboard-widget" *ngIf="widgets['traffic'].enabled" [style.grid-column]="widgets['traffic'].position.col" [style.grid-row]="widgets['traffic'].position.row">
|
||||
<div class="widget-container">
|
||||
<button *ngIf="isRemoveMode" class="btn btn-danger btn-sm widget-remove-btn" (click)="removeWidget('traffic')">
|
||||
<i class="fa-solid fa-times"></i>
|
||||
</button>
|
||||
<c-card class="h-100">
|
||||
<c-card-header class="d-flex justify-content-between align-items-center">
|
||||
<h4 class="card-title mb-0">Total Devices Traffic</h4>
|
||||
<i class="fa-solid fa-grip-vertical widget-drag-handle"></i>
|
||||
</c-card-header>
|
||||
<c-card-body>
|
||||
<form [formGroup]="trafficRadioGroup" class="mb-3">
|
||||
<c-button-group class="float-end me-3" role="group">
|
||||
<input class="btn-check" formControlName="trafficRadio" type="radio" value="5m" />
|
||||
<label (click)="setTrafficPeriod('5m')" cButton cFormCheckLabel color="secondary" variant="outline">5 Minutes</label>
|
||||
<input class="btn-check" formControlName="trafficRadio" type="radio" value="1h" />
|
||||
<label (click)="setTrafficPeriod('1h')" cButton cFormCheckLabel color="secondary" variant="outline">Hourly</label>
|
||||
<input class="btn-check" formControlName="trafficRadio" type="radio" value="daily" />
|
||||
<label (click)="setTrafficPeriod('daily')" cButton cFormCheckLabel color="secondary" variant="outline">Daily</label>
|
||||
<input class="btn-check" formControlName="trafficRadio" type="radio" value="live" />
|
||||
<label (click)="setTrafficPeriod('live')" cButton cFormCheckLabel color="secondary" variant="outline">Live</label>
|
||||
</c-button-group>
|
||||
</form>
|
||||
<c-chart [data]="chart_data" [options]="options" [height]="250" type="line"></c-chart>
|
||||
</c-card-body>
|
||||
</c-card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input class="btn-check" formControlName="trafficRadio" type="radio" value="1h" />
|
||||
<label (click)="setTrafficPeriod('1h')" cButton cFormCheckLabel color="secondary"
|
||||
variant="outline">Hourly</label>
|
||||
|
||||
<input class="btn-check" formControlName="trafficRadio" type="radio" value="daily" />
|
||||
<label (click)="setTrafficPeriod('daily')" cButton cFormCheckLabel color="secondary"
|
||||
variant="outline">Daily</label>
|
||||
<input class="btn-check" formControlName="trafficRadio" type="radio" value="live" />
|
||||
<label (click)="setTrafficPeriod('live')" cButton cFormCheckLabel color="secondary"
|
||||
variant="outline">Live</label>
|
||||
</c-button-group>
|
||||
</form>
|
||||
</c-col>
|
||||
</c-row>
|
||||
<c-chart [data]="chart_data" [options]="options" [height]="250" type="line">
|
||||
</c-chart>
|
||||
</c-card-body>
|
||||
</c-card>
|
||||
<c-row>
|
||||
<c-col xl="6" *ngIf="stats" lg="12" class="h-100" style="min-height: 160px!important;display: grid">
|
||||
<c-card class="mb-1 p-1 h-100" style="padding-left: 5px!important;">
|
||||
<div class="my-1">
|
||||
<h4 style="display: inline-block;">Version and Serial information</h4>
|
||||
|
||||
</div>
|
||||
<div *ngIf="!stats['license']" class="my-1">
|
||||
<div style="display: inline-block;margin-right: 5px;">
|
||||
<code style="padding: 0!important;">Serial:</code> <small
|
||||
style="background-color: #ccc;padding: 5px;border-radius: 5px;cursor: pointer;" (click)="copy_this()"
|
||||
[cdkCopyToClipboard]="stats['serial']">{{ stats['serial'] }}</small>
|
||||
<span *ngIf="copy_msg" style="color: #fff!important;" class="badge text-bg-success"><i
|
||||
class="fa-solid fa-check"></i>Copy</span>
|
||||
</div>
|
||||
<c-badge *ngIf="stats['username']" color="danger">Not Registred</c-badge>
|
||||
<c-badge *ngIf="!stats['username']" color="danger">License Validation failed</c-badge>
|
||||
</div>
|
||||
<div *ngIf="stats['license']=='connection_error'" class="my-1">
|
||||
<div style="display: inline-block;margin-right: 5px;">
|
||||
<code style="padding: 0!important;">Serial:</code> <small
|
||||
style="background-color: #ccc;padding: 5px;border-radius: 5px;cursor: pointer;" (click)="copy_this()"
|
||||
[cdkCopyToClipboard]="stats['serial']">{{ stats['serial'] }}</small>
|
||||
<span *ngIf="copy_msg" style="color: #fff!important;" class="badge text-bg-success mx-1"><i
|
||||
class="fa-solid fa-check"></i>Copy</span>
|
||||
</div>
|
||||
<c-badge class="mx-1" color="danger">Unable connect to server/Check server internet connection</c-badge>
|
||||
</div>
|
||||
<div *ngIf="stats['license']!='connection_error' && stats['license']" class="my-1">
|
||||
<div style="display: inline-block;margin-right: 5px;">
|
||||
<code style="padding: 0!important;">Serial:</code> <small
|
||||
style="background-color: #ccc;padding: 5px;border-radius: 5px;cursor: pointer;" (click)="copy_this()"
|
||||
[cdkCopyToClipboard]="stats['serial']">{{ stats['serial'] }}</small>
|
||||
<span *ngIf="copy_msg" style="color: #fff!important;" class="badge text-bg-success mx-1"><i
|
||||
class="fa-solid fa-check"></i>Copy</span>
|
||||
</div>
|
||||
<c-badge color="success">Registred</c-badge>
|
||||
<c-badge class="mx-1" color="info">License Type : {{stats['license']}}</c-badge>
|
||||
<c-badge *ngIf="stats['update_mode']!='auto'" color="info">Manual update</c-badge>
|
||||
<c-badge *ngIf="stats['update_mode']=='auto'" color="info">Auto update</c-badge>
|
||||
</div>
|
||||
<div *ngIf="stats['license']!='connection_error'" class="my-1">
|
||||
<span style="font-size: 0.9rem; display: inline-block;margin-right: 5px"><c-badge
|
||||
[color]="stats['update_available'] ? 'success' : 'secondary'"
|
||||
style="margin: 0!important;padding: 8px;height: 27px;">Your Mikroman version : {{stats['version']}}
|
||||
</c-badge>
|
||||
<i class="fa-solid fa-spinner fa-spin" *ngIf="stats['update_inprogress']"></i>
|
||||
<button cButton color="warning"
|
||||
*ngIf="stats['update_mode']!='auto' && stats['update_available'] && !stats['update_inprogress']" size="sm"
|
||||
(click)="showConfirmModal('update_mikroman')"
|
||||
style="font-size: 0.75em;position: relative;left: -4px;top: 1px;border-top-left-radius: 0;border-bottom-left-radius: 0;height: 27px;"><i
|
||||
class="fa-regular fa-hand-pointer fa-beat-fade"></i> Update availble </button>
|
||||
</span>
|
||||
<span style="font-size: 0.9rem; display: inline-block;"><c-badge
|
||||
[color]="stats['front_update_available'] ? 'success' : 'secondary'" style="padding: 8px;height: 27px;"
|
||||
color="secondary">Your Mikrofront version : {{front_version}}
|
||||
</c-badge>
|
||||
<i class="fa-solid fa-spinner fa-spin" *ngIf="stats['front_update_inprogress']"></i>
|
||||
<button cButton color="warning"
|
||||
*ngIf="stats['update_mode']!='auto' && stats['front_update_available'] && !stats['front_update_inprogress']"
|
||||
size="sm" (click)="showConfirmModal('update_mikrofront')"
|
||||
style="font-size: 0.75em;position: relative;left: -4px;top: 1px;border-top-left-radius: 0;border-bottom-left-radius: 0;height: 27px;"><i
|
||||
class="fa-regular fa-hand-pointer fa-beat-fade"></i> Update availble </button>
|
||||
</span>
|
||||
</div>
|
||||
<p *ngIf="!stats['license'] && !stats['username']" style="color: rgb(0, 119, 255);"><strong>License User name is not set in settings <a style="color: rgb(0, 119, 255);" target="_blank" href="https://mikrowizard.com/docs/register-serial-number/" >read more!</a></strong></p>
|
||||
<p *ngIf="!stats['license'] && stats['username']" style="color: rgb(0, 119, 255);"><strong>Serial number not submited<a style="color: rgb(0, 119, 255);" target="_blank" href="https://mikrowizard.com/docs/register-serial-number/" >read more!</a></strong> </p>
|
||||
|
||||
<!-- <div *ngIf="stats['update_mode']!='auto'" class="my-1">
|
||||
<button cButton color="warning" *ngIf="stats['update_available']" size="sm" style="font-size: 1em;"><i class="fa-regular fa-hand-pointer fa-beat-fade"></i> Update <strong>Mikroman</strong> and reload server</button>
|
||||
<button cButton color="warning" *ngIf="stats['front_update_available']" size="sm" style="font-size: 1em;margin-left: 5px;"><i class="fa-regular fa-hand-pointer fa-beat-fade"></i> Update <strong>MikroFront</strong> and reload Page</button>
|
||||
</div> -->
|
||||
</c-card>
|
||||
</c-col>
|
||||
<c-col xl="6" lg="12" class="h-100" style="min-height: 160px!important;display: grid;">
|
||||
<c-card class="h-100" *ngIf="stats" style="padding: 0!important;margin: 0!important;">
|
||||
<c-carousel [dark]="true" [animate]="false" [wrap]="false" [interval]="1000000">
|
||||
<c-carousel-indicators></c-carousel-indicators>
|
||||
<c-carousel-inner>
|
||||
<c-carousel-item style="display: flex;" *ngFor="let slide of stats['blog']; index as i;">
|
||||
<img [src]="slide.media_content" alt="{{slide.title}}" class="d-block" loading="lazy" style=" float: left;"
|
||||
height="150px" />
|
||||
<div style="padding: 20px;">
|
||||
<h5>{{slide.title}}</h5>
|
||||
<p style="max-width: 90%;" [innerHTML]="slide.summery"></p>
|
||||
<!-- Custom Wizard Widgets -->
|
||||
<div class="dashboard-widget" *ngFor="let widget of customWidgets; let i = index"
|
||||
[style.grid-column]="widget.position.col" [style.grid-row]="widget.position.row">
|
||||
<div class="widget-container">
|
||||
<button *ngIf="isRemoveMode" class="btn btn-danger btn-sm widget-remove-btn" (click)="removeCustomWidget(i)">
|
||||
<i class="fa-solid fa-times"></i>
|
||||
</button>
|
||||
<c-card class="h-100">
|
||||
<c-card-header class="d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0">{{widget.title}}</h5>
|
||||
<i class="fa-solid fa-grip-vertical widget-drag-handle"></i>
|
||||
</c-card-header>
|
||||
<c-card-body>
|
||||
<!-- CPU Widget -->
|
||||
<div *ngIf="widget.type === 'cpu'">
|
||||
<div class="text-center">
|
||||
<canvas id="cpu-chart-{{i}}" width="200" height="200"></canvas>
|
||||
<h3 class="mt-2">{{widget.data?.usage || 0}}%</h3>
|
||||
<p class="text-muted">CPU Usage</p>
|
||||
</div>
|
||||
</c-carousel-item>
|
||||
</c-carousel-inner>
|
||||
<c-carousel-control [routerLink] caption="Previous" direction="prev"></c-carousel-control>
|
||||
<c-carousel-control [routerLink] caption="Next" direction="next"></c-carousel-control>
|
||||
</c-carousel>
|
||||
</c-card>
|
||||
</c-col>
|
||||
</c-row>
|
||||
</div>
|
||||
|
||||
<!-- Memory Widget -->
|
||||
<div *ngIf="widget.type === 'memory'">
|
||||
<div class="progress mb-2" style="height: 20px;">
|
||||
<div class="progress-bar" role="progressbar" [style.width.%]="widget.data?.usage || 0">
|
||||
{{widget.data?.usage || 0}}%
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-muted">Memory Usage: {{widget.data?.used || 0}}GB / {{widget.data?.total || 0}}GB</p>
|
||||
</div>
|
||||
|
||||
<!-- Disk Widget -->
|
||||
<div *ngIf="widget.type === 'disk'">
|
||||
<div class="progress mb-2" style="height: 20px;">
|
||||
<div class="progress-bar bg-warning" role="progressbar" [style.width.%]="widget.data?.usage || 0">
|
||||
{{widget.data?.usage || 0}}%
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-muted">Disk Usage: {{widget.data?.used || 0}}GB / {{widget.data?.total || 0}}GB</p>
|
||||
</div>
|
||||
|
||||
<!-- Network Widget -->
|
||||
<div *ngIf="widget.type === 'network'">
|
||||
<div class="row">
|
||||
<div class="col-6 text-center">
|
||||
<h4 class="text-success">{{widget.data?.download || 0}}</h4>
|
||||
<small class="text-muted">Download</small>
|
||||
</div>
|
||||
<div class="col-6 text-center">
|
||||
<h4 class="text-primary">{{widget.data?.upload || 0}}</h4>
|
||||
<small class="text-muted">Upload</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</c-card-body>
|
||||
</c-card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Custom View Widgets -->
|
||||
<div class="dashboard-widget" *ngFor="let view of customViews; let i = index"
|
||||
[style.grid-column]="view.position.col" [style.grid-row]="view.position.row">
|
||||
<div class="widget-container">
|
||||
<button *ngIf="isRemoveMode" class="btn btn-danger btn-sm widget-remove-btn" (click)="removeCustomView(i)">
|
||||
<i class="fa-solid fa-times"></i>
|
||||
</button>
|
||||
<c-card class="h-100">
|
||||
<c-card-header class="d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0">{{view.name}}</h5>
|
||||
<i class="fa-solid fa-grip-vertical widget-drag-handle"></i>
|
||||
</c-card-header>
|
||||
<c-card-body>
|
||||
<p class="text-muted">Custom view content will be loaded here...</p>
|
||||
<div class="text-center">
|
||||
<i class="fa-solid fa-eye fa-3x text-secondary"></i>
|
||||
</div>
|
||||
</c-card-body>
|
||||
</c-card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Add Wizard Modal -->
|
||||
<c-modal #AddWizardModal backdrop="static" size="lg" [(visible)]="wizardModalVisible" id="AddWizardModal">
|
||||
<c-modal-header>
|
||||
<h5 cModalTitle>ADD WIZARD</h5>
|
||||
<button [cModalToggle]="AddWizardModal.id" cButtonClose></button>
|
||||
</c-modal-header>
|
||||
<c-modal-body>
|
||||
<form [formGroup]="wizardForm">
|
||||
<div class="mb-3">
|
||||
<label for="wizardType" class="form-label">Select Wizard Type</label>
|
||||
<select class="form-select" formControlName="wizardType" id="wizardType">
|
||||
<option value="">Choose wizard type...</option>
|
||||
<option value="cpu">CPU Monitor</option>
|
||||
<option value="memory">Memory Monitor</option>
|
||||
<option value="disk">Disk Monitor</option>
|
||||
<option value="network">Network Monitor</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="wizardTitle" class="form-label">Widget Title</label>
|
||||
<input type="text" class="form-control" formControlName="wizardTitle" id="wizardTitle" placeholder="Enter widget title">
|
||||
</div>
|
||||
</form>
|
||||
</c-modal-body>
|
||||
<c-modal-footer>
|
||||
<button cButton (click)="commitWizard()" color="primary" [disabled]="!wizardForm.valid">Commit</button>
|
||||
<button [cModalToggle]="AddWizardModal.id" cButton color="secondary">Cancel</button>
|
||||
</c-modal-footer>
|
||||
</c-modal>
|
||||
|
||||
<!-- Add View Modal -->
|
||||
<c-modal #AddViewModal backdrop="static" size="lg" [(visible)]="viewModalVisible" id="AddViewModal">
|
||||
<c-modal-header>
|
||||
<h5 cModalTitle>ADD VIEW</h5>
|
||||
<button [cModalToggle]="AddViewModal.id" cButtonClose></button>
|
||||
</c-modal-header>
|
||||
<c-modal-body>
|
||||
<form [formGroup]="viewForm">
|
||||
<div class="mb-3">
|
||||
<label for="viewName" class="form-label">View Name</label>
|
||||
<input type="text" class="form-control" formControlName="viewName" id="viewName" placeholder="Enter view name">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="viewDescription" class="form-label">Description (Optional)</label>
|
||||
<textarea class="form-control" formControlName="viewDescription" id="viewDescription" rows="3" placeholder="Enter view description"></textarea>
|
||||
</div>
|
||||
</form>
|
||||
</c-modal-body>
|
||||
<c-modal-footer>
|
||||
<button cButton (click)="commitView()" color="primary" [disabled]="!viewForm.valid">Commit</button>
|
||||
<button [cModalToggle]="AddViewModal.id" cButton color="secondary">Cancel</button>
|
||||
</c-modal-footer>
|
||||
</c-modal>
|
||||
|
||||
<!-- Existing Update Confirmation Modal -->
|
||||
<c-modal #ConfirmModal backdrop="static" size="lg" [(visible)]="ConfirmModalVisible" id="ConfirmModal">
|
||||
<c-modal-header>
|
||||
<h5 cModalTitle *ngIf="action=='update_mikroman'">Please Confirm Mikroman Update</h5>
|
||||
|
@ -246,9 +282,7 @@
|
|||
</div>
|
||||
</c-modal-body>
|
||||
<c-modal-footer>
|
||||
<button cButton (click)="ConfirmAction()" color="primary"> submit</button>
|
||||
<button [cModalToggle]="ConfirmModal.id" cButton color="secondary">
|
||||
Close
|
||||
</button>
|
||||
<button cButton (click)="ConfirmAction()" color="primary">Submit</button>
|
||||
<button [cModalToggle]="ConfirmModal.id" cButton color="secondary">Close</button>
|
||||
</c-modal-footer>
|
||||
</c-modal>
|
443
src/app/views/dashboard/dashboard.component.scss
Normal file
443
src/app/views/dashboard/dashboard.component.scss
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,62 +1,74 @@
|
|||
<div class="bg-light min-vh-100 d-flex flex-row align-items-center">
|
||||
<c-container>
|
||||
<c-row class="justify-content-center">
|
||||
<c-col md="8">
|
||||
<c-card-group>
|
||||
<c-card [ngStyle]="{'width.%': 44}" class="text-white py-5" style="background-color: #303c54;">
|
||||
<c-card-body class="text-center">
|
||||
<img style="width: 200px;" src="assets/img/brand/mikrowizard-full.jpg">
|
||||
</c-card-body>
|
||||
</c-card>
|
||||
<c-card class="p-4">
|
||||
<c-card-body>
|
||||
<form cForm [formGroup]="loginForm" >
|
||||
<h1>Login</h1>
|
||||
<p class="text-medium-emphasis">Sign In to your account</p>
|
||||
<c-input-group class="mb-3">
|
||||
<span cInputGroupText>
|
||||
<svg cIcon name="cilUser"></svg>
|
||||
</span>
|
||||
<input autoComplete="username" cFormControl placeholder="Username" formControlName="username" required #username/>
|
||||
</c-input-group>
|
||||
<c-input-group class="mb-1">
|
||||
<span cInputGroupText>
|
||||
<svg cIcon name="cilLockLocked"></svg>
|
||||
</span>
|
||||
<input
|
||||
autoComplete="current-password"
|
||||
cFormControl
|
||||
placeholder="Password"
|
||||
type="password"
|
||||
formControlName="password"
|
||||
required #password
|
||||
/>
|
||||
</c-input-group>
|
||||
<c-input-group class="mb-1" *ngIf="show_otp">
|
||||
<span cInputGroupText>
|
||||
<i class="fa-regular fa-clock"></i>
|
||||
</span>
|
||||
<input
|
||||
cFormControl
|
||||
placeholder="2FA TOTP key"
|
||||
formControlName="ga_code"
|
||||
required #ga_code
|
||||
/>
|
||||
</c-input-group>
|
||||
<code *ngIf="error_msg"><i class="fa-solid fa-triangle-exclamation"></i><small> {{error_msg}}</small></code>
|
||||
<c-row>
|
||||
<c-col mb-3 xs="6">
|
||||
<button type="submit" cButton (click)="onClickSubmit()" class="px-4" color="primary">
|
||||
Login
|
||||
</button>
|
||||
</c-col>
|
||||
</c-row>
|
||||
</form>
|
||||
</c-card-body>
|
||||
</c-card>
|
||||
<c-container>
|
||||
<c-row class="justify-content-center">
|
||||
<c-col md="8">
|
||||
<c-card-group>
|
||||
<c-card [ngStyle]="{'width.%': 44}" class="text-white py-5" style="background-color: #001C41;">
|
||||
<c-card-body class="text-center d-flex align-items-center justify-content-center">
|
||||
<img style="width: 250px;" src="assets/Pause GIF Image.gif">
|
||||
</c-card-body>
|
||||
</c-card>
|
||||
<c-card class="p-4">
|
||||
<c-card-body>
|
||||
<form cForm [formGroup]="loginForm">
|
||||
<h1 class="heading-text">Sign in</h1>
|
||||
<p class="text-medium-emphasis">Don't have an account? <a routerLink="/signup"
|
||||
class="signup-link">Sign up</a></p>
|
||||
<c-input-group class="mb-3">
|
||||
<span cInputGroupText>
|
||||
<svg cIcon name="cilUser"></svg>
|
||||
</span>
|
||||
<input autoComplete="username" cFormControl placeholder="Email/username field"
|
||||
formControlName="username" required #username />
|
||||
</c-input-group>
|
||||
<c-input-group class="mb-1">
|
||||
<span cInputGroupText>
|
||||
<svg cIcon name="cilLockLocked"></svg>
|
||||
</span>
|
||||
<input autoComplete="current-password" cFormControl placeholder="Password field"
|
||||
type="password" formControlName="password" required #password />
|
||||
</c-input-group>
|
||||
<c-input-group class="mb-1" *ngIf="show_otp">
|
||||
<span cInputGroupText>
|
||||
<i class="fa-regular fa-clock"></i>
|
||||
</span>
|
||||
<input cFormControl placeholder="2FA TOTP key" formControlName="ga_code" required
|
||||
#ga_code />
|
||||
</c-input-group>
|
||||
<code
|
||||
*ngIf="error_msg"><i class="fa-solid fa-triangle-exclamation"></i><small> {{error_msg}}</small></code>
|
||||
<c-row>
|
||||
<c-col mb-3 xs="6" class="w-100 mt-3">
|
||||
<button type="submit" cButton (click)="onClickSubmit()" class="px-4 w-100 btn-signin">
|
||||
Sign In
|
||||
</button>
|
||||
</c-col>
|
||||
<c-col mb-3 xs="6" class="w-100 mt-3">
|
||||
<p class="text-center">or continue with</p>
|
||||
</c-col>
|
||||
<c-col mb-3 xs="6" class="w-100">
|
||||
<button
|
||||
(click)="loginWithOffice365()"
|
||||
class="px-4 w-100 btn btn-social d-flex gap-2 justify-content-center align-items-center">
|
||||
<i class="fa-brands fa-microsoft"></i>
|
||||
<span>Login with Office 365</span>
|
||||
</button>
|
||||
</c-col>
|
||||
<c-col mb-3 xs="6" class="w-100 mt-3">
|
||||
<button
|
||||
(click)="loginWithApple()"
|
||||
class="px-4 w-100 btn btn-social d-flex gap-2 justify-content-center align-items-center">
|
||||
<i class="fa-brands fa-apple text-dark"></i>
|
||||
<span>Login with Apple</span>
|
||||
</button>
|
||||
</c-col>
|
||||
</c-row>
|
||||
</form>
|
||||
</c-card-body>
|
||||
</c-card>
|
||||
|
||||
</c-card-group>
|
||||
</c-col>
|
||||
</c-row>
|
||||
</c-container>
|
||||
</div>
|
||||
</c-card-group>
|
||||
</c-col>
|
||||
</c-row>
|
||||
</c-container>
|
||||
</div>
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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
|
||||
],
|
||||
|
|
115
src/app/views/pages/signup/signup.component.html
Normal file
115
src/app/views/pages/signup/signup.component.html
Normal file
|
@ -0,0 +1,115 @@
|
|||
<div class="bg-light min-vh-100 d-flex flex-row align-items-center">
|
||||
<c-container>
|
||||
<c-row class="justify-content-center">
|
||||
<c-col md="8">
|
||||
<c-card-group>
|
||||
<c-card [ngStyle]="{'width.%': 44}" class="text-white py-5" style="background-color: #001C41;">
|
||||
<c-card-body class="text-center d-flex align-items-center justify-content-center">
|
||||
<img style="width: 250px;" src="assets/Pause GIF Image.gif">
|
||||
</c-card-body>
|
||||
</c-card>
|
||||
<c-card class="p-4">
|
||||
<c-card-body>
|
||||
<form cForm [formGroup]="signupForm">
|
||||
<h1 class="heading-text">Create your account</h1>
|
||||
<p class="text-medium-emphasis">Already have an account? <a routerLink="/login"
|
||||
class="signup-link">Sign in</a></p>
|
||||
|
||||
<c-input-group class="mb-3">
|
||||
<span cInputGroupText>
|
||||
<svg cIcon name="cilUser"></svg>
|
||||
</span>
|
||||
<input cFormControl placeholder="Name field" formControlName="username" required />
|
||||
</c-input-group>
|
||||
<div *ngIf="submitted && signupForm.get('username')?.errors" class="text-danger mb-3">
|
||||
<small>Username is required</small>
|
||||
</div>
|
||||
|
||||
<c-input-group class="mb-3">
|
||||
<span cInputGroupText>
|
||||
<svg cIcon name="cilBuilding"></svg>
|
||||
</span>
|
||||
<input cFormControl placeholder="Organization field" formControlName="organization"
|
||||
required />
|
||||
</c-input-group>
|
||||
<div *ngIf="submitted && signupForm.get('organization')?.errors"
|
||||
class="text-danger mb-3">
|
||||
<small>Organization field is required</small>
|
||||
</div>
|
||||
|
||||
<c-input-group class="mb-3">
|
||||
<span cInputGroupText>
|
||||
<svg cIcon name="cilEnvelopeClosed"></svg>
|
||||
</span>
|
||||
<input cFormControl placeholder="Username/email address field"
|
||||
formControlName="email" required />
|
||||
</c-input-group>
|
||||
<div *ngIf="submitted && signupForm.get('email')?.errors" class="text-danger mb-3">
|
||||
<small *ngIf="signupForm.get('email')?.errors?.['required']">Email is
|
||||
required</small>
|
||||
<small *ngIf="signupForm.get('email')?.errors?.['email']">Please enter a valid
|
||||
email</small>
|
||||
</div>
|
||||
|
||||
<c-input-group class="mb-3">
|
||||
<span cInputGroupText>
|
||||
<svg cIcon name="cilLockLocked"></svg>
|
||||
</span>
|
||||
<input cFormControl placeholder="Password field" type="password"
|
||||
formControlName="password" required />
|
||||
</c-input-group>
|
||||
<div *ngIf="submitted && signupForm.get('password')?.errors" class="text-danger mb-3">
|
||||
<small *ngIf="signupForm.get('password')?.errors?.['required']">Password is
|
||||
required</small>
|
||||
<small *ngIf="signupForm.get('password')?.errors?.['minlength']">Password must be at
|
||||
least 6 characters</small>
|
||||
</div>
|
||||
|
||||
<c-input-group class="mb-3">
|
||||
<span cInputGroupText>
|
||||
<svg cIcon name="cilLockLocked"></svg>
|
||||
</span>
|
||||
<input cFormControl placeholder="Confirm Password field" type="password"
|
||||
formControlName="confirmPassword" required />
|
||||
</c-input-group>
|
||||
<div *ngIf="submitted && signupForm.get('confirmPassword')?.errors"
|
||||
class="text-danger mb-3">
|
||||
<small>Please confirm your password</small>
|
||||
</div>
|
||||
|
||||
<code
|
||||
*ngIf="error_msg"><i class="fa-solid fa-triangle-exclamation"></i><small> {{error_msg}}</small></code>
|
||||
<code *ngIf="success_msg"
|
||||
class="text-success"><i class="fa-solid fa-check"></i><small> {{success_msg}}</small></code>
|
||||
|
||||
<c-col mb-3 xs="6" class="w-100 mt-3">
|
||||
<button type="submit" cButton (click)="onClickSubmit()"
|
||||
class="px-4 w-100 btn-signin">
|
||||
Sign Up
|
||||
</button>
|
||||
</c-col>
|
||||
<c-col mb-3 xs="6" class="w-100 mt-3">
|
||||
<p class="text-center">or continue with</p>
|
||||
</c-col>
|
||||
<c-col mb-3 xs="6" class="w-100">
|
||||
<button
|
||||
class="px-4 w-100 btn btn-social d-flex gap-2 justify-content-center align-items-center">
|
||||
<i class="fa-brands fa-microsoft"></i>
|
||||
<span>Login with Office 365</span>
|
||||
</button>
|
||||
</c-col>
|
||||
<c-col mb-3 xs="6" class="w-100 mt-3">
|
||||
<button
|
||||
class="px-4 w-100 btn btn-social d-flex gap-2 justify-content-center align-items-center">
|
||||
<i class="fa-brands fa-apple text-dark"></i>
|
||||
<span>Login with Apple</span>
|
||||
</button>
|
||||
</c-col>
|
||||
</form>
|
||||
</c-card-body>
|
||||
</c-card>
|
||||
</c-card-group>
|
||||
</c-col>
|
||||
</c-row>
|
||||
</c-container>
|
||||
</div>
|
38
src/app/views/pages/signup/signup.component.scss
Normal file
38
src/app/views/pages/signup/signup.component.scss
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
61
src/app/views/pages/signup/signup.component.ts
Normal file
61
src/app/views/pages/signup/signup.component.ts
Normal file
|
@ -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!";
|
||||
});
|
||||
}
|
||||
}
|
BIN
src/assets/Pause GIF Image.gif
Normal file
BIN
src/assets/Pause GIF Image.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 44 KiB |
Loading…
Add table
Add a link
Reference in a new issue