A filter clause is shown in a panel above the grid. The panel was clamped to a single line - in the editor with an ellipsis, and in the viewer by letting the clause run the full width of the table - so a long or complex filter could not be read on screen. The panel is now collapsed to one line and carries a chevron that expands it to show the whole clause, wrapped, and collapses it again. The chevron is rendered only when the clause does not fit the collapsed line, so a short filter looks exactly as it did before. Whether it fits is measured from the DOM rather than guessed from the length of the text, since it depends on the rendered width; the measurement is deferred out of the change-detection cycle, and re-run when the clause, the available width, or the state changes. This replaces the editor's hover-only reveal with a real control, which is a button carrying aria-expanded and an accessible label, and gives the viewer the same affordance. Both panels share the styling, which was previously duplicated between the two component blocks. Tested with `client/cypress/e2e/filter-panel.cy.ts`, at a laptop-sized viewport, covering the three states: 1. no filter - the panel is not rendered 2. a short filter - shown in full, with no chevron 3. a long filter (an IN over every value of a free-text column, 646 characters) - collapsed with a chevron, expands to the whole clause over several lines, and collapses again 4. the editor panel behaves the same way for the same clause
998 lines
33 KiB
HTML
998 lines
33 KiB
HTML
<main class="content-area d-flex clr-flex-column">
|
|
<clr-modal
|
|
appFileDrop
|
|
(fileOver)="fileOverBase($event)"
|
|
[uploader]="uploader"
|
|
(fileDrop)="attachFile($event, true)"
|
|
[clrModalSize]="'xl'"
|
|
[clrModalStaticBackdrop]="false"
|
|
[clrModalClosable]="excelUploadState === 'Validating-DQ'"
|
|
[(clrModalOpen)]="showUploadModal"
|
|
class="relative"
|
|
>
|
|
<h3 class="modal-title">Upload File</h3>
|
|
<div class="modal-body">
|
|
<div class="drop-area">
|
|
<span>Drop file anywhere to upload!</span>
|
|
</div>
|
|
|
|
<div class="clr-col-md-12">
|
|
<!-- <div
|
|
class="card-header"
|
|
style="display: flex; flex-direction: column; justify-content: center"
|
|
>
|
|
<div
|
|
ng2FileDrop
|
|
*ngIf="!excelFileParsing"
|
|
[class.nv-file-over]="hasBaseDropZoneOver"
|
|
(fileOver)="fileOverBase($event)"
|
|
[uploader]="uploader"
|
|
class="well my-drop-zone"
|
|
(onFileDrop)="getFileDesc($event, true)"
|
|
>
|
|
<p class="file-drop-text">Drop File Here !</p>
|
|
</div>
|
|
</div> -->
|
|
<div class="clr-row card-block mt-15 d-flex justify-content-between">
|
|
<div class="clr-col-md-auto">
|
|
<div class="encoding-block">
|
|
<clr-radio-container class="mt-0" clrInline>
|
|
<clr-radio-wrapper>
|
|
<input
|
|
type="radio"
|
|
clrRadio
|
|
value="UTF-8"
|
|
name="encoding"
|
|
[(ngModel)]="encoding"
|
|
[disabled]="filename != ''"
|
|
/>
|
|
<label>UTF-8</label>
|
|
</clr-radio-wrapper>
|
|
<clr-radio-wrapper>
|
|
<input
|
|
type="radio"
|
|
clrRadio
|
|
value="WLATIN1"
|
|
name="encoding"
|
|
[(ngModel)]="encoding"
|
|
[disabled]="filename != ''"
|
|
/>
|
|
<label>WLATIN1</label>
|
|
</clr-radio-wrapper>
|
|
</clr-radio-container>
|
|
</div>
|
|
</div>
|
|
@if (!excelFileParsing && !uploadLoading) {
|
|
<div class="clr-col-md-3 filterBtn">
|
|
<span class="filterBtn w-100">
|
|
<label
|
|
for="file-upload"
|
|
class="btn btn-sm btn-outline profile-buttons w-100"
|
|
>
|
|
Browse
|
|
</label>
|
|
</span>
|
|
<input
|
|
hidden
|
|
#fileUploadInput
|
|
id="file-upload"
|
|
type="file"
|
|
appFileSelect
|
|
[uploader]="uploader"
|
|
(change)="attachFile($event)"
|
|
/>
|
|
</div>
|
|
}
|
|
|
|
@if (!excelFileParsing && !uploadLoading) {
|
|
<div class="clr-col-md-3 editBtn">
|
|
<button
|
|
[disabled]="true"
|
|
class="btnView btn btn-sm btn-success profile-buttons w-100"
|
|
(click)="uploadParsedFiles()"
|
|
>
|
|
Upload
|
|
</button>
|
|
</div>
|
|
}
|
|
|
|
<div class="clr-col-md-auto">
|
|
@if (excelUploadState !== 'Validating') {
|
|
<button
|
|
type="button"
|
|
class="btn btn-primary mr-0"
|
|
(click)="showUploadModal = false"
|
|
[disabled]="excelFileParsing || uploadLoading"
|
|
>
|
|
Close
|
|
</button>
|
|
}
|
|
@if (excelUploadState === 'Validating-DQ') {
|
|
<button
|
|
type="button"
|
|
class="btn btn-primary mr-0"
|
|
(click)="showUploadModal = false"
|
|
>
|
|
Skip
|
|
</button>
|
|
}
|
|
</div>
|
|
|
|
@if (excelFileParsing) {
|
|
<div class="excel-parsing clr-col-md-12">
|
|
<app-upload-stater #uploadStater></app-upload-stater>
|
|
</div>
|
|
}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<!-- <button type="button" class="btn btn-outline" (click)="showUploadModal = false">Cancel</button> -->
|
|
<!-- <button type="button" class="btn btn-primary" (click)="showUploadModal = false">Close</button> -->
|
|
</div>
|
|
</clr-modal>
|
|
|
|
@if (currentEditRecordIndex > -1) {
|
|
<app-edit-record
|
|
[currentRecord]="currentEditRecord"
|
|
[recordAction]="recordAction"
|
|
[libds]="libds"
|
|
[queryFilter]="queryFilter"
|
|
[filter]="filter"
|
|
[submitLoading]="submitLoading"
|
|
[headerPks]="headerPks"
|
|
[cellValidation]="cellValidation"
|
|
[currentRecordIndex]="currentEditRecordIndex"
|
|
[currentRecordLoadings]="currentEditRecordLoadings"
|
|
[currentRecordErrors]="currentEditRecordErrors"
|
|
[currentRecordValidator]="currentEditRecordValidator"
|
|
(onRecordChange)="confirmRecordEdit()"
|
|
(onRecordInputFocused)="onRecordInputFocus($event)"
|
|
(onRecordEditClose)="closeRecordEdit()"
|
|
(onNextRecord)="onNextRecord()"
|
|
(onPreviousRecord)="onPreviousRecord()"
|
|
></app-edit-record>
|
|
}
|
|
|
|
<div #ht class="clr-flex-1">
|
|
@if (!validationDone && submit) {
|
|
<div class="progresStatic progress loop">
|
|
<progress></progress>
|
|
</div>
|
|
}
|
|
|
|
<div
|
|
appDragNdrop
|
|
(fileDraggedOver)="onShowUploadModal()"
|
|
class="card border-0 box-shadow-none-i position-relative h-100 d-flex clr-flex-column background-transparent"
|
|
>
|
|
<div
|
|
class="card-header clr-row buttonBar headerBar clr-flex-md-row clr-justify-content-center clr-justify-content-lg-end"
|
|
>
|
|
@if (tableTrue && !embed) {
|
|
<div class="clr-col-12 clr-col-md-auto clr-col-lg-auto backBtn">
|
|
<span
|
|
class="btn icon-collapse btn-sm btn-icon btn-dimmed"
|
|
[routerLink]="['/home']"
|
|
>
|
|
<clr-icon
|
|
aria-hidden="true"
|
|
shape="caret"
|
|
dir="left"
|
|
size="20"
|
|
></clr-icon>
|
|
<span class="text">Back to table selection</span>
|
|
</span>
|
|
<span
|
|
(click)="viewboxManager()"
|
|
class="btn icon-collapse btn-sm btn-icon btn-dimmed viewbox-open"
|
|
>
|
|
<clr-icon
|
|
aria-hidden="true"
|
|
shape="view-cards"
|
|
size="20"
|
|
></clr-icon>
|
|
<span class="text">Viewboxes</span>
|
|
</span>
|
|
</div>
|
|
}
|
|
|
|
<div
|
|
class="clr-col-12 clr-col-md clr-col-lg d-flex flex-column align-items-center"
|
|
[class.clr-col-lg-12]="!tableTrue"
|
|
>
|
|
<h4
|
|
*ngVar="{
|
|
libName: (libds?.split('.'))![0],
|
|
tableName: (libds?.split('.'))![1]
|
|
} as libdsParsed"
|
|
class="editor-title text-center mt-0"
|
|
>
|
|
<clr-tooltip>
|
|
<clr-icon
|
|
clrTooltipTrigger
|
|
(click)="datasetInfo = true"
|
|
shape="info-circle"
|
|
aria-label="View dataset meta info"
|
|
class="is-highlight cursor-pointer"
|
|
size="24"
|
|
></clr-icon>
|
|
|
|
@if (libdsParsed.tableName.includes('-FC')) {
|
|
<clr-icon
|
|
aria-hidden="true"
|
|
shape="bolt"
|
|
class="color-yellow"
|
|
></clr-icon>
|
|
}
|
|
|
|
<span clrTooltipTrigger>
|
|
{{ libdsParsed.libName }}.<a
|
|
class="mr-10 view-table"
|
|
[routerLink]="'/view/data/' + libds!"
|
|
>{{ libdsParsed.tableName.replace('-FC', '') }}</a
|
|
>
|
|
</span>
|
|
|
|
@if (this.dsNote && this.dsNote.length > 0) {
|
|
<clr-tooltip-content
|
|
clrPosition="bottom-left"
|
|
clrSize="lg"
|
|
*clrIfOpen
|
|
>
|
|
{{ this.dsNote }}
|
|
</clr-tooltip-content>
|
|
}
|
|
</clr-tooltip>
|
|
|
|
@if (dataSource) {
|
|
@if (!zeroFilterRows) {
|
|
({{ dataSource.length | thousandSeparator: ',' }}
|
|
{{ dataSource.length === 1 ? 'row' : 'rows' }}, {{ cols.length
|
|
}}{{ cols.length === 1 ? ' col' : ' cols' }})
|
|
}
|
|
@if (zeroFilterRows) {
|
|
(0 rows)
|
|
}
|
|
}
|
|
</h4>
|
|
</div>
|
|
@if (tableTrue) {
|
|
<div class="clr-col-12 clr-col-md-auto clr-col-lg-auto btnCtrl">
|
|
@if (hotTable.readOnly && !uploadPreview) {
|
|
@if (!isVaEmbed) {
|
|
<button
|
|
type="button"
|
|
class="btnView btn icon-collapse btn-sm btn-icon btn-dimmed"
|
|
(click)="openQb()"
|
|
>
|
|
<clr-icon aria-hidden="true" shape="filter"></clr-icon>
|
|
<span class="text">Filter</span>
|
|
</button>
|
|
}
|
|
<button
|
|
type="button"
|
|
class="btn icon-collapse btn-sm btn-primary"
|
|
(click)="editTable()"
|
|
>
|
|
<clr-icon aria-hidden="true" shape="note"></clr-icon>
|
|
<span class="text">Edit</span>
|
|
</button>
|
|
@if (!columnLevelSecurityFlag) {
|
|
<button
|
|
(click)="onShowUploadModal()"
|
|
type="button"
|
|
class="btn icon-collapse btn-sm btn-success mr-0"
|
|
>
|
|
<clr-icon aria-hidden="true" shape="upload"></clr-icon>
|
|
<span class="text">Upload</span>
|
|
</button>
|
|
}
|
|
}
|
|
@if (!hotTable.readOnly && !uploadPreview) {
|
|
<button
|
|
type="button"
|
|
class="btn btn-sm btn-icon btn-outline-danger"
|
|
(click)="cancelEdit()"
|
|
>
|
|
<clr-icon aria-hidden="true" shape="times"></clr-icon>
|
|
<span>Cancel</span>
|
|
</button>
|
|
<clr-tooltip>
|
|
<button
|
|
clrTooltipTrigger
|
|
type="button"
|
|
class="btn btn-sm btn-success"
|
|
[clrLoading]="addingNewRow"
|
|
[class.dc-locked-control]="restrictions.restrictAddRow"
|
|
(click)="!restrictions.restrictAddRow ? addRow() : ''"
|
|
>
|
|
<clr-icon aria-hidden="true" shape="plus" size="16"></clr-icon
|
|
>Add Row
|
|
</button>
|
|
<clr-tooltip-content
|
|
clrPosition="bottom-left"
|
|
clrSize="lg"
|
|
*clrIfOpen
|
|
>
|
|
@if (restrictions.restrictAddRow) {
|
|
<span
|
|
>To unlock more than
|
|
{{ licenceState.value.editor_rows_allowed }}
|
|
{{
|
|
licenceState.value.editor_rows_allowed === 1
|
|
? 'row'
|
|
: 'rows'
|
|
}}, contact support@datacontroller.io</span
|
|
>
|
|
}
|
|
</clr-tooltip-content>
|
|
</clr-tooltip>
|
|
<button
|
|
type="button"
|
|
class="btn btn-sm btn-primary"
|
|
(click)="checkSave()"
|
|
>
|
|
<clr-icon aria-hidden="true" shape="check" size="20"></clr-icon
|
|
>Submit
|
|
</button>
|
|
}
|
|
@if (uploadPreview) {
|
|
<button
|
|
type="button"
|
|
class="btn btn-sm btn-icon btn-outline-danger btn-upload-preview"
|
|
(click)="discardSourceFile = true"
|
|
>
|
|
<clr-icon aria-hidden="true" shape="times"></clr-icon>
|
|
<span>Discard file</span>
|
|
</button>
|
|
@if (hotTable.readOnly) {
|
|
<button
|
|
type="button"
|
|
class="btn btn-sm btn-primary btn-upload-preview"
|
|
(click)="manualFileEditModal = true"
|
|
>
|
|
<clr-icon aria-hidden="true" shape="note"></clr-icon>
|
|
<span>Edit</span>
|
|
</button>
|
|
}
|
|
<button
|
|
type="button"
|
|
class="btn btn-sm btn-success preview-submit"
|
|
(click)="submitExcel()"
|
|
[clrLoading]="uploadLoading"
|
|
>
|
|
<clr-icon aria-hidden="true" shape="check" size="20"></clr-icon>
|
|
Submit
|
|
</button>
|
|
}
|
|
</div>
|
|
}
|
|
@if (!['', ' '].includes(queryText)) {
|
|
<div
|
|
#infoBar
|
|
class="clr-col-md-12 infoBar"
|
|
[class.expanded]="filterExpanded"
|
|
>
|
|
<span class="infoBar-text"
|
|
>FILTER :
|
|
<b>{{ queryText }}</b>
|
|
</span>
|
|
@if (filterOverflows) {
|
|
<button
|
|
type="button"
|
|
class="infoBar-toggle"
|
|
[attr.aria-expanded]="filterExpanded"
|
|
[attr.aria-label]="
|
|
filterExpanded
|
|
? 'Collapse the filter clause'
|
|
: 'Expand the filter clause'
|
|
"
|
|
(click)="toggleFilterPanel()"
|
|
>
|
|
<clr-icon aria-hidden="true" shape="caret down"></clr-icon>
|
|
</button>
|
|
}
|
|
</div>
|
|
}
|
|
</div>
|
|
<div
|
|
class="card-block p-0 clr-flex-1 background-transparent"
|
|
[ngClass]="{ headerBarFilter: !['', ' '].includes(queryText) }"
|
|
>
|
|
@if (!tableTrue) {
|
|
<div class="card-text">
|
|
<div
|
|
class="h-70vh d-flex justify-content-center flex-column align-items-center"
|
|
>
|
|
@if (!getdataError) {
|
|
<span class="spinner"> Loading... </span>
|
|
<div class="mt-10">
|
|
<p cds-text="section">Loading table</p>
|
|
</div>
|
|
}
|
|
@if (getdataError) {
|
|
<span>
|
|
<clr-icon
|
|
aria-hidden="true"
|
|
shape="error-standard"
|
|
class="error-icon"
|
|
></clr-icon>
|
|
</span>
|
|
<div class="mt-10">
|
|
<p cds-text="section">Loading table error</p>
|
|
</div>
|
|
}
|
|
</div>
|
|
</div>
|
|
}
|
|
|
|
<div
|
|
class="card-text hotEditor h-100 d-flex clr-flex-column background-transparent"
|
|
>
|
|
<!-- <div *ngIf="generateEditRecordUrlLoading" class="edit-record-spinner">
|
|
<span class="spinner">
|
|
Loading...
|
|
</span>
|
|
</div> -->
|
|
<!--{{ submit }}-->
|
|
|
|
<!--<hr>-->
|
|
<!--{{ validationDone }}-->
|
|
|
|
<div class="hot-wrapper clr-flex-1">
|
|
<hot-table
|
|
#hotInstance
|
|
id="hotTable"
|
|
class="edit-hot"
|
|
[class.hidden]="hotTable.hidden"
|
|
[data]="hotTable.data"
|
|
[settings]="hotTableSettings"
|
|
>
|
|
</hot-table>
|
|
</div>
|
|
|
|
<div>
|
|
@if (
|
|
tableTrue && !restrictions.removeAddRecordButton && !isVaEmbed
|
|
) {
|
|
<clr-tooltip>
|
|
<button
|
|
clrTooltipTrigger
|
|
type="button"
|
|
class="btn btn-sm btn-success mt-5-i"
|
|
[class.dc-locked-control]="restrictions.restrictAddRow"
|
|
(click)="
|
|
!restrictions.restrictAddRow ? addRecordButtonClick() : ''
|
|
"
|
|
>
|
|
<clr-icon
|
|
aria-hidden="true"
|
|
shape="plus"
|
|
size="16"
|
|
></clr-icon>
|
|
Add Record
|
|
</button>
|
|
<clr-tooltip-content
|
|
clrPosition="top-right"
|
|
clrSize="lg"
|
|
*clrIfOpen
|
|
>
|
|
@if (restrictions.restrictAddRow) {
|
|
<span
|
|
>To unlock more than
|
|
{{ licenceState.value.editor_rows_allowed }}
|
|
{{
|
|
licenceState.value.editor_rows_allowed === 1
|
|
? 'row'
|
|
: 'rows'
|
|
}}, contact support@datacontroller.io</span
|
|
>
|
|
}
|
|
</clr-tooltip-content>
|
|
</clr-tooltip>
|
|
}
|
|
|
|
<!-- VA data-driven content mode: filter controls only. Edit/Submit
|
|
use the normal top toolbar (same as embed=true). VA opens
|
|
read-only and filters live with the report; filtering
|
|
repopulates the grid, so it is held while editing (change shown
|
|
pending) and resumes on return to read-only. -->
|
|
@if (isVaEmbed) {
|
|
<div class="va-filter-controls w-100 mt-2-i">
|
|
<!-- Read-only: auto-apply toggle + manual Apply (confirm mode).
|
|
Hidden while editing (filtering is held). -->
|
|
@if (hotTable.readOnly) {
|
|
<label class="va-toggle">
|
|
<input
|
|
type="checkbox"
|
|
[checked]="vaAutoApply"
|
|
(change)="toggleVaAutoApply()"
|
|
/>
|
|
Auto-apply VA filters
|
|
</label>
|
|
@if (!vaAutoApply) {
|
|
<button
|
|
[disabled]="
|
|
vaFilterStatus !== 'pending' &&
|
|
vaFilterStatus !== 'loading'
|
|
"
|
|
type="button"
|
|
class="btn btn-sm btn-primary"
|
|
(click)="applyPendingVaFilter()"
|
|
>
|
|
Apply filter
|
|
</button>
|
|
}
|
|
}
|
|
<!-- Edit mode: filtering is held so edits aren't wiped. -->
|
|
@if (!hotTable.readOnly) {
|
|
<span class="va-disabled-text">
|
|
VA filtering paused while editing
|
|
</span>
|
|
}
|
|
<!-- Persistent live region (shown in both modes) so a filter
|
|
change that arrives while editing is announced as pending. -->
|
|
<span class="va-filter-status" role="status" aria-live="polite">
|
|
@if (vaFilterStatus === 'pending') {
|
|
<span class="va-pending-text"> Filter change pending </span>
|
|
}
|
|
@if (vaFilterStatus === 'loading') {
|
|
<span class="va-loading-text"> Loading filter… </span>
|
|
}
|
|
</span>
|
|
</div>
|
|
}
|
|
|
|
@if (
|
|
licenceState.value.editor_rows_allowed !== Infinity &&
|
|
dataSource &&
|
|
dataSource.length > licenceState.value.editor_rows_allowed
|
|
) {
|
|
<p class="mt-2-i w-100 text-center">
|
|
To display more than
|
|
{{ licenceState.value.editor_rows_allowed }} rows, contact
|
|
<contact-link />
|
|
</p>
|
|
}
|
|
</div>
|
|
<!--<hr>
|
|
<hot-table hotId="hotInstance" id="hotTable">
|
|
</hot-table>-->
|
|
@if (submit) {
|
|
<div class="modal">
|
|
<!-- && validationDone should be in constraint, but validation is not correct on front end so it is comment -->
|
|
<!-- TODO: fix validation in line above -->
|
|
<div
|
|
class="modal-dialog modal-lg"
|
|
role="dialog"
|
|
aria-hidden="true"
|
|
>
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<button aria-label="Close" class="close" type="button">
|
|
<clr-icon aria-hidden="true" shape="close"></clr-icon>
|
|
</button>
|
|
<h3 class="modal-title">
|
|
Submit for Approval ({{ rowsChanged.rowsAdded }} added,
|
|
{{ rowsChanged.rowsUpdated }} updated,
|
|
{{ rowsChanged.rowsDeleted }} deleted)
|
|
</h3>
|
|
</div>
|
|
<div class="modal-body">
|
|
@if (
|
|
totalRowsChanged > licenceState.value.submit_rows_limit &&
|
|
licenceState.value.submit_rows_limit !== Infinity
|
|
) {
|
|
<span class="d-block w-100 text-center color-red"
|
|
>Only
|
|
{{ licenceState.value.submit_rows_limit }}
|
|
{{
|
|
licenceState.value.submit_rows_limit === 1
|
|
? 'row'
|
|
: 'rows'
|
|
}}
|
|
will be submitted. To remove the restriction, contact
|
|
support@datacontroller.io</span
|
|
>
|
|
}
|
|
@if (tableTrue) {
|
|
<div class="clr-offset-md-2 clr-col-md-8">
|
|
<div class="text-area-full-width">
|
|
<label for="formFields_8" class="mb-5 d-block"
|
|
>Message</label
|
|
>
|
|
<textarea
|
|
clrTextarea
|
|
[(ngModel)]="message"
|
|
[disabled]="!validationDone"
|
|
tabindex="0"
|
|
[value]="
|
|
!validationDone
|
|
? 'Please wait while we validate ' +
|
|
cols.length * modifedRowsIndexes.length +
|
|
' cells.'
|
|
: ''
|
|
"
|
|
class="submit-reason"
|
|
type="text"
|
|
id="formFields_8"
|
|
></textarea>
|
|
</div>
|
|
<!-- TODO:approvers list -->
|
|
<!-- <div>
|
|
<div class="col-md-12" style="max-height: 150px;overflow: auto;">
|
|
<ul class="list">
|
|
<li *ngFor="let approve of approvers">{{approve}}</li>
|
|
</ul>
|
|
</div>
|
|
</div> -->
|
|
</div>
|
|
}
|
|
</div>
|
|
<div class="modal-footer justify-content-center-i">
|
|
<button
|
|
id="submitBtn"
|
|
[disabled]="!validationDone"
|
|
type="submit"
|
|
class="btn btn-sm btn-success-outline m-0"
|
|
tabindex="0"
|
|
(click)="saveTable(hotTable.data)"
|
|
>
|
|
Submit
|
|
</button>
|
|
<button
|
|
id="cancelSubmitBtn"
|
|
type="button"
|
|
class="btn btn-sm btn-outline"
|
|
tabindex="0"
|
|
(click)="
|
|
cancelSubmit(); submit = false; validationDone = 0
|
|
"
|
|
>
|
|
Cancel
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
}
|
|
|
|
<clr-modal [(clrModalOpen)]="submitLimitNotice">
|
|
<h3 class="modal-title">Notice</h3>
|
|
<div class="modal-body">
|
|
<p class="m-0">
|
|
Due to current licence, only
|
|
{{ licenceState.value.submit_rows_limit }} rows in a file will
|
|
be submitted. To remove the restriction, contact
|
|
support@datacontroller.io
|
|
</p>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button
|
|
type="button"
|
|
class="btn btn-sm btn-primary"
|
|
(click)="submitLimitNotice = false"
|
|
>
|
|
Cancel
|
|
</button>
|
|
<button
|
|
type="button"
|
|
class="btn btn-sm btn-primary"
|
|
(click)="uploadParsedFiles(); submitLimitNotice = false"
|
|
>
|
|
Submit
|
|
</button>
|
|
</div>
|
|
</clr-modal>
|
|
<clr-modal [(clrModalOpen)]="badEdit">
|
|
<h3 class="modal-title">{{ badEditTitle || 'Error' }}</h3>
|
|
<div class="modal-body">
|
|
<p>{{ badEditCause }}</p>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button
|
|
type="button"
|
|
class="btn btn-sm btn-primary"
|
|
(click)="badEdit = false"
|
|
>
|
|
Ok
|
|
</button>
|
|
</div>
|
|
</clr-modal>
|
|
<clr-modal [(clrModalOpen)]="pkNull">
|
|
<h3 class="modal-title">Error</h3>
|
|
<div class="modal-body">
|
|
<p>
|
|
Null values or incorrect data types cannot be submitted for
|
|
Primary Key columns
|
|
</p>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button
|
|
type="button"
|
|
class="btn btn-primary"
|
|
(click)="pkNull = false; badEdit = false"
|
|
>
|
|
Ok
|
|
</button>
|
|
</div>
|
|
</clr-modal>
|
|
<clr-modal [(clrModalOpen)]="noPkNull">
|
|
<h3 class="modal-title">Error</h3>
|
|
<div class="modal-body">
|
|
<p>Incorrect data types in non-Primary Key columns</p>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button
|
|
type="button"
|
|
class="btn btn-primary"
|
|
(click)="noPkNull = false; badEdit = false"
|
|
>
|
|
Ok
|
|
</button>
|
|
</div>
|
|
</clr-modal>
|
|
<clr-modal class="duplicate-keys-modal" [(clrModalOpen)]="pkDups">
|
|
<h3 class="modal-title">Error</h3>
|
|
<div class="modal-body">
|
|
<p>
|
|
Duplicate values cannot be submitted for Primary Key columns
|
|
</p>
|
|
<p>Duplicates found:</p>
|
|
@for (
|
|
duplicatePkIndex of duplicatePkIndexes;
|
|
track duplicatePkIndex;
|
|
let i = $index
|
|
) {
|
|
<span class="d-block">
|
|
On row {{ duplicatePkIndex + 1 }}:
|
|
{{
|
|
pkFields[duplicatePkIndex].replace('|', '')
|
|
| pkSpaceSeparate
|
|
}}
|
|
</span>
|
|
}
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button
|
|
type="button"
|
|
class="btn btn-primary"
|
|
(click)="pkDups = false"
|
|
>
|
|
Ok
|
|
</button>
|
|
</div>
|
|
</clr-modal>
|
|
<clr-modal [(clrModalOpen)]="tooLong">
|
|
<h3 class="modal-title">Error</h3>
|
|
<div class="modal-body">
|
|
<p>
|
|
The following columns have values that exceed length limits:
|
|
</p>
|
|
@for (cell of exceedCells; track cell) {
|
|
<p>{{ cell.col }}({{ cell.len }}), {{ cell.val }}</p>
|
|
}
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button
|
|
type="button"
|
|
class="btn btn-primary"
|
|
(click)="tooLong = false; cleanExceed()"
|
|
>
|
|
Ok
|
|
</button>
|
|
</div>
|
|
</clr-modal>
|
|
</div>
|
|
</div>
|
|
<!-- <div class="card-footer" style="display:flex; justify-content: center;">
|
|
</div> -->
|
|
</div>
|
|
</div>
|
|
</main>
|
|
|
|
@if (nullVariables) {
|
|
<div class="modal z-index-highest">
|
|
<div class="modal-dialog" role="dialog" aria-hidden="true">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<button aria-label="Close" class="close" type="button">
|
|
<clr-icon aria-hidden="true" shape="close"></clr-icon>
|
|
</button>
|
|
<h3 class="modal-title">Error</h3>
|
|
</div>
|
|
<div class="modal-body">
|
|
<p>You cannot submit empty clauses</p>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button
|
|
class="btn btn-sm btn-primary"
|
|
(click)="nullVariables = false"
|
|
type="button"
|
|
>
|
|
Ok
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
}
|
|
|
|
<clr-modal
|
|
[(clrModalOpen)]="filter"
|
|
[clrModalSize]="'xl'"
|
|
[clrModalClosable]="false"
|
|
class="filter-modal"
|
|
>
|
|
<h3 class="modal-title center text-center color-darker-gray">
|
|
Filter for table:
|
|
<span> {{ libds }} </span>
|
|
</h3>
|
|
<div class="modal-body">
|
|
@if (filter) {
|
|
<app-query #queryFilter></app-query>
|
|
}
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button (click)="resetFilter()" type="button" class="btn btn-sm btn-link">
|
|
reset filter
|
|
</button>
|
|
|
|
<button
|
|
type="button"
|
|
class="btn btn-sm btn-outline"
|
|
(click)="filter = false"
|
|
>
|
|
Cancel
|
|
</button>
|
|
<button
|
|
[clrLoading]="submitLoading"
|
|
type="button"
|
|
class="btn btn-sm btn-success-outline"
|
|
(click)="sendClause()"
|
|
>
|
|
Ok
|
|
</button>
|
|
</div>
|
|
</clr-modal>
|
|
|
|
<clr-modal [(clrModalOpen)]="queryErr">
|
|
<h3 class="modal-title">Error</h3>
|
|
<div class="modal-body">
|
|
<p>{{ queryErrMessage }}</p>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-outline" (click)="queryErr = false">
|
|
Cancel
|
|
</button>
|
|
<button type="button" class="btn btn-primary" (click)="queryErr = false">
|
|
Ok
|
|
</button>
|
|
</div>
|
|
</clr-modal>
|
|
|
|
<clr-modal
|
|
[(clrModalOpen)]="discardSourceFile"
|
|
[clrModalSize]="'xl'"
|
|
[clrModalClosable]="false"
|
|
>
|
|
<h3 class="modal-title center text-center color-darker-gray">Warning</h3>
|
|
<div class="modal-body">
|
|
This action will discard the source file and it's loaded data
|
|
<b>{{ filename }}</b> Do you wish to proceed?
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button
|
|
type="button"
|
|
class="btn btn-sm btn-outline"
|
|
(click)="discardSourceFile = false"
|
|
>
|
|
Cancel
|
|
</button>
|
|
<button
|
|
type="button"
|
|
class="btn btn-sm btn-success-outline"
|
|
(click)="discardSourceFile = false; discardPendingExcel(true)"
|
|
>
|
|
Ok
|
|
</button>
|
|
</div>
|
|
</clr-modal>
|
|
|
|
<clr-modal
|
|
[(clrModalOpen)]="filePasswordModal"
|
|
[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">
|
|
@if (fileUnlockError) {
|
|
<p class="m-0 color-red">Sorry that didn't work, try again.</p>
|
|
}
|
|
<button
|
|
type="button"
|
|
class="btn btn-sm btn-outline"
|
|
(click)="filePasswordModal = false; filePasswordSubject.next(undefined)"
|
|
>
|
|
Cancel upload
|
|
</button>
|
|
<button
|
|
type="button"
|
|
class="btn btn-sm btn-success-outline"
|
|
[disabled]="filePasswordInput.value.length < 1"
|
|
(click)="
|
|
filePasswordModal = false;
|
|
filePasswordSubject.next(filePasswordInput.value)
|
|
"
|
|
>
|
|
Unlock
|
|
</button>
|
|
</div>
|
|
</clr-modal>
|
|
|
|
<clr-modal
|
|
[(clrModalOpen)]="manualFileEditModal"
|
|
[clrModalSize]="'xl'"
|
|
[clrModalClosable]="false"
|
|
>
|
|
<h3 class="modal-title center text-center color-darker-gray">Warning</h3>
|
|
<div class="modal-body">
|
|
Manually editing this data will mean the source file
|
|
<b>{{ filename }}</b> will be discarded. Do you wish to proceed?
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button
|
|
type="button"
|
|
class="btn btn-sm btn-outline"
|
|
(click)="manualFileEditModal = false"
|
|
>
|
|
Cancel
|
|
</button>
|
|
<button
|
|
type="button"
|
|
class="btn btn-sm btn-success-outline"
|
|
(click)="manualFileEditModal = false; previewTableEditConfirm()"
|
|
>
|
|
Ok
|
|
</button>
|
|
</div>
|
|
</clr-modal>
|
|
|
|
<app-dataset-info
|
|
[(open)]="datasetInfo"
|
|
[dsmeta]="dsmeta"
|
|
[versions]="versions"
|
|
(rowClicked)="datasetInfoModalRowClicked($event)"
|
|
>
|
|
</app-dataset-info>
|
|
|
|
<app-viewboxes [(viewboxModal)]="viewboxes"></app-viewboxes>
|
|
|
|
<app-confirm-modal
|
|
[open]="confirmModal.open"
|
|
[title]="confirmModal.title"
|
|
[message]="confirmModal.message"
|
|
(result)="onConfirmModalResult($event)"
|
|
></app-confirm-modal>
|
|
|
|
<app-bulk-validation-modal
|
|
[open]="bulkValidation.active"
|
|
[done]="bulkValidation.done"
|
|
[total]="bulkValidation.total"
|
|
(cancel)="cancelBulkValidation({ revert: true })"
|
|
></app-bulk-validation-modal>
|