@sd-angular/core 19.0.31 → 19.0.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/data-state/index.d.ts +1 -0
- package/components/data-state/src/data-state.component.d.ts +41 -0
- package/components/import-excel/src/import-excel.component.d.ts +23 -1
- package/components/index.d.ts +1 -0
- package/components/quick-action/src/quick-action.component.d.ts +3 -1
- package/components/table/src/components/selector-action/selector-action.component.d.ts +11 -0
- package/components/table/src/table.component.d.ts +10 -1
- package/components/upload-file/src/upload-file.component.d.ts +5 -1
- package/fesm2022/sd-angular-core-components-button.mjs +2 -2
- package/fesm2022/sd-angular-core-components-button.mjs.map +1 -1
- package/fesm2022/sd-angular-core-components-data-state.mjs +72 -0
- package/fesm2022/sd-angular-core-components-data-state.mjs.map +1 -0
- package/fesm2022/sd-angular-core-components-form-generic.mjs +14 -14
- package/fesm2022/sd-angular-core-components-form-generic.mjs.map +1 -1
- package/fesm2022/sd-angular-core-components-import-excel.mjs +67 -18
- package/fesm2022/sd-angular-core-components-import-excel.mjs.map +1 -1
- package/fesm2022/sd-angular-core-components-inform.mjs +6 -6
- package/fesm2022/sd-angular-core-components-inform.mjs.map +1 -1
- package/fesm2022/sd-angular-core-components-query-bar.mjs +3 -3
- package/fesm2022/sd-angular-core-components-query-bar.mjs.map +1 -1
- package/fesm2022/sd-angular-core-components-query-builder.mjs +1 -1
- package/fesm2022/sd-angular-core-components-query-builder.mjs.map +1 -1
- package/fesm2022/sd-angular-core-components-quick-action.mjs +4 -2
- package/fesm2022/sd-angular-core-components-quick-action.mjs.map +1 -1
- package/fesm2022/sd-angular-core-components-stepper.mjs +2 -2
- package/fesm2022/sd-angular-core-components-stepper.mjs.map +1 -1
- package/fesm2022/sd-angular-core-components-table.mjs +119 -26
- package/fesm2022/sd-angular-core-components-table.mjs.map +1 -1
- package/fesm2022/sd-angular-core-components-tree.mjs +1 -1
- package/fesm2022/sd-angular-core-components-tree.mjs.map +1 -1
- package/fesm2022/sd-angular-core-components-upload-file.mjs +20 -6
- package/fesm2022/sd-angular-core-components-upload-file.mjs.map +1 -1
- package/fesm2022/sd-angular-core-components.mjs +1 -0
- package/fesm2022/sd-angular-core-components.mjs.map +1 -1
- package/fesm2022/sd-angular-core-forms-autocomplete.mjs +247 -59
- package/fesm2022/sd-angular-core-forms-autocomplete.mjs.map +1 -1
- package/fesm2022/sd-angular-core-forms-date-range.mjs +2 -2
- package/fesm2022/sd-angular-core-forms-date-range.mjs.map +1 -1
- package/fesm2022/sd-angular-core-forms-date.mjs +2 -2
- package/fesm2022/sd-angular-core-forms-date.mjs.map +1 -1
- package/fesm2022/sd-angular-core-forms-datetime.mjs +2 -2
- package/fesm2022/sd-angular-core-forms-datetime.mjs.map +1 -1
- package/fesm2022/sd-angular-core-forms-select.mjs +290 -67
- package/fesm2022/sd-angular-core-forms-select.mjs.map +1 -1
- package/fesm2022/sd-angular-core-i18n.mjs +155 -0
- package/fesm2022/sd-angular-core-i18n.mjs.map +1 -1
- package/fesm2022/sd-angular-core-modules-layout.mjs +6 -6
- package/fesm2022/sd-angular-core-modules-layout.mjs.map +1 -1
- package/fesm2022/sd-angular-core-services-confirm.mjs +43 -7
- package/fesm2022/sd-angular-core-services-confirm.mjs.map +1 -1
- package/fesm2022/sd-angular-core-services-notify.mjs +2 -2
- package/fesm2022/sd-angular-core-services-notify.mjs.map +1 -1
- package/fesm2022/sd-angular-core-utilities-read-state.mjs +87 -0
- package/fesm2022/sd-angular-core-utilities-read-state.mjs.map +1 -0
- package/fesm2022/sd-angular-core-utilities.mjs +1 -0
- package/fesm2022/sd-angular-core-utilities.mjs.map +1 -1
- package/forms/autocomplete/src/autocomplete.component.d.ts +13 -2
- package/forms/select/src/select.component.d.ts +11 -1
- package/i18n/src/en.d.ts +31 -0
- package/package.json +9 -1
- package/services/confirm/src/lib/components/dialog-confirm/dialog-confirm.component.d.ts +4 -0
- package/services/confirm/src/lib/confirm.service.d.ts +6 -0
- package/utilities/index.d.ts +1 -0
- package/utilities/read-state/index.d.ts +1 -0
- package/utilities/read-state/src/read-state.d.ts +37 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { SdDataState } from '@sd-angular/core/components/data-state';
|
|
1
2
|
import * as i0 from '@angular/core';
|
|
2
3
|
import { Pipe, Injectable, EventEmitter, inject, Output, ViewChild, Input, Component } from '@angular/core';
|
|
3
4
|
import * as i7 from '@angular/material/paginator';
|
|
@@ -118,6 +119,26 @@ class SdImportExcel {
|
|
|
118
119
|
// originItems: SdExcelItem[] = [];
|
|
119
120
|
hasDescription = false;
|
|
120
121
|
showing = 'ALL';
|
|
122
|
+
filters = [
|
|
123
|
+
{ value: 'ALL', label: 'core.component.import-excel.filter-all', icon: 'list_alt' },
|
|
124
|
+
{ value: 'SUCCESS', label: 'core.component.import-excel.filter-success', icon: 'check_circle_outline' },
|
|
125
|
+
{ value: 'WARNING', label: 'core.component.import-excel.filter-warning', icon: 'warning_amber' },
|
|
126
|
+
{ value: 'ERROR', label: 'core.component.import-excel.filter-error', icon: 'error_outline' },
|
|
127
|
+
];
|
|
128
|
+
filterCount = (value) => {
|
|
129
|
+
if (value === 'SUCCESS')
|
|
130
|
+
return this.numberOfSuccess;
|
|
131
|
+
if (value === 'WARNING')
|
|
132
|
+
return this.numberOfWarning;
|
|
133
|
+
if (value === 'ERROR')
|
|
134
|
+
return this.numberOfError;
|
|
135
|
+
return this.excelItems.length;
|
|
136
|
+
};
|
|
137
|
+
// why: callback có thể trả cùng thông báo nhiều lần; chỉ gộp phần hiển thị, giữ nguyên kết quả validation.
|
|
138
|
+
validationMessages = (item) => ({
|
|
139
|
+
errors: [...new Set(item.meta.errorMessages)],
|
|
140
|
+
warnings: [...new Set(item.meta.warningMessages)],
|
|
141
|
+
});
|
|
121
142
|
filteredItems = [];
|
|
122
143
|
viewItems = [];
|
|
123
144
|
numberOfSuccess = 0;
|
|
@@ -127,6 +148,11 @@ class SdImportExcel {
|
|
|
127
148
|
#paginator;
|
|
128
149
|
#paginatorSub;
|
|
129
150
|
set paginator(paginator) {
|
|
151
|
+
if (!paginator) {
|
|
152
|
+
this.#paginatorSub?.unsubscribe();
|
|
153
|
+
this.#paginator = undefined;
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
130
156
|
if (paginator && this.#paginator !== paginator) {
|
|
131
157
|
this.#paginator = paginator;
|
|
132
158
|
this.#paginatorSub?.unsubscribe(); // Clear cũ
|
|
@@ -168,10 +194,13 @@ class SdImportExcel {
|
|
|
168
194
|
this.numberOfError = 0;
|
|
169
195
|
this.numberOfWarning = 0;
|
|
170
196
|
this.showing = 'ALL';
|
|
197
|
+
this.file = undefined;
|
|
198
|
+
this.isUploaded = false;
|
|
171
199
|
};
|
|
172
200
|
#reload = () => {
|
|
173
|
-
const pageIndex = this.#paginator
|
|
174
|
-
|
|
201
|
+
const pageIndex = this.#paginator?.pageIndex ?? 0;
|
|
202
|
+
// why: lần đọc tệp đầu tiên chưa render paginator; dùng cùng pageSize của preview.
|
|
203
|
+
const pageSize = this.#paginator?.pageSize || 10;
|
|
175
204
|
this.filteredItems = this.excelItems.filter(item => {
|
|
176
205
|
const { errorMessages, warningMessages } = item.meta;
|
|
177
206
|
if (this.showing === 'SUCCESS') {
|
|
@@ -249,7 +278,8 @@ class SdImportExcel {
|
|
|
249
278
|
}
|
|
250
279
|
});
|
|
251
280
|
}
|
|
252
|
-
this.#paginator
|
|
281
|
+
if (this.#paginator)
|
|
282
|
+
this.#paginator.pageIndex = 0;
|
|
253
283
|
this.#reload();
|
|
254
284
|
this.ref.detectChanges();
|
|
255
285
|
this.isUploaded = false;
|
|
@@ -342,7 +372,10 @@ class SdImportExcel {
|
|
|
342
372
|
continue;
|
|
343
373
|
}
|
|
344
374
|
if (column.checkValueInArray && item[column.field] && !column.values.some(e => e.toString() === item[column.field].toString())) {
|
|
345
|
-
error[column.field] = this.#i18n.t('core.component.import-excel.value-not-in-list', {
|
|
375
|
+
error[column.field] = this.#i18n.t('core.component.import-excel.value-not-in-list', {
|
|
376
|
+
value: item[column.field],
|
|
377
|
+
list: column.values.join(),
|
|
378
|
+
});
|
|
346
379
|
errorMessages.push(`<strong>[${column.title || column.field}]</strong> ${error[column.field]}`);
|
|
347
380
|
continue;
|
|
348
381
|
}
|
|
@@ -356,29 +389,44 @@ class SdImportExcel {
|
|
|
356
389
|
}
|
|
357
390
|
if (format && item[column.field]) {
|
|
358
391
|
if (typeof val !== 'string') {
|
|
359
|
-
error[column.field] = this.#i18n.t('core.component.import-excel.invalid-date-format', {
|
|
392
|
+
error[column.field] = this.#i18n.t('core.component.import-excel.invalid-date-format', {
|
|
393
|
+
value: val ?? '',
|
|
394
|
+
format: column.format ?? '',
|
|
395
|
+
});
|
|
360
396
|
errorMessages.push(`<strong>[${column.title || column.field}]</strong> ${error[column.field]}`);
|
|
361
397
|
continue;
|
|
362
398
|
}
|
|
363
399
|
if (type === 'date' && !this.#isValidDate(format, val)) {
|
|
364
|
-
error[column.field] = this.#i18n.t('core.component.import-excel.invalid-date-format', {
|
|
400
|
+
error[column.field] = this.#i18n.t('core.component.import-excel.invalid-date-format', {
|
|
401
|
+
value: val ?? '',
|
|
402
|
+
format: column.format ?? '',
|
|
403
|
+
});
|
|
365
404
|
errorMessages.push(`<strong>[${column.title || column.field}]</strong> ${error[column.field]}`);
|
|
366
405
|
continue;
|
|
367
406
|
}
|
|
368
407
|
if (type === 'time' && !this.#isValidTime(format, val)) {
|
|
369
|
-
error[column.field] = this.#i18n.t('core.component.import-excel.invalid-date-format', {
|
|
408
|
+
error[column.field] = this.#i18n.t('core.component.import-excel.invalid-date-format', {
|
|
409
|
+
value: val ?? '',
|
|
410
|
+
format: column.format ?? '',
|
|
411
|
+
});
|
|
370
412
|
errorMessages.push(`<strong>[${column.title || column.field}]</strong> ${error[column.field]}`);
|
|
371
413
|
continue;
|
|
372
414
|
}
|
|
373
415
|
if (type === 'datetime' && !this.#isValidDateTime(format, val)) {
|
|
374
|
-
error[column.field] = this.#i18n.t('core.component.import-excel.invalid-date-format', {
|
|
416
|
+
error[column.field] = this.#i18n.t('core.component.import-excel.invalid-date-format', {
|
|
417
|
+
value: val ?? '',
|
|
418
|
+
format: column.format ?? '',
|
|
419
|
+
});
|
|
375
420
|
errorMessages.push(`<strong>[${column.title || column.field}]</strong> ${error[column.field]}`);
|
|
376
421
|
continue;
|
|
377
422
|
}
|
|
378
423
|
item[column.field] = DateUtilities.parseFrom(val, format);
|
|
379
424
|
}
|
|
380
425
|
if (item[column.field] && !DateUtilities.isDate(item[column.field])) {
|
|
381
|
-
error[column.field] = this.#i18n.t('core.component.import-excel.invalid-date-format', {
|
|
426
|
+
error[column.field] = this.#i18n.t('core.component.import-excel.invalid-date-format', {
|
|
427
|
+
value: val ?? '',
|
|
428
|
+
format: column.format ?? '',
|
|
429
|
+
});
|
|
382
430
|
errorMessages.push(`<strong>[${column.title || column.field}]</strong> ${error[column.field]}`);
|
|
383
431
|
continue;
|
|
384
432
|
}
|
|
@@ -426,7 +474,8 @@ class SdImportExcel {
|
|
|
426
474
|
};
|
|
427
475
|
view = (showing = 'ALL') => {
|
|
428
476
|
this.showing = showing;
|
|
429
|
-
this.#paginator
|
|
477
|
+
if (this.#paginator)
|
|
478
|
+
this.#paginator.pageIndex = 0;
|
|
430
479
|
this.#reload();
|
|
431
480
|
};
|
|
432
481
|
downloadTemplate = async () => {
|
|
@@ -496,7 +545,9 @@ class SdImportExcel {
|
|
|
496
545
|
}
|
|
497
546
|
return {
|
|
498
547
|
...result,
|
|
499
|
-
sdMessage: (e.meta.errorMessages.join(', ') ||
|
|
548
|
+
sdMessage: (e.meta.errorMessages.join(', ') ||
|
|
549
|
+
e.meta.warningMessages.join(', ') ||
|
|
550
|
+
this.#i18n.t('core.component.import-excel.success'))
|
|
500
551
|
?.replace(/<strong>/g, '')
|
|
501
552
|
.replace(/<\/strong>/g, '')
|
|
502
553
|
.replace(/<br>/g, '\n'),
|
|
@@ -538,13 +589,12 @@ class SdImportExcel {
|
|
|
538
589
|
this.sdClosed.emit();
|
|
539
590
|
};
|
|
540
591
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: SdImportExcel, deps: [{ token: i0.ChangeDetectorRef }, { token: i1$1.SdExcelService }, { token: i2.SdNotifyService }, { token: ColumnHiddenPipe }, { token: i4.SdLoadingService }], target: i0.ɵɵFactoryTarget.Component });
|
|
541
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.21", type: SdImportExcel, isStandalone: true, selector: "sd-import-excel", inputs: { option: "option" }, outputs: { sdClosed: "sdClosed" }, providers: [
|
|
542
|
-
ColumnHiddenPipe
|
|
543
|
-
], viewQueries: [{ propertyName: "modal", first: true, predicate: SdModal, descendants: true }, { propertyName: "paginator", first: true, predicate: MatPaginator, descendants: true }], ngImport: i0, template: "@if (option) {\n <sd-modal [title]=\"option.title || ('core.component.import-excel.title' | translate)\" (sdClosed)=\"onClosed()\" #modal>\n <div class=\"d-flex align-items-center mb-12\" style=\"min-height: 32px\">\n @if (excelItems.length) {\n <div class=\"d-flex\">\n <sd-button class=\"mr-2\" (click)=\"view('ALL')\" icon=\"cached\" [title]=\"'core.component.import-excel.see-all' | translate\" size=\"sm\" type=\"outline\"></sd-button>\n\n <sd-button\n class=\"mr-2\"\n (click)=\"view('SUCCESS')\"\n icon=\"done\"\n title=\"{{ numberOfSuccess }}\"\n [tooltip]=\"'core.component.import-excel.valid-rows' | translate\"\n size=\"sm\"\n color=\"success\">\n </sd-button>\n\n <sd-button\n class=\"mr-2\"\n (click)=\"view('WARNING')\"\n icon=\"warning\"\n title=\"{{ numberOfWarning }}\"\n [tooltip]=\"'core.component.import-excel.error-rows' | translate\"\n size=\"sm\"\n color=\"warning\">\n </sd-button>\n\n <sd-button\n (click)=\"view('ERROR')\"\n icon=\"error\"\n title=\"{{ numberOfError }}\"\n color=\"error\"\n [tooltip]=\"'core.component.import-excel.warning-rows' | translate\"\n size=\"sm\">\n </sd-button>\n </div>\n }\n </div>\n\n <div class=\"sd-box border rounded\">\n <div class=\"sd-box-body p-0\">\n <div class=\"table-responsive\">\n <table class=\"sd-table\">\n <thead>\n <tr>\n <th class=\"text-center c-sticky-left\" style=\"width: 50px\">#</th>\n @if (filteredItems.length) {\n <th class=\"text-center\" style=\"min-width: 250px; width: 250px\">{{ 'core.component.import-excel.status' | translate }}</th>\n }\n @for (column of option.columns; track column.field) {\n @if (column | columnHidden) {\n <th [ngStyle]=\"{ 'min-width': column.width || '300px', width: column.width || '300px' }\" [matTooltip]=\"column.title\">\n <span class=\"c-ellipsis\">\n {{ column.title }}\n </span>\n </th>\n }\n }\n </tr>\n </thead>\n\n <tbody>\n @if (viewItems.length) {\n @for (item of viewItems; track item.meta.excelIndex) {\n <tr>\n <td class=\"text-center c-sticky-left px-4\">\n <sd-badge\n type=\"tag\"\n [title]=\"item.meta.excelIndex\"\n [success]=\"!item.meta.warningMessages.length && !item.meta.errorMessages.length\"\n [warning]=\"!!item.meta.warningMessages.length && !item.meta.errorMessages.length\"\n [error]=\"!!item.meta.errorMessages.length\" />\n </td>\n\n <td style=\"min-width: 250px; width: 250px\">\n <div\n class=\"text-wrap\"\n [innerHTML]=\"\n item.meta.errorMessages[0] || item.meta.warningMessages[0] || ('<span class=\\'text-success\\'>' + ('core.component.import-excel.valid-data' | translate) + '</span>')\n \"></div>\n </td>\n\n @for (column of option.columns; track column.field) {\n @if (column | columnHidden) {\n <td\n class=\"align-middle\"\n [ngClass]=\"{\n 'bg-warning-light': item.meta.warning[column.field] && !item.meta.error[column.field],\n 'bg-error-light': item.meta.error[column.field]\n }\"\n [matTooltip]=\"item.meta.error[column.field] || item.meta.warning[column.field]\"\n [ngStyle]=\"{ 'min-width': column.width || '300px' }\"\n matTooltipPosition=\"above\">\n <div class=\"c-ellipsis\">\n @if (column.type !== 'array') {\n <span [innerHTML]=\"item | columnTransform: column | async\"></span>\n }\n\n @if (column.type === 'array') {\n @let arrayItems = item.data[column.field]?.split(column.divideString);\n\n <div class=\"d-flex align-items-center justify-content-between\">\n <span>{{ arrayItems?.length || 0 }} {{ column.unitString }}</span>\n\n @if (arrayItems?.length) {\n <button\n mat-icon-button\n [matMenuTriggerFor]=\"menu\"\n class=\"c-mat-menu\"\n style=\"width: 24px; height: 24px; line-height: 24px\">\n <mat-icon style=\"font-size: 18px\">open_in_new</mat-icon>\n </button>\n\n <mat-menu #menu=\"matMenu\">\n <div\n class=\"px-3 py-2\"\n style=\"max-width: 300px; max-height: 250px; overflow: auto\"\n (click)=\"$event.stopPropagation()\"\n aria-hidden=\"true\">\n @for (val of arrayItems; track $index) {\n <div class=\"border-bottom py-2 text-wrap\" [innerHTML]=\"val\"></div>\n }\n </div>\n </mat-menu>\n }\n </div>\n }\n </div>\n </td>\n }\n }\n </tr>\n }\n } @else {\n <tr>\n <td [attr.colspan]=\"option.columns.length + 2\" class=\"p-0 border-0 bg-white\">\n <div\n class=\"empty-state-wrapper\"\n (click)=\"downloadTemplate()\"\n [matTooltip]=\"'core.component.import-excel.download-template-tooltip' | translate\"\n aria-hidden=\"true\">\n <mat-icon class=\"empty-icon\">cloud_download</mat-icon>\n\n <h4>{{ 'core.component.import-excel.no-data-upload' | translate }}</h4>\n <span class=\"text-small text-link\"> {{ 'core.component.import-excel.click-here-template' | translate }} </span>\n\n @if (isDownloadTemplate) {\n <div class=\"mt-3\">\n <mat-spinner diameter=\"24\" mode=\"indeterminate\"></mat-spinner>\n </div>\n }\n </div>\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n </div>\n\n <div class=\"sd-box-footer border-top\">\n <div class=\"d-flex align-items-center justify-content-end\">\n <mat-paginator [length]=\"filteredItems.length || 0\" hidePageSize showFirstLastButtons></mat-paginator>\n </div>\n </div>\n </div>\n <sd-button sdFooterLeft (click)=\"upload()\" prefixIcon=\"file_upload\" [title]=\"'core.component.import-excel.upload' | translate\" size=\"sm\" color=\"info\" [loading]=\"uploading\">\n </sd-button>\n <div class=\"d-flex align-items-center\" sdFooter>\n @if (filteredItems.length) {\n <sd-button class=\"mr-4\" (click)=\"export()\" prefixIcon=\"get_app\" [title]=\"'core.component.import-excel.download' | translate\" size=\"sm\" color=\"success\" type=\"outline\">\n </sd-button>\n }\n <sd-button\n (click)=\"accept()\"\n [title]=\"'core.component.import-excel.confirm-save' | translate\"\n [disabled]=\"(numberOfSuccess === 0 && numberOfWarning === 0) || numberOfError > 0 || isUploaded\"\n color=\"primary\"\n type=\"fill\"\n size=\"sm\">\n </sd-button>\n </div>\n </sd-modal>\n}\n", styles: [".table-responsive{position:relative;min-height:50vh;height:50vh;overflow:auto;border:1px solid #dee2e6}.sd-table{width:100%;border-collapse:collapse;margin-bottom:0;font-size:14px}.sd-table thead th{position:sticky;top:0;background-color:#f8f9fa;border-bottom:2px solid #dee2e6;border-top:1px solid #dee2e6;padding:8px 6px;white-space:nowrap;z-index:10;font-weight:600}.sd-table thead th.c-sticky-left{z-index:20}.sd-table tbody td{vertical-align:middle;padding:6px;border-top:1px solid #dee2e6}.sd-table tbody tr:nth-of-type(odd){background-color:#0000000a}.sd-table tbody tr:nth-of-type(2n){background-color:#fff}.sd-table tbody tr:hover,.sd-table tbody tr:hover .c-sticky-left{background-color:#ececec}.sd-table tbody tr:nth-of-type(odd) .c-sticky-left{background-color:#f2f2f2}.sd-table tbody tr:nth-of-type(2n) .c-sticky-left{background-color:#fff}.c-sticky-left{position:sticky;left:0;border-right:1px solid #dee2e6!important;border-left:none!important;z-index:5}.empty-state-wrapper{height:100%;min-height:40vh;display:flex;flex-direction:column;align-items:center;justify-content:center;cursor:pointer;background-color:#f8f9fa;border:2px dashed #cbd5e0;border-radius:8px;margin:16px;transition:all .2s ease-in-out;color:#6c757d}.empty-state-wrapper:hover{background-color:#e3f2fd;border-color:#2196f3;color:#1976d2}.empty-state-wrapper:hover .empty-icon{transform:scale(1.1);color:#1976d2}.empty-state-wrapper:hover .text-link{text-decoration:underline}.empty-state-wrapper .empty-icon{font-size:64px;height:64px;width:64px;margin-bottom:16px;color:#a0aec0;transition:transform .2s}.empty-state-wrapper h4{font-weight:500;margin-bottom:4px}.c-ellipsis{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:block;padding:0 4px}.c-mat-menu{border:none;min-width:unset;padding:0 8px}.c-mat-menu mat-icon{font-size:18px;width:18px;height:18px;line-height:18px;vertical-align:middle}.cursor-pointer{cursor:pointer}:host ::ng-deep .mat-paginator-container{min-height:36px;height:36px;padding:0 8px}:host ::ng-deep .mat-paginator-range-label{margin:0 10px}:host ::ng-deep .mat-icon-button{width:32px;height:32px;line-height:32px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i5.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i5.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "pipe", type: i5.AsyncPipe, name: "async" }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: MatSlideToggleModule }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i6.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: MatPaginatorModule }, { kind: "component", type: i7.MatPaginator, selector: "mat-paginator", inputs: ["color", "pageIndex", "length", "pageSize", "pageSizeOptions", "hidePageSize", "showFirstLastButtons", "selectConfig", "disabled"], outputs: ["page"], exportAs: ["matPaginator"] }, { kind: "component", type: SdButton, selector: "sd-button", inputs: ["autoId", "type", "color", "size", "fontSet", "title", "width", "tooltip", "prefixIcon", "suffixIcon", "disabled", "loading", "block", "htmlType"], outputs: ["click"] }, { kind: "component", type: SdModal, selector: "sd-modal", inputs: ["autoId", "title", "color", "width", "height", "view", "modalClass", "lazyLoadContent", "hideClose", "disableBackdropClose"], outputs: ["sdClosed"] }, { kind: "pipe", type: ColumnTransformPipe, name: "columnTransform" }, { kind: "pipe", type: ColumnHiddenPipe, name: "columnHidden" }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i8.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i9.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "directive", type: i9.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "component", type: MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "component", type: SdBadge, selector: "sd-badge", inputs: ["type", "color", "primary", "secondary", "success", "info", "warning", "error", "fontSet", "title", "description", "tooltip", "icon", "size"], outputs: ["click"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
|
|
592
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.21", type: SdImportExcel, isStandalone: true, selector: "sd-import-excel", inputs: { option: "option" }, outputs: { sdClosed: "sdClosed" }, providers: [ColumnHiddenPipe], viewQueries: [{ propertyName: "modal", first: true, predicate: SdModal, descendants: true }, { propertyName: "paginator", first: true, predicate: MatPaginator, descendants: true }], ngImport: i0, template: "@if (option) {\n <sd-modal\n [title]=\"option.title || ('core.component.import-excel.title' | translate)\"\n width=\"min(1120px, calc(100vw - 32px))\"\n (sdClosed)=\"onClosed()\"\n #modal>\n <div class=\"sd-import-workspace\" [attr.aria-busy]=\"uploading\">\n @if (uploading) {\n <sd-data-state state=\"loading\" [title]=\"'core.component.import-excel.loading' | translate\" message=\"\" />\n } @else if (!excelItems.length) {\n <div class=\"sd-import-entry\">\n <sd-data-state\n state=\"empty\"\n icon=\"upload_file\"\n [title]=\"'core.component.import-excel.title' | translate\"\n [message]=\"'core.component.import-excel.choose-hint' | translate: { limit: option.limit || 1000 }\" />\n <div class=\"sd-import-entry-actions\">\n <sd-button\n data-import-action=\"choose\"\n (click)=\"upload()\"\n prefixIcon=\"upload_file\"\n [title]=\"'core.component.import-excel.choose-file' | translate\"\n color=\"primary\"\n type=\"fill\"\n size=\"sm\" />\n <sd-button\n data-import-action=\"template\"\n (click)=\"downloadTemplate()\"\n prefixIcon=\"download\"\n [title]=\"'core.component.import-excel.template' | translate\"\n color=\"secondary\"\n type=\"link\"\n size=\"sm\"\n [loading]=\"isDownloadTemplate\" />\n </div>\n </div>\n } @else {\n <div class=\"sd-import-file\">\n <span class=\"sd-import-file-icon\" aria-hidden=\"true\"><mat-icon>description</mat-icon></span>\n <div class=\"sd-import-file-info\">\n <strong>{{ file?.name || option.fileName || ('core.component.import-excel.title' | translate) }}</strong>\n <span>{{ 'core.component.import-excel.row-count' | translate: { count: excelItems.length } }}</span>\n </div>\n <sd-button\n (click)=\"upload()\"\n prefixIcon=\"upload_file\"\n [title]=\"'core.component.import-excel.replace-file' | translate\"\n color=\"secondary\"\n type=\"link\"\n size=\"sm\" />\n </div>\n\n <div class=\"sd-import-filters\" role=\"group\" [attr.aria-label]=\"'core.component.import-excel.status' | translate\">\n @for (filter of filters; track filter.value) {\n <button\n type=\"button\"\n class=\"sd-import-filter\"\n [class]=\"'sd-import-filter sd-import-filter--' + filter.value.toLowerCase()\"\n [attr.data-import-filter]=\"filter.value\"\n [attr.aria-pressed]=\"showing === filter.value\"\n (click)=\"view(filter.value)\">\n <mat-icon aria-hidden=\"true\">{{ filter.icon }}</mat-icon>\n <span>{{ filter.label | translate }}</span\n ><span class=\"sd-import-filter-count\">{{ filterCount(filter.value) }}</span>\n </button>\n }\n </div>\n\n @if (viewItems.length) {\n <div class=\"table-responsive\" tabindex=\"0\" role=\"region\" [attr.aria-label]=\"'core.component.import-excel.preview' | translate\">\n <table class=\"sd-table\">\n <thead>\n <tr>\n <th class=\"text-center c-sticky-left\" style=\"width: 50px\">#</th>\n <th class=\"sd-import-status\">{{ 'core.component.import-excel.status' | translate }}</th>\n @for (column of option.columns; track column.field) {\n @if (column | columnHidden) {\n <th [ngStyle]=\"{ 'min-width': column.width || '300px', width: column.width || '300px' }\" [matTooltip]=\"column.title\">\n <span class=\"c-ellipsis\">{{ column.title }}</span>\n </th>\n }\n }\n </tr>\n </thead>\n <tbody>\n @for (item of viewItems; track item.meta.excelIndex) {\n <tr>\n <td class=\"text-center c-sticky-left px-4\">\n <sd-badge\n type=\"tag\"\n [title]=\"item.meta.excelIndex\"\n [success]=\"!item.meta.warningMessages.length && !item.meta.errorMessages.length\"\n [warning]=\"!!item.meta.warningMessages.length && !item.meta.errorMessages.length\"\n [error]=\"!!item.meta.errorMessages.length\" />\n </td>\n <td class=\"sd-import-status\">\n @let messages = validationMessages(item);\n @let issueCount = messages.errors.length + messages.warnings.length;\n @if (issueCount) {\n <details class=\"sd-import-issues\">\n <summary>\n <span [innerHTML]=\"item.meta.errorMessages[0] || item.meta.warningMessages[0]\"></span>\n <span class=\"sd-import-issue-count\">{{\n 'core.component.import-excel.issue-count' | translate: { count: issueCount }\n }}</span>\n </summary>\n <ul>\n @for (message of messages.errors; track $index) {\n <li class=\"sd-import-error\">\n <span class=\"sd-import-issue-label\">{{ 'core.component.import-excel.filter-error' | translate }}:</span>\n <span [innerHTML]=\"message\"></span>\n </li>\n }\n @for (message of messages.warnings; track $index) {\n <li class=\"sd-import-warning\">\n <span class=\"sd-import-issue-label\">{{ 'core.component.import-excel.filter-warning' | translate }}:</span>\n <span [innerHTML]=\"message\"></span>\n </li>\n }\n </ul>\n </details>\n } @else {\n <span class=\"sd-import-valid\">{{ 'core.component.import-excel.valid-data' | translate }}</span>\n }\n </td>\n @for (column of option.columns; track column.field) {\n @if (column | columnHidden) {\n <td\n class=\"align-middle\"\n [ngClass]=\"{\n 'bg-warning-light': item.meta.warning[column.field] && !item.meta.error[column.field],\n 'bg-error-light': item.meta.error[column.field],\n }\"\n [matTooltip]=\"item.meta.error[column.field] || item.meta.warning[column.field]\"\n [ngStyle]=\"{ 'min-width': column.width || '300px' }\"\n matTooltipPosition=\"above\">\n <div class=\"c-ellipsis\">\n @if (column.type !== 'array') {\n <span [innerHTML]=\"item | columnTransform: column | async\"></span>\n } @else {\n @let arrayItems = item.data[column.field]?.split(column.divideString);\n <div class=\"d-flex align-items-center justify-content-between\">\n <span>{{ arrayItems?.length || 0 }} {{ column.unitString }}</span>\n @if (arrayItems?.length) {\n <button\n mat-icon-button\n [matMenuTriggerFor]=\"menu\"\n class=\"c-mat-menu\"\n [attr.aria-label]=\"column.title\"\n style=\"width: 24px; height: 24px; line-height: 24px\">\n <mat-icon style=\"font-size: 18px\">open_in_new</mat-icon>\n </button>\n <mat-menu #menu=\"matMenu\">\n <div\n class=\"px-3 py-2 text-wrap\"\n role=\"group\"\n style=\"max-width: 300px; max-height: 250px; overflow: auto\"\n (click)=\"$event.stopPropagation()\"\n (keydown.enter)=\"$event.stopPropagation()\">\n @for (val of arrayItems; track $index) {\n <div class=\"border-bottom py-2 text-wrap\" [innerHTML]=\"val\"></div>\n }\n </div>\n </mat-menu>\n }\n </div>\n }\n </div>\n </td>\n }\n }\n </tr>\n }\n </tbody>\n </table>\n </div>\n } @else {\n <sd-data-state\n class=\"sd-import-no-results\"\n state=\"empty\"\n compact\n [title]=\"'core.component.import-excel.no-filter-results' | translate\"\n [message]=\"'core.component.import-excel.no-filter-results-hint' | translate\"\n [actionLabel]=\"'core.component.import-excel.see-all' | translate\"\n (sdAction)=\"view('ALL')\" />\n }\n <mat-paginator\n class=\"sd-import-paginator\"\n [hidden]=\"!filteredItems.length\"\n [length]=\"filteredItems.length\"\n [pageSize]=\"10\"\n hidePageSize />\n\n <div class=\"sd-import-footer\">\n @if (numberOfError) {\n <p class=\"sd-import-blocked\" role=\"status\">\n {{ 'core.component.import-excel.blocked-errors' | translate: { count: numberOfError } }}\n </p>\n } @else if (isUploaded) {\n <p class=\"sd-import-submitted\" role=\"status\">{{ 'core.component.import-excel.submitted' | translate }}</p>\n }\n <div class=\"sd-import-footer-actions\">\n <sd-button\n (click)=\"export()\"\n prefixIcon=\"download\"\n [disabled]=\"!filteredItems.length\"\n [title]=\"'core.component.import-excel.download' | translate\"\n size=\"sm\"\n color=\"secondary\"\n type=\"link\" />\n <sd-button\n data-import-action=\"accept\"\n (click)=\"accept()\"\n [title]=\"'core.component.import-excel.confirm-save' | translate\"\n [disabled]=\"(numberOfSuccess === 0 && numberOfWarning === 0) || numberOfError > 0 || isUploaded\"\n color=\"primary\"\n type=\"fill\"\n size=\"sm\" />\n </div>\n </div>\n }\n </div>\n </sd-modal>\n}\n", styles: [":host{display:block}.sd-import-workspace{min-width:0;color:var(--sd-text, #24272b)}.sd-import-entry{padding:24px 0 32px}.sd-import-entry-actions{display:flex;flex-wrap:wrap;justify-content:center;gap:8px;padding:0 12px}.sd-import-file{display:flex;align-items:center;gap:12px;padding-bottom:16px}.sd-import-file-icon{display:flex;align-items:center;justify-content:center;flex:0 0 36px;height:36px;border-radius:10px;color:var(--sd-primary, #005cbb);background:var(--sd-primary-light, #e5effa)}.sd-import-file-info{display:flex;flex:1;min-width:0;flex-direction:column;gap:3px}.sd-import-file-info strong{font-size:14px;font-weight:600;overflow-wrap:anywhere}.sd-import-file-info>span{font-size:12px;color:var(--sd-text-secondary, #62676d)}.sd-import-filters{display:flex;flex-wrap:wrap;gap:8px;padding-bottom:16px}.sd-import-filter{--filter-color: var(--sd-primary, #005cbb);--filter-bg: var(--sd-primary-light, #e5effa);display:inline-flex;align-items:center;justify-content:center;gap:6px;min-height:36px;padding:6px 10px;border:1px solid var(--sd-border, #dfe2e7);border-radius:8px;background:var(--sd-surface, #fff);color:var(--sd-text-secondary, #62676d);cursor:pointer;font:inherit;font-size:13px}.sd-import-filter--success{--filter-color: var(--sd-success, #2b7d39);--filter-bg: var(--sd-success-light, #e4f3e6)}.sd-import-filter--warning{--filter-color: var(--sd-warning-dark, #8a5700);--filter-bg: var(--sd-warning-light, #fff3d8)}.sd-import-filter--error{--filter-color: var(--sd-error, #bc3030);--filter-bg: var(--sd-error-light, #fce8e8)}.sd-import-filter[aria-pressed=true]{background:var(--filter-bg);color:var(--filter-color);border-color:var(--filter-color);font-weight:600}.sd-import-filter:hover{background:var(--filter-bg);color:var(--filter-color)}.sd-import-filter:focus-visible{outline:2px solid var(--sd-primary, #005cbb);outline-offset:2px}.sd-import-filter sd-icon,.sd-import-filter mat-icon{width:18px;height:18px;font-size:18px}.sd-import-filter-count{font-variant-numeric:tabular-nums;min-width:16px;text-align:center}.table-responsive{position:relative;max-height:50vh;overflow:auto;border:1px solid var(--sd-border, #dfe2e7);border-radius:8px}.table-responsive:focus-visible{outline:2px solid var(--sd-primary, #005cbb);outline-offset:2px}.sd-table{width:100%;border-collapse:separate;border-spacing:0;font-size:13px}.sd-table thead th{position:sticky;top:0;padding:10px 8px;white-space:nowrap;z-index:2;font-weight:600;background:var(--sd-surface-variant, #f6f7f9);border-bottom:1px solid var(--sd-border, #dfe2e7)}.sd-table thead th.c-sticky-left{z-index:3}.sd-table tbody td{padding:10px 8px;border-bottom:1px solid var(--sd-border, #e8eaed);vertical-align:top}.sd-table tbody tr:last-child td{border-bottom:0}.sd-table tbody tr{background:var(--sd-surface, #fff)}.sd-table tbody tr:hover{background:var(--sd-surface-variant, #f6f7f9)}.c-sticky-left{position:sticky;left:0;background:inherit;z-index:1;border-right:1px solid var(--sd-border, #e8eaed)}.sd-import-status{min-width:240px;width:240px;max-width:320px;white-space:normal!important;overflow-wrap:anywhere}.sd-import-issues summary{cursor:pointer;line-height:1.5}.sd-import-issues summary:focus-visible{outline:2px solid var(--sd-primary, #005cbb);outline-offset:2px;border-radius:3px}.sd-import-issues ul{padding-left:18px;margin:8px 0 0}.sd-import-issues li+li{margin-top:6px}.sd-import-issue-count{display:block;color:var(--sd-primary, #005cbb);font-size:12px;margin-top:4px}.sd-import-issue-label{font-weight:600}.sd-import-error,.sd-import-blocked{color:var(--sd-error, #bc3030)}.sd-import-warning{color:var(--sd-warning-dark, #8a5700)}.sd-import-valid,.sd-import-submitted{color:var(--sd-success, #2b7d39)}.sd-import-no-results{display:block;padding:24px 0;border:1px solid var(--sd-border, #dfe2e7);border-radius:8px}.sd-import-footer{padding:12px 0 0;border-top:1px solid var(--sd-border, #dfe2e7)}.sd-import-footer p{margin:0 0 10px;font-size:13px;line-height:1.5}.sd-import-footer-actions{display:flex;justify-content:flex-end;flex-wrap:wrap;gap:8px}.c-ellipsis{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:block;padding:0 4px}.c-mat-menu{border:none;min-width:unset;padding:0 8px}.sd-import-paginator{background:transparent}.sd-import-paginator ::ng-deep .mat-mdc-paginator-container{padding:0;min-height:48px}.sd-import-paginator ::ng-deep .mat-mdc-paginator-range-label{white-space:nowrap;margin:0 12px 0 0}@media(max-width:600px){.sd-import-entry{padding:4px 0 16px}.sd-import-file{flex-wrap:wrap;gap:8px}.sd-import-file>sd-button{margin-left:auto}.sd-import-filters{display:grid;grid-template-columns:repeat(2,minmax(0,1fr))}.sd-import-filter{min-height:40px;padding:6px;gap:5px}.sd-import-footer-actions sd-button{flex:1;min-width:0}.sd-import-footer-actions ::ng-deep sd-button button{width:100%;min-height:40px}}@media(max-width:400px){.sd-import-footer-actions{display:grid;grid-template-columns:minmax(0,1fr)}.sd-import-status{min-width:180px;width:180px}}\n"], dependencies: [{ kind: "component", type: SdDataState, selector: "sd-data-state", inputs: ["state", "title", "message", "icon", "fontSet", "retryable", "retryLabel", "actionLabel", "compact", "fullPage"], outputs: ["sdRetry", "sdAction"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i5.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i5.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "pipe", type: i5.AsyncPipe, name: "async" }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: MatSlideToggleModule }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i6.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: MatPaginatorModule }, { kind: "component", type: i7.MatPaginator, selector: "mat-paginator", inputs: ["color", "pageIndex", "length", "pageSize", "pageSizeOptions", "hidePageSize", "showFirstLastButtons", "selectConfig", "disabled"], outputs: ["page"], exportAs: ["matPaginator"] }, { kind: "component", type: SdButton, selector: "sd-button", inputs: ["autoId", "type", "color", "size", "fontSet", "title", "width", "tooltip", "prefixIcon", "suffixIcon", "disabled", "loading", "block", "htmlType"], outputs: ["click"] }, { kind: "component", type: SdModal, selector: "sd-modal", inputs: ["autoId", "title", "color", "width", "height", "view", "modalClass", "lazyLoadContent", "hideClose", "disableBackdropClose"], outputs: ["sdClosed"] }, { kind: "pipe", type: ColumnTransformPipe, name: "columnTransform" }, { kind: "pipe", type: ColumnHiddenPipe, name: "columnHidden" }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i8.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i9.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "directive", type: i9.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "component", type: SdBadge, selector: "sd-badge", inputs: ["type", "color", "primary", "secondary", "success", "info", "warning", "error", "fontSet", "title", "description", "tooltip", "icon", "size"], outputs: ["click"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
|
|
544
593
|
}
|
|
545
594
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: SdImportExcel, decorators: [{
|
|
546
595
|
type: Component,
|
|
547
596
|
args: [{ selector: 'sd-import-excel', imports: [
|
|
597
|
+
SdDataState,
|
|
548
598
|
CommonModule,
|
|
549
599
|
FormsModule,
|
|
550
600
|
MatSlideToggleModule,
|
|
@@ -558,10 +608,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImpo
|
|
|
558
608
|
MatIconModule,
|
|
559
609
|
MatMenuModule,
|
|
560
610
|
MatProgressSpinner,
|
|
561
|
-
SdBadge,
|
|
562
|
-
|
|
563
|
-
ColumnHiddenPipe
|
|
564
|
-
], template: "@if (option) {\n <sd-modal [title]=\"option.title || ('core.component.import-excel.title' | translate)\" (sdClosed)=\"onClosed()\" #modal>\n <div class=\"d-flex align-items-center mb-12\" style=\"min-height: 32px\">\n @if (excelItems.length) {\n <div class=\"d-flex\">\n <sd-button class=\"mr-2\" (click)=\"view('ALL')\" icon=\"cached\" [title]=\"'core.component.import-excel.see-all' | translate\" size=\"sm\" type=\"outline\"></sd-button>\n\n <sd-button\n class=\"mr-2\"\n (click)=\"view('SUCCESS')\"\n icon=\"done\"\n title=\"{{ numberOfSuccess }}\"\n [tooltip]=\"'core.component.import-excel.valid-rows' | translate\"\n size=\"sm\"\n color=\"success\">\n </sd-button>\n\n <sd-button\n class=\"mr-2\"\n (click)=\"view('WARNING')\"\n icon=\"warning\"\n title=\"{{ numberOfWarning }}\"\n [tooltip]=\"'core.component.import-excel.error-rows' | translate\"\n size=\"sm\"\n color=\"warning\">\n </sd-button>\n\n <sd-button\n (click)=\"view('ERROR')\"\n icon=\"error\"\n title=\"{{ numberOfError }}\"\n color=\"error\"\n [tooltip]=\"'core.component.import-excel.warning-rows' | translate\"\n size=\"sm\">\n </sd-button>\n </div>\n }\n </div>\n\n <div class=\"sd-box border rounded\">\n <div class=\"sd-box-body p-0\">\n <div class=\"table-responsive\">\n <table class=\"sd-table\">\n <thead>\n <tr>\n <th class=\"text-center c-sticky-left\" style=\"width: 50px\">#</th>\n @if (filteredItems.length) {\n <th class=\"text-center\" style=\"min-width: 250px; width: 250px\">{{ 'core.component.import-excel.status' | translate }}</th>\n }\n @for (column of option.columns; track column.field) {\n @if (column | columnHidden) {\n <th [ngStyle]=\"{ 'min-width': column.width || '300px', width: column.width || '300px' }\" [matTooltip]=\"column.title\">\n <span class=\"c-ellipsis\">\n {{ column.title }}\n </span>\n </th>\n }\n }\n </tr>\n </thead>\n\n <tbody>\n @if (viewItems.length) {\n @for (item of viewItems; track item.meta.excelIndex) {\n <tr>\n <td class=\"text-center c-sticky-left px-4\">\n <sd-badge\n type=\"tag\"\n [title]=\"item.meta.excelIndex\"\n [success]=\"!item.meta.warningMessages.length && !item.meta.errorMessages.length\"\n [warning]=\"!!item.meta.warningMessages.length && !item.meta.errorMessages.length\"\n [error]=\"!!item.meta.errorMessages.length\" />\n </td>\n\n <td style=\"min-width: 250px; width: 250px\">\n <div\n class=\"text-wrap\"\n [innerHTML]=\"\n item.meta.errorMessages[0] || item.meta.warningMessages[0] || ('<span class=\\'text-success\\'>' + ('core.component.import-excel.valid-data' | translate) + '</span>')\n \"></div>\n </td>\n\n @for (column of option.columns; track column.field) {\n @if (column | columnHidden) {\n <td\n class=\"align-middle\"\n [ngClass]=\"{\n 'bg-warning-light': item.meta.warning[column.field] && !item.meta.error[column.field],\n 'bg-error-light': item.meta.error[column.field]\n }\"\n [matTooltip]=\"item.meta.error[column.field] || item.meta.warning[column.field]\"\n [ngStyle]=\"{ 'min-width': column.width || '300px' }\"\n matTooltipPosition=\"above\">\n <div class=\"c-ellipsis\">\n @if (column.type !== 'array') {\n <span [innerHTML]=\"item | columnTransform: column | async\"></span>\n }\n\n @if (column.type === 'array') {\n @let arrayItems = item.data[column.field]?.split(column.divideString);\n\n <div class=\"d-flex align-items-center justify-content-between\">\n <span>{{ arrayItems?.length || 0 }} {{ column.unitString }}</span>\n\n @if (arrayItems?.length) {\n <button\n mat-icon-button\n [matMenuTriggerFor]=\"menu\"\n class=\"c-mat-menu\"\n style=\"width: 24px; height: 24px; line-height: 24px\">\n <mat-icon style=\"font-size: 18px\">open_in_new</mat-icon>\n </button>\n\n <mat-menu #menu=\"matMenu\">\n <div\n class=\"px-3 py-2\"\n style=\"max-width: 300px; max-height: 250px; overflow: auto\"\n (click)=\"$event.stopPropagation()\"\n aria-hidden=\"true\">\n @for (val of arrayItems; track $index) {\n <div class=\"border-bottom py-2 text-wrap\" [innerHTML]=\"val\"></div>\n }\n </div>\n </mat-menu>\n }\n </div>\n }\n </div>\n </td>\n }\n }\n </tr>\n }\n } @else {\n <tr>\n <td [attr.colspan]=\"option.columns.length + 2\" class=\"p-0 border-0 bg-white\">\n <div\n class=\"empty-state-wrapper\"\n (click)=\"downloadTemplate()\"\n [matTooltip]=\"'core.component.import-excel.download-template-tooltip' | translate\"\n aria-hidden=\"true\">\n <mat-icon class=\"empty-icon\">cloud_download</mat-icon>\n\n <h4>{{ 'core.component.import-excel.no-data-upload' | translate }}</h4>\n <span class=\"text-small text-link\"> {{ 'core.component.import-excel.click-here-template' | translate }} </span>\n\n @if (isDownloadTemplate) {\n <div class=\"mt-3\">\n <mat-spinner diameter=\"24\" mode=\"indeterminate\"></mat-spinner>\n </div>\n }\n </div>\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n </div>\n\n <div class=\"sd-box-footer border-top\">\n <div class=\"d-flex align-items-center justify-content-end\">\n <mat-paginator [length]=\"filteredItems.length || 0\" hidePageSize showFirstLastButtons></mat-paginator>\n </div>\n </div>\n </div>\n <sd-button sdFooterLeft (click)=\"upload()\" prefixIcon=\"file_upload\" [title]=\"'core.component.import-excel.upload' | translate\" size=\"sm\" color=\"info\" [loading]=\"uploading\">\n </sd-button>\n <div class=\"d-flex align-items-center\" sdFooter>\n @if (filteredItems.length) {\n <sd-button class=\"mr-4\" (click)=\"export()\" prefixIcon=\"get_app\" [title]=\"'core.component.import-excel.download' | translate\" size=\"sm\" color=\"success\" type=\"outline\">\n </sd-button>\n }\n <sd-button\n (click)=\"accept()\"\n [title]=\"'core.component.import-excel.confirm-save' | translate\"\n [disabled]=\"(numberOfSuccess === 0 && numberOfWarning === 0) || numberOfError > 0 || isUploaded\"\n color=\"primary\"\n type=\"fill\"\n size=\"sm\">\n </sd-button>\n </div>\n </sd-modal>\n}\n", styles: [".table-responsive{position:relative;min-height:50vh;height:50vh;overflow:auto;border:1px solid #dee2e6}.sd-table{width:100%;border-collapse:collapse;margin-bottom:0;font-size:14px}.sd-table thead th{position:sticky;top:0;background-color:#f8f9fa;border-bottom:2px solid #dee2e6;border-top:1px solid #dee2e6;padding:8px 6px;white-space:nowrap;z-index:10;font-weight:600}.sd-table thead th.c-sticky-left{z-index:20}.sd-table tbody td{vertical-align:middle;padding:6px;border-top:1px solid #dee2e6}.sd-table tbody tr:nth-of-type(odd){background-color:#0000000a}.sd-table tbody tr:nth-of-type(2n){background-color:#fff}.sd-table tbody tr:hover,.sd-table tbody tr:hover .c-sticky-left{background-color:#ececec}.sd-table tbody tr:nth-of-type(odd) .c-sticky-left{background-color:#f2f2f2}.sd-table tbody tr:nth-of-type(2n) .c-sticky-left{background-color:#fff}.c-sticky-left{position:sticky;left:0;border-right:1px solid #dee2e6!important;border-left:none!important;z-index:5}.empty-state-wrapper{height:100%;min-height:40vh;display:flex;flex-direction:column;align-items:center;justify-content:center;cursor:pointer;background-color:#f8f9fa;border:2px dashed #cbd5e0;border-radius:8px;margin:16px;transition:all .2s ease-in-out;color:#6c757d}.empty-state-wrapper:hover{background-color:#e3f2fd;border-color:#2196f3;color:#1976d2}.empty-state-wrapper:hover .empty-icon{transform:scale(1.1);color:#1976d2}.empty-state-wrapper:hover .text-link{text-decoration:underline}.empty-state-wrapper .empty-icon{font-size:64px;height:64px;width:64px;margin-bottom:16px;color:#a0aec0;transition:transform .2s}.empty-state-wrapper h4{font-weight:500;margin-bottom:4px}.c-ellipsis{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:block;padding:0 4px}.c-mat-menu{border:none;min-width:unset;padding:0 8px}.c-mat-menu mat-icon{font-size:18px;width:18px;height:18px;line-height:18px;vertical-align:middle}.cursor-pointer{cursor:pointer}:host ::ng-deep .mat-paginator-container{min-height:36px;height:36px;padding:0 8px}:host ::ng-deep .mat-paginator-range-label{margin:0 10px}:host ::ng-deep .mat-icon-button{width:32px;height:32px;line-height:32px}\n"] }]
|
|
611
|
+
SdBadge,
|
|
612
|
+
TranslatePipe,
|
|
613
|
+
], providers: [ColumnHiddenPipe], template: "@if (option) {\n <sd-modal\n [title]=\"option.title || ('core.component.import-excel.title' | translate)\"\n width=\"min(1120px, calc(100vw - 32px))\"\n (sdClosed)=\"onClosed()\"\n #modal>\n <div class=\"sd-import-workspace\" [attr.aria-busy]=\"uploading\">\n @if (uploading) {\n <sd-data-state state=\"loading\" [title]=\"'core.component.import-excel.loading' | translate\" message=\"\" />\n } @else if (!excelItems.length) {\n <div class=\"sd-import-entry\">\n <sd-data-state\n state=\"empty\"\n icon=\"upload_file\"\n [title]=\"'core.component.import-excel.title' | translate\"\n [message]=\"'core.component.import-excel.choose-hint' | translate: { limit: option.limit || 1000 }\" />\n <div class=\"sd-import-entry-actions\">\n <sd-button\n data-import-action=\"choose\"\n (click)=\"upload()\"\n prefixIcon=\"upload_file\"\n [title]=\"'core.component.import-excel.choose-file' | translate\"\n color=\"primary\"\n type=\"fill\"\n size=\"sm\" />\n <sd-button\n data-import-action=\"template\"\n (click)=\"downloadTemplate()\"\n prefixIcon=\"download\"\n [title]=\"'core.component.import-excel.template' | translate\"\n color=\"secondary\"\n type=\"link\"\n size=\"sm\"\n [loading]=\"isDownloadTemplate\" />\n </div>\n </div>\n } @else {\n <div class=\"sd-import-file\">\n <span class=\"sd-import-file-icon\" aria-hidden=\"true\"><mat-icon>description</mat-icon></span>\n <div class=\"sd-import-file-info\">\n <strong>{{ file?.name || option.fileName || ('core.component.import-excel.title' | translate) }}</strong>\n <span>{{ 'core.component.import-excel.row-count' | translate: { count: excelItems.length } }}</span>\n </div>\n <sd-button\n (click)=\"upload()\"\n prefixIcon=\"upload_file\"\n [title]=\"'core.component.import-excel.replace-file' | translate\"\n color=\"secondary\"\n type=\"link\"\n size=\"sm\" />\n </div>\n\n <div class=\"sd-import-filters\" role=\"group\" [attr.aria-label]=\"'core.component.import-excel.status' | translate\">\n @for (filter of filters; track filter.value) {\n <button\n type=\"button\"\n class=\"sd-import-filter\"\n [class]=\"'sd-import-filter sd-import-filter--' + filter.value.toLowerCase()\"\n [attr.data-import-filter]=\"filter.value\"\n [attr.aria-pressed]=\"showing === filter.value\"\n (click)=\"view(filter.value)\">\n <mat-icon aria-hidden=\"true\">{{ filter.icon }}</mat-icon>\n <span>{{ filter.label | translate }}</span\n ><span class=\"sd-import-filter-count\">{{ filterCount(filter.value) }}</span>\n </button>\n }\n </div>\n\n @if (viewItems.length) {\n <div class=\"table-responsive\" tabindex=\"0\" role=\"region\" [attr.aria-label]=\"'core.component.import-excel.preview' | translate\">\n <table class=\"sd-table\">\n <thead>\n <tr>\n <th class=\"text-center c-sticky-left\" style=\"width: 50px\">#</th>\n <th class=\"sd-import-status\">{{ 'core.component.import-excel.status' | translate }}</th>\n @for (column of option.columns; track column.field) {\n @if (column | columnHidden) {\n <th [ngStyle]=\"{ 'min-width': column.width || '300px', width: column.width || '300px' }\" [matTooltip]=\"column.title\">\n <span class=\"c-ellipsis\">{{ column.title }}</span>\n </th>\n }\n }\n </tr>\n </thead>\n <tbody>\n @for (item of viewItems; track item.meta.excelIndex) {\n <tr>\n <td class=\"text-center c-sticky-left px-4\">\n <sd-badge\n type=\"tag\"\n [title]=\"item.meta.excelIndex\"\n [success]=\"!item.meta.warningMessages.length && !item.meta.errorMessages.length\"\n [warning]=\"!!item.meta.warningMessages.length && !item.meta.errorMessages.length\"\n [error]=\"!!item.meta.errorMessages.length\" />\n </td>\n <td class=\"sd-import-status\">\n @let messages = validationMessages(item);\n @let issueCount = messages.errors.length + messages.warnings.length;\n @if (issueCount) {\n <details class=\"sd-import-issues\">\n <summary>\n <span [innerHTML]=\"item.meta.errorMessages[0] || item.meta.warningMessages[0]\"></span>\n <span class=\"sd-import-issue-count\">{{\n 'core.component.import-excel.issue-count' | translate: { count: issueCount }\n }}</span>\n </summary>\n <ul>\n @for (message of messages.errors; track $index) {\n <li class=\"sd-import-error\">\n <span class=\"sd-import-issue-label\">{{ 'core.component.import-excel.filter-error' | translate }}:</span>\n <span [innerHTML]=\"message\"></span>\n </li>\n }\n @for (message of messages.warnings; track $index) {\n <li class=\"sd-import-warning\">\n <span class=\"sd-import-issue-label\">{{ 'core.component.import-excel.filter-warning' | translate }}:</span>\n <span [innerHTML]=\"message\"></span>\n </li>\n }\n </ul>\n </details>\n } @else {\n <span class=\"sd-import-valid\">{{ 'core.component.import-excel.valid-data' | translate }}</span>\n }\n </td>\n @for (column of option.columns; track column.field) {\n @if (column | columnHidden) {\n <td\n class=\"align-middle\"\n [ngClass]=\"{\n 'bg-warning-light': item.meta.warning[column.field] && !item.meta.error[column.field],\n 'bg-error-light': item.meta.error[column.field],\n }\"\n [matTooltip]=\"item.meta.error[column.field] || item.meta.warning[column.field]\"\n [ngStyle]=\"{ 'min-width': column.width || '300px' }\"\n matTooltipPosition=\"above\">\n <div class=\"c-ellipsis\">\n @if (column.type !== 'array') {\n <span [innerHTML]=\"item | columnTransform: column | async\"></span>\n } @else {\n @let arrayItems = item.data[column.field]?.split(column.divideString);\n <div class=\"d-flex align-items-center justify-content-between\">\n <span>{{ arrayItems?.length || 0 }} {{ column.unitString }}</span>\n @if (arrayItems?.length) {\n <button\n mat-icon-button\n [matMenuTriggerFor]=\"menu\"\n class=\"c-mat-menu\"\n [attr.aria-label]=\"column.title\"\n style=\"width: 24px; height: 24px; line-height: 24px\">\n <mat-icon style=\"font-size: 18px\">open_in_new</mat-icon>\n </button>\n <mat-menu #menu=\"matMenu\">\n <div\n class=\"px-3 py-2 text-wrap\"\n role=\"group\"\n style=\"max-width: 300px; max-height: 250px; overflow: auto\"\n (click)=\"$event.stopPropagation()\"\n (keydown.enter)=\"$event.stopPropagation()\">\n @for (val of arrayItems; track $index) {\n <div class=\"border-bottom py-2 text-wrap\" [innerHTML]=\"val\"></div>\n }\n </div>\n </mat-menu>\n }\n </div>\n }\n </div>\n </td>\n }\n }\n </tr>\n }\n </tbody>\n </table>\n </div>\n } @else {\n <sd-data-state\n class=\"sd-import-no-results\"\n state=\"empty\"\n compact\n [title]=\"'core.component.import-excel.no-filter-results' | translate\"\n [message]=\"'core.component.import-excel.no-filter-results-hint' | translate\"\n [actionLabel]=\"'core.component.import-excel.see-all' | translate\"\n (sdAction)=\"view('ALL')\" />\n }\n <mat-paginator\n class=\"sd-import-paginator\"\n [hidden]=\"!filteredItems.length\"\n [length]=\"filteredItems.length\"\n [pageSize]=\"10\"\n hidePageSize />\n\n <div class=\"sd-import-footer\">\n @if (numberOfError) {\n <p class=\"sd-import-blocked\" role=\"status\">\n {{ 'core.component.import-excel.blocked-errors' | translate: { count: numberOfError } }}\n </p>\n } @else if (isUploaded) {\n <p class=\"sd-import-submitted\" role=\"status\">{{ 'core.component.import-excel.submitted' | translate }}</p>\n }\n <div class=\"sd-import-footer-actions\">\n <sd-button\n (click)=\"export()\"\n prefixIcon=\"download\"\n [disabled]=\"!filteredItems.length\"\n [title]=\"'core.component.import-excel.download' | translate\"\n size=\"sm\"\n color=\"secondary\"\n type=\"link\" />\n <sd-button\n data-import-action=\"accept\"\n (click)=\"accept()\"\n [title]=\"'core.component.import-excel.confirm-save' | translate\"\n [disabled]=\"(numberOfSuccess === 0 && numberOfWarning === 0) || numberOfError > 0 || isUploaded\"\n color=\"primary\"\n type=\"fill\"\n size=\"sm\" />\n </div>\n </div>\n }\n </div>\n </sd-modal>\n}\n", styles: [":host{display:block}.sd-import-workspace{min-width:0;color:var(--sd-text, #24272b)}.sd-import-entry{padding:24px 0 32px}.sd-import-entry-actions{display:flex;flex-wrap:wrap;justify-content:center;gap:8px;padding:0 12px}.sd-import-file{display:flex;align-items:center;gap:12px;padding-bottom:16px}.sd-import-file-icon{display:flex;align-items:center;justify-content:center;flex:0 0 36px;height:36px;border-radius:10px;color:var(--sd-primary, #005cbb);background:var(--sd-primary-light, #e5effa)}.sd-import-file-info{display:flex;flex:1;min-width:0;flex-direction:column;gap:3px}.sd-import-file-info strong{font-size:14px;font-weight:600;overflow-wrap:anywhere}.sd-import-file-info>span{font-size:12px;color:var(--sd-text-secondary, #62676d)}.sd-import-filters{display:flex;flex-wrap:wrap;gap:8px;padding-bottom:16px}.sd-import-filter{--filter-color: var(--sd-primary, #005cbb);--filter-bg: var(--sd-primary-light, #e5effa);display:inline-flex;align-items:center;justify-content:center;gap:6px;min-height:36px;padding:6px 10px;border:1px solid var(--sd-border, #dfe2e7);border-radius:8px;background:var(--sd-surface, #fff);color:var(--sd-text-secondary, #62676d);cursor:pointer;font:inherit;font-size:13px}.sd-import-filter--success{--filter-color: var(--sd-success, #2b7d39);--filter-bg: var(--sd-success-light, #e4f3e6)}.sd-import-filter--warning{--filter-color: var(--sd-warning-dark, #8a5700);--filter-bg: var(--sd-warning-light, #fff3d8)}.sd-import-filter--error{--filter-color: var(--sd-error, #bc3030);--filter-bg: var(--sd-error-light, #fce8e8)}.sd-import-filter[aria-pressed=true]{background:var(--filter-bg);color:var(--filter-color);border-color:var(--filter-color);font-weight:600}.sd-import-filter:hover{background:var(--filter-bg);color:var(--filter-color)}.sd-import-filter:focus-visible{outline:2px solid var(--sd-primary, #005cbb);outline-offset:2px}.sd-import-filter sd-icon,.sd-import-filter mat-icon{width:18px;height:18px;font-size:18px}.sd-import-filter-count{font-variant-numeric:tabular-nums;min-width:16px;text-align:center}.table-responsive{position:relative;max-height:50vh;overflow:auto;border:1px solid var(--sd-border, #dfe2e7);border-radius:8px}.table-responsive:focus-visible{outline:2px solid var(--sd-primary, #005cbb);outline-offset:2px}.sd-table{width:100%;border-collapse:separate;border-spacing:0;font-size:13px}.sd-table thead th{position:sticky;top:0;padding:10px 8px;white-space:nowrap;z-index:2;font-weight:600;background:var(--sd-surface-variant, #f6f7f9);border-bottom:1px solid var(--sd-border, #dfe2e7)}.sd-table thead th.c-sticky-left{z-index:3}.sd-table tbody td{padding:10px 8px;border-bottom:1px solid var(--sd-border, #e8eaed);vertical-align:top}.sd-table tbody tr:last-child td{border-bottom:0}.sd-table tbody tr{background:var(--sd-surface, #fff)}.sd-table tbody tr:hover{background:var(--sd-surface-variant, #f6f7f9)}.c-sticky-left{position:sticky;left:0;background:inherit;z-index:1;border-right:1px solid var(--sd-border, #e8eaed)}.sd-import-status{min-width:240px;width:240px;max-width:320px;white-space:normal!important;overflow-wrap:anywhere}.sd-import-issues summary{cursor:pointer;line-height:1.5}.sd-import-issues summary:focus-visible{outline:2px solid var(--sd-primary, #005cbb);outline-offset:2px;border-radius:3px}.sd-import-issues ul{padding-left:18px;margin:8px 0 0}.sd-import-issues li+li{margin-top:6px}.sd-import-issue-count{display:block;color:var(--sd-primary, #005cbb);font-size:12px;margin-top:4px}.sd-import-issue-label{font-weight:600}.sd-import-error,.sd-import-blocked{color:var(--sd-error, #bc3030)}.sd-import-warning{color:var(--sd-warning-dark, #8a5700)}.sd-import-valid,.sd-import-submitted{color:var(--sd-success, #2b7d39)}.sd-import-no-results{display:block;padding:24px 0;border:1px solid var(--sd-border, #dfe2e7);border-radius:8px}.sd-import-footer{padding:12px 0 0;border-top:1px solid var(--sd-border, #dfe2e7)}.sd-import-footer p{margin:0 0 10px;font-size:13px;line-height:1.5}.sd-import-footer-actions{display:flex;justify-content:flex-end;flex-wrap:wrap;gap:8px}.c-ellipsis{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:block;padding:0 4px}.c-mat-menu{border:none;min-width:unset;padding:0 8px}.sd-import-paginator{background:transparent}.sd-import-paginator ::ng-deep .mat-mdc-paginator-container{padding:0;min-height:48px}.sd-import-paginator ::ng-deep .mat-mdc-paginator-range-label{white-space:nowrap;margin:0 12px 0 0}@media(max-width:600px){.sd-import-entry{padding:4px 0 16px}.sd-import-file{flex-wrap:wrap;gap:8px}.sd-import-file>sd-button{margin-left:auto}.sd-import-filters{display:grid;grid-template-columns:repeat(2,minmax(0,1fr))}.sd-import-filter{min-height:40px;padding:6px;gap:5px}.sd-import-footer-actions sd-button{flex:1;min-width:0}.sd-import-footer-actions ::ng-deep sd-button button{width:100%;min-height:40px}}@media(max-width:400px){.sd-import-footer-actions{display:grid;grid-template-columns:minmax(0,1fr)}.sd-import-status{min-width:180px;width:180px}}\n"] }]
|
|
565
614
|
}], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i1$1.SdExcelService }, { type: i2.SdNotifyService }, { type: ColumnHiddenPipe }, { type: i4.SdLoadingService }], propDecorators: { option: [{
|
|
566
615
|
type: Input,
|
|
567
616
|
args: [{ required: true }]
|