style: lint
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 1m29s

This commit is contained in:
Mihajlo Medjedovic 2024-06-14 15:46:35 +02:00
parent efcdc694dd
commit fc52e8f41a
11 changed files with 397 additions and 245 deletions

View File

@ -47,9 +47,7 @@ import { UploadStaterComponent } from './components/upload-stater/upload-stater.
import { DynamicExtendedCellValidation } from './models/dynamicExtendedCellValidation'
import { EditRecordInputFocusedEvent } from './models/edit-record/edit-record-events'
import { EditorRestrictions } from './models/editor-restrictions.model'
import {
parseTableColumns
} from './utils/grid.utils'
import { parseTableColumns } from './utils/grid.utils'
import {
errorRenderer,
noSpinnerRenderer,
@ -59,7 +57,10 @@ import { LicenceService } from '../services/licence.service'
import * as numbro from 'numbro'
import * as languages from 'numbro/dist/languages.min'
import { FileUploadEncoding } from '../models/FileUploadEncoding'
import { ParseResult, SpreadsheetService } from '../services/spreadsheet.service'
import {
ParseResult,
SpreadsheetService
} from '../services/spreadsheet.service'
@Component({
selector: 'app-editor',
@ -471,50 +472,58 @@ export class EditorComponent implements OnInit, AfterViewInit {
this.filename = file.name
this.spreadsheetService.parseExcelFile({
file: file,
uploader: this.uploader,
dcValidator: this.dcValidator!,
headerPks: this.headerPks,
headerArray: this.headerArray,
headerShow: this.headerShow,
timeHeaders: this.timeHeaders,
dateHeaders: this.dateHeaders,
dateTimeHeaders: this.dateTimeHeaders,
xlRules: this.xlRules,
encoding: this.encoding
}, (uploadState: string) => {
this.appendUploadState(uploadState)
}, (tableFoundInfo: string) => {
this.eventService.showInfoModal('Table Found', tableFoundInfo)
}).then((parseResult: ParseResult | undefined) => {
if (parseResult) {
this.excelFileReady = true
this.uploader = parseResult.uploader
if (parseResult.data && parseResult.headerShow) {
// If data is returned it means we parsed excel file
this.data = parseResult.data
this.headerShow = parseResult.headerShow
this.getPendingExcelPreview()
} else {
// otherwise it's csv file, and we send them directly
this.uploadParsedFiles()
this.spreadsheetService
.parseExcelFile(
{
file: file,
uploader: this.uploader,
dcValidator: this.dcValidator!,
headerPks: this.headerPks,
headerArray: this.headerArray,
headerShow: this.headerShow,
timeHeaders: this.timeHeaders,
dateHeaders: this.dateHeaders,
dateTimeHeaders: this.dateTimeHeaders,
xlRules: this.xlRules,
encoding: this.encoding
},
(uploadState: string) => {
this.appendUploadState(uploadState)
},
(tableFoundInfo: string) => {
this.eventService.showInfoModal('Table Found', tableFoundInfo)
}
}
}).catch((error: string) => {
this.eventService.showInfoModal('Error', error)
)
.then((parseResult: ParseResult | undefined) => {
if (parseResult) {
this.excelFileReady = true
this.showUploadModal = false
this.uploadPreview = false
this.uploader = parseResult.uploader
setTimeout(() => {
this.filename = ''
if (parseResult.data && parseResult.headerShow) {
// If data is returned it means we parsed excel file
this.data = parseResult.data
this.headerShow = parseResult.headerShow
this.getPendingExcelPreview()
} else {
// otherwise it's csv file, and we send them directly
this.uploadParsedFiles()
}
}
})
.catch((error: string) => {
this.eventService.showInfoModal('Error', error)
this.showUploadModal = false
this.uploadPreview = false
setTimeout(() => {
this.filename = ''
})
})
.finally(() => {
this.excelFileParsing = false
})
}).finally(() => {
this.excelFileParsing = false
})
}
/**
@ -728,7 +737,6 @@ export class EditorComponent implements OnInit, AfterViewInit {
this.sasStoreService.removeClause()
}
async sendClause() {
this.submitLoading = true
let nullVariableArr = []

View File

@ -13,7 +13,7 @@ const routes: Routes = [
{ path: '', pathMatch: 'full', redirectTo: 'tables' },
{ path: 'tables', component: HomeComponent },
{ path: 'excel-maps', loadChildren: () => XLMapModule },
{ path: 'multi-load', loadChildren: () => MultiDatasetModule}
{ path: 'multi-load', loadChildren: () => MultiDatasetModule }
]
}
]

View File

@ -1 +1 @@
export type FileUploadEncoding = 'UTF-8' | 'WLATIN1'
export type FileUploadEncoding = 'UTF-8' | 'WLATIN1'

View File

@ -8,9 +8,7 @@ const routes: Routes = [
{
path: '',
component: MultiDatasetRouteComponent,
children: [
{ path: '', component: MultiDatasetComponent }
]
children: [{ path: '', component: MultiDatasetComponent }]
}
]

View File

@ -4,15 +4,21 @@
</div>
<div class="text-center mb-10">
<button (click)="fileUploadInput.click()" class="btn btn-primary btn-sm" [disabled]="selectedFile !== null">Browse file</button>
<button
(click)="fileUploadInput.click()"
class="btn btn-primary btn-sm"
[disabled]="selectedFile !== null"
>
Browse file
</button>
<input
hidden
#fileUploadInput
id="file-upload"
type="file"
(change)="onFileChange($event)"
appFileSelect
/>
hidden
#fileUploadInput
id="file-upload"
type="file"
(change)="onFileChange($event)"
appFileSelect
/>
</div>
<ng-container *ngIf="parsedDatasets.length > 0">
@ -24,8 +30,16 @@
class="clr-treenode-link"
[class.active]="dataset.active"
>
<cds-icon *ngIf="dataset.status === 'error'" status="danger" shape="exclamation-circle"></cds-icon>
<cds-icon *ngIf="dataset.status === 'success'" status="success" shape="check-circle"></cds-icon>
<cds-icon
*ngIf="dataset.status === 'error'"
status="danger"
shape="exclamation-circle"
></cds-icon>
<cds-icon
*ngIf="dataset.status === 'success'"
status="success"
shape="check-circle"
></cds-icon>
<cds-icon shape="file"></cds-icon>
{{ dataset.libds }}
</button>
@ -39,9 +53,7 @@
</app-sidebar>
<div class="content-area">
<div
class="card no-borders h-100 d-flex clr-flex-column"
>
<div class="card no-borders h-100 d-flex clr-flex-column">
<div
class="header-row clr-row justify-content-between clr-justify-content-center w-100 m-0"
>
@ -65,24 +77,59 @@
<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>
<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>
<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>
<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>
<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 }}</p>
<p
*ngFor="let matchedDataset of matchedDatasets"
class="m-0 ml-5-i"
>
{{ matchedDataset }}
</p>
</div>
</div>
</div>
@ -105,20 +152,37 @@
<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>LIB1.MPE_X_DATA</strong></p>
<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>LIB1.MPE_X_DATA</strong>
</p>
</div>
<div>
<clr-toggle-wrapper>
<input type="checkbox" clrToggle name="options" required value="option1"/>
<input
type="checkbox"
clrToggle
name="options"
required
value="option1"
/>
<label>Include in submission</label>
</clr-toggle-wrapper>
</div>
</div>
</ng-container>
<!--
<!--
<div *ngIf="!noData && !noDataReqErr && table" class="clr-flex-1">
<hot-table
hotId="hotInstance"

View File

@ -1,8 +1,4 @@
import {
Component,
HostBinding,
OnInit,
} from '@angular/core'
import { Component, HostBinding, OnInit } from '@angular/core'
import { ActivatedRoute, Router } from '@angular/router'
import {
EventService,
@ -19,7 +15,10 @@ import { DcValidator } from '../shared/dc-validator/dc-validator'
import { ExcelRule } from '../models/TableData'
import { HotTableInterface } from '../models/HotTable.interface'
import { Col } from '../shared/dc-validator/models/col.model'
import { ParseResult, SpreadsheetService } from '../services/spreadsheet.service'
import {
ParseResult,
SpreadsheetService
} from '../services/spreadsheet.service'
@Component({
selector: 'app-multi-dataset',
@ -81,16 +80,26 @@ export class MultiDatasetComponent implements OnInit {
onFileChange(event: any) {
if (!event?.target?.files[0]) {
this.eventService.showAbortModal(null, 'No file found.', null, 'File Upload')
this.eventService.showAbortModal(
null,
'No file found.',
null,
'File Upload'
)
return
}
const file = event.target.files[0];
const fileTitle = file.name;
const file = event.target.files[0]
const fileTitle = file.name
const fileExtension = fileTitle.split('.').pop()
if (!['xlsx', 'xlsm', 'xlm'].includes(fileExtension)) {
this.eventService.showAbortModal(null, 'Only excel extensions are allowed. (xlsx)', null, 'Extension Error')
this.eventService.showAbortModal(
null,
'Only excel extensions are allowed. (xlsx)',
null,
'Extension Error'
)
return
}
@ -106,19 +115,22 @@ export class MultiDatasetComponent implements OnInit {
async onUploadFile() {
this.uploadLoading = true
const datasetFetchingPromises: Promise<EditorsGetDataServiceResponse | undefined>[] = []
const datasetFetchingPromises: Promise<
EditorsGetDataServiceResponse | undefined
>[] = []
let datasets: EditorsGetDataServiceResponse[] = []
for (let datasetLibds of this.matchedDatasets) {
const promise = this.fetchDataset(datasetLibds)
const promise = this.fetchDataset(datasetLibds)
datasetFetchingPromises.push(promise)
datasetFetchingPromises.push(promise)
}
Promise.allSettled(datasetFetchingPromises).then((res) => {
res.forEach((promise) => {
if (promise.status === 'fulfilled' && promise.value) datasets.push(promise.value)
if (promise.status === 'fulfilled' && promise.value)
datasets.push(promise.value)
})
this.uploadLoading = false
@ -126,28 +138,31 @@ export class MultiDatasetComponent implements OnInit {
const datasetObjects = this.buildDatasetsObjects(datasets)
for (let datasetObject of datasetObjects) {
this.spreadsheetService.parseExcelFile({
file: this.selectedFile!,
dcValidator: datasetObject.dcValidator!,
headerPks: datasetObject.headerPks,
headerArray: datasetObject.headerArray,
headerShow: [],
timeHeaders: datasetObject.timeHeaders,
dateHeaders: datasetObject.dateHeaders,
dateTimeHeaders: datasetObject.dateTimeHeaders,
xlRules: datasetObject.xlRules
}).then((parseResult: ParseResult | undefined) => {
console.log('parseResult', parseResult)
this.spreadsheetService
.parseExcelFile({
file: this.selectedFile!,
dcValidator: datasetObject.dcValidator!,
headerPks: datasetObject.headerPks,
headerArray: datasetObject.headerArray,
headerShow: [],
timeHeaders: datasetObject.timeHeaders,
dateHeaders: datasetObject.dateHeaders,
dateTimeHeaders: datasetObject.dateTimeHeaders,
xlRules: datasetObject.xlRules
})
.then((parseResult: ParseResult | undefined) => {
console.log('parseResult', parseResult)
if (parseResult && parseResult.data) {
this.parsedDatasets.push({
libds: datasetObject.libds,
parseResult: parseResult
})
}
}).catch((error: string) => {
this.eventService.showInfoModal('Error', error)
})
if (parseResult && parseResult.data) {
this.parsedDatasets.push({
libds: datasetObject.libds,
parseResult: parseResult
})
}
})
.catch((error: string) => {
this.eventService.showInfoModal('Error', error)
})
}
})
}
@ -158,14 +173,19 @@ export class MultiDatasetComponent implements OnInit {
this.matchedDatasets = []
inputDatasets.forEach((dataset: string) => {
const trimmedDataset = dataset.trim()
if (this.isValidDatasetFormat(trimmedDataset) && this.isValidDatasetReference(trimmedDataset) && !this.matchedDatasets.includes(trimmedDataset)) {
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.`)
console.warn(
`Sheet name: ${trimmedDataset} is not an actual dataset reference.`
)
}
})
@ -186,10 +206,15 @@ export class MultiDatasetComponent implements OnInit {
this.userInputDatasets = ''
sheetNames.forEach((sheetName: string, index: number) => {
if (this.isValidDatasetFormat(sheetName) && this.isValidDatasetReference(sheetName)) {
if (
this.isValidDatasetFormat(sheetName) &&
this.isValidDatasetReference(sheetName)
) {
this.matchedDatasets.push(sheetName)
} else {
console.warn(`Sheet name: ${sheetName} is not an actual dataset reference.`)
console.warn(
`Sheet name: ${sheetName} is not an actual dataset reference.`
)
}
})
}
@ -205,13 +230,15 @@ export class MultiDatasetComponent implements OnInit {
}
public get activeParsedDataset(): ParsedDataset | undefined {
return this.parsedDatasets.find(dataset => dataset.active)
return this.parsedDatasets.find((dataset) => dataset.active)
}
/**
* Fetches the table for given datasets params LIBRARY.TABLE
*/
async fetchDataset(libds: string): Promise<EditorsGetDataServiceResponse | undefined> {
* Fetches the table for given datasets params LIBRARY.TABLE
*/
async fetchDataset(
libds: string
): Promise<EditorsGetDataServiceResponse | undefined> {
let myParams: any = {
LIBDS: libds,
OUTDEST: 'WEB'
@ -235,39 +262,38 @@ export class MultiDatasetComponent implements OnInit {
private parseExcelSheetNames(): Promise<string[]> {
return new Promise((resolve, reject) => {
const reader = new FileReader();
const reader = new FileReader()
if (!this.selectedFile) {
console.warn('selectedFile is missing')
return resolve([])
}
reader.onload = (event: ProgressEvent<FileReader>) => {
if (!event?.target) {
console.warn('File reader event.target is missing')
return
if (!this.selectedFile) {
console.warn('selectedFile is missing')
return resolve([])
}
const data = event.target.result;
const workbook = XLSX.read(data, {
type: 'binary'
});
reader.onload = (event: ProgressEvent<FileReader>) => {
if (!event?.target) {
console.warn('File reader event.target is missing')
return
}
try {
const sheet_names_list = workbook.SheetNames;
const data = event.target.result
const workbook = XLSX.read(data, {
type: 'binary'
})
return resolve(sheet_names_list)
try {
const sheet_names_list = workbook.SheetNames
return resolve(sheet_names_list)
} catch (e) {
console.error(e)
}
}
catch (e) {
console.error(e)
reader.onerror = function (ex) {
console.log(ex)
}
};
reader.onerror = function (ex) {
console.log(ex);
};
reader.readAsBinaryString(this.selectedFile);
reader.readAsBinaryString(this.selectedFile)
})
}
@ -283,7 +309,7 @@ export class MultiDatasetComponent implements OnInit {
* example: LIB123.TABLE_123
*/
private isValidDatasetFormat(sheetName: string) {
const regex = /^\w{1,8}\.\w{1,32}$/gmi
const regex = /^\w{1,8}\.\w{1,32}$/gim
const correctFormat = regex.test(sheetName)
return correctFormat
@ -336,28 +362,40 @@ export class MultiDatasetComponent implements OnInit {
}
datasetObject.cols = response.data.cols
datasetObject.headerColumns = response.data.sasparams[0].COLHEADERS.split(',')
datasetObject.headerColumns =
response.data.sasparams[0].COLHEADERS.split(',')
datasetObject.headerPks = response.data.sasparams[0].PK.split(' ')
if (datasetObject.headerColumns.indexOf('_____DELETE__THIS__RECORD_____') !== -1) {
if (
datasetObject.headerColumns.indexOf(
'_____DELETE__THIS__RECORD_____'
) !== -1
) {
datasetObject.headerColumns[
datasetObject.headerColumns.indexOf('_____DELETE__THIS__RECORD_____')
datasetObject.headerColumns.indexOf(
'_____DELETE__THIS__RECORD_____'
)
] = 'Delete?'
}
datasetObject.headerArray = datasetObject.headerColumns.slice(1)
if (response.data.sasparams[0].DTVARS !== '') {
datasetObject.dateHeaders = response.data.sasparams[0].DTVARS.split(' ')
datasetObject.dateHeaders =
response.data.sasparams[0].DTVARS.split(' ')
}
if (response.data.sasparams[0].TMVARS !== '') {
datasetObject.timeHeaders = response.data.sasparams[0].TMVARS.split(' ')
datasetObject.timeHeaders =
response.data.sasparams[0].TMVARS.split(' ')
}
if (response.data.sasparams[0].DTTMVARS !== '') {
datasetObject.dateTimeHeaders = response.data.sasparams[0].DTTMVARS.split(' ')
datasetObject.dateTimeHeaders =
response.data.sasparams[0].DTTMVARS.split(' ')
}
if (response.data.xl_rules.length > 0) {
datasetObject.xlRules = this.helperService.deepClone(response.data.xl_rules)
datasetObject.xlRules = this.helperService.deepClone(
response.data.xl_rules
)
}
datasetObject.dcValidator = new DcValidator(
@ -368,7 +406,8 @@ export class MultiDatasetComponent implements OnInit {
response.data.dqdata
)
datasetObject.columnHeader = response.data.sasparams[0].COLHEADERS.split(',')
datasetObject.columnHeader =
response.data.sasparams[0].COLHEADERS.split(',')
datasetObjects.push(datasetObject)
}
@ -403,4 +442,4 @@ export interface ParsedDataset {
status?: 'success' | 'error'
active?: boolean
parseResult: ParseResult
}
}

View File

@ -1,18 +1,28 @@
import { Injectable } from '@angular/core';
import { Injectable } from '@angular/core'
import * as XLSX from '@sheet/crypto'
import { ExcelPasswordModalService, Result } from '../shared/excel-password-modal/excel-password-modal.service';
import { EventService } from './event.service';
import { isSpecialMissing } from '@sasjs/utils/input/validators';
import { dateFormat, dateToUtcTime, dateToTime } from '../editor/utils/date.utils';
import { excelDateToJSDate, getMissingHeaders } from '../editor/utils/grid.utils';
import { isStringNumber, isStringDecimal } from '../editor/utils/types.utils';
import SheetInfo from '../models/SheetInfo';
import { blobToFile } from '../xlmap/utils/file.utils';
import { ExcelRule } from '../models/TableData';
import { DcValidator } from '../shared/dc-validator/dc-validator';
import { LicenceService } from './licence.service';
import { FileUploadEncoding } from '../models/FileUploadEncoding';
import { FileUploader } from '../models/FileUploader.class';
import {
ExcelPasswordModalService,
Result
} from '../shared/excel-password-modal/excel-password-modal.service'
import { EventService } from './event.service'
import { isSpecialMissing } from '@sasjs/utils/input/validators'
import {
dateFormat,
dateToUtcTime,
dateToTime
} from '../editor/utils/date.utils'
import {
excelDateToJSDate,
getMissingHeaders
} from '../editor/utils/grid.utils'
import { isStringNumber, isStringDecimal } from '../editor/utils/types.utils'
import SheetInfo from '../models/SheetInfo'
import { blobToFile } from '../xlmap/utils/file.utils'
import { ExcelRule } from '../models/TableData'
import { DcValidator } from '../shared/dc-validator/dc-validator'
import { LicenceService } from './licence.service'
import { FileUploadEncoding } from '../models/FileUploadEncoding'
import { FileUploader } from '../models/FileUploader.class'
/**
* Used in combination with buffer
@ -26,7 +36,7 @@ const Buffer = require('buffer/').Buffer
type AOA = any[][]
export interface ParseParams {
file: File,
file: File
dcValidator: DcValidator
/**
* Parse function will manipulate and return the uploader array which can be provided with files already in the queue
@ -53,21 +63,20 @@ export interface ParseResult {
*/
headerShow?: string[]
rangeSheetRes?: SheetInfo
uploader: FileUploader,
uploader: FileUploader
}
@Injectable({
providedIn: 'root'
})
export class SpreadsheetService {
private licenceState = this.licenceService.licenceState
constructor(
private excelPasswordModalService: ExcelPasswordModalService,
private eventService: EventService,
private licenceService: LicenceService
) { }
) {}
/**
* Parses attached file and searches fo the matching data
@ -93,7 +102,8 @@ export class SpreadsheetService {
if (!parseParams.encoding) parseParams.encoding = 'UTF-8'
if (onParseStateChange) onParseStateChange(`Loading ${filename} into the browser`)
if (onParseStateChange)
onParseStateChange(`Loading ${filename} into the browser`)
let foundData = {
sheet: ''
@ -181,7 +191,10 @@ export class SpreadsheetService {
)
let csvArrayData: any[] = []
const rangeSheetRes: SheetInfo = this.getRangeAndSheet(wb, parseParams)
const rangeSheetRes: SheetInfo = this.getRangeAndSheet(
wb,
parseParams
)
missingHeaders = rangeSheetRes.missingHeaders
if (rangeSheetRes.foundData) {
@ -189,7 +202,10 @@ export class SpreadsheetService {
csvArrayHeadersMap = rangeSheetRes.csvArrayHeadersMap
const ws: XLSX.WorkSheet = wb.Sheets[rangeSheetRes.sheetName]
if (onParseStateChange) onParseStateChange(`Table found on sheet ${rangeSheetRes.sheetName} on row ${rangeSheetRes.startRow}`)
if (onParseStateChange)
onParseStateChange(
`Table found on sheet ${rangeSheetRes.sheetName} on row ${rangeSheetRes.startRow}`
)
let startAddress = ''
let endAddress = ''
@ -231,7 +247,10 @@ export class SpreadsheetService {
rangeSheetRes.rangeAddress = `${startAddress}:${endAddress}`
if (onTableFoundEvent) onTableFoundEvent(`Sheet: ${rangeSheetRes.sheetName}\nRange: ${rangeSheetRes.rangeAddress}`)
if (onTableFoundEvent)
onTableFoundEvent(
`Sheet: ${rangeSheetRes.sheetName}\nRange: ${rangeSheetRes.rangeAddress}`
)
} else {
missingHeaders = rangeSheetRes.missingHeaders
}
@ -266,11 +285,19 @@ export class SpreadsheetService {
parseParams.dateHeaders.length > 0 ||
parseParams.timeHeaders.length > 0
) {
csvArrayData = this.updateDateTimeCols(csvArrayHeaders, csvArrayData, parseParams)
csvArrayData = this.updateDateTimeCols(
csvArrayHeaders,
csvArrayData,
parseParams
)
}
if (parseParams.xlRules.length > 0) {
csvArrayData = this.updateXLRuleCols(csvArrayHeaders, csvArrayData, parseParams)
csvArrayData = this.updateXLRuleCols(
csvArrayHeaders,
csvArrayData,
parseParams
)
}
if (!isComplete) {
@ -334,7 +361,8 @@ export class SpreadsheetService {
const colRule = parseParams.dcValidator?.getRule(colName)
if (colRule?.type === 'numeric') {
if (isSpecialMissing(col) && !col.includes('.')) col = '.' + col
if (isSpecialMissing(col) && !col.includes('.'))
col = '.' + col
}
return col
@ -366,7 +394,10 @@ export class SpreadsheetService {
if (parseParams.encoding === 'WLATIN1') {
// WLATIN1
let encoded = iconv.decode(Buffer.from(csvContentClean), 'CP-1252')
let encoded = iconv.decode(
Buffer.from(csvContentClean),
'CP-1252'
)
blob = new Blob([encoded], { type: 'application/csv' })
} else {
// UTF-8
@ -378,7 +409,9 @@ export class SpreadsheetService {
}
if (data.length === 0) {
return reject(`Table in the file is empty. Data found on sheet: ${foundData.sheet}`)
return reject(
`Table in the file is empty. Data found on sheet: ${foundData.sheet}`
)
}
return resolve({
@ -392,7 +425,9 @@ export class SpreadsheetService {
} else if (fileType.toLowerCase() === 'csv') {
if (this.licenceState.value.submit_rows_limit !== Infinity) {
uploader.queue.pop()
return reject('Excel files only. To unlock CSV uploads, please contact support@datacontroller.io')
return reject(
'Excel files only. To unlock CSV uploads, please contact support@datacontroller.io'
)
}
if (parseParams.encoding === 'WLATIN1') {
@ -437,7 +472,10 @@ export class SpreadsheetService {
* @param wb Excel workbook
* @returns {object: SheetInfo} an object which contains necessary information about workbook that which sheet contains required data and what's the range
*/
private getRangeAndSheet(wb: XLSX.WorkBook, parseParams: ParseParams): SheetInfo {
private getRangeAndSheet(
wb: XLSX.WorkBook,
parseParams: ParseParams
): SheetInfo {
let data = []
let rangeStartRow: number = 0
@ -757,7 +795,11 @@ export class SpreadsheetService {
})
}
private updateDateTimeCols(headers: any, data: any, parseParams: ParseParams) {
private updateDateTimeCols(
headers: any,
data: any,
parseParams: ParseParams
) {
if (parseParams.dateHeaders.length > 0) {
let dateCols: number[] = []
parseParams.dateHeaders.forEach((element) => {

View File

@ -1,44 +1,38 @@
<ng-container *ngIf="options$ | async as options">
<clr-modal
[clrModalOpen]="options.open"
[clrModalSize]="'md'"
[clrModalClosable]="false"
>
<h3 class="modal-title center text-center color-darker-gray">
Password Protected File
</h3>
<div class="modal-body d-flex clr-justify-content-center">
<p class="m-0">Please enter password:</p>
<input
#filePasswordInput
data-lpignore="true"
autocomplete="off"
id="filePasswordInput"
type="text"
class="clr-input disable-password-manager"
/>
</div>
<div class="modal-footer">
<p *ngIf="options.error" class="m-0 color-red">
Sorry that didn't work, try again.
</p>
<button
type="button"
class="btn btn-sm btn-outline"
(click)="close()"
>
Cancel
</button>
<button
type="button"
class="btn btn-sm btn-success-outline"
[disabled]="filePasswordInput.value.length < 1"
(click)="
close(filePasswordInput.value)
"
>
Unlock
</button>
</div>
</clr-modal>
</ng-container>
[clrModalOpen]="options.open"
[clrModalSize]="'md'"
[clrModalClosable]="false"
>
<h3 class="modal-title center text-center color-darker-gray">
Password Protected File
</h3>
<div class="modal-body d-flex clr-justify-content-center">
<p class="m-0">Please enter password:</p>
<input
#filePasswordInput
data-lpignore="true"
autocomplete="off"
id="filePasswordInput"
type="text"
class="clr-input disable-password-manager"
/>
</div>
<div class="modal-footer">
<p *ngIf="options.error" class="m-0 color-red">
Sorry that didn't work, try again.
</p>
<button type="button" class="btn btn-sm btn-outline" (click)="close()">
Cancel
</button>
<button
type="button"
class="btn btn-sm btn-success-outline"
[disabled]="filePasswordInput.value.length < 1"
(click)="close(filePasswordInput.value)"
>
Unlock
</button>
</div>
</clr-modal>
</ng-container>

View File

@ -1,6 +1,9 @@
import { Component } from '@angular/core';
import { Observable } from 'rxjs';
import { ExcelPasswordModalService, Options } from './excel-password-modal.service';
import { Component } from '@angular/core'
import { Observable } from 'rxjs'
import {
ExcelPasswordModalService,
Options
} from './excel-password-modal.service'
@Component({
selector: 'app-excel-password-modal',
@ -8,15 +11,15 @@ import { ExcelPasswordModalService, Options } from './excel-password-modal.servi
styleUrl: './excel-password-modal.component.scss'
})
export class ExcelPasswordModalComponent {
options$: Observable<Options>;
options$: Observable<Options>
fileUnlockError: boolean = false
constructor(private excelPasswordModalService: ExcelPasswordModalService) {
this.options$ = this.excelPasswordModalService.optionsSubject$;
this.options$ = this.excelPasswordModalService.optionsSubject$
}
close(password?: string) {
this.excelPasswordModalService.close(password);
this.excelPasswordModalService.close(password)
}
}

View File

@ -1,5 +1,5 @@
import { Injectable } from '@angular/core';
import { Subject, Observable } from 'rxjs';
import { Injectable } from '@angular/core'
import { Subject, Observable } from 'rxjs'
export interface Options extends OpenOptions {
open: boolean
@ -20,28 +20,26 @@ export class ExcelPasswordModalService {
public optionsSubject$: Subject<Options> = new Subject()
public resultChange$: Subject<Result> = new Subject()
constructor() {
}
constructor() {}
public open(openOptions?: OpenOptions): Observable<Result> {
this.optionsSubject$.next({
open: true,
...openOptions
});
})
this.resultChange$ = new Subject<Result>();
return this.resultChange$.asObservable();
this.resultChange$ = new Subject<Result>()
return this.resultChange$.asObservable()
}
close(password?: string) {
this.optionsSubject$.next({
open: false
});
})
this.resultChange$.next({
password
});
this.resultChange$.complete();
})
this.resultChange$.complete()
}
}

View File

@ -124,10 +124,16 @@
routerLinkActive="active"
>Tables</a
>
<a clrVerticalNavLink routerLink="/home/excel-maps" routerLinkActive="active"
<a
clrVerticalNavLink
routerLink="/home/excel-maps"
routerLinkActive="active"
>Excel Maps</a
>
<a clrVerticalNavLink routerLink="/home/multi-load" routerLinkActive="active"
<a
clrVerticalNavLink
routerLink="/home/multi-load"
routerLinkActive="active"
>Multi Load</a
>
</clr-dropdown-menu>