Compare commits

...

141 Commits
v6.6.2 ... main

Author SHA1 Message Date
semantic-release-bot
3c62ff6913 chore(release): 6.10.1 [skip ci]
## [6.10.1](https://git.datacontroller.io/dc/dc/compare/v6.10.0...v6.10.1) (2024-06-07)

### Bug Fixes

* adding 60 more colours to crayons table. Closes [#112](#112) ([3521579](3521579dea))
* terms and conditions colours, editor on smaller screens show only icons ([e32d44b](e32d44b1bc))
2024-06-07 15:46:58 +00:00
f4f589af94 Merge pull request 'fix: adding 60 more colours to crayons table. Closes #112' (#114) from issue112 into main
All checks were successful
Release / Build-production-and-ng-test (push) Successful in 4m41s
Release / Build-and-test-development (push) Successful in 8m42s
Release / release (push) Successful in 10m11s
Reviewed-on: #114
2024-06-07 15:29:09 +00:00
3521579dea fix: adding 60 more colours to crayons table. Closes #112
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 4m45s
2024-06-07 16:23:40 +01:00
e97a6f52da Merge pull request 'fix: terms and conditions colours, editor on smaller screens show only icons' (#113) from issue111 into main
Some checks failed
Release / Build-and-test-development (push) Has been cancelled
Release / release (push) Has been cancelled
Release / Build-production-and-ng-test (push) Has been cancelled
Reviewed-on: #113
2024-06-07 15:22:00 +00:00
Mihajlo Medjedovic
3584aa35c7 chore: package-locks
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 4m49s
2024-06-07 17:07:23 +02:00
Mihajlo Medjedovic
b553520abe style: lint, package-locks
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 55s
2024-06-07 16:58:04 +02:00
Mihajlo Medjedovic
e32d44b1bc fix: terms and conditions colours, editor on smaller screens show only icons
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 52s
2024-06-07 16:34:47 +02:00
semantic-release-bot
105da1503f chore(release): 6.10.0 [skip ci]
# [6.10.0](https://git.datacontroller.io/dc/dc/compare/v6.9.0...v6.10.0) (2024-06-07)

### Features

* updated handsontable to v14 ([2f8d0b7](2f8d0b764a))
2024-06-07 08:58:45 +00:00
29298072e5 Merge pull request 'feat: updated handsontable to v14' (#110) from handonstable-14 into main
All checks were successful
Release / Build-production-and-ng-test (push) Successful in 4m39s
Release / Build-and-test-development (push) Successful in 8m44s
Release / release (push) Successful in 10m9s
Reviewed-on: #110
2024-06-07 08:40:54 +00:00
Mihajlo Medjedovic
154c10fee5 chore: licence checker
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 4m50s
2024-06-07 09:45:10 +02:00
7853f7cb6a Merge branch 'main' into handonstable-14
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 1m32s
2024-05-31 11:39:14 +00:00
semantic-release-bot
e54ecc8a35 chore(release): 6.9.0 [skip ci]
# [6.9.0](https://git.datacontroller.io/dc/dc/compare/v6.8.5...v6.9.0) (2024-05-31)

### Bug Fixes

* added colors.scss file, start of a refactor ([110ad9a](110ad9a6e9))
* added stealFocus directive ([9a79f37](9a79f37bf1))

### Features

* added app settings service to handle theme persistance, fix: optimised dark mode contrast ([35844e0](35844e0cf1))
* **dark mode:** clarity optimizations ([afa7e38](afa7e380aa))
* **dark mode:** lineage and metadata ([27907ed](27907ed00f))
* **dark mode:** refactoring clarity to enable dark mode, added toggle button ([5564aea](5564aea9c2))
* **dark mode:** removing custom css rules so clarity can handle dark/light modes. Handsontable css for dark mode ([2c0afd0](2c0afd0268))
2024-05-31 09:38:30 +00:00
d8b95c5739 Merge pull request 'Dark Mode' (#102) from issue98 into main
All checks were successful
Release / Build-production-and-ng-test (push) Successful in 4m35s
Release / Build-and-test-development (push) Successful in 8m31s
Release / release (push) Successful in 9m57s
Reviewed-on: #102
Reviewed-by: yury <yury@4gl.io>
2024-05-31 09:21:07 +00:00
Mihajlo Medjedovic
2f8d0b764a feat: updated handsontable to v14
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 1m17s
2024-05-30 23:35:05 +02:00
Mihajlo Medjedovic
d7732ed206 ci: fix
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 4m42s
2024-05-30 15:57:18 +02:00
Mihajlo Medjedovic
6e631cd9a5 ci: fix
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 4m43s
2024-05-30 15:53:51 +02:00
Mihajlo Medjedovic
0a9e5dd834 ci: fix
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 1m13s
2024-05-30 15:49:46 +02:00
Mihajlo Medjedovic
d14a4eaadd ci: fix
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 1m14s
2024-05-30 14:56:55 +02:00
Mihajlo Medjedovic
5f7c7fcc7b ci: fix
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 1m13s
2024-05-30 14:52:53 +02:00
Mihajlo Medjedovic
978f152ab6 ci: fix
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 1m39s
2024-05-30 14:46:10 +02:00
Mihajlo Medjedovic
68a2a606f3 ci: fix
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 1m14s
2024-05-30 14:22:01 +02:00
Mihajlo Medjedovic
bad43135d7 chore: sheet crypto package fix
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 1m16s
2024-05-30 14:11:54 +02:00
Mihajlo Medjedovic
110ad9a6e9 fix: added colors.scss file, start of a refactor
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 1m15s
2024-05-30 12:03:09 +02:00
Mihajlo Medjedovic
e98f288302 style: lint
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 1m16s
2024-05-30 09:47:38 +02:00
Mihajlo Medjedovic
9a79f37bf1 fix: added stealFocus directive 2024-05-30 09:47:08 +02:00
Mihajlo Medjedovic
85909cfc1e chore: prettier version
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 1m16s
2024-05-29 16:26:27 +02:00
Mihajlo Medjedovic
4330da520f style: lint
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 1m17s
2024-05-29 16:21:56 +02:00
Mihajlo Medjedovic
27907ed00f feat(dark mode): lineage and metadata 2024-05-29 16:21:28 +02:00
Mihajlo Medjedovic
31c90f3190 style: lint
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 1m15s
2024-05-29 14:36:17 +02:00
Mihajlo Medjedovic
35844e0cf1 feat: added app settings service to handle theme persistance, fix: optimised dark mode contrast 2024-05-29 14:35:43 +02:00
Mihajlo Medjedovic
afa7e380aa feat(dark mode): clarity optimizations
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 50s
2024-05-28 21:17:37 +02:00
cb9a5f0eb4 Merge branch 'main' into issue98
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 51s
2024-05-23 14:17:18 +00:00
Mihajlo Medjedovic
da522c557d chore(git): Merge branch 'issue98' of ssh://git.datacontroller.io:29419/dc/dc into issue98
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 51s
2024-05-23 16:16:57 +02:00
Mihajlo Medjedovic
2c0afd0268 feat(dark mode): removing custom css rules so clarity can handle dark/light modes. Handsontable css for dark mode 2024-05-23 16:16:47 +02:00
semantic-release-bot
20255c69c2 chore(release): 6.8.5 [skip ci]
## [6.8.5](https://git.datacontroller.io/dc/dc/compare/v6.8.4...v6.8.5) (2024-05-23)

### Bug Fixes

* bitemporal load issue [#105](#105) ([967698e](967698e4ce))
2024-05-23 11:34:51 +00:00
74f1c5416b Merge pull request 'fix: bitemporal load issue #105' (#108) from issue105 into main
All checks were successful
Release / Build-production-and-ng-test (push) Successful in 4m43s
Release / Build-and-test-development (push) Successful in 8m40s
Release / release (push) Successful in 10m6s
Reviewed-on: #108
2024-05-23 11:08:03 +00:00
feed7f1ded Merge branch 'main' into issue105
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 4m44s
2024-05-23 11:07:29 +00:00
967698e4ce fix: bitemporal load issue #105
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 4m49s
2024-05-23 12:07:11 +01:00
semantic-release-bot
71bd81ae47 chore(release): 6.8.4 [skip ci]
## [6.8.4](https://git.datacontroller.io/dc/dc/compare/v6.8.3...v6.8.4) (2024-05-22)

### Bug Fixes

* new approach to fixing [#105](#105) ([c11bd9a](c11bd9a2c5))
2024-05-22 16:56:15 +00:00
89a5153bb3 Merge pull request 'fix: new approach to fixing #105' (#106) from issue105 into main
All checks were successful
Release / Build-production-and-ng-test (push) Successful in 4m44s
Release / Build-and-test-development (push) Successful in 8m42s
Release / release (push) Successful in 10m14s
Reviewed-on: #106
2024-05-22 16:33:29 +00:00
c11bd9a2c5 fix: new approach to fixing #105
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 4m54s
2024-05-22 17:33:04 +01:00
59d46a9926 Merge branch 'main' into issue98
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 51s
2024-05-22 11:30:29 +00:00
semantic-release-bot
39c3e5411f chore(release): 6.8.3 [skip ci]
## [6.8.3](https://git.datacontroller.io/dc/dc/compare/v6.8.2...v6.8.3) (2024-05-09)

### Bug Fixes

* updating core to increase filename length, closes [#103](#103) ([ee58fd5](ee58fd5b4b))
2024-05-09 11:47:48 +00:00
aad419c55d Merge pull request 'fix: updating core to increase filename length, closes #103' (#104) from issue103 into main
All checks were successful
Release / Build-production-and-ng-test (push) Successful in 4m48s
Release / Build-and-test-development (push) Successful in 8m51s
Release / release (push) Successful in 10m9s
Reviewed-on: #104
2024-05-09 11:29:37 +00:00
^
ee58fd5b4b fix: updating core to increase filename length, closes #103
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 5m2s
2024-05-09 12:23:04 +01:00
Mihajlo Medjedovic
5564aea9c2 feat(dark mode): refactoring clarity to enable dark mode, added toggle button
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 51s
2024-05-08 16:51:01 +02:00
aedd2c451b Merge pull request 'release-ci' (#100) from release-ci into main
Some checks failed
Release / Build-production-and-ng-test (push) Successful in 5m1s
Release / Build-and-test-development (push) Successful in 8m54s
Release / release (push) Failing after 4m31s
Reviewed-on: #100
2024-05-06 21:33:57 +00:00
Mihajlo Medjedovic
6d597611b6 chore(git): Merge branch 'main' into release-ci
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 4m9s
2024-05-06 09:04:34 +02:00
Mihajlo Medjedovic
9ffe5efe5d ci: added viya.json to the release assets 2024-05-06 09:04:19 +02:00
semantic-release-bot
2715950d86 chore(release): 6.8.2 [skip ci]
## [6.8.2](https://git.datacontroller.io/dc/dc/compare/v6.8.1...v6.8.2) (2024-05-03)

### Bug Fixes

* dc_request_logs option feature ([93758ef](93758efb27))
* release process ([c0dc919](c0dc9191e3))
2024-05-03 07:19:26 +00:00
Mihajlo Medjedovic
77a7190f4d ci: fixing release script
All checks were successful
Release / Build-production-and-ng-test (push) Successful in 3m58s
Release / Build-and-test-development (push) Successful in 7m39s
Release / release (push) Successful in 8m23s
2024-05-03 09:03:56 +02:00
cc65890fea Merge pull request 'fix: dc_request_logs option feature closes #96' (#97) from issue96 into main
Some checks failed
Release / Build-production-and-ng-test (push) Successful in 3m58s
Release / Build-and-test-development (push) Successful in 7m52s
Release / release (push) Failing after 1m18s
Reviewed-on: #97
2024-05-02 22:25:29 +00:00
^
93758efb27 fix: dc_request_logs option feature
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 4m3s
see: https://docs.datacontroller.io/dcc-options/#dc_request_logs
2024-05-02 23:09:14 +01:00
Mihajlo Medjedovic
c0dc9191e3 fix: release process
Some checks failed
Release / Build-production-and-ng-test (push) Successful in 3m57s
Release / Build-and-test-development (push) Successful in 7m44s
Release / release (push) Failing after 1m17s
2024-05-02 18:41:25 +02:00
semantic-release-bot
485783a782 chore(release): 6.8.1 [skip ci]
## [6.8.1](https://git.datacontroller.io/dc/dc/compare/v6.8.0...v6.8.1) (2024-05-02)

### Bug Fixes

* hide approve button when table revertable ([ec0f539](ec0f539a33))
2024-05-02 16:10:44 +00:00
ee07bef2b8 Merge pull request 'fix: hide approve button when table revertable' (#95) from ci-fix into main
Some checks failed
Release / Build-production-and-ng-test (push) Successful in 3m59s
Release / Build-and-test-development (push) Successful in 7m45s
Release / release (push) Failing after 1m55s
Reviewed-on: #95
2024-05-02 15:57:22 +00:00
Mihajlo Medjedovic
0de8481314 ci: ng build
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 4m4s
2024-05-02 17:48:30 +02:00
Mihajlo Medjedovic
ec0f539a33 fix: hide approve button when table revertable
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 2m1s
2024-05-02 17:44:53 +02:00
semantic-release-bot
173ee2daff chore(release): 6.8.0 [skip ci]
# [6.8.0](https://git.datacontroller.io/dc/dc/compare/v6.7.0...v6.8.0) (2024-05-02)

### Bug Fixes

* ci sheet lib, submit message auto focus ([c5e4650](c5e4650327))
* **clarity:** new version style issues ([8c7de5a](8c7de5aad7))
* cypress tests ([3dd85cc](3dd85cc60b))
* ensuring that only restorable versions are restorable ([a402856](a4028562ce))
* ensuring version history only includes loaded versions ([51ebd25](51ebd25aa3))
* final testing on restore feature ([297a84d](297a84d3a4))
* issue with multiple adds/deletes, [#84](#84) ([904ca30](904ca30f91))
* load_ref var ([aaad9f7](aaad9f7207))
* removing alerts dummy data, closes [#93](#93) ([eba21e9](eba21e96b4))
* restore table version improvement ([549f357](549f35766b))
* **sas:** viewer versions fix ([c6595c1](c6595c1f61))
* stage and approve buttons renaming ([ef81e33](ef81e33f70))
* supporting SCD2 data reversions ([fa8396f](fa8396f039))
* table info modal, versions - column names ([801c8c6](801c8c6a9f))
* **updates:** angular, clarity, resolved legacy-peer-deps ([c60dd65](c60dd65a16))

### Features

* backend to show in getchangeinfo whether a user is allowed to restore ([8769841](8769841f08))
* list versions of target tables (backend) ([f8a14d4](f8a14d4bde))
* restore ([604c2e7](604c2e70bd))
* SAS services & tests for RESTORE, [#84](#84) ([9ad7ae4](9ad7ae47b5))
* staging page, restore buttons ([02a8a1c](02a8a1c565))
* table metadata modal, versions tab (and link) ([b27fea5](b27fea5b91))
* **versions:** getting list of versions (plus test) ([8003da9](8003da94e6))
2024-05-02 12:08:05 +00:00
Mihajlo Medjedovic
5c0091b5e8 chore: test workaround
Some checks failed
Release / Build-production-and-ng-test (push) Successful in 3m58s
Release / Build-and-test-development (push) Successful in 7m44s
Release / release (push) Failing after 1m55s
2024-05-02 13:54:44 +02:00
84dce7f6e8 Merge pull request 'Restore Previous State / Data Rollback' (#94) from restore into main
Some checks failed
Release / Build-production-and-ng-test (push) Successful in 4m1s
Release / Build-and-test-development (push) Failing after 8m26s
Release / release (push) Has been skipped
Reviewed-on: #94
2024-05-02 11:18:28 +00:00
Mihajlo Medjedovic
e8a943a35a chore: ng test
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 2m1s
2024-05-02 13:08:06 +02:00
Mihajlo Medjedovic
b3171a8125 ci: fix
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 1m57s
2024-05-02 12:56:52 +02:00
Mihajlo Medjedovic
d77f2eb674 chore(git): Merge branch 'deps-update' into restore
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 1m30s
2024-05-02 12:53:32 +02:00
Mihajlo Medjedovic
5474fad9cc chore: package-lock
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 1m2s
2024-05-02 12:46:50 +02:00
Mihajlo Medjedovic
3dd85cc60b fix: cypress tests 2024-05-02 12:39:07 +02:00
Mihajlo Medjedovic
510e412ff2 chore: cypress tests fix
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 15s
2024-05-02 11:25:46 +02:00
Mihajlo Medjedovic
3dfdccbc6b ci: sheet lib
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 1m2s
2024-05-02 10:04:31 +02:00
Mihajlo Medjedovic
a55661548a chore: licence checker
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 1m2s
2024-05-02 09:59:21 +02:00
Mihajlo Medjedovic
69363b37e9 ci: sheet lib
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 1m2s
2024-05-02 09:54:10 +02:00
Mihajlo Medjedovic
2d6a753921 ci: sheet lib
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 15s
2024-05-02 09:52:22 +02:00
Mihajlo Medjedovic
dc989e5668 ci: fix
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 58s
2024-05-02 09:38:43 +02:00
Mihajlo Medjedovic
227ac480d5 style: lint
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 16s
2024-05-02 09:30:23 +02:00
Mihajlo Medjedovic
c5e4650327 fix: ci sheet lib, submit message auto focus
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 16s
2024-05-02 09:28:39 +02:00
56cf271e77 Merge branch 'main' into deps-update
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 57s
2024-04-30 17:41:54 +00:00
^
fa8396f039 fix: supporting SCD2 data reversions
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 15s
2024-04-30 18:39:00 +01:00
^
904ca30f91 fix: issue with multiple adds/deletes, #84
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 16s
2024-04-30 17:46:00 +01:00
Mihajlo Medjedovic
549f35766b fix: restore table version improvement
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 15s
2024-04-30 15:43:17 +02:00
Mihajlo Medjedovic
1589c799ec chore(git): Merge branch 'restore' of ssh://git.datacontroller.io:29419/dc/dc into restore
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 15s
2024-04-30 15:26:07 +02:00
Mihajlo Medjedovic
604c2e70bd feat: restore 2024-04-30 15:25:58 +02:00
^
297a84d3a4 fix: final testing on restore feature
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 59s
2024-04-30 14:21:02 +01:00
^
aaad9f7207 fix: load_ref var
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 58s
2024-04-30 12:34:22 +01:00
^
a4028562ce fix: ensuring that only restorable versions are restorable
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 58s
2024-04-30 11:14:47 +01:00
Mihajlo Medjedovic
5ab3f98855 chore(git): Merge branch 'main' into restore
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 59s
2024-04-30 09:04:46 +02:00
^
eba21e96b4 fix: removing alerts dummy data, closes #93
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 18s
2024-04-30 00:22:44 +01:00
^
9ad7ae47b5 feat: SAS services & tests for RESTORE, #84 2024-04-30 00:20:40 +01:00
cf54e4c8f3 Update README.md
Some checks failed
Release / Build-production-and-ng-test (push) Failing after 1m31s
Release / Build-and-test-development (push) Has been skipped
Release / release (push) Has been skipped
2024-04-23 13:06:51 +00:00
Mihajlo Medjedovic
57aa6fa0fc ci: fix
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 58s
2024-04-15 09:37:20 +02:00
Mihajlo Medjedovic
4a01f3d490 ci: fix
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 57s
2024-04-12 21:37:49 +02:00
Mihajlo Medjedovic
80a0db951d chore: angular testing
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 1m5s
2024-04-12 15:48:00 +02:00
Mihajlo Medjedovic
3202cb8e08 ci: ng test fix
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 1m4s
2024-04-12 14:23:04 +02:00
Mihajlo Medjedovic
ddf36230bf ci: ng test fix
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 1m4s
2024-04-12 14:06:29 +02:00
Mihajlo Medjedovic
b67c2be968 chore: package json test script fix
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 1m3s
2024-04-12 13:53:35 +02:00
Mihajlo Medjedovic
dc2c8da92b ci: added angular tests on PR
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 1m10s
2024-04-12 13:30:44 +02:00
Mihajlo Medjedovic
5d6c3701d0 chore: licence checker update
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 1m3s
2024-04-12 13:28:18 +02:00
Mihajlo Medjedovic
b024e263b4 chore: updated package-lock
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 1m2s
2024-04-12 13:20:08 +02:00
b706864e40 Merge pull request 'Angular and Clarity Update' (#91) from deps-update into main
Some checks failed
Release / Build-production-and-ng-test (push) Failing after 1m37s
Release / Build-and-test-development (push) Has been skipped
Release / release (push) Has been skipped
Reviewed-on: #91
2024-04-12 10:45:57 +00:00
Mihajlo Medjedovic
60cc666b67 chore: licence checker
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 1m15s
2024-04-12 12:34:01 +02:00
Mihajlo Medjedovic
efff4dd553 chore(cypress): bigger viewport size
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 1m1s
2024-04-12 11:48:23 +02:00
Mihajlo Medjedovic
2b1dad8e48 style: lint
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 1m2s
2024-04-12 10:51:32 +02:00
Mihajlo Medjedovic
8c7de5aad7 fix(clarity): new version style issues 2024-04-12 10:51:11 +02:00
Mihajlo Medjedovic
1db8bc2573 style: lint
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 1m12s
2024-04-11 13:23:40 +02:00
Mihajlo Medjedovic
c60dd65a16 fix(updates): angular, clarity, resolved legacy-peer-deps 2024-04-11 13:23:24 +02:00
f7f59a4b0a Merge pull request 'feat: Display Previous Versions' (#88) from restore into main
Some checks failed
Release / Build-production-and-ng-test (push) Successful in 4m18s
Release / Build-and-test-development (push) Failing after 15m54s
Release / release (push) Has been skipped
Reviewed-on: #88
2024-04-04 13:24:25 +00:00
^
ec7615e7e3 chore: adding sheet-crypto to gitignoreg
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 1m5s
2024-04-03 10:56:49 +01:00
Mihajlo Medjedovic
f411c33754 chore(git): Merge branch 'main' into restore
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 1m4s
2024-04-01 13:51:33 +02:00
Mihajlo Medjedovic
79121168e4 style: lint
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 1m4s
2024-04-01 13:50:50 +02:00
Mihajlo Medjedovic
ef81e33f70 fix: stage and approve buttons renaming
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 15s
2024-04-01 13:37:41 +02:00
semantic-release-bot
96066c66cb chore(release): 6.7.0 [skip ci]
# [6.7.0](https://git.datacontroller.io/dc/dc/compare/v6.6.4...v6.7.0) (2024-04-01)

### Features

* numeric values in hot dropdown aligned right ([9635626](963562621d))
2024-04-01 11:23:31 +00:00
Mihajlo Medjedovic
b1819b776d chore(git): Merge branch 'restore' of ssh://git.datacontroller.io:29419/dc/dc into restore
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 15s
2024-04-01 13:09:34 +02:00
Mihajlo Medjedovic
bd7a392ffc chore(git): Merge branch 'main' into restore 2024-04-01 13:09:27 +02:00
7997b77158 Merge pull request 'Numeric values in hot dropdown aligned right' (#86) from numeric-values-diff into main
All checks were successful
Release / Build-production-and-ng-test (push) Successful in 4m26s
Release / Build-and-test-development (push) Successful in 8m10s
Release / release (push) Successful in 6m32s
Reviewed-on: #86
2024-04-01 11:08:53 +00:00
Mihajlo Medjedovic
d1966bcdc5 chore(git): Merge branch 'main' into numeric-values-diff
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 1m3s
2024-04-01 12:33:43 +02:00
semantic-release-bot
7b5bbe024d chore(release): 6.6.4 [skip ci]
## [6.6.4](https://git.datacontroller.io/dc/dc/compare/v6.6.3...v6.6.4) (2024-04-01)

### Bug Fixes

* ordering SOFTSELECT numerically in dropdown ([f522038](f522038b8d)), closes [#85](#85)
* reverting col ([fbbcf90](fbbcf90956))
* typo ([31d4e5c](31d4e5c727))
2024-04-01 09:51:54 +00:00
4d84f15aca Merge pull request 'ci: install sheet temporarily' (#89) from ci into main
All checks were successful
Release / Build-production-and-ng-test (push) Successful in 4m16s
Release / Build-and-test-development (push) Successful in 8m1s
Release / release (push) Successful in 6m22s
Reviewed-on: #89
2024-04-01 09:37:56 +00:00
Mihajlo Medjedovic
928937daab ci: sheet
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 1m4s
2024-04-01 11:14:00 +02:00
Mihajlo Medjedovic
3bd8d247e5 ci: sheet
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 1m3s
2024-04-01 10:59:03 +02:00
Mihajlo Medjedovic
cf6c9dd5f2 ci: sheet
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 1m0s
2024-04-01 10:55:59 +02:00
Mihajlo Medjedovic
ff55cbbaad ci: sheet
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 17s
2024-04-01 10:45:16 +02:00
Mihajlo Medjedovic
3eda4e2c58 ci: install sheet temporarily
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 17s
2024-04-01 10:39:30 +02:00
Mihajlo Medjedovic
02a8a1c565 feat: staging page, restore buttons 2024-03-29 13:08:48 +01:00
^
8769841f08 feat: backend to show in getchangeinfo whether a user is allowed to restore
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 15s
2024-03-28 16:03:27 +00:00
Mihajlo Medjedovic
7208fe1c3b chore(git): Merge branch 'restore' of ssh://git.datacontroller.io:29419/dc/dc into restore
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 15s
2024-03-26 19:14:53 +01:00
Mihajlo Medjedovic
801c8c6a9f fix: table info modal, versions - column names 2024-03-26 19:14:41 +01:00
^
51ebd25aa3 fix: ensuring version history only includes loaded versions
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 48s
2024-03-26 14:25:58 +00:00
Mihajlo Medjedovic
c6595c1f61 fix(sas): viewer versions fix
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 46s
2024-03-26 14:20:01 +01:00
Mihajlo Medjedovic
a267666e99 style: lint
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 46s
2024-03-25 22:41:25 +01:00
Mihajlo Medjedovic
b27fea5b91 feat: table metadata modal, versions tab (and link) 2024-03-25 22:41:00 +01:00
^
f8a14d4bde feat: list versions of target tables (backend)
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 47s
2024-03-25 12:37:11 +00:00
^
633e35338d chore: remove unnecessary concatenation 2024-03-20 21:38:44 +00:00
^
8003da94e6 feat(versions): getting list of versions (plus test) 2024-03-20 21:37:13 +00:00
c3af97ef57 Merge pull request 'issue85' (#87) from issue85 into main
Some checks failed
Release / Build-production-and-ng-test (push) Failing after 1m13s
Release / Build-and-test-development (push) Has been skipped
Release / release (push) Has been skipped
Reviewed-on: #87
2024-03-19 22:41:04 +00:00
31d4e5c727 fix: typo
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 47s
2024-03-19 22:36:00 +00:00
fbbcf90956 fix: reverting col 2024-03-19 22:35:16 +00:00
f522038b8d fix: ordering SOFTSELECT numerically in dropdown
Closes #85
2024-03-19 22:33:39 +00:00
Mihajlo Medjedovic
ace599b39f style: lint
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 46s
2024-03-19 10:01:30 +01:00
Mihajlo Medjedovic
963562621d feat: numeric values in hot dropdown aligned right
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 29s
2024-03-18 17:32:29 +01:00
semantic-release-bot
5171d07441 chore(release): 6.6.3 [skip ci]
## [6.6.3](https://git.datacontroller.io/dc/dc/compare/v6.6.2...v6.6.3) (2024-02-26)

### Bug Fixes

* allow empty clause value when NE or CONTAINS ([432450a](432450a15b))
2024-02-26 14:17:24 +00:00
9a0b9573d5 Merge pull request 'Allow empty clause value when operator is NE or CONTAINS' (#83) from issue-82 into main
All checks were successful
Release / Build-production-and-ng-test (push) Successful in 4m18s
Release / Build-and-test-development (push) Successful in 7m46s
Release / release (push) Successful in 6m18s
Reviewed-on: #83
2024-02-26 14:03:36 +00:00
Mihajlo Medjedovic
4733311ef3 style: lint
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 1m0s
2024-02-26 14:15:34 +01:00
Mihajlo Medjedovic
432450a15b fix: allow empty clause value when NE or CONTAINS 2024-02-26 14:14:51 +01:00
136 changed files with 10677 additions and 30409 deletions

View File

@ -8,9 +8,16 @@ jobs:
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- uses: actions/setup-node@v3 - uses: actions/setup-node@v4
with: with:
node-version: 18 node-version: 20.14.0
- name: Install Google Chrome
run: |
wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -
echo "deb http://dl.google.com/linux/chrome/deb/ stable main" > /etc/apt/sources.list.d/google.list
apt-get update
apt-get install -y google-chrome-stable xvfb
- name: Write .npmrc file - name: Write .npmrc file
run: echo "$NPMRC" > client/.npmrc run: echo "$NPMRC" > client/.npmrc
@ -21,8 +28,24 @@ jobs:
- name: Lint check - name: Lint check
run: npm run lint:check run: npm run lint:check
- name: Install dependencies
run: |
cd client
# Decrypt and Install sheet
echo ${{ secrets.SHEET_PWD }} | gpg --batch --yes --passphrase-fd 0 ./libraries/sheet-crypto.tgz.gpg
npm ci
- name: Licence checker - name: Licence checker
run: | run: |
cd client cd client
npm ci npm run license-checker
npm run license-checker
- name: Angular Tests
run: |
cd client
npm run test:headless
- name: Production Build
run: |
cd client
npm run build

View File

@ -11,9 +11,9 @@ jobs:
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- uses: actions/setup-node@v3 - uses: actions/setup-node@v4
with: with:
node-version: 18 node-version: 20.14.0
- name: Write .npmrc file - name: Write .npmrc file
run: | run: |
@ -34,7 +34,11 @@ jobs:
CYPRESS_CREDS: ${{ secrets.CYPRESS_CREDS }} CYPRESS_CREDS: ${{ secrets.CYPRESS_CREDS }}
- name: Install dependencies - name: Install dependencies
run: npm ci run: |
cd client
# Decrypt and Install sheet
echo ${{ secrets.SHEET_PWD }} | gpg --batch --yes --passphrase-fd 0 ./libraries/sheet-crypto.tgz.gpg
npm ci
- name: Check audit - name: Check audit
# Audit should fail and stop the CI if critical vulnerability found # Audit should fail and stop the CI if critical vulnerability found
@ -48,7 +52,7 @@ jobs:
- name: Angular Tests - name: Angular Tests
run: | run: |
cd client cd client
npm test -- --no-watch --no-progress --browsers=ChromeHeadlessCI npm run test:headless
- name: Angular Production Build - name: Angular Production Build
run: | run: |
@ -62,9 +66,9 @@ jobs:
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- uses: actions/setup-node@v3 - uses: actions/setup-node@v4
with: with:
node-version: 18 node-version: 20.14.0
- name: Write .npmrc file - name: Write .npmrc file
run: | run: |
@ -86,7 +90,11 @@ jobs:
CYPRESS_CREDS: ${{ secrets.CYPRESS_CREDS }} CYPRESS_CREDS: ${{ secrets.CYPRESS_CREDS }}
- name: Install dependencies - name: Install dependencies
run: npm ci run: |
cd client
# Decrypt and Install sheet
echo ${{ secrets.SHEET_PWD }} | gpg --batch --yes --passphrase-fd 0 ./libraries/sheet-crypto.tgz.gpg
npm ci
# Install pm2 and prepare SASJS server # Install pm2 and prepare SASJS server
- run: npm i -g pm2 - run: npm i -g pm2
@ -148,9 +156,9 @@ jobs:
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- uses: actions/setup-node@v3 - uses: actions/setup-node@v4
with: with:
node-version: 20 node-version: 20.14.0
- name: Write .npmrc file - name: Write .npmrc file
run: | run: |
@ -172,6 +180,16 @@ jobs:
apt-get update apt-get update
apt-get install doxygen -y apt-get install doxygen -y
- name: Frontend Preliminary Build
description: We want to prevent creating empty release if frontend fails
run: |
cd client
# Decrypt and Install sheet
echo ${{ secrets.SHEET_PWD }} | gpg --batch --yes --passphrase-fd 0 ./libraries/sheet-crypto.tgz.gpg
npm ci
npm i webpack
npm run build
- name: Create Empty Release (assets are posted later) - name: Create Empty Release (assets are posted later)
run: | run: |
npm i npm i
@ -184,7 +202,6 @@ jobs:
description: Must be created AFTER the release as the version (git tag) is used in the interface description: Must be created AFTER the release as the version (git tag) is used in the interface
run: | run: |
cd client cd client
npm ci
npm run build npm run build
- name: Build SAS9 EBI Release - name: Build SAS9 EBI Release
@ -228,6 +245,7 @@ jobs:
rm sasjsbuild/services/clickme.html rm sasjsbuild/services/clickme.html
sasjs b -t viya sasjs b -t viya
cp sasjsbuild/viya.sas ./viya.sas cp sasjsbuild/viya.sas ./viya.sas
cp sasjsbuild/viya.json ./viya.json
- name: Zip Frontend (including viya.json for full viya deploy) - name: Zip Frontend (including viya.json for full viya deploy)
run: | run: |
@ -263,3 +281,4 @@ jobs:
curl -k $URL -F attachment=@sas/sasjs_server.json.zip curl -k $URL -F attachment=@sas/sasjs_server.json.zip
curl -k $URL -F attachment=@sas/sas9.sas curl -k $URL -F attachment=@sas/sas9.sas
curl -k $URL -F attachment=@sas/viya.sas curl -k $URL -F attachment=@sas/viya.sas
curl -k $URL -F attachment=@sas/viya.json

3
.gitignore vendored
View File

@ -11,6 +11,9 @@ client/cypress/screenshots
client/cypress/results client/cypress/results
client/cypress/videos client/cypress/videos
client/documentation client/documentation
client/**/sheet-crypto.tgz
client/.nx
client/libraries/sheet-crypto.tgz
cypress.env.json cypress.env.json
sasjsbuild sasjsbuild
sasjsresults sasjsresults

View File

@ -1,3 +1,126 @@
## [6.10.1](https://git.datacontroller.io/dc/dc/compare/v6.10.0...v6.10.1) (2024-06-07)
### Bug Fixes
* adding 60 more colours to crayons table. Closes [#112](https://git.datacontroller.io/dc/dc/issues/112) ([3521579](https://git.datacontroller.io/dc/dc/commit/3521579dead089eebf62455686be3aee88bde687))
* terms and conditions colours, editor on smaller screens show only icons ([e32d44b](https://git.datacontroller.io/dc/dc/commit/e32d44b1bcdfeea43d19b21ec0ddf4af1ce3992a))
# [6.10.0](https://git.datacontroller.io/dc/dc/compare/v6.9.0...v6.10.0) (2024-06-07)
### Features
* updated handsontable to v14 ([2f8d0b7](https://git.datacontroller.io/dc/dc/commit/2f8d0b764a957ad8c11cd1088fad5e0670aa1731))
# [6.9.0](https://git.datacontroller.io/dc/dc/compare/v6.8.5...v6.9.0) (2024-05-31)
### Bug Fixes
* added colors.scss file, start of a refactor ([110ad9a](https://git.datacontroller.io/dc/dc/commit/110ad9a6e9ed39bd5591ae65c2d0005ba47ca758))
* added stealFocus directive ([9a79f37](https://git.datacontroller.io/dc/dc/commit/9a79f37bf143a1e05df7407358e2687c678e3e68))
### Features
* added app settings service to handle theme persistance, fix: optimised dark mode contrast ([35844e0](https://git.datacontroller.io/dc/dc/commit/35844e0cf1a639553269f2ab0f8666a56ab5cc47))
* **dark mode:** clarity optimizations ([afa7e38](https://git.datacontroller.io/dc/dc/commit/afa7e380aa3bdabd380c038522b9d73d9a8a3b91))
* **dark mode:** lineage and metadata ([27907ed](https://git.datacontroller.io/dc/dc/commit/27907ed00fe81f4c752ffe99d2fb029d5c884f0a))
* **dark mode:** refactoring clarity to enable dark mode, added toggle button ([5564aea](https://git.datacontroller.io/dc/dc/commit/5564aea9c25f8e81ff85afa8352325b9992e4043))
* **dark mode:** removing custom css rules so clarity can handle dark/light modes. Handsontable css for dark mode ([2c0afd0](https://git.datacontroller.io/dc/dc/commit/2c0afd02684cdf3bda374731b0359665e00ed95d))
## [6.8.5](https://git.datacontroller.io/dc/dc/compare/v6.8.4...v6.8.5) (2024-05-23)
### Bug Fixes
* bitemporal load issue [#105](https://git.datacontroller.io/dc/dc/issues/105) ([967698e](https://git.datacontroller.io/dc/dc/commit/967698e4ce1e0abcbc6f0aff8a4be6c512dee93c))
## [6.8.4](https://git.datacontroller.io/dc/dc/compare/v6.8.3...v6.8.4) (2024-05-22)
### Bug Fixes
* new approach to fixing [#105](https://git.datacontroller.io/dc/dc/issues/105) ([c11bd9a](https://git.datacontroller.io/dc/dc/commit/c11bd9a2c55e49f10451962cb2e222c21206bce5))
## [6.8.3](https://git.datacontroller.io/dc/dc/compare/v6.8.2...v6.8.3) (2024-05-09)
### Bug Fixes
* updating core to increase filename length, closes [#103](https://git.datacontroller.io/dc/dc/issues/103) ([ee58fd5](https://git.datacontroller.io/dc/dc/commit/ee58fd5b4bc0dd3e3f232c4f26bb85b2e7fe2b54))
## [6.8.2](https://git.datacontroller.io/dc/dc/compare/v6.8.1...v6.8.2) (2024-05-03)
### Bug Fixes
* dc_request_logs option feature ([93758ef](https://git.datacontroller.io/dc/dc/commit/93758efb275966c181f1ee8b6c752010909a0282))
* release process ([c0dc919](https://git.datacontroller.io/dc/dc/commit/c0dc9191e3b95ea6f7e5021fc0bdbcab0af4cc64))
## [6.8.1](https://git.datacontroller.io/dc/dc/compare/v6.8.0...v6.8.1) (2024-05-02)
### Bug Fixes
* hide approve button when table revertable ([ec0f539](https://git.datacontroller.io/dc/dc/commit/ec0f539a337b176c83a661ff520a6892d47efa02))
# [6.8.0](https://git.datacontroller.io/dc/dc/compare/v6.7.0...v6.8.0) (2024-05-02)
### Bug Fixes
* ci sheet lib, submit message auto focus ([c5e4650](https://git.datacontroller.io/dc/dc/commit/c5e46503272f3f3d9cd83ac04225babf79d4de44))
* **clarity:** new version style issues ([8c7de5a](https://git.datacontroller.io/dc/dc/commit/8c7de5aad7e7e32a64769696af9b93eb9a6225d3))
* cypress tests ([3dd85cc](https://git.datacontroller.io/dc/dc/commit/3dd85cc60bd5ac99bc930b6b9c89a8e707e4d51d))
* ensuring that only restorable versions are restorable ([a402856](https://git.datacontroller.io/dc/dc/commit/a4028562ce91b32ff971ab9821328b97cd23f381))
* ensuring version history only includes loaded versions ([51ebd25](https://git.datacontroller.io/dc/dc/commit/51ebd25aa362aa8e66c83b29b2c64aa0f206f5bd))
* final testing on restore feature ([297a84d](https://git.datacontroller.io/dc/dc/commit/297a84d3a4ebb47bef7f3ca9758978d727afed8d))
* issue with multiple adds/deletes, [#84](https://git.datacontroller.io/dc/dc/issues/84) ([904ca30](https://git.datacontroller.io/dc/dc/commit/904ca30f918da085fa05dae066367b512933d1a9))
* load_ref var ([aaad9f7](https://git.datacontroller.io/dc/dc/commit/aaad9f7207115599a006980fff099d59738dd2cd))
* removing alerts dummy data, closes [#93](https://git.datacontroller.io/dc/dc/issues/93) ([eba21e9](https://git.datacontroller.io/dc/dc/commit/eba21e96b4fa34e63b4477281f47d9a01d621f2e))
* restore table version improvement ([549f357](https://git.datacontroller.io/dc/dc/commit/549f35766ba7b5bbe55694845e85bfefc4193375))
* **sas:** viewer versions fix ([c6595c1](https://git.datacontroller.io/dc/dc/commit/c6595c1f618803d9202cba1a1fe76986449cf2e2))
* stage and approve buttons renaming ([ef81e33](https://git.datacontroller.io/dc/dc/commit/ef81e33f704d0b4f99405d96498e1d29ac817982))
* supporting SCD2 data reversions ([fa8396f](https://git.datacontroller.io/dc/dc/commit/fa8396f0394cbddb6dbacb4b355de078fad49980))
* table info modal, versions - column names ([801c8c6](https://git.datacontroller.io/dc/dc/commit/801c8c6a9fb95388a06a6c6284fec4dc25bb77c5))
* **updates:** angular, clarity, resolved legacy-peer-deps ([c60dd65](https://git.datacontroller.io/dc/dc/commit/c60dd65a1637333f11a0c39ef697c7292a6ede07))
### Features
* backend to show in getchangeinfo whether a user is allowed to restore ([8769841](https://git.datacontroller.io/dc/dc/commit/8769841f08694f672ef7ae1a17beacd0dbedda52))
* list versions of target tables (backend) ([f8a14d4](https://git.datacontroller.io/dc/dc/commit/f8a14d4bdef055b99930491d1f6fabe55a50a497))
* restore ([604c2e7](https://git.datacontroller.io/dc/dc/commit/604c2e70bdeeeb1aa5bb18b94f525ebd049397fa))
* SAS services & tests for RESTORE, [#84](https://git.datacontroller.io/dc/dc/issues/84) ([9ad7ae4](https://git.datacontroller.io/dc/dc/commit/9ad7ae47b5e793ce68ab21c9eeb8dee6cb85e496))
* staging page, restore buttons ([02a8a1c](https://git.datacontroller.io/dc/dc/commit/02a8a1c5654350cafc53b749cceb686fc6848c33))
* table metadata modal, versions tab (and link) ([b27fea5](https://git.datacontroller.io/dc/dc/commit/b27fea5b91e33b4673a3a991aedae558e366ca29))
* **versions:** getting list of versions (plus test) ([8003da9](https://git.datacontroller.io/dc/dc/commit/8003da94e615463ed3ddfd60b0cbf2e58615eab1))
# [6.7.0](https://git.datacontroller.io/dc/dc/compare/v6.6.4...v6.7.0) (2024-04-01)
### Features
* numeric values in hot dropdown aligned right ([9635626](https://git.datacontroller.io/dc/dc/commit/963562621ddf0e8d24a29a8481c5e6da1b040708))
## [6.6.4](https://git.datacontroller.io/dc/dc/compare/v6.6.3...v6.6.4) (2024-04-01)
### Bug Fixes
* ordering SOFTSELECT numerically in dropdown ([f522038](https://git.datacontroller.io/dc/dc/commit/f522038b8ddb1da14b8adbf8346d0a4539a94cc8)), closes [#85](https://git.datacontroller.io/dc/dc/issues/85)
* reverting col ([fbbcf90](https://git.datacontroller.io/dc/dc/commit/fbbcf90956bf538b032b0107c07b8576d20353b9))
* typo ([31d4e5c](https://git.datacontroller.io/dc/dc/commit/31d4e5c727f790d428fb2ea8da60dca929561805))
## [6.6.3](https://git.datacontroller.io/dc/dc/compare/v6.6.2...v6.6.3) (2024-02-26)
### Bug Fixes
* allow empty clause value when NE or CONTAINS ([432450a](https://git.datacontroller.io/dc/dc/commit/432450a15b51a269821ba1d430854f5d1dd04703))
## [6.6.2](https://git.datacontroller.io/dc/dc/compare/v6.6.1...v6.6.2) (2024-02-22) ## [6.6.2](https://git.datacontroller.io/dc/dc/compare/v6.6.1...v6.6.2) (2024-02-22)

View File

@ -27,4 +27,6 @@ For more information:
* Main site: https://datacontroller.io * Main site: https://datacontroller.io
* Docs: https://docs.datacontroller.io * Docs: https://docs.datacontroller.io
* Code: https://code.datacontroller.io * Code: https://code.datacontroller.io
For support, contact support@4gl.io or reach out on [Matrix](https://matrix.to/#/#dc:4gl.io)!

View File

@ -45,6 +45,7 @@
"numbro", "numbro",
"@clr/icons", "@clr/icons",
"@sasjs/adapter", "@sasjs/adapter",
"@sasjs/utils/types/serverType",
"@sasjs/utils/input/validators", "@sasjs/utils/input/validators",
"@sasjs/utils/utils/bytesToSize", "@sasjs/utils/utils/bytesToSize",
"base64-arraybuffer", "base64-arraybuffer",
@ -67,7 +68,6 @@
"src/styles.scss" "src/styles.scss"
], ],
"scripts": [ "scripts": [
"node_modules/@clr/icons/clr-icons.min.js",
"node_modules/marked/marked.min.js" "node_modules/marked/marked.min.js"
] ]
}, },
@ -116,10 +116,10 @@
"builder": "@angular-devkit/build-angular:dev-server", "builder": "@angular-devkit/build-angular:dev-server",
"configurations": { "configurations": {
"production": { "production": {
"browserTarget": "datacontroller:build:production" "buildTarget": "datacontroller:build:production"
}, },
"development": { "development": {
"browserTarget": "datacontroller:build:development" "buildTarget": "datacontroller:build:development"
} }
}, },
"defaultConfiguration": "development" "defaultConfiguration": "development"
@ -127,30 +127,27 @@
"extract-i18n": { "extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n", "builder": "@angular-devkit/build-angular:extract-i18n",
"options": { "options": {
"browserTarget": "datacontroller:build" "buildTarget": "datacontroller:build"
} }
}, },
"test": { "test": {
"builder": "@angular-devkit/build-angular:karma", "builder": "@angular-devkit/build-angular:karma",
"options": { "options": {
"tsConfig": "tsconfig.spec.json",
"inlineStyleLanguage": "scss",
"codeCoverage": true,
"polyfills": [ "polyfills": [
"src/polyfills.ts", "src/polyfills.ts",
"zone.js", "zone.js",
"zone.js/testing" "zone.js/testing"
], ],
"styles": [ "tsConfig": "tsconfig.spec.json",
"src/styles.scss" "inlineStyleLanguage": "scss",
],
"scripts": [
],
"assets": [ "assets": [
"src/favicon.ico", "src/favicon.ico",
"src/assets" "src/assets"
], ],
"styles": [
"src/styles.scss"
],
"scripts": [],
"karmaConfig": "karma.conf.js" "karmaConfig": "karma.conf.js"
} }
}, },

View File

@ -9,6 +9,8 @@ export default defineConfig({
html: true, html: true,
json: false, json: false,
}, },
viewportHeight: 900,
viewportWidth: 1600,
chromeWebSecurity: false, chromeWebSecurity: false,
defaultCommandTimeout: 30000, defaultCommandTimeout: 30000,

View File

@ -221,13 +221,13 @@ const submitExcel = (callback?: any) => {
const rejectExcel = (callback?: any) => { const rejectExcel = (callback?: any) => {
cy.get('button', { timeout: longerCommandTimeout }) cy.get('button', { timeout: longerCommandTimeout })
.should('contain', 'Go to approvals screen') .should('contain', 'Approve')
.then((allButtons: any) => { .then((allButtons: any) => {
for (let approvalButton of allButtons) { for (let approvalButton of allButtons) {
if ( if (
approvalButton.innerText approvalButton.innerText
.toLowerCase() .toLowerCase()
.includes('go to approvals screen') .includes('approve')
) { ) {
approvalButton.click() approvalButton.click()
break break

View File

@ -405,13 +405,13 @@ const submitExcel = (callback?: any) => {
const rejectExcel = (callback?: any) => { const rejectExcel = (callback?: any) => {
cy.get('button', { timeout: longerCommandTimeout }) cy.get('button', { timeout: longerCommandTimeout })
.should('contain', 'Go to approvals screen') .should('contain', 'Approve')
.then((allButtons: any) => { .then((allButtons: any) => {
for (let approvalButton of allButtons) { for (let approvalButton of allButtons) {
if ( if (
approvalButton.innerText approvalButton.innerText
.toLowerCase() .toLowerCase()
.includes('go to approvals screen') .includes('approve')
) { ) {
approvalButton.click() approvalButton.click()
break break
@ -438,13 +438,13 @@ const rejectExcel = (callback?: any) => {
const acceptExcel = (callback?: any) => { const acceptExcel = (callback?: any) => {
cy.get('button', { timeout: longerCommandTimeout }) cy.get('button', { timeout: longerCommandTimeout })
.should('contain', 'Go to approvals screen') .should('contain', 'Approve')
.then((allButtons: any) => { .then((allButtons: any) => {
for (let approvalButton of allButtons) { for (let approvalButton of allButtons) {
if ( if (
approvalButton.innerText approvalButton.innerText
.toLowerCase() .toLowerCase()
.includes('go to approvals screen') .includes('approve')
) { ) {
approvalButton.click() approvalButton.click()
break break

View File

@ -159,20 +159,21 @@ context('filtering tests: ', function () {
}) })
}) })
it('7 | filter bestnum field BETWEEN', (done) => { // TODO: fix
openTableFromTree(libraryToOpenIncludes, 'mpe_x_test') // it('7 | filter bestnum field BETWEEN', (done) => {
// openTableFromTree(libraryToOpenIncludes, 'mpe_x_test')
openFilterPopup(() => { // openFilterPopup(() => {
setFilterWithValue('SOME_BESTNUM', '0-10', 'between', () => { // setFilterWithValue('SOME_BESTNUM', '0-10', 'between', () => {
checkInfoBarIncludes( // checkInfoBarIncludes(
`AND,AND,0,SOME_BESTNUM,BETWEEN,0 AND 10`, // `AND,AND,0,SOME_BESTNUM,BETWEEN,0 AND 10`,
(includes: boolean) => { // (includes: boolean) => {
if (includes) done() // if (includes) done()
} // }
) // )
}) // })
}) // })
}) // })
this.afterEach(() => { this.afterEach(() => {
// cy.visit(`${hostUrl}/SASLogon/logout`) // cy.visit(`${hostUrl}/SASLogon/logout`)

View File

@ -699,13 +699,13 @@ const submitTable = (callback?: any) => {
const approveTable = (callback?: any) => { const approveTable = (callback?: any) => {
cy.get('button', { timeout: longerCommandTimeout }) cy.get('button', { timeout: longerCommandTimeout })
.should('contain', 'Go to approvals screen') .should('contain', 'Approve')
.then((allButtons: any) => { .then((allButtons: any) => {
for (let approvalButton of allButtons) { for (let approvalButton of allButtons) {
if ( if (
approvalButton.innerText approvalButton.innerText
.toLowerCase() .toLowerCase()
.includes('go to approvals screen') .includes('approve')
) { ) {
approvalButton.click() approvalButton.click()
break break

View File

@ -125,13 +125,13 @@ const submitExcel = (callback?: any) => {
const rejectExcel = (callback?: any) => { const rejectExcel = (callback?: any) => {
cy.get('button', { timeout: longerCommandTimeout }) cy.get('button', { timeout: longerCommandTimeout })
.should('contain', 'Go to approvals screen') .should('contain', 'Approve')
.then((allButtons: any) => { .then((allButtons: any) => {
for (let approvalButton of allButtons) { for (let approvalButton of allButtons) {
if ( if (
approvalButton.innerText approvalButton.innerText
.toLowerCase() .toLowerCase()
.includes('go to approvals screen') .includes('approve')
) { ) {
approvalButton.click() approvalButton.click()
break break

View File

@ -221,14 +221,10 @@ const submitExcel = (callback?: any) => {
const rejectExcel = (callback?: any) => { const rejectExcel = (callback?: any) => {
cy.get('button', { timeout: longerCommandTimeout }) cy.get('button', { timeout: longerCommandTimeout })
.should('contain', 'Go to approvals screen') .should('contain', 'Approve')
.then((allButtons: any) => { .then((allButtons: any) => {
for (let approvalButton of allButtons) { for (let approvalButton of allButtons) {
if ( if (approvalButton.innerText.toLowerCase().includes('approve')) {
approvalButton.innerText
.toLowerCase()
.includes('go to approvals screen')
) {
approvalButton.click() approvalButton.click()
break break
} }

View File

@ -407,14 +407,10 @@ const submitExcel = (callback?: any) => {
const rejectExcel = (callback?: any) => { const rejectExcel = (callback?: any) => {
cy.get('button', { timeout: longerCommandTimeout }) cy.get('button', { timeout: longerCommandTimeout })
.should('contain', 'Go to approvals screen') .should('contain', 'Approve')
.then((allButtons: any) => { .then((allButtons: any) => {
for (let approvalButton of allButtons) { for (let approvalButton of allButtons) {
if ( if (approvalButton.innerText.toLowerCase().includes('approve')) {
approvalButton.innerText
.toLowerCase()
.includes('go to approvals screen')
) {
approvalButton.click() approvalButton.click()
break break
} }
@ -440,14 +436,10 @@ const rejectExcel = (callback?: any) => {
const acceptExcel = (callback?: any) => { const acceptExcel = (callback?: any) => {
cy.get('button', { timeout: longerCommandTimeout }) cy.get('button', { timeout: longerCommandTimeout })
.should('contain', 'Go to approvals screen') .should('contain', 'Approve')
.then((allButtons: any) => { .then((allButtons: any) => {
for (let approvalButton of allButtons) { for (let approvalButton of allButtons) {
if ( if (approvalButton.innerText.toLowerCase().includes('approve')) {
approvalButton.innerText
.toLowerCase()
.includes('go to approvals screen')
) {
approvalButton.click() approvalButton.click()
break break
} }

View File

@ -699,14 +699,10 @@ const submitTable = (callback?: any) => {
const approveTable = (callback?: any) => { const approveTable = (callback?: any) => {
cy.get('button', { timeout: longerCommandTimeout }) cy.get('button', { timeout: longerCommandTimeout })
.should('contain', 'Go to approvals screen') .should('contain', 'Approve')
.then((allButtons: any) => { .then((allButtons: any) => {
for (let approvalButton of allButtons) { for (let approvalButton of allButtons) {
if ( if (approvalButton.innerText.toLowerCase().includes('approve')) {
approvalButton.innerText
.toLowerCase()
.includes('go to approvals screen')
) {
approvalButton.click() approvalButton.click()
break break
} }

View File

@ -125,14 +125,10 @@ const submitExcel = (callback?: any) => {
const rejectExcel = (callback?: any) => { const rejectExcel = (callback?: any) => {
cy.get('button', { timeout: longerCommandTimeout }) cy.get('button', { timeout: longerCommandTimeout })
.should('contain', 'Go to approvals screen') .should('contain', 'Approve')
.then((allButtons: any) => { .then((allButtons: any) => {
for (let approvalButton of allButtons) { for (let approvalButton of allButtons) {
if ( if (approvalButton.innerText.toLowerCase().includes('approve')) {
approvalButton.innerText
.toLowerCase()
.includes('go to approvals screen')
) {
approvalButton.click() approvalButton.click()
break break
} }

View File

@ -42,4 +42,4 @@ module.exports = function (config) {
} }
}, },
}); });
}; };

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@13.1.0;handsontable@13.1.0;hyperformula@2.5.0;jackspeak@2.2.0;path-scurry@1.7.0' '@cds/city@1.1.0;@handsontable/angular@14.3.0;handsontable@14.3.0;hyperformula@2.7.0;jackspeak@2.2.0;path-scurry@1.7.0'
}, },
(error, json) => { (error, json) => {
if (error) { if (error) {

26193
client/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -18,8 +18,8 @@
"deploy_sasjs": "rsync -avhe ssh ./dist/* --delete root@${npm_config_account}.4gl.io:/var/www/html/dc/dev", "deploy_sasjs": "rsync -avhe ssh ./dist/* --delete root@${npm_config_account}.4gl.io:/var/www/html/dc/dev",
"viyabuild": "cd build; ./viyabuild.sh", "viyabuild": "cd build; ./viyabuild.sh",
"lint": "cd .. && npm run lint", "lint": "cd .. && npm run lint",
"test": "ng test", "test": "npx ng test",
"test:headless": "ng test --browsers ChromeHeadless", "test:headless": "npx ng test --no-watch --no-progress --browsers ChromeHeadlessCI",
"watch": "ng test watch=true", "watch": "ng test watch=true",
"pree2e": "webdriver-manager update", "pree2e": "webdriver-manager update",
"e2e": "protractor protractor.config.js", "e2e": "protractor protractor.config.js",
@ -35,24 +35,25 @@
}, },
"private": true, "private": true,
"dependencies": { "dependencies": {
"@angular/animations": "^16.1.2", "@angular/animations": "^17.3.3",
"@angular/cdk": "^15.2.0", "@angular/cdk": "^17.3.3",
"@angular/common": "^16.1.2", "@angular/common": "^17.3.3",
"@angular/compiler": "^16.1.2", "@angular/compiler": "^17.3.3",
"@angular/core": "^16.1.2", "@angular/core": "^17.3.3",
"@angular/forms": "^16.1.2", "@angular/forms": "^17.3.3",
"@angular/platform-browser": "^16.1.2", "@angular/platform-browser": "^17.3.3",
"@angular/platform-browser-dynamic": "^16.1.2", "@angular/platform-browser-dynamic": "^17.3.3",
"@angular/router": "^16.1.2", "@angular/router": "^17.3.3",
"@cds/core": "^6.4.2", "@cds/core": "^6.10.0",
"@clr/angular": "^13.17.0", "@clr/angular": "^17.0.1",
"@clr/icons": "^13.0.2", "@clr/icons": "^13.0.2",
"@clr/ui": "^13.17.0", "@clr/ui": "^17.0.1",
"@handsontable/angular": "^13.1.0", "@handsontable/angular": "^14.3.0",
"@sasjs/adapter": "4.10.2", "@sasjs/adapter": "4.10.2",
"@sasjs/utils": "^3.4.0", "@sasjs/utils": "^3.4.0",
"@sheet/crypto": "1.20211122.1", "@sheet/crypto": "file:libraries/sheet-crypto.tgz",
"@types/d3-graphviz": "^2.6.7", "@types/d3-graphviz": "^2.6.7",
"@sheet/crypto": "file:libraries/sheet-crypto.tgz",
"@types/text-encoding": "0.0.35", "@types/text-encoding": "0.0.35",
"base64-arraybuffer": "^0.2.0", "base64-arraybuffer": "^0.2.0",
"buffer": "^5.4.3", "buffer": "^5.4.3",
@ -60,7 +61,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": "^13.1.0", "handsontable": "^14.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",
@ -78,24 +79,26 @@
"stream-http": "3.2.0", "stream-http": "3.2.0",
"text-encoding": "^0.7.0", "text-encoding": "^0.7.0",
"tslib": "^2.3.0", "tslib": "^2.3.0",
"zone.js": "~0.13.0" "vm": "^0.1.0",
"webpack": "^5.91.0",
"zone.js": "~0.14.4"
}, },
"devDependencies": { "devDependencies": {
"@angular-devkit/build-angular": "^16.1.0", "@angular-devkit/build-angular": "^17.3.3",
"@angular-eslint/builder": "16.0.3", "@angular-eslint/builder": "17.3.0",
"@angular-eslint/eslint-plugin": "16.0.3", "@angular-eslint/eslint-plugin": "17.3.0",
"@angular-eslint/eslint-plugin-template": "16.0.3", "@angular-eslint/eslint-plugin-template": "17.3.0",
"@angular-eslint/schematics": "16.0.3", "@angular-eslint/schematics": "17.3.0",
"@angular-eslint/template-parser": "16.0.3", "@angular-eslint/template-parser": "17.3.0",
"@angular/cli": "^16.1.0", "@angular/cli": "^17.3.3",
"@angular/compiler-cli": "^16.1.2", "@angular/compiler-cli": "^17.3.3",
"@babel/plugin-proposal-private-methods": "^7.18.6", "@babel/plugin-proposal-private-methods": "^7.18.6",
"@compodoc/compodoc": "^1.1.21", "@compodoc/compodoc": "^1.1.21",
"@cypress/webpack-preprocessor": "^5.17.1", "@cypress/webpack-preprocessor": "^5.17.1",
"@types/core-js": "^2.5.5", "@types/core-js": "^2.5.5",
"@types/crypto-js": "^4.2.1", "@types/crypto-js": "^4.2.1",
"@types/es6-shim": "^0.31.39", "@types/es6-shim": "^0.31.39",
"@types/jasmine": "~3.6.0", "@types/jasmine": "~5.1.4",
"@types/lodash-es": "^4.17.3", "@types/lodash-es": "^4.17.3",
"@types/marked": "^4.3.0", "@types/marked": "^4.3.0",
"@types/node": "12.20.50", "@types/node": "12.20.50",
@ -109,12 +112,12 @@
"es6-shim": "^0.35.5", "es6-shim": "^0.35.5",
"eslint": "^8.33.0", "eslint": "^8.33.0",
"git-describe": "^4.0.4", "git-describe": "^4.0.4",
"jasmine-core": "~3.6.0", "jasmine-core": "~5.1.2",
"karma": "~6.3.0", "karma": "~6.4.3",
"karma-chrome-launcher": "~3.1.0", "karma-chrome-launcher": "~3.2.0",
"karma-coverage": "~2.1.0", "karma-coverage": "~2.2.1",
"karma-jasmine": "~4.0.0", "karma-jasmine": "~5.1.0",
"karma-jasmine-html-reporter": "~1.7.0", "karma-jasmine-html-reporter": "~2.1.0",
"license-checker": "25.0.1", "license-checker": "25.0.1",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"mochawesome": "^7.1.3", "mochawesome": "^7.1.3",
@ -123,9 +126,7 @@
"rimraf": "3.0.2", "rimraf": "3.0.2",
"ts-loader": "^9.2.8", "ts-loader": "^9.2.8",
"ts-node": "^3.3.0", "ts-node": "^3.3.0",
"typedoc": "^0.24.8", "typescript": "~5.4.4",
"typedoc-plugin-external-module-name": "^4.0.6",
"typescript": "~4.9.4",
"wait-on": "^6.0.1", "wait-on": "^6.0.1",
"watch": "^1.0.2" "watch": "^1.0.2"
} }

View File

@ -12,7 +12,7 @@
<div class="alert-items"> <div class="alert-items">
<div class="alert-item static"> <div class="alert-item static">
<div class="alert-icon-wrapper"> <div class="alert-icon-wrapper">
<clr-icon class="mt-2" shape="warning-standard"></clr-icon> <cds-icon class="alert-icon" shape="warning-standard"></cds-icon>
</div> </div>
<div class="alert-text"> <div class="alert-text">
Data Controller (FREE Tier) - to upgrade contact Data Controller (FREE Tier) - to upgrade contact
@ -30,7 +30,7 @@
<div class="alert-items"> <div class="alert-items">
<div class="alert-item static"> <div class="alert-item static">
<div class="alert-icon-wrapper"> <div class="alert-icon-wrapper">
<clr-icon class="mt-2" shape="warning-standard"></clr-icon> <cds-icon class="alert-icon" shape="warning-standard"></cds-icon>
</div> </div>
<div class="alert-text"> <div class="alert-text">
Data Controller (FREE Tier) - Problem with licence Data Controller (FREE Tier) - Problem with licence
@ -55,7 +55,7 @@
<div class="alert-items"> <div class="alert-items">
<div class="alert-item static"> <div class="alert-item static">
<div class="alert-icon-wrapper"> <div class="alert-icon-wrapper">
<clr-icon class="mt-2" shape="warning-standard"></clr-icon> <cds-icon class="alert-icon" shape="warning-standard"></cds-icon>
</div> </div>
<div class="alert-text"> <div class="alert-text">
@ -85,7 +85,7 @@
<div class="alert-items"> <div class="alert-items">
<div class="alert-item static"> <div class="alert-item static">
<div class="alert-icon-wrapper"> <div class="alert-icon-wrapper">
<clr-icon class="mt-2" shape="warning-standard"></clr-icon> <cds-icon class="alert-icon" shape="warning-standard"></cds-icon>
</div> </div>
<div class="alert-text"> <div class="alert-text">
@ -204,14 +204,7 @@
</div> </div>
</ng-container> </ng-container>
<div class="header-actions"> <app-header-actions></app-header-actions>
<div class="nav-text">
<app-loading-indicator></app-loading-indicator>
</div>
<div class="dropdown">
<app-user-nav-dropdown></app-user-nav-dropdown>
</div>
</div>
</header> </header>
<nav <nav
*ngIf=" *ngIf="

View File

@ -1,3 +1,5 @@
@import '../colors.scss';
// Copyright (c) 2016 VMware, Inc. All Rights Reserved. // Copyright (c) 2016 VMware, Inc. All Rights Reserved.
// 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.
@ -6,7 +8,7 @@ app-requests-modal {
} }
header.app-header { header.app-header {
background: #314351 !important; background: $headerBackground !important;
color: #fff; color: #fff;
} }
@ -42,7 +44,7 @@ header.app-header {
align-items: center; align-items: center;
padding: 30px; padding: 30px;
z-index: 110; z-index: 110;
background: #314351; background: $headerBackground;
.expired-notice { .expired-notice {
color: #e0e0e0; color: #e0e0e0;
@ -91,33 +93,12 @@ header {
} }
} }
.nav .nav-link:hover {
.nav-link {
color: #fafafa;
opacity: .9;
line-height: 1.45rem;
}
.nav .nav-link:hover {
box-shadow: inset 0 -3px 0 transparent;
transition: box-shadow .2s ease-in;
}
.nav
.nav-link:hover {
color: #fafafa; color: #fafafa;
opacity: 1;
} }
.nav .nav-link.active { .nav-link.active {
background: #61717D; background: #61717D;
opacity: 1;
box-shadow: inset 0 -3px transparent;
// padding: 0 1rem 0 1rem;
}
.nav .nav-item {
margin-right: 1rem;
} }
} }
@ -127,15 +108,6 @@ header {
font-size: 12px; font-size: 12px;
} }
.btn.btn-success {
border-color: #62a420;
background-color: #16a57a!important;
color: #fff;
}
.btn.btn-success:hover {
background-color: #2add39;
color: #fff;
}
.toggle-switch input[type=checkbox]:checked+label:before { .toggle-switch input[type=checkbox]:checked+label:before {
border-color: #61717D; border-color: #61717D;
@ -163,59 +135,44 @@ header {
color: #fff; color: #fff;
} }
@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;
}
}
::ng-deep { ::ng-deep {
.htInvalid { .htInvalid {
background: black!important; background: black!important;
} }
@media screen and (max-width:480px) { @media screen and (max-width:480px) {
h2 { h2 {
font-size: .7rem!important; font-size: .7rem!important;
}
h3 {
font-size: .7rem;
}
} }
h3 {
font-size: .7rem;
}
}
.nav-link { .nav-link {
padding: 0rem 1rem 0rem 1rem; padding: 0rem 1rem 0rem 1rem;
} }
.btn-primary .btn, .btn.btn-primary { body[cds-theme="light"] {
border-color: #314351; .btn-primary .btn, .btn.btn-primary {
background-color: #314351; border-color: $headerBackground;
color: #fff; 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 { .btn {
cursor: pointer; cursor: pointer;
display: inline-block; display: inline-block;
@ -236,36 +193,32 @@ header {
font-weight: 500; font-weight: 500;
height: 1.5rem; height: 1.5rem;
padding: 0 .5rem; padding: 0 .5rem;
border-color: #314351;
background-color: transparent;
color: #314351;
}
.btn.btn-outline {
border-color: #314351;
background-color: transparent;
color: #314351;
} }
.btn.btn-outline:hover { .btn.btn-outline:hover {
border-color: #314351; border-color: $headerBackground;
background-color: #495A67; background-color: #495A67;
color: #fff; color: #fff;
} }
.btn.btn-success-outline:hover {
background-color: #5ea71f;
color: #fff7f7;
border-color: #9a9696;
}
// .btn.btn-success-outline {
// border-color: #266900;
// background-color: transparent;
// color: #318700;
// }
// .wtSpreader {
// } 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 { .htMobileEditorContainer .inputs textarea {
font-size: 13pt; font-size: 13pt;
@ -298,65 +251,68 @@ header {
width: 350px; width: 350px;
} }
.handsontable {
background-color: #ffffff;
// border: 1px solid #ccc;
border-radius: 3px;
}
.handsontable th {
background-color: #fafafa;
}
/* Left and right */ /* Left and right */
.ht_clone_left th {
border-right: 1px solid #ccc;
border-left: 1px solid #ccc;
}
/* Column headers */ /* Column headers */
.ht_clone_top th {
border-top: 1px solid #ccc; body[cds-theme="light"] {
border-right: 1px solid #ccc; .wtBorder {
border-bottom: 1px solid #ccc; background-color: #495A67!important;
}
.ht_master tr:nth-of-type(odd) > td {
filter: brightness(0.95);
}
} }
.ht_clone_top_left_corner th { $darkBorderColor: #697c85;
border-right: 1px solid #ccc;
}
.ht_master tr:nth-of-type(odd) > td { body[cds-theme="dark"] {
background-color: #f3f3f3; .ht_master tr:nth-of-type(odd) > td {
border: 1px solid rgb(197, 197, 197); filter: brightness(1.2);
border-bottom: 1px solid rgb(236, 235, 235); }
// padding: 1px 1px;
}
.ht_master tr:nth-of-type(even) > td { .ht_master:not(.emptyColumns) ~ .handsontable tbody tr th, .ht_master:not(.emptyColumns) ~ .handsontable:not(.ht_clone_top) thead tr th:first-child {
background-color: white; background-color: #2d4048;
border: 1px solid rgb(197, 197, 197); border-color: $darkBorderColor;
border-bottom: 1px solid rgb(236, 235, 235); }
// padding: 1px 1px;
}
.wtBorder { .handsontable td {
background-color: #495A67!important; // 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 { .handsontable .handsontable.ht_clone_top .wtHider {
padding: 0 0 0px 0!important; padding: 0 0 0px 0!important;
margin: 0px; margin: 0px;
border-bottom: 3px solid #d6d3d3; border-bottom: 3px solid #d6d3d3;
}
.content-container {
background: #F5F6FF;
} }
.card { body[cds-theme="light"] {
box-shadow: 0 0.125rem 0 0 #d7d7d7; .content-container {
border-radius: .0rem; // background: red;
border: 1px solid transparent; background: #F5F6FF;
// min-height: calc(100vh - 150px); }
} }
.datagrid-compact, .datagrid-history{ .datagrid-compact, .datagrid-history{
@ -364,8 +320,6 @@ header {
border-collapse: separate; border-collapse: separate;
border: 1px solid transparent; border: 1px solid transparent;
border-radius: .125rem; border-radius: .125rem;
background-color: #fff;
color: #565656;
margin: 0; margin: 0;
margin-top: 1rem; margin-top: 1rem;
max-width: 100%; max-width: 100%;
@ -387,8 +341,8 @@ header {
} }
.datagrid-footer { .datagrid-footer {
position: absolute; position: absolute;
right: 15px; right: 30px;
top: 2px; top: 1px;
} }
.datagrid .datagrid-head { .datagrid .datagrid-head {
background-color: #fff; background-color: #fff;
@ -408,7 +362,6 @@ header {
-webkit-box-direction: normal; -webkit-box-direction: normal;
-ms-flex-direction: column; -ms-flex-direction: column;
flex-direction: column; flex-direction: column;
background: #f5f6ff;
padding: .5rem 0; padding: .5rem 0;
border: 1px solid #ccc; border: 1px solid #ccc;
box-shadow: 0 1px 0.125rem hsla(0,0%,45%,.25); box-shadow: 0 1px 0.125rem hsla(0,0%,45%,.25);
@ -423,8 +376,6 @@ header {
border-collapse: separate; border-collapse: separate;
border: 1px solid transparent; border: 1px solid transparent;
border-radius: 0px; border-radius: 0px;
background-color: #fff;
color: #565656;
margin: 0; margin: 0;
margin-top: 1rem; margin-top: 1rem;
max-width: 100%; max-width: 100%;
@ -435,7 +386,6 @@ header {
font-size: .45833rem; font-size: .45833rem;
font-weight: 600; font-weight: 600;
letter-spacing: .03em; letter-spacing: .03em;
background-color: #fff;
vertical-align: bottom; vertical-align: bottom;
border-bottom: 1px solid #ccc; border-bottom: 1px solid #ccc;
text-transform: uppercase; text-transform: uppercase;
@ -458,4 +408,34 @@ header {
max-width: 400px; max-width: 400px;
width: 100%; 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

@ -13,6 +13,15 @@ import { InfoModal } from './models/InfoModal'
import { DcAdapterSettings } from './models/DcAdapterSettings' import { DcAdapterSettings } from './models/DcAdapterSettings'
import { AppStoreService } from './services/app-store.service' import { AppStoreService } from './services/app-store.service'
import { LicenceService } from './services/licence.service' import { LicenceService } from './services/licence.service'
import '@cds/core/icon/register.js'
import {
ClarityIcons,
exclamationTriangleIcon,
moonIcon,
sunIcon
} from '@cds/core/icon'
ClarityIcons.addIcons(moonIcon, sunIcon, exclamationTriangleIcon)
@Component({ @Component({
selector: 'my-app', selector: 'my-app',

View File

@ -20,10 +20,10 @@ import { UsernavRouteComponent } from './routes/usernav-route/usernav-route.comp
import { AppService } from './services/app.service' import { AppService } from './services/app.service'
import { InfoModalComponent } from './shared/abort-modal/info-modal.component' import { InfoModalComponent } from './shared/abort-modal/info-modal.component'
import { RequestsModalComponent } from './shared/requests-modal/requests-modal.component' import { RequestsModalComponent } from './shared/requests-modal/requests-modal.component'
import { HomeModule } from './home/home.module'
import { DirectivesModule } from './directives/directives.module' import { DirectivesModule } from './directives/directives.module'
import { ViyaApiExplorerComponent } from './viya-api-explorer/viya-api-explorer.component' import { ViyaApiExplorerComponent } from './viya-api-explorer/viya-api-explorer.component'
import { NgxJsonViewerModule } from 'ngx-json-viewer' import { NgxJsonViewerModule } from 'ngx-json-viewer'
import { AppSettingsService } from './services/app-settings.service'
@NgModule({ @NgModule({
declarations: [ declarations: [
@ -46,12 +46,11 @@ import { NgxJsonViewerModule } from 'ngx-json-viewer'
SharedModule, SharedModule,
ClarityModule, ClarityModule,
AppSharedModule, AppSharedModule,
HomeModule,
PipesModule, PipesModule,
DirectivesModule, DirectivesModule,
NgxJsonViewerModule NgxJsonViewerModule
], ],
providers: [AppService, SasStoreService, LicensingGuard], providers: [AppService, SasStoreService, LicensingGuard, AppSettingsService],
bootstrap: [AppComponent] bootstrap: [AppComponent]
}) })
export class AppModule {} export class AppModule {}

View File

@ -45,7 +45,10 @@ export const ROUTES: Routes = [
path: 'licensing', path: 'licensing',
loadChildren: () => LicensingModule loadChildren: () => LicensingModule
}, },
{ path: 'home', loadChildren: () => HomeModule }, {
path: 'home',
loadChildren: () => HomeModule
},
{ {
/** /**
* Load editor module with subroutes * Load editor module with subroutes

View File

@ -4,20 +4,23 @@ import { NgVarDirective } from './ng-var.directive'
import { DragNdropDirective } from './drag-ndrop.directive' import { DragNdropDirective } from './drag-ndrop.directive'
import { FileDropDirective } from './file-drop.directive' import { FileDropDirective } from './file-drop.directive'
import { FileSelectDirective } from './file-select.directive' import { FileSelectDirective } from './file-select.directive'
import { StealFocusDirective } from './steal-focus.directive'
@NgModule({ @NgModule({
declarations: [ declarations: [
NgVarDirective, NgVarDirective,
DragNdropDirective, DragNdropDirective,
FileDropDirective, FileDropDirective,
FileSelectDirective FileSelectDirective,
StealFocusDirective
], ],
imports: [CommonModule], imports: [CommonModule],
exports: [ exports: [
NgVarDirective, NgVarDirective,
DragNdropDirective, DragNdropDirective,
FileDropDirective, FileDropDirective,
FileSelectDirective FileSelectDirective,
StealFocusDirective
] ]
}) })
export class DirectivesModule {} export class DirectivesModule {}

View File

@ -0,0 +1,17 @@
import { Directive, HostListener } from '@angular/core'
@Directive({
selector: '[appStealFocus]'
})
export class StealFocusDirective {
constructor() {}
/**
* For some reason newest version of Clarity v17.0.1 is stealing focus when
* clicking on the input inside of the clr-tree-view
* This is workaround
*/
@HostListener('click', ['$event']) onClick(event: any) {
event.target.focus()
}
}

View File

@ -112,7 +112,7 @@
<div <div
*ngIf=" *ngIf="
['autocomplete'].includes( ['autocomplete', 'autocomplete.custom'].includes(
$any(currentRecordValidator?.getRule(col.key)?.editor) $any(currentRecordValidator?.getRule(col.key)?.editor)
) )
" "
@ -163,7 +163,7 @@
<div <div
*ngIf=" *ngIf="
['autocomplete'].includes( ['autocomplete', 'autocomplete.custom'].includes(
$any(currentRecordValidator?.getRule(col.key)?.editor) $any(currentRecordValidator?.getRule(col.key)?.editor)
) )
" "
@ -277,7 +277,7 @@
<div> <div>
<button <button
type="button" type="button"
class="btn btn-outline focusable" class="btn btn-outline focusable mr-5i"
(click)="currentRecord!.noLinkOption = false; closeRecordEdit()" (click)="currentRecord!.noLinkOption = false; closeRecordEdit()"
> >
Cancel Cancel

View File

@ -38,7 +38,6 @@
app-soft-select { app-soft-select {
display: block; display: block;
width: 224px; width: 224px;
background: #fff;
border: 1px solid #999; border: 1px solid #999;
color: #000; color: #000;
padding: calc(.25rem + 2px) .5rem; padding: calc(.25rem + 2px) .5rem;
@ -49,7 +48,6 @@
input { input {
width: 100%; width: 100%;
border: 0; border: 0;
background-color: #fff;
&:focus { &:focus {
background: none; background: none;
@ -132,7 +130,6 @@
clr-input-container { clr-input-container {
width: 224px; width: 224px;
background: #fff;
border: 1px solid #999; border: 1px solid #999;
color: #000; color: #000;
padding: calc(.25rem + 2px) .5rem; padding: calc(.25rem + 2px) .5rem;

View File

@ -164,19 +164,28 @@
<div <div
class="card-header clr-row buttonBar headerBar clr-flex-md-row clr-justify-content-center clr-justify-content-lg-end" class="card-header clr-row buttonBar headerBar clr-flex-md-row clr-justify-content-center clr-justify-content-lg-end"
> >
<div *ngIf="tableTrue" class="clr-col-12 clr-col-lg-4 backBtn"> <div
<span class="btn btn-sm" [routerLink]="['/home']"> *ngIf="tableTrue"
<clr-icon shape="caret" dir="left" size="20"></clr-icon>Back to class="clr-col-12 clr-col-md-3 clr-col-lg-4 backBtn"
table selection >
<span
class="btn icon-collapse btn-sm btn-icon btn-dimmed"
[routerLink]="['/home']"
>
<clr-icon shape="caret" dir="left" size="20"></clr-icon>
<span class="text">Back to table selection</span>
</span> </span>
<span (click)="viewboxManager()" class="btn btn-sm viewbox-open"> <span
(click)="viewboxManager()"
class="btn icon-collapse btn-sm btn-icon btn-dimmed viewbox-open"
>
<clr-icon shape="view-cards" size="20"></clr-icon> <clr-icon shape="view-cards" size="20"></clr-icon>
Viewboxes <span class="text">Viewboxes</span>
</span> </span>
</div> </div>
<div <div
class="clr-col-12 clr-col-lg-4 d-flex flex-column align-items-center" class="clr-col-12 clr-col-md-5 clr-col-lg-4 d-flex flex-column align-items-center"
[class.clr-col-lg-12]="!tableTrue" [class.clr-col-lg-12]="!tableTrue"
> >
<h4 <h4
@ -203,18 +212,21 @@
<span clrTooltipTrigger> <span clrTooltipTrigger>
{{ libdsParsed.libName }}.<a {{ libdsParsed.libName }}.<a
class="mr-10" class="mr-10 view-table"
[routerLink]="'/view/data/' + libds!" [routerLink]="'/view/data/' + libds!"
>{{ libdsParsed.tableName.replace('-FC', '') }}</a >{{ libdsParsed.tableName.replace('-FC', '') }}</a
> >
</span> </span>
<clr-tooltip-content
clrPosition="bottom-left" <ng-container *ngIf="this.dsNote && this.dsNote.length > 0">
clrSize="lg" <clr-tooltip-content
*clrIfOpen clrPosition="bottom-left"
> clrSize="lg"
{{ this.dsNote }} *clrIfOpen
</clr-tooltip-content> >
{{ this.dsNote }}
</clr-tooltip-content>
</ng-container>
</clr-tooltip> </clr-tooltip>
<ng-container *ngIf="dataSource"> <ng-container *ngIf="dataSource">
@ -228,34 +240,37 @@
</ng-container> </ng-container>
</h4> </h4>
</div> </div>
<div *ngIf="tableTrue" class="clr-col-12 clr-col-lg-4 btnCtrl"> <div
*ngIf="tableTrue"
class="clr-col-12 clr-col-md-4 clr-col-lg-4 btnCtrl"
>
<ng-container *ngIf="hotTable.readOnly && !uploadPreview"> <ng-container *ngIf="hotTable.readOnly && !uploadPreview">
<button <button
type="button" type="button"
class="btnView btn btn-sm btn-icon btn-block" class="btnView btn icon-collapse btn-sm btn-icon btn-block btn-dimmed"
(click)="openQb()" (click)="openQb()"
> >
<clr-icon shape="filter"></clr-icon> <clr-icon shape="filter"></clr-icon>
<span>Filter</span> <span class="text">Filter</span>
</button> </button>
<button <button
type="button" type="button"
class="btn btn-sm btn-primary btn-block" class="btn icon-collapse btn-sm btn-primary btn-block"
(click)="editTable()" (click)="editTable()"
> >
<clr-icon shape="note"></clr-icon> <clr-icon shape="note"></clr-icon>
<span>Edit</span> <span class="text">Edit</span>
</button> </button>
<button <button
*ngIf="!columnLevelSecurityFlag" *ngIf="!columnLevelSecurityFlag"
(click)="onShowUploadModal()" (click)="onShowUploadModal()"
type="button" type="button"
class="btn btn-sm btn-success btn-block mr-0" class="btn icon-collapse btn-sm btn-success btn-block mr-0"
> >
<clr-icon shape="upload"></clr-icon> <clr-icon shape="upload"></clr-icon>
<span>Upload</span> <span class="text">Upload</span>
</button> </button>
</ng-container> </ng-container>
@ -359,8 +374,8 @@
<ng-container *ngIf="!getdataError"> <ng-container *ngIf="!getdataError">
<span class="spinner"> Loading... </span> <span class="spinner"> Loading... </span>
<div> <div class="mt-10">
<h3>Loading table</h3> <p cds-text="section">Loading table</p>
</div> </div>
</ng-container> </ng-container>
@ -369,8 +384,8 @@
<clr-icon shape="error-standard" class="error-icon"></clr-icon> <clr-icon shape="error-standard" class="error-icon"></clr-icon>
</span> </span>
<div> <div class="mt-10">
<h3>Loading table error</h3> <p cds-text="section">Loading table error</p>
</div> </div>
</ng-container> </ng-container>
</div> </div>
@ -395,6 +410,7 @@
hotId="hotInstance" hotId="hotInstance"
id="hotTable" id="hotTable"
class="edit-hot" class="edit-hot"
className="htDark"
[class.hidden]="hotTable.hidden" [class.hidden]="hotTable.hidden"
[licenseKey]="hotTable.licenseKey" [licenseKey]="hotTable.licenseKey"
> >
@ -483,11 +499,15 @@
support&#64;datacontroller.io</span support&#64;datacontroller.io</span
> >
<div *ngIf="tableTrue" class="clr-offset-md-2 clr-col-md-8"> <div *ngIf="tableTrue" class="clr-offset-md-2 clr-col-md-8">
<div class="form-group"> <div class="text-area-full-width">
<label for="formFields_8">Message</label> <label for="formFields_8" class="mb-5 d-block"
>Message</label
>
<textarea <textarea
clrTextarea
[(ngModel)]="message" [(ngModel)]="message"
[disabled]="!validationDone" [disabled]="!validationDone"
tabindex="0"
[value]=" [value]="
!validationDone !validationDone
? 'Please wait while we validate ' + ? 'Please wait while we validate ' +
@ -495,10 +515,9 @@
' cells.' ' cells.'
: '' : ''
" "
class="w-100" class="submit-reason"
type="text" type="text"
id="formFields_8" id="formFields_8"
rows="5"
></textarea> ></textarea>
</div> </div>
<!-- TODO:approvers list --> <!-- TODO:approvers list -->
@ -517,6 +536,7 @@
[disabled]="!validationDone" [disabled]="!validationDone"
type="submit" type="submit"
class="btn btn-sm btn-success-outline m-0" class="btn btn-sm btn-success-outline m-0"
tabindex="0"
(click)="saveTable(hotTable.data)" (click)="saveTable(hotTable.data)"
> >
Submit Submit
@ -525,6 +545,7 @@
id="cancelSubmitBtn" id="cancelSubmitBtn"
type="button" type="button"
class="btn btn-sm btn-outline" class="btn btn-sm btn-outline"
tabindex="0"
(click)="cancelSubmit(); submit = false; validationDone = 0" (click)="cancelSubmit(); submit = false; validationDone = 0"
> >
Cancel Cancel
@ -843,6 +864,12 @@
</div> </div>
</clr-modal> </clr-modal>
<app-dataset-info [(open)]="datasetInfo" [dsmeta]="dsmeta"></app-dataset-info> <app-dataset-info
[(open)]="datasetInfo"
[dsmeta]="dsmeta"
[versions]="versions"
(rowClicked)="datasetInfoModalRowClicked($event)"
>
</app-dataset-info>
<app-viewboxes [(viewboxModal)]="viewboxes"></app-viewboxes> <app-viewboxes [(viewboxModal)]="viewboxes"></app-viewboxes>

View File

@ -21,12 +21,12 @@ hot-table {
.handsontable tbody th.ht__highlight, .handsontable thead th.ht__highlight { .handsontable tbody th.ht__highlight, .handsontable thead th.ht__highlight {
&.primaryKeyHeaderStyle { &.primaryKeyHeaderStyle {
background: #306b00b0; background-color: #306b00b0 !important;
} }
} }
.primaryKeyHeaderStyle { .primaryKeyHeaderStyle {
background: #306b006e; background-color: #306b006e !important;
} }
th.readonlyCell { th.readonlyCell {
@ -41,6 +41,12 @@ hot-table {
} }
} }
.submit-reason {
min-height: 120px;
max-height: 120px;
height: 120px;
}
.infoBar { .infoBar {
margin-top:14px; margin-top:14px;
background: #495967; background: #495967;
@ -80,8 +86,7 @@ hot-table {
-webkit-box-align: center; -webkit-box-align: center;
-ms-flex-align: center; -ms-flex-align: center;
align-items: center; align-items: center;
background: #ffffff; background: var(--clr-vertical-nav-bg-color);
background: #f5f6fe;
} }
.error-icon { .error-icon {
@ -214,6 +219,20 @@ hot-table {
width: 150px; 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 // FIXME
// Let's leave it here for a reference if there // Let's leave it here for a reference if there
// is an issue with viewboxes/filter modal overlaying // is an issue with viewboxes/filter modal overlaying

View File

@ -38,7 +38,8 @@ import { HotTableInterface } from '../models/HotTable.interface'
import { import {
$DataFormats, $DataFormats,
DSMeta, DSMeta,
EditorsGetDataServiceResponse EditorsGetDataServiceResponse,
Version
} from '../models/sas/editors-getdata.model' } from '../models/sas/editors-getdata.model'
import { DataFormat } from '../models/sas/common/DateFormat' import { DataFormat } from '../models/sas/common/DateFormat'
import SheetInfo from '../models/SheetInfo' import SheetInfo from '../models/SheetInfo'
@ -121,6 +122,7 @@ export class EditorComponent implements OnInit, AfterViewInit {
datasetInfo: boolean = false datasetInfo: boolean = false
dsmeta: DSMeta[] = [] dsmeta: DSMeta[] = []
versions: Version[] = []
dsNote = '' dsNote = ''
viewboxes: boolean = false viewboxes: boolean = false
@ -980,15 +982,18 @@ export class EditorComponent implements OnInit, AfterViewInit {
// Apply licence rows limitation if exists, it is only affecting data // Apply licence rows limitation if exists, it is only affecting data
// which will be send to SAS // which will be send to SAS
const strippedCsvArrayData = csvArrayData.slice(0, this.licenceState.value.submit_rows_limit) const strippedCsvArrayData = csvArrayData.slice(
0,
this.licenceState.value.submit_rows_limit
)
// To submit to sas service, we need clean version of CSV of file // To submit to sas service, we need clean version of CSV of file
// attached. XLSX will do the parsing and heavy lifting // attached. XLSX will do the parsing and heavy lifting
// First we create worksheet of json (data we extracted) // First we create worksheet of json (data we extracted)
let ws = XLSX.utils.json_to_sheet(strippedCsvArrayData, { let ws = XLSX.utils.json_to_sheet(strippedCsvArrayData, {
skipHeader: true skipHeader: true
}); })
// create CSV to be uploaded from worksheet // create CSV to be uploaded from worksheet
let csvContentClean = XLSX.utils.sheet_to_csv(ws); let csvContentClean = XLSX.utils.sheet_to_csv(ws)
// Prepend headers // Prepend headers
csvContentClean = csvArrayHeaders.join(',') + '\n' + csvContentClean csvContentClean = csvArrayHeaders.join(',') + '\n' + csvContentClean
@ -1953,13 +1958,13 @@ export class EditorComponent implements OnInit, AfterViewInit {
if (entry.values.length > 0) { if (entry.values.length > 0) {
hot.setCellMeta(entry.row, entry.col, 'renderer', 'autocomplete') hot.setCellMeta(entry.row, entry.col, 'renderer', 'autocomplete')
hot.setCellMeta(entry.row, entry.col, 'editor', 'autocomplete') hot.setCellMeta(entry.row, entry.col, 'editor', 'autocomplete.custom')
hot.setCellMeta(entry.row, entry.col, 'strict', entry.strict) hot.setCellMeta(entry.row, entry.col, 'strict', entry.strict)
hot.setCellMeta(entry.row, entry.col, 'filter', false) hot.setCellMeta(entry.row, entry.col, 'filter', false)
this.currentEditRecordValidator?.updateRule(entry.col, { this.currentEditRecordValidator?.updateRule(entry.col, {
renderer: 'autocomplete', renderer: 'autocomplete',
editor: 'autocomplete', editor: 'autocomplete.custom',
strict: entry.strict, strict: entry.strict,
filter: false filter: false
}) })
@ -2039,7 +2044,7 @@ export class EditorComponent implements OnInit, AfterViewInit {
(el: DynamicExtendedCellValidation) => (el: DynamicExtendedCellValidation) =>
el.DISPLAY_TYPE === 'C' ? el.RAW_VALUE_CHAR : el.RAW_VALUE_NUM el.DISPLAY_TYPE === 'C' ? el.RAW_VALUE_CHAR : el.RAW_VALUE_NUM
) )
const cellCol = hot.propToCol(cell) const cellCol = hot.propToCol(cell) as number
const dynamicValidationEl = const dynamicValidationEl =
this.disabledBasicDynamicCellValidationMap.find( this.disabledBasicDynamicCellValidationMap.find(
(x) => x.row === row && x.col === cellCol (x) => x.row === row && x.col === cellCol
@ -2054,13 +2059,13 @@ export class EditorComponent implements OnInit, AfterViewInit {
} }
hot.setCellMeta(row, cellCol, 'renderer', 'autocomplete') hot.setCellMeta(row, cellCol, 'renderer', 'autocomplete')
hot.setCellMeta(row, cellCol, 'editor', 'autocomplete') hot.setCellMeta(row, cellCol, 'editor', 'autocomplete.custom')
hot.setCellMeta(row, cellCol, 'strict', cellValidationEntry.strict) hot.setCellMeta(row, cellCol, 'strict', cellValidationEntry.strict)
hot.setCellMeta(row, cellCol, 'filter', false) hot.setCellMeta(row, cellCol, 'filter', false)
this.currentEditRecordValidator?.updateRule(cellCol, { this.currentEditRecordValidator?.updateRule(cellCol, {
renderer: 'autocomplete', renderer: 'autocomplete',
editor: 'autocomplete', editor: 'autocomplete.custom',
strict: cellValidationEntry.strict, strict: cellValidationEntry.strict,
filter: false filter: false
}) })
@ -2258,8 +2263,8 @@ export class EditorComponent implements OnInit, AfterViewInit {
setTimeout(() => { setTimeout(() => {
let txt: any = document.getElementById('formFields_8') let txt: any = document.getElementById('formFields_8')
txt.focus() if (txt) txt.focus()
}) }, 200)
}) })
// let cnt = 0; // let cnt = 0;
@ -2703,13 +2708,13 @@ export class EditorComponent implements OnInit, AfterViewInit {
const strict = this.cellValidationSource[validationSourceIndex].strict const strict = this.cellValidationSource[validationSourceIndex].strict
hot.setCellMeta(row, column, 'renderer', 'autocomplete') hot.setCellMeta(row, column, 'renderer', 'autocomplete')
hot.setCellMeta(row, column, 'editor', 'autocomplete') hot.setCellMeta(row, column, 'editor', 'autocomplete.custom')
hot.setCellMeta(row, column, 'strict', strict) hot.setCellMeta(row, column, 'strict', strict)
hot.setCellMeta(row, column, 'filter', false) hot.setCellMeta(row, column, 'filter', false)
this.currentEditRecordValidator?.updateRule(column, { this.currentEditRecordValidator?.updateRule(column, {
renderer: 'autocomplete', renderer: 'autocomplete',
editor: 'autocomplete', editor: 'autocomplete.custom',
strict: strict, strict: strict,
filter: false filter: false
}) })
@ -2761,6 +2766,7 @@ export class EditorComponent implements OnInit, AfterViewInit {
}) })
hot.setCellMeta(row, column, 'renderer', spinnerRenderer) hot.setCellMeta(row, column, 'renderer', spinnerRenderer)
this.currentEditRecordLoadings.push(column) this.currentEditRecordLoadings.push(column)
hot.render() hot.render()
@ -2931,6 +2937,16 @@ export class EditorComponent implements OnInit, AfterViewInit {
} }
} }
datasetInfoModalRowClicked(value: Version | DSMeta) {
if ((<Version>value).LOAD_REF !== undefined) {
// Type is Version
const row = value as Version
const url = `/stage/${row.LOAD_REF}`
this.router.navigate([url])
}
}
viewboxManager() { viewboxManager() {
this.viewboxes = true this.viewboxes = true
} }
@ -2943,6 +2959,37 @@ export class EditorComponent implements OnInit, AfterViewInit {
) )
} }
/**
* Function checks if selected hot cell is solo cell selected
* and if it is, set the `filter` property based on filter param.
*
* @param filter
*/
private setCellFilter(filter: boolean) {
const hotSelected = this.hotInstance.getSelected()
const selection = hotSelected ? hotSelected[0] : hotSelected
// When we open a dropdown we want filter disabled so value in cell
// don't filter out items, since we want to see them all.
// But when we start typing we want to be able to start filtering values
// again
if (selection) {
const startRow = selection[0]
const endRow = selection[2]
const startCell = selection[1]
const endCell = selection[3]
if (startRow === endRow && startCell === endCell) {
const cellMeta = this.hotInstance.getCellMeta(startRow, startCell)
// If filter is not already set at the value in the param, set it
if (cellMeta && cellMeta.filter === !filter) {
this.hotInstance.setCellMeta(startRow, startCell, 'filter', filter)
}
}
}
}
async ngOnInit() { async ngOnInit() {
this.licenceService.hot_license_key.subscribe( this.licenceService.hot_license_key.subscribe(
(hot_license_key: string | undefined) => { (hot_license_key: string | undefined) => {
@ -3010,6 +3057,7 @@ export class EditorComponent implements OnInit, AfterViewInit {
this.cols = response.data.cols this.cols = response.data.cols
this.dsmeta = response.data.dsmeta this.dsmeta = response.data.dsmeta
this.versions = response.data.versions || []
const notes = this.dsmeta.find((item) => item.NAME === 'NOTES') const notes = this.dsmeta.find((item) => item.NAME === 'NOTES')
const longDesc = this.dsmeta.find((item) => item.NAME === 'DD_LONGDESC') const longDesc = this.dsmeta.find((item) => item.NAME === 'DD_LONGDESC')
@ -3210,6 +3258,9 @@ export class EditorComponent implements OnInit, AfterViewInit {
if (col === 0) { if (col === 0) {
th.classList.add('firstColumnHeaderStyle') th.classList.add('firstColumnHeaderStyle')
} }
// Dark mode
th.classList.add('darkTH')
}, },
afterGetCellMeta: ( afterGetCellMeta: (
row: number, row: number,
@ -3298,28 +3349,16 @@ export class EditorComponent implements OnInit, AfterViewInit {
} }
) )
hot.addHook('beforeKeyDown', (e: any) => { hot.addHook('afterBeginEditing', () => {
const hotSelected = this.hotInstance.getSelected()
const selection = hotSelected ? hotSelected[0] : hotSelected
// When we open a dropdown we want filter disabled so value in cell // When we open a dropdown we want filter disabled so value in cell
// don't filter out items, since we want to see them all. // don't filter out items, since we want to see them all.
this.setCellFilter(false)
})
hot.addHook('beforeKeyDown', () => {
// When we start typing, we are enabling the filter since we want to find // When we start typing, we are enabling the filter since we want to find
// values faster. // values faster.
if (selection) { this.setCellFilter(true)
const startRow = selection[0]
const endRow = selection[2]
const startCell = selection[1]
const endCell = selection[3]
if (startRow === endRow && startCell === endCell) {
const cellMeta = this.hotInstance.getCellMeta(startRow, startCell)
if (cellMeta && cellMeta.filter === false) {
this.hotInstance.setCellMeta(startRow, startCell, 'filter', true)
}
}
}
}) })
hot.addHook('afterChange', (source: any, change: any) => { hot.addHook('afterChange', (source: any, change: any) => {
@ -3328,7 +3367,7 @@ export class EditorComponent implements OnInit, AfterViewInit {
const row = source[0][0] const row = source[0][0]
const colProp = source[0][1] const colProp = source[0][1]
const col = hot.propToCol(colProp) const col = hot.propToCol(colProp) as number
// On edit we enabled filter for this cell, now when editing is finished // On edit we enabled filter for this cell, now when editing is finished
// We want filter to be disabled again, to be ready for next dropdown opening. // We want filter to be disabled again, to be ready for next dropdown opening.

View File

@ -12,7 +12,6 @@ import { EditRecordComponent } from './components/edit-record/edit-record.compon
import { UploadStaterComponent } from './components/upload-stater/upload-stater.component' import { UploadStaterComponent } from './components/upload-stater/upload-stater.component'
import { EditorRoutingModule } from './editor-routing.module' import { EditorRoutingModule } from './editor-routing.module'
import { EditorComponent } from './editor.component' import { EditorComponent } from './editor.component'
import { HomeModule } from '../home/home.module'
import { DcTreeModule } from '../shared/dc-tree/dc-tree.module' import { DcTreeModule } from '../shared/dc-tree/dc-tree.module'
import { DragDropModule } from '@angular/cdk/drag-drop' import { DragDropModule } from '@angular/cdk/drag-drop'
import { ViewboxesModule } from '../shared/viewboxes/viewboxes.module' import { ViewboxesModule } from '../shared/viewboxes/viewboxes.module'
@ -33,7 +32,6 @@ registerAllModules()
AppSharedModule, AppSharedModule,
DirectivesModule, DirectivesModule,
SharedModule, SharedModule,
HomeModule,
PipesModule, PipesModule,
DcTreeModule, DcTreeModule,
DragDropModule, DragDropModule,

View File

@ -11,9 +11,11 @@ export const errorRenderer = (
value: any, value: any,
cellProperties: any cellProperties: any
) => { ) => {
addDarkClass(td)
td.innerHTML = `${ td.innerHTML = `${
value ? value.toString() : '' value ? value.toString() : ''
} <clr-icon shape="exclamation-circle" status="warning"></clr-icon>` } <cds-icon shape="exclamation-triangle" status="warning"></cds-icon>`
return td return td
} }
@ -31,6 +33,8 @@ export const noSpinnerRenderer = (
value: any, value: any,
cellProperties: any cellProperties: any
) => { ) => {
addDarkClass(td)
td.innerHTML = value ? value : '' td.innerHTML = value ? value : ''
return td return td
@ -50,9 +54,20 @@ export const spinnerRenderer = (
value: any, value: any,
cellProperties: any cellProperties: any
) => { ) => {
addDarkClass(td)
td.innerHTML = `${ td.innerHTML = `${
value ? value.toString() : '' value ? value.toString() : ''
} <span class="spinner spinner-sm vertical-align-middle"></span>` } <span class="spinner spinner-sm vertical-align-middle"></span>`
return td return td
} }
/**
* Adds a htDark class to a TD element if not existing
*/
const addDarkClass = (td: any) => {
if (!td.classList.contains('htDark')) {
td.classList.add('htDark')
}
}

View File

@ -3,6 +3,7 @@
<clr-tree-node *ngIf="groups" class="search-node"> <clr-tree-node *ngIf="groups" class="search-node">
<div class="tree-search-wrapper"> <div class="tree-search-wrapper">
<input <input
appStealFocus
clrInput clrInput
#searchLibTreeInput #searchLibTreeInput
placeholder="Filter by Groups" placeholder="Filter by Groups"
@ -27,7 +28,7 @@
<clr-tree-node <clr-tree-node
(click)="groupOnClick(group)" (click)="groupOnClick(group)"
*ngIf="!group['hidden']" *ngIf="!group['hidden']"
[class.table-active]="group.GROUPURI === groupUri" [class.active]="group.GROUPURI === groupUri"
> >
<p class="m-0 cursor-pointer list-padding"> <p class="m-0 cursor-pointer list-padding">
<clr-icon shape="users"></clr-icon> <clr-icon shape="users"></clr-icon>

View File

@ -1,26 +1,63 @@
@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{ .sidebar-height{
height: 100%; height: 100%;
} }
.group-info-text{ .group-info-text{
display: inline; display: inline;
font-size: 20px; font-size: 20px;
} }
.group-info{ .group-info{
background-color: #f9f9f9; border: 1px solid;
border: 1px solid #a7a7a7;
border-radius: 3px; border-radius: 3px;
box-shadow: 0px 2px 5px #dad7d7;
} }
.group-info td{ .group-info td{
text-align: center; text-align: center;
} }
.group-data{ .group-data{
background-color: #f9f9f9; border: 1px solid;
border: 1px solid #a7a7a7;
border-radius: 3px; border-radius: 3px;
box-shadow: 0px 2px 5px #dad7d7;
} }
.group-data{ .group-data{
min-height: auto; min-height: auto;
h3, h5{ h3, h5{
@ -28,15 +65,11 @@
} }
.member-table{ .member-table{
background-color: #f9f9f9;
width: 100%; width: 100%;
} }
.member-table thead{
background-color: #dadada;
}
.member-table tbody{ .member-table tbody{
tr:hover{ tr:hover{
background-color: #e6e6e6;
cursor: pointer; cursor: pointer;
} }
} }

View File

@ -4,6 +4,7 @@
<div class="tree-search-wrapper"> <div class="tree-search-wrapper">
<input <input
clrInput clrInput
appStealFocus
#searchLibTreeInput #searchLibTreeInput
placeholder="Libraries" placeholder="Libraries"
name="input" name="input"
@ -46,6 +47,7 @@
<clr-tree-node *ngIf="library['tables']" class="search-node"> <clr-tree-node *ngIf="library['tables']" class="search-node">
<div class="tree-search-wrapper"> <div class="tree-search-wrapper">
<input <input
appStealFocus
clrInput clrInput
#searchTreeInput #searchTreeInput
placeholder="Tables" placeholder="Tables"
@ -85,7 +87,7 @@
(click)="!tableLocked ? onTableClick(libTable, library) : ''" (click)="!tableLocked ? onTableClick(libTable, library) : ''"
class="clr-treenode-link" class="clr-treenode-link"
[class.dc-locked-control]="tableLocked" [class.dc-locked-control]="tableLocked"
[class.table-active]="libTabActive(library.LIBRARYREF, libTable)" [class.active]="libTabActive(library.LIBRARYREF, libTable)"
> >
<ng-container [ngSwitch]="libTable.includes('-FC')"> <ng-container [ngSwitch]="libTable.includes('-FC')">
<clr-icon *ngSwitchCase="true" shape="bolt"></clr-icon> <clr-icon *ngSwitchCase="true" shape="bolt"></clr-icon>
@ -94,15 +96,17 @@
{{ libTable.replace('-FC', '') }} {{ libTable.replace('-FC', '') }}
</button> </button>
<clr-tooltip-content <ng-container *ngIf="tableLocked">
clrPosition="bottom-right" <clr-tooltip-content
clrSize="lg" clrPosition="bottom-right"
*clrIfOpen clrSize="lg"
> *clrIfOpen
<span *ngIf="tableLocked"> >
To unlock all tables, contact support&#64;datacontroller.io <span>
</span> To unlock all tables, contact support&#64;datacontroller.io
</clr-tooltip-content> </span>
</clr-tooltip-content>
</ng-container>
</clr-tooltip> </clr-tooltip>
</clr-tree-node> </clr-tree-node>
</clr-tree-node> </clr-tree-node>
@ -124,12 +128,20 @@
size="60" size="60"
class="is-info icon-dc-fill" class="is-info icon-dc-fill"
></clr-icon> ></clr-icon>
<h3 *ngIf="treeNodeLibraries?.length! > 0" class="text-center color-gray"> <p
*ngIf="treeNodeLibraries?.length! > 0"
class="text-center color-gray mt-10"
cds-text="section"
>
Please select a table Please select a table
</h3> </p>
<h3 *ngIf="treeNodeLibraries?.length! < 1" class="text-center color-gray"> <p
*ngIf="treeNodeLibraries?.length! < 1"
class="text-center color-gray mt-10"
cds-text="section"
>
No Editable Tables Configured No Editable Tables Configured
</h3> </p>
</div> </div>
</div> </div>
</div> </div>

View File

@ -2,147 +2,152 @@
<div class="card-header">Licencing</div> <div class="card-header">Licencing</div>
<div [ngSwitch]="action" class="card-block"> <div [ngSwitch]="action" class="card-block">
<ng-container *ngSwitchCase="'key'"> <div class="card-text">
<p class="key-error" *ngIf="!keyError"> <ng-container *ngSwitchCase="'key'">
Licence key is invalid. We can't provide you more details at the moment <p class="key-error" *ngIf="!keyError">
Licence key is invalid. We can't provide you more details at the
moment
</p>
<p
class="key-error"
*ngIf="keyError"
[innerHTML]="licenseErrors[keyError]"
></p>
<p *ngIf="errorDetails"><strong>Details:</strong> {{ errorDetails }}</p>
</ng-container>
<ng-container *ngSwitchCase="'limit'">
<p class="key-error">
The registered number of users reached the limit specified for your
licence. Please contact
<contact-link classes="color-green" />
or your reseller to arrange additional licences for this product.
</p>
</ng-container>
<ng-container *ngSwitchCase="'update'">
<p class="key-error">
Update the license key by uploading the licence file or by pasting a
license key and activation key in the inputs below.
</p>
</ng-container>
<p>
<strong>SYSSITE:</strong>
<span
*ngFor="let id of syssite.value; let i = index"
[class.misskey]="missmatchedKey && missmatchedKey === id"
>
{{ id }}{{ i === syssite.value?.length! - 1 ? '' : ',' }}
</span>
<a
class="tooltip tooltip-md tooltip-top-right"
(click)="copySyssite(copyIcon, copyTooltip, syssite.value || [])"
>
<clr-icon
#copyIcon
class="cursor-pointer"
shape="copy"
size="15"
></clr-icon>
<span #copyTooltip class="tooltip-content">Copy to clipboard</span>
</a>
</p> </p>
<p <p *ngIf="licenseKeyData && userCountLimitation" class="m-0">
class="key-error" <strong>Allowed users:</strong>
*ngIf="keyError" {{ licenseKeyData.users_allowed }}
[innerHTML]="licenseErrors[keyError]"
></p>
<p *ngIf="errorDetails"><strong>Details:</strong> {{ errorDetails }}</p>
</ng-container>
<ng-container *ngSwitchCase="'limit'">
<p class="key-error">
The registered number of users reached the limit specified for your
licence. Please contact
<contact-link classes="color-green" />
or your reseller to arrange additional licences for this product.
</p> </p>
</ng-container>
<ng-container *ngSwitchCase="'update'"> <clr-tabs>
<p class="key-error"> <clr-tab>
Update the license key by uploading the licence file or by pasting a <button clrTabLink>Upload licence</button>
license key and activation key in the inputs below. <clr-tab-content>
</p> <input
</ng-container> #licenceFile
(change)="onFileCapture($event)"
<p> type="file"
<strong>SYSSITE:</strong> hidden
<span />
*ngFor="let id of syssite.value; let i = index" <div
[class.misskey]="missmatchedKey && missmatchedKey === id" (click)="licenceFile.click()"
> appFileDrop
{{ id }}{{ i === syssite.value?.length! - 1 ? '' : ',' }} (fileDrop)="onFileCapture($event, true)"
</span> class="drop-area"
>
<a <clr-spinner
class="tooltip tooltip-md tooltip-top-right" class="spinner-sm"
(click)="copySyssite(copyIcon, copyTooltip, syssite.value || [])" *ngIf="licenceFileLoading"
> ></clr-spinner>
<clr-icon <ng-container *ngIf="!licenceFileLoading">
#copyIcon <div *ngIf="licencefile.filename === ''">
class="cursor-pointer" Drop / Browse licence file
shape="copy" </div>
size="15" <div *ngIf="licencefile.filename !== ''">
></clr-icon> Selected file: <strong>{{ licencefile.filename }}</strong>
<span #copyTooltip class="tooltip-content">Copy to clipboard</span> </div>
</a> <div *ngIf="licenceFileError">
</p> <strong>{{ licenceFileError }}</strong>
</div>
<p *ngIf="licenseKeyData && userCountLimitation" class="m-0"> </ng-container>
<strong>Allowed users:</strong>
{{ licenseKeyData.users_allowed }}
</p>
<clr-tabs>
<clr-tab>
<button clrTabLink>Upload licence</button>
<clr-tab-content>
<input
#licenceFile
(change)="onFileCapture($event)"
type="file"
hidden
/>
<div
(click)="licenceFile.click()"
appFileDrop
(fileDrop)="onFileCapture($event, true)"
class="drop-area"
>
<clr-spinner
class="spinner-sm"
*ngIf="licenceFileLoading"
></clr-spinner>
<ng-container *ngIf="!licenceFileLoading">
<div *ngIf="licencefile.filename === ''">
Drop / Browse licence file
</div>
<div *ngIf="licencefile.filename !== ''">
Selected file: <strong>{{ licencefile.filename }}</strong>
</div>
<div *ngIf="licenceFileError">
<strong>{{ licenceFileError }}</strong>
</div>
</ng-container>
</div>
</clr-tab-content>
</clr-tab>
<clr-tab>
<button clrTabLink>Paste licence</button>
<clr-tab-content>
<form class="clr-form license-key-form">
<p>Licence key:</p>
<div class="clr-control-container">
<textarea
[(ngModel)]="licenceKeyValue"
(mouseleave)="trimKeys()"
name="license-key-area"
placeholder="Paste licence key here"
class="clr-textarea"
></textarea>
</div> </div>
</form> </clr-tab-content>
</clr-tab>
<form class="clr-form activation-key-form"> <clr-tab>
<p>Activation key:</p> <button clrTabLink>Paste licence</button>
<div class="clr-control-container"> <clr-tab-content>
<textarea <form class="clr-form license-key-form">
[(ngModel)]="activationKeyValue" <p>Licence key:</p>
(mouseleave)="trimKeys()" <div class="clr-control-container">
name="activation-key-area" <textarea
placeholder="Paste activation key here" [(ngModel)]="licenceKeyValue"
class="clr-textarea" (mouseleave)="trimKeys()"
></textarea> name="license-key-area"
</div> placeholder="Paste licence key here"
</form> class="clr-textarea"
</clr-tab-content> ></textarea>
</clr-tab> </div>
</clr-tabs> </form>
<button <form class="clr-form activation-key-form">
(click)="applyKeys()" <p>Activation key:</p>
class="btn btn-primary apply-keys" <div class="clr-control-container">
[clrLoading]="applyingKeys" <textarea
[disabled]="disableApplyButton" [(ngModel)]="activationKeyValue"
> (mouseleave)="trimKeys()"
Apply licence keys name="activation-key-area"
</button> placeholder="Paste activation key here"
class="clr-textarea"
></textarea>
</div>
</form>
</clr-tab-content>
</clr-tab>
</clr-tabs>
</div>
<button <div class="card-footer d-flex clr-align-items-center">
*ngIf="isAppFreeTier.value" <button
routerLink="/" (click)="applyKeys()"
class="btn btn-sm btn-link" class="btn btn-primary apply-keys"
> [clrLoading]="applyingKeys"
Continue with free tier [disabled]="disableApplyButton"
</button> >
Apply licence keys
</button>
<button
*ngIf="isAppFreeTier.value"
routerLink="/"
class="btn btn-sm btn-link"
>
Continue with free tier
</button>
</div>
</div> </div>
</div> </div>

View File

@ -33,7 +33,6 @@
.apply-keys { .apply-keys {
height: 40px; height: 40px;
width: 200px;
} }
.drop-area { .drop-area {

View File

@ -3,6 +3,7 @@
<clr-tree-node *ngIf="libraryList" class="search-node"> <clr-tree-node *ngIf="libraryList" class="search-node">
<div class="tree-search-wrapper"> <div class="tree-search-wrapper">
<input <input
appStealFocus
clrInput clrInput
#searchLibTreeInput #searchLibTreeInput
placeholder="Libraries" placeholder="Libraries"
@ -42,6 +43,7 @@
<clr-tree-node *ngIf="library['tables']" class="search-node"> <clr-tree-node *ngIf="library['tables']" class="search-node">
<div class="tree-search-wrapper"> <div class="tree-search-wrapper">
<input <input
appStealFocus
clrInput clrInput
#searchTreeInput #searchTreeInput
placeholder="Tables" placeholder="Tables"
@ -85,6 +87,7 @@
<clr-tree-node *ngIf="libTable['columns']" class="search-node"> <clr-tree-node *ngIf="libTable['columns']" class="search-node">
<div class="tree-search-wrapper"> <div class="tree-search-wrapper">
<input <input
appStealFocus
clrInput clrInput
#searchTreeInput #searchTreeInput
placeholder="Columns" placeholder="Columns"
@ -138,7 +141,9 @@
size="60" size="60"
class="is-info icon-dc-fill" class="is-info icon-dc-fill"
></clr-icon> ></clr-icon>
<h3 class="text-center color-gray">Please select a column or table</h3> <p class="text-center color-gray mt-10" cds-text="section">
Please select a column or table
</p>
</div> </div>
<ng-container *ngIf="column || table"> <ng-container *ngIf="column || table">
@ -180,13 +185,13 @@
<button <button
(click)="limitDotDepth = true" (click)="limitDotDepth = true"
type="button" type="button"
class="btn btn-outline" class="btn btn-outline mr-5"
> >
Limit depth Limit depth
</button> </button>
<!-- <button class="btn btn-outline" (click)='showSvg()'> Open in New Tab </button> --> <!-- <button class="btn btn-outline" (click)='showSvg()'> Open in New Tab </button> -->
<div class="btn-group d-block"> <div class="btn-group direction d-block">
<div <div
class="radio btn" class="radio btn"
(click)=" (click)="

View File

@ -1,6 +1,8 @@
@import '../../colors.scss';
.toggle-switch input[type=checkbox]:checked+label:before { .toggle-switch input[type=checkbox]:checked+label:before {
border-color: #314351; border-color: $headerBackground;
background-color: #314351!important; background-color: $headerBackground !important;
transition: .15s ease-in; transition: .15s ease-in;
transition-property: border-color,background-color; transition-property: border-color,background-color;
} }
@ -41,6 +43,10 @@ clr-tree-node button {
white-space: nowrap; white-space: nowrap;
} }
.btn-group.direction {
margin-left: var(--cds-global-space-6);
}
.graph-render-spinner { .graph-render-spinner {
position: absolute; position: absolute;
top: 0; top: 0;

View File

@ -22,6 +22,7 @@
<clr-tree-node *ngIf="metaDataList" class="search-node"> <clr-tree-node *ngIf="metaDataList" class="search-node">
<div class="tree-search-wrapper"> <div class="tree-search-wrapper">
<input <input
appStealFocus
clrInput clrInput
#searchLibTreeInput #searchLibTreeInput
placeholder="search SAS Types" placeholder="search SAS Types"
@ -72,7 +73,9 @@
size="60" size="60"
class="is-info icon-dc-fill" class="is-info icon-dc-fill"
></clr-icon> ></clr-icon>
<h3 class="text-center color-gray">Please select a type</h3> <p class="text-center color-gray mt-10" cds-text="section">
Please select a type
</p>
</div> </div>
<div class="loadingSpinner" *ngIf="loading"> <div class="loadingSpinner" *ngIf="loading">
@ -123,12 +126,18 @@
[class.object-header]="!entry.count" [class.object-header]="!entry.count"
class="full-width" class="full-width"
> >
<clr-icon <div>
*ngIf="!entry.count" <clr-icon
shape="rack-server" *ngIf="!entry.count"
></clr-icon> shape="rack-server"
<clr-icon *ngIf="entry.count" shape="block"></clr-icon> ></clr-icon>
{{ entry.display }} <clr-icon
*ngIf="entry.count"
shape="block"
></clr-icon>
{{ entry.display }}
</div>
<p class="float-right object-uri" *ngIf="!entry.count"> <p class="float-right object-uri" *ngIf="!entry.count">
{{ entry.URI }} {{ entry.URI }}
</p> </p>
@ -163,9 +172,15 @@
[clrExpandable]="true" [clrExpandable]="true"
> >
<div [class.object-header]="!entry.count" class="full-width"> <div [class.object-header]="!entry.count" class="full-width">
<clr-icon *ngIf="!entry.count" shape="rack-server"></clr-icon> <div>
<clr-icon *ngIf="entry.count" shape="block"></clr-icon> <clr-icon
{{ entry.display }} *ngIf="!entry.count"
shape="rack-server"
></clr-icon>
<clr-icon *ngIf="entry.count" shape="block"></clr-icon>
{{ entry.display }}
</div>
<p class="float-right object-uri" *ngIf="!entry.count"> <p class="float-right object-uri" *ngIf="!entry.count">
{{ entry.URI }} {{ entry.URI }}
</p> </p>

View File

@ -1,14 +1,27 @@
::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{ .objects-col{
height: 75vh; height: 75vh;
overflow: scroll; overflow: scroll;
border: 1px solid #cccccc; border: 1px solid #cccccc;
background: white;
border-radius: 4px; border-radius: 4px;
} }
.cols-head { .cols-head {
background: #fafafa;
border: 1px solid #cccccc; border: 1px solid #cccccc;
padding: 10px; padding: 10px;
display: flex; display: flex;
@ -40,11 +53,13 @@
margin-top: 5px; margin-top: 5px;
} }
.object-header{ .object-header{
display: flex;
align-items: center;
justify-content: space-between;
padding-left: 3px; padding-left: 3px;
padding-right: 3px; padding-right: 3px;
} }
.object-header:hover{ .object-header:hover{
background-color: #d8e3e9;
border-radius: 3px; border-radius: 3px;
} }
.datagrid-host{ .datagrid-host{

View File

@ -0,0 +1,9 @@
export interface AppSettings {
persistSelectedTheme: boolean
selectedTheme: AppThemes
}
export enum AppThemes {
light = 'light',
dark = 'dark'
}

View File

@ -17,6 +17,7 @@ export interface EditorsGetDataSASResponse extends BaseSASResponse {
dqrules: DQRule[] dqrules: DQRule[]
dsmeta: DSMeta[] dsmeta: DSMeta[]
dqdata: DQData[] dqdata: DQData[]
versions: Version[]
cols: Col[] cols: Col[]
maxvarlengths: Maxvarlength[] maxvarlengths: Maxvarlength[]
xl_rules: any[] xl_rules: any[]
@ -27,6 +28,18 @@ export interface DSMeta {
ODS_TABLE: string ODS_TABLE: string
NAME: string NAME: string
VALUE: string VALUE: string
[key: string]: string
}
export interface Version {
LOAD_REF: string
USER_NM: string
VERSION_DTTM: string
VERSION_DESC: string
CHANGED_RECORDS: number
NEW_RECORDS: number
DELETED_RECORDS: number
[key: string]: string | number
} }
export interface Sasdata { export interface Sasdata {

View File

@ -0,0 +1,9 @@
import { BaseSASResponse } from './common/BaseSASResponse'
export interface EditorsRestoreServiceResponse extends BaseSASResponse {
restore_out: RestoreOut[]
}
export interface RestoreOut {
LOADREF: string
}

View File

@ -143,7 +143,6 @@
(ngModelChange)=" (ngModelChange)="
setVariableOperator(queryIndex, query.operator, clauseIndex) setVariableOperator(queryIndex, query.operator, clauseIndex)
" "
class="mt-2"
clrSelect clrSelect
> >
<option *ngFor="let opr of query.operators">{{ opr }}</option> <option *ngFor="let opr of query.operators">{{ opr }}</option>

View File

@ -1,3 +1,25 @@
::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 { .content {
display: flex; display: flex;
@ -9,13 +31,12 @@
display: flex; display: flex;
justify-content: center; justify-content: center;
flex-direction: column; flex-direction: column;
background: #e9e9e9;
padding: 15px; padding: 15px;
} }
.clause-query { .clause-query {
padding: 30px 0px 20px 20px; padding: 30px 0px 20px 20px;
background: #fbf8f8;
display: flex; display: flex;
justify-content: center; justify-content: center;
flex-direction: column; flex-direction: column;
@ -220,8 +241,11 @@
vertical-align: middle; vertical-align: middle;
margin: 0; margin: 0;
} }
:not(pre) > code[class*="language-"], pre[class*="language-"] {
background-color: #fbf8f8; ::ng-deep body[cds-theme="dark"] {
.line-numbers {
border-color: #989797 !important;
}
} }
pre[class*="language-"] { pre[class*="language-"] {

View File

@ -878,17 +878,25 @@ export class QueryComponent
*/ */
public hasInvalidCluase(clauses: any): boolean { public hasInvalidCluase(clauses: any): boolean {
for (let clause of clauses) { for (let clause of clauses) {
clause['invalidClause'] = false
if ( if (
clause.variable === null || clause.value === '' &&
clause.operator === null || !(clause.operator === 'NE' || clause.operator === 'CONTAINS')
clause.value === null || ) {
clause.value === '' clause['invalidClause'] = true
return true
}
if (
clause.variable === null ||
clause.operator === null ||
clause.value === null
) { ) {
clause['invalidClause'] = true clause['invalidClause'] = true
return true return true
} else {
clause['invalidClause'] = false
} }
} }

View File

@ -139,10 +139,7 @@
<div class="card-header p-0"> <div class="card-header p-0">
<div class="clr-row"> <div class="clr-row">
<div class="clr-col-md-4 approvalBack"> <div class="clr-col-md-4 approvalBack">
<span <span class="btn btn-outline m-0" (click)="goToApprovalsList()">
class="btn btn-sm btn-outline m-0"
(click)="goToApprovalsList()"
>
<clr-icon shape="caret" dir="left" size="20"></clr-icon>Back to <clr-icon shape="caret" dir="left" size="20"></clr-icon>Back to
approvals list approvals list
</span> </span>
@ -209,22 +206,22 @@
<div class="d-flex justify-content-center mt-0"> <div class="d-flex justify-content-center mt-0">
<div class="clr-row clr-gap-5 clr-gap-sm-0"> <div class="clr-row clr-gap-5 clr-gap-sm-0">
<button <button
class="btn btn-sm btn-outline text-center mt-5" class="btn btn-sm btn-outline text-center mt-5 mr-5i"
(click)="goToBase(jsParams?.TABLE_NM)" (click)="goToBase(jsParams?.TABLE_NM)"
> >
Go to base table screen View base table
</button> </button>
<button <button
class="btn btn-sm btn-success-outline text-center mt-5" class="btn btn-sm btn-success-outline text-center mt-5 mr-5i"
(click)="getTable(tableId)" (click)="getTable(tableId)"
> >
Go to edited screen View staged data
</button> </button>
<button <button
class="btn btn-sm btn-info-outline text-center mt-5" class="btn btn-sm btn-info-outline text-center mt-5"
(click)="goBack(jsParams?.TABLE_NM)" (click)="goBack(jsParams?.TABLE_NM)"
> >
Go back to editor Edit base table
</button> </button>
</div> </div>
</div> </div>
@ -236,7 +233,7 @@
id="acceptBtn" id="acceptBtn"
[clrLoading]="acceptLoading" [clrLoading]="acceptLoading"
type="submit" type="submit"
class="btn btn-sm btn-success" class="btn btn-sm btn-success mr-5i"
(click)="approveTable()" (click)="approveTable()"
[disabled]=" [disabled]="
!loadingTable || params?.ISAPPROVER === 'NO' || noChanges !loadingTable || params?.ISAPPROVER === 'NO' || noChanges
@ -246,7 +243,7 @@
</button> </button>
<button <button
id="rejectBtn" id="rejectBtn"
class="btn btn-sm btn btn-danger mr-0" class="btn btn-sm btn btn-danger mr-5i"
(click)="rejectOpen = true" (click)="rejectOpen = true"
[disabled]=" [disabled]="
!loadingTable || params?.ISAPPROVER === 'NO' || noChanges !loadingTable || params?.ISAPPROVER === 'NO' || noChanges
@ -287,19 +284,15 @@
> >
<span class="label label-warning"> <span class="label label-warning">
Changed Rows Changed Rows
<span class="badge badge-warning">{{ <span class="badge">{{ lens.updated }}</span>
lens.updated
}}</span>
</span> </span>
<span class="label label-success"> <span class="label label-success">
Added Rows Added Rows
<span class="badge badge-success">{{ lens.new }}</span> <span class="badge">{{ lens.new }}</span>
</span> </span>
<span class="label label-danger"> <span class="label label-danger">
Deleted Rows Deleted Rows
<span class="badge badge-danger">{{ <span class="badge">{{ lens.deleted }}</span>
lens.deleted
}}</span>
</span> </span>
</div> </div>
</div> </div>
@ -314,8 +307,8 @@
class="h-24vh d-flex flex-column justify-content-center align-items-center" class="h-24vh d-flex flex-column justify-content-center align-items-center"
> >
<span class="spinner"> Loading... </span> <span class="spinner"> Loading... </span>
<div *ngIf="!loadingTable"> <div *ngIf="!loadingTable" class="mt-10">
<h3>Loading table</h3> <p cds-text="section">Loading table</p>
</div> </div>
</div> </div>
@ -394,9 +387,9 @@
<div class="card-header"> <div class="card-header">
<div class="clr-row"> <div class="clr-row">
<div class="clr-col-md-4 approvalBack"> <div class="clr-col-md-4 approvalBack">
<span class="btn btn-sm btn-outline" (click)="goToSubmitList()"> <span class="btn btn-outline" (click)="goToSubmitList()">
<clr-icon shape="caret" dir="left" size="20"></clr-icon>Back to <cds-icon shape="angle" direction="left" size="20"></cds-icon
submitted list >Back to submitted list
</span> </span>
</div> </div>
<div class="clr-col-md-4"> <div class="clr-col-md-4">
@ -443,22 +436,22 @@
<div class="d-flex justify-content-center mt-0"> <div class="d-flex justify-content-center mt-0">
<div class="clr-row clr-gap-5 clr-gap-sm-0"> <div class="clr-row clr-gap-5 clr-gap-sm-0">
<button <button
class="btn btn-sm btn-outline text-center mt-5" class="btn btn-sm btn-outline text-center mt-5 mr-5i"
(click)="goToBase(subObj.base)" (click)="goToBase(subObj.base)"
> >
Go to base table screen View base table
</button> </button>
<button <button
class="btn btn-sm btn-success-outline text-center mt-5" class="btn btn-sm btn-success-outline text-center mt-5 mr-5i"
(click)="getTable(subObj.tableId)" (click)="getTable(subObj.tableId)"
> >
Go to edited screen View staged data
</button> </button>
<button <button
class="btn btn-sm btn-info-outline text-center mt-5" class="btn btn-sm btn-info-outline text-center mt-5"
(click)="goBack(subObj.base)" (click)="goBack(subObj.base)"
> >
Go back to editor Edit base table
</button> </button>
</div> </div>
</div> </div>
@ -480,15 +473,15 @@
> >
<span class="label label-warning"> <span class="label label-warning">
Changed Rows Changed Rows
<span class="badge badge-warning">{{ lens.updated }}</span> <span class="badge">{{ lens.updated }}</span>
</span> </span>
<span class="label label-success"> <span class="label label-success">
Added Rows Added Rows
<span class="badge badge-success">{{ lens.new }}</span> <span class="badge">{{ lens.new }}</span>
</span> </span>
<span class="label label-danger"> <span class="label label-danger">
Deleted Rows Deleted Rows
<span class="badge badge-danger">{{ lens.deleted }}</span> <span class="badge">{{ lens.deleted }}</span>
</span> </span>
</div> </div>
</div> </div>
@ -522,8 +515,8 @@
class="h-25vh d-flex flex-column justify-content-center align-items-center" class="h-25vh d-flex flex-column justify-content-center align-items-center"
> >
<span class="spinner"> Loading... </span> <span class="spinner"> Loading... </span>
<div *ngIf="!loadingTable"> <div *ngIf="!loadingTable" class="mt-10">
<h3>Loading table</h3> <p cds-text="section">Loading table</p>
</div> </div>
</div> </div>
<div class="tableCont"> <div class="tableCont">

View File

@ -1,3 +1,5 @@
@import '../../../colors.scss';
.loader { .loader {
display:flex; display:flex;
justify-content: center; justify-content: center;
@ -10,19 +12,48 @@
} }
.addedRow { .addedRow {
background: rgb(146, 208, 154);
border: 1px solid rgba(9, 77, 117, 0.2); border: 1px solid rgba(9, 77, 117, 0.2);
border-radius: 5px; border-radius: 5px;
} }
.deletedRow { .deletedRow {
background: rgb(230, 179, 179);
border: 1px solid rgba(70, 71, 70, 0.2); border: 1px solid rgba(70, 71, 70, 0.2);
border-radius: 5px; 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 { .updatedRow {
background: #fafda8;
border: 1px solid rgba(9, 117, 9, 0.2); border: 1px solid rgba(9, 117, 9, 0.2);
border-radius: 5px; border-radius: 5px;
} }
@ -45,7 +76,7 @@ background: rgba(252, 135, 120, 0.4);
font-size: .54167rem; font-size: .54167rem;
font-weight: 400; font-weight: 400;
letter-spacing: normal; letter-spacing: normal;
background: #314351; background: $headerBackground;
border-radius: .125rem; border-radius: .125rem;
color: #f0f1ec;; color: #f0f1ec;;
line-height: .75rem; line-height: .75rem;
@ -68,8 +99,8 @@ background: rgba(252, 135, 120, 0.4);
top: auto; top: auto;
right: auto; right: auto;
content: ""; content: "";
border-left: .25rem solid #314351; border-left: .25rem solid $headerBackground;
border-top: .20833rem solid #314351; border-top: .20833rem solid $headerBackground;
border-right: .25rem solid transparent; border-right: .25rem solid transparent;
border-bottom: .20833rem solid transparent; border-bottom: .20833rem solid transparent;
} }
@ -79,8 +110,8 @@ border: 0px solid;
} }
.toggle-switch input[type=checkbox]:checked+label:before { .toggle-switch input[type=checkbox]:checked+label:before {
border-color: #314351; border-color: $headerBackground;
background-color: #314351!important; background-color: $headerBackground !important;
transition: .15s ease-in; transition: .15s ease-in;
transition-property: border-color,background-color; transition-property: border-color,background-color;
} }
@ -140,7 +171,7 @@ border: 0px solid;
.tooll { .tooll {
position: absolute; position: absolute;
background: #e6b3b3; background: #e6b3b3;
color: #314351; color: $headerBackground;
top: 0px; top: 0px;
height: 36px; height: 36px;
width: 100%; width: 100%;

View File

@ -99,19 +99,11 @@
</clr-dg-cell> </clr-dg-cell>
</clr-dg-row> </clr-dg-row>
<clr-dg-footer class="d-flex justify-content-start"> <clr-dg-footer>
<span>items per page</span> <clr-dg-pagination #pagination [clrDgPageSize]="10">
<select [(ngModel)]="itemsNum"> <clr-dg-page-size [clrPageSizeOptions]="[3, 5, 10, 15]"
<option [ngValue]="3">3</option> >Items per page</clr-dg-page-size
<option [ngValue]="5">5</option> >
<option [ngValue]="10">10</option>
<option [ngValue]="15">15</option>
</select>
<clr-dg-pagination
#pagination
[clrDgPageSize]="itemsNum"
class="center"
>
{{ pagination.firstItem + 1 }} - {{ pagination.lastItem + 1 }} of {{ pagination.firstItem + 1 }} - {{ pagination.lastItem + 1 }} of
{{ pagination.totalItems }} approvals {{ pagination.totalItems }} approvals
</clr-dg-pagination> </clr-dg-pagination>

View File

@ -1,3 +1,5 @@
@import '../../../colors.scss';
.column-center { .column-center {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -13,11 +15,11 @@
} }
.tooltip.tooltip-bottom-left>.tooltip-content, .tooltip .tooltip-content.tooltip-bottom-left { .tooltip.tooltip-bottom-left>.tooltip-content, .tooltip .tooltip-content.tooltip-bottom-left {
background: #314351!important; background: $headerBackground !important;
} }
.tooltip.tooltip-bottom-left>.tooltip-content:before, .tooltip .tooltip-content.tooltip-bottom-left:before { .tooltip.tooltip-bottom-left>.tooltip-content:before, .tooltip .tooltip-content.tooltip-bottom-left:before {
border-right: .25rem solid #314351; border-right: .25rem solid $headerBackground;
border-bottom: .20833rem solid #314351; border-bottom: .20833rem solid $headerBackground;
} }
.noBorder { .noBorder {

View File

@ -86,19 +86,11 @@
</clr-dg-cell> </clr-dg-cell>
</clr-dg-row> </clr-dg-row>
<clr-dg-footer class="d-flex justify-content-start"> <clr-dg-footer>
<span>items per page</span> <clr-dg-pagination #pagination [clrDgPageSize]="10">
<select [(ngModel)]="itemsNum"> <clr-dg-page-size [clrPageSizeOptions]="[3, 5, 10, 15]"
<option [ngValue]="3">3</option> >Items per page</clr-dg-page-size
<option [ngValue]="5">5</option> >
<option [ngValue]="10">10</option>
<option [ngValue]="15">15</option>
</select>
<clr-dg-pagination
#pagination
[clrDgPageSize]="itemsNum"
class="center"
>
{{ pagination.firstItem + 1 }} - {{ pagination.firstItem + 1 }} -
{{ pagination.lastItem + 1 }} of {{ pagination.lastItem + 1 }} of
{{ pagination.totalItems }} submissions {{ pagination.totalItems }} submissions

View File

@ -1,13 +1,15 @@
@import '../../../colors.scss';
.noBorder { .noBorder {
border-bottom: 1px solid transparent!important; border-bottom: 1px solid transparent!important;
} }
.tooltip.tooltip-bottom-left>.tooltip-content, .tooltip .tooltip-content.tooltip-bottom-left { .tooltip.tooltip-bottom-left>.tooltip-content, .tooltip .tooltip-content.tooltip-bottom-left {
background: #314351!important; background: $headerBackground !important;
} }
.tooltip.tooltip-bottom-left>.tooltip-content:before, .tooltip .tooltip-content.tooltip-bottom-left:before { .tooltip.tooltip-bottom-left>.tooltip-content:before, .tooltip .tooltip-content.tooltip-bottom-left:before {
border-right: .25rem solid #314351; border-right: .25rem solid $headerBackground;
border-bottom: .20833rem solid #314351; border-bottom: .20833rem solid $headerBackground;
} }
.no-submitted-tables { .no-submitted-tables {

View File

@ -3,6 +3,7 @@
<clr-tree-node *ngIf="roles" class="search-node"> <clr-tree-node *ngIf="roles" class="search-node">
<div class="tree-search-wrapper"> <div class="tree-search-wrapper">
<input <input
appStealFocus
clrInput clrInput
#searchLibTreeInput #searchLibTreeInput
placeholder="Filter by Roles" placeholder="Filter by Roles"
@ -26,7 +27,7 @@
<clr-tree-node <clr-tree-node
(click)="roleOnClick(role)" (click)="roleOnClick(role)"
*ngIf="!role['hidden']" *ngIf="!role['hidden']"
[class.table-active]="role.ROLEURI === roleUri" [class.active]="role.ROLEURI === roleUri"
> >
<p class="m-0 cursor-pointer list-padding"> <p class="m-0 cursor-pointer list-padding">
<clr-icon shape="blocks-group"></clr-icon> <clr-icon shape="blocks-group"></clr-icon>

View File

@ -1,25 +1,61 @@
@import '../../colors.scss';
::ng-deep body[cds-theme="dark"] {
.role {
background-color: $headerBackground;
border-color: $headerBackground;
}
.role-data {
background-color: $headerBackground;
border-color: $headerBackground;
}
.member-table tbody{
tr:hover{
background-color: #29404b;
}
}
}
::ng-deep body[cds-theme="light"] {
.role-info{
background-color: #f9f9f9;
border-color: #a7a7a7;
box-shadow: 0px 2px 5px #dad7d7;
}
.role-data {
background-color: #f9f9f9;
border-color: #a7a7a7;
box-shadow: 0px 2px 5px #dad7d7;
}
.member-table tbody{
tr:hover{
background-color: #e6e6e6;
}
}
}
.sidebar-height{ .sidebar-height{
height: 100%; height: 100%;
} }
.role-info-text{ .role-info-text{
display: inline; display: inline;
font-size: 20px; font-size: 20px;
} }
.role-info{ .role-info{
background-color: #f9f9f9; border: 1px solid;
border: 1px solid #a7a7a7;
border-radius: 3px; border-radius: 3px;
box-shadow: 0px 2px 5px #dad7d7;
} }
.role-info td{ .role-info td{
text-align: center; text-align: center;
} }
.role-data{ .role-data{
background-color: #f9f9f9; border: 1px solid;
border: 1px solid #a7a7a7;
border-radius: 3px; border-radius: 3px;
box-shadow: 0px 2px 5px #dad7d7;
} }
.role-data{ .role-data{
min-height: unset; min-height: unset;
@ -28,15 +64,10 @@
} }
} }
.member-table{ .member-table{
background-color: #f9f9f9;
width: 100%; width: 100%;
} }
.member-table thead{
background-color: #dadada;
}
.member-table tbody{ .member-table tbody{
tr:hover{ tr:hover{
background-color: #e6e6e6;
cursor: pointer; cursor: pointer;
} }
} }

View File

@ -0,0 +1,56 @@
import { BehaviorSubject } from 'rxjs'
import { AppSettings, AppThemes } from '../models/AppSettings'
export class AppSettingsService {
public defaultSettings: AppSettings = {
persistSelectedTheme: true,
selectedTheme: AppThemes.light
}
public settings = new BehaviorSubject<AppSettings>(this.defaultSettings)
constructor() {
this.restoreAppSettings()
}
/**
* Restore app settings from the local storage
*/
restoreAppSettings() {
try {
const serializedSettings = localStorage.getItem('app-settings')
if (serializedSettings) {
const settings = JSON.parse(serializedSettings)
this.setAppSettings(settings)
} else {
console.info(
'No app settings stored in the localStorage, we will set to default values.'
)
}
} catch (err) {
console.warn('Error restoring settings from local storgae.', err)
}
}
/**
* Save app settings to the local storage
*/
storeAppSettings() {
localStorage.setItem('app-settings', JSON.stringify(this.settings.value))
}
/**
* Function used in the app to update global settings object
*
* @param appSettings contains properties that should be updated in settings
*/
setAppSettings(appSettings: Partial<AppSettings>) {
this.settings.next({
...this.settings.value,
...appSettings
})
this.storeAppSettings()
}
}

View File

@ -7,6 +7,8 @@ import { BehaviorSubject } from 'rxjs'
import { LoggerService } from './logger.service' import { LoggerService } from './logger.service'
import { EnvironmentInfo } from '../system/models/environment-info.model' import { EnvironmentInfo } from '../system/models/environment-info.model'
import { LicenceService } from './licence.service' import { LicenceService } from './licence.service'
import { AppSettingsService } from './app-settings.service'
import { AppThemes } from '../models/AppSettings'
@Injectable() @Injectable()
export class AppService { export class AppService {
@ -18,6 +20,7 @@ export class AppService {
private eventService: EventService, private eventService: EventService,
private sasService: SasService, private sasService: SasService,
private loggerService: LoggerService, private loggerService: LoggerService,
private appSettingsService: AppSettingsService,
private router: Router private router: Router
) { ) {
this.subscribe() this.subscribe()
@ -29,6 +32,19 @@ export class AppService {
} }
} }
}) })
const appSettings = this.appSettingsService.settings.value
if (!!appSettings.persistSelectedTheme) {
if (appSettings.selectedTheme === AppThemes.light) {
this.eventService.toggleDarkMode(false)
} else if (appSettings.selectedTheme === AppThemes.dark) {
this.eventService.toggleDarkMode(true)
} else {
// Fallback to light mode
this.eventService.toggleDarkMode(false)
}
}
} }
sasServiceInit() { sasServiceInit() {
@ -95,6 +111,7 @@ export class AppService {
MEMSIZE: res.MEMSIZE, MEMSIZE: res.MEMSIZE,
SYSPROCESSMODE: res.SYSPROCESSMODE, SYSPROCESSMODE: res.SYSPROCESSMODE,
SYSHOSTNAME: res.SYSHOSTNAME, SYSHOSTNAME: res.SYSHOSTNAME,
SYSUSERID: res.SYSUSERID,
SYSHOSTINFOLONG: res.SYSHOSTINFOLONG, SYSHOSTINFOLONG: res.SYSHOSTINFOLONG,
SYSENCODING: res.SYSENCODING, SYSENCODING: res.SYSENCODING,
AUTOEXEC: res.AUTOEXEC, AUTOEXEC: res.AUTOEXEC,

View File

@ -1,6 +1,9 @@
import { Injectable, Output, EventEmitter } from '@angular/core' import { Injectable, Output, EventEmitter } from '@angular/core'
import { InfoModal, AbortDetails } from '../models/InfoModal' import { InfoModal, AbortDetails } from '../models/InfoModal'
import { AlertsService } from '../shared/alerts/alerts.service' import { AlertsService } from '../shared/alerts/alerts.service'
import { BehaviorSubject } from 'rxjs'
import { AppSettingsService } from './app-settings.service'
import { AppThemes } from '../models/AppSettings'
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
@ -17,7 +20,23 @@ export class EventService {
public viewLastUrl: string | null = null public viewLastUrl: string | null = null
public sidebarCloseLimit = 1280 public sidebarCloseLimit = 1280
constructor(private alertsService: AlertsService) {} public darkMode: BehaviorSubject<boolean> = new BehaviorSubject(false)
constructor(private appSettingsService: AppSettingsService) {}
toggleDarkMode(value: boolean) {
this.darkMode.next(value)
if (value) {
document.body.setAttribute('cds-theme', 'dark')
} else {
document.body.setAttribute('cds-theme', 'light')
}
this.appSettingsService.setAppSettings({
selectedTheme: value ? AppThemes.dark : AppThemes.light
})
}
public showDemoLimitModal(featureName: string) { public showDemoLimitModal(featureName: string) {
this.onDemoLimitModalShow.emit(featureName) this.onDemoLimitModalShow.emit(featureName)

View File

@ -1,6 +1,6 @@
.input-val { .input-val {
border: 0px; border: 0px;
background: #fbf8f8; background: transparent;
border-bottom: 1px solid #999999; border-bottom: 1px solid #999999;
} }
@ -13,6 +13,29 @@ input {
} }
} }
::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 { .autocomplete-wrapper {
.overlay { .overlay {
position: fixed; position: fixed;
@ -25,7 +48,6 @@ input {
.datalist { .datalist {
position: fixed; position: fixed;
background: white;
box-shadow: 0px 3px 10px -1px #0000002b; box-shadow: 0px 3px 10px -1px #0000002b;
overflow: auto; overflow: auto;
z-index: 2000; z-index: 2000;

View File

@ -6,25 +6,31 @@
> >
<h3 class="modal-title center text-center color-darker-gray">Dataset Meta</h3> <h3 class="modal-title center text-center color-darker-gray">Dataset Meta</h3>
<div class="modal-body"> <div class="modal-body">
<p *ngIf="dsmetaGroupped.length < 1" class="text-center"> <p *ngIf="dsmetaTabs.length < 1" class="text-center">
No dataset meta to show. No dataset meta to show.
</p> </p>
<clr-tabs clrLayout="vertical"> <clr-tabs clrLayout="vertical">
<clr-tab *ngFor="let dsmeta of dsmetaGroupped; let index = index"> <clr-tab *ngFor="let tab of tabs; let index = index">
<button clrTabLink id="link1">{{ dsmeta.group }}</button> <button clrTabLink id="link1">{{ tab.name }}</button>
<clr-tab-content <clr-tab-content
id="content1" id="content1"
*clrIfActive="index === 0" *clrIfActive="index === 0"
class="d-flex clr-justify-content-center w-100" class="d-flex clr-justify-content-center w-100"
> >
<clr-datagrid> <clr-datagrid>
<clr-dg-column>Name</clr-dg-column> <ng-container *ngFor="let col of tab.colsToDisplay">
<clr-dg-column>Value</clr-dg-column> <clr-dg-column>{{ col.colName || col.colKey }}</clr-dg-column>
</ng-container>
<clr-dg-row *ngFor="let info of dsmeta.dsmeta"> <clr-dg-row
<clr-dg-cell>{{ info.NAME }}</clr-dg-cell> (click)="tab.onRowClick ? tab.onRowClick(info) : ''"
<clr-dg-cell>{{ info.VALUE }}</clr-dg-cell> class="clickable-row"
*ngFor="let info of tab.meta"
>
<ng-container *ngFor="let col of tab.colsToDisplay">
<clr-dg-cell>{{ info[col.colKey] }}</clr-dg-cell>
</ng-container>
</clr-dg-row> </clr-dg-row>
</clr-datagrid> </clr-datagrid>
</clr-tab-content> </clr-tab-content>

View File

@ -13,4 +13,23 @@
} }
} }
} }
}
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

@ -7,8 +7,8 @@ import {
Output, Output,
SimpleChanges SimpleChanges
} from '@angular/core' } from '@angular/core'
import { DSMeta } from 'src/app/models/sas/editors-getdata.model' import { DSMeta, Version } from 'src/app/models/sas/editors-getdata.model'
import { DSMetaGroupped } from './models/dsmeta-groupped.model' import { Tab } from './models/dsmeta-groupped.model'
@Component({ @Component({
selector: 'app-dataset-info', selector: 'app-dataset-info',
@ -18,10 +18,15 @@ import { DSMetaGroupped } from './models/dsmeta-groupped.model'
export class DatasetInfoComponent implements OnInit, OnChanges { export class DatasetInfoComponent implements OnInit, OnChanges {
@Input() open: boolean = false @Input() open: boolean = false
@Input() dsmeta: DSMeta[] = [] @Input() dsmeta: DSMeta[] = []
@Input() versions: Version[] = []
@Output() openChange = new EventEmitter<boolean>() @Output() openChange = new EventEmitter<boolean>()
@Output() rowClicked = new EventEmitter<Version | DSMeta>()
dsmetaGroupped: DSMetaGroupped[] = [] dsmetaTabs: Tab<DSMeta>[] = []
versionsTabs: Tab<Version>[] = []
tabs: Tab<DSMeta | Version>[] = []
constructor() {} constructor() {}
@ -30,28 +35,58 @@ export class DatasetInfoComponent implements OnInit, OnChanges {
ngOnChanges(changes: SimpleChanges): void { ngOnChanges(changes: SimpleChanges): void {
if (changes.dsmeta?.currentValue?.length > 0) { if (changes.dsmeta?.currentValue?.length > 0) {
this.parseDSMeta() this.parseDSMeta()
this.parseVersions()
this.tabs = [...[...this.dsmetaTabs], ...[...this.versionsTabs]]
} }
} }
parseDSMeta() { parseDSMeta() {
this.dsmetaGroupped = [] this.dsmetaTabs = []
for (let info of this.dsmeta) { for (let info of this.dsmeta) {
let groupIndex = this.dsmetaGroupped.findIndex( let groupIndex = this.dsmetaTabs.findIndex(
(x) => x.group === info.ODS_TABLE (x) => x.name === info.ODS_TABLE
) )
if (groupIndex < 0) if (groupIndex < 0)
groupIndex = groupIndex =
this.dsmetaGroupped.push({ this.dsmetaTabs.push({
group: info.ODS_TABLE, name: info.ODS_TABLE,
dsmeta: [] title: 'Dataset Meta',
colsToDisplay: [{ colKey: 'NAME' }, { colKey: 'VALUE' }],
meta: [],
onRowClick: (value: DSMeta) => {
this.rowClicked.emit(value)
}
}) - 1 }) - 1
this.dsmetaGroupped[groupIndex].dsmeta.push(info) this.dsmetaTabs[groupIndex].meta.push(info)
} }
} }
parseVersions() {
this.versionsTabs = [
{
name: 'VERSIONS',
title: 'Dataset Meta',
colsToDisplay: [
{ colKey: 'LOAD_REF' },
{ colKey: 'USER_NM' },
{ colKey: 'VERSION_DTTM' },
{ colKey: 'NEW_RECORDS', colName: 'ADD' },
{ colKey: 'CHANGED_RECORDS', colName: 'MOD' },
{ colKey: 'DELETED_RECORDS', colName: 'DEL' },
{ colKey: 'VERSION_DESC' }
],
meta: this.versions,
onRowClick: (value: Version) => {
this.rowClicked.emit(value)
}
}
]
}
onOpenChange(open: boolean) { onOpenChange(open: boolean) {
this.open = open this.open = open
this.openChange.emit(open) this.openChange.emit(open)

View File

@ -1,6 +1,14 @@
import { DSMeta } from 'src/app/models/sas/editors-getdata.model' export interface Tab<T> {
name: string
export interface DSMetaGroupped { title: string
group: string /**
dsmeta: DSMeta[] * Columns to be displayed in the the grid
* If empty, all columns will be displayed
*/
colsToDisplay: {
colKey: string
colName?: string
}[]
meta: T[]
onRowClick?: (value: any) => void
} }

View File

@ -2,6 +2,7 @@
<clr-tree-node *ngIf="libraryList" class="search-node"> <clr-tree-node *ngIf="libraryList" class="search-node">
<div class="tree-search-wrapper"> <div class="tree-search-wrapper">
<input <input
appStealFocus
clrInput clrInput
#searchLibTreeInput #searchLibTreeInput
placeholder="Libraries" placeholder="Libraries"
@ -45,6 +46,7 @@
<clr-tree-node *ngIf="library['tables']" class="search-node"> <clr-tree-node *ngIf="library['tables']" class="search-node">
<div class="tree-search-wrapper"> <div class="tree-search-wrapper">
<input <input
appStealFocus
clrInput clrInput
#searchTreeInput #searchTreeInput
placeholder="Tables" placeholder="Tables"
@ -91,7 +93,7 @@
" "
class="clr-treenode-link" class="clr-treenode-link"
[class.dc-locked-control]="tableLocked" [class.dc-locked-control]="tableLocked"
[class.table-active]="libTabActive(library.LIBRARYREF, libTable)" [class.active]="libTabActive(library.LIBRARYREF, libTable)"
> >
<ng-container [ngSwitch]="libTable.includes('-FC')"> <ng-container [ngSwitch]="libTable.includes('-FC')">
<clr-icon *ngSwitchCase="true" shape="bolt"></clr-icon> <clr-icon *ngSwitchCase="true" shape="bolt"></clr-icon>
@ -114,6 +116,7 @@
<clr-tree-node *ngIf="libTable['columns']" class="search-node"> <clr-tree-node *ngIf="libTable['columns']" class="search-node">
<div class="tree-search-wrapper"> <div class="tree-search-wrapper">
<input <input
appStealFocus
clrInput clrInput
#searchTreeInput #searchTreeInput
placeholder="Columns" placeholder="Columns"

View File

@ -20,6 +20,7 @@ import { parseColType } from './utils/parseColType'
import { dqValidate } from './validations/dq-validation' import { dqValidate } from './validations/dq-validation'
import { specialMissingNumericValidator } from './validations/hot-custom-validators' import { specialMissingNumericValidator } from './validations/hot-custom-validators'
import { applyNumericFormats } from './utils/applyNumericFormats' import { applyNumericFormats } from './utils/applyNumericFormats'
import { CustomAutocompleteEditor } from './editors/numericAutocomplete'
export class DcValidator { export class DcValidator {
private rules: DcValidation[] = [] private rules: DcValidation[] = []
@ -38,6 +39,8 @@ export class DcValidator {
dqData: DQData[], dqData: DQData[],
hotInstance?: Handsontable hotInstance?: Handsontable
) { ) {
this.registerCustomEditors()
this.sasparams = sasparams this.sasparams = sasparams
this.hotInstance = hotInstance this.hotInstance = hotInstance
this.rules = parseColType(sasparams.COLTYPE) this.rules = parseColType(sasparams.COLTYPE)
@ -51,6 +54,13 @@ export class DcValidator {
this.setupValidations() this.setupValidations()
} }
registerCustomEditors() {
Handsontable.editors.registerEditor(
'autocomplete.custom',
CustomAutocompleteEditor
)
}
getRules(): DcValidation[] { getRules(): DcValidation[] {
return this.rules return this.rules
} }
@ -262,6 +272,7 @@ export class DcValidator {
if (source.length > 0) { if (source.length > 0) {
this.rules[i].source = source this.rules[i].source = source
this.rules[i].type = 'autocomplete' this.rules[i].type = 'autocomplete'
this.rules[i].editor = 'autocomplete.custom'
this.rules[i].filter = false this.rules[i].filter = false
} }
@ -315,7 +326,10 @@ export class DcValidator {
// Because of dynamic cell validation, that will change the type of cell to dropdown // Because of dynamic cell validation, that will change the type of cell to dropdown
// `rules[i].colType` could be different type (eg. numeric). So we check if current cell is dropdown, to call HOT native dropdown validator // `rules[i].colType` could be different type (eg. numeric). So we check if current cell is dropdown, to call HOT native dropdown validator
if (this.editor === 'autocomplete') { if (
this.editor === 'autocomplete' ||
this.editor === 'autocomplete.custom'
) {
self self
.getHandsontableValidator('autocomplete') .getHandsontableValidator('autocomplete')
.call(this, value, (valid: boolean) => { .call(this, value, (valid: boolean) => {

View File

@ -0,0 +1,28 @@
import Handsontable from 'handsontable'
import Core from 'handsontable/core'
export class CustomAutocompleteEditor extends Handsontable.editors
.AutocompleteEditor {
constructor(instance: Core) {
super(instance)
}
createElements() {
super.createElements()
}
// Listbox open
open(event?: Event | undefined): void {
super.open(event)
if (this.isCellNumeric()) {
this.htContainer.classList.add('numericListbox')
} else {
this.htContainer.classList.remove('numericListbox')
}
}
isCellNumeric() {
return this.cellProperties?.className?.includes('htNumeric')
}
}

View File

@ -11,6 +11,8 @@ $clr-green: #60b515;
height: $clr-header-height; height: $clr-header-height;
display: flex; display: flex;
align-items: center; align-items: center;
height: 100%;
margin-right: 10px;
.spinner { .spinner {
vertical-align: middle; vertical-align: middle;

View File

@ -1,5 +1,7 @@
@import '../../../colors.scss';
.sideBarProps { .sideBarProps {
background: #314351!important; background: $headerBackground !important;
color: #e0e0e0; color: #e0e0e0;
} }

View File

@ -16,8 +16,6 @@
*ngFor="let programLog of sasjsRequests; let i = index" *ngFor="let programLog of sasjsRequests; let i = index"
[id]="'request_' + i" [id]="'request_' + i"
[clrStackViewLevel]="1" [clrStackViewLevel]="1"
[clrStackViewSetsize]="3"
[clrStackViewPosinset]="3"
> >
<clr-stack-label> <clr-stack-label>
{{ programLog.serviceLink }} {{ programLog.serviceLink }}

View File

@ -70,7 +70,6 @@
border-radius: 3px; border-radius: 3px;
border: 1px solid #e2e2e2; border: 1px solid #e2e2e2;
background-color: #fbfbfb;
height: 48vh; height: 48vh;
overflow: auto; overflow: auto;

View File

@ -8,7 +8,7 @@ import { LoadingIndicatorComponent } from './loading-indicator/loading-indicator
import { LoginComponent } from './login/login.component' import { LoginComponent } from './login/login.component'
import { UserService } from './user.service' import { UserService } from './user.service'
import { AlertsService } from './alerts/alerts.service' import { AlertsService } from './alerts/alerts.service'
import { UserNavDropdownComponent } from './user-nav-dropdown/user-nav-dropdown.component' import { HeaderActions } from './user-nav-dropdown/header-actions.component'
import { AlertsComponent } from './alerts/alerts.component' import { AlertsComponent } from './alerts/alerts.component'
import { TermsComponent } from './terms/terms.component' import { TermsComponent } from './terms/terms.component'
import { DirectivesModule } from '../directives/directives.module' import { DirectivesModule } from '../directives/directives.module'
@ -26,7 +26,7 @@ import { ContactLinkComponent } from './contact-link/contact-link.component'
declarations: [ declarations: [
LoadingIndicatorComponent, LoadingIndicatorComponent,
LoginComponent, LoginComponent,
UserNavDropdownComponent, HeaderActions,
AlertsComponent, AlertsComponent,
TermsComponent, TermsComponent,
DatasetInfoComponent, DatasetInfoComponent,
@ -35,7 +35,7 @@ import { ContactLinkComponent } from './contact-link/contact-link.component'
exports: [ exports: [
LoadingIndicatorComponent, LoadingIndicatorComponent,
LoginComponent, LoginComponent,
UserNavDropdownComponent, HeaderActions,
AlertsComponent, AlertsComponent,
TermsComponent, TermsComponent,
DatasetInfoComponent, DatasetInfoComponent,

View File

@ -1,5 +1,10 @@
$sidebarWidth: 272px; $sidebarWidth: 272px;
.clr-vertical-nav .nav-link.active {
background-color: transparent;
padding-left: 5px;
}
clr-vertical-nav { clr-vertical-nav {
width: $sidebarWidth; width: $sidebarWidth;
min-width: $sidebarWidth; min-width: $sidebarWidth;
@ -7,7 +12,6 @@ clr-vertical-nav {
max-width: 375px; max-width: 375px;
position: relative; position: relative;
box-shadow: 0 0 5px rgba(0, 0, 0, 0.12); box-shadow: 0 0 5px rgba(0, 0, 0, 0.12);
background-color: white !important;
.sun-dropdown { .sun-dropdown {
min-height: 36px min-height: 36px

View File

@ -1,6 +1,6 @@
.input-val { .input-val {
border: 0px; border: 0px;
background: #fbf8f8; background: transparent;
border-bottom: 1px solid #999999; border-bottom: 1px solid #999999;
} }

View File

@ -19,8 +19,6 @@
.card-block { .card-block {
flex: 1; flex: 1;
overflow: auto; overflow: auto;
background-color: #f4f4f4;
} }
} }

View File

@ -0,0 +1,90 @@
<div class="header-actions">
<div class="d-flex clr-align-items-center">
<cds-icon
*ngIf="!darkMode.value"
(click)="toggleDarkMode(true)"
size="md"
class="theme-toggle-icon cursor-pointer"
shape="moon"
inverse="false"
solid="false"
></cds-icon>
<cds-icon
*ngIf="!!darkMode.value"
(click)="toggleDarkMode(false)"
size="md"
class="theme-toggle-icon cursor-pointer"
shape="sun"
inverse="false"
solid="false"
></cds-icon>
</div>
<app-loading-indicator></app-loading-indicator>
<clr-dropdown class="app-nav-dropdown">
<button class="nav-text color-white" clrDropdownToggle>
<span>{{ userName }}</span>
<span *ngIf="userName !== 'Not logged in' && isViya"
><img class="avatar-img" src="{{ getPictureUrl() }}" alt=""
/></span>
<span
class="badge badge-danger"
*ngIf="!sasjsConfig.debug"
[class.hidden]="failedReqs.length === 0"
>{{ failedReqs.length }}</span
>
<span
class="badge badge-info"
*ngIf="sasjsConfig.debug"
[class.hidden]="debugLogs.length === 0"
>{{ debugLogs.length }}</span
>
<clr-icon *ngIf="!isViya" shape="caret down"></clr-icon>
</button>
<clr-dropdown-menu clrPosition="bottom-right" *clrIfOpen>
<div #dropdownItemDebug class="debug-switch-item" clrDropdownItem>
<clr-toggle-container
class="toggle-switch"
(click)="onDebugRowClick($event, dropdownItemDebug)"
>
<clr-toggle-wrapper>
<input
id="debug-toggle1"
type="checkbox"
[(ngModel)]="sasjsConfig.debug"
(ngModelChange)="onDebugModeChange()"
clrToggle
/>
<label>Debug Mode</label>
</clr-toggle-wrapper>
</clr-toggle-container>
</div>
<a (click)="openRequestsModal()" clrDropdownItem>
<span>SAS Requests</span>
</a>
<ng-container *ngIf="!isDeployPage">
<a
target="_blank"
href="https://docs.datacontroller.io"
clrDropdownItem
>
<span class="dropdown-text">Documentation</span>
</a>
</ng-container>
<div class="separator"></div>
<a href="..." routerLink="/system" clrDropdownItem>
<span>System</span>
</a>
<a href="..." (click)="logout($event)" clrDropdownItem>
<span>Log Out</span>
<clr-icon class="clr-logout" shape="logout"></clr-icon>
</a>
<div class="copyRight">
<span>v{{ commitVer }}</span>
</div>
</clr-dropdown-menu>
</clr-dropdown>
</div>

View File

@ -1,17 +1,12 @@
// it must be a better way to read clarity variables...
//@import '../../../../node_modules/@clr/ui/src/utils/helpers.clarity';
//@import '../../../../node_modules/@clr/ui/src/color/utils/colors.clarity';
//@import '../../../../node_modules/@clr/ui/src/color/utils/contrast-cache.clarity';
//@import '../../../../node_modules/@clr/ui/src/color/utils/helpers.clarity';
//@import '../../../../node_modules/@clr/ui/src/utils/variables.clarity';
$clr-header-height: 3rem; $clr-header-height: 3rem;
$clr-near-white: #fafafa; $clr-near-white: #fafafa;
$clr-dark-gray: #565656; $clr-dark-gray: #565656;
$clr-light-gray: #eee; $clr-light-gray: #eee;
:host {
display: contents;
}
.copyRight { .copyRight {
margin-top: 10px; margin-top: 10px;
@ -70,9 +65,6 @@ $clr-light-gray: #eee;
height: 45px; height: 45px;
line-height: 40px; line-height: 40px;
span.dropdown-text{
color: $clr-dark-gray;
}
.badge { .badge {
position: absolute; position: absolute;

View File

@ -8,11 +8,11 @@ import { EventService } from '../../services/event.service'
import { Router } from '@angular/router' import { Router } from '@angular/router'
@Component({ @Component({
selector: 'app-user-nav-dropdown', selector: 'app-header-actions',
templateUrl: './user-nav-dropdown.component.html', templateUrl: './header-actions.component.html',
styleUrls: ['./user-nav-dropdown.component.scss'] styleUrls: ['./header-actions.component.scss']
}) })
export class UserNavDropdownComponent implements OnInit, OnDestroy { export class HeaderActions implements OnInit, OnDestroy {
public userName: string = 'Not logged in' public userName: string = 'Not logged in'
private reqSub: Subscription = new Subscription() private reqSub: Subscription = new Subscription()
private userSub: Subscription = new Subscription() private userSub: Subscription = new Subscription()
@ -26,6 +26,7 @@ export class UserNavDropdownComponent implements OnInit, OnDestroy {
public sasjsConfig: SASjsConfig = new SASjsConfig() public sasjsConfig: SASjsConfig = new SASjsConfig()
public requestsCount: number = 0 public requestsCount: number = 0
public commitVer: string = '' public commitVer: string = ''
public darkMode = this.eventService.darkMode
constructor( constructor(
private userService: UserService, private userService: UserService,
@ -51,6 +52,10 @@ export class UserNavDropdownComponent implements OnInit, OnDestroy {
this.userSub.unsubscribe() this.userSub.unsubscribe()
} }
toggleDarkMode(value: boolean) {
this.eventService.toggleDarkMode(value)
}
openRequestsModal() { openRequestsModal() {
this.eventService.openRequestsModal() this.eventService.openRequestsModal()
} }

View File

@ -1,121 +0,0 @@
<clr-dropdown class="app-nav-dropdown d-md-block">
<button class="nav-text color-white" clrDropdownToggle>
<span>{{ userName }}</span>
<span *ngIf="userName !== 'Not logged in' && isViya"
><img class="avatar-img" src="{{ getPictureUrl() }}" alt=""
/></span>
<span
class="badge badge-danger"
*ngIf="!sasjsConfig.debug"
[class.hidden]="failedReqs.length === 0"
>{{ failedReqs.length }}</span
>
<span
class="badge badge-info"
*ngIf="sasjsConfig.debug"
[class.hidden]="debugLogs.length === 0"
>{{ debugLogs.length }}</span
>
<clr-icon *ngIf="!isViya" shape="caret down"></clr-icon>
</button>
<clr-dropdown-menu clrPosition="bottom-right" *clrIfOpen>
<div #dropdownItemDebug class="debug-switch-item" clrDropdownItem>
<clr-toggle-container
class="toggle-switch"
(click)="onDebugRowClick($event, dropdownItemDebug)"
>
<clr-toggle-wrapper>
<input
id="debug-toggle1"
type="checkbox"
[(ngModel)]="sasjsConfig.debug"
(ngModelChange)="onDebugModeChange()"
clrToggle
/>
<label>Debug Mode</label>
</clr-toggle-wrapper>
</clr-toggle-container>
</div>
<a (click)="openRequestsModal()" clrDropdownItem>
<span>SAS Requests</span>
</a>
<ng-container *ngIf="!isDeployPage">
<a target="_blank" href="https://docs.datacontroller.io" clrDropdownItem>
<span class="dropdown-text">Documentation</span>
</a>
</ng-container>
<div class="separator"></div>
<a href="..." routerLink="/system" clrDropdownItem>
<span>System</span>
</a>
<a href="..." (click)="logout($event)" clrDropdownItem>
<span>Log Out</span>
<clr-icon class="clr-logout" shape="logout"></clr-icon>
</a>
<div class="copyRight">
<span>v{{ commitVer }}</span>
</div>
</clr-dropdown-menu>
</clr-dropdown>
<div class="content-container h-auto">
<nav class="sidenav d-block d-md-none" [clr-nav-level]="2">
<section class="sidenav-content">
<a href="..." class="nav-link active">
{{ userName }}
</a>
<div>
<form>
<div class="toggle-switch">
<input
id="debug-toggle2"
type="checkbox"
[(ngModel)]="sasjsConfig.debug"
(ngModelChange)="onDebugModeChange()"
[ngModelOptions]="{ standalone: true }"
/>
<label
for="debug-toggle2"
class="debug-toggle-label color-dark-gray"
>Debug Mode</label
>
</div>
</form>
</div>
<!-- <a href="..." class="nav-link d-block" [routerLink]="['/application-logs']">
<span>Application Logs</span>
<span class="badge" *ngIf="appLogs.length > 0">{{appLogs.length}}</span>
</a>
<a *ngIf="debugMode" class="nav-link d-block" href="..." [routerLink]="['/debug-logs']">
<span>Debug Logs</span>
<span class="badge badge-info" *ngIf="debugLogs.length > 0">{{debugLogs.length}}</span>
</a>
<a *ngIf="!debugMode" class="nav-link d-block" href="..." [routerLink]="['/failed-requests']">
<span>Failed Requests</span>
<span class="badge badge-danger" *ngIf="failedReqs.length > 0">{{failedReqs.length}}</span>
</a>
<a href="..." class="nav-link d-block" [routerLink]="['/errors']">
<span>Errors</span>
<span class="badge badge-warning" *ngIf="sasErrors.length > 0">{{sasErrors.length}}</span>
</a> -->
<a
class="nav-link d-block"
target="_blank"
href="https://docs.datacontroller.io"
>
<span>Documentation</span>
</a>
<div class="separator"></div>
<a routerLink="/system" class="nav-link d-block">
<span>System</span>
<clr-icon shape="logout"></clr-icon>
</a>
<a href="..." class="nav-link d-block" (click)="logout($event)">
<span>Log Out</span>
<clr-icon shape="logout"></clr-icon>
</a>
</section>
</nav>
</div>

View File

@ -389,6 +389,7 @@
*ngIf="viewboxTableIndex > -1" *ngIf="viewboxTableIndex > -1"
[hotId]="'hotInstance_viewbox_' + viewbox.id" [hotId]="'hotInstance_viewbox_' + viewbox.id"
id="hotTable" id="hotTable"
className="htDark"
[readOnly]="true" [readOnly]="true"
[modifyColWidth]="maxWidthCheker" [modifyColWidth]="maxWidthCheker"
[copyPaste]="viewboxTables[viewboxTableIndex].hotTable.copyPaste" [copyPaste]="viewboxTables[viewboxTableIndex].hotTable.copyPaste"

View File

@ -38,6 +38,10 @@ dc-tree {
.add-new { .add-new {
width: 50%; width: 50%;
padding-right: 5px; padding-right: 5px;
> p {
margin-bottom: 1px;
}
} }
.viewbox-limit-notice { .viewbox-limit-notice {
@ -45,6 +49,38 @@ dc-tree {
color: #E74C3C 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 { .currently-open {
width: 50%; width: 50%;
@ -55,10 +91,6 @@ dc-tree {
&.selected { &.selected {
background: #3c85002e; background: #3c85002e;
} }
&:hover {
background: #e8e8e8;
}
} }
} }
@ -180,7 +212,6 @@ dc-tree {
.cols-list { .cols-list {
border: solid 1px #ccc; border: solid 1px #ccc;
min-height: 60px; min-height: 60px;
background: white;
border-radius: 4px; border-radius: 4px;
overflow: hidden; overflow: hidden;
display: block; display: block;
@ -199,7 +230,6 @@ dc-tree {
justify-content: space-between; justify-content: space-between;
box-sizing: border-box; box-sizing: border-box;
cursor: move; cursor: move;
background: white;
font-size: 14px; font-size: 14px;
&.search:focus { &.search:focus {

View File

@ -433,6 +433,8 @@ export class ViewboxesComponent implements OnInit, AfterViewInit, OnDestroy {
viewboxTable!.hotTable.headerPks.indexOf(column) > -1 viewboxTable!.hotTable.headerPks.indexOf(column) > -1
if (isPKCol) th.classList.add('primaryKeyHeaderStyle') if (isPKCol) th.classList.add('primaryKeyHeaderStyle')
// Dark mode
th.classList.add('darkTH')
} }
}) })
hotInstance?.render() hotInstance?.render()
@ -1076,7 +1078,7 @@ export class ViewboxesComponent implements OnInit, AfterViewInit, OnDestroy {
const hotInstance = this.getViewboxHotInstance(viewboxId) const hotInstance = this.getViewboxHotInstance(viewboxId)
if (hotInstance) { if (hotInstance) {
const column = hotInstance.propToCol(colProp) const column = hotInstance.propToCol(colProp) as number
const plugin = hotInstance.getPlugin('manualColumnMove') const plugin = hotInstance.getPlugin('manualColumnMove')
plugin.moveColumn(column, finalIndex) plugin.moveColumn(column, finalIndex)
@ -1141,7 +1143,7 @@ export class ViewboxesComponent implements OnInit, AfterViewInit, OnDestroy {
if (hotInstance) { if (hotInstance) {
const columns = colProps.map((prop: string) => { const columns = colProps.map((prop: string) => {
return hotInstance.propToCol(prop) return hotInstance.propToCol(prop) as number
}) })
hotInstance.updateSettings({ hotInstance.updateSettings({

View File

@ -16,7 +16,7 @@
</div> </div>
</div> </div>
<div class="clr-row" ng-if="loaded"> <div class="clr-row" ng-if="loaded">
<div class="card-block clr-col-md-6"> <div class="card-block clr-col-md-6 no-borders">
<div class="card-header py-5"> <div class="card-header py-5">
<div class="card-title text-center">Basic Submitted Details</div> <div class="card-title text-center">Basic Submitted Details</div>
</div> </div>
@ -51,41 +51,65 @@
</span> </span>
</div> </div>
</div> </div>
<div class="clr-col-md-6 card-block"> <div class="clr-col-md-6 card-block no-borders">
<div class="card-header py-5"> <div class="card-header py-5">
<div class="card-title text-center">Actions</div> <div class="card-title text-center">Actions</div>
</div> </div>
<div class="mt-20"> <div class="mt-20">
<div class="row"> <div class="row">
<button <button
class="btn btn-sm btn-outline text-center mt-20" class="btn btn-sm btn-outline text-center mr-5i"
(click)="viewerTableScreen()" (click)="viewerTableScreen()"
[disabled]="revertingChanges"
> >
Go to base table screen View base table
</button> </button>
<button <button
*ngIf="!(tableDetails?.['ALLOW_RESTORE'] === 'YES')"
id="approval-btn" id="approval-btn"
class="btn btn-sm btn-success-outline text-center mt-20" class="btn btn-sm btn-success-outline text-center mr-5i"
[disabled]=" [disabled]="
tableDetails?.REVIEW_STATUS_ID === 'APPROVED' || tableDetails?.REVIEW_STATUS_ID === 'APPROVED' ||
tableDetails?.REVIEW_STATUS_ID === 'REJECTED' tableDetails?.REVIEW_STATUS_ID === 'REJECTED'
" "
(click)="approveTableScreen()" (click)="approveTableScreen()"
[disabled]="revertingChanges"
> >
Go to approvals screen Approve
</button> </button>
<button <button
class="btn btn-sm btn-info-outline text-center mt-20" class="btn btn-sm btn-info-outline text-center mr-5i"
(click)="goBack()" (click)="goBack()"
[disabled]="revertingChanges"
> >
Go back to editor Edit base table
</button> </button>
<button <button
class="btn btn-sm btn-success text-center mt-20 min-w-0" class="btn btn-sm btn-success text-center mr-5i min-w-0"
(click)="download(tableDetails?.TABLE_ID)" (click)="download(tableDetails?.TABLE_ID)"
> >
<clr-icon shape="download"></clr-icon> <clr-icon shape="download"></clr-icon>
</button> </button>
<clr-tooltip>
<button
*ngIf="tableDetails?.['ALLOW_RESTORE'] === 'YES'"
(click)="revertChanges()"
clrTooltipTrigger
[clrLoading]="revertingChanges"
class="btn btn-sm btn-danger text-center mt-20"
>
REVERT
<clr-tooltip-content
clrPosition="bottom-left"
clrSize="lg"
*clrIfOpen
>
<span> Revert this and all subsequent changes </span>
</clr-tooltip-content>
</button>
</clr-tooltip>
</div> </div>
</div> </div>
</div> </div>
@ -94,12 +118,14 @@
<hot-table <hot-table
hotId="hotInstance" hotId="hotInstance"
id="hotTable" id="hotTable"
className="htDark"
[data]="hotTable.data" [data]="hotTable.data"
[colHeaders]="hotTable.colHeaders" [colHeaders]="hotTable.colHeaders"
[columns]="hotTable.columns" [columns]="hotTable.columns"
[maxRows]="hotTable.maxRows" [maxRows]="hotTable.maxRows"
[height]="hotTable.height" [height]="hotTable.height"
[licenseKey]="hotTable.licenseKey" [licenseKey]="hotTable.licenseKey"
[afterGetColHeader]="hotTable.afterGetColHeader"
stretchH="all" stretchH="all"
[cells]="hotTable.cells" [cells]="hotTable.cells"
> >

View File

@ -12,4 +12,10 @@
cursor:pointer; cursor:pointer;
margin-top:10px; margin-top:10px;
color: #007cbb; color: #007cbb;
}
::ng-deep body[cds-theme="dark"] {
.baseTableLink {
color: #4ec0ff;
}
} }

View File

@ -4,10 +4,10 @@ import { Router } from '@angular/router'
import { ActivatedRoute } from '@angular/router' import { ActivatedRoute } from '@angular/router'
import { SasService } from '../services/sas.service' import { SasService } from '../services/sas.service'
import { EventService } from '../services/event.service' import { EventService } from '../services/event.service'
import { AppService } from '../services/app.service'
import { HotTableInterface } from '../models/HotTable.interface' import { HotTableInterface } from '../models/HotTable.interface'
import { LicenceService } from '../services/licence.service' import { LicenceService } from '../services/licence.service'
import { globals } from '../_globals' import { globals } from '../_globals'
import { EditorsRestoreServiceResponse } from '../models/sas/editors-restore.model'
@Component({ @Component({
selector: 'app-stage', selector: 'app-stage',
@ -23,6 +23,7 @@ export class StageComponent implements OnInit {
public keysArray: any public keysArray: any
public tableDetails: any public tableDetails: any
public loaded: boolean = false public loaded: boolean = false
public revertingChanges: boolean = false
public licenceState = this.licenceService.licenceState public licenceState = this.licenceService.licenceState
public hotTable: HotTableInterface = { public hotTable: HotTableInterface = {
data: [], data: [],
@ -31,7 +32,11 @@ export class StageComponent implements OnInit {
height: 500, height: 500,
settings: {}, settings: {},
licenseKey: undefined, licenseKey: undefined,
maxRows: this.licenceState.value.stage_rows_allowed || Infinity maxRows: this.licenceState.value.stage_rows_allowed || Infinity,
afterGetColHeader: (column, th, headerLevel) => {
// Dark mode
th.classList.add('darkTH')
}
} }
constructor( constructor(
@ -162,6 +167,31 @@ export class StageComponent implements OnInit {
} }
} }
revertChanges() {
this.revertingChanges = true
const data = {
restore_in: [
{
load_ref: this.table_id
}
]
}
this.sasService
.request('editors/restore', data)
.then((res: EditorsRestoreServiceResponse) => {
if (res.restore_out) {
this.route.navigate([`/stage`]).then(() => {
this.route.navigate([`/stage/${res.restore_out[0].LOADREF}`])
})
}
})
.finally(() => {
this.revertingChanges = false
})
}
private setFocus() { private setFocus() {
setTimeout(() => { setTimeout(() => {
let approvalBtn: any = window.document.getElementById('approval-btn') let approvalBtn: any = window.document.getElementById('approval-btn')

View File

@ -6,6 +6,7 @@ export interface EnvironmentInfo {
MEMSIZE: string MEMSIZE: string
SYSPROCESSMODE: string SYSPROCESSMODE: string
SYSHOSTNAME: string SYSHOSTNAME: string
SYSUSERID: string
SYSHOSTINFOLONG: string SYSHOSTINFOLONG: string
SYSENCODING: string SYSENCODING: string
AUTOEXEC: string AUTOEXEC: string

View File

@ -9,86 +9,94 @@
class="sys-info d-flex clr-justify-content-center clr-flex-column clr-flex-lg-row" class="sys-info d-flex clr-justify-content-center clr-flex-column clr-flex-lg-row"
> >
<div> <div>
<h6 class="m-0">Environment Details <span class="dark"></span></h6> <h6 cds-text="subsection" class="mb-10">
<p class="m-0"> Environment Details <span class="dark"></span>
</h6>
<p cds-text="label" class="m-0">
SYSSITE: <span class="dark">{{ environmentInfo?.SYSSITE }}</span> SYSSITE: <span class="dark">{{ environmentInfo?.SYSSITE }}</span>
</p> </p>
<p class="m-0"> <p cds-text="label" class="m-0">
SYSSCPL: <span class="dark">{{ environmentInfo?.SYSSCPL }}</span> SYSSCPL: <span class="dark">{{ environmentInfo?.SYSSCPL }}</span>
</p> </p>
<p class="m-0"> <p cds-text="label" class="m-0">
SYSTCPIPHOSTNAME: SYSTCPIPHOSTNAME:
<span class="dark">{{ environmentInfo?.SYSTCPIPHOSTNAME }}</span> <span class="dark">{{ environmentInfo?.SYSTCPIPHOSTNAME }}</span>
</p> </p>
<p class="m-0"> <p cds-text="label" class="m-0">
SYSVLONG: <span class="dark">{{ environmentInfo?.SYSVLONG }}</span> SYSVLONG: <span class="dark">{{ environmentInfo?.SYSVLONG }}</span>
</p> </p>
<p class="m-0"> <p cds-text="label" class="m-0">
MEMSIZE: <span class="dark">{{ environmentInfo?.MEMSIZE }}</span> MEMSIZE: <span class="dark">{{ environmentInfo?.MEMSIZE }}</span>
</p> </p>
<p class="m-0"> <p cds-text="label" class="m-0">
SYSPROCESSMODE: SYSPROCESSMODE:
<span class="dark">{{ environmentInfo?.SYSPROCESSMODE }}</span> <span class="dark">{{ environmentInfo?.SYSPROCESSMODE }}</span>
</p> </p>
<p class="m-0"> <p cds-text="label" class="m-0">
SYSHOSTNAME: SYSHOSTNAME:
<span class="dark">{{ environmentInfo?.SYSHOSTNAME }}</span> <span class="dark">{{ environmentInfo?.SYSHOSTNAME }}</span>
</p> </p>
<p class="m-0"> <p cds-text="label" class="m-0">
SYSUSERID:
<span class="dark">{{ environmentInfo?.SYSUSERID }}</span>
</p>
<p cds-text="label" class="m-0">
SYSHOSTINFOLONG: SYSHOSTINFOLONG:
<span class="dark">{{ environmentInfo?.SYSHOSTINFOLONG }}</span> <span class="dark">{{ environmentInfo?.SYSHOSTINFOLONG }}</span>
</p> </p>
<p class="m-0"> <p cds-text="label" class="m-0">
SYSENCODING: SYSENCODING:
<span class="dark">{{ environmentInfo?.SYSENCODING }}</span> <span class="dark">{{ environmentInfo?.SYSENCODING }}</span>
</p> </p>
<p class="m-0"> <p cds-text="label" class="m-0">
AUTOEXEC: <span class="dark">{{ environmentInfo?.AUTOEXEC }}</span> AUTOEXEC: <span class="dark">{{ environmentInfo?.AUTOEXEC }}</span>
</p> </p>
<p class="m-0">
DC ADMIN GROUP:
<span class="dark">{{ environmentInfo?.DC_ADMIN_GROUP }}</span>
</p>
</div> </div>
<div class="d-flex clr-justify-content-lg-center"> <div class="d-flex clr-justify-content-lg-center">
<div> <div>
<h6 class="m-0"> <h6 cds-text="subsection" class="mb-10">
Data Controller Details <span class="dark"></span> Data Controller Details <span class="dark"></span>
</h6> </h6>
<p class="m-0"> <p cds-text="label" class="m-0">
Application version: Application version:
<span class="dark">{{ appInfo.appVersion }}</span> <span class="dark">{{ appInfo.appVersion }}</span>
</p> </p>
<p class="m-0"> <p cds-text="label" class="m-0">
Build timestamp: Build timestamp:
<span class="dark">{{ appInfo.buildTimestamp }}</span> <span class="dark">{{ appInfo.buildTimestamp }}</span>
</p> </p>
<p class="m-0"> <p cds-text="label" class="m-0">
Adapter version: Adapter version:
<span class="dark">{{ appInfo.adapterVersion }}</span> <span class="dark">{{ appInfo.adapterVersion }}</span>
</p> </p>
<p class="m-0"> <p cds-text="label" class="m-0">
HTTP: <span class="dark">{{ http ? 'YES' : 'NO' }}</span> HTTP: <span class="dark">{{ http ? 'YES' : 'NO' }}</span>
</p> </p>
<p cds-text="label" class="m-0">
DC Admin Group:
<span class="dark">{{ environmentInfo?.DC_ADMIN_GROUP }}</span>
</p>
</div> </div>
</div> </div>
<div> <div>
<h6 class="m-0">Licence details <span class="dark"></span></h6> <h6 cds-text="subsection" class="mb-10">
<p class="m-0"> Licence details <span class="dark"></span>
</h6>
<p cds-text="label" class="m-0">
Valid until: Valid until:
<span class="dark">{{ licenceInfo?.valid_until }}</span> <span class="dark">{{ licenceInfo?.valid_until }}</span>
</p> </p>
<p class="m-0"> <p cds-text="label" class="m-0">
Users allowed: Users allowed:
<span class="dark">{{ licenceInfo?.users_allowed }}</span> <span class="dark">{{ licenceInfo?.users_allowed }}</span>
</p> </p>
<p class="m-0"> <p cds-text="label" class="m-0">
Site IDs: Site IDs:
<span class="dark">{{ licenceInfo?.site_id_multiple }}</span> <span class="dark">{{ licenceInfo?.site_id_multiple }}</span>
</p> </p>
<p class="m-0"> <p cds-text="label" class="m-0">
Free Tier: Free Tier:
<span class="dark">{{ licenceInfo?.demo ? 'YES' : 'NO' }}</span> <span class="dark">{{ licenceInfo?.demo ? 'YES' : 'NO' }}</span>
</p> </p>
@ -157,25 +165,25 @@
licenceState.value.lineage_daily_limit licenceState.value.lineage_daily_limit
}}</span> }}</span>
</p> </p>
<p class="m-0"> <p cds-text="label" class="m-0">
Viewboxes: Viewboxes:
<span class="dark">{{ <span class="dark">{{
licenceState.value.viewbox ? 'YES' : 'NO' licenceState.value.viewbox ? 'YES' : 'NO'
}}</span> }}</span>
</p> </p>
<p class="m-0"> <p cds-text="label" class="m-0">
File Upload: File Upload:
<span class="dark">{{ <span class="dark">{{
licenceState.value.fileUpload ? 'YES' : 'NO' licenceState.value.fileUpload ? 'YES' : 'NO'
}}</span> }}</span>
</p> </p>
<p class="m-0"> <p cds-text="label" class="m-0">
Edit record: Edit record:
<span class="dark">{{ <span class="dark">{{
licenceState.value.editRecord ? 'YES' : 'NO' licenceState.value.editRecord ? 'YES' : 'NO'
}}</span> }}</span>
</p> </p>
<p class="m-0"> <p cds-text="label" class="m-0">
Add record: Add record:
<span class="dark">{{ <span class="dark">{{
licenceState.value.addRecord ? 'YES' : 'NO' licenceState.value.addRecord ? 'YES' : 'NO'
@ -186,6 +194,16 @@
<hr class="w-100 light" /> <hr class="w-100 light" />
<!-- Keep the logic in case we in future have more then 2 themes (light, dark) -->
<!-- -->
<!-- <div class="user-action">
Keep selected theme after reload
<clr-checkbox-wrapper>
<input [(ngModel)]="settings.persistSelectedTheme" (change)="settingChange($event)" value="persistDarkMode" type="checkbox" clrToggle />
</clr-checkbox-wrapper>
</div> -->
<ng-container *ngIf="environmentInfo?.ISADMIN === 1"> <ng-container *ngIf="environmentInfo?.ISADMIN === 1">
<div *ngIf="serverType === 'SAS9'" class="admin-action"> <div *ngIf="serverType === 'SAS9'" class="admin-action">
Refresh Data Lineage Refresh Data Lineage

View File

@ -12,7 +12,7 @@
} }
} }
.admin-action { .admin-action, .user-action {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
@ -27,7 +27,8 @@
} }
.dark { .dark {
color: black; font-weight: bold;
margin-left: 5px;
} }
@media (max-width: 993px) { @media (max-width: 993px) {

View File

@ -7,6 +7,8 @@ import { LicenceService } from '../services/licence.service'
import { SasService } from '../services/sas.service' import { SasService } from '../services/sas.service'
import { AppInfo } from './models/app-info.model' import { AppInfo } from './models/app-info.model'
import { EnvironmentInfo } from './models/environment-info.model' import { EnvironmentInfo } from './models/environment-info.model'
import { AppSettingsService } from '../services/app-settings.service'
import { AppSettings } from '../models/AppSettings'
@Component({ @Component({
selector: 'app-system', selector: 'app-system',
@ -36,25 +38,36 @@ export class SystemComponent implements OnInit {
Infinity = Infinity Infinity = Infinity
licenceState = this.licenceService.licenceState licenceState = this.licenceService.licenceState
settings: AppSettings
constructor( constructor(
private appService: AppService, private appService: AppService,
private sasService: SasService, private sasService: SasService,
private licenceService: LicenceService private licenceService: LicenceService,
private appSettingsService: AppSettingsService
) { ) {
this.serverType = this.sasService.getServerType() this.serverType = this.sasService.getServerType()
this.licenceInfo = this.licenceService.getLicenseKeyData() this.licenceInfo = this.licenceService.getLicenseKeyData()
this.environmentInfo = this.appService.getEnvironmentInfo() this.environmentInfo = this.appService.getEnvironmentInfo()
this.settings = this.appSettingsService.settings.value
if (this.environmentInfo) { if (this.environmentInfo) {
this.environmentInfo.AUTOEXEC = decodeURIComponent( this.environmentInfo.AUTOEXEC = decodeURIComponent(
this.environmentInfo.AUTOEXEC this.environmentInfo.AUTOEXEC
) )
} }
this.appSettingsService.settings.subscribe((settings: AppSettings) => {
this.settings = settings
})
} }
ngOnInit(): void {} ngOnInit(): void {}
settingChange(event: Event) {
this.appSettingsService.setAppSettings(this.settings)
}
downloadConfiguration() { downloadConfiguration() {
let sasjsConfig = this.sasService.getSasjsConfig() let sasjsConfig = this.sasService.getSasjsConfig()
let storage = sasjsConfig.serverUrl let storage = sasjsConfig.serverUrl

View File

@ -4,9 +4,10 @@ import { CommonModule } from '@angular/common'
import { SystemRoutingModule } from './system-routing.module' import { SystemRoutingModule } from './system-routing.module'
import { SystemComponent } from './system.component' import { SystemComponent } from './system.component'
import { ClarityModule } from '@clr/angular' import { ClarityModule } from '@clr/angular'
import { FormsModule } from '@angular/forms'
@NgModule({ @NgModule({
declarations: [SystemComponent], declarations: [SystemComponent],
imports: [CommonModule, SystemRoutingModule, ClarityModule] imports: [CommonModule, SystemRoutingModule, ClarityModule, FormsModule]
}) })
export class SystemModule {} export class SystemModule {}

View File

@ -3,6 +3,7 @@
<clr-tree-node *ngIf="users" class="search-node"> <clr-tree-node *ngIf="users" class="search-node">
<div class="tree-search-wrapper"> <div class="tree-search-wrapper">
<input <input
appStealFocus
clrInput clrInput
#searchLibTreeInput #searchLibTreeInput
placeholder="Filter by Users" placeholder="Filter by Users"
@ -26,7 +27,7 @@
<clr-tree-node <clr-tree-node
(click)="userOnClick(user)" (click)="userOnClick(user)"
*ngIf="!user['hidden']" *ngIf="!user['hidden']"
[class.table-active]="userInfo?.URI === user.URI" [class.active]="userInfo?.URI === user.URI"
> >
<p class="m-0 cursor-pointer list-padding"> <p class="m-0 cursor-pointer list-padding">
<clr-icon shape="user"></clr-icon> <clr-icon shape="user"></clr-icon>

View File

@ -1,24 +1,61 @@
@import '../../colors.scss';
.sidebar-height{ .sidebar-height{
height: 100%; height: 100%;
} }
.user-info-text{ .user-info-text{
display: inline; display: inline;
font-size: 20px; font-size: 20px;
} }
::ng-deep body[cds-theme="dark"] {
.user-info {
background-color: $headerBackground;
border-color: $headerBackground;
}
.user-data {
background-color: $headerBackground;
border-color: $headerBackground;
}
.user-table tbody{
tr:hover{
background-color: #29404b;
}
}
}
::ng-deep body[cds-theme="light"] {
.user-info {
background-color: #f9f9f9;
border-color: #a7a7a7;
box-shadow: 0px 2px 5px #dad7d7;
}
.user-data {
background-color: #f9f9f9;
border-color: #a7a7a7;
box-shadow: 0px 2px 5px #dad7d7;
}
.user-table tbody{
tr:hover{
background-color: #e6e6e6;
}
}
}
.user-info{ .user-info{
background-color: #f9f9f9; border: 1px solid;
border: 1px solid #a7a7a7;
border-radius: 3px; border-radius: 3px;
box-shadow: 0px 2px 5px #dad7d7;
} }
.user-info td{ .user-info td{
text-align: center; text-align: center;
} }
.user-data{ .user-data{
background-color: #f9f9f9; border: 1px solid;
border: 1px solid #a7a7a7;
border-radius: 3px; border-radius: 3px;
box-shadow: 0px 2px 5px #dad7d7;
} }
.user-data{ .user-data{
min-height: auto; min-height: auto;
@ -28,7 +65,6 @@
} }
.user-table{ .user-table{
background-color: #f9f9f9;
width: 100%; width: 100%;
} }
.user-table thead{ .user-table thead{
@ -36,7 +72,6 @@
} }
.user-table tbody{ .user-table tbody{
tr:hover{ tr:hover{
background-color: #e6e6e6;
cursor: pointer; cursor: pointer;
} }
} }

View File

@ -7,6 +7,7 @@
<clr-tree-node *ngIf="libraries" class="search-node"> <clr-tree-node *ngIf="libraries" class="search-node">
<div class="tree-search-wrapper"> <div class="tree-search-wrapper">
<input <input
appStealFocus
clrInput clrInput
#searchLibTreeInput #searchLibTreeInput
[(ngModel)]="librariesSearch" [(ngModel)]="librariesSearch"
@ -50,6 +51,7 @@
<clr-tree-node *ngIf="library['tables']" class="search-node"> <clr-tree-node *ngIf="library['tables']" class="search-node">
<div class="tree-search-wrapper"> <div class="tree-search-wrapper">
<input <input
appStealFocus
clrInput clrInput
#searchTreeInput #searchTreeInput
[id]="'search_' + library.LIBRARYREF" [id]="'search_' + library.LIBRARYREF"
@ -91,7 +93,7 @@
(click)="!tableLocked ? onTableClick(libTable, library) : ''" (click)="!tableLocked ? onTableClick(libTable, library) : ''"
class="clr-treenode-link" class="clr-treenode-link"
[class.dc-locked-control]="tableLocked" [class.dc-locked-control]="tableLocked"
[class.table-active]="libTabActive(library.LIBRARYREF, libTable)" [class.active]="libTabActive(library.LIBRARYREF, libTable)"
> >
<ng-container [ngSwitch]="libTable.includes('-FC')"> <ng-container [ngSwitch]="libTable.includes('-FC')">
<clr-icon *ngSwitchCase="true" shape="bolt"></clr-icon> <clr-icon *ngSwitchCase="true" shape="bolt"></clr-icon>
@ -99,15 +101,18 @@
</ng-container> </ng-container>
{{ libTable.replace('-FC', '') }} {{ libTable.replace('-FC', '') }}
</button> </button>
<clr-tooltip-content
clrPosition="bottom-right" <ng-container *ngIf="tableLocked">
clrSize="lg" <clr-tooltip-content
*clrIfOpen clrPosition="bottom-right"
> clrSize="lg"
<span *ngIf="tableLocked"> *clrIfOpen
To unlock all tables, contact support&#64;datacontroller.io >
</span> <span>
</clr-tooltip-content> To unlock all tables, contact support&#64;datacontroller.io
</span>
</clr-tooltip-content>
</ng-container>
</clr-tooltip> </clr-tooltip>
</clr-tree-node> </clr-tree-node>
</clr-tree-node> </clr-tree-node>
@ -192,7 +197,7 @@
<div class="modal-body web-query"> <div class="modal-body web-query">
<div class="row"> <div class="row">
<div class="clr-col-lg-12 clr-col-md-12 clr-col-sm-12 clr-col-xs-12"> <div class="clr-col-lg-12 clr-col-md-12 clr-col-sm-12 clr-col-xs-12">
<div class="card"> <div class="card no-borders">
<div class="card-header d-flex justify-content-between"> <div class="card-header d-flex justify-content-between">
<span>Copy the below into your preferred client tool:</span> <span>Copy the below into your preferred client tool:</span>
@ -222,15 +227,16 @@
</div> </div>
</div> </div>
</div> </div>
<div class="card-block word-break-all"> <div class="card-block web-query-wrapper word-break-all">
<textarea <textarea
class="web-query-text w-100" clrTextarea
class="web-query-text"
rows="4" rows="4"
cols="50" cols="50"
#cliCommandInput #cliCommandInput
(focus)="onCliCommandFocus($event)" (focus)="onCliCommandFocus($event)"
type="text" type="text"
value="{{ webQueryText }}" [value]="webQueryText"
readonly readonly
> >
</textarea> </textarea>
@ -312,12 +318,15 @@
<div class="loadingSpinner" *ngIf="loadingTableView"> <div class="loadingSpinner" *ngIf="loadingTableView">
<span class="spinner"> Loading... </span> <span class="spinner"> Loading... </span>
<div> <div class="mt-10">
<h4>Loading table viewer</h4> <p cds-text="section">Loading Table Viewer</p>
</div> </div>
</div> </div>
<div class="card h-100 d-flex clr-flex-column" *ngIf="!loadingTableView"> <div
class="card no-borders h-100 d-flex clr-flex-column"
*ngIf="!loadingTableView"
>
<div <div
*ngIf="table" *ngIf="table"
class="header-row clr-row justify-content-between clr-justify-content-center w-100 m-0" class="header-row clr-row justify-content-between clr-justify-content-center w-100 m-0"
@ -358,10 +367,11 @@
</section> </section>
<div class="title-col clr-col-auto clr-flex-column clr-flex-sm-row"> <div class="title-col clr-col-auto clr-flex-column clr-flex-sm-row">
<h3 <p
class="viewerTitle clr-flex-column d-flex clr-flex-sm-row clr-align-items-center clr-justify-content-center" cds-text="section"
class="clr-flex-column d-flex clr-flex-sm-row clr-align-items-center clr-justify-content-center"
> >
<clr-tooltip class="d-flex"> <clr-tooltip class="d-flex clr-align-items-center">
<clr-icon <clr-icon
clrTooltipTrigger clrTooltipTrigger
(click)="datasetInfo = true" (click)="datasetInfo = true"
@ -373,19 +383,22 @@
<clr-icon <clr-icon
*ngIf="tableTitle?.includes('-FC')" *ngIf="tableTitle?.includes('-FC')"
shape="bolt" shape="bolt"
class="color-yellow mt-5 mr-5" class="color-yellow mr-5"
></clr-icon> ></clr-icon>
<span clrTooltipTrigger *ngIf="tableTitle && tableTitle.length > 0"> <span clrTooltipTrigger *ngIf="tableTitle && tableTitle.length > 0">
{{ tableTitle?.replace('-FC', '') }} {{ tableTitle?.replace('-FC', '') }}
</span> </span>
<clr-tooltip-content
clrPosition="bottom-left" <ng-container *ngIf="this.dsNote && this.dsNote.length > 0">
clrSize="lg" <clr-tooltip-content
*clrIfOpen clrPosition="bottom-left"
> clrSize="lg"
{{ this.dsNote }} *clrIfOpen
</clr-tooltip-content> >
{{ this.dsNote }}
</clr-tooltip-content>
</ng-container>
</clr-tooltip> </clr-tooltip>
<ng-container *ngIf="tableTitle && tableTitle.length > 0"> <ng-container *ngIf="tableTitle && tableTitle.length > 0">
@ -401,7 +414,7 @@
shape="refresh" shape="refresh"
></clr-icon> ></clr-icon>
</ng-container> </ng-container>
</h3> </p>
</div> </div>
<div class="options-col clr-col-md"> <div class="options-col clr-col-md">
@ -419,62 +432,34 @@
options options
</button> </button>
<clr-dropdown-menu clrPosition="bottom-right" *clrIfOpen> <clr-dropdown-menu clrPosition="bottom-right" *clrIfOpen>
<button <div (click)="newViewbox()" clrDropdownItem>
type="button"
class="btn btn-sm btn-success-outline"
(click)="newViewbox()"
clrDropdownItem
>
<clr-icon shape="view-cards"></clr-icon> <clr-icon shape="view-cards"></clr-icon>
<span>Viewboxes</span> <span>Viewboxes</span>
</button> </div>
<button <div
type="button"
class="btn btn-sm btn-success-outline"
*ngIf="tableEditExists()" *ngIf="tableEditExists()"
(click)="editTable()" (click)="editTable()"
clrDropdownItem clrDropdownItem
> >
<clr-icon shape="pencil"></clr-icon> <clr-icon shape="pencil"></clr-icon>
<span>Edit</span> <span>Edit</span>
</button> </div>
<button <div *ngIf="tableuri" (click)="goToLineage()" clrDropdownItem>
type="button"
class="btn btn-sm btn-success-outline"
*ngIf="tableuri"
(click)="goToLineage()"
clrDropdownItem
>
<clr-icon shape="switch"></clr-icon> <clr-icon shape="switch"></clr-icon>
<span>Lineage</span> <span>Lineage</span>
</button> </div>
<button <div (click)="openQb()" clrDropdownItem>
type="button"
class="btn btn-sm btn-outline btn-block"
(click)="openQb()"
clrDropdownItem
>
<clr-icon shape="filter"></clr-icon> <clr-icon shape="filter"></clr-icon>
<span>Filter</span> <span>Filter</span>
</button> </div>
<button <div (click)="openDownload = true" clrDropdownItem>
type="button"
class="btn btn-sm btn-success-outline"
(click)="openDownload = true"
clrDropdownItem
>
<clr-icon shape="download"></clr-icon> <clr-icon shape="download"></clr-icon>
<span>Download</span> <span>Download</span>
</button> </div>
<button <div (click)="showWebQuery()" clrDropdownItem>
type="button"
class="btn btn-sm btn-success-outline"
(click)="showWebQuery()"
clrDropdownItem
>
<clr-icon shape="download-cloud"></clr-icon> <clr-icon shape="download-cloud"></clr-icon>
<span>Web Query URL</span> <span>Web Query URL</span>
</button> </div>
</clr-dropdown-menu> </clr-dropdown-menu>
</clr-dropdown> </clr-dropdown>
</div> </div>
@ -500,7 +485,9 @@
size="60" size="60"
class="is-info icon-dc-fill" class="is-info icon-dc-fill"
></clr-icon> ></clr-icon>
<h3 class="text-center color-gray">Please select a library</h3> <h3 class="text-center color-gray mt-10" cds-text="section">
Please select a library
</h3>
</div> </div>
<ng-container *ngIf="!noData && !noDataReqErr && !table && lib"> <ng-container *ngIf="!noData && !noDataReqErr && !table && lib">
@ -511,10 +498,10 @@
class="form-block table-search-wrapper sw clr-col-md" class="form-block table-search-wrapper sw clr-col-md"
></section> ></section>
<div class="title-col clr-col-auto"> <div class="title-col clr-col-auto mt-15 mb-15">
<h3 class="viewerTitle mt-17"> <p cds-text="section">
{{ lib }} {{ lib }}
</h3> </p>
<clr-icon <clr-icon
(click)="reloadLibInfo()" (click)="reloadLibInfo()"
class="refresh-table" class="refresh-table"
@ -538,7 +525,9 @@
size="40" size="40"
class="is-info icon-dc-fill" class="is-info icon-dc-fill"
></clr-icon> ></clr-icon>
<h3 class="text-center color-gray">Please select a table</h3> <p class="text-center color-gray mt-10" cds-text="section">
Please select a table
</p>
</div> </div>
<div *ngIf="libinfo !== null" class="libinfo m-0 clr-row"> <div *ngIf="libinfo !== null" class="libinfo m-0 clr-row">
@ -628,6 +617,7 @@
<hot-table <hot-table
hotId="hotInstance" hotId="hotInstance"
id="hotTable" id="hotTable"
className="htDark"
[multiColumnSorting]="true" [multiColumnSorting]="true"
[viewportRowRenderingOffset]="50" [viewportRowRenderingOffset]="50"
[data]="hotTable.data" [data]="hotTable.data"
@ -643,6 +633,7 @@
[cells]="hotTable.cells" [cells]="hotTable.cells"
[maxRows]="hotTable.maxRows" [maxRows]="hotTable.maxRows"
[manualColumnResize]="true" [manualColumnResize]="true"
[afterGetColHeader]="hotTable.afterGetColHeader"
[rowHeaders]="hotTable.rowHeaders" [rowHeaders]="hotTable.rowHeaders"
[rowHeaderWidth]="hotTable.rowHeaderWidth" [rowHeaderWidth]="hotTable.rowHeaderWidth"
[rowHeights]="hotTable.rowHeights" [rowHeights]="hotTable.rowHeights"
@ -667,6 +658,12 @@
</div> </div>
</div> </div>
<app-dataset-info [(open)]="datasetInfo" [dsmeta]="dsmeta"></app-dataset-info> <app-dataset-info
[(open)]="datasetInfo"
[dsmeta]="dsmeta"
[versions]="versions"
(rowClicked)="datasetInfoModalRowClicked($event)"
>
</app-dataset-info>
<app-viewboxes [(viewboxModal)]="viewboxOpen"></app-viewboxes> <app-viewboxes [(viewboxModal)]="viewboxOpen"></app-viewboxes>

View File

@ -1,3 +1,5 @@
@import '../../colors.scss';
.card { .card {
margin-top: 0; margin-top: 0;
@ -11,8 +13,8 @@ clr-tree-node button {
} }
.toggle-switch input[type=checkbox]:checked+label:before { .toggle-switch input[type=checkbox]:checked+label:before {
border-color: #314351; border-color: $headerBackground;
background-color: #314351!important; background-color: $headerBackground !important;
transition: .15s ease-in; transition: .15s ease-in;
transition-property: border-color,background-color; transition-property: border-color,background-color;
} }
@ -54,11 +56,6 @@ clr-tree-node button {
} }
} }
.viewerTitle {
text-align:center;
margin-bottom: 15px;
}
.dropdown-menu { .dropdown-menu {
width: 180px; width: 180px;
margin-top: -18px; margin-top: -18px;
@ -125,8 +122,14 @@ clr-tree-node button {
hot-table { hot-table {
::ng-deep { ::ng-deep {
.handsontable tbody th.ht__highlight, .handsontable thead th.ht__highlight {
&.primaryKeyHeaderStyle {
background-color: #306b00b0 !important;
}
}
.primaryKeyHeaderStyle { .primaryKeyHeaderStyle {
background: #306b006e; background-color: #306b006e !important;
} }
} }
} }
@ -144,6 +147,45 @@ hot-table {
.web-query-text { .web-query-text {
min-height: 100px; min-height: 100px;
max-height: 100px; max-height: 100px;
width: 100% !important;
}
.web-query-wrapper {
::ng-deep {
.clr-control-container {
width: 100%;
}
clr-textarea-container {
margin: 0 !important;
}
.clr-textarea-wrapper {
margin: 0 !important;
}
}
}
}
.table-search-wrapper {
margin-left: 0;
form {
padding-left: 0;
}
clr-icon {
margin-top: 4px;
}
::ng-deep {
clr-input-container {
margin-top: 0;
}
.clr-control-container {
margin-left: 0;
}
} }
} }

View File

@ -25,7 +25,11 @@ import { FilterGroup, FilterQuery } from '../models/FilterQuery'
import { HotTableInterface } from '../models/HotTable.interface' import { HotTableInterface } from '../models/HotTable.interface'
import { LoggerService } from '../services/logger.service' import { LoggerService } from '../services/logger.service'
import Handsontable from 'handsontable' import Handsontable from 'handsontable'
import { $DataFormats, DSMeta } from '../models/sas/editors-getdata.model' import {
$DataFormats,
DSMeta,
Version
} from '../models/sas/editors-getdata.model'
import { mergeColsRules } from '../shared/dc-validator/utils/mergeColsRules' import { mergeColsRules } from '../shared/dc-validator/utils/mergeColsRules'
import { PublicViewtablesServiceResponse } from '../models/sas/public-viewtables.model' import { PublicViewtablesServiceResponse } from '../models/sas/public-viewtables.model'
import { PublicViewlibsServiceResponse } from '../models/sas/public-viewlibs.model' import { PublicViewlibsServiceResponse } from '../models/sas/public-viewlibs.model'
@ -95,6 +99,7 @@ export class ViewerComponent implements AfterContentInit, AfterViewInit {
public $dataFormats: $DataFormats | null = null public $dataFormats: $DataFormats | null = null
public datasetInfo: boolean = false public datasetInfo: boolean = false
public dsmeta: DSMeta[] = [] public dsmeta: DSMeta[] = []
public versions: Version[] = []
public dsNote = '' public dsNote = ''
public licenceState = this.licenceService.licenceState public licenceState = this.licenceService.licenceState
@ -107,11 +112,14 @@ export class ViewerComponent implements AfterContentInit, AfterViewInit {
height: '100%', height: '100%',
maxRows: this.licenceState.value.viewer_rows_allowed || Infinity, maxRows: this.licenceState.value.viewer_rows_allowed || Infinity,
settings: {}, settings: {},
afterGetColHeader: undefined,
licenseKey: undefined, licenseKey: undefined,
rowHeaders: (index: number) => { rowHeaders: (index: number) => {
return ' ' return ' '
}, },
afterGetColHeader: (col: number, th: any, headerLevel: number) => {
// Dark mode
th.classList.add('darkTH')
},
rowHeaderWidth: 15, rowHeaderWidth: 15,
rowHeights: 20, rowHeights: 20,
contextMenu: ['copy_with_column_headers', 'copy_column_headers_only'], contextMenu: ['copy_with_column_headers', 'copy_column_headers_only'],
@ -247,6 +255,7 @@ export class ViewerComponent implements AfterContentInit, AfterViewInit {
this.hotTable.data = res.viewdata this.hotTable.data = res.viewdata
this.$dataFormats = res.$viewdata this.$dataFormats = res.$viewdata
this.dsmeta = res.dsmeta this.dsmeta = res.dsmeta
this.versions = res.versions || []
this.setDSNote() this.setDSNote()
this.numberOfRows = res.sasparams[0].NOBS this.numberOfRows = res.sasparams[0].NOBS
this.queryText = res.sasparams[0].FILTER_TEXT this.queryText = res.sasparams[0].FILTER_TEXT
@ -805,6 +814,7 @@ export class ViewerComponent implements AfterContentInit, AfterViewInit {
this.hotTable.data = res.viewdata this.hotTable.data = res.viewdata
this.$dataFormats = res.$viewdata this.$dataFormats = res.$viewdata
this.dsmeta = res.dsmeta this.dsmeta = res.dsmeta
this.versions = res.versions || []
this.setDSNote() this.setDSNote()
this.queryText = res.sasparams[0].FILTER_TEXT this.queryText = res.sasparams[0].FILTER_TEXT
let columns: any[] = [] let columns: any[] = []
@ -1019,6 +1029,16 @@ export class ViewerComponent implements AfterContentInit, AfterViewInit {
this.sasStoreService.removeClause() this.sasStoreService.removeClause()
} }
public datasetInfoModalRowClicked(value: Version | DSMeta) {
if ((<Version>value).LOAD_REF !== undefined) {
// Type is Version
const row = value as Version
const url = `/stage/${row.LOAD_REF}`
this.router.navigate([url])
}
}
private setDSNote() { private setDSNote() {
const notes = this.dsmeta.find((item) => item.NAME === 'NOTES') const notes = this.dsmeta.find((item) => item.NAME === 'NOTES')
const longDesc = this.dsmeta.find((item) => item.NAME === 'DD_LONGDESC') const longDesc = this.dsmeta.find((item) => item.NAME === 'DD_LONGDESC')
@ -1054,6 +1074,9 @@ export class ViewerComponent implements AfterContentInit, AfterViewInit {
const isPKCol = column && this.headerPks.indexOf(column) > -1 const isPKCol = column && this.headerPks.indexOf(column) > -1
if (isPKCol) th.classList.add('primaryKeyHeaderStyle') if (isPKCol) th.classList.add('primaryKeyHeaderStyle')
// Dark mode
th.classList.add('darkTH')
} }
}) })
} }

View File

@ -38,6 +38,7 @@
<clr-tree-node *ngIf="endpointLinks.length > 0" class="search-node"> <clr-tree-node *ngIf="endpointLinks.length > 0" class="search-node">
<div class="tree-search-wrapper"> <div class="tree-search-wrapper">
<input <input
appStealFocus
clrInput clrInput
#searchLinksTreeInput #searchLinksTreeInput
placeholder="Search links" placeholder="Search links"

View File

@ -7,6 +7,7 @@
<clr-tree-node class="search-node"> <clr-tree-node class="search-node">
<div class="tree-search-wrapper"> <div class="tree-search-wrapper">
<input <input
appStealFocus
clrInput clrInput
#searchXLMapTreeInput #searchXLMapTreeInput
placeholder="Filter by Id" placeholder="Filter by Id"
@ -32,7 +33,7 @@
<button <button
(click)="xlmapOnClick(xlmap)" (click)="xlmapOnClick(xlmap)"
class="clr-treenode-link" class="clr-treenode-link"
[class.table-active]="isActiveXLMap(xlmap.id)" [class.active]="isActiveXLMap(xlmap.id)"
> >
<clr-icon shape="file"></clr-icon> <clr-icon shape="file"></clr-icon>
{{ xlmap.id }} {{ xlmap.id }}
@ -49,12 +50,20 @@
size="60" size="60"
class="is-info icon-dc-fill" class="is-info icon-dc-fill"
></clr-icon> ></clr-icon>
<h3 *ngIf="xlmaps.length > 0" class="text-center color-gray"> <p
*ngIf="xlmaps.length > 0"
class="text-center color-gray mt-10"
cds-text="section"
>
Please select a map Please select a map
</h3> </p>
<h3 *ngIf="xlmaps.length < 1" class="text-center color-gray"> <p
*ngIf="xlmaps.length < 1"
class="text-center color-gray mt-10"
cds-text="section"
>
No excel map is found No excel map is found
</h3> </p>
</div> </div>
<div class="loadingSpinner" *ngIf="isLoading"> <div class="loadingSpinner" *ngIf="isLoading">
@ -94,13 +103,21 @@
}}</i> }}</i>
<h5 class="d-flex clr-col-12 clr-justify-content-center mt-5-i"> <h5 class="d-flex clr-col-12 clr-justify-content-center mt-5-i">
Rules Source: Rules Source:
<a class="ml-10" [routerLink]="'/view/data/' + rulesSource"> <a
cds-text="labelLink"
class="ml-10"
[routerLink]="'/view/data/' + rulesSource"
>
{{ rulesSource }} {{ rulesSource }}
</a> </a>
</h5> </h5>
<h5 class="d-flex clr-col-12 clr-justify-content-center mt-5-i"> <h5 class="d-flex clr-col-12 clr-justify-content-center mt-5-i">
Target dataset: Target dataset:
<a class="ml-10" [routerLink]="'/view/data/' + selectedXLMap.targetDS"> <a
cds-text="labelLink"
class="ml-10"
[routerLink]="'/view/data/' + selectedXLMap.targetDS"
>
{{ selectedXLMap.targetDS }} {{ selectedXLMap.targetDS }}
</a> </a>
</h5> </h5>
@ -110,6 +127,7 @@
<hot-table <hot-table
hotId="hotInstance" hotId="hotInstance"
id="hot-table" id="hot-table"
className="htDark"
[multiColumnSorting]="true" [multiColumnSorting]="true"
[viewportRowRenderingOffset]="50" [viewportRowRenderingOffset]="50"
[data]="selectedTab === TabsEnum.Rules ? xlmapRules : xlData" [data]="selectedTab === TabsEnum.Rules ? xlmapRules : xlData"
@ -122,6 +140,7 @@
[filters]="true" [filters]="true"
[height]="'100%'" [height]="'100%'"
stretchH="all" stretchH="all"
[afterGetColHeader]="afterGetColHeader"
[modifyColWidth]="maxWidthChecker" [modifyColWidth]="maxWidthChecker"
[cells]="getCellConfiguration" [cells]="getCellConfiguration"
[maxRows]="hotTableMaxRows" [maxRows]="hotTableMaxRows"

View File

@ -117,6 +117,7 @@ export class XLMapComponent implements AfterContentInit, AfterViewInit, OnInit {
data: 'VALUE_TXT' data: 'VALUE_TXT'
} }
] ]
public xlData: XLUploadEntry[] = [] public xlData: XLUploadEntry[] = []
public showUploadModal = false public showUploadModal = false
@ -142,6 +143,11 @@ export class XLMapComponent implements AfterContentInit, AfterViewInit, OnInit {
private sasService: SasService private sasService: SasService
) {} ) {}
public afterGetColHeader(column: number, th: any) {
// Dark mode
th.classList.add('darkTH')
}
public xlmapOnClick(xlmap: XLMapListItem) { public xlmapOnClick(xlmap: XLMapListItem) {
if (xlmap.id !== this.selectedXLMap?.id) { if (xlmap.id !== this.selectedXLMap?.id) {
this.selectedXLMap = xlmap this.selectedXLMap = xlmap

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