32 Commits

Author SHA1 Message Date
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
4be0614604 Merge pull request 'chore: package-lock' (#155) from lockfile-fix into main
All checks were successful
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
27cbff2bc5 chore: remove package.json comment
All checks were successful
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
c41c8963f2 chore: package.json comments
Some checks failed
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
7249d4fa29 chore: package-lock
All checks were successful
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
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
Some checks failed
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
f9decbd366 style: lint
Some checks failed
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
1dc69341ca fix: requests modal causing VIYA CSP errors 2025-05-26 15:39:28 +02:00
75ae19fa8e style: lint
Some checks failed
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
9de04e9a0c fix: sas viya service init timing issue
Some checks failed
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
983f59cd51 style: lint
Some checks failed
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
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
6e96b1daec style: lint
All checks were successful
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
9604661f3b fix: improved deploy flow for Viya 2025-05-22 10:47:15 +02:00
4bd215491f fix: viya deploy page improved flow
All checks were successful
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
6a7dd451b5 style: lint
Some checks failed
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
841201adab fix: CSP issues, clarity local library build, fixed some style issues
Some checks failed
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
e013e62776 chore(git): Merge branch 'main' into css-refactor
Some checks failed
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
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
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
0337318e0b Merge pull request 'Updated hot, clarity and improved accessibility score.' (#152) from hot-clarity-accessiblity-update into main
All checks were successful
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
2844c70f95 fix: updated hot, clarity and improved accessibility score.
All checks were successful
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
7e11c8f375 chore(release): 6.14.6 [skip ci]
## [6.14.6](https://git.datacontroller.io/dc/dc/compare/v6.14.5...v6.14.6) (2025-04-03)

### Bug Fixes

* history table modal links styling ([c63fcdd](c63fcdd465))
2025-04-03 08:32:36 +00:00
23cbbce964 Merge pull request 'History table modal links styling' (#151) from history-links into main
All checks were successful
Release / Build-production-and-ng-test (push) Successful in 3m47s
Release / Build-and-test-development (push) Successful in 8m19s
Release / release (push) Successful in 7m55s
Reviewed-on: #151
2025-04-03 08:16:57 +00:00
c63fcdd465 fix: history table modal links styling
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 3m58s
Build / Build-and-test-development (pull_request) Successful in 8m24s
2025-04-02 19:15:29 +02:00
82412b2659 chore(release): 6.14.5 [skip ci]
## [6.14.5](https://git.datacontroller.io/dc/dc/compare/v6.14.4...v6.14.5) (2025-03-24)

### Bug Fixes

* improving accessibility lighthouse score ([7f3577c](7f3577c3ef))
* prevent errors when using sqlrc in a DI job in a HOOK ([d1f0879](d1f0879f0a))
* user profile style fix, new select library and table icons ([69f8830](69f883034f))
2025-03-24 23:10:35 +00:00
eef3832e40 Merge pull request 'User profile style fix, new select library and table icons, improved accessibility score' (#150) from styling into main
All checks were successful
Release / Build-production-and-ng-test (push) Successful in 3m53s
Release / Build-and-test-development (push) Successful in 8m14s
Release / release (push) Successful in 8m14s
Reviewed-on: #150
2025-03-24 22:42:40 +00:00
63b75a1c61 Merge branch 'main' into styling
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 3m51s
Build / Build-and-test-development (pull_request) Successful in 8m14s
2025-03-24 22:42:31 +00:00
d1f0879f0a fix: prevent errors when using sqlrc in a DI job in a HOOK
Some checks failed
Release / release (push) Blocked by required conditions
Release / Build-production-and-ng-test (push) Successful in 3m54s
Release / Build-and-test-development (push) Has been cancelled
2025-03-24 21:35:17 +00:00
36416aab2e style: lint
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 3m53s
Build / Build-and-test-development (pull_request) Successful in 8m16s
2025-03-21 14:08:57 +01:00
7f3577c3ef fix: improving accessibility lighthouse score 2025-03-21 14:08:24 +01:00
69f883034f fix: user profile style fix, new select library and table icons
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 49s
Build / Build-and-test-development (pull_request) Successful in 8m21s
2025-03-20 17:33:35 +01:00
129 changed files with 7481 additions and 6294 deletions

View File

@ -1,3 +1,39 @@
## [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)
### Bug Fixes
* history table modal links styling ([c63fcdd](https://git.datacontroller.io/dc/dc/commit/c63fcdd465950ada439d7d69622a3886e8f3a783))
## [6.14.5](https://git.datacontroller.io/dc/dc/compare/v6.14.4...v6.14.5) (2025-03-24)
### Bug Fixes
* improving accessibility lighthouse score ([7f3577c](https://git.datacontroller.io/dc/dc/commit/7f3577c3ef9f44e55a58bc64fbf89a3a64006dd4))
* prevent errors when using sqlrc in a DI job in a HOOK ([d1f0879](https://git.datacontroller.io/dc/dc/commit/d1f0879f0acf7e816c80f7635fd02f4f284214ed))
* user profile style fix, new select library and table icons ([69f8830](https://git.datacontroller.io/dc/dc/commit/69f883034fabbed31aa5d832e20561c4ae3042db))
## [6.14.4](https://git.datacontroller.io/dc/dc/compare/v6.14.3...v6.14.4) (2025-03-18) ## [6.14.4](https://git.datacontroller.io/dc/dc/compare/v6.14.3...v6.14.4) (2025-03-18)

Binary file not shown.

Binary file not shown.

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@14.6.2;handsontable@14.6.2;hyperformula@2.7.1;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) {

2986
client/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -44,11 +44,11 @@
"@angular/platform-browser": "^17.3.3", "@angular/platform-browser": "^17.3.3",
"@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.10.0", "@cds/core": "^6.15.1",
"@clr/angular": "^17.0.1", "@clr/angular": "file:libraries/clr-angular-17.9.0.tgz",
"@clr/icons": "^13.0.2", "@clr/icons": "^13.0.2",
"@clr/ui": "^17.0.1", "@clr/ui": "file:libraries/clr-ui-17.9.0.tgz",
"@handsontable/angular": "^14.3.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": "^14.3.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",

View File

@ -148,5 +148,8 @@ export const globals: {
}, },
handsontable: { handsontable: {
darkTableHeaderClass: 'darkTH' darkTableHeaderClass: 'darkTH'
},
userDropdownConfig: {
closeOnDebugClick: false
} }
} }

View File

@ -139,10 +139,15 @@
[routerLink]="['/']" [routerLink]="['/']"
class="nav-link" class="nav-link"
> >
<img class="without-text d-block d-md-none" src="images/dc-logo.svg" /> <img
class="without-text d-block d-md-none"
src="images/dc-logo.svg"
alt="datacontroller logo without text"
/>
<img <img
class="with-text d-none d-md-block" class="with-text d-none d-md-block"
src="images/datacontroller.svg" src="images/datacontroller.svg"
alt="datacontroller logo"
/> />
</a> </a>
@ -283,7 +288,11 @@
<!-- App Loading Page --> <!-- App Loading Page -->
<div *ngIf="!startupDataLoaded" class="app-loading"> <div *ngIf="!startupDataLoaded" class="app-loading">
<img class="loading-logo" src="images/datacontroller.svg" /> <img
class="loading-logo"
src="images/datacontroller.svg"
alt="datacontroller logo"
/>
<div *ngIf="appActive === null" class="slider"> <div *ngIf="appActive === null" class="slider">
<div class="line"></div> <div class="line"></div>

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: #61717D;
}
}
.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;
}
}

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

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

View File

@ -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;
}
}

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()
} }

View File

@ -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,36 @@
</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">
<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">
<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> <!-- 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 +135,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 +168,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>

View File

@ -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;
}

View File

@ -1,15 +1,28 @@
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 {
Item,
ViyaApiIdentities
} from 'src/app/viya-api-explorer/models/viya-api-identities.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
@ -28,8 +41,14 @@ 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 adminGroups: { id: string; name: string }[] = []
/** 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 +69,33 @@ 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.getAdminGroups()
}
public async getAdminGroups() {
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
}
})
}),
(err: any) => {
this.adminGroupsLoading = false
this.loggerService.error('Error while getting admin groups', err)
this.eventService.showAbortModal('admin groups', err)
}
}
/** /**
* Executes sas.json file to deploy the backend * Executes sas.json file to deploy the backend
@ -63,7 +105,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 +139,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()
@ -125,7 +174,7 @@ export class AutomaticComponent 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
} }
@ -148,8 +197,21 @@ 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
})
}
}) })
.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

View File

@ -1,4 +0,0 @@
.clear-memory-button {
right: 10px;
top: 2px;
}

View File

@ -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
} }

View File

@ -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;
}
}

View File

@ -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

View File

@ -1,238 +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;
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
}

View File

@ -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

View File

@ -1,8 +0,0 @@
:host {
display: block;
}
p {
margin: 0;
text-align: center;
}

View File

@ -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

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,13 +193,14 @@
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
clrTooltipTrigger clrTooltipTrigger
(click)="datasetInfo = true" (click)="datasetInfo = true"
shape="info-circle" shape="info-circle"
aria-label="View dataset meta info"
class="is-highlight cursor-pointer" class="is-highlight cursor-pointer"
size="24" size="24"
></clr-icon> ></clr-icon>

View File

@ -1,246 +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;
}
// 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;
// }

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')

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;
}
}

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

View File

@ -123,11 +123,11 @@
</div> </div>
<div *ngIf="!loading" class="no-table-selected"> <div *ngIf="!loading" class="no-table-selected">
<clr-icon <img
shape="warning-standard" src="images/select-table.png"
size="60" class="select-table-icon"
class="is-info icon-dc-fill" alt="select table icon"
></clr-icon> />
<p <p
*ngIf="treeNodeLibraries?.length! > 0" *ngIf="treeNodeLibraries?.length! > 0"
class="text-center color-gray mt-10" class="text-center color-gray mt-10"

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%;
}

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

View File

@ -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
}

View File

@ -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

View File

@ -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;
}
}

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

View File

@ -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;
}

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 { 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

View File

@ -127,7 +127,7 @@
class="no-table-selected pointer-events-none" class="no-table-selected pointer-events-none"
> >
<clr-icon <clr-icon
shape="warning-standard" shape="upload-cloud"
size="40" size="40"
class="is-info icon-dc-fill" class="is-info icon-dc-fill"
></clr-icon> ></clr-icon>

View File

@ -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;
}

View File

@ -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

View File

@ -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() {}

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;
}

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

View File

@ -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;
}
}

View File

@ -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() {

View File

@ -89,6 +89,7 @@
<clr-dg-cell class="p-0 d-flex justify-content-center"> <clr-dg-cell class="p-0 d-flex justify-content-center">
<button <button
class="btn btn-success" class="btn btn-success"
aria-label="Download audit file"
[id]="approveItem.tableId" [id]="approveItem.tableId"
(click)=" (click)="
download(approveItem.tableId); $event.stopPropagation() download(approveItem.tableId); $event.stopPropagation()

View File

@ -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);
}

View File

@ -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

View File

@ -24,19 +24,19 @@
<a <a
*ngIf="ind < 1" *ngIf="ind < 1"
(click)="getTable(approveData[col])" (click)="getTable(approveData[col])"
class="cursor-pointer" class="cursor-pointer table-link"
>{{ approveData[col] }}</a >{{ approveData[col] }}</a
> >
<div *ngIf="ind < 2 && ind >= 1"> <div *ngIf="ind < 2 && ind >= 1">
<a <a
(click)="getBaseTable(approveData[col])" (click)="getBaseTable(approveData[col])"
class="cursor-pointer" class="cursor-pointer table-link"
>VIEW</a >VIEW</a
> >
<span> / </span> <span> / </span>
<a <a
(click)="getEditTable(approveData[col])" (click)="getEditTable(approveData[col])"
class="cursor-pointer" class="cursor-pointer table-link"
>EDIT</a >EDIT</a
> >
</div> </div>
@ -47,7 +47,12 @@
</table> </table>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-outline" (click)="openModal = false"> <button
type="button"
aria-label="Close modal"
class="btn btn-outline"
(click)="openModal = false"
>
OK OK
</button> </button>
</div> </div>
@ -119,6 +124,7 @@
<clr-dg-cell class="verCenter">{{ historyItem.reviewed }}</clr-dg-cell> <clr-dg-cell class="verCenter">{{ historyItem.reviewed }}</clr-dg-cell>
<clr-dg-cell class="verCenter p-0 d-flex justify-content-center"> <clr-dg-cell class="verCenter p-0 d-flex justify-content-center">
<button <button
aria-label="Download audit file"
class="btn btn-success" class="btn btn-success"
(click)="download(historyItem.tableId); $event.stopPropagation()" (click)="download(historyItem.tableId); $event.stopPropagation()"
> >

View File

@ -1,37 +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);
}

View File

@ -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> = []

View File

@ -64,7 +64,11 @@
<!-- <clr-dg-cell>{{sub.approver}}</clr-dg-cell> --> <!-- <clr-dg-cell>{{sub.approver}}</clr-dg-cell> -->
<clr-dg-cell>{{ sub.submitReason }}</clr-dg-cell> <clr-dg-cell>{{ sub.submitReason }}</clr-dg-cell>
<clr-dg-cell> <clr-dg-cell>
<div class="row justify-content-around" role="tooltip"> <div
class="row justify-content-around"
role="tooltip"
aria-label="Go to staged data screen"
>
<a <a
class="column-center links tooltip tooltip-md tooltip-bottom-left color-blue" class="column-center links tooltip tooltip-md tooltip-bottom-left color-blue"
(click)="goToStage(sub.tableId)" (click)="goToStage(sub.tableId)"
@ -79,6 +83,7 @@
<clr-dg-cell class="p-0 d-flex justify-content-center"> <clr-dg-cell class="p-0 d-flex justify-content-center">
<button <button
class="btn btn-success" class="btn btn-success"
aria-label="Download audit file for table record"
(click)="download(sub.tableId); $event.stopPropagation()" (click)="download(sub.tableId); $event.stopPropagation()"
> >
<clr-icon shape="download"></clr-icon> <clr-icon shape="download"></clr-icon>

View File

@ -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);
}

View File

@ -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

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

View File

@ -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() {}

View File

@ -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() {}

View File

@ -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() {}

View File

@ -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() {}

View File

@ -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() {}

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
} }

View File

@ -4,6 +4,9 @@ import { Observable } 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'
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
@ -22,7 +25,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 +61,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)
}) // })
} }
/** /**
@ -97,4 +109,41 @@ export class SasViyaService {
withCredentials: true withCredentials: true
}) })
} }
getComputeContexts(): Observable<any> {
return this.http.get<any>(`${this.serverUrl}/compute/contexts`, {
withCredentials: true
})
}
/**
* @param path Path to the folder
* @returns The folder info object
*/
getFolderByPath(path: string): Observable<ViyaApiFolder> {
return this.http.get<any>(
`${this.serverUrl}/folders/folders/@item?path=${path}`,
{
withCredentials: true
}
)
}
getFolderMembers(folderId: string): Observable<ViyaApiFolderMembers> {
return this.http.get<any>(
`${this.serverUrl}/folders/folders/${folderId}/members`,
{
withCredentials: true
}
)
}
getAdminGroups(limit: number = 5000): Observable<ViyaApiIdentities> {
return this.http.get<any>(
`${this.serverUrl}/identities/groups?sortBy=name&limit=${limit}`,
{
withCredentials: true
}
)
}
} }

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 })
}) })
} }

View File

@ -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>

View File

@ -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;
}
}

View File

@ -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()

View File

@ -1,3 +0,0 @@
clr-alerts {
display: block;
}

View File

@ -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> = []

View File

@ -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;
}
}

View File

@ -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

View File

@ -1,3 +0,0 @@
.unset {
color: unset;
}

View File

@ -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 = ''

View File

@ -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;
}
}

View File

@ -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

View File

@ -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;
}
}
}
}
}

View File

@ -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

View File

@ -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;
}
}

View File

@ -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$

View File

@ -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;
}
}
}
}
}

View File

@ -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

View File

@ -1,13 +1,17 @@
<div class="login-sidebar-wrapper" [class.active]="isActive"> <div class="login-sidebar-wrapper" [class.active]="isActive">
<div class="login-sidebar sideBarProps"> <div class="login-sidebar sideBarProps">
<!--img src="images/data_controller.png" alt=""--> <!--img src="images/data_controller.png" alt=""-->
<img class="login-logo" src="images/datacontroller.svg" alt="" /> <img
class="login-logo"
src="images/datacontroller.svg"
alt="datacontroller logo"
/>
<form class="login" (ngSubmit)="submit()"> <form class="login" (ngSubmit)="submit()">
<label class="title"> <label class="title">
<h3 class="welcome">Welcome to</h3> <h3 class="welcome">Welcome to</h3>
Data Controller Data Controller
<h5 class="hint">Capture, Review, and Approve</h5> <h4 class="hint">Capture, Review, and Approve</h4>
</label> </label>
<div class="login-group"> <div class="login-group">
<input <input

View File

@ -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, .username, .password {
width: 100%;
}
.clr-input-wrapper {
max-width: none;
}
button {
max-width: none;
}
}
.login-logo {
max-width: 200px
}

View File

@ -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()

View File

@ -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)"

View File

@ -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;
}

View File

@ -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

View File

@ -11,7 +11,7 @@
class="d-flex justify-content-center sub-dropdown" class="d-flex justify-content-center sub-dropdown"
> >
<clr-dropdown> <clr-dropdown>
<button class="dropdown-toggle btn btn-link" clrDropdownTrigger> <button class="dropdown-toggle btn btn-primary" clrDropdownTrigger>
{{ getSubPage() }} {{ getSubPage() }}
<clr-icon shape="caret down"></clr-icon> <clr-icon shape="caret down"></clr-icon>
</button> </button>
@ -113,7 +113,7 @@
class="d-flex justify-content-center sub-dropdown" class="d-flex justify-content-center sub-dropdown"
> >
<clr-dropdown> <clr-dropdown>
<button class="dropdown-toggle btn btn-link" clrDropdownTrigger> <button class="dropdown-toggle btn btn-primary" clrDropdownTrigger>
{{ getSubPage() }} {{ getSubPage() }}
<clr-icon shape="caret down"></clr-icon> <clr-icon shape="caret down"></clr-icon>
</button> </button>

View File

@ -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;
}
}

View File

@ -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

View File

@ -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;
}
}

View File

@ -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 = ''

View File

@ -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
}

View File

@ -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

View File

@ -23,10 +23,13 @@
<app-loading-indicator></app-loading-indicator> <app-loading-indicator></app-loading-indicator>
<clr-dropdown class="app-nav-dropdown"> <clr-dropdown class="app-nav-dropdown">
<button class="nav-text color-white" clrDropdownToggle> <button class="nav-text color-white user-nav-toggle" clrDropdownToggle>
<span>{{ userName }}</span> <span>{{ userName }}</span>
<span *ngIf="userName !== 'Not logged in' && isViya" <span *ngIf="userName !== 'Not logged in' && isViya"
><img class="avatar-img" src="{{ getPictureUrl() }}" alt="" ><img
class="avatar-img"
src="{{ getPictureUrl() }}"
alt="profile picture"
/></span> /></span>
<span <span
class="badge badge-danger" class="badge badge-danger"

View File

@ -1,149 +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;
}
}
.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;
}

View File

@ -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 {

View File

@ -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

View File

@ -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
}
}
}

View File

@ -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

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