4 Commits

Author SHA1 Message Date
b4e5dd74d9 chore: licence checker sync
All checks were successful
Test / Build-production-and-ng-test (push) Successful in 6m27s
Test / Build-and-test-development (push) Successful in 12m0s
Test / Build-and-test-development-latest-adapter (push) Successful in 11m55s
2023-10-12 14:55:22 +02:00
64746b0aae Merge pull request 'sheetjs' (#53) from sheetjs into development
Some checks failed
Test / Build-production-and-ng-test (push) Failing after 3m15s
Test / Build-and-test-development-latest-adapter (push) Has been cancelled
Test / Build-and-test-development (push) Has been cancelled
Reviewed-on: #53
2023-10-12 12:52:31 +00:00
16ae77c804 style: lint
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 1m23s
2023-10-12 14:50:55 +02:00
481c14f066 fix: reverting SheetJS upgrade, it was breaking the excel upload 2023-10-12 14:50:34 +02:00
184 changed files with 13938 additions and 13336 deletions

View File

@ -1,5 +1,5 @@
name: Build name: Build
run-name: Running Lint Check and Licence checker on Pull Request run-name: Running Lint Check
on: [pull_request] on: [pull_request]
jobs: jobs:
@ -8,16 +8,9 @@ jobs:
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- uses: actions/setup-node@v4 - uses: actions/setup-node@v3
with: with:
node-version: 20.14.0 node-version: 18
- 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
@ -25,27 +18,8 @@ jobs:
env: env:
NPMRC: ${{ secrets.NPMRC}} NPMRC: ${{ secrets.NPMRC}}
- name: Lint check - run: npm run lint:check
run: npm run lint:check - run: |
- name: Install dependencies
run: |
cd client cd client
# Decrypt and Install sheet
echo ${{ secrets.SHEET_PWD }} | gpg --batch --yes --passphrase-fd 0 ./libraries/sheet-crypto.tgz.gpg
npm ci npm ci
npm run license-checker
- name: Licence checker
run: |
cd client
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

@ -0,0 +1,226 @@
name: Test
run-name: Building and testing development branch
on:
push:
branches:
- development
jobs:
Build-production-and-ng-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18
- name: Write .npmrc file
run: |
touch client/.npmrc
echo '${{ secrets.NPMRC}}' > client/.npmrc
- name: Install Chrome for Angular tests
run: |
apt-get update
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
apt install -y ./google-chrome*.deb;
export CHROME_BIN=/usr/bin/google-chrome
- name: Write cypress credentials
run: echo "$CYPRESS_CREDS" > ./client/cypress.env.json
shell: bash
env:
CYPRESS_CREDS: ${{ secrets.CYPRESS_CREDS }}
- name: Install dependencies
run: npm ci
- name: Check audit
# Audit should fail and stop the CI if critical vulnerability found
run: |
npm audit --audit-level=critical --omit=dev
cd ./sas
npm audit --audit-level=critical --omit=dev
cd ../client
npm audit --audit-level=critical --omit=dev
- name: Angular Tests
run: |
cd client
npm test -- --no-watch --no-progress --browsers=ChromeHeadlessCI
- name: Angular Production Build
run: |
cd client
npm run postinstall
npm run build
Build-and-test-development:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18
- name: Write .npmrc file
run: |
touch client/.npmrc
echo '${{ secrets.NPMRC}}' > client/.npmrc
- run: apt-get update
- run: wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
- run: apt install -y ./google-chrome*.deb;
- run: export CHROME_BIN=/usr/bin/google-chrome
- run: apt-get update -y
- run: apt-get -y install libgtk2.0-0 libgtk-3-0 libgbm-dev libnotify-dev libgconf-2-4 libnss3 libxss1 libasound2 libxtst6 xauth xvfb
- run: apt -y install jq
- name: Write cypress credentials
run: echo "$CYPRESS_CREDS" > ./client/cypress.env.json
shell: bash
env:
CYPRESS_CREDS: ${{ secrets.CYPRESS_CREDS }}
- name: Install dependencies
run: npm ci
# Install pm2 and prepare SASJS server
- run: npm i -g pm2
- run: curl -L https://github.com/sasjs/server/releases/latest/download/linux.zip > linux.zip
- run: unzip linux.zip
- run: touch .env
- run: echo RUN_TIMES=js >> .env
- run: echo NODE_PATH=node >> .env
- run: echo CORS=enable >> .env
- run: echo WHITELIST=http://localhost:4200 >> .env
- run: cat .env
- run: pm2 start api-linux --wait-ready
- name: Deploy mocked services
run: |
cd ./sas/mocks/sasjs
npm install -g @sasjs/cli
npm install -g replace-in-files-cli
sasjs cbd -t server-ci
# sasjs request services/admin/makedata -t server-ci -d ./deploy/makeData4GL.json -c ./deploy/requestConfig.json -o ./output.json
- name: Install ZIP
run: |
apt-get update
apt-get install zip
- name: Prepare and run frontend and cypress
run: |
cd ./client
mv ./cypress.env.example.json ./cypress.env.json
replace-in-files --regex='"username".*' --replacement='"username":"'${{ secrets.CYPRESS_USERNAME_SASJS }}'",' ./cypress.env.json
replace-in-files --regex='"password".*' --replacement='"password":"'${{ secrets.CYPRESS_PWD_SASJS }}'" ' ./cypress.env.json
cat ./cypress.env.json
npm run postinstall
# Prepare index.html to SASJS local
replace-in-files --regex='serverUrl=".*?"' --replacement='serverUrl="http://localhost:5000"' ./src/index.html
replace-in-files --regex='appLoc=".*?"' --replacement='appLoc="/Public/app/devtest"' ./src/index.html
replace-in-files --regex='serverType=".*?"' --replacement='serverType="SASJS"' ./src/index.html
replace-in-files --regex='"hosturl".*' --replacement='hosturl:"http://localhost:4200",' ./cypress.config.ts
cat ./cypress.config.ts
# Start frontend and run cypress
npm start & npx wait-on http://localhost:4200 && npx cypress run --browser chrome --spec "cypress/e2e/liveness.cy.ts,cypress/e2e/editor.cy.ts,cypress/e2e/excel.cy.ts,cypress/e2e/filtering.cy.ts,cypress/e2e/licensing.cy.ts"
- name: Zip Cypress videos
if: always()
run: |
zip -r cypress-videos ./client/cypress/videos
- name: Cypress videos artifacts
uses: actions/upload-artifact@v3
with:
name: cypress-videos.zip
path: cypress-videos.zip
Build-and-test-development-latest-adapter:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18
- name: Write .npmrc file
run: echo "$NPMRC" > client/.npmrc
shell: bash
env:
NPMRC: ${{ secrets.NPMRC}}
- run: apt-get update
- run: wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
- run: apt install -y ./google-chrome*.deb;
- run: export CHROME_BIN=/usr/bin/google-chrome
- run: apt-get update -y
- run: apt-get -y install libgtk2.0-0 libgtk-3-0 libgbm-dev libnotify-dev libgconf-2-4 libnss3 libxss1 libasound2 libxtst6 xauth xvfb
- run: apt -y install jq
- name: Write cypress credentials
run: echo "$CYPRESS_CREDS" > ./client/cypress.env.json
shell: bash
env:
CYPRESS_CREDS: ${{ secrets.CYPRESS_CREDS }}
- name: Install dependencies
run: npm ci
# Install pm2 and prepare SASJS server
- run: npm i -g pm2
- run: curl -L https://github.com/sasjs/server/releases/latest/download/linux.zip > linux.zip
- run: unzip linux.zip
- run: touch .env
- run: echo RUN_TIMES=js >> .env
- run: echo NODE_PATH=node >> .env
- run: echo CORS=enable >> .env
- run: echo WHITELIST=http://localhost:4200 >> .env
- run: cat .env
- run: pm2 start api-linux --wait-ready
- name: Deploy mocked services
run: |
cd ./sas/mocks/sasjs
npm install -g @sasjs/cli
npm install -g replace-in-files-cli
sasjs cbd -t server-ci
- name: Install ZIP
run: |
apt-get update
apt-get install zip
- name: Prepare and run frontend and cypress
run: |
cd ./client
mv ./cypress.env.example.json ./cypress.env.json
replace-in-files --regex='"username".*' --replacement='"username":"'${{ secrets.CYPRESS_USERNAME_SASJS }}'",' ./cypress.env.json
replace-in-files --regex='"password".*' --replacement='"password":"'${{ secrets.CYPRESS_PWD_SASJS }}'" ' ./cypress.env.json
cat ./cypress.env.json
npm run postinstall
npm install @sasjs/adapter@latest
# Prepare index.html to SASJS local
replace-in-files --regex='serverUrl=".*?"' --replacement='serverUrl="http://localhost:5000"' ./src/index.html
replace-in-files --regex='appLoc=".*?"' --replacement='appLoc="/Public/app/devtest"' ./src/index.html
replace-in-files --regex='serverType=".*?"' --replacement='serverType="SASJS"' ./src/index.html
replace-in-files --regex='"hosturl".*' --replacement='hosturl:"http://localhost:4200",' ./cypress.config.ts
cat ./cypress.config.ts
# Start frontend and run cypress
npm start & npx wait-on http://localhost:4200 && npx cypress run --browser chrome --spec "cypress/e2e/liveness.cy.ts,cypress/e2e/editor.cy.ts,cypress/e2e/excel.cy.ts,cypress/e2e/filtering.cy.ts,cypress/e2e/licensing.cy.ts"
- name: Zip Cypress videos
if: always()
run: |
zip -r cypress-videos ./client/cypress/videos
- name: Cypress videos artifacts
uses: actions/upload-artifact@v3
with:
name: cypress-videos-latest-adapter.zip
path: cypress-videos.zip

View File

@ -1,164 +1,18 @@
name: Release name: Release
run-name: Testing and Releasing DC run-name: Releasing DC
on: on:
push: push:
branches: branches:
- main - main
jobs: jobs:
Build-production-and-ng-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v4
with:
node-version: 20.14.0
- name: Write .npmrc file
run: |
touch client/.npmrc
echo '${{ secrets.NPMRC}}' > client/.npmrc
- name: Install Chrome for Angular tests
run: |
apt-get update
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
apt install -y ./google-chrome*.deb;
export CHROME_BIN=/usr/bin/google-chrome
- name: Write cypress credentials
run: echo "$CYPRESS_CREDS" > ./client/cypress.env.json
shell: bash
env:
CYPRESS_CREDS: ${{ secrets.CYPRESS_CREDS }}
- 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: Check audit
# Audit should fail and stop the CI if critical vulnerability found
run: |
npm audit --audit-level=critical --omit=dev
cd ./sas
npm audit --audit-level=critical --omit=dev
cd ../client
npm audit --audit-level=critical --omit=dev
- name: Angular Tests
run: |
cd client
npm run test:headless
- name: Angular Production Build
run: |
cd client
npm run postinstall
npm run build
Build-and-test-development:
runs-on: ubuntu-latest
needs: Build-production-and-ng-test
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v4
with:
node-version: 20.14.0
- name: Write .npmrc file
run: |
touch client/.npmrc
echo '${{ secrets.NPMRC}}' > client/.npmrc
- run: apt-get update
- run: wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
- run: apt install -y ./google-chrome*.deb;
- run: export CHROME_BIN=/usr/bin/google-chrome
- run: apt-get update -y
- run: apt-get -y install libgtk2.0-0 libgtk-3-0 libgbm-dev libnotify-dev libgconf-2-4 libnss3 libxss1 libasound2 libxtst6 xauth xvfb
- run: apt -y install jq
- name: Write cypress credentials
run: echo "$CYPRESS_CREDS" > ./client/cypress.env.json
shell: bash
env:
CYPRESS_CREDS: ${{ secrets.CYPRESS_CREDS }}
- 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
# Install pm2 and prepare SASJS server
- run: npm i -g pm2
- run: curl -L https://github.com/sasjs/server/releases/latest/download/linux.zip > linux.zip
- run: unzip linux.zip
- run: touch .env
- run: echo RUN_TIMES=js >> .env
- run: echo NODE_PATH=node >> .env
- run: echo CORS=enable >> .env
- run: echo WHITELIST=http://localhost:4200 >> .env
- run: cat .env
- run: pm2 start api-linux --wait-ready
- name: Deploy mocked services
run: |
cd ./sas/mocks/sasjs
npm install -g @sasjs/cli
npm install -g replace-in-files-cli
sasjs cbd -t server-ci
# sasjs request services/admin/makedata -t server-ci -d ./deploy/makeData4GL.json -c ./deploy/requestConfig.json -o ./output.json
- name: Install ZIP
run: |
apt-get update
apt-get install zip
- name: Prepare and run frontend and cypress
run: |
cd ./client
mv ./cypress.env.example.json ./cypress.env.json
replace-in-files --regex='"username".*' --replacement='"username":"'${{ secrets.CYPRESS_USERNAME_SASJS }}'",' ./cypress.env.json
replace-in-files --regex='"password".*' --replacement='"password":"'${{ secrets.CYPRESS_PWD_SASJS }}'" ' ./cypress.env.json
cat ./cypress.env.json
npm run postinstall
# Prepare index.html to SASJS local
replace-in-files --regex='serverUrl=".*?"' --replacement='serverUrl="http://localhost:5000"' ./src/index.html
replace-in-files --regex='appLoc=".*?"' --replacement='appLoc="/Public/app/devtest"' ./src/index.html
replace-in-files --regex='serverType=".*?"' --replacement='serverType="SASJS"' ./src/index.html
replace-in-files --regex='"hosturl".*' --replacement='hosturl:"http://localhost:4200",' ./cypress.config.ts
cat ./cypress.config.ts
# Start frontend and run cypress
npm start & npx wait-on http://localhost:4200 && npx cypress run --browser chrome --spec "cypress/e2e/liveness.cy.ts,cypress/e2e/editor.cy.ts,cypress/e2e/excel.cy.ts,cypress/e2e/filtering.cy.ts,cypress/e2e/licensing.cy.ts"
- name: Zip Cypress videos
if: always()
run: |
zip -r cypress-videos ./client/cypress/videos
- name: Add cypress videos artifacts
if: always()
uses: actions/upload-artifact@v3
with:
name: cypress-videos.zip
path: cypress-videos.zip
release: release:
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [Build-production-and-ng-test, Build-and-test-development]
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- uses: actions/setup-node@v4 - uses: actions/setup-node@v3
with: with:
node-version: 20.14.0 node-version: 18
- name: Write .npmrc file - name: Write .npmrc file
run: | run: |
@ -176,19 +30,6 @@ jobs:
npm i -g @sasjs/cli npm i -g @sasjs/cli
# jq is used to parse the release JSON # jq is used to parse the release JSON
apt-get install jq -y apt-get install jq -y
# doxygen is used for the SASJS docs
apt-get update
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: |
@ -202,6 +43,7 @@ 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
@ -245,7 +87,6 @@ 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: |
@ -261,12 +102,6 @@ jobs:
npm run compodoc:build npm run compodoc:build
surfer put --token ${{ secrets.TSDOC_TOKEN }} --server webdoc.datacontroller.io documentation/* / surfer put --token ${{ secrets.TSDOC_TOKEN }} --server webdoc.datacontroller.io documentation/* /
- name: Release code.datacontroller.io
run: |
cd sas
sasjs doc
surfer put --token ${{ secrets.CODE_DATACONTROLLER_IO }} --server code.datacontroller.io sasjsbuild/sasdocs/* /
- name: Upload assets to release - name: Upload assets to release
run: | run: |
RELEASE_ID=`curl -k 'https://git.datacontroller.io/api/v1/repos/dc/dc/releases/latest?access_token=${{ secrets.RELEASE_TOKEN }}' | jq -r '.id'` RELEASE_ID=`curl -k 'https://git.datacontroller.io/api/v1/repos/dc/dc/releases/latest?access_token=${{ secrets.RELEASE_TOKEN }}' | jq -r '.id'`
@ -281,4 +116,3 @@ 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,9 +11,6 @@ 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

35
.vscode/settings.json vendored
View File

@ -1,19 +1,18 @@
{ {
"cSpell.words": [ "cSpell.words": [
"Licence", "SYSERRORTEXT",
"SYSERRORTEXT", "SYSWARNINGTEXT"
"SYSWARNINGTEXT", ],
"xlmaprules", "editor.rulers": [
"xlmaps" 80
], ],
"editor.rulers": [80], "files.trimTrailingWhitespace": true,
"files.trimTrailingWhitespace": true, "[markdown]": {
"[markdown]": { "files.trimTrailingWhitespace": false
"files.trimTrailingWhitespace": false },
}, "workbench.colorCustomizations": {
"workbench.colorCustomizations": { "titleBar.activeForeground": "#ebe8e8",
"titleBar.activeForeground": "#ebe8e8", "titleBar.activeBackground": "#95ff0053",
"titleBar.activeBackground": "#95ff0053" },
}, "terminal.integrated.wordSeparators": " ()[]{}',\"`─‘’"
"terminal.integrated.wordSeparators": " ()[]{}',\"`─‘’" }
}

View File

@ -1,253 +1,3 @@
# [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)
### Bug Fixes
* excel with commas getting wrapped in quotes ([3860134](https://git.datacontroller.io/dc/dc/commit/38601346a529cfe3787bb286a639e0293c365020))
## [6.6.1](https://git.datacontroller.io/dc/dc/compare/v6.6.0...v6.6.1) (2024-02-19)
### Bug Fixes
* **client:** bumped @sasjs/adapter with fixed redirected login ([eb1c09d](https://git.datacontroller.io/dc/dc/commit/eb1c09d7909ba07faf763da261545dc1efaec1b3))
# [6.6.0](https://git.datacontroller.io/dc/dc/compare/v6.5.2...v6.6.0) (2024-02-12)
### Bug Fixes
* adjust the col numbers in extracted data ([cff5989](https://git.datacontroller.io/dc/dc/commit/cff598955930d2581349e5c6e8b2dd3f9ac96b4c))
### Features
* extra table metadata for [#75](https://git.datacontroller.io/dc/dc/issues/75) ([837821f](https://git.datacontroller.io/dc/dc/commit/837821fd01477d340524dfdaf8dd3d3758cf3095))
* show dsnote on hover title ([6565834](https://git.datacontroller.io/dc/dc/commit/6565834ad4089ecf2de39967e6ed6f217ee4a0a5))
## [6.5.2](https://git.datacontroller.io/dc/dc/compare/v6.5.1...v6.5.2) (2024-02-06)
### Bug Fixes
* ordering mpe_selectbox data by the data values after selectbox_order ([2b54034](https://git.datacontroller.io/dc/dc/commit/2b5403497317632a4be8a00f21455c036f1e6461))
## [6.5.1](https://git.datacontroller.io/dc/dc/compare/v6.5.0...v6.5.1) (2024-02-02)
### Bug Fixes
* ensuring submitter email can be pulled from mpe_emails ([eac0104](https://git.datacontroller.io/dc/dc/commit/eac0104d7aebaf98ff1d1c504c1ce3b25d4a0ce8))
# [6.5.0](https://git.datacontroller.io/dc/dc/compare/v6.4.0...v6.5.0) (2024-01-26)
### Features
* filtering by reference to Variables as well as Values ([6eb1aa8](https://git.datacontroller.io/dc/dc/commit/6eb1aa85d29294d63e6af377e622fbed7fd1fab8))
# [6.4.0](https://git.datacontroller.io/dc/dc/compare/v6.3.1...v6.4.0) (2024-01-24)
### Bug Fixes
* add dcLib to globals ([5d93346](https://git.datacontroller.io/dc/dc/commit/5d93346b52eda27c2829770e96686a713296d373))
* add service to get xlmap rules and fixed interface name ([9ffa30a](https://git.datacontroller.io/dc/dc/commit/9ffa30ab747f5b62acbd452431a5e6e440afcb80))
* increasing length of mpe_excel_map cols to ([2d4d068](https://git.datacontroller.io/dc/dc/commit/2d4d068413dcdac98581f08939e74bde65b73428))
* providing info on mapids to FE ([fd94945](https://git.datacontroller.io/dc/dc/commit/fd94945466c1a797ddc89815258a65624a9cb0cf))
* removing tables from EDIT menu that are in xlmaps ([9550ae4](https://git.datacontroller.io/dc/dc/commit/9550ae4d1154a0272f8a2427ac9d2afdfd699c96))
* removing XLMAP_TARGETLIBDS from mpe_xlmaps_rules table ([93702c6](https://git.datacontroller.io/dc/dc/commit/93702c63dc280cdba1e46f0fd8fe0deaec879611))
* renaming TABLE macvar to LOAD_REF in postdata.sas ([01915a2](https://git.datacontroller.io/dc/dc/commit/01915a2db9a4dfb94e4e8213e2c32181da36d349))
* reverting xlmap in getdata change ([2d6e747](https://git.datacontroller.io/dc/dc/commit/2d6e747db9b84e9fb0dfcf9102a2f7dd2cb51891))
* update edit tab to load ([516e5a2](https://git.datacontroller.io/dc/dc/commit/516e5a206216f79ab1dce9f4eab0d31115743160))
### Features
* adding ability to define the target table for excel maps ([c86fba9](https://git.datacontroller.io/dc/dc/commit/c86fba9dc75ddc6033132f469ad1c31b9131b12e))
* adding ismap attribute to getdata response (and fixing test) ([2702bb3](https://git.datacontroller.io/dc/dc/commit/2702bb3c84c45903def1aa2b8cc20a6dd080281b))
* Complex Excel Uploads ([cf19381](https://git.datacontroller.io/dc/dc/commit/cf193810606f287b8d6f864c4eb64d43c5ab5f3c)), closes [#69](https://git.datacontroller.io/dc/dc/issues/69)
* Create Tables / Files dropdown under load tab ([b473b19](https://git.datacontroller.io/dc/dc/commit/b473b198a61f468dff74cd8e64692e7847084a80))
* display list of maps in sidebar ([5aec024](https://git.datacontroller.io/dc/dc/commit/5aec0242429942f8a989b5fb79f8d3865e9de01a))
* implemented the logic for xlmap component ([50696bb](https://git.datacontroller.io/dc/dc/commit/50696bb926dd00472db65a008771a4b6352871be))
* model changes for [#69](https://git.datacontroller.io/dc/dc/issues/69) ([271543a](https://git.datacontroller.io/dc/dc/commit/271543a446a2116718f99f0540e3cd911f9f5fe7))
* new getxlmaps service to return rules for a particular xlmap_id ([56264ec](https://git.datacontroller.io/dc/dc/commit/56264ecc6908bf6c8e3e666dfeba7068d6195df8))
* validating the excel map after stage (adding load-ref) ([a485c3b](https://git.datacontroller.io/dc/dc/commit/a485c3b78724a36f7bacb264fb02140cc62d6512))
## [6.3.1](https://git.datacontroller.io/dc/dc/compare/v6.3.0...v6.3.1) (2024-01-01)
### Bug Fixes
* enabling excel uploads to tables with retained keys, also adding more validation to MPE_TABLES updates ([3efccc4](https://git.datacontroller.io/dc/dc/commit/3efccc4cf3752763d049836724f2491c287f65db))
# [6.3.0](https://git.datacontroller.io/dc/dc/compare/v6.2.8...v6.3.0) (2023-12-04)
### Features
* viewer row handle ([dadac4f](https://git.datacontroller.io/dc/dc/commit/dadac4f13f85b5446198b6340cad28844defc94d))
## [6.2.8](https://git.datacontroller.io/dc/dc/compare/v6.2.7...v6.2.8) (2023-12-04)
### Bug Fixes
* bumping sasjs/core to fix mp_loadformat issue ([a1d308e](https://git.datacontroller.io/dc/dc/commit/a1d308ea078786b27bf7ec940d018fc657d4c398))
* new logic for -fc suffix. Closes [#63](https://git.datacontroller.io/dc/dc/issues/63) ([5579db0](https://git.datacontroller.io/dc/dc/commit/5579db0eafc668b1bc310099b7cc3062e0598fc4))
## [6.2.7](https://git.datacontroller.io/dc/dc/compare/v6.2.6...v6.2.7) (2023-11-09)
### Bug Fixes
* **audit:** updated crypto-js (hashing rows in dynamic cell validation) ([a7aa42a](https://git.datacontroller.io/dc/dc/commit/a7aa42a59b71597399924b8d2d06010c806321f3))
* missing dependency and avoiding label length limit issue ([91f128c](https://git.datacontroller.io/dc/dc/commit/91f128c2fead1e4f72267d689e67f49ec9a2ab35))
## [6.2.6](https://git.datacontroller.io/dc/dc/compare/v6.2.5...v6.2.6) (2023-10-18)
### Bug Fixes
* bumping core to address mm_assigndirectlib issue ([c27cdab](https://git.datacontroller.io/dc/dc/commit/c27cdab3fccbde814a29424d0344173a73ea816c))
## [6.2.5](https://git.datacontroller.io/dc/dc/compare/v6.2.4...v6.2.5) (2023-10-17)
### Bug Fixes
* enabling AUTHDOMAIN in MM_ASSIGNDIRECTLIB ([008b45a](https://git.datacontroller.io/dc/dc/commit/008b45ad175ec0e6026f5ef3bc210470226e328f))
## [6.2.4](https://git.datacontroller.io/dc/dc/compare/v6.2.3...v6.2.4) (2023-10-16)
### Bug Fixes
* Enable display of metadata-only tables. Closes [#56](https://git.datacontroller.io/dc/dc/issues/56) ([f3e82b4](https://git.datacontroller.io/dc/dc/commit/f3e82b4ee2a9c1c851f812ac60e9eaf05f91a0f9))
## [6.2.3](https://git.datacontroller.io/dc/dc/compare/v6.2.2...v6.2.3) (2023-10-12)
### Bug Fixes
* bumping core library to avoid non-ascii char in mp_validatecols.sas. [#50](https://git.datacontroller.io/dc/dc/issues/50) ([11b06f6](https://git.datacontroller.io/dc/dc/commit/11b06f6416300b6d70b1570c415d5a5c004976db))
* removing copyright symbol from mpe_alerts macro. [#50](https://git.datacontroller.io/dc/dc/issues/50) ([adb7eb7](https://git.datacontroller.io/dc/dc/commit/adb7eb77550c68a2dab15a6ff358129820e9b612))
## [6.2.2](https://git.datacontroller.io/dc/dc/compare/v6.2.1...v6.2.2) (2023-10-09) ## [6.2.2](https://git.datacontroller.io/dc/dc/compare/v6.2.1...v6.2.2) (2023-10-09)

View File

@ -27,6 +27,4 @@ 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,7 +45,6 @@
"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",
@ -68,6 +67,7 @@
"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": {
"buildTarget": "datacontroller:build:production" "browserTarget": "datacontroller:build:production"
}, },
"development": { "development": {
"buildTarget": "datacontroller:build:development" "browserTarget": "datacontroller:build:development"
} }
}, },
"defaultConfiguration": "development" "defaultConfiguration": "development"
@ -127,27 +127,30 @@
"extract-i18n": { "extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n", "builder": "@angular-devkit/build-angular:extract-i18n",
"options": { "options": {
"buildTarget": "datacontroller:build" "browserTarget": "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"
], ],
"tsConfig": "tsconfig.spec.json", "styles": [
"inlineStyleLanguage": "scss", "src/styles.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,8 +9,6 @@ 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', 'Approve') .should('contain', 'Go to approvals screen')
.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('approve') .includes('go to approvals screen')
) { ) {
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', 'Approve') .should('contain', 'Go to approvals screen')
.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('approve') .includes('go to approvals screen')
) { ) {
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', 'Approve') .should('contain', 'Go to approvals screen')
.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('approve') .includes('go to approvals screen')
) { ) {
approvalButton.click() approvalButton.click()
break break

View File

@ -159,21 +159,20 @@ context('filtering tests: ', function () {
}) })
}) })
// TODO: fix it('7 | filter bestnum field BETWEEN', (done) => {
// it('7 | filter bestnum field BETWEEN', (done) => { openTableFromTree(libraryToOpenIncludes, 'mpe_x_test')
// 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', 'Approve') .should('contain', 'Go to approvals screen')
.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('approve') .includes('go to approvals screen')
) { ) {
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', 'Approve') .should('contain', 'Go to approvals screen')
.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('approve') .includes('go to approvals screen')
) { ) {
approvalButton.click() approvalButton.click()
break break

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -10,7 +10,7 @@ const check = (cwd) => {
onlyAllow: onlyAllow:
'AFLv2.1;Apache 2.0;Apache-2.0;Apache*;Artistic-2.0;0BSD;BSD*;BSD-2-Clause;BSD-3-Clause;CC0-1.0;CC-BY-3.0;CC-BY-4.0;ISC;MIT;MPL-2.0;ODC-By-1.0;Python-2.0;Unlicense;', 'AFLv2.1;Apache 2.0;Apache-2.0;Apache*;Artistic-2.0;0BSD;BSD*;BSD-2-Clause;BSD-3-Clause;CC0-1.0;CC-BY-3.0;CC-BY-4.0;ISC;MIT;MPL-2.0;ODC-By-1.0;Python-2.0;Unlicense;',
excludePackages: excludePackages:
'@cds/city@1.1.0;@handsontable/angular@14.3.0;handsontable@14.3.0;hyperformula@2.7.0;jackspeak@2.2.0;path-scurry@1.7.0' '@cds/city@1.1.0;@handsontable/angular@13.1.0;handsontable@13.1.0;hyperformula@2.6.0;jackspeak@2.2.0;path-scurry@1.7.0'
}, },
(error, json) => { (error, json) => {
if (error) { if (error) {

10336
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": "npx ng test", "test": "ng test",
"test:headless": "npx ng test --no-watch --no-progress --browsers ChromeHeadlessCI", "test:headless": "ng test --browsers ChromeHeadless",
"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,33 +35,32 @@
}, },
"private": true, "private": true,
"dependencies": { "dependencies": {
"@angular/animations": "^17.3.3", "@angular/animations": "^16.1.2",
"@angular/cdk": "^17.3.3", "@angular/cdk": "^15.2.0",
"@angular/common": "^17.3.3", "@angular/common": "^16.1.2",
"@angular/compiler": "^17.3.3", "@angular/compiler": "^16.1.2",
"@angular/core": "^17.3.3", "@angular/core": "^16.1.2",
"@angular/forms": "^17.3.3", "@angular/forms": "^16.1.2",
"@angular/platform-browser": "^17.3.3", "@angular/platform-browser": "^16.1.2",
"@angular/platform-browser-dynamic": "^17.3.3", "@angular/platform-browser-dynamic": "^16.1.2",
"@angular/router": "^17.3.3", "@angular/router": "^16.1.2",
"@cds/core": "^6.10.0", "@cds/core": "^6.4.2",
"@clr/angular": "^17.0.1", "@clr/angular": "^13.17.0",
"@clr/icons": "^13.0.2", "@clr/icons": "^13.0.2",
"@clr/ui": "^17.0.1", "@clr/ui": "^13.17.0",
"@handsontable/angular": "^14.3.0", "@handsontable/angular": "^13.1.0",
"@sasjs/adapter": "4.10.2", "@sasjs/adapter": "4.10.1",
"@sasjs/utils": "^3.4.0", "@sasjs/utils": "^3.4.0",
"@sheet/crypto": "file:libraries/sheet-crypto.tgz", "@sheet/crypto": "1.20211122.1",
"@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",
"crypto-browserify": "3.12.0", "crypto-browserify": "3.12.0",
"crypto-js": "^4.2.0", "crypto-js": "^3.3.0",
"d3-graphviz": "^5.0.2", "d3-graphviz": "^5.0.2",
"fs-extra": "^7.0.1", "fs-extra": "^7.0.1",
"handsontable": "^14.3.0", "handsontable": "^13.1.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",
@ -79,26 +78,24 @@
"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",
"vm": "^0.1.0", "zone.js": "~0.13.0"
"webpack": "^5.91.0",
"zone.js": "~0.14.4"
}, },
"devDependencies": { "devDependencies": {
"@angular-devkit/build-angular": "^17.3.3", "@angular-devkit/build-angular": "^16.1.0",
"@angular-eslint/builder": "17.3.0", "@angular-eslint/builder": "16.0.3",
"@angular-eslint/eslint-plugin": "17.3.0", "@angular-eslint/eslint-plugin": "16.0.3",
"@angular-eslint/eslint-plugin-template": "17.3.0", "@angular-eslint/eslint-plugin-template": "16.0.3",
"@angular-eslint/schematics": "17.3.0", "@angular-eslint/schematics": "16.0.3",
"@angular-eslint/template-parser": "17.3.0", "@angular-eslint/template-parser": "16.0.3",
"@angular/cli": "^17.3.3", "@angular/cli": "^16.1.0",
"@angular/compiler-cli": "^17.3.3", "@angular/compiler-cli": "^16.1.2",
"@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.0.1",
"@types/es6-shim": "^0.31.39", "@types/es6-shim": "^0.31.39",
"@types/jasmine": "~5.1.4", "@types/jasmine": "~3.6.0",
"@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",
@ -112,12 +109,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": "~5.1.2", "jasmine-core": "~3.6.0",
"karma": "~6.4.3", "karma": "~6.3.0",
"karma-chrome-launcher": "~3.2.0", "karma-chrome-launcher": "~3.1.0",
"karma-coverage": "~2.2.1", "karma-coverage": "~2.1.0",
"karma-jasmine": "~5.1.0", "karma-jasmine": "~4.0.0",
"karma-jasmine-html-reporter": "~2.1.0", "karma-jasmine-html-reporter": "~1.7.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",
@ -126,7 +123,9 @@
"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",
"typescript": "~5.4.4", "typedoc": "^0.24.8",
"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

@ -37,12 +37,6 @@ export const initFilter: { filter: FilterCache } = {
} }
} }
export interface XLMapListItem {
id: string
description: string
targetDS: string
}
/** /**
* Cached filtering values across whole app (editor, viewer, viewboxes) * Cached filtering values across whole app (editor, viewer, viewboxes)
* Cached lineage libraries, tables * Cached lineage libraries, tables
@ -52,8 +46,6 @@ export interface XLMapListItem {
*/ */
export const globals: { export const globals: {
rootParam: string rootParam: string
dcLib: string
xlmaps: XLMapListItem[]
editor: any editor: any
viewer: any viewer: any
viewboxes: ViewboxCache viewboxes: ViewboxCache
@ -65,13 +57,11 @@ export const globals: {
[key: string]: any [key: string]: any
} = { } = {
rootParam: <string>'', rootParam: <string>'',
dcLib: '',
xlmaps: [],
editor: { editor: {
startupSet: <boolean>false, startupSet: <boolean>false,
treeNodeLibraries: <any[] | null>[], treeNodeLibraries: <any[] | null>[],
libsAndTables: <any[]>[], libsAndTables: <any[]>[],
libraries: <string[] | undefined>[], libraries: <String[] | undefined>[],
library: <string>'', library: <string>'',
table: <string>'', table: <string>'',
filter: <FilterCache>{ filter: <FilterCache>{

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">
<cds-icon class="alert-icon" shape="warning-standard"></cds-icon> <clr-icon class="mt-2" shape="warning-standard"></clr-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">
<cds-icon class="alert-icon" shape="warning-standard"></cds-icon> <clr-icon class="mt-2" shape="warning-standard"></clr-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">
<cds-icon class="alert-icon" shape="warning-standard"></cds-icon> <clr-icon class="mt-2" shape="warning-standard"></clr-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">
<cds-icon class="alert-icon" shape="warning-standard"></cds-icon> <clr-icon class="mt-2" shape="warning-standard"></clr-icon>
</div> </div>
<div class="alert-text"> <div class="alert-text">
@ -168,7 +168,7 @@
</button> </button>
<clr-dropdown-menu *clrIfOpen clrPosition="bottom-left"> <clr-dropdown-menu *clrIfOpen clrPosition="bottom-left">
<a [routerLink]="['/view']" clrDropdownItem>VIEW</a> <a [routerLink]="['/view']" clrDropdownItem>VIEW</a>
<a [routerLink]="['/home']" clrDropdownItem>LOAD</a> <a [routerLink]="['/home']" clrDropdownItem>EDIT</a>
<a [routerLink]="['/review/submitted']" clrDropdownItem>REVIEW</a> <a [routerLink]="['/review/submitted']" clrDropdownItem>REVIEW</a>
</clr-dropdown-menu> </clr-dropdown-menu>
</clr-dropdown> </clr-dropdown>
@ -189,7 +189,7 @@
router.url.includes('edit-record') || router.url.includes('edit-record') ||
router.url.includes('home') router.url.includes('home')
" "
>LOAD</a >EDIT</a
> >
<a <a
[routerLink]="['/review/submitted']" [routerLink]="['/review/submitted']"
@ -204,7 +204,14 @@
</div> </div>
</ng-container> </ng-container>
<app-header-actions></app-header-actions> <div class="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,5 +1,3 @@
@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.
@ -8,7 +6,7 @@ app-requests-modal {
} }
header.app-header { header.app-header {
background: $headerBackground !important; background: #314351 !important;
color: #fff; color: #fff;
} }
@ -44,7 +42,7 @@ header.app-header {
align-items: center; align-items: center;
padding: 30px; padding: 30px;
z-index: 110; z-index: 110;
background: $headerBackground; background: #314351;
.expired-notice { .expired-notice {
color: #e0e0e0; color: #e0e0e0;
@ -93,12 +91,33 @@ header {
} }
} }
.nav-link:hover { .nav
color: #fafafa; .nav-link {
color: #fafafa;
opacity: .9;
line-height: 1.45rem;
} }
.nav-link.active { .nav .nav-link:hover {
background: #61717D; box-shadow: inset 0 -3px 0 transparent;
transition: box-shadow .2s ease-in;
}
.nav
.nav-link:hover {
color: #fafafa;
opacity: 1;
}
.nav .nav-link.active {
background: #61717D;
opacity: 1;
box-shadow: inset 0 -3px transparent;
// padding: 0 1rem 0 1rem;
}
.nav .nav-item {
margin-right: 1rem;
} }
} }
@ -108,6 +127,15 @@ 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;
@ -135,44 +163,59 @@ header {
color: #fff; color: #fff;
} }
::ng-deep { @media screen and (max-width: 768px) {
.htInvalid { .navBarResp {
background: black!important; display: flex;
justify-content: flex-start;
background: #495A67;
color: #fff;
} }
@media screen and (max-width:480px) { .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 {
h2 { padding: 0 .5rem 0 1rem;
font-size: .7rem!important; width: 100%;
} max-width: 100%;
h3 { overflow: hidden;
font-size: .7rem; 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 {
.htInvalid {
background: black!important;
}
@media screen and (max-width:480px) {
h2 {
font-size: .7rem!important;
}
h3 {
font-size: .7rem;
}
}
.nav-link { .nav-link {
padding: 0rem 1rem 0rem 1rem; padding: 0rem 1rem 0rem 1rem;
} }
body[cds-theme="light"] { .btn-primary .btn, .btn.btn-primary {
.btn-primary .btn, .btn.btn-primary { border-color: #314351;
border-color: $headerBackground; background-color: #314351;
background-color: $headerBackground; color: #fff;
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;
@ -193,32 +236,36 @@ 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: $headerBackground; border-color: #314351;
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;
@ -251,68 +298,65 @@ header {
width: 350px; width: 350px;
} }
.handsontable {
/* Left and right */ background-color: #ffffff;
// border: 1px solid #ccc;
/* Column headers */ border-radius: 3px;
}
body[cds-theme="light"] { .handsontable th {
.wtBorder { background-color: #fafafa;
background-color: #495A67!important;
}
.ht_master tr:nth-of-type(odd) > td {
filter: brightness(0.95);
}
} }
$darkBorderColor: #697c85; /* Left and right */
.ht_clone_left th {
border-right: 1px solid #ccc;
border-left: 1px solid #ccc;
}
body[cds-theme="dark"] { /* Column headers */
.ht_master tr:nth-of-type(odd) > td { .ht_clone_top th {
filter: brightness(1.2); border-top: 1px solid #ccc;
} border-right: 1px solid #ccc;
border-bottom: 1px solid #ccc;
}
.ht_master:not(.emptyColumns) ~ .handsontable tbody tr th, .ht_master:not(.emptyColumns) ~ .handsontable:not(.ht_clone_top) thead tr th:first-child { .ht_clone_top_left_corner th {
background-color: #2d4048; border-right: 1px solid #ccc;
border-color: $darkBorderColor; }
}
.handsontable td { .ht_master tr:nth-of-type(odd) > td {
// border-right: 1px solid #697c85; background-color: #f3f3f3;
// border-bottom: 1px solid #697c85; border: 1px solid rgb(197, 197, 197);
border-color: $darkBorderColor; border-bottom: 1px solid rgb(236, 235, 235);
} // padding: 1px 1px;
}
.handsontable tr:first-child th, .handsontable tr:first-child td { .ht_master tr:nth-of-type(even) > td {
border-color: $darkBorderColor; background-color: white;
} border: 1px solid rgb(197, 197, 197);
border-bottom: 1px solid rgb(236, 235, 235);
// padding: 1px 1px;
}
.handsontable .handsontable.ht_clone_top .wtHider { .wtBorder {
border-color: $darkBorderColor; background-color: #495A67!important;
}
.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;
} }
body[cds-theme="light"] { .card {
.content-container { box-shadow: 0 0.125rem 0 0 #d7d7d7;
// background: red; border-radius: .0rem;
background: #F5F6FF; border: 1px solid transparent;
} // min-height: calc(100vh - 150px);
} }
.datagrid-compact, .datagrid-history{ .datagrid-compact, .datagrid-history{
@ -320,6 +364,8 @@ 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%;
@ -341,8 +387,8 @@ header {
} }
.datagrid-footer { .datagrid-footer {
position: absolute; position: absolute;
right: 30px; right: 15px;
top: 1px; top: 2px;
} }
.datagrid .datagrid-head { .datagrid .datagrid-head {
background-color: #fff; background-color: #fff;
@ -362,6 +408,7 @@ 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);
@ -376,6 +423,8 @@ 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%;
@ -386,6 +435,7 @@ 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;
@ -408,34 +458,4 @@ 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,15 +13,6 @@ 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,11 +46,12 @@ import { AppSettingsService } from './services/app-settings.service'
SharedModule, SharedModule,
ClarityModule, ClarityModule,
AppSharedModule, AppSharedModule,
HomeModule,
PipesModule, PipesModule,
DirectivesModule, DirectivesModule,
NgxJsonViewerModule NgxJsonViewerModule
], ],
providers: [AppService, SasStoreService, LicensingGuard, AppSettingsService], providers: [AppService, SasStoreService, LicensingGuard],
bootstrap: [AppComponent] bootstrap: [AppComponent]
}) })
export class AppModule {} export class AppModule {}

View File

@ -4,19 +4,19 @@
* The full license information can be found in LICENSE in the root directory of this project. * The full license information can be found in LICENSE in the root directory of this project.
*/ */
import { ModuleWithProviders } from '@angular/core' import { ModuleWithProviders } from '@angular/core'
import { RouterModule, Routes } from '@angular/router' import { Routes, RouterModule } from '@angular/router'
import { HomeComponent } from './home/home.component'
import { NotFoundComponent } from './not-found/not-found.component' import { NotFoundComponent } from './not-found/not-found.component'
import { DeployModule } from './deploy/deploy.module'
import { EditorModule } from './editor/editor.module'
import { HomeModule } from './home/home.module'
import { LicensingModule } from './licensing/licensing.module'
import { ReviewModule } from './review/review.module'
import { ReviewRouteComponent } from './routes/review-route/review-route.component' import { ReviewRouteComponent } from './routes/review-route/review-route.component'
import { StageModule } from './stage/stage.module' import { StageModule } from './stage/stage.module'
import { SystemModule } from './system/system.module' import { EditorModule } from './editor/editor.module'
import { ViewerModule } from './viewer/viewer.module' import { ViewerModule } from './viewer/viewer.module'
import { ReviewModule } from './review/review.module'
import { DeployModule } from './deploy/deploy.module'
import { LicensingModule } from './licensing/licensing.module'
import { SystemModule } from './system/system.module'
/** /**
* Defining routes * Defining routes
@ -45,10 +45,7 @@ export const ROUTES: Routes = [
path: 'licensing', path: 'licensing',
loadChildren: () => LicensingModule loadChildren: () => LicensingModule
}, },
{ { path: 'home', component: HomeComponent },
path: 'home',
loadChildren: () => HomeModule
},
{ {
/** /**
* Load editor module with subroutes * Load editor module with subroutes

View File

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

@ -1,17 +0,0 @@
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

@ -24,8 +24,8 @@
generatedRecordUrl generatedRecordUrl
? 'copy to clipboard' ? 'copy to clipboard'
: generateEditRecordUrlLoading : generateEditRecordUrlLoading
? 'Generating url...' ? 'Generating url...'
: 'Link to this record' : 'Link to this record'
}} }}
</button> </button>
</ng-container> </ng-container>
@ -112,7 +112,7 @@
<div <div
*ngIf=" *ngIf="
['autocomplete', 'autocomplete.custom'].includes( ['autocomplete'].includes(
$any(currentRecordValidator?.getRule(col.key)?.editor) $any(currentRecordValidator?.getRule(col.key)?.editor)
) )
" "
@ -163,7 +163,7 @@
<div <div
*ngIf=" *ngIf="
['autocomplete', 'autocomplete.custom'].includes( ['autocomplete'].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 mr-5i" class="btn btn-outline focusable"
(click)="currentRecord!.noLinkOption = false; closeRecordEdit()" (click)="currentRecord!.noLinkOption = false; closeRecordEdit()"
> >
Cancel Cancel

View File

@ -38,6 +38,7 @@
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;
@ -48,6 +49,7 @@
input { input {
width: 100%; width: 100%;
border: 0; border: 0;
background-color: #fff;
&:focus { &:focus {
background: none; background: none;
@ -130,6 +132,7 @@
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

@ -165,14 +165,11 @@
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 *ngIf="tableTrue" class="clr-col-12 clr-col-lg-4 backBtn">
<span class="btn btn-sm btn-icon btn-dimmed" [routerLink]="['/home']"> <span class="btn btn-sm" [routerLink]="['/home']">
<clr-icon shape="caret" dir="left" size="20"></clr-icon> <clr-icon shape="caret" dir="left" size="20"></clr-icon>Back to
Back to table selection table selection
</span> </span>
<span <span (click)="viewboxManager()" class="btn btn-sm viewbox-open">
(click)="viewboxManager()"
class="btn 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 Viewboxes
</span> </span>
@ -189,40 +186,24 @@
} as libdsParsed" } as libdsParsed"
class="editor-title text-center mt-0-i" class="editor-title text-center mt-0-i"
> >
<clr-tooltip> <clr-icon
<clr-icon (click)="datasetInfo = true"
clrTooltipTrigger shape="info-circle"
(click)="datasetInfo = true" class="is-highlight cursor-pointer"
shape="info-circle" size="24"
class="is-highlight cursor-pointer" ></clr-icon>
size="24"
></clr-icon>
<clr-icon <clr-icon
*ngIf="libdsParsed.tableName.includes('-FC')" *ngIf="libdsParsed.tableName.includes('-FC')"
shape="bolt" shape="bolt"
class="color-yellow" class="color-yellow"
></clr-icon> ></clr-icon>
<span clrTooltipTrigger>
{{ libdsParsed.libName }}.<a
class="mr-10 view-table"
[routerLink]="'/view/data/' + libds!"
>{{ libdsParsed.tableName.replace('-FC', '') }}</a
>
</span>
<ng-container *ngIf="this.dsNote && this.dsNote.length > 0">
<clr-tooltip-content
clrPosition="bottom-left"
clrSize="lg"
*clrIfOpen
>
{{ this.dsNote }}
</clr-tooltip-content>
</ng-container>
</clr-tooltip>
{{ libdsParsed.libName }}.<a
class="mr-10"
[routerLink]="'/view/data/' + libds!"
>{{ libdsParsed.tableName.replace('-FC', '') }}</a
>
<ng-container *ngIf="dataSource"> <ng-container *ngIf="dataSource">
<ng-container *ngIf="!zeroFilterRows"> <ng-container *ngIf="!zeroFilterRows">
({{ dataSource.length | thousandSeparator: ',' }} ({{ dataSource.length | thousandSeparator: ',' }}
@ -238,7 +219,7 @@
<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 btn-dimmed" class="btnView btn btn-sm btn-icon btn-block"
(click)="openQb()" (click)="openQb()"
> >
<clr-icon shape="filter"></clr-icon> <clr-icon shape="filter"></clr-icon>
@ -299,7 +280,7 @@
licenceState.value.editor_rows_allowed === 1 licenceState.value.editor_rows_allowed === 1
? 'row' ? 'row'
: 'rows' : 'rows'
}}, contact support&#64;datacontroller.io</span }}, contact support@datacontroller.io</span
> >
</clr-tooltip-content> </clr-tooltip-content>
</clr-tooltip> </clr-tooltip>
@ -365,8 +346,8 @@
<ng-container *ngIf="!getdataError"> <ng-container *ngIf="!getdataError">
<span class="spinner"> Loading... </span> <span class="spinner"> Loading... </span>
<div class="mt-10"> <div>
<p cds-text="section">Loading table</p> <h3>Loading table</h3>
</div> </div>
</ng-container> </ng-container>
@ -375,8 +356,8 @@
<clr-icon shape="error-standard" class="error-icon"></clr-icon> <clr-icon shape="error-standard" class="error-icon"></clr-icon>
</span> </span>
<div class="mt-10"> <div>
<p cds-text="section">Loading table error</p> <h3>Loading table error</h3>
</div> </div>
</ng-container> </ng-container>
</div> </div>
@ -401,7 +382,6 @@
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"
> >
@ -437,7 +417,7 @@
licenceState.value.editor_rows_allowed === 1 licenceState.value.editor_rows_allowed === 1
? 'row' ? 'row'
: 'rows' : 'rows'
}}, contact support&#64;datacontroller.io</span }}, contact support@datacontroller.io</span
> >
</clr-tooltip-content> </clr-tooltip-content>
</clr-tooltip> </clr-tooltip>
@ -487,18 +467,14 @@
: 'rows' : 'rows'
}} }}
will be submitted. To remove the restriction, contact will be submitted. To remove the restriction, contact
support&#64;datacontroller.io</span support@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="text-area-full-width"> <div class="form-group">
<label for="formFields_8" class="mb-5 d-block" <label for="formFields_8">Message</label>
>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 ' +
@ -506,9 +482,10 @@
' cells.' ' cells.'
: '' : ''
" "
class="submit-reason" class="w-100"
type="text" type="text"
id="formFields_8" id="formFields_8"
rows="5"
></textarea> ></textarea>
</div> </div>
<!-- TODO:approvers list --> <!-- TODO:approvers list -->
@ -527,7 +504,6 @@
[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
@ -536,7 +512,6 @@
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
@ -553,7 +528,7 @@
Due to current licence, only Due to current licence, only
{{ licenceState.value.submit_rows_limit }} rows in a file will {{ licenceState.value.submit_rows_limit }} rows in a file will
be submitted. To remove the restriction, contact be submitted. To remove the restriction, contact
support&#64;datacontroller.io support@datacontroller.io
</p> </p>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
@ -855,12 +830,6 @@
</div> </div>
</clr-modal> </clr-modal>
<app-dataset-info <app-dataset-info [(open)]="datasetInfo" [dsmeta]="dsmeta"></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-color: #306b00b0 !important; background: #306b00b0;
} }
} }
.primaryKeyHeaderStyle { .primaryKeyHeaderStyle {
background-color: #306b006e !important; background: #306b006e;
} }
th.readonlyCell { th.readonlyCell {
@ -41,12 +41,6 @@ hot-table {
} }
} }
.submit-reason {
min-height: 120px;
max-height: 120px;
height: 120px;
}
.infoBar { .infoBar {
margin-top:14px; margin-top:14px;
background: #495967; background: #495967;
@ -86,7 +80,8 @@ 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: var(--clr-vertical-nav-bg-color); background: #ffffff;
background: #f5f6fe;
} }
.error-icon { .error-icon {
@ -219,10 +214,6 @@ hot-table {
width: 150px; width: 150px;
} }
.view-table {
font-size: inherit !important;
}
// 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,8 +38,7 @@ 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'
@ -122,8 +121,6 @@ export class EditorComponent implements OnInit, AfterViewInit {
datasetInfo: boolean = false datasetInfo: boolean = false
dsmeta: DSMeta[] = [] dsmeta: DSMeta[] = []
versions: Version[] = []
dsNote = ''
viewboxes: boolean = false viewboxes: boolean = false
@ -942,30 +939,13 @@ export class EditorComponent implements OnInit, AfterViewInit {
return row.map((col: any, index: number) => { return row.map((col: any, index: number) => {
if (!col && col !== 0) col = '' if (!col && col !== 0) col = ''
/** if (isNaN(col)) {
* Keeping this for the reference col = col.replace(/"/g, '""')
* Code below used to convert JSON to CSV
* now the XLSX is converting to CSV
*/
// if (isNaN(col)) {
// // Match and replace the double quotes, ignore the first and last char
// // in case they are double quotes already
// col = col.replace(/(?<!^)"(?!$)/g, '""')
// if (col.search(/,/g) > -1 || if (col.search(/,/g) > -1) {
// col.search(/\r|\n/g) > -1 col = '"' + col + '"'
// ) { }
// // Missing quotes at the end }
// if (col.search(/"$/g) < 0) {
// col = col + '"' // So we add them
// }
// // Missing quotes at the start
// if (col.search(/^"/g) < 0) {
// col = '"' + col // So we add them
// }
// }
// }
const colName = this.headerShow[index] const colName = this.headerShow[index]
const colRule = this.dcValidator?.getRule(colName) const colRule = this.dcValidator?.getRule(colName)
@ -980,30 +960,20 @@ export class EditorComponent implements OnInit, AfterViewInit {
this.data = csvArrayData this.data = csvArrayData
// Apply licence rows limitation if exists, it is only affecting data let csvContent = csvArrayHeaders.join(',') + '\n'
// which will be send to SAS // Apply licence rows limitation if exists
const strippedCsvArrayData = csvArrayData.slice( csvContent += csvArrayData
0, .slice(0, this.licenceState.value.submit_rows_limit)
this.licenceState.value.submit_rows_limit .map((e) => e.join(','))
) .join('\n')
// To submit to sas service, we need clean version of CSV of file
// attached. XLSX will do the parsing and heavy lifting
// First we create worksheet of json (data we extracted)
let ws = XLSX.utils.json_to_sheet(strippedCsvArrayData, {
skipHeader: true
})
// create CSV to be uploaded from worksheet
let csvContentClean = XLSX.utils.sheet_to_csv(ws)
// Prepend headers
csvContentClean = csvArrayHeaders.join(',') + '\n' + csvContentClean
if (this.encoding === 'WLATIN1') { if (this.encoding === 'WLATIN1') {
let encoded = iconv.decode(Buffer.from(csvContentClean), 'CP-1252') let encoded = iconv.decode(Buffer.from(csvContent), 'CP-1252')
let blob = new Blob([encoded], { type: 'application/csv' }) let blob = new Blob([encoded], { type: 'application/csv' })
let newCSVFile: File = this.blobToFile(blob, this.filename + '.csv') let newCSVFile: File = this.blobToFile(blob, this.filename + '.csv')
this.uploader.addToQueue([newCSVFile]) this.uploader.addToQueue([newCSVFile])
} else { } else {
let blob = new Blob([csvContentClean], { type: 'application/csv' }) let blob = new Blob([csvContent], { type: 'application/csv' })
let newCSVFile: File = this.blobToFile(blob, this.filename + '.csv') let newCSVFile: File = this.blobToFile(blob, this.filename + '.csv')
this.uploader.addToQueue([newCSVFile]) this.uploader.addToQueue([newCSVFile])
} }
@ -1958,13 +1928,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.custom') hot.setCellMeta(entry.row, entry.col, 'editor', 'autocomplete')
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.custom', editor: 'autocomplete',
strict: entry.strict, strict: entry.strict,
filter: false filter: false
}) })
@ -2044,7 +2014,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) as number const cellCol = hot.propToCol(cell)
const dynamicValidationEl = const dynamicValidationEl =
this.disabledBasicDynamicCellValidationMap.find( this.disabledBasicDynamicCellValidationMap.find(
(x) => x.row === row && x.col === cellCol (x) => x.row === row && x.col === cellCol
@ -2059,13 +2029,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.custom') hot.setCellMeta(row, cellCol, 'editor', 'autocomplete')
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.custom', editor: 'autocomplete',
strict: cellValidationEntry.strict, strict: cellValidationEntry.strict,
filter: false filter: false
}) })
@ -2263,8 +2233,8 @@ export class EditorComponent implements OnInit, AfterViewInit {
setTimeout(() => { setTimeout(() => {
let txt: any = document.getElementById('formFields_8') let txt: any = document.getElementById('formFields_8')
if (txt) txt.focus() txt.focus()
}, 200) })
}) })
// let cnt = 0; // let cnt = 0;
@ -2708,13 +2678,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.custom') hot.setCellMeta(row, column, 'editor', 'autocomplete')
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.custom', editor: 'autocomplete',
strict: strict, strict: strict,
filter: false filter: false
}) })
@ -2766,7 +2736,6 @@ 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()
@ -2937,16 +2906,6 @@ 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
} }
@ -2959,37 +2918,6 @@ 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) => {
@ -3036,7 +2964,7 @@ export class EditorComponent implements OnInit, AfterViewInit {
await this.sasStoreService await this.sasStoreService
.callService(myParams, 'SASControlTable', 'editors/getdata', this.libds) .callService(myParams, 'SASControlTable', 'editors/getdata', this.libds)
.then((res: EditorsGetDataServiceResponse) => { .then((res: EditorsGetdataServiceResponse) => {
this.initSetup(res) this.initSetup(res)
}) })
.catch((err: any) => { .catch((err: any) => {
@ -3048,7 +2976,7 @@ export class EditorComponent implements OnInit, AfterViewInit {
ngAfterViewInit() {} ngAfterViewInit() {}
initSetup(response: EditorsGetDataServiceResponse) { initSetup(response: EditorsGetdataServiceResponse) {
this.hotInstance = this.hotRegisterer.getInstance('hotInstance') this.hotInstance = this.hotRegisterer.getInstance('hotInstance')
if (this.getdataError) return if (this.getdataError) return
@ -3057,21 +2985,6 @@ 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 longDesc = this.dsmeta.find((item) => item.NAME === 'DD_LONGDESC')
const shortDesc = this.dsmeta.find((item) => item.NAME === 'DD_SHORTDESC')
if (notes && notes.VALUE) {
this.dsNote = notes.VALUE
} else if (longDesc && longDesc.VALUE) {
this.dsNote = longDesc.VALUE
} else if (shortDesc && shortDesc.VALUE) {
this.dsNote = shortDesc.VALUE
} else {
this.dsNote = ''
}
const hot: Handsontable = this.hotInstance const hot: Handsontable = this.hotInstance
@ -3258,9 +3171,6 @@ 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,
@ -3349,16 +3259,28 @@ export class EditorComponent implements OnInit, AfterViewInit {
} }
) )
hot.addHook('afterBeginEditing', () => { hot.addHook('beforeKeyDown', (e: any) => {
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.
this.setCellFilter(true) 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 (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) => {
@ -3367,7 +3289,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) as number const col = hot.propToCol(colProp)
// 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,6 +12,7 @@ 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'
@ -32,6 +33,7 @@ registerAllModules()
AppSharedModule, AppSharedModule,
DirectivesModule, DirectivesModule,
SharedModule, SharedModule,
HomeModule,
PipesModule, PipesModule,
DcTreeModule, DcTreeModule,
DragDropModule, DragDropModule,

View File

@ -11,11 +11,9 @@ export const errorRenderer = (
value: any, value: any,
cellProperties: any cellProperties: any
) => { ) => {
addDarkClass(td)
td.innerHTML = `${ td.innerHTML = `${
value ? value.toString() : '' value ? value.toString() : ''
} <cds-icon shape="exclamation-triangle" status="warning"></cds-icon>` } <clr-icon shape="exclamation-circle" status="warning"></clr-icon>`
return td return td
} }
@ -33,8 +31,6 @@ 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
@ -54,20 +50,9 @@ 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,7 +3,6 @@
<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"
@ -28,7 +27,7 @@
<clr-tree-node <clr-tree-node
(click)="groupOnClick(group)" (click)="groupOnClick(group)"
*ngIf="!group['hidden']" *ngIf="!group['hidden']"
[class.active]="group.GROUPURI === groupUri" [class.table-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,63 +1,26 @@
@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{
border: 1px solid; background-color: #f9f9f9;
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{
border: 1px solid; background-color: #f9f9f9;
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{
@ -65,11 +28,15 @@
} }
.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

@ -1,23 +0,0 @@
import { NgModule } from '@angular/core'
import { RouterModule, Routes } from '@angular/router'
import { HomeRouteComponent } from '../routes/home-route/home-route.component'
import { HomeComponent } from './home.component'
import { XLMapModule } from '../xlmap/xlmap.module'
const routes: Routes = [
{
path: '',
component: HomeRouteComponent,
children: [
{ path: '', pathMatch: 'full', redirectTo: 'tables' },
{ path: 'tables', component: HomeComponent },
{ path: 'files', loadChildren: () => XLMapModule }
]
}
]
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class HomeRoutingModule {}

View File

@ -4,7 +4,6 @@
<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"
@ -47,7 +46,6 @@
<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"
@ -87,7 +85,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.active]="libTabActive(library.LIBRARYREF, libTable)" [class.table-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>
@ -96,17 +94,15 @@
{{ libTable.replace('-FC', '') }} {{ libTable.replace('-FC', '') }}
</button> </button>
<ng-container *ngIf="tableLocked"> <clr-tooltip-content
<clr-tooltip-content clrPosition="bottom-right"
clrPosition="bottom-right" clrSize="lg"
clrSize="lg" *clrIfOpen
*clrIfOpen >
> <span *ngIf="tableLocked">
<span> To unlock all tables, contact support@datacontroller.io
To unlock all tables, contact support&#64;datacontroller.io </span>
</span> </clr-tooltip-content>
</clr-tooltip-content>
</ng-container>
</clr-tooltip> </clr-tooltip>
</clr-tree-node> </clr-tree-node>
</clr-tree-node> </clr-tree-node>
@ -128,20 +124,12 @@
size="60" size="60"
class="is-info icon-dc-fill" class="is-info icon-dc-fill"
></clr-icon> ></clr-icon>
<p <h3 *ngIf="treeNodeLibraries?.length! > 0" class="text-center color-gray">
*ngIf="treeNodeLibraries?.length! > 0"
class="text-center color-gray mt-10"
cds-text="section"
>
Please select a table Please select a table
</p> </h3>
<p <h3 *ngIf="treeNodeLibraries?.length! < 1" class="text-center color-gray">
*ngIf="treeNodeLibraries?.length! < 1"
class="text-center color-gray mt-10"
cds-text="section"
>
No Editable Tables Configured No Editable Tables Configured
</p> </h3>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,18 +1,15 @@
import { CommonModule } from '@angular/common'
import { NgModule } from '@angular/core' import { NgModule } from '@angular/core'
import { FormsModule } from '@angular/forms' import { CommonModule } from '@angular/common'
import { ClarityModule } from '@clr/angular'
import { AppSharedModule } from '../app-shared.module'
import { DirectivesModule } from '../directives/directives.module'
import { HomeRouteComponent } from '../routes/home-route/home-route.component'
import { DcTreeModule } from '../shared/dc-tree/dc-tree.module'
import { HomeRoutingModule } from './home-routing.module'
import { HomeComponent } from './home.component' import { HomeComponent } from './home.component'
import { ClarityModule } from '@clr/angular'
import { FormsModule } from '@angular/forms'
import { AppSharedModule } from '../app-shared.module'
import { DcTreeModule } from '../shared/dc-tree/dc-tree.module'
import { DirectivesModule } from '../directives/directives.module'
@NgModule({ @NgModule({
declarations: [HomeComponent, HomeRouteComponent], declarations: [HomeComponent],
imports: [ imports: [
HomeRoutingModule,
FormsModule, FormsModule,
ClarityModule, ClarityModule,
AppSharedModule, AppSharedModule,

View File

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

View File

@ -3,7 +3,6 @@
<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"
@ -43,7 +42,6 @@
<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"
@ -87,7 +85,6 @@
<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"
@ -141,9 +138,7 @@
size="60" size="60"
class="is-info icon-dc-fill" class="is-info icon-dc-fill"
></clr-icon> ></clr-icon>
<p class="text-center color-gray mt-10" cds-text="section"> <h3 class="text-center color-gray">Please select a column or table</h3>
Please select a column or table
</p>
</div> </div>
<ng-container *ngIf="column || table"> <ng-container *ngIf="column || table">
@ -185,13 +180,13 @@
<button <button
(click)="limitDotDepth = true" (click)="limitDotDepth = true"
type="button" type="button"
class="btn btn-outline mr-5" class="btn btn-outline"
> >
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 direction d-block"> <div class="btn-group d-block">
<div <div
class="radio btn" class="radio btn"
(click)=" (click)="

View File

@ -1,8 +1,6 @@
@import '../../colors.scss';
.toggle-switch input[type=checkbox]:checked+label:before { .toggle-switch input[type=checkbox]:checked+label:before {
border-color: $headerBackground; border-color: #314351;
background-color: $headerBackground !important; background-color: #314351!important;
transition: .15s ease-in; transition: .15s ease-in;
transition-property: border-color,background-color; transition-property: border-color,background-color;
} }
@ -43,10 +41,6 @@ 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

@ -656,10 +656,11 @@ export class LineageComponent {
this.flatdata = res.flatdata this.flatdata = res.flatdata
if (this.libraryList) { if (this.libraryList) {
let libraryToSelect = this.libraryList.find((library: any) => let libraryToSelect = this.libraryList.find(
res.info[0]?.LIBURI?.toUpperCase()?.includes( (library: any) =>
library?.LIBRARYID?.toUpperCase() res.info[0]?.LIBURI?.toUpperCase()?.includes(
) library?.LIBRARYID?.toUpperCase()
)
) )
let tableToSelect: any let tableToSelect: any

View File

@ -22,7 +22,6 @@
<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"
@ -73,9 +72,7 @@
size="60" size="60"
class="is-info icon-dc-fill" class="is-info icon-dc-fill"
></clr-icon> ></clr-icon>
<p class="text-center color-gray mt-10" cds-text="section"> <h3 class="text-center color-gray">Please select a type</h3>
Please select a type
</p>
</div> </div>
<div class="loadingSpinner" *ngIf="loading"> <div class="loadingSpinner" *ngIf="loading">
@ -126,18 +123,12 @@
[class.object-header]="!entry.count" [class.object-header]="!entry.count"
class="full-width" class="full-width"
> >
<div> <clr-icon
<clr-icon *ngIf="!entry.count"
*ngIf="!entry.count" shape="rack-server"
shape="rack-server" ></clr-icon>
></clr-icon> <clr-icon *ngIf="entry.count" shape="block"></clr-icon>
<clr-icon {{ entry.display }}
*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>
@ -172,15 +163,9 @@
[clrExpandable]="true" [clrExpandable]="true"
> >
<div [class.object-header]="!entry.count" class="full-width"> <div [class.object-header]="!entry.count" class="full-width">
<div> <clr-icon *ngIf="!entry.count" shape="rack-server"></clr-icon>
<clr-icon <clr-icon *ngIf="entry.count" shape="block"></clr-icon>
*ngIf="!entry.count" {{ entry.display }}
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,27 +1,14 @@
::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;
@ -53,13 +40,11 @@
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

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

View File

@ -6,7 +6,6 @@ export interface FilterClause {
operators: string[] operators: string[]
type: string type: string
value: any value: any
valueVariable: boolean
values: { formatted: string; unformatted: any }[] values: { formatted: string; unformatted: any }[]
variable: string variable: string
} }

View File

@ -4,12 +4,12 @@ import { DQData, SASParam } from '../TableData'
import { BaseSASResponse } from './common/BaseSASResponse' import { BaseSASResponse } from './common/BaseSASResponse'
import { DataFormat } from './common/DateFormat' import { DataFormat } from './common/DateFormat'
export interface EditorsGetDataServiceResponse { export interface EditorsGetdataServiceResponse {
data: EditorsGetDataSASResponse data: EditorsGetdataSASResponse
libds: string libds: string
} }
export interface EditorsGetDataSASResponse extends BaseSASResponse { export interface EditorsGetdataSASResponse extends BaseSASResponse {
$sasdata: $DataFormats $sasdata: $DataFormats
sasdata: Sasdata[] sasdata: Sasdata[]
sasparams: SASParam[] sasparams: SASParam[]
@ -17,7 +17,6 @@ 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[]
@ -28,18 +27,6 @@ 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

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

View File

@ -143,6 +143,7 @@
(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>
@ -412,10 +413,7 @@
> >
<app-soft-select <app-soft-select
label="Value" label="Value"
[secondLabel]="'Variable'"
[emitOnlySelected]="query.valueVariable"
[inputId]="'vals_' + queryIndex + '_' + clauseIndex" [inputId]="'vals_' + queryIndex + '_' + clauseIndex"
(selectedLabelChange)="selectedLabelChange($event, query)"
[(value)]="query.value" [(value)]="query.value"
[enableLoadMore]="query.nobs > query.values.length" [enableLoadMore]="query.nobs > query.values.length"
(onInputEvent)=" (onInputEvent)="
@ -425,19 +423,9 @@
onAutocompleteLoadingMore($event, query.variable, queryIndex, clauseIndex) onAutocompleteLoadingMore($event, query.variable, queryIndex, clauseIndex)
" "
> >
<div *ngIf="!query.valueVariable"> <option [value]="column.unformatted" *ngFor="let column of query.values">
<option [value]="column.unformatted" *ngFor="let column of query.values"> {{ column.formatted.trim() }}
{{ column.formatted.trim() }} </option>
</option>
</div>
<div *ngIf="query.valueVariable">
<ng-container *ngFor="let column of cols">
<option [value]="column.NAME" *ngIf="column.TYPE === query.type">
{{ column.NAME }}
</option>
</ng-container>
</div>
</app-soft-select> </app-soft-select>
</ng-template> </ng-template>

View File

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

View File

@ -95,7 +95,6 @@ export class QueryComponent
variable: null, variable: null,
operator: null, operator: null,
value: null, value: null,
valueVariable: false,
startrow: 0, startrow: 0,
rows: 0, rows: 0,
nobs: 0, nobs: 0,
@ -194,20 +193,6 @@ export class QueryComponent
*/ */
usePickersChange() { usePickersChange() {
this.queryDateTime = [] this.queryDateTime = []
if (this.usePickers) {
this.clauses.queryObj.forEach((queryObj: any) => {
queryObj.elements.forEach((element: any) => {
const isDateOrTime = ['DATETIME', 'TIME', 'DATE'].includes(
element.ddtype
)
if (isDateOrTime && element.valueVariable) {
element.value = ''
element.valueVariable = false
}
})
})
}
} }
/** /**
@ -268,6 +253,8 @@ export class QueryComponent
get(globals, objPath).filter.libds = this.libds get(globals, objPath).filter.libds = this.libds
} }
get(globals, objPath).filter.clauses = this.clauses get(globals, objPath).filter.clauses = this.clauses
console.log('globals', globals)
} }
/** /**
@ -763,12 +750,6 @@ export class QueryComponent
) )
} }
public selectedLabelChange(label: string, query: any) {
query.valueVariable = label === 'Variable'
query.value = ''
this.whereClauseFn()
}
public variableInputChange( public variableInputChange(
queryVariable: any, queryVariable: any,
index: number, index: number,
@ -878,25 +859,17 @@ 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 (
clause.value === '' &&
!(clause.operator === 'NE' || clause.operator === 'CONTAINS')
) {
clause['invalidClause'] = true
return true
}
if ( if (
clause.variable === null || clause.variable === null ||
clause.operator === null || clause.operator === null ||
clause.value === null clause.value === null ||
clause.value === ''
) { ) {
clause['invalidClause'] = true clause['invalidClause'] = true
return true return true
} else {
clause['invalidClause'] = false
} }
} }

View File

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

View File

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

View File

@ -99,11 +99,19 @@
</clr-dg-cell> </clr-dg-cell>
</clr-dg-row> </clr-dg-row>
<clr-dg-footer> <clr-dg-footer class="d-flex justify-content-start">
<clr-dg-pagination #pagination [clrDgPageSize]="10"> <span>items per page</span>
<clr-dg-page-size [clrPageSizeOptions]="[3, 5, 10, 15]" <select [(ngModel)]="itemsNum">
>Items per page</clr-dg-page-size <option [ngValue]="3">3</option>
> <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,5 +1,3 @@
@import '../../../colors.scss';
.column-center { .column-center {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -15,11 +13,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: $headerBackground !important; background: #314351!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 $headerBackground; border-right: .25rem solid #314351;
border-bottom: .20833rem solid $headerBackground; border-bottom: .20833rem solid #314351;
} }
.noBorder { .noBorder {

View File

@ -72,7 +72,7 @@
> >
To unlock more than To unlock more than
{{ licenceState.value.history_rows_allowed }} records, contact {{ licenceState.value.history_rows_allowed }} records, contact
support&#64;datacontroller.io support@datacontroller.io
</p> </p>
</div> </div>

View File

@ -86,11 +86,19 @@
</clr-dg-cell> </clr-dg-cell>
</clr-dg-row> </clr-dg-row>
<clr-dg-footer> <clr-dg-footer class="d-flex justify-content-start">
<clr-dg-pagination #pagination [clrDgPageSize]="10"> <span>items per page</span>
<clr-dg-page-size [clrPageSizeOptions]="[3, 5, 10, 15]" <select [(ngModel)]="itemsNum">
>Items per page</clr-dg-page-size <option [ngValue]="3">3</option>
> <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,15 +1,13 @@
@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: $headerBackground !important; background: #314351!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 $headerBackground; border-right: .25rem solid #314351;
border-bottom: .20833rem solid $headerBackground; border-bottom: .20833rem solid #314351;
} }
.no-submitted-tables { .no-submitted-tables {

View File

@ -3,7 +3,6 @@
<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"
@ -27,7 +26,7 @@
<clr-tree-node <clr-tree-node
(click)="roleOnClick(role)" (click)="roleOnClick(role)"
*ngIf="!role['hidden']" *ngIf="!role['hidden']"
[class.active]="role.ROLEURI === roleUri" [class.table-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,61 +1,25 @@
@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{
border: 1px solid; background-color: #f9f9f9;
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{
border: 1px solid; background-color: #f9f9f9;
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;
@ -64,10 +28,15 @@
} }
} }
.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

@ -1 +0,0 @@
<router-outlet></router-outlet>

View File

@ -1,17 +0,0 @@
import { Component, OnInit, OnDestroy } from '@angular/core'
@Component({
selector: 'app-home-route',
templateUrl: './home-route.component.html',
styleUrls: ['./home-route.component.scss'],
host: {
class: 'content-container'
}
})
export class HomeRouteComponent implements OnInit, OnDestroy {
constructor() {}
ngOnInit() {}
ngOnDestroy() {}
}

View File

@ -1 +0,0 @@
<router-outlet></router-outlet>

View File

@ -1,17 +0,0 @@
import { Component, OnInit, OnDestroy } from '@angular/core'
@Component({
selector: 'app-xlmap-route',
templateUrl: './xlmap-route.component.html',
styleUrls: ['./xlmap-route.component.scss'],
host: {
class: 'content-container'
}
})
export class XLMapRouteComponent implements OnInit, OnDestroy {
constructor() {}
ngOnInit() {}
ngOnDestroy() {}
}

View File

@ -1,56 +0,0 @@
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,8 +7,6 @@ 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 {
@ -20,7 +18,6 @@ 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()
@ -32,19 +29,6 @@ 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() {
@ -90,7 +74,6 @@ export class AppService {
missingProps.push('Globvars') missingProps.push('Globvars')
if (!res.sasdatasets) missingProps.push('Sasdatasets') if (!res.sasdatasets) missingProps.push('Sasdatasets')
if (!res.saslibs) missingProps.push('Saslibs') if (!res.saslibs) missingProps.push('Saslibs')
if (!res.xlmaps) missingProps.push('XLMaps')
if (missingProps.length > 0) { if (missingProps.length > 0) {
startupServiceError = true startupServiceError = true
@ -111,7 +94,6 @@ 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,
@ -153,17 +135,10 @@ export class AppService {
globals.editor.libsAndTables = libsAndTables globals.editor.libsAndTables = libsAndTables
} }
globals.xlmaps = res.xlmaps.map((xlmap: any) => ({
id: xlmap[0],
description: xlmap[1],
targetDS: xlmap[2]
}))
globals.editor.treeNodeLibraries = treeNodeLibraries globals.editor.treeNodeLibraries = treeNodeLibraries
globals.editor.libraries = libraries globals.editor.libraries = libraries
globals.editor.startupSet = true globals.editor.startupSet = true
globals.dcLib = res.globvars[0].DCLIB
await this.licenceService.activation(res) await this.licenceService.activation(res)
}) })
.catch((err: any) => { .catch((err: any) => {

View File

@ -1,9 +1,6 @@
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'
@ -20,23 +17,7 @@ export class EventService {
public viewLastUrl: string | null = null public viewLastUrl: string | null = null
public sidebarCloseLimit = 1280 public sidebarCloseLimit = 1280
public darkMode: BehaviorSubject<boolean> = new BehaviorSubject(false) constructor(private alertsService: AlertsService) {}
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

@ -10,8 +10,8 @@ import { globals } from '../_globals'
import { FilterClause, FilterGroup, FilterQuery } from '../models/FilterQuery' import { FilterClause, FilterGroup, FilterQuery } from '../models/FilterQuery'
import { import {
$DataFormats, $DataFormats,
EditorsGetDataSASResponse, EditorsGetdataSASResponse,
EditorsGetDataServiceResponse EditorsGetdataServiceResponse
} from '../models/sas/editors-getdata.model' } from '../models/sas/editors-getdata.model'
import { LoggerService } from './logger.service' import { LoggerService } from './logger.service'
import { isSpecialMissing } from '@sasjs/utils/input/validators' import { isSpecialMissing } from '@sasjs/utils/input/validators'
@ -57,13 +57,13 @@ export class SasStoreService {
libds: string libds: string
) { ) {
this.libds = libds this.libds = libds
const tables: any = {} let tables: any = {}
tables[tableName] = [tableData] tables[tableName] = [tableData]
const res: EditorsGetDataSASResponse = await this.sasService.request( let res: EditorsGetdataSASResponse = await this.sasService.request(
program, program,
tables tables
) )
const response: EditorsGetDataServiceResponse = { let response: EditorsGetdataServiceResponse = {
data: res, data: res,
libds: this.libds libds: this.libds
} }
@ -209,14 +209,6 @@ export class SasStoreService {
return res return res
} }
public async getXLMapRules(id: string) {
const tables = {
getxlmaps_in: [{ XLMAP_ID: id }]
}
const res: any = await this.sasService.request('editors/getxlmaps', tables)
return res
}
public async getDetails(tableData: any, tableName: string, program: string) { public async getDetails(tableData: any, tableName: string, program: string) {
let tables: any = {} let tables: any = {}
tables[tableName] = [tableData] tables[tableName] = [tableData]
@ -416,18 +408,14 @@ export class SasStoreService {
for (let index = 0; index < clauses.queryObj.length; index++) { for (let index = 0; index < clauses.queryObj.length; index++) {
let string = '' let string = ''
let clause = clauses.queryObj[index] let clause = clauses.queryObj[index]
for (let ind = 0; ind < clause.elements.length; ind++) { for (let ind = 0; ind < clause.elements.length; ind++) {
let query = clause.elements[ind] let query = clause.elements[ind]
if (ind < clause.elements.length - 1) { if (ind < clause.elements.length - 1) {
opr = clause.clauseLogic opr = clause.clauseLogic
} else { } else {
opr = '' opr = ''
} }
let val: any let val: any
for (let k = 0; k < query.values.length; k++) { for (let k = 0; k < query.values.length; k++) {
if ( if (
typeof query.value === 'string' && typeof query.value === 'string' &&
@ -498,8 +486,6 @@ export class SasStoreService {
} }
let type = query.type let type = query.type
//if the value is variable, omit quotes in the 'where' string
const isValueVariable = query.valueVariable
let variable = query.variable === null ? '' : query.variable let variable = query.variable === null ? '' : query.variable
let oper = query.operator === null ? '' : query.operator let oper = query.operator === null ? '' : query.operator
// let value = val === null ? "''" : val; // let value = val === null ? "''" : val;
@ -513,14 +499,10 @@ export class SasStoreService {
if (type === 'char' && oper !== 'IN' && oper !== 'NOT IN') { if (type === 'char' && oper !== 'IN' && oper !== 'NOT IN') {
if (typeof value === 'undefined') { if (typeof value === 'undefined') {
value = '' value = ''
} value = " '" + value + "' "
if (isValueVariable) {
value = ' ' + value + ' ' //without quotes, with spaces
} else { } else {
value = " '" + value + "' " //with quotes and spaces value = " '" + value + "' "
} }
string = string + ' ' + variable + ' ' + oper + value + opr string = string + ' ' + variable + ' ' + oper + value + opr
} else { } else {
if (type === 'num' && typeof value === 'undefined') { if (type === 'num' && typeof value === 'undefined') {
@ -614,7 +596,7 @@ export class SasStoreService {
rawValue = '.' rawValue = '.'
} }
} else { } else {
if (filterClause.type === 'char' && !filterClause.valueVariable) { if (filterClause.type === 'char') {
rawValue = `'${filterClause.value.replace(/'/g, "''")}'` rawValue = `'${filterClause.value.replace(/'/g, "''")}'`
} }
} }

View File

@ -1,6 +1,6 @@
.input-val { .input-val {
border: 0px; border: 0px;
background: transparent; background: #fbf8f8;
border-bottom: 1px solid #999999; border-bottom: 1px solid #999999;
} }
@ -13,29 +13,6 @@ 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;
@ -48,6 +25,7 @@ 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

@ -2,5 +2,5 @@
[ngClass]="classes" [ngClass]="classes"
[class.unset]="classes !== ''" [class.unset]="classes !== ''"
href="mailto:support@datacontroller.io?subject=Licence" href="mailto:support@datacontroller.io?subject=Licence"
>support&#64;datacontroller.io</a >support@datacontroller.io</a
> >

View File

@ -6,31 +6,25 @@
> >
<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="dsmetaTabs.length < 1" class="text-center"> <p *ngIf="dsmetaGroupped.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 tab of tabs; let index = index"> <clr-tab *ngFor="let dsmeta of dsmetaGroupped; let index = index">
<button clrTabLink id="link1">{{ tab.name }}</button> <button clrTabLink id="link1">{{ dsmeta.group }}</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>
<ng-container *ngFor="let col of tab.colsToDisplay"> <clr-dg-column>Name</clr-dg-column>
<clr-dg-column>{{ col.colName || col.colKey }}</clr-dg-column> <clr-dg-column>Value</clr-dg-column>
</ng-container>
<clr-dg-row <clr-dg-row *ngFor="let info of dsmeta.dsmeta">
(click)="tab.onRowClick ? tab.onRowClick(info) : ''" <clr-dg-cell>{{ info.NAME }}</clr-dg-cell>
class="clickable-row" <clr-dg-cell>{{ info.VALUE }}</clr-dg-cell>
*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,23 +13,4 @@
} }
} }
} }
}
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, Version } from 'src/app/models/sas/editors-getdata.model' import { DSMeta } from 'src/app/models/sas/editors-getdata.model'
import { Tab } from './models/dsmeta-groupped.model' import { DSMetaGroupped } from './models/dsmeta-groupped.model'
@Component({ @Component({
selector: 'app-dataset-info', selector: 'app-dataset-info',
@ -18,15 +18,10 @@ import { Tab } 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>()
dsmetaTabs: Tab<DSMeta>[] = [] dsmetaGroupped: DSMetaGroupped[] = []
versionsTabs: Tab<Version>[] = []
tabs: Tab<DSMeta | Version>[] = []
constructor() {} constructor() {}
@ -35,58 +30,28 @@ 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.dsmetaTabs = [] this.dsmetaGroupped = []
for (let info of this.dsmeta) { for (let info of this.dsmeta) {
let groupIndex = this.dsmetaTabs.findIndex( let groupIndex = this.dsmetaGroupped.findIndex(
(x) => x.name === info.ODS_TABLE (x) => x.group === info.ODS_TABLE
) )
if (groupIndex < 0) if (groupIndex < 0)
groupIndex = groupIndex =
this.dsmetaTabs.push({ this.dsmetaGroupped.push({
name: info.ODS_TABLE, group: info.ODS_TABLE,
title: 'Dataset Meta', dsmeta: []
colsToDisplay: [{ colKey: 'NAME' }, { colKey: 'VALUE' }],
meta: [],
onRowClick: (value: DSMeta) => {
this.rowClicked.emit(value)
}
}) - 1 }) - 1
this.dsmetaTabs[groupIndex].meta.push(info) this.dsmetaGroupped[groupIndex].dsmeta.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,14 +1,6 @@
export interface Tab<T> { import { DSMeta } from 'src/app/models/sas/editors-getdata.model'
name: string
title: string export interface DSMetaGroupped {
/** group: string
* Columns to be displayed in the the grid dsmeta: DSMeta[]
* If empty, all columns will be displayed
*/
colsToDisplay: {
colKey: string
colName?: string
}[]
meta: T[]
onRowClick?: (value: any) => void
} }

View File

@ -2,7 +2,6 @@
<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"
@ -46,7 +45,6 @@
<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"
@ -93,7 +91,7 @@
" "
class="clr-treenode-link" class="clr-treenode-link"
[class.dc-locked-control]="tableLocked" [class.dc-locked-control]="tableLocked"
[class.active]="libTabActive(library.LIBRARYREF, libTable)" [class.table-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>
@ -108,7 +106,7 @@
*clrIfOpen *clrIfOpen
> >
<span *ngIf="tableLocked"> <span *ngIf="tableLocked">
To unlock all tables, contact support&#64;datacontroller.io To unlock all tables, contact support@datacontroller.io
</span> </span>
</clr-tooltip-content> </clr-tooltip-content>
@ -116,7 +114,6 @@
<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,7 +20,6 @@ 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[] = []
@ -39,8 +38,6 @@ 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)
@ -54,13 +51,6 @@ export class DcValidator {
this.setupValidations() this.setupValidations()
} }
registerCustomEditors() {
Handsontable.editors.registerEditor(
'autocomplete.custom',
CustomAutocompleteEditor
)
}
getRules(): DcValidation[] { getRules(): DcValidation[] {
return this.rules return this.rules
} }
@ -272,7 +262,6 @@ 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
} }
@ -326,10 +315,7 @@ 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 ( if (this.editor === 'autocomplete') {
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

@ -1,28 +0,0 @@
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,8 +11,6 @@ $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,7 +1,5 @@
@import '../../../colors.scss';
.sideBarProps { .sideBarProps {
background: $headerBackground !important; background: #314351!important;
color: #e0e0e0; color: #e0e0e0;
} }

View File

@ -16,6 +16,8 @@
*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,6 +70,7 @@
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 { HeaderActions } from './user-nav-dropdown/header-actions.component' import { UserNavDropdownComponent } from './user-nav-dropdown/user-nav-dropdown.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,
HeaderActions, UserNavDropdownComponent,
AlertsComponent, AlertsComponent,
TermsComponent, TermsComponent,
DatasetInfoComponent, DatasetInfoComponent,
@ -35,7 +35,7 @@ import { ContactLinkComponent } from './contact-link/contact-link.component'
exports: [ exports: [
LoadingIndicatorComponent, LoadingIndicatorComponent,
LoginComponent, LoginComponent,
HeaderActions, UserNavDropdownComponent,
AlertsComponent, AlertsComponent,
TermsComponent, TermsComponent,
DatasetInfoComponent, DatasetInfoComponent,

View File

@ -107,29 +107,7 @@
</clr-tab-content> </clr-tab-content>
</clr-tab> </clr-tab>
</clr-tabs> </clr-tabs>
<p *ngIf="isMainRoute('home')" class="page-title">Edit</p>
<div
*ngIf="isMainRoute('home')"
class="d-flex justify-content-center sub-dropdown"
>
<clr-dropdown>
<button class="dropdown-toggle btn btn-link" clrDropdownTrigger>
{{ getSubPage() }}
<clr-icon shape="caret down"></clr-icon>
</button>
<clr-dropdown-menu *clrIfOpen>
<a
clrVerticalNavLink
routerLink="/home/tables"
routerLinkActive="active"
>Tables</a
>
<a clrVerticalNavLink routerLink="/home/files" routerLinkActive="active"
>Files</a
>
</clr-dropdown-menu>
</clr-dropdown>
</div>
<div class="nav-divider"></div> <div class="nav-divider"></div>

View File

@ -1,10 +1,5 @@
$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;
@ -12,6 +7,7 @@ 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,22 +1,4 @@
<label <label *ngIf="label" class="clr-control-label">{{ label }}</label>
*ngIf="label"
[class.secondLabelActive]="secondLabel && secondLabel.length > 0"
class="clr-control-label"
>
<span
(click)="onChangeLabel('first')"
[class.value-type-selected]="labelSelected === 'first'"
>{{ label }}</span
>
<ng-container *ngIf="secondLabel">
/
<span
(click)="onChangeLabel('second')"
[class.value-type-selected]="labelSelected === 'second'"
>{{ secondLabel }}</span
>
</ng-container>
</label>
<ng-container [ngSwitch]="type"> <ng-container [ngSwitch]="type">
<ng-container *ngSwitchCase="'date'"> <ng-container *ngSwitchCase="'date'">
<clr-date-container> <clr-date-container>

View File

@ -1,6 +1,6 @@
.input-val { .input-val {
border: 0px; border: 0px;
background: transparent; background: #fbf8f8;
border-bottom: 1px solid #999999; border-bottom: 1px solid #999999;
} }
@ -28,12 +28,4 @@ clr-date-container {
margin-top: -5px; margin-top: -5px;
} }
} }
}
label.secondLabelActive span {
&:not(.value-type-selected) {
text-decoration: line-through;
cursor: pointer;
opacity: 0.6;
}
} }

View File

@ -18,7 +18,6 @@ import { OnLoadingMoreEvent } from '../autocomplete/autocomplete.component'
export class SoftSelectComponent implements OnInit, OnChanges { export class SoftSelectComponent implements OnInit, OnChanges {
@Input() inputId: string = '' @Input() inputId: string = ''
@Input() label: string | undefined @Input() label: string | undefined
@Input() secondLabel: string | undefined
@Input() value: Date | string | null = '' @Input() value: Date | string | null = ''
@Input() disabled: boolean = false @Input() disabled: boolean = false
@Input() type: string = 'text' @Input() type: string = 'text'
@ -31,24 +30,20 @@ export class SoftSelectComponent implements OnInit, OnChanges {
@Output() focusinInput: EventEmitter<any> = new EventEmitter() @Output() focusinInput: EventEmitter<any> = new EventEmitter()
@Output() onAutocompleteLoadingMore: EventEmitter<OnLoadingMoreEvent> = @Output() onAutocompleteLoadingMore: EventEmitter<OnLoadingMoreEvent> =
new EventEmitter() new EventEmitter()
@Output() selectedLabelChange: EventEmitter<string> = new EventEmitter()
@ViewChild('input') inputElement: any @ViewChild('input') inputElement: any
temp: Date | string | null = '' temp: Date | string | null = ''
inputFocused: boolean = false inputFocused: boolean = false
labelSelected: LabelTypes = 'first'
constructor() {} constructor() {}
ngOnChanges(changes: SimpleChanges): void { ngOnChanges(changes: SimpleChanges): void {
if ( if (
changes.value && changes.value &&
changes.value.currentValue !== changes.value.previousValue changes.value.currentValue !== changes.value.previousValue
) { )
this.valueChange.emit(changes.value.currentValue) this.valueChange.emit(changes.value.currentValue)
}
} }
ngOnInit(): void {} ngOnInit(): void {}
@ -90,14 +85,4 @@ export class SoftSelectComponent implements OnInit, OnChanges {
onFocusinInput(event: any) { onFocusinInput(event: any) {
this.focusinInput.emit(event) this.focusinInput.emit(event)
} }
onChangeLabel(label: LabelTypes) {
this.labelSelected = label
const selectedLabelText = label === 'first' ? this.label : this.secondLabel
this.selectedLabelChange.emit(selectedLabelText)
}
} }
export type LabelTypes = 'first' | 'second'

View File

@ -1,90 +0,0 @@
<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

@ -0,0 +1,121 @@
<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

@ -1,12 +1,17 @@
// 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;
@ -65,6 +70,9 @@ $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-header-actions', selector: 'app-user-nav-dropdown',
templateUrl: './header-actions.component.html', templateUrl: './user-nav-dropdown.component.html',
styleUrls: ['./header-actions.component.scss'] styleUrls: ['./user-nav-dropdown.component.scss']
}) })
export class HeaderActions implements OnInit, OnDestroy { export class UserNavDropdownComponent 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,7 +26,6 @@ export class HeaderActions 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,
@ -52,10 +51,6 @@ export class HeaderActions 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

@ -13,7 +13,7 @@
class="licence-notice" class="licence-notice"
>To unlock more then {{ licenceState.value.viewbox_limit }} >To unlock more then {{ licenceState.value.viewbox_limit }}
{{ licenceState.value.viewbox_limit === 1 ? 'viewbox' : 'viewboxes' }}, {{ licenceState.value.viewbox_limit === 1 ? 'viewbox' : 'viewboxes' }},
contact support&#64;datacontroller.io</span contact support@datacontroller.io</span
> >
</h3> </h3>
@ -389,7 +389,6 @@
*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"

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