Compare commits
23 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
b1db4ea590 | ||
822ddb1274 | |||
|
672dd6d4f1 | ||
|
b3ac73d903 | ||
|
f8554dd5e7 | ||
88679c0c9a | |||
|
a08a717ca8 | ||
c8b6fdbfdb | |||
|
b495c41626 | ||
|
7f4be474c6 | ||
7f6f68fcbb | |||
|
03fd7db033 | ||
|
9dc5c66f7b | ||
|
aa1b08632e | ||
|
6bbe354c9e | ||
|
8ff429793b | ||
|
70d010127a | ||
|
696717c509 | ||
|
71c308d052 | ||
|
bed5b320ad | ||
b0e827412e | |||
|
63e9af402e | ||
fd55105f62 |
@@ -10,3 +10,14 @@ else
|
|||||||
echo "❌ Prettier check failed! We ran lint:fix for you. Please add & commit again."
|
echo "❌ Prettier check failed! We ran lint:fix for you. Please add & commit again."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
## Avoid large commits
|
||||||
|
# https://www.backblaze.com/blog/how-many-bytes-are-in-a-megabyte-really/
|
||||||
|
size_limit=$((2 * 2**20)) # 2mbs
|
||||||
|
# https://git-scm.com/docs/git-rev-list#Documentation/git-rev-list.txt---disk-usage
|
||||||
|
commit_size=$(git rev-list --disk-usage HEAD^..HEAD)
|
||||||
|
test "$commit_size" -lt "$size_limit" || (
|
||||||
|
echo "Commit size is too large: $commit_size > $size_limit"
|
||||||
|
echo "Force commit using --no-verify"
|
||||||
|
exit 1
|
||||||
|
)
|
@@ -126,7 +126,7 @@ jobs:
|
|||||||
replace-in-files --regex='"hosturl".*' --replacement='hosturl:"http://localhost:4200",' ./cypress.config.ts
|
replace-in-files --regex='"hosturl".*' --replacement='hosturl:"http://localhost:4200",' ./cypress.config.ts
|
||||||
cat ./cypress.config.ts
|
cat ./cypress.config.ts
|
||||||
# Start frontend and run cypress
|
# 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-multi-load.cy.ts,cypress/e2e/excel.cy.ts,cypress/e2e/csv.cy.ts,cypress/e2e/filtering.cy.ts,cypress/e2e/licensing.cy.ts"
|
npx ng serve --host 0.0.0.0 --port 4200 & 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-multi-load.cy.ts,cypress/e2e/excel.cy.ts,cypress/e2e/csv.cy.ts,cypress/e2e/filtering.cy.ts,cypress/e2e/licensing.cy.ts"
|
||||||
|
|
||||||
- name: Zip Cypress videos
|
- name: Zip Cypress videos
|
||||||
if: always()
|
if: always()
|
||||||
|
@@ -136,7 +136,7 @@ jobs:
|
|||||||
replace-in-files --regex='"hosturl".*' --replacement='hosturl:"http://localhost:4200",' ./cypress.config.ts
|
replace-in-files --regex='"hosturl".*' --replacement='hosturl:"http://localhost:4200",' ./cypress.config.ts
|
||||||
cat ./cypress.config.ts
|
cat ./cypress.config.ts
|
||||||
# Start frontend and run cypress
|
# 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-multi-load.cy.ts,cypress/e2e/excel.cy.ts,cypress/e2e/csv.cy.ts,cypress/e2e/filtering.cy.ts,cypress/e2e/licensing.cy.ts"
|
npx ng serve --host 0.0.0.0 --port 4200 & 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-multi-load.cy.ts,cypress/e2e/excel.cy.ts,cypress/e2e/csv.cy.ts,cypress/e2e/filtering.cy.ts,cypress/e2e/licensing.cy.ts"
|
||||||
|
|
||||||
- name: Zip Cypress videos
|
- name: Zip Cypress videos
|
||||||
if: always()
|
if: always()
|
||||||
|
21
CHANGELOG.md
21
CHANGELOG.md
@@ -1,3 +1,24 @@
|
|||||||
|
## [7.1.1](https://git.datacontroller.io/dc/dc/compare/v7.1.0...v7.1.1) (2025-07-24)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **viewboxes:** hot v16 fails to load because of relative height `100%` ([672dd6d](https://git.datacontroller.io/dc/dc/commit/672dd6d4f1fda27e3706dd7caa42b45922319497))
|
||||||
|
|
||||||
|
# [7.1.0](https://git.datacontroller.io/dc/dc/compare/v7.0.3...v7.1.0) (2025-07-23)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* adapter bump ([b495c41](https://git.datacontroller.io/dc/dc/commit/b495c41626c85b7c4141d9361e4d3a826efd6c05))
|
||||||
|
* bumping CLI to 4.12.10 ([a08a717](https://git.datacontroller.io/dc/dc/commit/a08a717ca8d49e8a7d63f3fd91c6a7d42a1d6d8b))
|
||||||
|
* bumping sasjs/core and sasjs/cli ([63e9af4](https://git.datacontroller.io/dc/dc/commit/63e9af402ed65f6be4426e76ee1376a40e6ed097))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* improving accessibility score up to 100, hot update to v16.0.1 ([71c308d](https://git.datacontroller.io/dc/dc/commit/71c308d052400ecedc03f8020a5a69471ac6b116))
|
||||||
|
|
||||||
## [7.0.3](https://git.datacontroller.io/dc/dc/compare/v7.0.2...v7.0.3) (2025-06-26)
|
## [7.0.3](https://git.datacontroller.io/dc/dc/compare/v7.0.2...v7.0.3) (2025-06-26)
|
||||||
|
|
||||||
|
|
||||||
|
@@ -10,7 +10,7 @@ const check = (cwd) => {
|
|||||||
onlyAllow:
|
onlyAllow:
|
||||||
'AFLv2.1;Apache 2.0;Apache-2.0;Apache*;Artistic-2.0;0BSD;BSD*;BSD-2-Clause;BSD-3-Clause;CC0-1.0;CC-BY-3.0;CC-BY-4.0;ISC;MIT;MPL-2.0;ODC-By-1.0;Python-2.0;Unlicense;',
|
'AFLv2.1;Apache 2.0;Apache-2.0;Apache*;Artistic-2.0;0BSD;BSD*;BSD-2-Clause;BSD-3-Clause;CC0-1.0;CC-BY-3.0;CC-BY-4.0;ISC;MIT;MPL-2.0;ODC-By-1.0;Python-2.0;Unlicense;',
|
||||||
excludePackages:
|
excludePackages:
|
||||||
'@cds/city@1.1.0;@handsontable/angular@15.3.0;handsontable@15.3.0;hyperformula@2.7.1;hyperformula@3.0.0;jackspeak@3.4.3;path-scurry@1.11.1;package-json-from-dist@1.0.1'
|
'@cds/city@1.1.0;@handsontable/angular@16.0.1;handsontable@16.0.1;hyperformula@2.7.1;hyperformula@3.0.0;jackspeak@3.4.3;path-scurry@1.11.1;package-json-from-dist@1.0.1'
|
||||||
},
|
},
|
||||||
(error, json) => {
|
(error, json) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
|
1308
client/package-lock.json
generated
1308
client/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -48,8 +48,8 @@
|
|||||||
"@clr/angular": "file:libraries/clr-angular-17.9.0.tgz",
|
"@clr/angular": "file:libraries/clr-angular-17.9.0.tgz",
|
||||||
"@clr/icons": "^13.0.2",
|
"@clr/icons": "^13.0.2",
|
||||||
"@clr/ui": "file:libraries/clr-ui-17.9.0.tgz",
|
"@clr/ui": "file:libraries/clr-ui-17.9.0.tgz",
|
||||||
"@handsontable/angular": "^15.3.0",
|
"@handsontable/angular": "^16.0.1",
|
||||||
"@sasjs/adapter": "^4.12.1",
|
"@sasjs/adapter": "^4.12.2",
|
||||||
"@sasjs/utils": "^3.4.0",
|
"@sasjs/utils": "^3.4.0",
|
||||||
"@sheet/crypto": "file:libraries/sheet-crypto.tgz",
|
"@sheet/crypto": "file:libraries/sheet-crypto.tgz",
|
||||||
"@types/d3-graphviz": "^2.6.7",
|
"@types/d3-graphviz": "^2.6.7",
|
||||||
@@ -60,7 +60,7 @@
|
|||||||
"crypto-js": "^4.2.0",
|
"crypto-js": "^4.2.0",
|
||||||
"d3-graphviz": "^5.0.2",
|
"d3-graphviz": "^5.0.2",
|
||||||
"fs-extra": "^7.0.1",
|
"fs-extra": "^7.0.1",
|
||||||
"handsontable": "^15.3.0",
|
"handsontable": "^16.0.1",
|
||||||
"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",
|
||||||
|
@@ -167,7 +167,7 @@ export class AutomaticComponent implements OnInit {
|
|||||||
public async getAdminGroups() {
|
public async getAdminGroups() {
|
||||||
return new Promise<void>((resolve, reject) => {
|
return new Promise<void>((resolve, reject) => {
|
||||||
this.adminGroupsLoading = true
|
this.adminGroupsLoading = true
|
||||||
this.sasViyaService
|
;(this.sasViyaService
|
||||||
.getAdminGroups()
|
.getAdminGroups()
|
||||||
.subscribe((res: ViyaApiIdentities) => {
|
.subscribe((res: ViyaApiIdentities) => {
|
||||||
this.adminGroupsLoading = false
|
this.adminGroupsLoading = false
|
||||||
@@ -187,7 +187,7 @@ export class AutomaticComponent implements OnInit {
|
|||||||
this.eventService.showAbortModal('admin groups', err)
|
this.eventService.showAbortModal('admin groups', err)
|
||||||
|
|
||||||
reject(err)
|
reject(err)
|
||||||
}
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -3,6 +3,7 @@ import {
|
|||||||
ChangeDetectorRef,
|
ChangeDetectorRef,
|
||||||
Component,
|
Component,
|
||||||
ElementRef,
|
ElementRef,
|
||||||
|
OnDestroy,
|
||||||
OnInit,
|
OnInit,
|
||||||
QueryList,
|
QueryList,
|
||||||
ViewChild,
|
ViewChild,
|
||||||
@@ -71,7 +72,7 @@ import { ParseResult } from '../models/ParseResult.interface'
|
|||||||
},
|
},
|
||||||
encapsulation: ViewEncapsulation.None
|
encapsulation: ViewEncapsulation.None
|
||||||
})
|
})
|
||||||
export class EditorComponent implements OnInit, AfterViewInit {
|
export class EditorComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
@ViewChildren('uploadStater')
|
@ViewChildren('uploadStater')
|
||||||
uploadStaterCompList: QueryList<UploadStaterComponent> = new QueryList()
|
uploadStaterCompList: QueryList<UploadStaterComponent> = new QueryList()
|
||||||
@ViewChildren('queryFilter')
|
@ViewChildren('queryFilter')
|
||||||
@@ -124,7 +125,7 @@ export class EditorComponent implements OnInit, AfterViewInit {
|
|||||||
colHeaders: [],
|
colHeaders: [],
|
||||||
hidden: true,
|
hidden: true,
|
||||||
columns: [],
|
columns: [],
|
||||||
height: '100%',
|
height: 'calc(100vh - 160px)',
|
||||||
minSpareRows: 1,
|
minSpareRows: 1,
|
||||||
licenseKey: undefined,
|
licenseKey: undefined,
|
||||||
readOnly: true,
|
readOnly: true,
|
||||||
@@ -350,6 +351,9 @@ export class EditorComponent implements OnInit, AfterViewInit {
|
|||||||
|
|
||||||
public licenceState = this.licenceService.licenceState
|
public licenceState = this.licenceService.licenceState
|
||||||
|
|
||||||
|
private ariaObserver: MutationObserver | undefined
|
||||||
|
private ariaCheckInterval: any | undefined
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private licenceService: LicenceService,
|
private licenceService: LicenceService,
|
||||||
private eventService: EventService,
|
private eventService: EventService,
|
||||||
@@ -896,6 +900,11 @@ export class EditorComponent implements OnInit, AfterViewInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.reSetCellValidationValues()
|
this.reSetCellValidationValues()
|
||||||
|
|
||||||
|
// Fix ARIA accessibility issues after table edit
|
||||||
|
setTimeout(() => {
|
||||||
|
this.fixAriaAccessibility()
|
||||||
|
}, 100)
|
||||||
}, 0)
|
}, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2262,7 +2271,173 @@ export class EditorComponent implements OnInit, AfterViewInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ngAfterViewInit() {}
|
ngAfterViewInit() {
|
||||||
|
// Fix ARIA accessibility issues after table initialization
|
||||||
|
setTimeout(() => {
|
||||||
|
this.fixAriaAccessibility()
|
||||||
|
}, 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
// Clean up the MutationObserver
|
||||||
|
if (this.ariaObserver) {
|
||||||
|
this.ariaObserver.disconnect()
|
||||||
|
this.ariaObserver = undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up the interval
|
||||||
|
if (this.ariaCheckInterval) {
|
||||||
|
clearInterval(this.ariaCheckInterval)
|
||||||
|
this.ariaCheckInterval = undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fixes ARIA accessibility issues in the Handsontable component
|
||||||
|
* This addresses the accessibility report issues with treegrid and presentation roles
|
||||||
|
*/
|
||||||
|
private fixAriaAccessibility() {
|
||||||
|
// Use a more aggressive approach to find and fix all ARIA issues
|
||||||
|
const fixAriaIssues = () => {
|
||||||
|
// Specifically target Handsontable wrapper elements that are causing issues
|
||||||
|
const hotWrappers = document.querySelectorAll(
|
||||||
|
'.ht-wrapper, .wtHolder, [id^="ht_"]'
|
||||||
|
)
|
||||||
|
|
||||||
|
hotWrappers.forEach((wrapper) => {
|
||||||
|
// Remove problematic ARIA attributes from Handsontable wrappers
|
||||||
|
wrapper.removeAttribute('role')
|
||||||
|
wrapper.removeAttribute('aria-rowcount')
|
||||||
|
wrapper.removeAttribute('aria-colcount')
|
||||||
|
wrapper.removeAttribute('aria-multiselectable')
|
||||||
|
})
|
||||||
|
|
||||||
|
// Find all elements with problematic ARIA roles in the entire document
|
||||||
|
const allTreegridElements = document.querySelectorAll('[role="treegrid"]')
|
||||||
|
const allPresentationElements = document.querySelectorAll(
|
||||||
|
'[role="presentation"]'
|
||||||
|
)
|
||||||
|
|
||||||
|
// Fix treegrid role issues - remove them completely as they're causing problems
|
||||||
|
allTreegridElements.forEach((element) => {
|
||||||
|
element.removeAttribute('role')
|
||||||
|
element.removeAttribute('aria-rowcount')
|
||||||
|
element.removeAttribute('aria-colcount')
|
||||||
|
element.removeAttribute('aria-multiselectable')
|
||||||
|
})
|
||||||
|
|
||||||
|
// Fix presentation role issues - remove them if they contain interactive elements
|
||||||
|
allPresentationElements.forEach((element) => {
|
||||||
|
const hasInteractiveChildren =
|
||||||
|
element.querySelectorAll(
|
||||||
|
'button, input, select, textarea, [tabindex], [onclick], [contenteditable]'
|
||||||
|
).length > 0
|
||||||
|
if (hasInteractiveChildren) {
|
||||||
|
element.removeAttribute('role')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Also fix any elements with aria-rowcount="-1" which is problematic
|
||||||
|
const negativeRowCountElements = document.querySelectorAll(
|
||||||
|
'[aria-rowcount="-1"]'
|
||||||
|
)
|
||||||
|
negativeRowCountElements.forEach((element) => {
|
||||||
|
element.removeAttribute('aria-rowcount')
|
||||||
|
})
|
||||||
|
|
||||||
|
// Ensure proper table structure
|
||||||
|
const tableElements = document.querySelectorAll('table')
|
||||||
|
tableElements.forEach((table) => {
|
||||||
|
if (!table.getAttribute('role')) {
|
||||||
|
table.setAttribute('role', 'table')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure table headers have proper scope
|
||||||
|
const headerCells = table.querySelectorAll('th')
|
||||||
|
headerCells.forEach((th) => {
|
||||||
|
if (!th.getAttribute('scope')) {
|
||||||
|
th.setAttribute('scope', 'col')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// Add proper ARIA labels to interactive elements
|
||||||
|
const interactiveElements = document.querySelectorAll(
|
||||||
|
'button, input, select, textarea, [contenteditable]'
|
||||||
|
)
|
||||||
|
interactiveElements.forEach((element) => {
|
||||||
|
if (
|
||||||
|
!element.getAttribute('aria-label') &&
|
||||||
|
!element.getAttribute('aria-labelledby')
|
||||||
|
) {
|
||||||
|
const textContent = element.textContent?.trim()
|
||||||
|
if (textContent) {
|
||||||
|
element.setAttribute('aria-label', textContent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the fix immediately
|
||||||
|
fixAriaIssues()
|
||||||
|
|
||||||
|
// Run it again after a short delay to catch any dynamically created elements
|
||||||
|
setTimeout(fixAriaIssues, 100)
|
||||||
|
setTimeout(fixAriaIssues, 500)
|
||||||
|
setTimeout(fixAriaIssues, 1000)
|
||||||
|
setTimeout(fixAriaIssues, 2000)
|
||||||
|
|
||||||
|
// Set up a periodic check to ensure accessibility fixes are maintained
|
||||||
|
if (!this.ariaCheckInterval) {
|
||||||
|
this.ariaCheckInterval = setInterval(fixAriaIssues, 3000)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up a MutationObserver to continuously monitor for new problematic elements
|
||||||
|
if (!this.ariaObserver) {
|
||||||
|
this.ariaObserver = new MutationObserver((mutations) => {
|
||||||
|
let shouldFix = false
|
||||||
|
mutations.forEach((mutation) => {
|
||||||
|
if (
|
||||||
|
mutation.type === 'attributes' &&
|
||||||
|
(mutation.attributeName === 'role' ||
|
||||||
|
mutation.attributeName === 'aria-rowcount')
|
||||||
|
) {
|
||||||
|
shouldFix = true
|
||||||
|
}
|
||||||
|
if (mutation.type === 'childList') {
|
||||||
|
mutation.addedNodes.forEach((node) => {
|
||||||
|
if (node.nodeType === Node.ELEMENT_NODE) {
|
||||||
|
const element = node as Element
|
||||||
|
if (
|
||||||
|
element.hasAttribute('role') ||
|
||||||
|
element.hasAttribute('aria-rowcount')
|
||||||
|
) {
|
||||||
|
shouldFix = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (shouldFix) {
|
||||||
|
setTimeout(fixAriaIssues, 50)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Start observing the entire document for changes
|
||||||
|
this.ariaObserver.observe(document.body, {
|
||||||
|
childList: true,
|
||||||
|
subtree: true,
|
||||||
|
attributes: true,
|
||||||
|
attributeFilter: [
|
||||||
|
'role',
|
||||||
|
'aria-rowcount',
|
||||||
|
'aria-colcount',
|
||||||
|
'aria-multiselectable'
|
||||||
|
]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
initSetup(response: EditorsGetDataServiceResponse) {
|
initSetup(response: EditorsGetDataServiceResponse) {
|
||||||
this.hotInstance = this.hotRegisterer.getInstance('hotInstance')
|
this.hotInstance = this.hotRegisterer.getInstance('hotInstance')
|
||||||
@@ -2597,6 +2772,17 @@ export class EditorComponent implements OnInit, AfterViewInit {
|
|||||||
|
|
||||||
hot.addHook('afterRender', (isForced: boolean) => {
|
hot.addHook('afterRender', (isForced: boolean) => {
|
||||||
this.eventService.dispatchEvent('resize')
|
this.eventService.dispatchEvent('resize')
|
||||||
|
|
||||||
|
// Fix ARIA accessibility issues after each render
|
||||||
|
this.fixAriaAccessibility()
|
||||||
|
})
|
||||||
|
|
||||||
|
// Add a more frequent accessibility fix hook
|
||||||
|
hot.addHook('afterChange', () => {
|
||||||
|
// Fix ARIA accessibility issues after any data change
|
||||||
|
setTimeout(() => {
|
||||||
|
this.fixAriaAccessibility()
|
||||||
|
}, 50)
|
||||||
})
|
})
|
||||||
|
|
||||||
hot.addHook('afterCreateRow', (source: any, change: any) => {
|
hot.addHook('afterCreateRow', (source: any, change: any) => {
|
||||||
@@ -2660,5 +2846,10 @@ export class EditorComponent implements OnInit, AfterViewInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
hot.render()
|
hot.render()
|
||||||
|
|
||||||
|
// Fix ARIA accessibility issues after table initialization
|
||||||
|
setTimeout(() => {
|
||||||
|
this.fixAriaAccessibility()
|
||||||
|
}, 500)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -99,6 +99,11 @@ export class MetadataComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
this.pageSize = 5
|
this.pageSize = 5
|
||||||
|
|
||||||
|
// Initialize filters for accessibility
|
||||||
|
this.typeFilter = new TypeFilter()
|
||||||
|
this.nameFilter = new NameFilter()
|
||||||
|
this.valueFilter = new ValueFilter()
|
||||||
|
|
||||||
if (
|
if (
|
||||||
globals.metadata.metaDataList &&
|
globals.metadata.metaDataList &&
|
||||||
globals.metadata.metaRepositories &&
|
globals.metadata.metaRepositories &&
|
||||||
|
@@ -33,12 +33,34 @@
|
|||||||
<div class="clr-col-md-12" ng-if="loaded">
|
<div class="clr-col-md-12" ng-if="loaded">
|
||||||
<div *ngIf="approveList && remained !== 0">
|
<div *ngIf="approveList && remained !== 0">
|
||||||
<clr-datagrid class="datagrid-compact datagrid-custom-footer">
|
<clr-datagrid class="datagrid-compact datagrid-custom-footer">
|
||||||
<clr-dg-column [clrDgField]="'submitter'">SUBMITTER</clr-dg-column>
|
<clr-dg-column [clrDgField]="'submitter'">
|
||||||
<clr-dg-column [clrDgField]="'baseTable'">BASE TABLE</clr-dg-column>
|
SUBMITTER
|
||||||
<clr-dg-column [clrDgField]="'submitted'">SUBMITTED</clr-dg-column>
|
<clr-dg-string-filter
|
||||||
<clr-dg-column [clrDgField]="'submitReason'"
|
[clrDgStringFilter]="submitterFilter"
|
||||||
>SUBMIT REASON</clr-dg-column
|
aria-label="Filter submitter"
|
||||||
>
|
></clr-dg-string-filter>
|
||||||
|
</clr-dg-column>
|
||||||
|
<clr-dg-column [clrDgField]="'baseTable'">
|
||||||
|
BASE TABLE
|
||||||
|
<clr-dg-string-filter
|
||||||
|
[clrDgStringFilter]="baseTableFilter"
|
||||||
|
aria-label="Filter base table"
|
||||||
|
></clr-dg-string-filter>
|
||||||
|
</clr-dg-column>
|
||||||
|
<clr-dg-column [clrDgField]="'submitted'">
|
||||||
|
SUBMITTED
|
||||||
|
<clr-dg-string-filter
|
||||||
|
[clrDgStringFilter]="submittedFilter"
|
||||||
|
aria-label="Filter submitted date"
|
||||||
|
></clr-dg-string-filter>
|
||||||
|
</clr-dg-column>
|
||||||
|
<clr-dg-column [clrDgField]="'submitReason'">
|
||||||
|
SUBMIT REASON
|
||||||
|
<clr-dg-string-filter
|
||||||
|
[clrDgStringFilter]="submitReasonFilter"
|
||||||
|
aria-label="Filter submit reason"
|
||||||
|
></clr-dg-string-filter>
|
||||||
|
</clr-dg-column>
|
||||||
<clr-dg-column>ACTION</clr-dg-column>
|
<clr-dg-column>ACTION</clr-dg-column>
|
||||||
<clr-dg-column>DOWNLOAD</clr-dg-column>
|
<clr-dg-column>DOWNLOAD</clr-dg-column>
|
||||||
|
|
||||||
@@ -51,15 +73,19 @@
|
|||||||
<clr-dg-cell>{{ approveItem.submitReason }}</clr-dg-cell>
|
<clr-dg-cell>{{ approveItem.submitReason }}</clr-dg-cell>
|
||||||
<clr-dg-cell>
|
<clr-dg-cell>
|
||||||
<div
|
<div
|
||||||
class="clr-row"
|
class="clr-row d-flex justify-content-around"
|
||||||
role="tooltip"
|
role="toolbar"
|
||||||
class="d-flex justify-content-around"
|
aria-label="Table actions"
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
class="column-center links tooltip tooltip-md tooltip-bottom-left color-green"
|
class="column-center links tooltip tooltip-md tooltip-bottom-left color-green"
|
||||||
(click)="getClicked(i)"
|
(click)="getClicked(i)"
|
||||||
>
|
>
|
||||||
<clr-icon shape="check" size="24"></clr-icon>
|
<clr-icon
|
||||||
|
shape="check"
|
||||||
|
size="24"
|
||||||
|
aria-hidden="true"
|
||||||
|
></clr-icon>
|
||||||
<span class="tooltip-content">Go to review page screen</span>
|
<span class="tooltip-content">Go to review page screen</span>
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
@@ -70,10 +96,12 @@
|
|||||||
*ngIf="!approveItem.rejectLoading"
|
*ngIf="!approveItem.rejectLoading"
|
||||||
shape="ban"
|
shape="ban"
|
||||||
size="22"
|
size="22"
|
||||||
|
aria-hidden="true"
|
||||||
></clr-icon>
|
></clr-icon>
|
||||||
<clr-spinner
|
<clr-spinner
|
||||||
*ngIf="approveItem.rejectLoading"
|
*ngIf="approveItem.rejectLoading"
|
||||||
[clrSmall]="true"
|
[clrSmall]="true"
|
||||||
|
aria-hidden="true"
|
||||||
></clr-spinner>
|
></clr-spinner>
|
||||||
<span class="tooltip-content">Reject</span>
|
<span class="tooltip-content">Reject</span>
|
||||||
</a>
|
</a>
|
||||||
@@ -81,7 +109,11 @@
|
|||||||
class="column-center links tooltip tooltip-md tooltip-bottom-left color-blue"
|
class="column-center links tooltip tooltip-md tooltip-bottom-left color-blue"
|
||||||
(click)="getTable(approveItem.tableId)"
|
(click)="getTable(approveItem.tableId)"
|
||||||
>
|
>
|
||||||
<clr-icon shape="code" size="28"></clr-icon>
|
<clr-icon
|
||||||
|
shape="code"
|
||||||
|
size="28"
|
||||||
|
aria-hidden="true"
|
||||||
|
></clr-icon>
|
||||||
<span class="tooltip-content">Go to staged data screen</span>
|
<span class="tooltip-content">Go to staged data screen</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -8,6 +8,7 @@ import { SasStoreService } from '../../services/sas-store.service'
|
|||||||
import { Router } from '@angular/router'
|
import { Router } from '@angular/router'
|
||||||
import { SasService } from '../../services/sas.service'
|
import { SasService } from '../../services/sas.service'
|
||||||
import { EventService } from '../../services/event.service'
|
import { EventService } from '../../services/event.service'
|
||||||
|
import { ClrDatagridStringFilterInterface } from '@clr/angular'
|
||||||
|
|
||||||
interface ApproveData {
|
interface ApproveData {
|
||||||
tableId: string
|
tableId: string
|
||||||
@@ -19,6 +20,32 @@ interface ApproveData {
|
|||||||
rejectLoading?: boolean
|
rejectLoading?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class SubmitterFilter implements ClrDatagridStringFilterInterface<ApproveData> {
|
||||||
|
accepts(data: ApproveData, search: string): boolean {
|
||||||
|
return data.submitter.toLowerCase().indexOf(search.toLowerCase()) >= 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BaseTableFilter implements ClrDatagridStringFilterInterface<ApproveData> {
|
||||||
|
accepts(data: ApproveData, search: string): boolean {
|
||||||
|
return data.baseTable.toLowerCase().indexOf(search.toLowerCase()) >= 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SubmittedFilter implements ClrDatagridStringFilterInterface<ApproveData> {
|
||||||
|
accepts(data: ApproveData, search: string): boolean {
|
||||||
|
return data.submitted.toLowerCase().indexOf(search.toLowerCase()) >= 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SubmitReasonFilter
|
||||||
|
implements ClrDatagridStringFilterInterface<ApproveData>
|
||||||
|
{
|
||||||
|
accepts(data: ApproveData, search: string): boolean {
|
||||||
|
return data.submitReason.toLowerCase().indexOf(search.toLowerCase()) >= 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-approve',
|
selector: 'app-approve',
|
||||||
templateUrl: './approve.component.html',
|
templateUrl: './approve.component.html',
|
||||||
@@ -35,6 +62,12 @@ export class ApproveComponent implements OnInit {
|
|||||||
public loaded: boolean = false
|
public loaded: boolean = false
|
||||||
public itemsNum: number = 10
|
public itemsNum: number = 10
|
||||||
|
|
||||||
|
// Filter instances for datagrid accessibility
|
||||||
|
public submitterFilter = new SubmitterFilter()
|
||||||
|
public baseTableFilter = new BaseTableFilter()
|
||||||
|
public submittedFilter = new SubmittedFilter()
|
||||||
|
public submitReasonFilter = new SubmitReasonFilter()
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private sasStoreService: SasStoreService,
|
private sasStoreService: SasStoreService,
|
||||||
private eventService: EventService,
|
private eventService: EventService,
|
||||||
|
@@ -85,16 +85,48 @@
|
|||||||
class="datagrid-history datagrid-custom-footer"
|
class="datagrid-history datagrid-custom-footer"
|
||||||
*ngIf="loaded"
|
*ngIf="loaded"
|
||||||
>
|
>
|
||||||
<clr-dg-column [clrDgField]="'basetable'">BASE_TABLE</clr-dg-column>
|
<clr-dg-column [clrDgField]="'basetable'">
|
||||||
<clr-dg-column [clrDgField]="'status'">STATUS</clr-dg-column>
|
BASE_TABLE
|
||||||
<clr-dg-column [clrDgField]="'submitter'">SUBMITTER</clr-dg-column>
|
<clr-dg-string-filter
|
||||||
<clr-dg-column [clrDgField]="'submittedReason'"
|
[clrDgStringFilter]="baseTableFilter"
|
||||||
>SUBMIT REASON</clr-dg-column
|
aria-label="Filter base table"
|
||||||
>
|
></clr-dg-string-filter>
|
||||||
<clr-dg-column [clrDgField]="'submitted'">SUBMITTED</clr-dg-column>
|
</clr-dg-column>
|
||||||
<clr-dg-column [clrDgField]="'reviewed'"
|
<clr-dg-column [clrDgField]="'status'">
|
||||||
>APPROVED / REJECTED</clr-dg-column
|
STATUS
|
||||||
>
|
<clr-dg-string-filter
|
||||||
|
[clrDgStringFilter]="statusFilter"
|
||||||
|
aria-label="Filter status"
|
||||||
|
></clr-dg-string-filter>
|
||||||
|
</clr-dg-column>
|
||||||
|
<clr-dg-column [clrDgField]="'submitter'">
|
||||||
|
SUBMITTER
|
||||||
|
<clr-dg-string-filter
|
||||||
|
[clrDgStringFilter]="submitterFilter"
|
||||||
|
aria-label="Filter submitter"
|
||||||
|
></clr-dg-string-filter>
|
||||||
|
</clr-dg-column>
|
||||||
|
<clr-dg-column [clrDgField]="'submittedReason'">
|
||||||
|
SUBMIT REASON
|
||||||
|
<clr-dg-string-filter
|
||||||
|
[clrDgStringFilter]="submitReasonFilter"
|
||||||
|
aria-label="Filter submit reason"
|
||||||
|
></clr-dg-string-filter>
|
||||||
|
</clr-dg-column>
|
||||||
|
<clr-dg-column [clrDgField]="'submitted'">
|
||||||
|
SUBMITTED
|
||||||
|
<clr-dg-string-filter
|
||||||
|
[clrDgStringFilter]="submittedFilter"
|
||||||
|
aria-label="Filter submitted date"
|
||||||
|
></clr-dg-string-filter>
|
||||||
|
</clr-dg-column>
|
||||||
|
<clr-dg-column [clrDgField]="'reviewed'">
|
||||||
|
APPROVED / REJECTED
|
||||||
|
<clr-dg-string-filter
|
||||||
|
[clrDgStringFilter]="reviewedFilter"
|
||||||
|
aria-label="Filter reviewed date"
|
||||||
|
></clr-dg-string-filter>
|
||||||
|
</clr-dg-column>
|
||||||
<clr-dg-column>DOWNLOAD</clr-dg-column>
|
<clr-dg-column>DOWNLOAD</clr-dg-column>
|
||||||
|
|
||||||
<clr-dg-row
|
<clr-dg-row
|
||||||
|
@@ -8,6 +8,55 @@ import {
|
|||||||
EventService,
|
EventService,
|
||||||
SasService
|
SasService
|
||||||
} from 'src/app/services'
|
} from 'src/app/services'
|
||||||
|
import { ClrDatagridStringFilterInterface } from '@clr/angular'
|
||||||
|
|
||||||
|
interface HistoryData {
|
||||||
|
tableId: string
|
||||||
|
basetable: string
|
||||||
|
status: string
|
||||||
|
submitter: string
|
||||||
|
submittedReason: string
|
||||||
|
submitted: string
|
||||||
|
reviewed: string
|
||||||
|
}
|
||||||
|
|
||||||
|
class BaseTableFilter implements ClrDatagridStringFilterInterface<HistoryData> {
|
||||||
|
accepts(data: HistoryData, search: string): boolean {
|
||||||
|
return data.basetable.toLowerCase().indexOf(search.toLowerCase()) >= 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class StatusFilter implements ClrDatagridStringFilterInterface<HistoryData> {
|
||||||
|
accepts(data: HistoryData, search: string): boolean {
|
||||||
|
return data.status.toLowerCase().indexOf(search.toLowerCase()) >= 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SubmitterFilter implements ClrDatagridStringFilterInterface<HistoryData> {
|
||||||
|
accepts(data: HistoryData, search: string): boolean {
|
||||||
|
return data.submitter.toLowerCase().indexOf(search.toLowerCase()) >= 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SubmitReasonFilter
|
||||||
|
implements ClrDatagridStringFilterInterface<HistoryData>
|
||||||
|
{
|
||||||
|
accepts(data: HistoryData, search: string): boolean {
|
||||||
|
return data.submittedReason.toLowerCase().indexOf(search.toLowerCase()) >= 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SubmittedFilter implements ClrDatagridStringFilterInterface<HistoryData> {
|
||||||
|
accepts(data: HistoryData, search: string): boolean {
|
||||||
|
return data.submitted.toLowerCase().indexOf(search.toLowerCase()) >= 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ReviewedFilter implements ClrDatagridStringFilterInterface<HistoryData> {
|
||||||
|
accepts(data: HistoryData, search: string): boolean {
|
||||||
|
return data.reviewed.toLowerCase().indexOf(search.toLowerCase()) >= 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-history',
|
selector: 'app-history',
|
||||||
@@ -29,6 +78,14 @@ export class HistoryComponent implements OnInit {
|
|||||||
public approveData: any = {}
|
public approveData: any = {}
|
||||||
public sasjsConfig: SASjsConfig = new SASjsConfig()
|
public sasjsConfig: SASjsConfig = new SASjsConfig()
|
||||||
|
|
||||||
|
// Filter instances for datagrid accessibility
|
||||||
|
public baseTableFilter = new BaseTableFilter()
|
||||||
|
public statusFilter = new StatusFilter()
|
||||||
|
public submitterFilter = new SubmitterFilter()
|
||||||
|
public submitReasonFilter = new SubmitReasonFilter()
|
||||||
|
public submittedFilter = new SubmittedFilter()
|
||||||
|
public reviewedFilter = new ReviewedFilter()
|
||||||
|
|
||||||
public histParams: { HIST: number; STARTROW: number; NOBS: number } = {
|
public histParams: { HIST: number; STARTROW: number; NOBS: number } = {
|
||||||
HIST: 0,
|
HIST: 0,
|
||||||
STARTROW: 1,
|
STARTROW: 1,
|
||||||
|
@@ -44,10 +44,20 @@
|
|||||||
<div *ngIf="submitterList && remained !== 0">
|
<div *ngIf="submitterList && remained !== 0">
|
||||||
<clr-datagrid class="datagrid-compact datagrid-custom-footer">
|
<clr-datagrid class="datagrid-compact datagrid-custom-footer">
|
||||||
<clr-dg-column>BASE TABLE</clr-dg-column>
|
<clr-dg-column>BASE TABLE</clr-dg-column>
|
||||||
<clr-dg-column [clrDgField]="'submitted'">SUBMITTED</clr-dg-column>
|
<clr-dg-column [clrDgField]="'submitted'">
|
||||||
<clr-dg-column [clrDgField]="'submitReason'"
|
SUBMITTED
|
||||||
>SUBMIT REASON</clr-dg-column
|
<clr-dg-string-filter
|
||||||
>
|
[clrDgStringFilter]="submittedFilter"
|
||||||
|
aria-label="Filter submitted date"
|
||||||
|
></clr-dg-string-filter>
|
||||||
|
</clr-dg-column>
|
||||||
|
<clr-dg-column [clrDgField]="'submitReason'">
|
||||||
|
SUBMIT REASON
|
||||||
|
<clr-dg-string-filter
|
||||||
|
[clrDgStringFilter]="submitReasonFilter"
|
||||||
|
aria-label="Filter submit reason"
|
||||||
|
></clr-dg-string-filter>
|
||||||
|
</clr-dg-column>
|
||||||
<clr-dg-column class="d-flex justify-content-center"
|
<clr-dg-column class="d-flex justify-content-center"
|
||||||
>ACTION</clr-dg-column
|
>ACTION</clr-dg-column
|
||||||
>
|
>
|
||||||
|
@@ -7,6 +7,7 @@ import {
|
|||||||
import { Subscription } from 'rxjs'
|
import { Subscription } from 'rxjs'
|
||||||
import { ActivatedRoute, Router } from '@angular/router'
|
import { ActivatedRoute, Router } from '@angular/router'
|
||||||
import { SasStoreService, EventService, SasService } from '../../services'
|
import { SasStoreService, EventService, SasService } from '../../services'
|
||||||
|
import { ClrDatagridStringFilterInterface } from '@clr/angular'
|
||||||
|
|
||||||
interface SubmitterData {
|
interface SubmitterData {
|
||||||
tableId: string
|
tableId: string
|
||||||
@@ -16,6 +17,22 @@ interface SubmitterData {
|
|||||||
approver: string
|
approver: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class SubmittedFilter
|
||||||
|
implements ClrDatagridStringFilterInterface<SubmitterData>
|
||||||
|
{
|
||||||
|
accepts(data: SubmitterData, search: string): boolean {
|
||||||
|
return data.submitted.toLowerCase().indexOf(search.toLowerCase()) >= 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SubmitReasonFilter
|
||||||
|
implements ClrDatagridStringFilterInterface<SubmitterData>
|
||||||
|
{
|
||||||
|
accepts(data: SubmitterData, search: string): boolean {
|
||||||
|
return data.submitReason.toLowerCase().indexOf(search.toLowerCase()) >= 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-submitter',
|
selector: 'app-submitter',
|
||||||
templateUrl: './submitter.component.html',
|
templateUrl: './submitter.component.html',
|
||||||
@@ -37,6 +54,10 @@ export class SubmitterComponent implements OnInit, AfterViewInit {
|
|||||||
private _readySub!: Subscription
|
private _readySub!: Subscription
|
||||||
private _backToSub!: Subscription
|
private _backToSub!: Subscription
|
||||||
|
|
||||||
|
// Filter instances for datagrid accessibility
|
||||||
|
public submittedFilter = new SubmittedFilter()
|
||||||
|
public submitReasonFilter = new SubmitReasonFilter()
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private sasStoreService: SasStoreService,
|
private sasStoreService: SasStoreService,
|
||||||
private eventService: EventService,
|
private eventService: EventService,
|
||||||
|
@@ -401,7 +401,7 @@
|
|||||||
[columns]="viewboxTables[viewboxTableIndex].hotTable.columns"
|
[columns]="viewboxTables[viewboxTableIndex].hotTable.columns"
|
||||||
[filters]="true"
|
[filters]="true"
|
||||||
[dropdownMenu]="viewboxTables[viewboxTableIndex].hotTable.dropdownMenu"
|
[dropdownMenu]="viewboxTables[viewboxTableIndex].hotTable.dropdownMenu"
|
||||||
[height]="viewboxTables[viewboxTableIndex].hotTable.height"
|
[height]="calculateTableHeight(viewbox)"
|
||||||
stretchH="all"
|
stretchH="all"
|
||||||
[cells]="viewboxTables[viewboxTableIndex].hotTable.cells"
|
[cells]="viewboxTables[viewboxTableIndex].hotTable.cells"
|
||||||
[maxRows]="viewboxTables[viewboxTableIndex].hotTable.maxRows"
|
[maxRows]="viewboxTables[viewboxTableIndex].hotTable.maxRows"
|
||||||
|
@@ -111,7 +111,7 @@ export class ViewboxesComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
},
|
},
|
||||||
columns: [],
|
columns: [],
|
||||||
cols: [],
|
cols: [],
|
||||||
height: '100%',
|
height: 200, //WORKAROUND: Changed from '100%' to fixed pixel value because otherwize hot does not load
|
||||||
settings: {},
|
settings: {},
|
||||||
hiddenColumns: true,
|
hiddenColumns: true,
|
||||||
manualColumnMove: false,
|
manualColumnMove: false,
|
||||||
@@ -513,6 +513,11 @@ export class ViewboxesComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
this.helperService.debounceCall(1000, () => {
|
this.helperService.debounceCall(1000, () => {
|
||||||
this.viewboxChanged()
|
this.viewboxChanged()
|
||||||
this.eventService.dispatchEvent('resize')
|
this.eventService.dispatchEvent('resize')
|
||||||
|
|
||||||
|
// Refresh all viewbox tables after resize
|
||||||
|
this.viewboxes.forEach((viewbox) => {
|
||||||
|
this.refreshTableAfterResize(viewbox)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -672,6 +677,11 @@ export class ViewboxesComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.setAllHandleTransform()
|
this.setAllHandleTransform()
|
||||||
|
|
||||||
|
// Refresh all tables after snap to grid
|
||||||
|
this.viewboxes.forEach((viewbox) => {
|
||||||
|
this.refreshTableAfterResize(viewbox)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -713,6 +723,11 @@ export class ViewboxesComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
viewbox.minimized = false
|
viewbox.minimized = false
|
||||||
|
|
||||||
this.viewboxChanged()
|
this.viewboxChanged()
|
||||||
|
|
||||||
|
// Refresh table after restoring
|
||||||
|
setTimeout(() => {
|
||||||
|
this.refreshTableAfterResize(viewbox)
|
||||||
|
}, 100)
|
||||||
}
|
}
|
||||||
|
|
||||||
collapse(viewbox: Viewbox) {
|
collapse(viewbox: Viewbox) {
|
||||||
@@ -723,6 +738,11 @@ export class ViewboxesComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
expand(viewbox: Viewbox) {
|
expand(viewbox: Viewbox) {
|
||||||
viewbox.collapsed = false
|
viewbox.collapsed = false
|
||||||
this.viewboxChanged()
|
this.viewboxChanged()
|
||||||
|
|
||||||
|
// Refresh table after expanding
|
||||||
|
setTimeout(() => {
|
||||||
|
this.refreshTableAfterResize(viewbox)
|
||||||
|
}, 100)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1158,6 +1178,35 @@ export class ViewboxesComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WORKAROUND: This is a workaround to calculate the height of the table since `100%`
|
||||||
|
* makes hot not load
|
||||||
|
* Calculate available height for Handsontable
|
||||||
|
* @param viewbox The viewbox to calculate height for
|
||||||
|
* @returns Available height in pixels
|
||||||
|
*/
|
||||||
|
calculateTableHeight(viewbox: Viewbox): number {
|
||||||
|
// Calculate the exact height of the content div
|
||||||
|
const dragHandleHeight = 20
|
||||||
|
const searchFormHeight = 38
|
||||||
|
// Return the exact remaining height for the table
|
||||||
|
return viewbox.height - dragHandleHeight - searchFormHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refresh Handsontable instance after resize
|
||||||
|
* @param viewbox The viewbox to refresh
|
||||||
|
*/
|
||||||
|
refreshTableAfterResize(viewbox: Viewbox): void {
|
||||||
|
const hotInstance = this.getViewboxHotInstance(viewbox.id)
|
||||||
|
if (hotInstance) {
|
||||||
|
// Force the table to recalculate its dimensions
|
||||||
|
setTimeout(() => {
|
||||||
|
hotInstance.refreshDimensions()
|
||||||
|
}, 100)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param viewboxId
|
* @param viewboxId
|
||||||
|
@@ -46,6 +46,10 @@
|
|||||||
rejected: tableDetails?.REVIEW_STATUS_ID === 'REJECTED',
|
rejected: tableDetails?.REVIEW_STATUS_ID === 'REJECTED',
|
||||||
accepted: tableDetails?.REVIEW_STATUS_ID === 'APPROVED'
|
accepted: tableDetails?.REVIEW_STATUS_ID === 'APPROVED'
|
||||||
}"
|
}"
|
||||||
|
[attr.aria-label]="
|
||||||
|
'Review status: ' + tableDetails?.REVIEW_STATUS_ID
|
||||||
|
"
|
||||||
|
role="status"
|
||||||
>
|
>
|
||||||
{{ tableDetails?.REVIEW_STATUS_ID }}
|
{{ tableDetails?.REVIEW_STATUS_ID }}
|
||||||
</span>
|
</span>
|
||||||
@@ -61,6 +65,7 @@
|
|||||||
class="btn btn-sm btn-outline text-center mr-5i"
|
class="btn btn-sm btn-outline text-center mr-5i"
|
||||||
(click)="viewerTableScreen()"
|
(click)="viewerTableScreen()"
|
||||||
[disabled]="revertingChanges"
|
[disabled]="revertingChanges"
|
||||||
|
aria-label="View base table"
|
||||||
>
|
>
|
||||||
View base table
|
View base table
|
||||||
</button>
|
</button>
|
||||||
@@ -74,6 +79,7 @@
|
|||||||
"
|
"
|
||||||
(click)="approveTableScreen()"
|
(click)="approveTableScreen()"
|
||||||
[disabled]="revertingChanges"
|
[disabled]="revertingChanges"
|
||||||
|
aria-label="Approve table"
|
||||||
>
|
>
|
||||||
Approve
|
Approve
|
||||||
</button>
|
</button>
|
||||||
@@ -81,14 +87,16 @@
|
|||||||
class="btn btn-sm btn-info-outline text-center mr-5i"
|
class="btn btn-sm btn-info-outline text-center mr-5i"
|
||||||
(click)="goBack()"
|
(click)="goBack()"
|
||||||
[disabled]="revertingChanges"
|
[disabled]="revertingChanges"
|
||||||
|
aria-label="Edit base table"
|
||||||
>
|
>
|
||||||
Edit base table
|
Edit base table
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="btn btn-sm btn-success text-center mr-5i min-w-0"
|
class="btn btn-sm btn-success text-center mr-5i min-w-0"
|
||||||
(click)="download(tableDetails?.TABLE_ID)"
|
(click)="download(tableDetails?.TABLE_ID)"
|
||||||
|
aria-label="Download audit file"
|
||||||
>
|
>
|
||||||
<clr-icon shape="download"></clr-icon>
|
<clr-icon shape="download" aria-hidden="true"></clr-icon>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<clr-tooltip>
|
<clr-tooltip>
|
||||||
@@ -98,6 +106,7 @@
|
|||||||
clrTooltipTrigger
|
clrTooltipTrigger
|
||||||
[clrLoading]="revertingChanges"
|
[clrLoading]="revertingChanges"
|
||||||
class="btn btn-sm btn-danger text-center mt-20"
|
class="btn btn-sm btn-danger text-center mt-20"
|
||||||
|
aria-label="Revert this and all subsequent changes"
|
||||||
>
|
>
|
||||||
REVERT
|
REVERT
|
||||||
|
|
||||||
@@ -128,6 +137,8 @@
|
|||||||
[afterGetColHeader]="hotTable.afterGetColHeader"
|
[afterGetColHeader]="hotTable.afterGetColHeader"
|
||||||
stretchH="all"
|
stretchH="all"
|
||||||
[cells]="hotTable.cells"
|
[cells]="hotTable.cells"
|
||||||
|
[settings]="hotTable.settings"
|
||||||
|
aria-label="Staged data table"
|
||||||
>
|
>
|
||||||
<!--[licenseKey]=null-->
|
<!--[licenseKey]=null-->
|
||||||
</hot-table>
|
</hot-table>
|
||||||
|
@@ -1,4 +1,10 @@
|
|||||||
import { Component, OnInit, ViewEncapsulation } from '@angular/core'
|
import {
|
||||||
|
Component,
|
||||||
|
OnInit,
|
||||||
|
ViewEncapsulation,
|
||||||
|
ViewChild,
|
||||||
|
AfterViewInit
|
||||||
|
} from '@angular/core'
|
||||||
import { SasStoreService } from '../services/sas-store.service'
|
import { SasStoreService } from '../services/sas-store.service'
|
||||||
import { Router } from '@angular/router'
|
import { Router } from '@angular/router'
|
||||||
import { ActivatedRoute } from '@angular/router'
|
import { ActivatedRoute } from '@angular/router'
|
||||||
@@ -19,7 +25,7 @@ import { RequestWrapperResponse } from '../models/request-wrapper/RequestWrapper
|
|||||||
},
|
},
|
||||||
encapsulation: ViewEncapsulation.None
|
encapsulation: ViewEncapsulation.None
|
||||||
})
|
})
|
||||||
export class StageComponent implements OnInit {
|
export class StageComponent implements OnInit, AfterViewInit {
|
||||||
public table_id: any
|
public table_id: any
|
||||||
public jsParams: any
|
public jsParams: any
|
||||||
public keysArray: any
|
public keysArray: any
|
||||||
@@ -32,12 +38,26 @@ export class StageComponent implements OnInit {
|
|||||||
colHeaders: [],
|
colHeaders: [],
|
||||||
columns: [],
|
columns: [],
|
||||||
height: 500,
|
height: 500,
|
||||||
settings: {},
|
settings: {
|
||||||
|
// Disable problematic ARIA attributes that cause accessibility issues
|
||||||
|
ariaTags: false,
|
||||||
|
// Use grid role instead of treegrid for better accessibility
|
||||||
|
tableClassName: 'htCenter',
|
||||||
|
// Disable focus management to avoid focus catcher issues
|
||||||
|
outsideClickDeselects: false,
|
||||||
|
// Use simpler accessibility mode
|
||||||
|
autoWrapRow: false,
|
||||||
|
autoWrapCol: false
|
||||||
|
},
|
||||||
licenseKey: undefined,
|
licenseKey: undefined,
|
||||||
maxRows: this.licenceState.value.stage_rows_allowed || Infinity,
|
maxRows: this.licenceState.value.stage_rows_allowed || Infinity,
|
||||||
afterGetColHeader: (column, th, headerLevel) => {
|
afterGetColHeader: (column, th, headerLevel) => {
|
||||||
// Dark mode
|
// Dark mode
|
||||||
th.classList.add(globals.handsontable.darkTableHeaderClass)
|
th.classList.add(globals.handsontable.darkTableHeaderClass)
|
||||||
|
},
|
||||||
|
afterInit: () => {
|
||||||
|
// Fix accessibility issues with focus catcher inputs
|
||||||
|
this.fixFocusCatcherAccessibility()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,6 +189,13 @@ export class StageComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ngAfterViewInit() {
|
||||||
|
// Additional accessibility fixes after view is initialized
|
||||||
|
setTimeout(() => {
|
||||||
|
this.fixFocusCatcherAccessibility()
|
||||||
|
}, 500)
|
||||||
|
}
|
||||||
|
|
||||||
revertChanges() {
|
revertChanges() {
|
||||||
this.revertingChanges = true
|
this.revertingChanges = true
|
||||||
|
|
||||||
@@ -204,4 +231,27 @@ export class StageComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
}, 200)
|
}, 200)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fixFocusCatcherAccessibility() {
|
||||||
|
// Add labels to focus catcher inputs to fix accessibility issues
|
||||||
|
setTimeout(() => {
|
||||||
|
const focusCatchers = document.querySelectorAll('.htFocusCatcher')
|
||||||
|
focusCatchers.forEach((input: any, index: number) => {
|
||||||
|
if (input) {
|
||||||
|
// Add proper accessibility attributes
|
||||||
|
input.setAttribute('aria-label', `Table focus catcher ${index + 1}`)
|
||||||
|
input.setAttribute('aria-hidden', 'true')
|
||||||
|
input.setAttribute('tabindex', '-1')
|
||||||
|
input.setAttribute('role', 'presentation')
|
||||||
|
// Add a hidden label element
|
||||||
|
const label = document.createElement('label')
|
||||||
|
label.setAttribute('for', input.id || `htFocusCatcher${index}`)
|
||||||
|
label.setAttribute('aria-hidden', 'true')
|
||||||
|
label.style.display = 'none'
|
||||||
|
label.textContent = `Table focus catcher ${index + 1}`
|
||||||
|
input.parentNode?.insertBefore(label, input)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}, 100)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -622,6 +622,7 @@
|
|||||||
|
|
||||||
<div *ngIf="!noData && !noDataReqErr && table" class="clr-flex-1">
|
<div *ngIf="!noData && !noDataReqErr && table" class="clr-flex-1">
|
||||||
<hot-table
|
<hot-table
|
||||||
|
#hotInstance
|
||||||
hotId="hotInstance"
|
hotId="hotInstance"
|
||||||
id="hotTable"
|
id="hotTable"
|
||||||
className="htDark"
|
className="htDark"
|
||||||
|
@@ -3,9 +3,11 @@ import {
|
|||||||
AfterContentInit,
|
AfterContentInit,
|
||||||
ChangeDetectorRef,
|
ChangeDetectorRef,
|
||||||
AfterViewInit,
|
AfterViewInit,
|
||||||
|
OnDestroy,
|
||||||
ViewChildren,
|
ViewChildren,
|
||||||
QueryList,
|
QueryList,
|
||||||
ViewEncapsulation
|
ViewEncapsulation,
|
||||||
|
ViewChild
|
||||||
} from '@angular/core'
|
} from '@angular/core'
|
||||||
import { SasStoreService } from '../services/sas-store.service'
|
import { SasStoreService } from '../services/sas-store.service'
|
||||||
import { Subscription } from 'rxjs'
|
import { Subscription } from 'rxjs'
|
||||||
@@ -49,10 +51,15 @@ import { RequestWrapperResponse } from '../models/request-wrapper/RequestWrapper
|
|||||||
},
|
},
|
||||||
encapsulation: ViewEncapsulation.None
|
encapsulation: ViewEncapsulation.None
|
||||||
})
|
})
|
||||||
export class ViewerComponent implements AfterContentInit, AfterViewInit {
|
export class ViewerComponent
|
||||||
|
implements AfterContentInit, AfterViewInit, OnDestroy
|
||||||
|
{
|
||||||
@ViewChildren('queryFilter')
|
@ViewChildren('queryFilter')
|
||||||
queryFilterCompList: QueryList<QueryComponent> = new QueryList()
|
queryFilterCompList: QueryList<QueryComponent> = new QueryList()
|
||||||
|
|
||||||
|
@ViewChild('hotInstance', { static: false })
|
||||||
|
hotInstanceViewChild!: Handsontable
|
||||||
|
|
||||||
public libraries!: Array<any>
|
public libraries!: Array<any>
|
||||||
public librariesPaging: boolean = false
|
public librariesPaging: boolean = false
|
||||||
public librariesSearch: string = ''
|
public librariesSearch: string = ''
|
||||||
@@ -107,11 +114,14 @@ export class ViewerComponent implements AfterContentInit, AfterViewInit {
|
|||||||
public licenceState = this.licenceService.licenceState
|
public licenceState = this.licenceService.licenceState
|
||||||
public Infinity = Infinity
|
public Infinity = Infinity
|
||||||
|
|
||||||
|
private ariaObserver: MutationObserver | undefined
|
||||||
|
private ariaCheckInterval: any | undefined
|
||||||
|
|
||||||
public hotTable: HotTableInterface = {
|
public hotTable: HotTableInterface = {
|
||||||
data: [],
|
data: [],
|
||||||
colHeaders: [],
|
colHeaders: [],
|
||||||
columns: [],
|
columns: [],
|
||||||
height: '100%',
|
height: 'calc(100vh - 182px)',
|
||||||
maxRows: this.licenceState.value.viewer_rows_allowed || Infinity,
|
maxRows: this.licenceState.value.viewer_rows_allowed || Infinity,
|
||||||
settings: {},
|
settings: {},
|
||||||
licenseKey: undefined,
|
licenseKey: undefined,
|
||||||
@@ -530,7 +540,6 @@ export class ViewerComponent implements AfterContentInit, AfterViewInit {
|
|||||||
`#search_${library.LIBRARYREF}`
|
`#search_${library.LIBRARYREF}`
|
||||||
)
|
)
|
||||||
|
|
||||||
this.loggerService.log('[libTreeSearchInput]', libTreeSearchInput)
|
|
||||||
if (libTreeSearchInput) libTreeSearchInput.focus()
|
if (libTreeSearchInput) libTreeSearchInput.focus()
|
||||||
|
|
||||||
if (library && library.libinfo) this.libinfo = library.libinfo
|
if (library && library.libinfo) this.libinfo = library.libinfo
|
||||||
@@ -819,28 +828,35 @@ export class ViewerComponent implements AfterContentInit, AfterViewInit {
|
|||||||
this.versions = res.versions || []
|
this.versions = res.versions || []
|
||||||
this.setDSNote()
|
this.setDSNote()
|
||||||
this.queryText = res.sasparams[0].FILTER_TEXT
|
this.queryText = res.sasparams[0].FILTER_TEXT
|
||||||
let columns: any[] = []
|
|
||||||
let colArr = []
|
|
||||||
|
|
||||||
for (let key in res.viewdata[0]) {
|
// Initialize columns only if we have data
|
||||||
if (key) {
|
if (res.viewdata && res.viewdata.length > 0) {
|
||||||
colArr.push(key)
|
let columns: any[] = []
|
||||||
|
let colArr = []
|
||||||
|
|
||||||
|
for (let key in res.viewdata[0]) {
|
||||||
|
if (key) {
|
||||||
|
colArr.push(key)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (let index = 0; index < colArr.length; index++) {
|
||||||
|
columns.push({ data: colArr[index] })
|
||||||
|
}
|
||||||
|
|
||||||
|
this.hotTable.colHeaders = colArr
|
||||||
|
this.hotTable.columns = columns
|
||||||
|
} else {
|
||||||
|
// Set empty arrays if no data
|
||||||
|
this.hotTable.colHeaders = []
|
||||||
|
this.hotTable.columns = []
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let index = 0; index < colArr.length; index++) {
|
// Set cells function
|
||||||
columns.push({ data: colArr[index] })
|
this.hotTable.cells = () => {
|
||||||
|
return { readOnly: true }
|
||||||
}
|
}
|
||||||
|
|
||||||
let cells = function () {
|
|
||||||
let cellProperties = { readOnly: true }
|
|
||||||
return cellProperties
|
|
||||||
}
|
|
||||||
|
|
||||||
this.hotTable.colHeaders = colArr
|
|
||||||
this.hotTable.columns = columns
|
|
||||||
this.hotTable.cells = cells
|
|
||||||
|
|
||||||
this.tableFlag = false
|
this.tableFlag = false
|
||||||
let ds = []
|
let ds = []
|
||||||
ds = libDataset.split('.')
|
ds = libDataset.split('.')
|
||||||
@@ -911,6 +927,11 @@ export class ViewerComponent implements AfterContentInit, AfterViewInit {
|
|||||||
//That is intorduced by HOT update
|
//That is intorduced by HOT update
|
||||||
if (!this.noData && !this.noDataReqErr && libDataset) this.setupHot()
|
if (!this.noData && !this.noDataReqErr && libDataset) this.setupHot()
|
||||||
|
|
||||||
|
// Fix ARIA accessibility issues after data loading
|
||||||
|
setTimeout(() => {
|
||||||
|
this.fixAriaAccessibility()
|
||||||
|
}, 1500)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is hacky fix for closing the nav drodpwon, when clicking on the handsontable area.
|
* This is hacky fix for closing the nav drodpwon, when clicking on the handsontable area.
|
||||||
* Without it, hot table does not steal focus, so it doesn't close the nav.
|
* Without it, hot table does not steal focus, so it doesn't close the nav.
|
||||||
@@ -1065,7 +1086,7 @@ export class ViewerComponent implements AfterContentInit, AfterViewInit {
|
|||||||
if (this.hotInstance) {
|
if (this.hotInstance) {
|
||||||
this.hotInstance.updateSettings({
|
this.hotInstance.updateSettings({
|
||||||
height: this.hotTable.height,
|
height: this.hotTable.height,
|
||||||
modifyColWidth: function (width: any, col: any) {
|
modifyColWidth: (width: any, col: any) => {
|
||||||
if (width > 500) return 500
|
if (width > 500) return 500
|
||||||
else return width
|
else return width
|
||||||
},
|
},
|
||||||
@@ -1081,8 +1102,26 @@ export class ViewerComponent implements AfterContentInit, AfterViewInit {
|
|||||||
th.classList.add(globals.handsontable.darkTableHeaderClass)
|
th.classList.add(globals.handsontable.darkTableHeaderClass)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Add hooks for accessibility fixes
|
||||||
|
this.hotInstance.addHook('afterRender', () => {
|
||||||
|
// Fix ARIA accessibility issues after each render
|
||||||
|
this.fixAriaAccessibility()
|
||||||
|
})
|
||||||
|
|
||||||
|
this.hotInstance.addHook('afterChange', () => {
|
||||||
|
// Fix ARIA accessibility issues after any data change
|
||||||
|
setTimeout(() => {
|
||||||
|
this.fixAriaAccessibility()
|
||||||
|
}, 50)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fix ARIA accessibility issues after table setup
|
||||||
|
setTimeout(() => {
|
||||||
|
this.fixAriaAccessibility()
|
||||||
|
}, 500)
|
||||||
}, 1000)
|
}, 1000)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1148,7 +1187,173 @@ export class ViewerComponent implements AfterContentInit, AfterViewInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ngAfterViewInit() {}
|
ngAfterViewInit() {
|
||||||
|
// Fix ARIA accessibility issues after table initialization
|
||||||
|
setTimeout(() => {
|
||||||
|
this.fixAriaAccessibility()
|
||||||
|
}, 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
// Clean up the MutationObserver
|
||||||
|
if (this.ariaObserver) {
|
||||||
|
this.ariaObserver.disconnect()
|
||||||
|
this.ariaObserver = undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up the interval
|
||||||
|
if (this.ariaCheckInterval) {
|
||||||
|
clearInterval(this.ariaCheckInterval)
|
||||||
|
this.ariaCheckInterval = undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fixes ARIA accessibility issues in the Handsontable component
|
||||||
|
* This addresses the accessibility report issues with treegrid and presentation roles
|
||||||
|
*/
|
||||||
|
private fixAriaAccessibility() {
|
||||||
|
// Use a more aggressive approach to find and fix all ARIA issues
|
||||||
|
const fixAriaIssues = () => {
|
||||||
|
// Specifically target Handsontable wrapper elements that are causing issues
|
||||||
|
const hotWrappers = document.querySelectorAll(
|
||||||
|
'.ht-wrapper, .wtHolder, [id^="ht_"]'
|
||||||
|
)
|
||||||
|
|
||||||
|
hotWrappers.forEach((wrapper) => {
|
||||||
|
// Remove problematic ARIA attributes from Handsontable wrappers
|
||||||
|
wrapper.removeAttribute('role')
|
||||||
|
wrapper.removeAttribute('aria-rowcount')
|
||||||
|
wrapper.removeAttribute('aria-colcount')
|
||||||
|
wrapper.removeAttribute('aria-multiselectable')
|
||||||
|
})
|
||||||
|
|
||||||
|
// Find all elements with problematic ARIA roles in the entire document
|
||||||
|
const allTreegridElements = document.querySelectorAll('[role="treegrid"]')
|
||||||
|
const allPresentationElements = document.querySelectorAll(
|
||||||
|
'[role="presentation"]'
|
||||||
|
)
|
||||||
|
|
||||||
|
// Fix treegrid role issues - remove them completely as they're causing problems
|
||||||
|
allTreegridElements.forEach((element) => {
|
||||||
|
element.removeAttribute('role')
|
||||||
|
element.removeAttribute('aria-rowcount')
|
||||||
|
element.removeAttribute('aria-colcount')
|
||||||
|
element.removeAttribute('aria-multiselectable')
|
||||||
|
})
|
||||||
|
|
||||||
|
// Fix presentation role issues - remove them if they contain interactive elements
|
||||||
|
allPresentationElements.forEach((element) => {
|
||||||
|
const hasInteractiveChildren =
|
||||||
|
element.querySelectorAll(
|
||||||
|
'button, input, select, textarea, [tabindex], [onclick], [contenteditable]'
|
||||||
|
).length > 0
|
||||||
|
if (hasInteractiveChildren) {
|
||||||
|
element.removeAttribute('role')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Also fix any elements with aria-rowcount="-1" which is problematic
|
||||||
|
const negativeRowCountElements = document.querySelectorAll(
|
||||||
|
'[aria-rowcount="-1"]'
|
||||||
|
)
|
||||||
|
negativeRowCountElements.forEach((element) => {
|
||||||
|
element.removeAttribute('aria-rowcount')
|
||||||
|
})
|
||||||
|
|
||||||
|
// Ensure proper table structure
|
||||||
|
const tableElements = document.querySelectorAll('table')
|
||||||
|
tableElements.forEach((table) => {
|
||||||
|
if (!table.getAttribute('role')) {
|
||||||
|
table.setAttribute('role', 'table')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure table headers have proper scope
|
||||||
|
const headerCells = table.querySelectorAll('th')
|
||||||
|
headerCells.forEach((th) => {
|
||||||
|
if (!th.getAttribute('scope')) {
|
||||||
|
th.setAttribute('scope', 'col')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// Add proper ARIA labels to interactive elements
|
||||||
|
const interactiveElements = document.querySelectorAll(
|
||||||
|
'button, input, select, textarea, [contenteditable]'
|
||||||
|
)
|
||||||
|
interactiveElements.forEach((element) => {
|
||||||
|
if (
|
||||||
|
!element.getAttribute('aria-label') &&
|
||||||
|
!element.getAttribute('aria-labelledby')
|
||||||
|
) {
|
||||||
|
const textContent = element.textContent?.trim()
|
||||||
|
if (textContent) {
|
||||||
|
element.setAttribute('aria-label', textContent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the fix immediately
|
||||||
|
fixAriaIssues()
|
||||||
|
|
||||||
|
// Run it again after a short delay to catch any dynamically created elements
|
||||||
|
setTimeout(fixAriaIssues, 100)
|
||||||
|
setTimeout(fixAriaIssues, 500)
|
||||||
|
setTimeout(fixAriaIssues, 1000)
|
||||||
|
setTimeout(fixAriaIssues, 2000)
|
||||||
|
|
||||||
|
// Set up a periodic check to ensure accessibility fixes are maintained
|
||||||
|
if (!this.ariaCheckInterval) {
|
||||||
|
this.ariaCheckInterval = setInterval(fixAriaIssues, 3000)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up a MutationObserver to continuously monitor for new problematic elements
|
||||||
|
if (!this.ariaObserver) {
|
||||||
|
this.ariaObserver = new MutationObserver((mutations) => {
|
||||||
|
let shouldFix = false
|
||||||
|
mutations.forEach((mutation) => {
|
||||||
|
if (
|
||||||
|
mutation.type === 'attributes' &&
|
||||||
|
(mutation.attributeName === 'role' ||
|
||||||
|
mutation.attributeName === 'aria-rowcount')
|
||||||
|
) {
|
||||||
|
shouldFix = true
|
||||||
|
}
|
||||||
|
if (mutation.type === 'childList') {
|
||||||
|
mutation.addedNodes.forEach((node) => {
|
||||||
|
if (node.nodeType === Node.ELEMENT_NODE) {
|
||||||
|
const element = node as Element
|
||||||
|
if (
|
||||||
|
element.hasAttribute('role') ||
|
||||||
|
element.hasAttribute('aria-rowcount')
|
||||||
|
) {
|
||||||
|
shouldFix = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (shouldFix) {
|
||||||
|
setTimeout(fixAriaIssues, 50)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Start observing the entire document for changes
|
||||||
|
this.ariaObserver.observe(document.body, {
|
||||||
|
childList: true,
|
||||||
|
subtree: true,
|
||||||
|
attributes: true,
|
||||||
|
attributeFilter: [
|
||||||
|
'role',
|
||||||
|
'aria-rowcount',
|
||||||
|
'aria-colcount',
|
||||||
|
'aria-multiselectable'
|
||||||
|
]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async ngAfterContentInit() {
|
async ngAfterContentInit() {
|
||||||
if (this.hotTable.data.length > 0) {
|
if (this.hotTable.data.length > 0) {
|
||||||
|
@@ -1042,12 +1042,12 @@ app-approve {
|
|||||||
// HISTORY.COMPONENT
|
// HISTORY.COMPONENT
|
||||||
app-history {
|
app-history {
|
||||||
.rejected {
|
.rejected {
|
||||||
color: #f83126;
|
color: #92201a;
|
||||||
font-weight: bold
|
font-weight: bold
|
||||||
}
|
}
|
||||||
|
|
||||||
.accepted {
|
.accepted {
|
||||||
color: #3fc424;
|
color: #105c26;
|
||||||
font-weight: bold
|
font-weight: bold
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1206,6 +1206,8 @@ app-viewer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
hot-table {
|
hot-table {
|
||||||
|
height: calc(100vh - 200px);
|
||||||
|
|
||||||
.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-color: #306b00b0 !important;
|
||||||
@@ -3442,6 +3444,10 @@ app-approve-details {
|
|||||||
width: 175px
|
width: 175px
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#rejectBtn {
|
||||||
|
background-color: #a62f16 !important;
|
||||||
|
}
|
||||||
|
|
||||||
.formatted-values-toggle {
|
.formatted-values-toggle {
|
||||||
min-width: 75px
|
min-width: 75px
|
||||||
}
|
}
|
||||||
@@ -3630,12 +3636,12 @@ app-excel-password-modal {
|
|||||||
// STAGE.COMPONENT
|
// STAGE.COMPONENT
|
||||||
app-stage {
|
app-stage {
|
||||||
.rejected {
|
.rejected {
|
||||||
color: #f83126;
|
color: #92201a;
|
||||||
font-weight: bold
|
font-weight: bold
|
||||||
}
|
}
|
||||||
|
|
||||||
.accepted {
|
.accepted {
|
||||||
color: #3fc424;
|
color: #105c26;
|
||||||
font-weight: bold
|
font-weight: bold
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3644,6 +3650,25 @@ app-stage {
|
|||||||
margin-top:10px;
|
margin-top:10px;
|
||||||
color: #007cbb;
|
color: #007cbb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Accessibility fixes for handsontable focus catchers
|
||||||
|
.htFocusCatcher {
|
||||||
|
// Hide from screen readers but maintain functionality
|
||||||
|
position: absolute !important;
|
||||||
|
left: -9999px !important;
|
||||||
|
width: 1px !important;
|
||||||
|
height: 1px !important;
|
||||||
|
overflow: hidden !important;
|
||||||
|
clip: rect(0, 0, 0, 0) !important;
|
||||||
|
border: 0 !important;
|
||||||
|
margin: -1px !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
|
||||||
|
// Ensure it's not focusable by screen readers
|
||||||
|
&:focus {
|
||||||
|
outline: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
body[cds-theme="dark"] {
|
body[cds-theme="dark"] {
|
||||||
|
12
package-lock.json
generated
12
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "dcfrontend",
|
"name": "dcfrontend",
|
||||||
"version": "6.14.7",
|
"version": "7.0.3",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "dcfrontend",
|
"name": "dcfrontend",
|
||||||
"version": "6.14.7",
|
"version": "7.0.3",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@saithodev/semantic-release-gitea": "^2.1.0",
|
"@saithodev/semantic-release-gitea": "^2.1.0",
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
"@semantic-release/npm": "11.0.0",
|
"@semantic-release/npm": "11.0.0",
|
||||||
"@semantic-release/release-notes-generator": "^11.0.4",
|
"@semantic-release/release-notes-generator": "^11.0.4",
|
||||||
"commit-and-tag-version": "^11.2.2",
|
"commit-and-tag-version": "^11.2.2",
|
||||||
"prettier": "3.2.5"
|
"prettier": "3.6.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/code-frame": {
|
"node_modules/@babel/code-frame": {
|
||||||
@@ -6690,9 +6690,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/prettier": {
|
"node_modules/prettier": {
|
||||||
"version": "3.2.5",
|
"version": "3.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz",
|
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz",
|
||||||
"integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==",
|
"integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"bin": {
|
"bin": {
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "dcfrontend",
|
"name": "dcfrontend",
|
||||||
"version": "7.0.3",
|
"version": "7.1.1",
|
||||||
"description": "Data Controller",
|
"description": "Data Controller",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@saithodev/semantic-release-gitea": "^2.1.0",
|
"@saithodev/semantic-release-gitea": "^2.1.0",
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
"@semantic-release/git": "^10.0.1",
|
"@semantic-release/git": "^10.0.1",
|
||||||
"@semantic-release/release-notes-generator": "^11.0.4",
|
"@semantic-release/release-notes-generator": "^11.0.4",
|
||||||
"commit-and-tag-version": "^11.2.2",
|
"commit-and-tag-version": "^11.2.2",
|
||||||
"prettier": "3.2.5"
|
"prettier": "3.6.2"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"install": "cd client && npm i && cd ../sas && npm i",
|
"install": "cd client && npm i && cd ../sas && npm i",
|
||||||
|
207
sas/package-lock.json
generated
207
sas/package-lock.json
generated
@@ -6,8 +6,8 @@
|
|||||||
"": {
|
"": {
|
||||||
"name": "dc-sas",
|
"name": "dc-sas",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sasjs/cli": "^4.12.8",
|
"@sasjs/cli": "^4.12.10",
|
||||||
"@sasjs/core": "^4.59.1"
|
"@sasjs/core": "^4.59.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@coolaj86/urequest": {
|
"node_modules/@coolaj86/urequest": {
|
||||||
@@ -30,28 +30,28 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sasjs/adapter": {
|
"node_modules/@sasjs/adapter": {
|
||||||
"version": "4.12.1",
|
"version": "4.12.2",
|
||||||
"resolved": "https://registry.npmjs.org/@sasjs/adapter/-/adapter-4.12.1.tgz",
|
"resolved": "https://registry.npmjs.org/@sasjs/adapter/-/adapter-4.12.2.tgz",
|
||||||
"integrity": "sha512-0217oZIkrecOyQygRe6Azgc1C4TIcjB5noi15fU4Rd5GsfDpleKVfQQdzYZ7im/vesWlLIVl/yUT67MMaQe0ew==",
|
"integrity": "sha512-OV5mx3N2Hywhp0M+SBLTuG42x/LDnMxrb2/pwG4RQbhfzvAwdHVEoXRouDJ49RMSY9s6TJcwUPh+Xzafl5sG/g==",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sasjs/utils": "3.5.2",
|
"@sasjs/utils": "3.5.2",
|
||||||
"axios": "1.8.2",
|
"axios": "1.8.2",
|
||||||
"axios-cookiejar-support": "5.0.5",
|
"axios-cookiejar-support": "5.0.5",
|
||||||
"form-data": "4.0.0",
|
"form-data": "4.0.4",
|
||||||
"https": "1.0.0",
|
"https": "1.0.0",
|
||||||
"tough-cookie": "4.1.3"
|
"tough-cookie": "4.1.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sasjs/cli": {
|
"node_modules/@sasjs/cli": {
|
||||||
"version": "4.12.8",
|
"version": "4.12.10",
|
||||||
"resolved": "https://registry.npmjs.org/@sasjs/cli/-/cli-4.12.8.tgz",
|
"resolved": "https://registry.npmjs.org/@sasjs/cli/-/cli-4.12.10.tgz",
|
||||||
"integrity": "sha512-20WiywlcLV22T1XfkAumV66mM3PfQ1N9ihJfpxoOVCFs2wF/ZertqnP4BZ5CE1dc30M1C2oDOzzSSVZqePPQnw==",
|
"integrity": "sha512-eWh7cTtIEH9PnKdooRWZ8yWVle4jPb0D7k+QKaPSBIW+ZbtyS87yNVU4MwkonSKCHneuhvrMMo8YV8QrqoUo7Q==",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sasjs/adapter": "4.12.1",
|
"@sasjs/adapter": "4.12.2",
|
||||||
"@sasjs/core": "4.59.1",
|
"@sasjs/core": "4.59.1",
|
||||||
"@sasjs/lint": "2.4.3",
|
"@sasjs/lint": "2.4.3",
|
||||||
"@sasjs/utils": "3.5.2",
|
"@sasjs/utils": "3.5.2",
|
||||||
@@ -76,12 +76,18 @@
|
|||||||
"sasjs": "build/index.js"
|
"sasjs": "build/index.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sasjs/core": {
|
"node_modules/@sasjs/cli/node_modules/@sasjs/core": {
|
||||||
"version": "4.59.1",
|
"version": "4.59.1",
|
||||||
"resolved": "https://registry.npmjs.org/@sasjs/core/-/core-4.59.1.tgz",
|
"resolved": "https://registry.npmjs.org/@sasjs/core/-/core-4.59.1.tgz",
|
||||||
"integrity": "sha512-52GNI4nIll5YivI8uobWrucE6TkHcTjcbKTr/YPC9eTauC4sh0V0MptebfAJ5E6vE5P2WevNZGr42KdDpckLpg==",
|
"integrity": "sha512-52GNI4nIll5YivI8uobWrucE6TkHcTjcbKTr/YPC9eTauC4sh0V0MptebfAJ5E6vE5P2WevNZGr42KdDpckLpg==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@sasjs/core": {
|
||||||
|
"version": "4.59.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@sasjs/core/-/core-4.59.5.tgz",
|
||||||
|
"integrity": "sha512-PAPinHjuFi3P/MxkZHGSF0iLWoBFnpxon7/SshHPcBRmSQwmFOvh9X8xdE8ZClqY9XDOwFZ6sqeOQ7GYZliLfw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@sasjs/lint": {
|
"node_modules/@sasjs/lint": {
|
||||||
"version": "2.4.3",
|
"version": "2.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/@sasjs/lint/-/lint-2.4.3.tgz",
|
"resolved": "https://registry.npmjs.org/@sasjs/lint/-/lint-2.4.3.tgz",
|
||||||
@@ -302,9 +308,10 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/brace-expansion": {
|
"node_modules/brace-expansion": {
|
||||||
"version": "1.1.11",
|
"version": "1.1.12",
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
|
||||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"balanced-match": "^1.0.0",
|
"balanced-match": "^1.0.0",
|
||||||
"concat-map": "0.0.1"
|
"concat-map": "0.0.1"
|
||||||
@@ -341,6 +348,19 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/call-bind-apply-helpers": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"es-errors": "^1.3.0",
|
||||||
|
"function-bind": "^1.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/chalk": {
|
"node_modules/chalk": {
|
||||||
"version": "4.1.2",
|
"version": "4.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||||
@@ -555,6 +575,20 @@
|
|||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/dunder-proto": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"call-bind-apply-helpers": "^1.0.1",
|
||||||
|
"es-errors": "^1.3.0",
|
||||||
|
"gopd": "^1.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/emoji-regex": {
|
"node_modules/emoji-regex": {
|
||||||
"version": "8.0.0",
|
"version": "8.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||||
@@ -571,6 +605,51 @@
|
|||||||
"url": "https://github.com/fb55/entities?sponsor=1"
|
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/es-define-property": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/es-errors": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/es-object-atoms": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"es-errors": "^1.3.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/es-set-tostringtag": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"es-errors": "^1.3.0",
|
||||||
|
"get-intrinsic": "^1.2.6",
|
||||||
|
"has-tostringtag": "^1.0.2",
|
||||||
|
"hasown": "^2.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/escalade": {
|
"node_modules/escalade": {
|
||||||
"version": "3.1.2",
|
"version": "3.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz",
|
||||||
@@ -614,12 +693,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/form-data": {
|
"node_modules/form-data": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz",
|
||||||
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
|
"integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"asynckit": "^0.4.0",
|
"asynckit": "^0.4.0",
|
||||||
"combined-stream": "^1.0.8",
|
"combined-stream": "^1.0.8",
|
||||||
|
"es-set-tostringtag": "^2.1.0",
|
||||||
|
"hasown": "^2.0.2",
|
||||||
"mime-types": "^2.1.12"
|
"mime-types": "^2.1.12"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -661,6 +743,43 @@
|
|||||||
"node": "6.* || 8.* || >= 10.*"
|
"node": "6.* || 8.* || >= 10.*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/get-intrinsic": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"call-bind-apply-helpers": "^1.0.2",
|
||||||
|
"es-define-property": "^1.0.1",
|
||||||
|
"es-errors": "^1.3.0",
|
||||||
|
"es-object-atoms": "^1.1.1",
|
||||||
|
"function-bind": "^1.1.2",
|
||||||
|
"get-proto": "^1.0.1",
|
||||||
|
"gopd": "^1.2.0",
|
||||||
|
"has-symbols": "^1.1.0",
|
||||||
|
"hasown": "^2.0.2",
|
||||||
|
"math-intrinsics": "^1.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/get-proto": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"dunder-proto": "^1.0.1",
|
||||||
|
"es-object-atoms": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/glob": {
|
"node_modules/glob": {
|
||||||
"version": "7.2.3",
|
"version": "7.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
||||||
@@ -681,6 +800,18 @@
|
|||||||
"url": "https://github.com/sponsors/isaacs"
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/gopd": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/graceful-fs": {
|
"node_modules/graceful-fs": {
|
||||||
"version": "4.2.11",
|
"version": "4.2.11",
|
||||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
|
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
|
||||||
@@ -695,6 +826,33 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/has-symbols": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/has-tostringtag": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"has-symbols": "^1.0.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/hasown": {
|
"node_modules/hasown": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
|
||||||
@@ -742,9 +900,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/http-cookie-agent/node_modules/agent-base": {
|
"node_modules/http-cookie-agent/node_modules/agent-base": {
|
||||||
"version": "7.1.3",
|
"version": "7.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
|
||||||
"integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==",
|
"integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 14"
|
"node": ">= 14"
|
||||||
@@ -1048,6 +1206,15 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/math-intrinsics": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/mime-db": {
|
"node_modules/mime-db": {
|
||||||
"version": "1.52.0",
|
"version": "1.52.0",
|
||||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||||
|
@@ -28,7 +28,7 @@
|
|||||||
},
|
},
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sasjs/cli": "^4.12.8",
|
"@sasjs/cli": "^4.12.10",
|
||||||
"@sasjs/core": "^4.59.1"
|
"@sasjs/core": "^4.59.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
Reference in New Issue
Block a user