Compare commits

..

50 Commits

Author SHA1 Message Date
allan d80c59afce Merge branch 'main' into deploy-context
Build / Build-and-ng-test (pull_request) Has been cancelled
Build / Build-and-test-development (pull_request) Has been cancelled
2025-06-05 12:30:19 +00:00
Mihajlo Medjedovic abdbb67471 fix: viya deploy load data timing
Build / Build-and-ng-test (pull_request) Failing after 4m0s
Build / Build-and-test-development (pull_request) Failing after 2h56m12s
2025-06-05 12:07:26 +02:00
Mihajlo Medjedovic 037a97b6ff fix: automatic viya deploy timing issue 2025-06-04 17:37:44 +02:00
Mihajlo Medjedovic a0a529ad38 style: lint 2025-06-04 17:36:13 +02:00
Mihajlo Medjedovic 72239558af feat: viya deploy, update the index.html contextname 2025-06-04 17:35:15 +02:00
semantic-release-bot d2097ad6dd chore(release): 6.15.2 [skip ci]
## [6.15.2](https://git.datacontroller.io/dc/dc/compare/v6.15.1...v6.15.2) (2025-06-04)

### Bug Fixes

* pipeline updates for DC.html ([624a7a8](624a7a8f37))
2025-06-04 14:26:44 +00:00
allan 1bd542cddb Merge pull request 'fix: pipeline updates for DC.html' (#165) from issue156b into main
Release / Build-production-and-ng-test (push) Successful in 4m29s
Release / Build-and-test-development (push) Successful in 8m37s
Release / release (push) Successful in 8m17s
Reviewed-on: #165
2025-06-04 13:53:24 +00:00
allan fb1c1ee874 Merge branch 'main' into issue156b
Build / Build-and-ng-test (pull_request) Successful in 4m1s
Build / Build-and-test-development (pull_request) Successful in 8m34s
2025-06-04 13:48:58 +00:00
allan 624a7a8f37 fix: pipeline updates for DC.html
Build / Build-and-ng-test (pull_request) Successful in 4m5s
Build / Build-and-test-development (pull_request) Successful in 8m42s
2025-06-04 14:41:36 +01:00
semantic-release-bot 381378f532 chore(release): 6.15.1 [skip ci]
## [6.15.1](https://git.datacontroller.io/dc/dc/compare/v6.15.0...v6.15.1) (2025-06-04)

### Bug Fixes

* updating pipeline to default to streaming on viya ([4b55894](4b558948d9))
2025-06-04 12:57:08 +00:00
allan af05486c0e Merge pull request 'fix: updating pipeline to default to streaming on viya' (#164) from issue156b into main
Release / Build-production-and-ng-test (push) Successful in 3m57s
Release / Build-and-test-development (push) Successful in 8m41s
Release / release (push) Failing after 7m35s
Reviewed-on: #164
2025-06-04 12:28:12 +00:00
allan 4b558948d9 fix: updating pipeline to default to streaming on viya
Build / Build-and-ng-test (pull_request) Successful in 4m4s
Build / Build-and-test-development (pull_request) Successful in 8m33s
Also added error captures on makedata
2025-06-04 13:27:28 +01:00
semantic-release-bot d9cff42f5c chore(release): 6.15.0 [skip ci]
# [6.15.0](https://git.datacontroller.io/dc/dc/compare/v6.14.10...v6.15.0) (2025-06-04)

### Bug Fixes

* makedata with context name ([da4d0b2](da4d0b28c7))

### Features

* viya deploy context ([6c96ef7](6c96ef7fb0))
2025-06-04 09:26:02 +00:00
allan 2a3d2b8d0d Merge pull request 'feat: viya deploy context' (#163) from deploy-context into main
Release / Build-production-and-ng-test (push) Successful in 4m3s
Release / Build-and-test-development (push) Successful in 8m39s
Release / release (push) Successful in 8m30s
Reviewed-on: #163
2025-06-04 09:09:28 +00:00
Mihajlo Medjedovic d0f453d291 chore: typo
Build / Build-and-ng-test (pull_request) Successful in 3m59s
Build / Build-and-test-development (pull_request) Successful in 8m31s
2025-06-04 09:41:06 +02:00
Mihajlo Medjedovic 8e65dd0eae style: lint
Build / Build-and-ng-test (pull_request) Successful in 3m59s
Build / Build-and-test-development (pull_request) Successful in 8m25s
2025-06-03 21:06:48 +02:00
Mihajlo Medjedovic da4d0b28c7 fix: makedata with context name
Build / Build-and-ng-test (pull_request) Failing after 49s
Build / Build-and-test-development (pull_request) Successful in 8m32s
2025-06-03 21:00:54 +02:00
Mihajlo Medjedovic 6c96ef7fb0 feat: viya deploy context
Build / Build-and-ng-test (pull_request) Failing after 47s
Build / Build-and-test-development (pull_request) Successful in 8m35s
2025-06-03 20:22:39 +02:00
semantic-release-bot 997f09adde chore(release): 6.14.10 [skip ci]
## [6.14.10](https://git.datacontroller.io/dc/dc/compare/v6.14.9...v6.14.10) (2025-06-02)

### Bug Fixes

* bump core ([0e8503e](0e8503ed2b))
* default to home directory for SAS Drive in Viya ([9682b54](9682b548e6))
2025-06-02 17:51:20 +00:00
allan 0e8503ed2b fix: bump core
Release / Build-production-and-ng-test (push) Successful in 4m31s
Release / Build-and-test-development (push) Successful in 8m40s
Release / release (push) Successful in 8m20s
2025-06-02 18:32:33 +01:00
allan 97dfcd79b1 Merge pull request 'issue156' (#161) from issue156 into main
Release / Build-and-test-development (push) Has been cancelled
Release / release (push) Has been cancelled
Release / Build-production-and-ng-test (push) Has been cancelled
Reviewed-on: #161
2025-06-02 17:29:18 +00:00
allan 9a12a2e41f Merge branch 'main' into issue156
Build / Build-and-ng-test (pull_request) Successful in 4m9s
Build / Build-and-test-development (pull_request) Has been cancelled
2025-06-02 17:27:14 +00:00
semantic-release-bot 5c114e562b chore(release): 6.14.9 [skip ci]
## [6.14.9](https://git.datacontroller.io/dc/dc/compare/v6.14.8...v6.14.9) (2025-06-02)

### Bug Fixes

* default DC path for viya ([f3125ff](f3125ff464))
2025-06-02 10:36:05 +00:00
allan ae696a0be0 Merge pull request 'Viya default DC Path by user name' (#162) from viya-smooth-deploy into main
Release / Build-production-and-ng-test (push) Successful in 3m56s
Release / Build-and-test-development (push) Successful in 8m33s
Release / release (push) Successful in 8m22s
Reviewed-on: #162
2025-06-02 10:19:51 +00:00
Mihajlo Medjedovic 22d46a5dcc style: lint
Build / Build-and-ng-test (pull_request) Successful in 4m8s
Build / Build-and-test-development (pull_request) Successful in 8m34s
2025-06-02 11:24:30 +02:00
Mihajlo Medjedovic f3125ff464 fix: default DC path for viya 2025-06-02 11:24:18 +02:00
allan 9682b548e6 fix: default to home directory for SAS Drive in Viya
Build / Build-and-ng-test (pull_request) Successful in 4m10s
Build / Build-and-test-development (pull_request) Successful in 8m35s
2025-05-29 15:05:18 +01:00
semantic-release-bot ec11a74265 chore(release): 6.14.8 [skip ci]
## [6.14.8](https://git.datacontroller.io/dc/dc/compare/v6.14.7...v6.14.8) (2025-05-28)

### Bug Fixes

* CSP issues, clarity local library build, fixed some style issues ([841201a](841201adab))
* deploy page, makedata error handling, added local build of clarity, to address clr-stack-view CSP issues (inline styles) ([7b5e7ae](7b5e7ae184))
* improved deploy flow for Viya ([9604661](9604661f3b))
* requests modal causing VIYA CSP errors ([1dc6934](1dc69341ca))
* sas viya service init timing issue ([9de04e9](9de04e9a0c))
* scss of components transferred to the global styles.scss so we do not cause CSP (inline styles) issues when streaming to Viya ([6c171a6](6c171a6394))
* viya deploy page improved flow ([4bd2154](4bd215491f))
2025-05-28 18:13:32 +00:00
allan 4be0614604 Merge pull request 'chore: package-lock' (#155) from lockfile-fix into main
Release / Build-production-and-ng-test (push) Successful in 4m5s
Release / Build-and-test-development (push) Successful in 8m48s
Release / release (push) Successful in 8m32s
Reviewed-on: #155
2025-05-28 17:56:46 +00:00
Mihajlo Medjedovic 27cbff2bc5 chore: remove package.json comment
Build / Build-and-ng-test (pull_request) Successful in 4m13s
Build / Build-and-test-development (pull_request) Successful in 8m49s
2025-05-28 19:18:32 +02:00
Mihajlo Medjedovic c41c8963f2 chore: package.json comments
Build / Build-and-ng-test (pull_request) Failing after 49s
Build / Build-and-test-development (pull_request) Failing after 50s
2025-05-28 19:03:54 +02:00
Mihajlo Medjedovic 7249d4fa29 chore: package-lock
Build / Build-and-ng-test (pull_request) Successful in 4m14s
Build / Build-and-test-development (pull_request) Successful in 8m50s
2025-05-28 19:01:16 +02:00
Mihajlo Medjedovic 2e0c60cc0d chore: fix package-lock 2025-05-28 18:57:43 +02:00
allan 7c5e47f5e4 Merge pull request 'Scss of components transferred to the global styles.scss so we do not cause CSP (inline styles) issues when streaming to Viya' (#153) from css-refactor into main
Release / Build-production-and-ng-test (push) Failing after 43s
Release / Build-and-test-development (push) Has been skipped
Release / release (push) Has been skipped
Reviewed-on: #153
2025-05-28 16:57:20 +00:00
Mihajlo Medjedovic f9decbd366 style: lint
Build / Build-and-ng-test (pull_request) Failing after 53s
Build / Build-and-test-development (pull_request) Failing after 50s
2025-05-26 15:39:48 +02:00
Mihajlo Medjedovic 1dc69341ca fix: requests modal causing VIYA CSP errors 2025-05-26 15:39:28 +02:00
Mihajlo Medjedovic 75ae19fa8e style: lint
Build / Build-and-ng-test (pull_request) Failing after 1m11s
Build / Build-and-test-development (pull_request) Failing after 1m18s
2025-05-23 13:35:58 +02:00
Mihajlo Medjedovic 9de04e9a0c fix: sas viya service init timing issue
Build / Build-and-ng-test (pull_request) Failing after 48s
Build / Build-and-test-development (pull_request) Failing after 1m15s
2025-05-23 13:35:37 +02:00
Mihajlo Medjedovic 983f59cd51 style: lint
Build / Build-and-ng-test (pull_request) Failing after 1m11s
Build / Build-and-test-development (pull_request) Failing after 1m11s
2025-05-23 11:32:27 +02:00
Mihajlo Medjedovic 7b5e7ae184 fix: deploy page, makedata error handling, added local build of clarity, to address clr-stack-view CSP issues (inline styles) 2025-05-23 11:30:15 +02:00
Mihajlo Medjedovic 6e96b1daec style: lint
Build / Build-and-ng-test (pull_request) Successful in 3m57s
Build / Build-and-test-development (pull_request) Successful in 8m28s
2025-05-22 10:47:46 +02:00
Mihajlo Medjedovic 9604661f3b fix: improved deploy flow for Viya 2025-05-22 10:47:15 +02:00
Mihajlo Medjedovic 4bd215491f fix: viya deploy page improved flow
Build / Build-and-ng-test (pull_request) Successful in 4m10s
Build / Build-and-test-development (pull_request) Successful in 8m43s
2025-05-21 14:13:03 +02:00
Mihajlo Medjedovic 6a7dd451b5 style: lint
Build / Build-and-ng-test (pull_request) Failing after 49s
Build / Build-and-test-development (pull_request) Failing after 47s
2025-05-21 09:55:05 +02:00
Mihajlo Medjedovic 841201adab fix: CSP issues, clarity local library build, fixed some style issues
Build / Build-and-ng-test (pull_request) Failing after 49s
Build / Build-and-test-development (pull_request) Failing after 49s
2025-05-21 09:36:36 +02:00
Mihajlo Medjedovic e013e62776 chore(git): Merge branch 'main' into css-refactor
Build / Build-and-ng-test (pull_request) Failing after 50s
Build / Build-and-test-development (pull_request) Successful in 8m48s
2025-05-13 15:59:08 +02:00
Mihajlo Medjedovic 6c171a6394 fix: scss of components transferred to the global styles.scss so we do not cause CSP (inline styles) issues when streaming to Viya 2025-05-13 15:54:57 +02:00
semantic-release-bot a377f6e8d6 chore(release): 6.14.7 [skip ci]
## [6.14.7](https://git.datacontroller.io/dc/dc/compare/v6.14.6...v6.14.7) (2025-05-08)

### Bug Fixes

* updated hot, clarity and improved accessibility score. ([2844c70](2844c70f95))
2025-05-08 11:54:20 +00:00
allan 0337318e0b Merge pull request 'Updated hot, clarity and improved accessibility score.' (#152) from hot-clarity-accessiblity-update into main
Release / Build-production-and-ng-test (push) Successful in 4m2s
Release / Build-and-test-development (push) Successful in 8m36s
Release / release (push) Successful in 8m19s
Reviewed-on: #152
Reviewed-by: allan <allan@4gl.io>
2025-05-08 11:37:55 +00:00
Mihajlo Medjedovic 2844c70f95 fix: updated hot, clarity and improved accessibility score.
Build / Build-and-ng-test (pull_request) Successful in 4m10s
Build / Build-and-test-development (pull_request) Successful in 8m41s
2025-05-06 15:58:30 +02:00
121 changed files with 6312 additions and 4695 deletions
+11 -11
View File
@@ -237,20 +237,20 @@ jobs:
cd sas cd sas
sasjs c -t viya sasjs c -t viya
rm -rf sasjsbuild/tests rm -rf sasjsbuild/tests
sed -i -e 's/servertype="SASJS"/servertype="SASVIYA"/g' sasjsbuild/services/clickme.html sed -i -e 's/servertype="SASJS"/servertype="SASVIYA"/g' sasjsbuild/services/DC.html
sasjs b -t viya
cp sasjsbuild/viya.sas ./demostream_viya.sas
# compile Viya Full deploy (without web)
rm -rf sasjsbuild/services/web
rm sasjsbuild/services/clickme.html
sasjs b -t viya sasjs b -t viya
cp sasjsbuild/viya.sas ./viya.sas cp sasjsbuild/viya.sas ./viya.sas
cp sasjsbuild/viya.json ./viya.json # compile Viya Full deploy (without web)
rm -rf sasjsbuild/services/web
rm sasjsbuild/services/DC.html
sasjs b -t viya
cp sasjsbuild/viya.sas ./viya_noweb.sas
cp sasjsbuild/viya.json ./viya_noweb.json
- name: Zip Frontend (including viya.json for full viya deploy) - name: Zip Frontend (including viya.json for full viya deploy)
run: | run: |
cd sas cd sas
cp sasjsbuild/viya.json ../client/dist cp sasjsbuild/viya.json ../client/dist/viya.json
cd .. cd ..
zip -r frontend.zip ./client/dist zip -r frontend.zip ./client/dist
@@ -277,8 +277,8 @@ jobs:
URL="https://git.datacontroller.io/api/v1/repos/dc/dc/releases/$RELEASE_ID/assets?access_token=${{ secrets.RELEASE_TOKEN }}" URL="https://git.datacontroller.io/api/v1/repos/dc/dc/releases/$RELEASE_ID/assets?access_token=${{ secrets.RELEASE_TOKEN }}"
curl -k $URL -F attachment=@frontend.zip curl -k $URL -F attachment=@frontend.zip
curl -k $URL -F attachment=@sas/demostream_sas9.sas curl -k $URL -F attachment=@sas/demostream_sas9.sas
curl -k $URL -F attachment=@sas/demostream_viya.sas curl -k $URL -F attachment=@sas/viya.sas
curl -k $URL -F attachment=@sas/sasjs_server.json.zip curl -k $URL -F attachment=@sas/sasjs_server.json.zip
curl -k $URL -F attachment=@sas/sas9.sas curl -k $URL -F attachment=@sas/sas9.sas
curl -k $URL -F attachment=@sas/viya.sas curl -k $URL -F attachment=@sas/viya_noweb.sas
curl -k $URL -F attachment=@sas/viya.json curl -k $URL -F attachment=@sas/viya_noweb.json
+61
View File
@@ -1,3 +1,64 @@
## [6.15.2](https://git.datacontroller.io/dc/dc/compare/v6.15.1...v6.15.2) (2025-06-04)
### Bug Fixes
* pipeline updates for DC.html ([624a7a8](https://git.datacontroller.io/dc/dc/commit/624a7a8f37f0265cf576da310ac330c75aa417cf))
## [6.15.1](https://git.datacontroller.io/dc/dc/compare/v6.15.0...v6.15.1) (2025-06-04)
### Bug Fixes
* updating pipeline to default to streaming on viya ([4b55894](https://git.datacontroller.io/dc/dc/commit/4b558948d997f456ff25a12a58827fe0d2075493))
# [6.15.0](https://git.datacontroller.io/dc/dc/compare/v6.14.10...v6.15.0) (2025-06-04)
### Bug Fixes
* makedata with context name ([da4d0b2](https://git.datacontroller.io/dc/dc/commit/da4d0b28c7109afd6f96455e1e0e80a40d25a942))
### Features
* viya deploy context ([6c96ef7](https://git.datacontroller.io/dc/dc/commit/6c96ef7fb0a55754a84ff0a8bbab838b78c1acaf))
## [6.14.10](https://git.datacontroller.io/dc/dc/compare/v6.14.9...v6.14.10) (2025-06-02)
### Bug Fixes
* bump core ([0e8503e](https://git.datacontroller.io/dc/dc/commit/0e8503ed2bb22a0fc3924ac929e7f19626772e0a))
* default to home directory for SAS Drive in Viya ([9682b54](https://git.datacontroller.io/dc/dc/commit/9682b548e6106d99d97dcc023a35d93addfd5170))
## [6.14.9](https://git.datacontroller.io/dc/dc/compare/v6.14.8...v6.14.9) (2025-06-02)
### Bug Fixes
* default DC path for viya ([f3125ff](https://git.datacontroller.io/dc/dc/commit/f3125ff4641e47e33cb203228f5b1014ea3343bc))
## [6.14.8](https://git.datacontroller.io/dc/dc/compare/v6.14.7...v6.14.8) (2025-05-28)
### Bug Fixes
* CSP issues, clarity local library build, fixed some style issues ([841201a](https://git.datacontroller.io/dc/dc/commit/841201adab582149b1cca3a42e75f7cac75167f9))
* deploy page, makedata error handling, added local build of clarity, to address clr-stack-view CSP issues (inline styles) ([7b5e7ae](https://git.datacontroller.io/dc/dc/commit/7b5e7ae18414152f9b9d8f2d94fc94de43152003))
* improved deploy flow for Viya ([9604661](https://git.datacontroller.io/dc/dc/commit/9604661f3b76111387bc9474cc26348d73ab112e))
* requests modal causing VIYA CSP errors ([1dc6934](https://git.datacontroller.io/dc/dc/commit/1dc69341cadb837e1f11624d5cf35788bbb98d96))
* sas viya service init timing issue ([9de04e9](https://git.datacontroller.io/dc/dc/commit/9de04e9a0ce016e1a9fb8b19c656077079ddcf2f))
* scss of components transferred to the global styles.scss so we do not cause CSP (inline styles) issues when streaming to Viya ([6c171a6](https://git.datacontroller.io/dc/dc/commit/6c171a6394aba8104fe0f50aa8a4e6b9fa8023a2))
* viya deploy page improved flow ([4bd2154](https://git.datacontroller.io/dc/dc/commit/4bd215491f8cdc68f78bade68e7cb98e07edc81e))
## [6.14.7](https://git.datacontroller.io/dc/dc/compare/v6.14.6...v6.14.7) (2025-05-08)
### Bug Fixes
* updated hot, clarity and improved accessibility score. ([2844c70](https://git.datacontroller.io/dc/dc/commit/2844c70f9507036216b8b621900c2bb9010c1d34))
## [6.14.6](https://git.datacontroller.io/dc/dc/compare/v6.14.5...v6.14.6) (2025-04-03) ## [6.14.6](https://git.datacontroller.io/dc/dc/compare/v6.14.5...v6.14.6) (2025-04-03)
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -10,7 +10,7 @@ const check = (cwd) => {
onlyAllow: onlyAllow:
'AFLv2.1;Apache 2.0;Apache-2.0;Apache*;Artistic-2.0;0BSD;BSD*;BSD-2-Clause;BSD-3-Clause;CC0-1.0;CC-BY-3.0;CC-BY-4.0;ISC;MIT;MPL-2.0;ODC-By-1.0;Python-2.0;Unlicense;', 'AFLv2.1;Apache 2.0;Apache-2.0;Apache*;Artistic-2.0;0BSD;BSD*;BSD-2-Clause;BSD-3-Clause;CC0-1.0;CC-BY-3.0;CC-BY-4.0;ISC;MIT;MPL-2.0;ODC-By-1.0;Python-2.0;Unlicense;',
excludePackages: excludePackages:
'@cds/city@1.1.0;@handsontable/angular@15.2.0;handsontable@15.2.0;hyperformula@2.7.1;hyperformula@3.0.0;jackspeak@3.4.3;path-scurry@1.11.1;package-json-from-dist@1.0.1' '@cds/city@1.1.0;@handsontable/angular@15.3.0;handsontable@15.3.0;hyperformula@2.7.1;hyperformula@3.0.0;jackspeak@3.4.3;path-scurry@1.11.1;package-json-from-dist@1.0.1'
}, },
(error, json) => { (error, json) => {
if (error) { if (error) {
+1136 -773
View File
File diff suppressed because it is too large Load Diff
+4 -4
View File
@@ -45,10 +45,10 @@
"@angular/platform-browser-dynamic": "^17.3.3", "@angular/platform-browser-dynamic": "^17.3.3",
"@angular/router": "^17.3.3", "@angular/router": "^17.3.3",
"@cds/core": "^6.15.1", "@cds/core": "^6.15.1",
"@clr/angular": "^17.8.0", "@clr/angular": "file:libraries/clr-angular-17.9.0.tgz",
"@clr/icons": "^13.0.2", "@clr/icons": "^13.0.2",
"@clr/ui": "^17.8.0", "@clr/ui": "file:libraries/clr-ui-17.9.0.tgz",
"@handsontable/angular": "^15.2.0", "@handsontable/angular": "^15.3.0",
"@sasjs/adapter": "^4.11.0", "@sasjs/adapter": "^4.11.0",
"@sasjs/utils": "^3.4.0", "@sasjs/utils": "^3.4.0",
"@sheet/crypto": "file:libraries/sheet-crypto.tgz", "@sheet/crypto": "file:libraries/sheet-crypto.tgz",
@@ -60,7 +60,7 @@
"crypto-js": "^4.2.0", "crypto-js": "^4.2.0",
"d3-graphviz": "^5.0.2", "d3-graphviz": "^5.0.2",
"fs-extra": "^7.0.1", "fs-extra": "^7.0.1",
"handsontable": "^15.2.0", "handsontable": "^15.3.0",
"https-browserify": "1.0.0", "https-browserify": "1.0.0",
"hyperformula": "^2.5.0", "hyperformula": "^2.5.0",
"iconv-lite": "^0.5.0", "iconv-lite": "^0.5.0",
+3
View File
@@ -148,5 +148,8 @@ export const globals: {
}, },
handsontable: { handsontable: {
darkTableHeaderClass: 'darkTH' darkTableHeaderClass: 'darkTH'
},
userDropdownConfig: {
closeOnDebugClick: false
} }
} }
-447
View File
@@ -1,447 +0,0 @@
@import '../colors.scss';
// Copyright (c) 2016 VMware, Inc. All Rights Reserved.
// This software is released under MIT license.
// The full license information can be found in LICENSE in the root directory of this project.
app-requests-modal {
z-index: 10000;
}
header.app-header {
background: $headerBackground !important;
color: #fff;
}
.logo img.without-text {
width: 30px;
}
.logo img.with-text {
width: 210px;
}
.header-hamburger-trigger {
display: block;
background: transparent;
border: 0;
margin-left: 10px;
}
.demo-expired-notice {
display: flex;
justify-content: center;
align-items: center;
position: fixed;
left: 0;
top: 0;
height: 100vh !important;
width: 100vw !important;
z-index: 105;
background: rgba(33, 33, 33, .5);
.expired-details {
flex-direction: column;
align-items: center;
padding: 30px;
z-index: 110;
background: $headerBackground;
.expired-notice {
color: #e0e0e0;
font-size: 16px;
.mailto {
color: #8dc53e;
}
}
}
}
.main-container .update-key {
display: flex;
align-items: center;
color: white;
padding: 0px 10px;
background: #00000026;
}
.alert-icon-wrapper {
margin-top: 0 !important;
}
.nav-text {
margin-right: 20px;
}
.sidebar-toggle {
display: flex;
height: 100%;
align-items: center;
padding-left: 10px;
clr-icon {
cursor: pointer;
width: 30px;
height: 30px;
}
}
header {
.header-actions {
.dropdown {
position: unset; //without it, when opening user dropdown scrollbar was displaying without reason
}
}
.nav-link:hover {
color: #fafafa;
}
.nav-link.active {
background: #1d2f3d;
}
}
.notf {
background: #16a57a;
color: #fffcfc;
font-size: 12px;
}
.toggle-switch input[type=checkbox]:checked+label:before {
border-color: #61717D;
background-color: #61717D;
transition: .15s ease-in;
transition-property: border-color,background-color;
}
.main-container {
min-height: 100vh !important;
}
.main-container .content-container .content-area {
padding: 0rem 1rem 1rem 1rem;
}
.content-container {
z-index: 0!important;
}
.navBarResp {
display: flex;
justify-content: center;
background: #495A67;
color: #fff;
}
::ng-deep {
.htInvalid {
background: black!important;
}
@media screen and (max-width:480px) {
h2 {
font-size: .7rem!important;
}
h3 {
font-size: .7rem;
}
}
.nav-link {
padding: 0rem 1rem 0rem 1rem;
}
body[cds-theme="light"] {
.btn-primary .btn, .btn.btn-primary {
border-color: $headerBackground;
background-color: $headerBackground;
color: #fff;
}
}
body[cds-theme="dark"] {
.btn-primary .btn, .btn.btn-primary {
border-color: #5e7382;
background-color: #5e7382;
color: #fff;
clr-icon, cds-icon {
color: #fff
}
}
}
.btn-primary .btn, .btn.btn-primary {
&:disabled {
opacity: 0.65;
}
}
.btn {
cursor: pointer;
display: inline-block;
-webkit-appearance: none!important;
border-radius: .125rem;
border: 1px solid;
min-width: 3rem;
max-width: 15rem;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
text-align: center;
text-transform: uppercase;
vertical-align: middle;
line-height: 1.5rem;
letter-spacing: .12em;
font-size: .5rem;
font-weight: 500;
height: 1.5rem;
padding: 0 .5rem;
}
.btn.btn-outline:hover {
border-color: $headerBackground;
background-color: #495A67;
color: #fff;
}
body[cds-theme="dark"] {
.btn.btn-icon.btn-dimmed {
color: #7295ae;
}
}
body[cds-theme="light"] {
.btn.btn-icon.btn-dimmed {
color: $headerBackground;
}
.btn.btn-outline {
border-color: $headerBackground;
background-color: transparent;
color: $headerBackground;
}
}
.htMobileEditorContainer .inputs textarea {
font-size: 13pt;
border: 2px solid #485967;
border-radius: 4px;
-webkit-appearance: none;
box-shadow: none;
position: absolute;
left: 14px;
right: 0px;
top: 0;
bottom: 0;
padding: 7pt;
width: 290px;
}
.htMobileEditorContainer .positionControls {
width: 333px;
position: absolute;
right: 5pt;
top: 50px;
bottom: 0;
display: flex;
justify-content: center;
}
.htMobileEditorContainer.active {
display: block;
height: 120px;
width: 350px;
}
/* Left and right */
/* Column headers */
body[cds-theme="light"] {
.wtBorder {
background-color: #495A67!important;
}
.ht_master tr:nth-of-type(odd) > td {
filter: brightness(0.95);
}
}
$darkBorderColor: #697c85;
body[cds-theme="dark"] {
.ht_master tr:nth-of-type(odd) > td {
filter: brightness(1.2);
}
.ht_master:not(.emptyColumns) ~ .handsontable tbody tr th, .ht_master:not(.emptyColumns) ~ .handsontable:not(.ht_clone_top) thead tr th:first-child {
background-color: #2d4048;
border-color: $darkBorderColor;
}
.handsontable td {
// border-right: 1px solid #697c85;
// border-bottom: 1px solid #697c85;
border-color: $darkBorderColor;
}
.handsontable tr:first-child th, .handsontable tr:first-child td {
border-color: $darkBorderColor;
}
.handsontable .handsontable.ht_clone_top .wtHider {
border-color: $darkBorderColor;
}
.handsontable .changeType {
background-color: #3c5662;
border-color: $darkBorderColor;
}
.handsontableInput {
background-color: #708b98;
}
}
.handsontable .handsontable.ht_clone_top .wtHider {
padding: 0 0 0px 0!important;
margin: 0px;
border-bottom: 3px solid #d6d3d3;
}
body[cds-theme="light"] {
.content-container {
// background: red;
background: #F5F6FF;
}
}
.datagrid-compact, .datagrid-history{
.datagrid {
border-collapse: separate;
border: 1px solid transparent;
border-radius: .125rem;
margin: 0;
margin-top: 1rem;
max-width: 100%;
width: 100%;
padding: 15px 15px 50px 15px;
}
.datagrid-foot {
-webkit-box-pack: end;
-ms-flex-pack: end;
justify-content: flex-end;
height: 1.5rem;
padding: 0 .5rem;
line-height: calc(1.5rem - 3px);
font-size: .45833rem;
background-color: #fff;
border-top: 1px solid #ccc;
border-radius: 0px;
// border-radius: 0 0 .125rem .125rem;
}
.datagrid-footer {
position: absolute;
right: 30px;
top: 1px;
}
.datagrid .datagrid-head {
background-color: #fff;
border-bottom: 1px solid #ccc;
}
}
.dropdown-menu {
position: absolute;
top: 100%;
left: 0;
margin-top: .083333rem;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
-ms-flex-direction: column;
flex-direction: column;
padding: .5rem 0;
border: 1px solid #ccc;
box-shadow: 0 1px 0.125rem hsla(0,0%,45%,.25);
min-width: 5rem;
max-width: 15rem;
border-radius: .125rem;
visibility: hidden;
z-index: 1000;
}
.table {
border-collapse: separate;
border: 1px solid transparent;
border-radius: 0px;
margin: 0;
margin-top: 1rem;
max-width: 100%;
width: 100%;
}
.table th {
font-size: .45833rem;
font-weight: 600;
letter-spacing: .03em;
vertical-align: bottom;
border-bottom: 1px solid #ccc;
text-transform: uppercase;
}
.modal-header {
border-bottom: 2px solid #e4e4e4;
padding: 0 0 .5rem 0;
margin-bottom: 1rem;
}
.main-container .content-container {
min-height: 0px;
position: relative;
}
}
.app-loading {
.loading-logo {
max-width: 400px;
width: 100%;
}
}
@media screen and (max-width: 768px) {
.navBarResp {
display: flex;
justify-content: flex-start;
background: #495A67;
color: #fff;
}
.main-container .sub-nav.clr-nav-level-1 .nav .nav-link, .main-container .sub-nav.clr-nav-level-2 .nav .nav-link, .main-container .subnav.clr-nav-level-1 .nav .nav-link, .main-container .subnav.clr-nav-level-2 .nav .nav-link {
padding: 0 .5rem 0 1rem;
width: 100%;
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
border-radius: .125rem 0 0 .125rem;
color: #95c84b;
}
.card-block, .card-footer {
padding: 10px 0px 0px 0px;
}
.main-container[_ngcontent-c0] .content-container[_ngcontent-c0] .content-area[_ngcontent-c0] {
padding: 0rem 0rem 0rem 0rem;
}
}
+8 -2
View File
@@ -1,4 +1,9 @@
import { ChangeDetectorRef, Component, ElementRef } from '@angular/core' import {
ChangeDetectorRef,
Component,
ElementRef,
ViewEncapsulation
} from '@angular/core'
import { Router } from '@angular/router' import { Router } from '@angular/router'
import { VERSION } from '../environments/version' import { VERSION } from '../environments/version'
import { ActivatedRoute } from '@angular/router' import { ActivatedRoute } from '@angular/router'
@@ -36,7 +41,8 @@ ClarityIcons.addIcons(
@Component({ @Component({
selector: 'my-app', selector: 'my-app',
templateUrl: './app.component.html', templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'] styleUrls: ['./app.component.scss'],
encapsulation: ViewEncapsulation.None
}) })
export class AppComponent { export class AppComponent {
private dcAdapterSettings: DcAdapterSettings | undefined private dcAdapterSettings: DcAdapterSettings | undefined
+1 -1
View File
@@ -5,7 +5,7 @@
<div class="card-header">Terms and Conditions</div> <div class="card-header">Terms and Conditions</div>
<div class="card-block"> <div class="card-block">
<div class="card-text"> <div class="card-text">
<p> <p class="mt-0">
The Demo version of Data Controller is free for EVALUATION purposes The Demo version of Data Controller is free for EVALUATION purposes
only. Before proceeding with configuration, please confirm that you only. Before proceeding with configuration, please confirm that you
have read, understood, and agreed to the have read, understood, and agreed to the
@@ -1,50 +0,0 @@
.card {
margin-top: 0;
}
.btn {
margin-top: 10px;
}
.log-wrapper {
width: 100%;
background: #f0f0f0;
border: 1px solid #c9c9c9;
padding: 10px;
overflow: auto;
white-space: pre-wrap;
}
#contexts-btn {
padding: 0;
min-width: 30px;
margin-left: 10px;
height: 30px;
display: inline-flex;
justify-content: center;
align-items: center;
padding-top: 3px;
}
.validation-bar {
display: flex;
margin-top: 20px;
align-items: center;
clr-icon {
margin-right: 5px;
}
}
.autodeploy-section {
padding: 0px 15px;
.clr-checkbox-wrapper {
margin: 20px 0 20px 0;
}
.btn-autodeploy {
display: block;
margin: 15px 0 15px 0;
}
}
+3 -21
View File
@@ -1,4 +1,4 @@
import { Component, OnInit } from '@angular/core' import { Component, OnInit, ViewEncapsulation } from '@angular/core'
import { SasService } from '../services/sas.service' import { SasService } from '../services/sas.service'
import { SASjsConfig } from '@sasjs/adapter' import { SASjsConfig } from '@sasjs/adapter'
import { Router } from '@angular/router' import { Router } from '@angular/router'
@@ -13,7 +13,8 @@ import { DcAdapterSettings } from '../models/DcAdapterSettings'
styleUrls: ['./deploy.component.scss'], styleUrls: ['./deploy.component.scss'],
host: { host: {
class: 'content-container' class: 'content-container'
} },
encapsulation: ViewEncapsulation.None
}) })
export class DeployComponent implements OnInit { export class DeployComponent implements OnInit {
public step: number = 0 public step: number = 0
@@ -56,25 +57,6 @@ export class DeployComponent implements OnInit {
} }
ngOnInit() { ngOnInit() {
if (this.sasJsConfig.serverType === ServerType.SasViya) {
fetch('sasbuild/viya.json')
.then((res) => res.text())
.then((res) => {
let initJsonFile: any = null
try {
initJsonFile = JSON.parse(res)
} catch (err) {
console.error(err)
}
if (initJsonFile) {
this.jsonFile = initJsonFile
this.loggerService.log(this.jsonFile)
}
})
}
this.setDeployDefaults() this.setDeployDefaults()
} }
@@ -9,14 +9,17 @@
<p class="m-0 align-self-start">Done</p> <p class="m-0 align-self-start">Done</p>
<hr class="w-100" /> <hr class="w-100" />
<div class="deploy-status-row"> <div
*ngIf="autoDeployStatus.deployServicePack !== null"
class="deploy-status-row"
>
<clr-icon <clr-icon
*ngIf="autoDeployStatus.deployServicePack" *ngIf="autoDeployStatus.deployServicePack === true"
class="deploy-success" class="deploy-success"
shape="success-standard" shape="success-standard"
></clr-icon> ></clr-icon>
<clr-icon <clr-icon
*ngIf="!autoDeployStatus.deployServicePack" *ngIf="!autoDeployStatus.deployServicePack === false"
class="deploy-error" class="deploy-error"
shape="times-circle" shape="times-circle"
></clr-icon> ></clr-icon>
@@ -52,7 +55,7 @@
class="deploy-error" class="deploy-error"
shape="times-circle" shape="times-circle"
></clr-icon> ></clr-icon>
LAUNCH / CONFIGURE LAUNCH
</button> </button>
<button <button
@@ -94,20 +97,72 @@
</div> </div>
<label for="dcloc" class="mt-20 clr-control-label">DC Loc</label> <label for="dcloc" class="mt-20 clr-control-label">DC Loc</label>
<div class="mb-10 clr-control-container"> <div class="mb-10 clr-control-container dc-loc-input-wrapper">
<div class="clr-input-wrapper"> <div class="clr-input-wrapper small-mt">
<p class="mt-0">{{ dcPath }}</p> <input clrInput name="dcloc" [(ngModel)]="dcPath" />
</div> </div>
</div> </div>
<label for="dcloc" class="mt-20 clr-control-label">SAS Admin group</label> <label for="dcloc" class="mt-20 clr-control-label">SAS Admin group</label>
<div class="mb-10 clr-control-container"> <div class="mb-10 clr-control-container">
<div class="clr-input-wrapper"> <div class="clr-input-wrapper small-mt">
<p class="mt-0">{{ selectedAdminGroup }}</p> <select
*ngIf="!adminGroupsLoading"
clrSelect
name="options"
[(ngModel)]="selectedAdminGroup"
>
<option *ngFor="let adminGroup of adminGroups" [value]="adminGroup.id">
{{ adminGroup.name }}
</option>
</select>
<clr-spinner
clrInline
class="spinner-sm"
*ngIf="adminGroupsLoading"
></clr-spinner>
</div> </div>
</div> </div>
<clr-checkbox-wrapper> <label for="computeContext" class="mt-20 clr-control-label"
>Compute Context</label
>
<div class="mb-10 clr-control-container">
<div class="clr-input-wrapper small-mt">
<select
*ngIf="!computeContextsLoading"
clrSelect
name="options"
(ngModelChange)="onComputeContextChange($event)"
[(ngModel)]="selectedComputeContext"
>
<option
*ngFor="let computeContext of computeContexts"
[value]="computeContext.id"
>
{{ computeContext.name }}
</option>
</select>
<clr-spinner
clrInline
class="spinner-sm"
*ngIf="computeContextsLoading"
></clr-spinner>
</div>
</div>
<ng-container *ngIf="runningAsUser">
<label for="dcloc" class="mt-20 clr-control-label">Running as user:</label>
<div class="mb-10 clr-control-container">
<div class="clr-input-wrapper">
<p class="mt-0">{{ runningAsUser }}</p>
</div>
</div>
</ng-container>
<!-- Keeping this for a reference in case future VIYA changes and starts allowing separate backend and frontend) -->
<!-- <clr-checkbox-wrapper>
<input <input
clrCheckbox clrCheckbox
[(ngModel)]="recreateDatabase" [(ngModel)]="recreateDatabase"
@@ -116,19 +171,28 @@
checked checked
/> />
<label>Recreate database</label> <label>Recreate database</label>
</clr-checkbox-wrapper> </clr-checkbox-wrapper> -->
<hr /> <hr />
<button <button
(click)="runAutoDeploy()"
class="btn-autodeploy btn btn-primary d-inline-block mr-10"
>
Deploy
</button>
<!-- Keeping this for a reference in case future VIYA changes and starts allowing separate backend and frontend) -->
<!-- <button
(click)="executeJson()" (click)="executeJson()"
class="btn-autodeploy btn btn-primary d-inline-block mr-10" class="btn-autodeploy btn btn-primary d-inline-block mr-10"
[disabled]="!jsonFile" [disabled]="!jsonFile"
> >
Deploy {{ !jsonFile ? '(json file is not available)' : '' }} Deploy {{ !jsonFile ? '(json file is not available)' : '' }}
</button> </button> -->
<button <!-- <button
(click)="uploadJsonAuto.click()" (click)="uploadJsonAuto.click()"
class="btn-autodeploy btn btn-primary d-inline-block mr-10" class="btn-autodeploy btn btn-primary d-inline-block mr-10"
> >
@@ -140,7 +204,7 @@
hidden hidden
(click)="clearUploadInput($event)" (click)="clearUploadInput($event)"
(change)="onJsonFileChange($event)" (change)="onJsonFileChange($event)"
/> /> -->
<clr-modal [(clrModalOpen)]="recreateDatabaseModal" [clrModalClosable]="false"> <clr-modal [(clrModalOpen)]="recreateDatabaseModal" [clrModalClosable]="false">
<h3 class="modal-title">Warning</h3> <h3 class="modal-title">Warning</h3>
@@ -1,61 +0,0 @@
.auto-deploy {
display: flex;
justify-content: center;
align-items: center;
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.4);
z-index: 100;
}
.spinner-box {
width: 400px;
padding: 20px;
border-radius: 3px;
background: #fff;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
box-shadow: 1px 1px 8px 0px #00000082;
.buttons {
display: flex;
justify-content: space-between;
width: 100%;
}
}
.deploy-status-row {
display: flex;
align-items: center;
align-self: flex-start;
p {
margin: 0 0 0 10px;
}
}
.deploy-success {
color: #6ECF44;
}
.deploy-error {
color: #E74C3C;
// width: 20px;
// height: 20px;
}
.deploy-undeterminated {
color: #cacaca;
}
hr {
border: 0;
border-bottom: 1px solid #00000045;
}
@@ -1,15 +1,34 @@
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core' import {
Component,
EventEmitter,
Input,
OnInit,
Output,
ViewEncapsulation
} from '@angular/core'
import SASjs, { SASjsConfig } from '@sasjs/adapter' import SASjs, { SASjsConfig } from '@sasjs/adapter'
import { DcAdapterSettings } from 'src/app/models/DcAdapterSettings' import { DcAdapterSettings } from 'src/app/models/DcAdapterSettings'
import { DeployService } from 'src/app/services/deploy.service' import { DeployService } from 'src/app/services/deploy.service'
import { EventService } from 'src/app/services/event.service' import { EventService } from 'src/app/services/event.service'
import { LoggerService } from 'src/app/services/logger.service' import { LoggerService } from 'src/app/services/logger.service'
import { SasViyaService } from 'src/app/services/sas-viya.service'
import { SasService } from 'src/app/services/sas.service' import { SasService } from 'src/app/services/sas.service'
import { ViyaApiCurrentUser } from 'src/app/viya-api-explorer/models/viya-api-current-user.model'
import {
Item,
ViyaApiIdentities
} from 'src/app/viya-api-explorer/models/viya-api-identities.model'
import { ComputeContextDetails } from 'src/app/viya-api-explorer/models/viya-compute-context-details.model'
import {
ViyaComputeContexts,
Item as ComputeContextItem
} from 'src/app/viya-api-explorer/models/viya-compute-contexts.model'
@Component({ @Component({
selector: 'app-automatic-deploy', selector: 'app-automatic-deploy',
templateUrl: './automatic.component.html', templateUrl: './automatic.component.html',
styleUrls: ['./automatic.component.scss'] styleUrls: ['./automatic.component.scss'],
encapsulation: ViewEncapsulation.None
}) })
export class AutomaticComponent implements OnInit { export class AutomaticComponent implements OnInit {
@Input() sasJs!: SASjs @Input() sasJs!: SASjs
@@ -21,6 +40,7 @@ export class AutomaticComponent implements OnInit {
@Output() onNavigateToHome: EventEmitter<any> = new EventEmitter<any>() @Output() onNavigateToHome: EventEmitter<any> = new EventEmitter<any>()
public selectedComputeContext: string = ''
public makeDataResponse: string = '' public makeDataResponse: string = ''
public jsonFile: any = null public jsonFile: any = null
public autodeploying: boolean = false public autodeploying: boolean = false
@@ -28,8 +48,19 @@ export class AutomaticComponent implements OnInit {
public recreateDatabaseModal: boolean = false public recreateDatabaseModal: boolean = false
public isSubmittingJson: boolean = false public isSubmittingJson: boolean = false
public isJsonSubmitted: boolean = false public isJsonSubmitted: boolean = false
public recreateDatabase: boolean = false /**
* Default was `false` when deploy was done with frontend and backend separately.
* Now we are using only streaming app, so we always want to recreate database (makedata)
*/
public recreateDatabase: boolean = true
public createDatabaseLoading: boolean = false public createDatabaseLoading: boolean = false
public adminGroupsLoading: boolean = false
public currentUserInfoLoading: boolean = false
public computeContextsLoading: boolean = false
public adminGroups: { id: string; name: string }[] = []
public runningAsUser: string | undefined
public currentUserInfo: ViyaApiCurrentUser | null = null
public computeContexts: ComputeContextItem[] = []
/** autoDeployStatus /** autoDeployStatus
* This object presents the status for two steps that we have for deploy. * This object presents the status for two steps that we have for deploy.
@@ -50,10 +81,123 @@ export class AutomaticComponent implements OnInit {
private eventService: EventService, private eventService: EventService,
private deployService: DeployService, private deployService: DeployService,
private sasService: SasService, private sasService: SasService,
private sasViyaService: SasViyaService,
private loggerService: LoggerService private loggerService: LoggerService
) {} ) {}
ngOnInit(): void {} ngOnInit(): void {
this.loadData()
}
public async loadData() {
await this.getAdminGroups()
await this.getComputeContexts()
await this.getCurrentUser()
setTimeout(() => {
if (this.selectedComputeContext) {
this.onComputeContextChange(this.selectedComputeContext)
}
}, 500)
}
public async getComputeContexts() {
return new Promise<void>((resolve, reject) => {
this.computeContextsLoading = true
this.sasViyaService.getComputeContexts().subscribe(
(res: ViyaComputeContexts) => {
this.computeContextsLoading = false
const defaultContext = res.items.find(
(item: ComputeContextItem) =>
item.name === 'SAS Job Execution compute context'
)
if (defaultContext) {
this.selectedComputeContext = defaultContext.id
}
this.computeContexts = res.items
resolve()
},
(err) => {
reject(err)
}
)
})
}
public async getCurrentUser() {
return new Promise<void>((resolve, reject) => {
this.currentUserInfoLoading = true
this.sasViyaService.getCurrentUser().subscribe(
(res: ViyaApiCurrentUser) => {
this.currentUserInfoLoading = false
this.currentUserInfo = res
this.dcPath = `/export/viya/homes/${res.id}`
resolve()
},
(err) => {
console.error('Error while getting current user', err)
reject(err)
}
)
})
}
public async getAdminGroups() {
return new Promise<void>((resolve, reject) => {
this.adminGroupsLoading = true
this.sasViyaService
.getAdminGroups()
.subscribe((res: ViyaApiIdentities) => {
this.adminGroupsLoading = false
// Map admin groups with only needed fields
this.adminGroups = res.items.map((item: Item) => {
return {
id: item.id,
name: item.name
}
})
resolve()
}),
(err: any) => {
this.adminGroupsLoading = false
this.loggerService.error('Error while getting admin groups', err)
this.eventService.showAbortModal('admin groups', err)
reject(err)
}
})
}
public async onComputeContextChange(computeContextId: string) {
this.sasViyaService
.getComputeContextById(computeContextId)
.subscribe((res: ComputeContextDetails) => {
if (res.attributes && res.attributes.runServerAs) {
this.runningAsUser = res.attributes.runServerAs
} else {
this.runningAsUser = this.currentUserInfo?.id || 'unknown'
}
})
}
public getComputeContextName(id: string): string | undefined {
return (
this.computeContexts.find(
(context: ComputeContextItem) => context.id === id
)?.name || undefined
)
}
/** /**
* Executes sas.json file to deploy the backend * Executes sas.json file to deploy the backend
@@ -63,7 +207,6 @@ export class AutomaticComponent implements OnInit {
* to create database if checkbox is toggled on * to create database if checkbox is toggled on
*/ */
public async executeJson() { public async executeJson() {
this.autodeploying = true
this.isSubmittingJson = true this.isSubmittingJson = true
try { try {
@@ -98,6 +241,14 @@ export class AutomaticComponent implements OnInit {
} }
this.isSubmittingJson = false this.isSubmittingJson = false
}
public async runAutoDeploy(executeJson: boolean = false) {
this.autodeploying = true
if (executeJson) {
this.executeJson()
}
if (this.recreateDatabase) { if (this.recreateDatabase) {
this.createDatabase() this.createDatabase()
@@ -119,14 +270,27 @@ export class AutomaticComponent implements OnInit {
] ]
} }
// Get and run service using the selected context name
let selectedComputeContextName = this.sasJsConfig.contextName
if (this.selectedComputeContext.length && this.computeContexts.length) {
const computeContextName = this.getComputeContextName(
this.selectedComputeContext
)
if (computeContextName) {
selectedComputeContextName = computeContextName
}
}
/** /**
* We are overriding default `sasjsConfig` object fields with this object fields. * We are overriding default `sasjsConfig` object fields with this object fields.
* Here we want to run this request using original WEB method. * Here we want to run this request using original WEB method.
* contextName: null is the MUST field for it. * contextName: null is the MUST field for it.
*/ */
let overrideConfig = { let overrideConfig = {
useComputeApi: false, useComputeApi: null,
contextName: this.sasJsConfig.contextName, contextName: selectedComputeContextName,
debug: true debug: true
} }
@@ -148,8 +312,23 @@ export class AutomaticComponent implements OnInit {
} else { } else {
this.autoDeployStatus.runMakeData = false this.autoDeployStatus.runMakeData = false
} }
if (typeof res.sasjsAbort !== 'undefined') {
const abortRes = res
const abortMsg = abortRes.sasjsAbort[0].MSG
const macMsg = abortRes.sasjsAbort[0].MAC
this.eventService.showAbortModal('makedata', abortMsg, {
SYSWARNINGTEXT: abortRes.SYSWARNINGTEXT,
SYSERRORTEXT: abortRes.SYSERRORTEXT,
MAC: macMsg
})
}
this.updateIndexHtmlComputeContext()
}) })
.catch((err: any) => { .catch((err: any) => {
this.eventService.showAbortModal('makedata', JSON.stringify(err))
this.autoDeployStatus.runMakeData = false this.autoDeployStatus.runMakeData = false
this.autodeployDone = true this.autodeployDone = true
@@ -161,6 +340,50 @@ export class AutomaticComponent implements OnInit {
}) })
} }
/**
* Only when on Viya, this method will update the `contextname` in the `DataController.html` on the SAS drive
* This is needed to ensure that the DC will use the same compute context `makedata` service used to run against.
*/
public async updateIndexHtmlComputeContext() {
const indexHtmlContent = await this.sasService.getFileContent(
`${this.appLoc}/services`,
'DataController.html'
)
if (!indexHtmlContent) {
this.loggerService.error(
`Failed to get DataController.html at ${this.appLoc}/services`
)
return
}
const computeContextName = this.getComputeContextName(
this.selectedComputeContext
)
if (!computeContextName) {
this.loggerService.error(
`Compute context name not found for ID: ${this.selectedComputeContext} | List: ${JSON.stringify(this.computeContexts)}`
)
return
}
const updatedContent = indexHtmlContent.replace(
/contextname="[^"]*"/g,
`contextname="${computeContextName}"`
)
await this.sasService
.updateFileContent(
`${this.appLoc}/services`,
'DataController.html',
updatedContent
)
.catch((err: any) => {
this.loggerService.error(`Failed to update DataController.html: ${err}`)
})
}
public downloadFile( public downloadFile(
content: any, content: any,
filename: string, filename: string,
@@ -1,4 +0,0 @@
.clear-memory-button {
right: 10px;
top: 2px;
}
@@ -1,4 +1,11 @@
import { Component, Input, OnInit, Output, EventEmitter } from '@angular/core' import {
Component,
Input,
OnInit,
Output,
EventEmitter,
ViewEncapsulation
} from '@angular/core'
import SASjs, { SASjsConfig } from '@sasjs/adapter' import SASjs, { SASjsConfig } from '@sasjs/adapter'
import { DcAdapterSettings } from 'src/app/models/DcAdapterSettings' import { DcAdapterSettings } from 'src/app/models/DcAdapterSettings'
import { RequestWrapperResponse } from 'src/app/models/request-wrapper/RequestWrapperResponse' import { RequestWrapperResponse } from 'src/app/models/request-wrapper/RequestWrapperResponse'
@@ -10,7 +17,8 @@ import { SasService } from 'src/app/services/sas.service'
@Component({ @Component({
selector: 'app-manual-deploy', selector: 'app-manual-deploy',
templateUrl: './manual.component.html', templateUrl: './manual.component.html',
styleUrls: ['./manual.component.scss'] styleUrls: ['./manual.component.scss'],
encapsulation: ViewEncapsulation.None
}) })
export class ManualComponent implements OnInit { export class ManualComponent implements OnInit {
@Input() sasJs!: SASjs @Input() sasJs!: SASjs
@@ -266,7 +274,7 @@ export class ManualComponent implements OnInit {
* contextName: null is the MUST field for it. * contextName: null is the MUST field for it.
*/ */
let overrideConfig = { let overrideConfig = {
useComputeApi: false, useComputeApi: null,
contextName: this.sasJsConfig.contextName, contextName: this.sasJsConfig.contextName,
debug: true debug: true
} }
@@ -1,23 +0,0 @@
.clr-control-container {
width: 50vw;
}
.clr-input-wrapper {
width: 100%;
input {
width: 100%;
}
}
.thinProgress {
left: 0px;
right: 0;
width: unset;
height: 1px;
margin-top: 0 !important;
&::after {
top: 0;
}
}
@@ -1,5 +1,12 @@
import { Location } from '@angular/common' import { Location } from '@angular/common'
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core' import {
Component,
EventEmitter,
Input,
OnInit,
Output,
ViewEncapsulation
} from '@angular/core'
import SASjs, { SASjsConfig } from '@sasjs/adapter' import SASjs, { SASjsConfig } from '@sasjs/adapter'
import { ServerType } from '@sasjs/utils/types/serverType' import { ServerType } from '@sasjs/utils/types/serverType'
import { DcAdapterSettings } from 'src/app/models/DcAdapterSettings' import { DcAdapterSettings } from 'src/app/models/DcAdapterSettings'
@@ -12,7 +19,8 @@ import { SasjsService } from 'src/app/services/sasjs.service'
@Component({ @Component({
selector: 'app-sasjs-configurator', selector: 'app-sasjs-configurator',
templateUrl: './sasjs-configurator.component.html', templateUrl: './sasjs-configurator.component.html',
styleUrls: ['./sasjs-configurator.component.scss'] styleUrls: ['./sasjs-configurator.component.scss'],
encapsulation: ViewEncapsulation.None
}) })
export class SasjsConfiguratorComponent implements OnInit { export class SasjsConfiguratorComponent implements OnInit {
@Input() sasJs!: SASjs @Input() sasJs!: SASjs
@@ -1,243 +0,0 @@
.record-edit-modal {
.column-entry {
display: flex;
justify-content: space-between;
.name-input-row {
width: 100%;
max-width: 260px;
.cell-desc {
margin-right: 30px;
margin-top: 10px;
}
}
.inputs-wrapper {
flex: 1;
display: flex;
align-items: center;
::ng-deep >*:not(.date-field):not(clr-select-container) {
flex: 1;
}
}
p {
margin-top: 0px;
}
::ng-deep {
.clr-textarea-wrapper {
margin-top: 0 !important;
}
.clr-form-control {
margin-top: 0px !important;
}
app-soft-select {
display: block;
width: 224px;
border: 1px solid #999;
color: #000;
padding: calc(.25rem + 2px) .5rem;
border-radius: .125rem;
font-size: .541667rem;
margin-right: 6px;
input {
width: 100%;
border: 0;
&:focus {
background: none;
border: 0 !important;
}
&::-webkit-outer-spin-button,
&::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
}
}
}
&:first-child p:first-child {
margin-top: 0;
}
}
.date-field {
position: relative;
display: inline-block;
textarea {
width: 230px;
}
.date-picker {
position: absolute;
right: 0;
top: 4px;
::ng-deep {
// clr-datepicker-view-manager {
// transform: unset !important;
// left: unset !important;
// right: 70px !important;
// }
.clr-input-group {
border: 0 !important;
}
}
}
}
.modal-body {
padding-bottom: 10px;
}
::ng-deep {
clr-select-container {
border: 1px solid #999;
color: #000;
border-radius: .125rem;
margin-right: 5px;
.clr-select-wrapper {
max-height: unset;
&::after {
top: 15px;
}
}
select {
height: auto;
padding: 10px;
padding-right: 20px;
border: 0 !important;
&:focus {
background: 0 0 !important;
}
&:hover {
background: transparent;
}
}
}
clr-input-container {
width: 224px;
border: 1px solid #999;
color: #000;
padding: calc(.25rem + 2px) .5rem;
border-radius: .125rem;
font-size: .541667rem;
margin-right: 6px;
.clr-input-group {
width: 100%;
border: 0;
}
input {
width: 100%;
border: 0;
&:focus {
background: none;
border: 0 !important;
}
&::-webkit-outer-spin-button,
&::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
}
&.invalid-data {
border-color: red;
}
}
.modal-dialog {
width: 80vw;
}
.clr-control-container {
width: 100%;
textarea {
width: 100%;
resize: none;
border-color: #999;
&.invalid-data {
border-color: red;
outline: 0;
}
&.not-char {
font-family: "Lucida Console", Monaco, monospace;
}
}
}
.generate-record-url {
right: 40px;
top: 40px;
font-size: 12px;
}
.generate-record-url-button {
right: 25px;
top: 5px;
}
.modal-header {
padding: 0 0 1rem 0;
}
.modal-footer {
display: flex;
align-items: center;
justify-content: space-between;
// height: 65px;
.alert {
margin: 0;
}
}
}
}
.prev-next {
display: flex;
align-items: center;
p {
margin: 0;
}
button {
margin: 0px 10px;
}
}
.focusable {
&:focus {
box-shadow: 0 0 3px 0px #5aa220;
}
}
.entry-input-left-offset {
left: -30px;
}
.validation-info-alert {
width: 310px
}
@@ -1,5 +1,12 @@
import { KeyValue } from '@angular/common' import { KeyValue } from '@angular/common'
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core' import {
Component,
EventEmitter,
Input,
OnInit,
Output,
ViewEncapsulation
} from '@angular/core'
import moment from 'moment' import moment from 'moment'
import { ValidateFilterSASResponse } from 'src/app/models/sas/validate-filter.model' import { ValidateFilterSASResponse } from 'src/app/models/sas/validate-filter.model'
import { QueryClause } from 'src/app/models/TableData' import { QueryClause } from 'src/app/models/TableData'
@@ -16,7 +23,8 @@ import { EditRecordModal } from '../../models/EditRecordModal'
@Component({ @Component({
selector: 'app-edit-record', selector: 'app-edit-record',
templateUrl: './edit-record.component.html', templateUrl: './edit-record.component.html',
styleUrls: ['./edit-record.component.scss'] styleUrls: ['./edit-record.component.scss'],
encapsulation: ViewEncapsulation.None
}) })
export class EditRecordComponent implements OnInit { export class EditRecordComponent implements OnInit {
@Input() currentRecord!: EditRecordModal @Input() currentRecord!: EditRecordModal
@@ -1,8 +0,0 @@
:host {
display: block;
}
p {
margin: 0;
text-align: center;
}
@@ -1,4 +1,4 @@
import { Component, OnInit } from '@angular/core' import { Component, OnInit, ViewEncapsulation } from '@angular/core'
/** /**
* Goal of this component is to recieve array of strings where every element is one state * Goal of this component is to recieve array of strings where every element is one state
@@ -10,7 +10,8 @@ import { Component, OnInit } from '@angular/core'
@Component({ @Component({
selector: 'app-upload-stater', selector: 'app-upload-stater',
templateUrl: './upload-stater.component.html', templateUrl: './upload-stater.component.html',
styleUrls: ['./upload-stater.component.scss'] styleUrls: ['./upload-stater.component.scss'],
encapsulation: ViewEncapsulation.None
}) })
export class UploadStaterComponent implements OnInit { export class UploadStaterComponent implements OnInit {
public statesList: string[] = [] //States appended to be displayed public statesList: string[] = [] //States appended to be displayed
+2 -2
View File
@@ -36,7 +36,7 @@
<div class="clr-row card-block mt-15 d-flex justify-content-between"> <div class="clr-row card-block mt-15 d-flex justify-content-between">
<div class="clr-col-md-auto"> <div class="clr-col-md-auto">
<div class="encoding-block"> <div class="encoding-block">
<clr-radio-container class="mt-0-i" clrInline> <clr-radio-container class="mt-0" clrInline>
<clr-radio-wrapper> <clr-radio-wrapper>
<input <input
type="radio" type="radio"
@@ -193,7 +193,7 @@
libName: (libds?.split('.'))![0], libName: (libds?.split('.'))![0],
tableName: (libds?.split('.'))![1] tableName: (libds?.split('.'))![1]
} as libdsParsed" } as libdsParsed"
class="editor-title text-center mt-0-i" class="editor-title text-center mt-0"
> >
<clr-tooltip> <clr-tooltip>
<clr-icon <clr-icon
-247
View File
@@ -1,247 +0,0 @@
.card {
margin-top: 0;
border: 0;
}
.buttonBar {
padding: 2px 10px 2px 10px;
align-items: center;
}
.testRed {
color: white;
background: rgba(255,0,0, 0.8) !important;
}
hot-table {
::ng-deep {
.firstColumnHeaderStyle button.changeType {
display: none;
}
.handsontable tbody th.ht__highlight, .handsontable thead th.ht__highlight {
&.primaryKeyHeaderStyle {
background-color: #306b00b0 !important;
}
}
.primaryKeyHeaderStyle {
background-color: #306b006e !important;
}
th.readonlyCell {
div {
opacity: 0.4;
}
}
td.readonlyCell {
opacity: 0.5
}
}
}
.submit-reason {
min-height: 120px;
max-height: 120px;
height: 120px;
}
.infoBar {
margin-top:14px;
background: #495967;
color: white;
text-align:center;
padding: 3px;
font-size: 16px;
height: 30px;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
span {
width: 80%;
}
&:hover {
height: unset;
white-space: normal;
span {
width: unset;
}
}
}
.pkHeader {
background: #687682;
color: #fff;
margin: -1px -1px -1px -1px;
}
.headerBar {
// padding: 13px 0px 14px 0px;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
background: var(--clr-vertical-nav-bg-color);
}
.error-icon {
width: 30px;
height: 30px;
color: red;
}
.btnCtrl {
display:flex;
justify-content:flex-end;
}
.card-header {
border-bottom: 1px solid transparent;
}
.hidden {
visibility: hidden;
}
.my-drop-zone {
border: solid 1px lightgray;
border-radius: 10px;
background: whitesmoke;
box-shadow: inset 0px 0px 4px 2px #a7a5a52b;
height: 50vh;
}
.nv-file-over {
border: solid 2px green;
} /* Default class applied to drop zones on over */
.file-drop-text{
text-align: center;
}
.nv-file-over {
border: solid 2px green;
} /* Default class applied to drop zones on over */
.file-drop-text{
text-align: center;
}
@media screen and (max-width: 768px) {
.progresStatic {
margin-top:9px!important;
}
.progress, .progress-static {
width: calc(100% - 14px);
}
}
.hotEditor {
position: relative;
}
.excel-parsing {
display: flex;
flex-direction: column;
align-items: center;
position: relative;
.details {
margin: 0;
position: absolute;
top: -45px;
}
}
.edit-record-spinner {
display: flex;
justify-content: center;
align-items: center;
background: rgba(255, 255, 255, 0.6);
position: absolute;
top: 0px;
bottom: 0px;
width: 100%;
z-index: 500;
}
@media screen and (max-width: 480px) {
.progresStatic {
margin-top:32px!important;
}
.card-block, .card-footer {
padding: 10px 0px 0px 0px;
}
}
.content-area {
padding: 0 0.8rem 0.8rem 0.8rem !important;
padding-top: 0;
// .card {
// min-height: calc(100vh - 160px);
// }
}
.drop-area {
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
display: flex;
justify-content: center;
align-items: flex-start;
margin: 1px;
border: 2px dashed #fff;
z-index: -1;
span {
font-size: 20px;
margin-top: 20px;
padding: 10px;
background: #dbdbdb;
border-radius: 5px;
color: black;
}
}
#submitBtn, #cancelSubmitBtn {
width: 150px;
}
.view-table {
font-size: inherit !important;
color: #1a57bb !important;
}
// When width is smaller remove the text from the buttons
// keep only the icons
@media (max-width: 992px) {
.icon-collapse {
.text {
display: none;
}
}
}
// FIXME
// Let's leave it here for a reference if there
// is an issue with viewboxes/filter modal overlaying
// we will remove it if no issues found
// .filter-modal {
// z-index: 1210;
// }
+1 -1
View File
@@ -69,7 +69,7 @@ import { ParseResult } from '../models/ParseResult.interface'
host: { host: {
class: 'content-container' class: 'content-container'
}, },
encapsulation: ViewEncapsulation.Emulated encapsulation: ViewEncapsulation.None
}) })
export class EditorComponent implements OnInit, AfterViewInit { export class EditorComponent implements OnInit, AfterViewInit {
@ViewChildren('uploadStater') @ViewChildren('uploadStater')
-85
View File
@@ -1,85 +0,0 @@
@import '../../colors.scss';
::ng-deep body[cds-theme="dark"] {
.group-info {
background-color: $headerBackground;
border-color: $headerBackground;
}
.group-data {
background-color: $headerBackground;
border-color: $headerBackground;
}
.member-table tbody{
tr:hover{
background-color: #29404b;
}
}
}
::ng-deep body[cds-theme="light"] {
.group-info{
background-color: #f9f9f9;
border-color: #a7a7a7;
box-shadow: 0px 2px 5px #dad7d7;
}
.group-data {
background-color: #f9f9f9;
border-color: #a7a7a7;
box-shadow: 0px 2px 5px #dad7d7;
}
.member-table tbody{
tr:hover{
background-color: #e6e6e6;
}
}
}
.sidebar-height{
height: 100%;
}
.group-info-text{
display: inline;
font-size: 20px;
}
.group-info{
border: 1px solid;
border-radius: 3px;
}
.group-info td{
text-align: center;
}
.group-data{
border: 1px solid;
border-radius: 3px;
}
.group-data{
min-height: auto;
h3, h5{
text-align: center;
}
.member-table{
width: 100%;
}
.member-table tbody{
tr:hover{
cursor: pointer;
}
}
}
.table-container{
overflow: auto;
}
@media screen and (max-width: 768px){
.group-data{
min-height: unset !important;
}
}
+3 -2
View File
@@ -1,5 +1,5 @@
import { Location } from '@angular/common' import { Location } from '@angular/common'
import { Component, OnInit } from '@angular/core' import { Component, OnInit, ViewEncapsulation } from '@angular/core'
import { ActivatedRoute, Router } from '@angular/router' import { ActivatedRoute, Router } from '@angular/router'
import { SASjsConfig } from '@sasjs/adapter' import { SASjsConfig } from '@sasjs/adapter'
import { ServerType } from '@sasjs/utils/types/serverType' import { ServerType } from '@sasjs/utils/types/serverType'
@@ -14,7 +14,8 @@ import { RequestWrapperResponse } from '../models/request-wrapper/RequestWrapper
styleUrls: ['./group.component.scss'], styleUrls: ['./group.component.scss'],
host: { host: {
class: 'content-container' class: 'content-container'
} },
encapsulation: ViewEncapsulation.None
}) })
export class GroupComponent implements OnInit { export class GroupComponent implements OnInit {
public groups: Array<any> | undefined public groups: Array<any> | undefined
-32
View File
@@ -1,32 +0,0 @@
clr-tree-node button {
white-space: nowrap;
}
.card-block {
height: 100%;
padding: 0;
}
.no-table-selected {
position: relative;
height: 100%;
}
::ng-deep {
// .badge.badge-info {
// background: #6a9235!important;
// color: #fff;
// }
clr-icon.is-blue, clr-icon.is-info {
fill: #6a9235;
}
}
.spinner-wrapper-fullpage {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
}
+3 -2
View File
@@ -3,7 +3,7 @@
* This software is released under MIT license. * This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project. * The full license information can be found in LICENSE in the root directory of this project.
*/ */
import { Component, AfterContentInit } from '@angular/core' import { Component, AfterContentInit, ViewEncapsulation } from '@angular/core'
import { Router } from '@angular/router' import { Router } from '@angular/router'
import { ActivatedRoute } from '@angular/router' import { ActivatedRoute } from '@angular/router'
import { globals } from '../_globals' import { globals } from '../_globals'
@@ -18,7 +18,8 @@ import { LicenceService } from '../services/licence.service'
templateUrl: './home.component.html', templateUrl: './home.component.html',
host: { host: {
class: 'content-container' class: 'content-container'
} },
encapsulation: ViewEncapsulation.None
}) })
export class HomeComponent implements AfterContentInit { export class HomeComponent implements AfterContentInit {
public treeNodeLibraries: Array<any> | null = null public treeNodeLibraries: Array<any> | null = null
@@ -1,51 +0,0 @@
:host {
height: calc(100% - 96px);
padding: 20px 20px;
}
.card {
margin-top: 0;
}
.key-error {
font-size: 16px;
}
.misskey {
color: #E74C3C;
}
.license-key-form, .activation-key-form {
padding: 0;
.clr-control-container {
width: 100%;
textarea {
width: 100%;
height: 170px;
max-height: 170px;
min-height: 170px;
resize: none;
}
}
}
.apply-keys {
height: 40px;
}
.drop-area {
display: flex;
justify-content: center;
align-items: center;
padding: 15px;
border: 2px dashed #b2b2b2;
border-radius: 4px;
cursor: pointer;
margin: 10px 0;
}
clr-tabs button {
box-shadow: none !important
}
@@ -1,4 +1,4 @@
import { Component, OnInit } from '@angular/core' import { Component, OnInit, ViewEncapsulation } from '@angular/core'
import { ActivatedRoute, Router } from '@angular/router' import { ActivatedRoute, Router } from '@angular/router'
import { AppService, LicenceService, SasService } from '../services' import { AppService, LicenceService, SasService } from '../services'
import { LicenseKeyData } from '../models/LicenseKeyData' import { LicenseKeyData } from '../models/LicenseKeyData'
@@ -14,7 +14,8 @@ enum LicenseActions {
@Component({ @Component({
selector: 'app-licensing', selector: 'app-licensing',
templateUrl: './licensing.component.html', templateUrl: './licensing.component.html',
styleUrls: ['./licensing.component.scss'] styleUrls: ['./licensing.component.scss'],
encapsulation: ViewEncapsulation.None
}) })
export class LicensingComponent implements OnInit { export class LicensingComponent implements OnInit {
public action: LicenseActions | null = null public action: LicenseActions | null = null
@@ -1,85 +0,0 @@
@import '../../colors.scss';
.toggle-switch input[type=checkbox]:checked+label:before {
border-color: $headerBackground;
background-color: $headerBackground !important;
transition: .15s ease-in;
transition-property: border-color,background-color;
}
#graph{
height: calc(100vh - 195px);
overflow: hidden;
text-align: center;
display: block;
width: 100%;
border: 1px solid #e4e4e4;
margin-top: 10px;
}
.selection-wrapper {
width: 100%;
max-width: 670px;
}
.column-active {
background: #d8e3e9;
color: black;
}
.content-area {
padding: 0.5rem !important;
.card {
min-height: calc(100vh - 120px);
.card-block {
padding: 0.5rem 0.35rem !important;
}
}
}
clr-tree-node button {
white-space: nowrap;
}
.btn-group.direction {
margin-left: var(--cds-global-space-6);
}
.graph-render-spinner {
position: absolute;
top: 0;
width: 100%;
display: flex;
justify-content: center;
margin-top: 10px;
}
.biglineage-row {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.modal-footer {
p {
margin: 0;
}
}
.lineage-title-wrapper {
left: 12px;
}
.max-depth-input {
width: 100%;
}
@media (max-width: 768px) {
.toggle-switch-container {
margin-bottom: 20px;
}
}
+3 -2
View File
@@ -1,4 +1,4 @@
import { Component } from '@angular/core' import { Component, ViewEncapsulation } from '@angular/core'
import { Location } from '@angular/common' import { Location } from '@angular/common'
import { globals } from '../_globals' import { globals } from '../_globals'
import * as d3Viz from 'd3-graphviz' import * as d3Viz from 'd3-graphviz'
@@ -18,7 +18,8 @@ const moment = require('moment')
templateUrl: './lineage.component.html', templateUrl: './lineage.component.html',
host: { host: {
class: 'content-container' class: 'content-container'
} },
encapsulation: ViewEncapsulation.None
}) })
export class LineageComponent { export class LineageComponent {
public switchFlag: boolean = false public switchFlag: boolean = false
@@ -1,82 +0,0 @@
::ng-deep body[cds-theme="dark"] {
.object-header:hover {
background-color: #405560;
}
}
::ng-deep body[cds-theme="light"] {
.objects-col {
background: white;
}
.object-header:hover {
background-color: #d8e3e9;
}
}
.objects-col{
height: 75vh;
overflow: scroll;
border: 1px solid #cccccc;
border-radius: 4px;
}
.cols-head {
border: 1px solid #cccccc;
padding: 10px;
display: flex;
}
.object-text {
display: flex;
align-items: flex-start;
justify-content: space-between;
margin-left: 10px;
flex: 1;
}
.repo-dropdown{
margin-right: 15px;
margin-left: 15px;
margin-bottom: 10px;
}
.clr-accordion-title{
width: 100%;
}
.float-right{
margin: 0px;
float: right;
}
.full-width{
width: 100%;
}
.object-uri{
margin: 0px;
margin-top: 5px;
}
.object-header{
display: flex;
align-items: center;
justify-content: space-between;
padding-left: 3px;
padding-right: 3px;
}
.object-header:hover{
border-radius: 3px;
}
.datagrid-host{
display: unset !important;
}
.card {
margin-top: 0;
flex: 1;
display: flex;
flex-direction: column;
}
.content-area {
padding: 0.5rem !important;
display: flex;
flex-direction: column;
}
@@ -1,5 +1,5 @@
import { Location } from '@angular/common' import { Location } from '@angular/common'
import { Component, OnInit } from '@angular/core' import { Component, OnInit, ViewEncapsulation } from '@angular/core'
import { ActivatedRoute, Router } from '@angular/router' import { ActivatedRoute, Router } from '@angular/router'
import { ClrDatagridStringFilterInterface } from '@clr/angular' import { ClrDatagridStringFilterInterface } from '@clr/angular'
import { Observable, of } from 'rxjs' import { Observable, of } from 'rxjs'
@@ -50,7 +50,8 @@ class ValueFilter implements ClrDatagridStringFilterInterface<MetaData> {
styleUrls: ['./metadata.component.scss'], styleUrls: ['./metadata.component.scss'],
host: { host: {
class: 'content-container' class: 'content-container'
} },
encapsulation: ViewEncapsulation.None
}) })
export class MetadataComponent implements OnInit { export class MetadataComponent implements OnInit {
metaDataList: Array<any> | undefined metaDataList: Array<any> | undefined
@@ -1,54 +0,0 @@
.no-table-selected {
display: flex;
justify-content: center;
flex-direction: column;
align-items: center;
position: absolute;
background: var(--clr-vertical-nav-bg-color);
z-index: 10;
width: 100%;
height: 100%;
top: 0;
}
.header-row {
padding: 15px 0;
border-bottom: 1px solid #d3d3d3;
}
.dataset-input-wrapper {
max-width: 500px;
width: 100%;
textarea {
min-height: 200px;
height: 200px;
}
}
.submit-reason {
min-height: 70px;
max-height: 70px;
height: 70px;
}
.log-wrapper {
margin: 0 10px;
height: auto;
}
::ng-deep td.not-matched {
background-color: #ff000054;
}
.dataset-selection-actions {
border-top: 1px solid #d3d3d3;
}
.licence-limit-notice {
color: var(--cds-alias-status-warning-dark);
}
.submission-results {
border-bottom: 1px solid #d3d3d3;
}
@@ -4,7 +4,8 @@ import {
ElementRef, ElementRef,
HostBinding, HostBinding,
OnInit, OnInit,
ViewChild ViewChild,
ViewEncapsulation
} from '@angular/core' } from '@angular/core'
import { import {
EventService, EventService,
@@ -45,7 +46,8 @@ enum FileLoadingState {
@Component({ @Component({
selector: 'app-multi-dataset', selector: 'app-multi-dataset',
templateUrl: './multi-dataset.component.html', templateUrl: './multi-dataset.component.html',
styleUrls: ['./multi-dataset.component.scss'] styleUrls: ['./multi-dataset.component.scss'],
encapsulation: ViewEncapsulation.None
}) })
export class MultiDatasetComponent implements OnInit { export class MultiDatasetComponent implements OnInit {
@HostBinding('class.content-container') contentContainerClass = true @HostBinding('class.content-container') contentContainerClass = true
@@ -1,4 +1,4 @@
import { Component, OnInit } from '@angular/core' import { Component, OnInit, ViewEncapsulation } from '@angular/core'
@Component({ @Component({
selector: 'app-not-found', selector: 'app-not-found',
@@ -6,7 +6,8 @@ import { Component, OnInit } from '@angular/core'
styleUrls: ['./not-found.component.scss'], styleUrls: ['./not-found.component.scss'],
host: { host: {
class: 'content-container' class: 'content-container'
} },
encapsulation: ViewEncapsulation.None
}) })
export class NotFoundComponent implements OnInit { export class NotFoundComponent implements OnInit {
constructor() {} constructor() {}
-307
View File
@@ -1,307 +0,0 @@
::ng-deep {
body[cds-theme="dark"] {
.clause-logic {
background: #192a30;
}
.clause-query {
background: #263e48;
}
}
body[cds-theme="light"] {
.clause-logic {
background: #e9e9e9;
}
.clause-query {
background: #fbf8f8;
}
}
}
.content {
display: flex;
.clauses-container {
display: flex;
flex-direction: column;
.clause-logic {
display: flex;
justify-content: center;
flex-direction: column;
padding: 15px;
}
.clause-query {
padding: 30px 0px 20px 20px;
display: flex;
justify-content: center;
flex-direction: column;
position: relative;
& > .clr-row {
justify-content: space-between;
&:not(:last-child) {
padding-bottom: 15px;
margin-bottom: 15px;
border-bottom: 1px solid rgba(0, 0, 0, 0.16)
}
}
.remove-group-clause-button {
position: absolute;
top: 0px;
right: 10px;
color: gray;
}
.variable-col {
display: flex;
align-items: flex-start;
padding-bottom: 1px;
.datalist-wrapper {
width: 100%;
input {
width: 100%;
}
}
}
.operator-col {
display: flex;
align-items: flex-start;
clr-select-container {
height: 45px;
margin-top: 0;
width: 100%;
}
}
.value-col {
display: flex;
align-items: flex-start;
padding-bottom: 1px;
.checkbox-vals {
width: 100%;
padding: 0px 5px;
border-bottom: 1px solid rgba(0, 0, 0, 0.3);
clr-checkbox-container {
margin-top: 0;
}
section {
max-height: 120px;
overflow-y: scroll;
}
}
.single-field-vals {
width: 100%;
::ng-deep {
.clr-control-container {
width: 100%;
.clr-input-wrapper {
max-width: none;
.clr-input-group {
width: 100%;
}
}
}
}
& > input {
width: 100%;
}
input[type=time] {
width: 100%;
padding-right: 17px;
}
}
.range-vals {
width: 100%;
::ng-deep {
.clr-control-container {
width: 100%;
.clr-input-wrapper {
max-width: none;
.clr-input-group {
width: 100%;
}
}
}
}
.from {
margin-bottom: 10px;
& > input {
width: 100%;
}
input[type=time] {
width: 100%;
padding-right: 17px;
}
}
.from, .to {
min-width: 100px;
& > input {
width: 100%;
}
input[type=time] {
width: 100%;
padding-right: 17px;
}
}
}
.contains-vals {
width: 100%;
::ng-deep {
.clr-control-container {
width: 100%;
.clr-input-wrapper {
max-width: none;
.clr-input-group {
width: 100%;
}
}
}
}
& > input {
width: 100%;
}
input[type=time] {
width: 100%;
padding-right: 17px;
}
}
}
.clause-buttons {
display: flex;
justify-content: space-around;
align-items: center;
flex-direction: row;
align-items: center;
button {
min-width: auto;
}
}
}
}
}
.invalid-clause {
border-left: 2px solid #d94b31;
}
.clause-row {
clr-icon {
margin: 0;
}
}
.clause-row:after {
position: relative;
content: "";
height: .41667rem;
width: .41667rem;
top: .29167rem;
right: .25rem;
background-image: url(data:image/svg+xml;charset=utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org…%2C9.84%2C3.24a0.68%2C0.68%2C0%2C1%2C1%2C1%2C1Z%22%2F%3E%0A%3C%2Fsvg%3E%0A);
background-repeat: no-repeat;
background-size: contain;
vertical-align: middle;
margin: 0;
}
::ng-deep body[cds-theme="dark"] {
.line-numbers {
border-color: #989797 !important;
}
}
pre[class*="language-"] {
padding: 8px;
margin: 0;
border-radius: 1px;
display: flex;
justify-content: center;
align-items: center;
min-height: 66px;
position: relative;
span.spinner {
position: absolute;
left: 10px;
top: 10px;
}
code {
white-space: pre-wrap;
word-break: break-word;
}
}
.input-val {
border: 0px;
background: #fbf8f8;
border-bottom: 1px solid #999999;
}
clr-date-container {
margin-top: 2px !important;
}
input[type="time"] {
border: 0;
background: transparent;
border-bottom: 1px solid #b3b3b3;
&:focus {
outline: none;
}
}
.in-values-modal {
.modal-footer {
border-top: 1px solid #d8d8d8;
margin-top: 10px;
}
}
.progress, .progress-static {
background-color: transparent;
width: 100%;
height: 4px;
top: 3px;
}
+4 -2
View File
@@ -6,7 +6,8 @@ import {
OnDestroy, OnDestroy,
ChangeDetectorRef, ChangeDetectorRef,
LOCALE_ID, LOCALE_ID,
Input Input,
ViewEncapsulation
} from '@angular/core' } from '@angular/core'
import { SasStoreService } from '../services/sas-store.service' import { SasStoreService } from '../services/sas-store.service'
import { globals } from '../_globals' import { globals } from '../_globals'
@@ -27,7 +28,8 @@ registerLocaleData(localeEnGB)
selector: 'app-query', selector: 'app-query',
templateUrl: './query.component.html', templateUrl: './query.component.html',
styleUrls: ['./query.component.scss'], styleUrls: ['./query.component.scss'],
providers: [{ provide: LOCALE_ID, useValue: 'en-GB' }] providers: [{ provide: LOCALE_ID, useValue: 'en-GB' }],
encapsulation: ViewEncapsulation.None
}) })
export class QueryComponent export class QueryComponent
implements AfterViewInit, AfterContentInit, OnDestroy implements AfterViewInit, AfterContentInit, OnDestroy
@@ -1,210 +0,0 @@
@import '../../../colors.scss';
.loader {
display:flex;
justify-content: center;
height:75vh;
align-items:center;
flex-direction:column
}
.modalLarge {
width: 50rem!important;
}
.addedRow {
border: 1px solid rgba(9, 77, 117, 0.2);
border-radius: 5px;
}
.deletedRow {
border: 1px solid rgba(70, 71, 70, 0.2);
border-radius: 5px;
}
::ng-deep body[cds-theme="dark"] {
table {
.updatedRow {
background: #93971e;
}
.addedRow {
background: rgb(86 153 95);
}
.deletedRow {
background: rgb(138 90 90);
}
}
}
::ng-deep body[cds-theme="light"] {
table {
.updatedRow {
background: #fafda8;
}
.addedRow {
background: rgb(146, 208, 154);
}
.deletedRow {
background: rgb(230, 179, 179);
}
}
}
.updatedRow {
border: 1px solid rgba(9, 117, 9, 0.2);
border-radius: 5px;
}
.table {
border: 0px solid;
}
.ch {
background: rgba(0,0,0,.1);
border: 1px solid rgba(104, 100, 0, 0.4);
border-radius: 5px;
}
.ch:hover {
background: rgba(252, 135, 120, 0.4);
}
.tooltip .tooltip-content.tooltip-top-right, .tooltip.tooltip-top-right>.tooltip-content, .tooltip>.tooltip-content {
font-size: .54167rem;
font-weight: 400;
letter-spacing: normal;
background: $headerBackground;
border-radius: .125rem;
color: #f0f1ec;;
line-height: .75rem;
margin: 0;
padding: .375rem .5rem;
width: 235px;
position: absolute;
top: auto;
bottom: 100%;
left: 12px;
right: auto;
border-bottom-left-radius: 0;
margin-bottom: .66667rem;
}
.tooltip .tooltip-content.tooltip-top-right:before, .tooltip.tooltip-top-right>.tooltip-content:before, .tooltip>.tooltip-content:before {
position: absolute;
bottom: -.375rem;
left: 0;
top: auto;
right: auto;
content: "";
border-left: .25rem solid $headerBackground;
border-top: .20833rem solid $headerBackground;
border-right: .25rem solid transparent;
border-bottom: .20833rem solid transparent;
}
.table {
border: 0px solid;
}
.toggle-switch input[type=checkbox]:checked+label:before {
border-color: $headerBackground;
background-color: $headerBackground !important;
transition: .15s ease-in;
transition-property: border-color,background-color;
}
.tableCont {
overflow:auto;
margin: 15px 10px 10px 10px;
td {
word-break: break-word;
}
}
.approvalInfo {
display: flex;
justify-content: flex-end
}
.approvalBack {
display: flex;
justify-content: flex-start;
}
@media screen and (max-width:768px) {
.approvalInfo {
display: flex;
justify-content: center;
margin-top: 15px;
}
.approvalBack {
display: flex;
justify-content: center;
margin-bottom: 15px;
}
.card {
margin-top:0rem!important;
min-height: calc(100vh - 0px)!important;
}
.table td.left, .table th.left {
text-align: left;
width: 150px!important;
flex: 0
}
}
.table td.left, .table th.left {
text-align: left;
flex: 1;
width: 300px!important;
}
.tooll {
position: absolute;
background: #e6b3b3;
color: $headerBackground;
top: 0px;
height: 36px;
width: 100%;
left: 0px;
justify-content: center;
align-items: center;
display: flex;
}
#acceptBtn, #rejectBtn {
width: 175px
}
.formatted-values-toggle {
min-width: 75px
}
clr-modal {
::ng-deep {
.modal-body-wrapper {
overflow: auto;
}
}
}
.rows-notice {
display: flex;
align-items: center;
margin-right: 10px;
color: #6a6a6a;
font-size: 15px;
clr-icon {
margin: 0;
}
}
@@ -1,6 +1,11 @@
import { ActivatedRoute } from '@angular/router' import { ActivatedRoute } from '@angular/router'
import { SasStoreService } from '../../services/sas-store.service' import { SasStoreService } from '../../services/sas-store.service'
import { Component, AfterViewInit, OnDestroy } from '@angular/core' import {
Component,
AfterViewInit,
OnDestroy,
ViewEncapsulation
} from '@angular/core'
import { Subscription } from 'rxjs' import { Subscription } from 'rxjs'
import { Router } from '@angular/router' import { Router } from '@angular/router'
import { EventService } from '../../services/event.service' import { EventService } from '../../services/event.service'
@@ -22,7 +27,8 @@ interface ChangesObj {
styleUrls: ['./approve-details.component.scss'], styleUrls: ['./approve-details.component.scss'],
host: { host: {
class: 'content-container' class: 'content-container'
} },
encapsulation: ViewEncapsulation.None
}) })
export class ApproveDetailsComponent implements AfterViewInit, OnDestroy { export class ApproveDetailsComponent implements AfterViewInit, OnDestroy {
private _detailsSub: Subscription | undefined private _detailsSub: Subscription | undefined
@@ -349,13 +355,12 @@ export class ApproveDetailsComponent implements AfterViewInit, OnDestroy {
this.params = param this.params = param
this.response = res this.response = res
this.calcDiff() this.calcDiff()
this.callChangesInfo(this.tableId)
}) })
.catch((err: any) => err) .catch((err: any) => err)
.finally(() => { .finally(() => {
this.loadingTable = true this.loadingTable = true
}) })
this.callChangesInfo(this.tableId)
} }
) )
if (typeof this.router.snapshot.params['tableId'] === 'undefined') { if (typeof this.router.snapshot.params['tableId'] === 'undefined') {
@@ -378,6 +383,7 @@ export class ApproveDetailsComponent implements AfterViewInit, OnDestroy {
this.params = param this.params = param
this.response = res this.response = res
this.calcDiff() this.calcDiff()
this.callChangesInfo(this.tableId)
}) })
.catch((err: any) => { .catch((err: any) => {
this.acceptLoading = false this.acceptLoading = false
@@ -386,8 +392,6 @@ export class ApproveDetailsComponent implements AfterViewInit, OnDestroy {
this.loadingTable = true this.loadingTable = true
this.setFocus() this.setFocus()
}) })
this.callChangesInfo(this.tableId)
} }
ngOnDestroy() { ngOnDestroy() {
@@ -1,43 +0,0 @@
@import '../../../colors.scss';
.column-center {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.datagrid .datagrid-column .datagrid-column-title{
outline: none!important;
}
.links {
font-weight: 700;cursor: pointer;
}
.tooltip.tooltip-bottom-left>.tooltip-content, .tooltip .tooltip-content.tooltip-bottom-left {
background: $headerBackground !important;
}
.tooltip.tooltip-bottom-left>.tooltip-content:before, .tooltip .tooltip-content.tooltip-bottom-left:before {
border-right: .25rem solid $headerBackground;
border-bottom: .20833rem solid $headerBackground;
}
.noBorder {
border-bottom: 1px solid transparent!important;
}
.approvals-list-wrapper {
height: 70vh;
display: flex;
justify-content: center;
flex-direction: column;
align-items: center;
}
.noapprovals-info-wrapper {
display: flex;
justify-content: center;
flex-direction: column;
align-items: center;
height: calc(100vh - 200px);
}
@@ -1,4 +1,9 @@
import { Component, OnInit, ChangeDetectorRef } from '@angular/core' import {
Component,
OnInit,
ChangeDetectorRef,
ViewEncapsulation
} from '@angular/core'
import { SasStoreService } from '../../services/sas-store.service' import { SasStoreService } from '../../services/sas-store.service'
import { Router } from '@angular/router' import { Router } from '@angular/router'
import { SasService } from '../../services/sas.service' import { SasService } from '../../services/sas.service'
@@ -20,7 +25,8 @@ interface ApproveData {
styleUrls: ['./approve.component.scss'], styleUrls: ['./approve.component.scss'],
host: { host: {
class: 'content-container' class: 'content-container'
} },
encapsulation: ViewEncapsulation.None
}) })
export class ApproveComponent implements OnInit { export class ApproveComponent implements OnInit {
public approveList: Array<ApproveData> | undefined public approveList: Array<ApproveData> | undefined
@@ -1,42 +0,0 @@
.rejected {
color: #f83126;
font-weight: bold
}
.accepted {
color: #3fc424;
font-weight: bold
}
.hsCell {
display: flex !important;
flex-direction: column !important;
justify-content: center !important;
align-items: center !important;
padding: 7px;
}
.btCell {
display: flex !important;
justify-content: center !important;
}
.verCenter {
display: flex;
align-items: center;
word-break: break-all;
}
.load-more {
input {
width: 90px;
}
}
#noDataContainer {
height: calc(100vh - 200px);
}
.table-link {
text-decoration: underline;
font-weight: bold;
}
@@ -1,4 +1,4 @@
import { Component, OnInit } from '@angular/core' import { Component, OnInit, ViewEncapsulation } from '@angular/core'
import { Router } from '@angular/router' import { Router } from '@angular/router'
import { SASjsConfig } from '@sasjs/adapter' import { SASjsConfig } from '@sasjs/adapter'
@@ -15,7 +15,8 @@ import {
styleUrls: ['./history.component.scss'], styleUrls: ['./history.component.scss'],
host: { host: {
class: 'content-container' class: 'content-container'
} },
encapsulation: ViewEncapsulation.None
}) })
export class HistoryComponent implements OnInit { export class HistoryComponent implements OnInit {
public history: Array<any> = [] public history: Array<any> = []
@@ -1,17 +0,0 @@
@import '../../../colors.scss';
.noBorder {
border-bottom: 1px solid transparent!important;
}
.tooltip.tooltip-bottom-left>.tooltip-content, .tooltip .tooltip-content.tooltip-bottom-left {
background: $headerBackground !important;
}
.tooltip.tooltip-bottom-left>.tooltip-content:before, .tooltip .tooltip-content.tooltip-bottom-left:before {
border-right: .25rem solid $headerBackground;
border-bottom: .20833rem solid $headerBackground;
}
.no-submitted-tables {
height: calc(100vh - 200px);
}
@@ -1,4 +1,9 @@
import { Component, AfterViewInit, OnInit } from '@angular/core' import {
Component,
AfterViewInit,
OnInit,
ViewEncapsulation
} from '@angular/core'
import { Subscription } from 'rxjs' import { Subscription } from 'rxjs'
import { ActivatedRoute, Router } from '@angular/router' import { ActivatedRoute, Router } from '@angular/router'
import { SasStoreService, EventService, SasService } from '../../services' import { SasStoreService, EventService, SasService } from '../../services'
@@ -17,7 +22,8 @@ interface SubmitterData {
styleUrls: ['./submitter.component.scss'], styleUrls: ['./submitter.component.scss'],
host: { host: {
class: 'content-container' class: 'content-container'
} },
encapsulation: ViewEncapsulation.None
}) })
export class SubmitterComponent implements OnInit, AfterViewInit { export class SubmitterComponent implements OnInit, AfterViewInit {
public remained: number = 0 public remained: number = 0
+3 -2
View File
@@ -1,4 +1,4 @@
import { Component, OnInit } from '@angular/core' import { Component, OnInit, ViewEncapsulation } from '@angular/core'
import { globals } from '../_globals' import { globals } from '../_globals'
import { HelperService } from '../services/helper.service' import { HelperService } from '../services/helper.service'
import { Location } from '@angular/common' import { Location } from '@angular/common'
@@ -12,7 +12,8 @@ import { RequestWrapperResponse } from '../models/request-wrapper/RequestWrapper
styleUrls: ['./role.component.scss'], styleUrls: ['./role.component.scss'],
host: { host: {
class: 'content-container' class: 'content-container'
} },
encapsulation: ViewEncapsulation.None
}) })
export class RoleComponent implements OnInit { export class RoleComponent implements OnInit {
public roles: Array<any> | undefined public roles: Array<any> | undefined
@@ -1,4 +1,4 @@
import { Component, OnInit, OnDestroy } from '@angular/core' import { Component, OnInit, OnDestroy, ViewEncapsulation } from '@angular/core'
@Component({ @Component({
selector: 'app-home-route', selector: 'app-home-route',
@@ -6,7 +6,8 @@ import { Component, OnInit, OnDestroy } from '@angular/core'
styleUrls: ['./home-route.component.scss'], styleUrls: ['./home-route.component.scss'],
host: { host: {
class: 'content-container' class: 'content-container'
} },
encapsulation: ViewEncapsulation.None
}) })
export class HomeRouteComponent implements OnInit, OnDestroy { export class HomeRouteComponent implements OnInit, OnDestroy {
constructor() {} constructor() {}
@@ -1,4 +1,4 @@
import { Component, OnInit } from '@angular/core' import { Component, OnInit, ViewEncapsulation } from '@angular/core'
@Component({ @Component({
selector: 'app-review-route', selector: 'app-review-route',
@@ -6,7 +6,8 @@ import { Component, OnInit } from '@angular/core'
styleUrls: ['./review-route.component.scss'], styleUrls: ['./review-route.component.scss'],
host: { host: {
class: 'content-container' class: 'content-container'
} },
encapsulation: ViewEncapsulation.None
}) })
export class ReviewRouteComponent implements OnInit { export class ReviewRouteComponent implements OnInit {
constructor() {} constructor() {}
@@ -1,4 +1,4 @@
import { Component, OnInit } from '@angular/core' import { Component, OnInit, ViewEncapsulation } from '@angular/core'
@Component({ @Component({
selector: 'app-usernav-route', selector: 'app-usernav-route',
@@ -6,7 +6,8 @@ import { Component, OnInit } from '@angular/core'
styleUrls: ['./usernav-route.component.scss'], styleUrls: ['./usernav-route.component.scss'],
host: { host: {
class: 'content-container' class: 'content-container'
} },
encapsulation: ViewEncapsulation.None
}) })
export class UsernavRouteComponent implements OnInit { export class UsernavRouteComponent implements OnInit {
constructor() {} constructor() {}
@@ -1,4 +1,4 @@
import { Component, OnInit, OnDestroy } from '@angular/core' import { Component, OnInit, OnDestroy, ViewEncapsulation } from '@angular/core'
@Component({ @Component({
selector: 'app-view-route', selector: 'app-view-route',
@@ -6,7 +6,8 @@ import { Component, OnInit, OnDestroy } from '@angular/core'
styleUrls: ['./view-route.component.scss'], styleUrls: ['./view-route.component.scss'],
host: { host: {
class: 'content-container' class: 'content-container'
} },
encapsulation: ViewEncapsulation.None
}) })
export class ViewRouteComponent implements OnInit, OnDestroy { export class ViewRouteComponent implements OnInit, OnDestroy {
constructor() {} constructor() {}
@@ -1,4 +1,4 @@
import { Component, OnInit, OnDestroy } from '@angular/core' import { Component, OnInit, OnDestroy, ViewEncapsulation } from '@angular/core'
@Component({ @Component({
selector: 'app-xlmap-route', selector: 'app-xlmap-route',
@@ -6,7 +6,8 @@ import { Component, OnInit, OnDestroy } from '@angular/core'
styleUrls: ['./xlmap-route.component.scss'], styleUrls: ['./xlmap-route.component.scss'],
host: { host: {
class: 'content-container' class: 'content-container'
} },
encapsulation: ViewEncapsulation.None
}) })
export class XLMapRouteComponent implements OnInit, OnDestroy { export class XLMapRouteComponent implements OnInit, OnDestroy {
constructor() {} constructor() {}
+2 -2
View File
@@ -223,7 +223,7 @@ export class SasStoreService {
tables[tableName] = [tableData] tables[tableName] = [tableData]
return ( return (
await this.sasService.request(program, tables, { await this.sasService.request(program, tables, {
useComputeApi: false useComputeApi: null // Using WEB APPROACH as a temporary workaround until VIYA JES API is fixed. For other server types then VIYA this is not applicable
}) })
).adapterResponse ).adapterResponse
} }
@@ -232,7 +232,7 @@ export class SasStoreService {
tables[tableName] = [tableData] tables[tableName] = [tableData]
return ( return (
await this.sasService.request(program, tables, { await this.sasService.request(program, tables, {
useComputeApi: false useComputeApi: null // Using WEB APPROACH as a temporary workaround until VIYA JES API is fixed. For other server types then VIYA this is not applicable
}) })
).adapterResponse ).adapterResponse
} }
+129 -10
View File
@@ -1,9 +1,21 @@
import { HttpClient } from '@angular/common/http' import {
HttpClient,
HttpContext,
HttpErrorResponse,
HttpHeaders,
HttpParams
} from '@angular/common/http'
import { Injectable } from '@angular/core' import { Injectable } from '@angular/core'
import { Observable } from 'rxjs' import { catchError, Observable, throwError } from 'rxjs'
import { Collection } from '../viya-api-explorer/models/collection.model' import { Collection } from '../viya-api-explorer/models/collection.model'
import { AppStoreService } from './app-store.service' import { AppStoreService } from './app-store.service'
import { ViyaApis } from '../viya-api-explorer/models/viya-apis.models' import { ViyaApis } from '../viya-api-explorer/models/viya-apis.models'
import { ViyaApiFolderMembers } from '../viya-api-explorer/models/viya-api-folder-content.model'
import { ViyaApiFolder } from '../viya-api-explorer/models/viya-api-folder.model'
import { ViyaApiIdentities } from '../viya-api-explorer/models/viya-api-identities.model'
import { ViyaApiCurrentUser } from '../viya-api-explorer/models/viya-api-current-user.model'
import { ViyaComputeContexts } from '../viya-api-explorer/models/viya-compute-contexts.model'
import { ComputeContextDetails } from '../viya-api-explorer/models/viya-compute-context-details.model'
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
@@ -22,7 +34,8 @@ export class SasViyaService {
}, },
Compute: { Compute: {
jobs: '/jobDefinitions', jobs: '/jobDefinitions',
jobExecution: '/jobExecution' jobExecution: '/jobExecution',
contexts: '/compute/contexts'
}, },
Decision_Management: { Decision_Management: {
modelManagement: '/modelManagement', modelManagement: '/modelManagement',
@@ -57,15 +70,23 @@ export class SasViyaService {
constructor( constructor(
private http: HttpClient, private http: HttpClient,
private appStoreService: AppStoreService private appStoreService: AppStoreService
) { ) {}
/**
* This function is replacing the constructor.
* The reason for this is timing issues, other services eg. sas.service, app-store.service
* must be initialized before this bit of code is executed.
* This function is being called by `sas.service`
*/
setup() {
const adapterConfig = this.appStoreService.getDcAdapterSettings() const adapterConfig = this.appStoreService.getDcAdapterSettings()
this.serverUrl = adapterConfig?.serverUrl || '' this.serverUrl = adapterConfig?.serverUrl || ''
//example // example collection request
this.getByCollection('jobs').subscribe((res) => { // this.getByCollection('jobs').subscribe((res) => {
console.log('res', res) // console.log('res', res)
}) // })
} }
/** /**
@@ -82,7 +103,7 @@ export class SasViyaService {
* @returns * @returns
*/ */
getByUrl(url: string): Observable<Collection> { getByUrl(url: string): Observable<Collection> {
return this.http.get<Collection>(`${this.serverUrl}${url}`, { return this.get<Collection>(`${this.serverUrl}${url}`, {
withCredentials: true withCredentials: true
}) })
} }
@@ -93,8 +114,106 @@ export class SasViyaService {
* @returns * @returns
*/ */
getByCollection(apiCollection: string): Observable<Collection> { getByCollection(apiCollection: string): Observable<Collection> {
return this.http.get<Collection>(`${this.serverUrl}${apiCollection}`, { return this.get<Collection>(`${this.serverUrl}${apiCollection}`, {
withCredentials: true withCredentials: true
}) })
} }
getComputeContexts(): Observable<ViyaComputeContexts> {
return this.get<ViyaComputeContexts>(`${this.serverUrl}/compute/contexts`, {
withCredentials: true
})
}
getComputeContextById(id: string): Observable<ComputeContextDetails> {
return this.get<ComputeContextDetails>(
`${this.serverUrl}/compute/contexts/${id}`,
{
withCredentials: true
}
)
}
/**
* @param path Path to the folder
* @returns The folder info object
*/
getFolderByPath(path: string): Observable<ViyaApiFolder> {
return this.get<ViyaApiFolder>(
`${this.serverUrl}/folders/folders/@item?path=${path}`,
{
withCredentials: true
}
)
}
getFolderMembers(folderId: string): Observable<ViyaApiFolderMembers> {
return this.get<ViyaApiFolderMembers>(
`${this.serverUrl}/folders/folders/${folderId}/members`,
{
withCredentials: true
}
)
}
getAdminGroups(limit: number = 5000): Observable<ViyaApiIdentities> {
return this.get<ViyaApiIdentities>(
`${this.serverUrl}/identities/groups?sortBy=name&limit=${limit}`,
{
withCredentials: true
}
)
}
getCurrentUser(): Observable<ViyaApiCurrentUser> {
return this.get<ViyaApiCurrentUser>(
`${this.serverUrl}/identities/users/@currentUser`,
{
withCredentials: true
}
)
}
get<T>(
url: string,
options?: {
headers?:
| HttpHeaders
| {
[header: string]: string | string[]
}
context?: HttpContext
observe?: 'body'
params?:
| HttpParams
| {
[param: string]:
| string
| number
| boolean
| ReadonlyArray<string | number | boolean>
}
reportProgress?: boolean
responseType?: 'json'
withCredentials?: boolean
transferCache?:
| {
includeHeaders?: string[]
}
| boolean
}
): Observable<T> {
return this.http.get<T>(url, options).pipe(
catchError((err: HttpErrorResponse) => {
console.log('url', url)
console.log('err.status', err.status)
if (err.status === 449 || err.status === 401) {
// Retry once if we got a 449
return this.http.get<T>(url, options)
}
// Otherwise propagate the error
return throwError(() => err)
})
)
}
} }
+103 -12
View File
@@ -16,6 +16,9 @@ import { RequestWrapperOptions } from '../models/request-wrapper/RequestWrapperO
import { ErrorBody } from '../models/ErrorBody' import { ErrorBody } from '../models/ErrorBody'
import { UploadFileResponse } from '../models/UploadFile' import { UploadFileResponse } from '../models/UploadFile'
import { RequestWrapperResponse } from '../models/request-wrapper/RequestWrapperResponse' import { RequestWrapperResponse } from '../models/request-wrapper/RequestWrapperResponse'
import { SasViyaService } from './sas-viya.service'
import { ViyaApiFolder } from '../viya-api-explorer/models/viya-api-folder.model'
import { ViyaApiFolderMembers } from '../viya-api-explorer/models/viya-api-folder-content.model'
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
@@ -38,6 +41,7 @@ export class SasService {
private userService: UserService, private userService: UserService,
private eventService: EventService, private eventService: EventService,
private sasjsService: SasjsService, private sasjsService: SasjsService,
private sasViyaService: SasViyaService,
private loggerService: LoggerService, private loggerService: LoggerService,
private router: Router private router: Router
) {} ) {}
@@ -51,6 +55,7 @@ export class SasService {
this.dcAdapterSettings = this.appStoreService.getDcAdapterSettings() this.dcAdapterSettings = this.appStoreService.getDcAdapterSettings()
this.sasjsService.setup() this.sasjsService.setup()
this.sasViyaService.setup()
if (!this.dcAdapterSettings) { if (!this.dcAdapterSettings) {
this.eventService.showInfoModal( this.eventService.showInfoModal(
@@ -423,21 +428,93 @@ export class SasService {
typeof this.sasjsAdapter.getFolder !== 'undefined' typeof this.sasjsAdapter.getFolder !== 'undefined'
let appLocExists: boolean = false let appLocExists: boolean = false
let errorMessage: string | undefined = undefined
if (getFolderExistsInAdapter) { if (getFolderExistsInAdapter) {
appLocExists = await this.appLocCheck(path) const results = await this.appLocCheck(path)
appLocExists = results.found
errorMessage = results.errorMessage
} else { } else {
appLocExists = await this.appLocCheckPreAxiosdAdapter(path) appLocExists = await this.appLocCheckPreAxiosdAdapter(path)
} }
if (appLocExists) { if (appLocExists) {
this.loadStartupServiceEmitter.emit() // Check if there is appLoc/services/admin/makedata.sas present
// if yes, it needs to be run, so we redirect to /deploy
// if not, we load the startup service
this.viyaMakedataSuccessfull().then(
(success: boolean) => {
if (success) {
this.loadStartupServiceEmitter.emit()
} else {
this.eventService.startupDataLoaded()
this.router.navigateByUrl('/deploy')
}
},
(error: any) => {
console.error('Error while looking for the file: makedata.sas', error)
}
)
} else {
const errorMessageToShow =
(errorMessage ||
'Viya services are not present on the current appLoc, or API not reachable. Check the ADAPTER configuration.') +
`\nAppLoc: ${path}`
this.eventService.showInfoModal('Error', errorMessageToShow)
} }
} }
public appLocCheck(path: string): Promise<boolean> { private async viyaMakedataSuccessfull(): Promise<boolean> {
return new Promise((resolve, reject) => {
const sasjsConfig = this.getSasjsConfig()
const configuratorFolder = `${sasjsConfig.appLoc}/services/admin`
this.sasViyaService.getFolderByPath(configuratorFolder).subscribe(
(folderInfo: ViyaApiFolder) => {
const folderId = folderInfo.id
if (!folderId) {
console.error(
`Folder ID is not present. ${configuratorFolder}`,
sasjsConfig
)
resolve(false)
}
this.sasViyaService.getFolderMembers(folderId).subscribe(
(members: ViyaApiFolderMembers) => {
if (
!members.items.some((item: any) => item.name === 'makedata')
) {
// Makedata.sas is not present, which means it was run
resolve(true)
} else {
// Makedata.sas is present, which means it was not run
resolve(false)
}
},
(err: any) => {
console.error('Error getting folder contents', err)
reject()
}
)
},
(err: any) => {
console.warn('Error getting folder info', err)
reject(err)
}
)
})
}
public appLocCheck(
path: string
): Promise<{ found: boolean; errorMessage?: string }> {
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {
let statusNotFound: boolean = false let fetchError: string = ''
let res: any let res: any
@@ -448,20 +525,21 @@ export class SasService {
this.appLocCheckPending = true this.appLocCheckPending = true
this.shouldLogin.next(true) this.shouldLogin.next(true)
resolve(false) resolve({ found: false })
} else if (err.name === 'NotFoundeError') {
fetchError = err.message
} else { } else {
statusNotFound = true fetchError =
'Viya services are not present on the current appLoc, or API not reachable. Check the ADAPTER configuration.'
} }
} }
if (statusNotFound) { if (fetchError.length) {
console.warn('Viya services are not present on the current appLoc.') console.warn(fetchError)
this.eventService.startupDataLoaded() return resolve({ found: false, errorMessage: fetchError })
this.router.navigateByUrl('/deploy')
return resolve(false)
} }
resolve(true) resolve({ found: true })
}) })
} }
@@ -579,4 +657,17 @@ export class SasService {
} }
} }
} }
// Viya specific functions
public getFileContent(folderPath: string, fileName: string) {
return this.sasjsAdapter.getFileContent(folderPath, fileName)
}
public updateFileContent(
folderPath: string,
fileName: string,
content: string
) {
return this.sasjsAdapter.updateFileContent(folderPath, fileName, content)
}
} }
@@ -7,7 +7,10 @@
> >
<h3 class="modal-title"> <h3 class="modal-title">
{{ data.modalTitle }} {{ data.modalTitle }}
<p *ngIf="data.sasService && data.sasService.length > 0" class="sasService"> <p
*ngIf="data.sasService && data.sasService.length > 0"
class="sasService mt-0"
>
SAS Service: <strong>{{ data.sasService }}</strong> SAS Service: <strong>{{ data.sasService }}</strong>
</p> </p>
</h3> </h3>
@@ -1,43 +0,0 @@
.clr-abort-modal {
::ng-deep {
.modal {
z-index: 2050;
}
.modal-title-wrapper {
width: 100%;
}
.modal {
z-index: 2050;
}
}
}
.modal-title {
position: relative;
}
.sasService {
position: absolute;
top: 0px;
right: 10px;
margin: 0;
}
.modal-footer {
position: relative;
border-top: 1px solid #dcdcdc;
}
.systext {
overflow: auto;
margin-top: 20px;
padding: 10px 0;
border-top: 1px solid #dcdcdc;
p {
margin-top: 0;
word-wrap: break-word;
}
}
@@ -1,4 +1,11 @@
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core' import {
Component,
EventEmitter,
Input,
OnInit,
Output,
ViewEncapsulation
} from '@angular/core'
import { Router } from '@angular/router' import { Router } from '@angular/router'
import { ServerType } from '@sasjs/utils/types/serverType' import { ServerType } from '@sasjs/utils/types/serverType'
import { EventService } from 'src/app/services/event.service' import { EventService } from 'src/app/services/event.service'
@@ -9,7 +16,8 @@ import { AbortDetails, InfoModal } from '../../models/InfoModal'
@Component({ @Component({
selector: 'app-info-modal', selector: 'app-info-modal',
templateUrl: './info-modal.component.html', templateUrl: './info-modal.component.html',
styleUrls: ['./info-modal.component.scss'] styleUrls: ['./info-modal.component.scss'],
encapsulation: ViewEncapsulation.None
}) })
export class InfoModalComponent implements OnInit { export class InfoModalComponent implements OnInit {
@Output() onConfirmModalClick: EventEmitter<any> = new EventEmitter() @Output() onConfirmModalClick: EventEmitter<any> = new EventEmitter()
@@ -1,3 +0,0 @@
clr-alerts {
display: block;
}
@@ -1,4 +1,4 @@
import { Component, OnInit } from '@angular/core' import { Component, OnInit, ViewEncapsulation } from '@angular/core'
import { Subscription } from 'rxjs' import { Subscription } from 'rxjs'
import { Alert } from './alert' import { Alert } from './alert'
import { AlertsService } from './alerts.service' import { AlertsService } from './alerts.service'
@@ -6,7 +6,8 @@ import { AlertsService } from './alerts.service'
@Component({ @Component({
selector: 'app-alerts', selector: 'app-alerts',
templateUrl: './alerts.component.html', templateUrl: './alerts.component.html',
styleUrls: ['./alerts.component.scss'] styleUrls: ['./alerts.component.scss'],
encapsulation: ViewEncapsulation.None
}) })
export class AlertsComponent implements OnInit { export class AlertsComponent implements OnInit {
public alerts: Array<Alert> = [] public alerts: Array<Alert> = []
@@ -1,75 +0,0 @@
.input-val {
border: 0px;
background: transparent;
border-bottom: 1px solid #999999;
}
input {
width: 100%;
outline: none;
&::-webkit-calendar-picker-indicator {
margin-top: -5px;
}
}
::ng-deep {
body[cds-theme="dark"] {
.datalist {
background: #21333b;
border: 1px solid #575757;
}
input {
color: #fff;
}
.datalist option {
color: #fff
}
}
body[cds-theme="light"] {
.datalist {
background: #fff;
}
}
}
.autocomplete-wrapper {
.overlay {
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 0;
z-index: 1500;
}
.datalist {
position: fixed;
box-shadow: 0px 3px 10px -1px #0000002b;
overflow: auto;
z-index: 2000;
::ng-deep {
option {
padding: 5px 10px;
cursor: pointer;
&:hover {
background: #0000000f;
}
&.focused {
background: #0000000f;
}
}
}
}
.load-more {
text-align: center;
border-top: 1px solid #e6e6e6;
}
}
@@ -7,7 +7,8 @@ import {
Input, Input,
OnInit, OnInit,
Output, Output,
ViewChild ViewChild,
ViewEncapsulation
} from '@angular/core' } from '@angular/core'
export type OnLoadingMoreEvent = { export type OnLoadingMoreEvent = {
@@ -17,7 +18,8 @@ export type OnLoadingMoreEvent = {
@Component({ @Component({
selector: 'app-autocomplete', selector: 'app-autocomplete',
templateUrl: './autocomplete.component.html', templateUrl: './autocomplete.component.html',
styleUrls: ['./autocomplete.component.scss'] styleUrls: ['./autocomplete.component.scss'],
encapsulation: ViewEncapsulation.None
}) })
export class AutocompleteComponent implements OnInit, AfterViewInit { export class AutocompleteComponent implements OnInit, AfterViewInit {
@ViewChild('input') inputElement: any @ViewChild('input') inputElement: any
@@ -1,3 +0,0 @@
.unset {
color: unset;
}
@@ -1,9 +1,10 @@
import { Component, Input, OnInit } from '@angular/core' import { Component, Input, OnInit, ViewEncapsulation } from '@angular/core'
@Component({ @Component({
selector: 'contact-link', selector: 'contact-link',
templateUrl: './contact-link.component.html', templateUrl: './contact-link.component.html',
styleUrls: ['./contact-link.component.scss'] styleUrls: ['./contact-link.component.scss'],
encapsulation: ViewEncapsulation.None
}) })
export class ContactLinkComponent implements OnInit { export class ContactLinkComponent implements OnInit {
@Input() classes: string = '' @Input() classes: string = ''
@@ -1,35 +0,0 @@
.modal-body {
clr-tabs {
max-height: 70vh;
}
::ng-deep {
.tab-content {
width: 100%;
overflow: auto;
.datagrid-outer-wrapper {
width: 100%;
}
}
}
}
clr-modal {
::ng-deep {
.modal-dialog {
height: 100%;
}
}
}
.clickable-row {
cursor: pointer;
}
::ng-deep {
.datagrid-table .datagrid-cell:focus {
outline: none;
outline-offset: 0;
}
}
@@ -5,7 +5,8 @@ import {
OnChanges, OnChanges,
OnInit, OnInit,
Output, Output,
SimpleChanges SimpleChanges,
ViewEncapsulation
} from '@angular/core' } from '@angular/core'
import { DSMeta, Version } from 'src/app/models/sas/editors-getdata.model' import { DSMeta, Version } from 'src/app/models/sas/editors-getdata.model'
import { Tab } from './models/dsmeta-groupped.model' import { Tab } from './models/dsmeta-groupped.model'
@@ -13,7 +14,8 @@ import { Tab } from './models/dsmeta-groupped.model'
@Component({ @Component({
selector: 'app-dataset-info', selector: 'app-dataset-info',
templateUrl: './dataset-info.component.html', templateUrl: './dataset-info.component.html',
styleUrls: ['./dataset-info.component.scss'] styleUrls: ['./dataset-info.component.scss'],
encapsulation: ViewEncapsulation.None
}) })
export class DatasetInfoComponent implements OnInit, OnChanges { export class DatasetInfoComponent implements OnInit, OnChanges {
@Input() open: boolean = false @Input() open: boolean = false
@@ -1,17 +0,0 @@
clr-tree-node button {
white-space: nowrap;
}
clr-tree {
::ng-deep {
.clr-tree-node-content-container {
&:focus {
.clr-treenode-link {
background: #e8e8e8;
background: var(--clr-tree-link-hover-color, #e8e8e8);
text-decoration: none;
}
}
}
}
}
@@ -9,7 +9,8 @@ import {
Output, Output,
QueryList, QueryList,
SimpleChanges, SimpleChanges,
ViewChildren ViewChildren,
ViewEncapsulation
} from '@angular/core' } from '@angular/core'
import { HelperService } from 'src/app/services/helper.service' import { HelperService } from 'src/app/services/helper.service'
import { LicenceService } from 'src/app/services/licence.service' import { LicenceService } from 'src/app/services/licence.service'
@@ -20,7 +21,8 @@ import { TableClickEmitter } from './models/TableClickEmitter'
@Component({ @Component({
selector: 'dc-tree', selector: 'dc-tree',
templateUrl: './dc-tree.component.html', templateUrl: './dc-tree.component.html',
styleUrls: ['./dc-tree.component.scss'] styleUrls: ['./dc-tree.component.scss'],
encapsulation: ViewEncapsulation.None
}) })
export class DcTreeComponent implements OnInit, AfterViewInit, OnChanges { export class DcTreeComponent implements OnInit, AfterViewInit, OnChanges {
// REFACTOR NOTICE // REFACTOR NOTICE
@@ -1,18 +0,0 @@
.excel-password-root {
::ng-deep {
.modal {
z-index: 1060;
}
}
}
.modal-footer {
display: flex;
align-items: center;
justify-content: space-between;
.buttons {
display: flex;
gap: 5px;
}
}
@@ -1,4 +1,4 @@
import { Component } from '@angular/core' import { Component, ViewEncapsulation } from '@angular/core'
import { Observable } from 'rxjs' import { Observable } from 'rxjs'
import { ExcelPasswordModalService } from './excel-password-modal.service' import { ExcelPasswordModalService } from './excel-password-modal.service'
import { Options } from './models/options.interface' import { Options } from './models/options.interface'
@@ -6,7 +6,8 @@ import { Options } from './models/options.interface'
@Component({ @Component({
selector: 'app-excel-password-modal', selector: 'app-excel-password-modal',
styleUrls: ['./excel-password-modal.component.scss'], styleUrls: ['./excel-password-modal.component.scss'],
templateUrl: './excel-password-modal.component.html' templateUrl: './excel-password-modal.component.html',
encapsulation: ViewEncapsulation.None
}) })
export class ExcelPasswordModalComponent { export class ExcelPasswordModalComponent {
options$: Observable<Options> = this.excelPasswordModalService.optionsSubject$ options$: Observable<Options> = this.excelPasswordModalService.optionsSubject$
@@ -1,53 +0,0 @@
// there are no clear instructions how to use Clarity ui variables
$clr-header-height: 2.5rem;
$clr-dark-gray: #565656;
$clr-red: #c92100;
$clr-yellow: rgb(233, 191, 4);
$clr-green: #60b515;
.loading-indicator {
line-height: $clr-header-height;
height: $clr-header-height;
display: flex;
align-items: center;
height: 100%;
margin-right: 10px;
.spinner {
vertical-align: middle;
}
clr-signpost-content {
line-height: 24px;
color: $clr-dark-gray;
cursor: auto;
p {
margin-top: 10px;
display: flex;
justify-content: space-between;
&:first-child {
margin-top: 0;
}
&:last-child {
margin-bottom: 0;
}
span {
margin-left: 10px;
&.running {
color: $clr-yellow;
}
&.success {
color: $clr-green;
}
&.fail {
color: $clr-red;
}
}
}
}
}
@@ -1,4 +1,4 @@
import { Component, OnDestroy, OnInit } from '@angular/core' import { Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core'
import { Subscription } from 'rxjs' import { Subscription } from 'rxjs'
import { Service } from '../service.interface' import { Service } from '../service.interface'
@@ -6,7 +6,8 @@ import { Service } from '../service.interface'
@Component({ @Component({
selector: 'app-loading-indicator', selector: 'app-loading-indicator',
templateUrl: './loading-indicator.component.html', templateUrl: './loading-indicator.component.html',
styleUrls: ['./loading-indicator.component.scss'] styleUrls: ['./loading-indicator.component.scss'],
encapsulation: ViewEncapsulation.None
}) })
export class LoadingIndicatorComponent implements OnInit, OnDestroy { export class LoadingIndicatorComponent implements OnInit, OnDestroy {
public loading: boolean = false public loading: boolean = false
@@ -1,129 +0,0 @@
@import '../../../colors.scss';
.sideBarProps {
background: $headerBackground !important;
color: #e0e0e0;
}
.sideBarProps h2, .sideBarProps h3, .sideBarProps h4, .sideBarProps h5, .sideBarProps input {
color: #e0e0e0;
}
.sideBarProps button {
border-color: wheat!important;
}
.sideBarProps a {
color: #e0e0e0;
}
.login-sidebar-wrapper {
width: 100%;
height: 100%;
position: fixed;
top: 0;
left: 0;
pointer-events: none;
z-index: 10000;
>* {
pointer-events: auto;
}
.login-sidebar {
width: 400px;
position: absolute;
top: 0;
bottom: 0;
left: 0;
background: #fff;
border-right: 1px solid #ddd;
padding: 40px;
form.login {
z-index: 101;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
-ms-flex-direction: column;
flex-direction: column;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
.title {
font-size: 32px;
letter-spacing: normal;
line-height: 36px;
.welcome {
margin-top: 36px;
}
}
.login-group {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
-ms-flex-direction: column;
flex-direction: column;
padding: 48px 0 0 0;
>* {
margin: 6px 0 18px 0;
}
}
}
-webkit-transform: translateX(-400px);
transform: translateX(-400px);
transition: -webkit-transform .3s ease;
transition: transform .3s ease;
transition: transform .3s ease, -webkit-transform .3s ease;
}
&.active {
.login-sidebar {
-webkit-transform: translateX(0);
transform: translateX(0);
z-index: 101;
}
.overlay {
display: block;
}
}
.overlay {
display: none;
position: fixed;
top: 0;
left: 0;
height: 100%;
width: 100%;
background: rgba(33, 33, 33, .5);
z-index: 100;
}
}
::ng-deep .login-sidebar-wrapper .login-group {
.clr-control-container, .clr-input-group, .username, .password {
width: 100%;
}
.clr-input-wrapper {
max-width: none;
}
button {
max-width: none;
}
}
.login-logo {
max-width: 200px
}
@@ -1,4 +1,4 @@
import { Component, OnInit, OnDestroy } from '@angular/core' import { Component, OnInit, OnDestroy, ViewEncapsulation } from '@angular/core'
import { LoggerService } from '../../services/logger.service' import { LoggerService } from '../../services/logger.service'
import { Subscription } from 'rxjs' import { Subscription } from 'rxjs'
@@ -12,7 +12,8 @@ interface User {
@Component({ @Component({
selector: 'app-login', selector: 'app-login',
templateUrl: './login.component.html', templateUrl: './login.component.html',
styleUrls: ['./login.component.scss'] styleUrls: ['./login.component.scss'],
encapsulation: ViewEncapsulation.None
}) })
export class LoginComponent implements OnInit, OnDestroy { export class LoginComponent implements OnInit, OnDestroy {
private _subscription: Subscription = new Subscription() private _subscription: Subscription = new Subscription()
@@ -86,9 +86,17 @@
</clr-tree> </clr-tree>
<div <div
*ngIf="serverType !== 'SASVIYA'"
class="log-wrapper saslog" class="log-wrapper saslog"
[innerHTML]="programLog.logFile" [innerHTML]="programLog.logFile"
></div> ></div>
<div
*ngIf="serverType === 'SASVIYA'"
class="log-wrapper saslog viya"
[textContent]="programLog.logFile"
></div>
<button <button
*ngIf="programLog.logFile?.length! > 0" *ngIf="programLog.logFile?.length! > 0"
(click)="downloadLog(programLog.logFile)" (click)="downloadLog(programLog.logFile)"
@@ -1,68 +0,0 @@
::ng-deep {
.requests-modal .modal-header .close clr-icon {
display: block !important;
}
.requests-modal .modal-content {
padding: 20px 10px 5px 10px;
}
.work-tables-dropdown button {
color: var(--clr-nav-link-color, #8c8c8c) !important;
}
.stack-view {
height: auto !important;
//Following lines will fix clarity (requests modal white) issue.
//Clarity version that has issue is: v12
mask-image: none !important;
-webkit-mask-image: none !important;
}
.content {
clr-icon {
margin-bottom: 5px;
}
pre {
word-break: break-all;
white-space: pre-wrap;
max-height: initial;
overflow: visible;
border: 0;
}
.stack-block-label {
// Clarity before V3 -----
// display: none;
// Clarity V3 -----
width: 100%;
padding-left: .6rem !important;
.stack-view-key {
display: none !important;
}
}
}
.err-links {
.clr-treenode-children {
max-height: 55px;
overflow: auto;
}
}
}
.dropdown-item {
&.selected {
background: #d8e3e9;
}
}
.no-reqs {
border-top: 1px solid #0000001a;
padding-top: 5px;
text-align: center;
}
@@ -1,10 +1,17 @@
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core' import {
Component,
EventEmitter,
Input,
OnInit,
Output,
ViewEncapsulation
} from '@angular/core'
import { SASjsRequest } from '@sasjs/adapter' import { SASjsRequest } from '@sasjs/adapter'
import * as moment from 'moment' import * as moment from 'moment'
import { HelperService } from 'src/app/services/helper.service' import { HelperService } from 'src/app/services/helper.service'
import { LoggerService } from '../../services/logger.service' import { LoggerService } from '../../services/logger.service'
import { SasService } from '../../services/sas.service' import { SasService } from '../../services/sas.service'
import { ServerType } from '@sasjs/utils/types/serverType'
interface SASjsRequestExtended extends SASjsRequest { interface SASjsRequestExtended extends SASjsRequest {
parsedTimestamp?: string parsedTimestamp?: string
logErrors?: string[] logErrors?: string[]
@@ -15,7 +22,8 @@ interface SASjsRequestExtended extends SASjsRequest {
@Component({ @Component({
selector: 'app-requests-modal', selector: 'app-requests-modal',
templateUrl: './requests-modal.component.html', templateUrl: './requests-modal.component.html',
styleUrls: ['./requests-modal.component.scss'] styleUrls: ['./requests-modal.component.scss'],
encapsulation: ViewEncapsulation.None
}) })
export class RequestsModalComponent implements OnInit { export class RequestsModalComponent implements OnInit {
private _opened: boolean = false private _opened: boolean = false
@@ -38,6 +46,7 @@ export class RequestsModalComponent implements OnInit {
public tablesActive: boolean = false public tablesActive: boolean = false
public sasjsConfig = this.sasService.getSasjsConfig() public sasjsConfig = this.sasService.getSasjsConfig()
public serverType: string
public sasjsRequests: SASjsRequestExtended[] = [] public sasjsRequests: SASjsRequestExtended[] = []
public workTables: any public workTables: any
@@ -45,7 +54,10 @@ export class RequestsModalComponent implements OnInit {
private sasService: SasService, private sasService: SasService,
private loggerService: LoggerService, private loggerService: LoggerService,
private helperService: HelperService private helperService: HelperService
) {} ) {
this.sasjsConfig = this.sasService.getSasjsConfig()
this.serverType = this.sasService.getServerType()
}
ngOnInit(): void {} ngOnInit(): void {}
@@ -90,21 +102,81 @@ export class RequestsModalComponent implements OnInit {
requestStackId: string, requestStackId: string,
type: string type: string
) { ) {
let allLines: any = document.querySelectorAll( const logWrapper: HTMLElement | null = document.querySelector(
`#${requestStackId} .log-wrapper.saslog font`
)
let logWrapper: any = document.querySelector(
`#${requestStackId} .log-wrapper.saslog` `#${requestStackId} .log-wrapper.saslog`
) )
for (let line of allLines) { if (!logWrapper) return
if (line.textContent.includes(linkingLine)) {
logWrapper.scrollTop = line.offsetTop - logWrapper.offsetTop
line.style.backgroundColor = '#61a2202b'
setTimeout(() => { if (this.serverType === 'SASVIYA') {
line.style = '' // For VIYA (textContent approach)
}, 3000) const logContent = logWrapper.textContent || ''
const logLines = logContent.split('\n')
let lineIndex = -1
// Find the matching line index
for (let i = 0; i < logLines.length; i++) {
if (logLines[i].includes(linkingLine)) {
lineIndex = i
break
}
}
if (lineIndex === -1) return
// Create a temporary div to calculate line height
const tempDiv = document.createElement('div')
tempDiv.className = 'temp-line-height-calc'
tempDiv.textContent = 'X'
logWrapper.appendChild(tempDiv)
const lineHeight = tempDiv.clientHeight
logWrapper.removeChild(tempDiv)
// Scroll to the appropriate position
logWrapper.scrollTop = lineHeight * lineIndex
// Create highlighting overlay at that position
const overlay = document.createElement('div')
overlay.className = `line-highlight-overlay ${type === 'error' ? 'error-highlight' : 'warning-highlight'}`
// We are setting the height using a CSS variable to the HTML element
// this does not trigger CSP errors because it's driven by JS
overlay.classList.add('temp-height-setter')
document.documentElement.style.setProperty(
'--line-height',
`${lineHeight}px`
)
overlay.classList.add('line-position-setter')
document.documentElement.style.setProperty(
'--line-top',
`${lineHeight * lineIndex}px`
)
logWrapper.appendChild(overlay)
setTimeout(() => {
if (logWrapper.contains(overlay)) {
logWrapper.removeChild(overlay)
}
}, 3000)
} else {
// For non-VIYA (innerHTML approach)
const allLines: NodeListOf<Element> = document.querySelectorAll(
`#${requestStackId} .log-wrapper.saslog font`
)
for (let line of Array.from(allLines)) {
if (line.textContent?.includes(linkingLine)) {
logWrapper.scrollTop =
(line as HTMLElement).offsetTop - logWrapper.offsetTop
// Use class instead of inline style
line.classList.add('highlighted-line')
setTimeout(() => {
line.classList.remove('highlighted-line')
}, 3000)
break
}
} }
} }
} }
@@ -117,7 +189,9 @@ export class RequestsModalComponent implements OnInit {
let errorLines = [] let errorLines = []
let warningLines = [] let warningLines = []
// Create a safe version of the log content
let logLines = req.logFile.split('\n') let logLines = req.logFile.split('\n')
let originalLogLines = [...logLines]
for (let i = 0; i < logLines.length; i++) { for (let i = 0; i < logLines.length; i++) {
if (/<.*>ERROR/gm.test(logLines[i])) { if (/<.*>ERROR/gm.test(logLines[i])) {
@@ -129,7 +203,9 @@ export class RequestsModalComponent implements OnInit {
} else if (/^ERROR/gm.test(logLines[i])) { } else if (/^ERROR/gm.test(logLines[i])) {
errorLines.push(logLines[i]) errorLines.push(logLines[i])
logLines[i] = '<font>' + logLines[i] + '</font>' if (this.serverType !== 'SASVIYA') {
logLines[i] = '<font class="error-line">' + logLines[i] + '</font>'
}
} }
if (/<.*>WARNING/gm.test(logLines[i])) { if (/<.*>WARNING/gm.test(logLines[i])) {
@@ -141,12 +217,41 @@ export class RequestsModalComponent implements OnInit {
} else if (/^WARNING/gm.test(logLines[i])) { } else if (/^WARNING/gm.test(logLines[i])) {
warningLines.push(logLines[i]) warningLines.push(logLines[i])
logLines[i] = '<font>' + logLines[i] + '</font>' if (this.serverType !== 'SASVIYA') {
logLines[i] = '<font class="warning-line">' + logLines[i] + '</font>'
}
} }
} }
this.loggerService.log(warningLines) this.loggerService.log(warningLines)
// For VIYA, store both versions of the log
if (this.serverType === 'SASVIYA') {
req.originalLogFile = originalLogLines.join('\n')
req.logFileLineMap = {}
// Create a mapping of error/warning lines to their indices
errorLines.forEach((errorLine) => {
for (let i = 0; i < originalLogLines.length; i++) {
if (originalLogLines[i].includes(errorLine)) {
if (!req.logFileLineMap.errors) req.logFileLineMap.errors = {}
req.logFileLineMap.errors[errorLine] = i
break
}
}
})
warningLines.forEach((warningLine) => {
for (let i = 0; i < originalLogLines.length; i++) {
if (originalLogLines[i].includes(warningLine)) {
if (!req.logFileLineMap.warnings) req.logFileLineMap.warnings = {}
req.logFileLineMap.warnings[warningLine] = i
break
}
}
})
}
req.logFile = logLines.join('\n') req.logFile = logLines.join('\n')
req.logErrors = errorLines req.logErrors = errorLines
req.logWarnings = warningLines req.logWarnings = warningLines
@@ -1,82 +0,0 @@
$sidebarWidth: 272px;
.clr-vertical-nav .nav-link.active {
background-color: transparent;
}
clr-vertical-nav {
width: $sidebarWidth;
min-width: $sidebarWidth;
height: 100%;
max-width: 375px;
position: relative;
box-shadow: 0 0 5px rgba(0, 0, 0, 0.12);
.sun-dropdown {
min-height: 36px
}
.nav-tree {
height: 100%;
overflow-y: auto;
}
&.closed {
margin-left: -$sidebarWidth;
}
.resize-handle {
position: absolute;
top: 0;
bottom: 0;
right: -3px;
border-right: 4px solid #80b441;
cursor: col-resize;
opacity: 0;
transition: all 0.1s ease-in-out;
&:hover {
opacity: .5;
}
&.resizing {
opacity: 1;
}
}
&.resizing {
transition: none !important;
}
}
.nav-divider {
border: 0;
border-top: 1px solid #d3d3d3;
margin-bottom: 0;
}
.page-title {
margin: 0;
font-size: 18px;
text-align: center;
}
.zero-margin{
margin: 0px;
}
.user-nav-btn{
padding: 0px;
padding-left: 2px;
padding-right: 2px;
}
#sidebarNav {
z-index: 200;
}
@media (max-width: 767px) { //768
#sidebarNav {
position: absolute;
bottom: 0;
top: 0;
}
}
@@ -5,7 +5,8 @@ import {
ViewChild, ViewChild,
ElementRef, ElementRef,
EventEmitter, EventEmitter,
Output Output,
ViewEncapsulation
} from '@angular/core' } from '@angular/core'
import { Router } from '@angular/router' import { Router } from '@angular/router'
import { EventService } from '../../services/event.service' import { EventService } from '../../services/event.service'
@@ -16,7 +17,8 @@ import { globals } from '../../_globals'
@Component({ @Component({
selector: 'app-sidebar', selector: 'app-sidebar',
templateUrl: './sidebar.component.html', templateUrl: './sidebar.component.html',
styleUrls: ['./sidebar.component.scss'] styleUrls: ['./sidebar.component.scss'],
encapsulation: ViewEncapsulation.None
}) })
export class SidebarComponent implements OnInit { export class SidebarComponent implements OnInit {
@ViewChild('sidebarNav') sidebarNav!: ElementRef @ViewChild('sidebarNav') sidebarNav!: ElementRef
@@ -1,39 +0,0 @@
.input-val {
border: 0px;
background: transparent;
border-bottom: 1px solid #999999;
}
input {
width: 100%;
outline: none;
&::-webkit-calendar-picker-indicator {
margin-top: -5px;
}
}
clr-date-container {
position: relative;
margin-top: 2px !important;
::ng-deep {
.clr-input-group-icon-action {
position: absolute;
right: -5px;
}
input::-webkit-calendar-picker-indicator {
margin-right: 20px;
margin-top: -5px;
}
}
}
label.secondLabelActive span {
&:not(.value-type-selected) {
text-decoration: line-through;
cursor: pointer;
opacity: 0.6;
}
}
@@ -6,14 +6,16 @@ import {
OnInit, OnInit,
Output, Output,
SimpleChanges, SimpleChanges,
ViewChild ViewChild,
ViewEncapsulation
} from '@angular/core' } from '@angular/core'
import { OnLoadingMoreEvent } from '../autocomplete/autocomplete.component' import { OnLoadingMoreEvent } from '../autocomplete/autocomplete.component'
@Component({ @Component({
selector: 'app-soft-select', selector: 'app-soft-select',
templateUrl: './soft-select.component.html', templateUrl: './soft-select.component.html',
styleUrls: ['./soft-select.component.scss'] styleUrls: ['./soft-select.component.scss'],
encapsulation: ViewEncapsulation.None
}) })
export class SoftSelectComponent implements OnInit, OnChanges { export class SoftSelectComponent implements OnInit, OnChanges {
@Input() inputId: string = '' @Input() inputId: string = ''
@@ -1,27 +0,0 @@
:host {
height: 100%;
background: #f5f6ff;
}
.clr-checkbox-wrapper {
&.disabled {
opacity: 0.5;
}
}
.card {
height: 100%;
display: flex;
flex-direction: column;
margin-top: 0;
.card-block {
flex: 1;
overflow: auto;
}
}
.accept-checkbox {
padding: 10px 15px
}
@@ -3,7 +3,8 @@ import {
Component, Component,
ElementRef, ElementRef,
OnInit, OnInit,
ViewChild ViewChild,
ViewEncapsulation
} from '@angular/core' } from '@angular/core'
import { SasService } from '../../services/sas.service' import { SasService } from '../../services/sas.service'
import * as marked from 'marked' import * as marked from 'marked'
@@ -13,7 +14,8 @@ import { RequestWrapperResponse } from 'src/app/models/request-wrapper/RequestWr
@Component({ @Component({
selector: 'app-terms', selector: 'app-terms',
templateUrl: './terms.component.html', templateUrl: './terms.component.html',
styleUrls: ['./terms.component.scss'] styleUrls: ['./terms.component.scss'],
encapsulation: ViewEncapsulation.None
}) })
export class TermsComponent implements OnInit, AfterViewInit { export class TermsComponent implements OnInit, AfterViewInit {
@ViewChild('markdownCard') markdownCard!: ElementRef @ViewChild('markdownCard') markdownCard!: ElementRef
@@ -1,156 +0,0 @@
$clr-header-height: 3rem;
$clr-near-white: #fafafa;
$clr-dark-gray: #565656;
$clr-light-gray: #eee;
:host {
display: contents;
}
.copyRight {
margin-top: 10px;
span {
word-break: break-word;
white-space: pre-wrap;
width: 100%;
text-align: center;
line-height: 1.5;
}
}
.user-nav-toggle {
padding: 0 10px !important;
display: flex;
align-items: center;
height: 100%;
}
.app-nav-dropdown {
//left padding for branding is also 24px
padding-right: 15px;
line-height: $clr-header-height;
height: $clr-header-height;
>button.dropdown-toggle {
color: $clr-near-white;
position: relative;
&:after {
content: "";
background-color: $clr-near-white;
opacity: .15;
left: 0;
position: absolute;
width: 1px;
height: 40px;
top: 10px;
}
.badge {
&.hidden {
visibility: hidden;
}
}
}
clr-dropdown-menu {
color: $clr-dark-gray;
padding-bottom: 0;
.separator {
margin: 10px 0;
border-bottom: 1px solid gray;
// border-bottom: 1px solid $clr-dark-gray;
}
.dropdown-item {
display: flex;
flex-direction: row;
align-items: center;
position: relative;
height: 45px;
line-height: 40px;
.badge {
position: absolute;
top: 15px;
right: 26px;
margin-right: 0;
}
&.debug-switch-item {
padding: 0;
}
.toggle-switch {
margin: 0;
width: 100%;
height: 100%;
justify-content: center;
}
.clr-logout{
position: absolute;
right: 20px;
top: 15px;
}
::ng-deep {
.clr-control-container {
width: 100%;
height: 100%;
}
clr-toggle-wrapper {
width: 100%;
margin: 0;
height: 100%;
padding: 10px 20px;
}
input {
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 100%;
height: 100%;
cursor: pointer;
}
.clr-toggle-wrapper input[type=checkbox]+label::after {
transition: none !important;
}
}
}
}
}
.debug-toggle-label {
padding-left: 42px;
}
@media (max-width: 768px){
.sidenav-content{
a.nav-link.active {
color: inherit;
padding: 15px;
background: #565656;
}
}
}
.nav-link.d-block{
span.badge{
position: absolute;
}
}
.avatar-img{
width: 40px;
height: 40px;
margin-left: 10px;
border-radius: 50px;
}
@@ -1,4 +1,4 @@
import { Component, OnInit, OnDestroy } from '@angular/core' import { Component, OnInit, OnDestroy, ViewEncapsulation } from '@angular/core'
import { Subscription } from 'rxjs' import { Subscription } from 'rxjs'
import { UserService } from '../user.service' import { UserService } from '../user.service'
import { VERSION } from '../../../environments/version' import { VERSION } from '../../../environments/version'
@@ -6,11 +6,13 @@ import { SasService } from '../../services/sas.service'
import { SASjsConfig } from '@sasjs/adapter' import { SASjsConfig } from '@sasjs/adapter'
import { EventService } from '../../services/event.service' import { EventService } from '../../services/event.service'
import { Router } from '@angular/router' import { Router } from '@angular/router'
import { globals } from 'src/app/_globals'
@Component({ @Component({
selector: 'app-header-actions', selector: 'app-header-actions',
templateUrl: './header-actions.component.html', templateUrl: './header-actions.component.html',
styleUrls: ['./header-actions.component.scss'] styleUrls: ['./header-actions.component.scss'],
encapsulation: ViewEncapsulation.None
}) })
export class HeaderActions implements OnInit, OnDestroy { export class HeaderActions implements OnInit, OnDestroy {
public userName: string = 'Not logged in' public userName: string = 'Not logged in'
@@ -69,9 +71,11 @@ export class HeaderActions implements OnInit, OnDestroy {
public onDebugRowClick(evt: Event, dropdownToggle: any): void { public onDebugRowClick(evt: Event, dropdownToggle: any): void {
evt.stopPropagation() evt.stopPropagation()
setTimeout(() => { if (globals.userDropdownConfig.closeOnDebugClick) {
dropdownToggle.click() setTimeout(() => {
}, 300) dropdownToggle.click()
}, 300)
}
} }
public logout(evt: any): void { public logout(evt: any): void {
@@ -19,7 +19,7 @@
<div class="modal-body d-flex"> <div class="modal-body d-flex">
<div class="add-new d-flex clr-flex-column"> <div class="add-new d-flex clr-flex-column">
<p class="mt-0-i">Add new</p> <p class="mt-0">Add new</p>
<hr class="w-100" /> <hr class="w-100" />
@@ -41,7 +41,7 @@
<div class="currently-open"> <div class="currently-open">
<div class="d-flex clr-justify-content-between"> <div class="d-flex clr-justify-content-between">
<p class="mt-0-i">Currently open</p> <p class="mt-0">Currently open</p>
<div> <div>
<a <a
@@ -198,7 +198,7 @@
</ng-container> </ng-container>
<ng-container *ngIf="hotColumns.colHeadersVisible"> <ng-container *ngIf="hotColumns.colHeadersVisible">
<p class="mt-0-i">Columns To Display (drag and re-order)</p> <p class="mt-0">Columns To Display (drag and re-order)</p>
<div <div
class="cols-list" class="cols-list"
cdkDropList cdkDropList
@@ -1,317 +0,0 @@
.licence-notice {
font-size: 14px;
display: block;
opacity: 0.6;}
clr-modal.root-modal {
::ng-deep {
.modal-body-wrapper {
height: calc(100% - 60px);
}
.modal-content {
height: 80vh;
}
}
.modal-footer {
padding: 0;
}
.modal-body {
max-height: 100%;
height: 100%;
}
z-index: 1300;
}
dc-tree {
overflow: auto;
flex: 1;
}
.tooltip-long {
word-break: break-word;
}
.add-new {
width: 50%;
padding-right: 5px;
> p {
margin-bottom: 1px;
}
}
.viewbox-limit-notice {
opacity: .7;
color: #E74C3C
}
::ng-deep body[cds-theme="dark"] {
.currently-open .open-viewbox:hover {
background: #314b57;
}
.cols-list {
background: #314b57;
color: #fff;
border-color: #858585;
}
.col-box {
background: #314b57;
border-color: #858585;
color: inherit;
}
}
::ng-deep body[cds-theme="light"] {
.currently-open .open-viewbox:hover {
background: #e8e8e8;
}
.cols-list {
background: #fff;
}
.col-box {
background: #fff;
}
}
.currently-open {
width: 50%;
.open-viewbox {
cursor: pointer;
padding: 3px 5px;
&.selected {
background: #3c85002e;
}
}
}
.viewboxes-container {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
pointer-events: none;
z-index: 999; // make it 1020 if there are issues
}
.viewbox {
min-width: 200px;
min-height: 200px;
position: fixed;
left: 0;
top: 0;
pointer-events: all;
display: flex;
flex-direction: column;
border-top-left-radius: 3px;
border-top-right-radius: 3px;
box-shadow: 0px 0px 10px -3px black;
&.focused {
z-index: 1100;
outline: none;
}
.content {
border: 1px solid #0000004d;
background: white;
height: 100%;
width: 100%;
flex: 1;
overflow-x: auto;
}
.drag-handle {
width: 100%;
min-height: 20px;
background-color: #3c8500;
border-top-left-radius: 3px;
border-top-right-radius: 3px;
color: #fff;
padding: 0 5px;
pointer-events: all;
.table-title {
white-space: pre-wrap;
word-break: break-all;
}
.actions {
display: flex;
min-width: 35px;
clr-icon {
cursor: pointer;
margin-left: 5px;
&:hover {
transform: scale(1.3);
}
}
}
}
.click-icon {
cursor: pointer;
&:hover {
transform: scale(1.3);
}
}
.dragHandle {
position: absolute;
// bottom: -8px;
// right: -8px;
// background-color: black;
}
.dragHandle.corner {
width: 15px;
height: 15px;
cursor: nwse-resize;
}
.dragHandle.right {
width: 2px;
height: 100%;
cursor: ew-resize;
}
.dragHandle.bottom {
height: 2px;
width: 100%;
cursor: ns-resize;
}
}
.cols-search {
width: 100%;
margin-top: 5px;
border: 1px solid #00000047;
border-radius: 3px;
}
.cols-list {
border: solid 1px #ccc;
min-height: 60px;
border-radius: 4px;
overflow: hidden;
display: block;
width: 400px;
max-width: 100%;
margin-top: 5px;
}
.col-box {
padding: 2px 10px;
border-bottom: solid 1px #ccc;
color: rgba(0, 0, 0, 0.87);
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
box-sizing: border-box;
cursor: move;
font-size: 14px;
&.search:focus {
background: #0000001a;
}
&.primaryKeyHeaderStyle {
background: #306b0024;
}
}
.cdk-drag-preview {
box-sizing: border-box;
border-radius: 4px;
box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2),
0 8px 10px 1px rgba(0, 0, 0, 0.14),
0 3px 14px 2px rgba(0, 0, 0, 0.12);
z-index: 1300 !important;
}
.cdk-drag-placeholder {
opacity: 0;
}
.cdk-drag-animating {
transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
}
.col-box:last-child {
border: none;
}
.cols-list.cdk-drop-list-dragging .col-box:not(.cdk-drag-placeholder) {
transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
}
// FIXME
// Let's leave it here for a reference if there
// is an issue with viewboxes/filter modal overlaying
// we will remove it if no issues found
// clr-modal.filter-modal {
// ::ng-deep {
// .modal {
// z-index: 1210 !important;
// }
// }
// }
.disabled {
opacity: 0.5;
transform: none !important;
pointer-events: none;
}
.filter-active {
color: #0072a3;
}
hot-table {
::ng-deep {
.firstColumnHeaderStyle button.changeType {
display: none;
}
.handsontable tbody th.ht__highlight, .handsontable thead th.ht__highlight {
&.primaryKeyHeaderStyle {
background: #306b00b0;
}
}
.primaryKeyHeaderStyle {
background: #306b006e;
}
th.readonlyCell {
div {
opacity: 0.4;
}
}
td.readonlyCell {
opacity: 0.5
}
}
}
@@ -17,7 +17,8 @@ import {
OnInit, OnInit,
Output, Output,
QueryList, QueryList,
ViewChildren ViewChildren,
ViewEncapsulation
} from '@angular/core' } from '@angular/core'
import { ActivatedRoute, Router } from '@angular/router' import { ActivatedRoute, Router } from '@angular/router'
import { HotTableRegisterer } from '@handsontable/angular' import { HotTableRegisterer } from '@handsontable/angular'
@@ -46,7 +47,8 @@ import { Viewbox } from './models/viewbox.model'
@Component({ @Component({
selector: 'app-viewboxes', selector: 'app-viewboxes',
templateUrl: './viewboxes.component.html', templateUrl: './viewboxes.component.html',
styleUrls: ['./viewboxes.component.scss'] styleUrls: ['./viewboxes.component.scss'],
encapsulation: ViewEncapsulation.None
}) })
export class ViewboxesComponent implements OnInit, AfterViewInit, OnDestroy { export class ViewboxesComponent implements OnInit, AfterViewInit, OnDestroy {
@ViewChildren('resizeBox') resizeBoxQuery!: QueryList<ElementRef> //make query list, handle multiple @ViewChildren('resizeBox') resizeBoxQuery!: QueryList<ElementRef> //make query list, handle multiple
-21
View File
@@ -1,21 +0,0 @@
.rejected {
color: #f83126;
font-weight: bold
}
.accepted {
color: #3fc424;
font-weight: bold
}
.baseTableLink {
cursor:pointer;
margin-top:10px;
color: #007cbb;
}
::ng-deep body[cds-theme="dark"] {
.baseTableLink {
color: #4ec0ff;
}
}
+3 -2
View File
@@ -1,4 +1,4 @@
import { Component, OnInit } from '@angular/core' import { Component, OnInit, ViewEncapsulation } from '@angular/core'
import { SasStoreService } from '../services/sas-store.service' import { SasStoreService } from '../services/sas-store.service'
import { Router } from '@angular/router' import { Router } from '@angular/router'
import { ActivatedRoute } from '@angular/router' import { ActivatedRoute } from '@angular/router'
@@ -16,7 +16,8 @@ import { RequestWrapperResponse } from '../models/request-wrapper/RequestWrapper
styleUrls: ['./stage.component.scss'], styleUrls: ['./stage.component.scss'],
host: { host: {
class: 'content-container' class: 'content-container'
} },
encapsulation: ViewEncapsulation.None
}) })
export class StageComponent implements OnInit { export class StageComponent implements OnInit {
public table_id: any public table_id: any
@@ -1,42 +0,0 @@
.content-box {
max-width: 1289px;
}
.sys-info {
>div {
// width: 238px;
flex: 1;
}
div:nth-child(2) {
margin: 0 30px;
}
}
.admin-action, .user-action {
display: flex;
justify-content: space-between;
align-items: center;
padding: 5px 10px;
border-radius: 6px;
border: 1px solid #dedede;
margin-bottom: 5px;
button {
min-width: 102px;
}
}
.dark {
font-weight: bold;
margin-left: 5px;
}
@media (max-width: 993px) {
.sys-info div:nth-child(2) {
margin: 0;
}
.sys-info div {
margin: 20px 0;
}
}
+3 -2
View File
@@ -1,4 +1,4 @@
import { Component, OnInit } from '@angular/core' import { Component, OnInit, ViewEncapsulation } from '@angular/core'
import moment from 'moment' import moment from 'moment'
import { VERSION } from 'src/environments/version' import { VERSION } from 'src/environments/version'
import { LicenseKeyData } from '../models/LicenseKeyData' import { LicenseKeyData } from '../models/LicenseKeyData'
@@ -17,7 +17,8 @@ import { RequestWrapperResponse } from '../models/request-wrapper/RequestWrapper
styleUrls: ['./system.component.scss'], styleUrls: ['./system.component.scss'],
host: { host: {
class: 'content-container' class: 'content-container'
} },
encapsulation: ViewEncapsulation.None
}) })
export class SystemComponent implements OnInit { export class SystemComponent implements OnInit {
appInfo: AppInfo = { appInfo: AppInfo = {
-92
View File
@@ -1,92 +0,0 @@
@import '../../colors.scss';
.sidebar-height{
height: 100%;
}
.user-info-text{
display: inline;
font-size: 20px;
}
::ng-deep body[cds-theme="dark"] {
.user-info {
background-color: $headerBackground;
border-color: $headerBackground;
}
.user-data {
background-color: $headerBackground;
border-color: $headerBackground;
}
.user-table tbody{
tr:hover{
background-color: #29404b;
}
}
}
::ng-deep body[cds-theme="light"] {
.user-info {
background-color: #f9f9f9;
border-color: #a7a7a7;
box-shadow: 0px 2px 5px #dad7d7;
}
.user-data {
background-color: #f9f9f9;
border-color: #a7a7a7;
box-shadow: 0px 2px 5px #dad7d7;
}
.user-table tbody{
tr:hover{
background-color: #e6e6e6;
}
}
}
.user-info{
border: 1px solid;
border-radius: 3px;
}
.user-info td{
text-align: center;
}
.user-data{
border: 1px solid;
border-radius: 3px;
}
.user-data{
min-height: auto;
h3, h5{
text-align: center;
}
}
.user-table{
width: 100%;
}
.user-table thead{
background-color: #dadada;
}
.user-table tbody{
tr:hover{
cursor: pointer;
}
}
.width-50{
width: 50%;
}
.width-33{
width: 33%;
}
.table-container{
overflow-y: scroll;
max-height: 500px;
}
@media screen and (max-width: 768px){
.user-data{
min-height: unset !important;
}
}
+3 -2
View File
@@ -1,4 +1,4 @@
import { Component, OnInit } from '@angular/core' import { Component, OnInit, ViewEncapsulation } from '@angular/core'
import { globals } from '../_globals' import { globals } from '../_globals'
import { HelperService } from '../services/helper.service' import { HelperService } from '../services/helper.service'
import { Router, ActivatedRoute } from '@angular/router' import { Router, ActivatedRoute } from '@angular/router'
@@ -14,7 +14,8 @@ import { RequestWrapperResponse } from '../models/request-wrapper/RequestWrapper
styleUrls: ['./user.component.scss'], styleUrls: ['./user.component.scss'],
host: { host: {
class: 'content-container' class: 'content-container'
} },
encapsulation: ViewEncapsulation.None
}) })
export class UserComponent implements OnInit { export class UserComponent implements OnInit {
public users: Array<any> | undefined public users: Array<any> | undefined
-217
View File
@@ -1,217 +0,0 @@
@import '../../colors.scss';
.card {
margin-top: 0;
flex: 1;
display: flex;
flex-direction: column;
}
clr-tree-node button {
white-space: nowrap;
}
.toggle-switch input[type=checkbox]:checked+label:before {
border-color: $headerBackground;
background-color: $headerBackground !important;
transition: .15s ease-in;
transition-property: border-color,background-color;
}
.header-row {
.title-col {
display: flex;
align-items: center;
}
.options-col {
display: flex;
justify-content: flex-end;
}
}
.sw {
margin: 1rem 0rem 0.5rem 1rem;
}
.infoBar {
margin-top:10px;
background: #495967;
color: white;
text-align:center;
padding: 3px;
font-size: 16px;
}
.filterSide {
margin-top: 26px;
margin-bottom: 19px;
}
.options-dropdown {
::ng-deep {
clr-icon {
margin-right: 5px;
}
}
}
.dropdown-menu {
width: 180px;
margin-top: -18px;
padding: 0;
}
.dropdown-menu .btn {
width: 100%;
text-transform: none;
}
.cardFlex {
display:flex;
justify-content: center
}
.noData {
display:flex;
justify-content:center;
flex-direction:column;
align-items: center;
flex: 1;
}
.filterBtn {
display: flex;
justify-content: flex-end;
}
.editBtn {
display: flex;
justify-content: flex-start;
}
.btnView{
margin: 0px!important;
}
.content-area {
padding: 0.5rem !important;
display: flex;
flex-direction: column;
}
.download-select {
::ng-deep {
.clr-select-wrapper {
max-height: unset !important;
}
}
}
.refresh-table {
cursor: pointer;
margin-left: 5px;
}
.libinfo {
padding: 10px 20px;
overflow: auto;
}
hot-table {
::ng-deep {
.handsontable tbody th.ht__highlight, .handsontable thead th.ht__highlight {
&.primaryKeyHeaderStyle {
background-color: #306b00b0 !important;
}
}
.primaryKeyHeaderStyle {
background-color: #306b006e !important;
}
}
}
.no-table-selected-info {
background: none;
display: flex;
align-items: center;
flex-direction: column;
}
.web-query {
max-height: 35vh;
.web-query-text {
min-height: 100px;
max-height: 100px;
width: 100% !important;
}
.web-query-wrapper {
::ng-deep {
.clr-control-container {
width: 100%;
}
clr-textarea-container {
margin: 0 !important;
}
.clr-textarea-wrapper {
margin: 0 !important;
}
}
}
}
.table-search-wrapper {
margin-left: 0;
form {
padding-left: 0;
}
clr-icon {
margin-top: 4px;
}
::ng-deep {
clr-input-container {
margin-top: 0;
}
.clr-control-container {
margin-left: 0;
}
}
}
// FIXME
// Let's leave it here for a reference if there
// is an issue with viewboxes/filter modal overlaying
// we will remove it if no issues found
// .filter-modal {
// z-index: 1210;
// }
@media screen and (max-width: 768px) {
.filterBtn {
display: flex;
justify-content: center;
}
.editBtn {
display: flex;
justify-content: center;
}
}
@media (min-width: 576px) {
.row {
margin-right: 0rem;
margin-left: 0rem;
}
}
+4 -2
View File
@@ -4,7 +4,8 @@ import {
ChangeDetectorRef, ChangeDetectorRef,
AfterViewInit, AfterViewInit,
ViewChildren, ViewChildren,
QueryList QueryList,
ViewEncapsulation
} from '@angular/core' } from '@angular/core'
import { SasStoreService } from '../services/sas-store.service' import { SasStoreService } from '../services/sas-store.service'
import { Subscription } from 'rxjs' import { Subscription } from 'rxjs'
@@ -45,7 +46,8 @@ import { RequestWrapperResponse } from '../models/request-wrapper/RequestWrapper
styleUrls: ['./viewer.component.scss'], styleUrls: ['./viewer.component.scss'],
host: { host: {
class: 'content-container' class: 'content-container'
} },
encapsulation: ViewEncapsulation.None
}) })
export class ViewerComponent implements AfterContentInit, AfterViewInit { export class ViewerComponent implements AfterContentInit, AfterViewInit {
@ViewChildren('queryFilter') @ViewChildren('queryFilter')

Some files were not shown because too many files have changed in this diff Show More