feat(multi load): added HOT for user datasets input
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 51s
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 51s
This commit is contained in:
parent
6df7d8d2ba
commit
18363bbbeb
@ -19,10 +19,11 @@ import {
|
||||
exclamationTriangleIcon,
|
||||
fileIcon,
|
||||
moonIcon,
|
||||
sunIcon
|
||||
sunIcon,
|
||||
trashIcon
|
||||
} from '@cds/core/icon'
|
||||
|
||||
ClarityIcons.addIcons(moonIcon, sunIcon, exclamationTriangleIcon, fileIcon)
|
||||
ClarityIcons.addIcons(moonIcon, sunIcon, exclamationTriangleIcon, fileIcon, trashIcon)
|
||||
|
||||
@Component({
|
||||
selector: 'my-app',
|
||||
|
@ -109,134 +109,169 @@
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="!parsedDatasets.length">
|
||||
<div class="d-flex clr-justify-content-center mt-15">
|
||||
<div class="dataset-input-wrapper">
|
||||
<p cds-text="secondary regular" class="mb-20">
|
||||
Selected file: <strong>{{ selectedFile?.name }}</strong>
|
||||
</p>
|
||||
<p cds-text="secondary regular">
|
||||
Paste or type the list of datasets to upload:
|
||||
</p>
|
||||
|
||||
<button
|
||||
(click)="onAutoDetectColumns()"
|
||||
class="mt-15 btn btn-primary-outline btn-sm"
|
||||
>
|
||||
Auto detect
|
||||
</button>
|
||||
|
||||
<clr-textarea-container class="m-0">
|
||||
<textarea
|
||||
clrTextarea
|
||||
[(ngModel)]="userInputDatasets"
|
||||
(input)="onUserInputDatasetsChange()"
|
||||
class="w-100-i"
|
||||
></textarea>
|
||||
<clr-control-helper
|
||||
>Every row is one dataset. Format:
|
||||
LIBRARY.TABLE</clr-control-helper
|
||||
>
|
||||
</clr-textarea-container>
|
||||
|
||||
<div class="text-right mt-10">
|
||||
<button
|
||||
(click)="onDiscardFile()"
|
||||
class="btn btn-danger btn-sm"
|
||||
[disabled]="uploadLoading"
|
||||
>
|
||||
Discard file
|
||||
</button>
|
||||
<button
|
||||
(click)="onUploadFile()"
|
||||
class="btn btn-primary btn-sm"
|
||||
[disabled]="!matchedDatasets.length"
|
||||
[clrLoading]="uploadLoading"
|
||||
>
|
||||
Continue
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div *ngIf="matchedDatasets.length">
|
||||
<p><strong>Matched datasets:</strong></p>
|
||||
<p
|
||||
*ngFor="let matchedDataset of matchedDatasets"
|
||||
class="m-0 ml-5-i"
|
||||
>
|
||||
{{ matchedDataset }}
|
||||
<ng-container *ngIf="selectedFile !== null">
|
||||
<ng-container *ngIf="!parsedDatasets.length">
|
||||
<div class="d-flex clr-justify-content-center mt-15">
|
||||
<div class="dataset-input-wrapper">
|
||||
<p cds-text="secondary regular" class="mb-20">
|
||||
Selected file: <strong>{{ selectedFile?.name }}</strong>
|
||||
<cds-icon (click)="onDiscardFile()" shape="trash" status="danger" class="ml-5 cursor-pointer"></cds-icon>
|
||||
</p>
|
||||
<p cds-text="secondary regular" class="mb-15">
|
||||
Paste or type the list of datasets to upload:
|
||||
</p>
|
||||
|
||||
<clr-control-helper class="mb-5">Each row is one dataset. We automatically detected some tables by the sheetname.</clr-control-helper>
|
||||
|
||||
<hot-table
|
||||
hotId="hotInstanceUserDataset"
|
||||
id="hotTableUserDataset"
|
||||
class="mt-15"
|
||||
[afterGetColHeader]="afterGetColHeader"
|
||||
[settings]="hotUserDatasets"
|
||||
[licenseKey]="hotTableLicenseKey"
|
||||
stretchH="all"
|
||||
>
|
||||
</hot-table>
|
||||
|
||||
<div class="dataset-selection-actions text-right mt-10">
|
||||
<button
|
||||
(click)="onUploadFile()"
|
||||
class="btn btn-primary btn-sm"
|
||||
[disabled]="!matchedDatasets.length"
|
||||
[clrLoading]="uploadLoading"
|
||||
>
|
||||
Continue
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="parsedDatasets.length">
|
||||
<div
|
||||
*ngIf="!activeParsedDataset"
|
||||
class="no-table-selected pointer-events-none"
|
||||
>
|
||||
<clr-icon
|
||||
shape="warning-standard"
|
||||
size="40"
|
||||
class="is-info icon-dc-fill"
|
||||
></clr-icon>
|
||||
<p class="text-center color-gray mt-10" cds-text="section">
|
||||
Please select a dataset on the left to
|
||||
{{
|
||||
!submittedDatasets.length
|
||||
? 'review data'
|
||||
: 'review submitted results'
|
||||
}}
|
||||
</p>
|
||||
</div>
|
||||
<ng-container *ngIf="parsedDatasets.length">
|
||||
<div
|
||||
*ngIf="!activeParsedDataset && !activeSubmittedDataset"
|
||||
class="no-table-selected pointer-events-none"
|
||||
>
|
||||
<clr-icon
|
||||
shape="warning-standard"
|
||||
size="40"
|
||||
class="is-info icon-dc-fill"
|
||||
></clr-icon>
|
||||
<p class="text-center color-gray mt-10" cds-text="section">
|
||||
Please select a dataset on the left to
|
||||
{{
|
||||
!submittedDatasets.length
|
||||
? 'review data'
|
||||
: 'review submitted results'
|
||||
}}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="activeParsedDataset">
|
||||
<ng-container *ngIf="activeParsedDataset">
|
||||
<div class="d-flex clr-justify-content-between p-10">
|
||||
<div>
|
||||
<p cds-text="secondary regular" class="mb-10">
|
||||
Found in range:
|
||||
<strong
|
||||
>"{{
|
||||
activeParsedDataset.parseResult.rangeSheetRes?.sheetName
|
||||
}}"!{{
|
||||
activeParsedDataset.parseResult.rangeSheetRes?.rangeAddress
|
||||
}}</strong
|
||||
>
|
||||
</p>
|
||||
<p cds-text="secondary regular">
|
||||
Matched with dataset:
|
||||
<strong>{{ activeParsedDataset.libds }}</strong>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<clr-toggle-wrapper>
|
||||
<input
|
||||
type="checkbox"
|
||||
clrToggle
|
||||
[(ngModel)]="activeParsedDataset.includeInSubmission"
|
||||
name="options"
|
||||
required
|
||||
value="option1"
|
||||
/>
|
||||
<label>Include in submission</label>
|
||||
</clr-toggle-wrapper>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="isHotHidden" class="text-center w-100">
|
||||
<clr-spinner class="spinner-md"></clr-spinner>
|
||||
</div>
|
||||
|
||||
<hot-table
|
||||
hotId="hotInstance"
|
||||
id="hotTable"
|
||||
class="mt-15"
|
||||
[afterGetColHeader]="afterGetColHeader"
|
||||
[className]="['htDark', 'htCustomHidden']"
|
||||
[licenseKey]="hotTableLicenseKey"
|
||||
[multiColumnSorting]="true"
|
||||
[viewportRowRenderingOffset]="50"
|
||||
[manualColumnResize]="true"
|
||||
[filters]="true"
|
||||
stretchH="all"
|
||||
>
|
||||
</hot-table>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="activeSubmittedDataset">
|
||||
<div class="d-flex clr-justify-content-between p-10">
|
||||
<div>
|
||||
<p cds-text="secondary regular" class="mb-10">
|
||||
Found in range:
|
||||
<strong
|
||||
>"{{
|
||||
activeParsedDataset.parseResult.rangeSheetRes?.sheetName
|
||||
activeSubmittedDataset.parseResult.rangeSheetRes?.sheetName
|
||||
}}"!{{
|
||||
activeParsedDataset.parseResult.rangeSheetRes?.rangeAddress
|
||||
activeSubmittedDataset.parseResult.rangeSheetRes?.rangeAddress
|
||||
}}</strong
|
||||
>
|
||||
</p>
|
||||
<p cds-text="secondary regular">
|
||||
<p cds-text="secondary regular" class="mb-10">
|
||||
Matched with dataset:
|
||||
<strong>{{ activeParsedDataset.libds }}</strong>
|
||||
<strong>{{ activeSubmittedDataset.libds }}</strong>
|
||||
</p>
|
||||
<p cds-text="secondary regular" class="mb-10">
|
||||
Status:
|
||||
<span *ngIf="activeSubmittedDataset.success" class="color-green"><strong>SUCCESS</strong></span>
|
||||
<span *ngIf="activeSubmittedDataset.error" class="color-red"><strong>ERROR</strong></span>
|
||||
</p>
|
||||
<p *ngIf="activeSubmittedDataset.error" cds-text="secondary regular">
|
||||
Error details:
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<clr-toggle-wrapper>
|
||||
<input
|
||||
type="checkbox"
|
||||
clrToggle
|
||||
[(ngModel)]="activeParsedDataset.includeInSubmission"
|
||||
name="options"
|
||||
required
|
||||
value="option1"
|
||||
/>
|
||||
<label>Include in submission</label>
|
||||
</clr-toggle-wrapper>
|
||||
<button
|
||||
(click)="reSubmitTable(activeSubmittedDataset)"
|
||||
class="btn btn-primary mt-10"
|
||||
[clrLoading]="submitLoading"
|
||||
>
|
||||
Resubmit
|
||||
</button>
|
||||
<button
|
||||
(click)="downloadFile(activeSubmittedDataset.success || activeSubmittedDataset.error)"
|
||||
class="btn btn-primary-outline mt-10"
|
||||
>
|
||||
Download log
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hot-table
|
||||
hotId="hotInstance"
|
||||
id="hotTable"
|
||||
class="mt-15"
|
||||
className="htDark"
|
||||
[licenseKey]="hotTableLicenseKey"
|
||||
[multiColumnSorting]="true"
|
||||
[viewportRowRenderingOffset]="50"
|
||||
[manualColumnResize]="true"
|
||||
[filters]="true"
|
||||
stretchH="all"
|
||||
>
|
||||
</hot-table>
|
||||
<div *ngIf="activeSubmittedDataset.error" class="error-field mt-15">
|
||||
<div class="log-wrapper">
|
||||
{{ activeSubmittedDataset.error | json }}
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
|
||||
@ -274,8 +309,7 @@
|
||||
</div>
|
||||
|
||||
<p cds-text="caption_clean" class="mt-10">
|
||||
Sheets which did not match any dataset will be ignored. Tables will be
|
||||
sent sequentially, logs will be available after all tables are submitted.
|
||||
Tables will be sent sequentially, logs will be available after all tables are submitted.
|
||||
</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
|
@ -17,7 +17,7 @@
|
||||
}
|
||||
|
||||
.dataset-input-wrapper {
|
||||
max-width: 350px;
|
||||
max-width: 500px;
|
||||
width: 100%;
|
||||
|
||||
textarea {
|
||||
@ -30,4 +30,17 @@
|
||||
min-height: 70px;
|
||||
max-height: 70px;
|
||||
height: 70px;
|
||||
}
|
||||
|
||||
.log-wrapper {
|
||||
margin: 0 10px;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
::ng-deep td.not-matched {
|
||||
background-color: #ff000054;
|
||||
}
|
||||
|
||||
.dataset-selection-actions {
|
||||
border-top: 1px solid #d3d3d3;
|
||||
}
|
@ -2,13 +2,13 @@ import {
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
HostBinding,
|
||||
OnInit,
|
||||
SimpleChanges
|
||||
OnInit
|
||||
} from '@angular/core'
|
||||
import {
|
||||
EventService,
|
||||
HelperService,
|
||||
LicenceService,
|
||||
SasService,
|
||||
SasStoreService
|
||||
} from '../services'
|
||||
import * as XLSX from '@sheet/crypto'
|
||||
@ -24,8 +24,9 @@ import {
|
||||
} from '../services/spreadsheet.service'
|
||||
import Handsontable from 'handsontable'
|
||||
import { HotTableRegisterer } from '@handsontable/angular'
|
||||
import { Router } from '@angular/router'
|
||||
import { EditorsStageDataSASResponse } from '../models/sas/editors-stagedata.model'
|
||||
import { CellChange, ChangeSource } from 'handsontable/common'
|
||||
import { baseAfterGetColHeader } from '../shared/utils/hot.utils'
|
||||
|
||||
@Component({
|
||||
selector: 'app-multi-dataset',
|
||||
@ -57,11 +58,50 @@ export class MultiDatasetComponent implements OnInit {
|
||||
} = {}
|
||||
|
||||
public hotInstance!: Handsontable
|
||||
public hotInstanceUserDataset!: Handsontable
|
||||
private hotRegisterer: HotTableRegisterer
|
||||
|
||||
public showSubmitReasonModal: boolean = false
|
||||
public submitReasonMessage: string = ''
|
||||
|
||||
public hotUserDatasets: Handsontable.GridSettings = {
|
||||
colHeaders: [
|
||||
'Library',
|
||||
'Table'
|
||||
],
|
||||
data: [
|
||||
['', ''],
|
||||
['', ''],
|
||||
['', ''],
|
||||
['', ''],
|
||||
['', ''],
|
||||
['', ''],
|
||||
['', ''],
|
||||
['', ''],
|
||||
['', ''],
|
||||
['', ''],
|
||||
['', ''],
|
||||
['', '']
|
||||
],
|
||||
width: '100%',
|
||||
height: '305px',
|
||||
className: ['htDark'],
|
||||
contextMenu: {
|
||||
items: {
|
||||
row_above: {
|
||||
name: 'Insert Row above'
|
||||
},
|
||||
row_below: {
|
||||
name: 'Insert Row below'
|
||||
}
|
||||
}
|
||||
},
|
||||
manualRowMove: true,
|
||||
columnSorting: true
|
||||
}
|
||||
|
||||
public afterGetColHeader = baseAfterGetColHeader
|
||||
|
||||
constructor(
|
||||
private eventService: EventService,
|
||||
private licenceService: LicenceService,
|
||||
@ -122,6 +162,9 @@ export class MultiDatasetComponent implements OnInit {
|
||||
|
||||
this.selectedFile = event.target.files[0]
|
||||
event.target.value = '' // Reset the upload input
|
||||
|
||||
this.initUserInputHot()
|
||||
this.onAutoDetectColumns()
|
||||
}
|
||||
|
||||
onDiscardFile() {
|
||||
@ -197,7 +240,7 @@ export class MultiDatasetComponent implements OnInit {
|
||||
}
|
||||
})
|
||||
.catch((error: string) => {
|
||||
this.eventService.showInfoModal('Error', error)
|
||||
console.warn('Parsing excel file error.', error)
|
||||
})
|
||||
}
|
||||
})
|
||||
@ -217,44 +260,111 @@ export class MultiDatasetComponent implements OnInit {
|
||||
|
||||
initHot() {
|
||||
setTimeout(() => {
|
||||
if (!this.hotInstance)
|
||||
this.hotInstance = this.hotRegisterer.getInstance('hotInstance')
|
||||
this.hotInstance = this.hotRegisterer.getInstance('hotInstance')
|
||||
|
||||
if (this.activeParsedDataset) {
|
||||
this.hotInstance.updateSettings({
|
||||
data: this.activeParsedDataset.datasetInfo.data.sasdata,
|
||||
data: this.activeParsedDataset.datasource,
|
||||
colHeaders: this.activeParsedDataset.datasetInfo.headerColumns,
|
||||
columns: this.activeParsedDataset.datasetInfo.dcValidator?.getRules(),
|
||||
readOnly: true,
|
||||
height: '300px'
|
||||
height: '300px',
|
||||
className: 'htDark'
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
onUserInputDatasetsChange() {
|
||||
this.helperService.debounceCall(500, () => {
|
||||
const inputDatasets = this.userInputDatasets.split('\n')
|
||||
initUserInputHot() {
|
||||
setTimeout(() => {
|
||||
this.hotInstanceUserDataset = this.hotRegisterer.getInstance('hotInstanceUserDataset')
|
||||
|
||||
this.matchedDatasets = []
|
||||
|
||||
inputDatasets.forEach((dataset: string) => {
|
||||
const trimmedDataset = dataset.trim()
|
||||
|
||||
if (
|
||||
this.isValidDatasetFormat(trimmedDataset) &&
|
||||
this.isValidDatasetReference(trimmedDataset) &&
|
||||
!this.matchedDatasets.includes(trimmedDataset)
|
||||
) {
|
||||
this.matchedDatasets.push(trimmedDataset)
|
||||
} else {
|
||||
console.warn(
|
||||
`Sheet name: ${trimmedDataset} is not an actual dataset reference.`
|
||||
)
|
||||
this.hotInstanceUserDataset.addHook('beforeChange', (changes: (CellChange | null)[], source: ChangeSource) => {
|
||||
if (changes) {
|
||||
for (let change of changes) {
|
||||
if (change && change[3]) {
|
||||
change[3] = change[3].toUpperCase()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
console.log('this.matchedDatasets', this.matchedDatasets)
|
||||
this.hotInstanceUserDataset.addHook('afterChange', async (changes: CellChange[] | null, source: ChangeSource) => {
|
||||
if (changes) {
|
||||
if (source === 'edit') {
|
||||
await this.onUserInputDatasetsChange()
|
||||
}
|
||||
|
||||
for (let change of changes) {
|
||||
const row = change[0] as number
|
||||
|
||||
this.markUnmatchedRows(row)
|
||||
}
|
||||
|
||||
this.hotInstanceUserDataset.render()
|
||||
}
|
||||
})
|
||||
|
||||
this.hotInstanceUserDataset.addHook('afterRemoveRow', async (index: number, amount: number, physicalRows: number[], source?: Handsontable.ChangeSource | undefined) => {
|
||||
await this.onUserInputDatasetsChange()
|
||||
|
||||
for (let row of physicalRows) {
|
||||
this.markUnmatchedRows(row)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
markUnmatchedRows(row: number) {
|
||||
const dataAtRow = this.hotInstanceUserDataset.getDataAtRow(row) as number[]
|
||||
const dataset = `${dataAtRow[0]}.${dataAtRow[1]}`
|
||||
const cellMetaAtRow = this.hotInstanceUserDataset.getCellMetaAtRow(row)
|
||||
|
||||
if (dataAtRow && dataAtRow[0] && dataAtRow[1]) {
|
||||
if (!this.matchedDatasets.includes(dataset)) {
|
||||
cellMetaAtRow.forEach(cellMeta => {
|
||||
this.hotInstanceUserDataset.setCellMeta(row, cellMeta.col, 'className', 'not-matched')
|
||||
})
|
||||
} else {
|
||||
cellMetaAtRow.forEach(cellMeta => {
|
||||
this.hotInstanceUserDataset.setCellMeta(row, cellMeta.col, 'className', '')
|
||||
})
|
||||
}
|
||||
} else {
|
||||
cellMetaAtRow.forEach(cellMeta => {
|
||||
this.hotInstanceUserDataset.setCellMeta(row, cellMeta.col, 'className', '')
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
onUserInputDatasetsChange() {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.helperService.debounceCall(100, () => {
|
||||
// Parse datasets
|
||||
const inputDatasets = this.getDatasetsFromHot()
|
||||
|
||||
this.matchedDatasets = []
|
||||
|
||||
inputDatasets.forEach((dataset: string) => {
|
||||
const trimmedDataset = dataset.trim()
|
||||
|
||||
if (
|
||||
this.isValidDatasetFormat(trimmedDataset) &&
|
||||
this.isValidDatasetReference(trimmedDataset) &&
|
||||
!this.matchedDatasets.includes(trimmedDataset)
|
||||
) {
|
||||
this.matchedDatasets.push(trimmedDataset)
|
||||
} else {
|
||||
console.warn(
|
||||
`Sheet name: ${trimmedDataset} is not an actual dataset reference.`
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
this.cdr.detectChanges()
|
||||
|
||||
resolve(undefined)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@ -286,6 +396,27 @@ export class MultiDatasetComponent implements OnInit {
|
||||
|
||||
// Set matched datasets to textarea, dataset per row
|
||||
this.userInputDatasets = this.matchedDatasets.join('\n')
|
||||
|
||||
const hotReadyData = this.matchedDatasets.map(matchedDs => {
|
||||
return [
|
||||
matchedDs.split('.')[0],
|
||||
matchedDs.split('.')[1]
|
||||
]
|
||||
})
|
||||
|
||||
// Add empty rows to fill initial number of rows if data has less rows
|
||||
// The reason is to fill the height of the table
|
||||
const initialNumberOfRows = this.hotUserDatasets.data!.length
|
||||
|
||||
if (hotReadyData.length < initialNumberOfRows) {
|
||||
const missingRows = initialNumberOfRows - hotReadyData.length
|
||||
|
||||
for (let i = 0; i < missingRows; i++) {
|
||||
hotReadyData.push(['', ''])
|
||||
}
|
||||
}
|
||||
|
||||
this.hotInstanceUserDataset.updateData(hotReadyData)
|
||||
}
|
||||
|
||||
onParsedDatasetClick(parsedDataset: ParsedDataset) {
|
||||
@ -294,16 +425,48 @@ export class MultiDatasetComponent implements OnInit {
|
||||
parsedDataset.active = true
|
||||
|
||||
this.cdr.detectChanges()
|
||||
|
||||
this.initHot()
|
||||
}
|
||||
|
||||
onSubmittedDatasetClick(submittedDataset: SubmittedDatasetResult) {}
|
||||
onSubmittedDatasetClick(submittedDataset: SubmittedDatasetResult) {
|
||||
this.deselectAllSubmittedDatasets()
|
||||
|
||||
submittedDataset.active = true
|
||||
}
|
||||
|
||||
public get activeParsedDataset(): ParsedDataset | undefined {
|
||||
return this.parsedDatasets.find((dataset) => dataset.active)
|
||||
}
|
||||
|
||||
public get activeSubmittedDataset(): SubmittedDatasetResult | undefined {
|
||||
return this.submittedDatasets.find((dataset) => dataset.active)
|
||||
}
|
||||
|
||||
public get notFoundDatasets(): string[] {
|
||||
const userDatasets = this.getDatasetsFromHot()
|
||||
|
||||
return userDatasets.filter(userDs => !this.matchedDatasets.includes(userDs.trim())).filter(userDs => userDs.length)
|
||||
}
|
||||
|
||||
public get isHotHidden() :boolean {
|
||||
if (!this.hotInstance) return true
|
||||
|
||||
try {
|
||||
const className = this.hotInstance.getSettings().className
|
||||
|
||||
return !!className && className.includes('htCustomHidden')
|
||||
} catch (err) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
public downloadFile(
|
||||
response: any
|
||||
) {
|
||||
const filename = `stagedata-${this.activeSubmittedDataset?.libds}-log`
|
||||
this.helperService.downloadTextFile(filename, JSON.stringify(response))
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the table for given datasets params LIBRARY.TABLE
|
||||
*/
|
||||
@ -333,17 +496,22 @@ export class MultiDatasetComponent implements OnInit {
|
||||
|
||||
/**
|
||||
* Sends tables to the SAS sequentially
|
||||
*
|
||||
* @param explicitDatasets if empty all datasets will be sent, otherwise only datasets
|
||||
* in the array will be sent. eg. ['lib1.table_1', 'lib1.table_2']
|
||||
*/
|
||||
async submitTables() {
|
||||
async submitTables(explicitDatasets?: string[]) {
|
||||
console.info('Submitting multiple tables', this.parsedDatasets)
|
||||
|
||||
this.submitLoading = true
|
||||
|
||||
let requestsResults: SubmittedDatasetResult[] = []
|
||||
let requestsResults: SubmittedDatasetResult[] = explicitDatasets ? this.submittedDatasets : []
|
||||
|
||||
for (let table of this.parsedDatasets) {
|
||||
// Skip the table if toggle switch is off
|
||||
if (!table.includeInSubmission) continue
|
||||
// Skip the table if datasets is present and this table not defined in it
|
||||
if (explicitDatasets && !explicitDatasets.includes(table.libds)) continue
|
||||
|
||||
let updateParams: any = {}
|
||||
|
||||
@ -387,7 +555,8 @@ export class MultiDatasetComponent implements OnInit {
|
||||
table.datasource,
|
||||
'SASControlTable',
|
||||
'editors/stagedata',
|
||||
table.datasetInfo.data.$sasdata
|
||||
table.datasetInfo.data.$sasdata,
|
||||
true
|
||||
)
|
||||
.then((res: EditorsStageDataSASResponse) => {
|
||||
success = res
|
||||
@ -398,22 +567,53 @@ export class MultiDatasetComponent implements OnInit {
|
||||
error = err
|
||||
})
|
||||
|
||||
requestsResults.push({
|
||||
const requestResult = {
|
||||
success,
|
||||
error,
|
||||
parseResult: table.parseResult,
|
||||
libds: table.libds
|
||||
})
|
||||
}
|
||||
|
||||
// If explicit datasets are set don't just push to th array
|
||||
// instead replace if result already exist from before (this might be re-submit)
|
||||
if (explicitDatasets) {
|
||||
const existingResultIndex = requestsResults.findIndex(result => result.libds === table.libds)
|
||||
|
||||
if (existingResultIndex) {
|
||||
requestsResults[existingResultIndex] = requestResult
|
||||
} else {
|
||||
requestsResults.push(requestResult)
|
||||
}
|
||||
} else {
|
||||
requestsResults.push(requestResult)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log('requestsResults', requestsResults)
|
||||
|
||||
this.submittedDatasets = requestsResults
|
||||
this.showSubmitReasonModal = false
|
||||
this.submitLoading = false
|
||||
this.deselectAllParsedDatasets()
|
||||
}
|
||||
|
||||
async reSubmitTable(activeSubmittedDataset: SubmittedDatasetResult) {
|
||||
// Submit only particular table
|
||||
this.submitTables([activeSubmittedDataset.libds])
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns list of strings containing datasets in the HOT user input
|
||||
* Format: LIBRARY.TABLE
|
||||
*/
|
||||
private getDatasetsFromHot(): string[] {
|
||||
if (!this.hotInstanceUserDataset) return []
|
||||
|
||||
const hotData = this.hotInstanceUserDataset.getData()
|
||||
|
||||
return hotData.filter(row => row[0]?.length && row[1]?.length).map(row => row ? `${row[0]}.${row[1]}` : '')
|
||||
}
|
||||
|
||||
private parseExcelSheetNames(): Promise<string[]> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader()
|
||||
@ -575,6 +775,12 @@ export class MultiDatasetComponent implements OnInit {
|
||||
parsedDataset.active = false
|
||||
}
|
||||
}
|
||||
|
||||
private deselectAllSubmittedDatasets() {
|
||||
for (let submittedDataset of this.submittedDatasets) {
|
||||
submittedDataset.active = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface DatasetsObject extends EditorsGetDataServiceResponse {
|
||||
@ -605,5 +811,6 @@ export interface SubmittedDatasetResult {
|
||||
libds: string
|
||||
success: EditorsStageDataSASResponse | undefined
|
||||
error: any
|
||||
parseResult: ParseResult
|
||||
active?: boolean
|
||||
}
|
||||
|
@ -83,7 +83,8 @@ export class SasStoreService {
|
||||
tableData: any,
|
||||
tableName: string,
|
||||
program: string,
|
||||
$dataFormats: $DataFormats | null
|
||||
$dataFormats: $DataFormats | null,
|
||||
suppressErrorSuccessMessages?: boolean
|
||||
): Promise<EditorsStageDataSASResponse> {
|
||||
// add sp as third argument of createData call
|
||||
|
||||
@ -99,7 +100,10 @@ export class SasStoreService {
|
||||
|
||||
tables[tableName] = [tableParams]
|
||||
|
||||
let res: any = await this.sasService.request(program, tables)
|
||||
let res: any = await this.sasService.request(program, tables, null, {
|
||||
suppressErrorAbortModal: suppressErrorSuccessMessages,
|
||||
suppressSuccessAbortModal: suppressErrorSuccessMessages
|
||||
})
|
||||
|
||||
return res
|
||||
}
|
||||
|
@ -93,7 +93,8 @@ export class SasService {
|
||||
* @param url service to run reuqest against
|
||||
* @param data to be sent to backend service
|
||||
* @param config additional parameters to force eg. { debug: false }
|
||||
* @param wrapperOptions used to suppress error or success abort modals after request is finished
|
||||
* @param wrapperOptions used to provide options to the request wrapper function
|
||||
* for example to suppress error or success abort modals after request is finished
|
||||
* @returns
|
||||
*/
|
||||
public request(
|
||||
|
@ -61,20 +61,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.log-wrapper {
|
||||
min-height: 50px;
|
||||
padding: 10px;
|
||||
margin-top: 10px;
|
||||
|
||||
white-space: pre-wrap;
|
||||
border-radius: 3px;
|
||||
|
||||
border: 1px solid #e2e2e2;
|
||||
|
||||
height: 48vh;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.no-reqs {
|
||||
border-top: 1px solid #0000001a;
|
||||
padding-top: 5px;
|
||||
|
7
client/src/app/shared/utils/hot.utils.ts
Normal file
7
client/src/app/shared/utils/hot.utils.ts
Normal file
@ -0,0 +1,7 @@
|
||||
/**
|
||||
* Function reused in HOT instances to add a class used for dark mode
|
||||
*/
|
||||
export const baseAfterGetColHeader = (column: number, TH: HTMLTableCellElement, headerLevel: number) => {
|
||||
// Dark mode
|
||||
TH.classList.add('darkTH')
|
||||
}
|
@ -95,6 +95,20 @@ body[cds-theme="light"] {
|
||||
}
|
||||
}
|
||||
|
||||
.log-wrapper {
|
||||
min-height: 50px;
|
||||
padding: 10px;
|
||||
margin-top: 10px;
|
||||
|
||||
white-space: pre-wrap;
|
||||
border-radius: 3px;
|
||||
|
||||
border: 1px solid #e2e2e2;
|
||||
|
||||
height: 48vh;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
// Custom loading spinner
|
||||
.slider {
|
||||
position: absolute;
|
||||
@ -807,6 +821,10 @@ clr-icon.is-info {
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
.htCustomHidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
body[cds-theme="dark"] {
|
||||
.htDark {
|
||||
background: #888;
|
||||
|
Loading…
Reference in New Issue
Block a user