@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.
Files changed (65) hide show
  1. package/components/data-state/index.d.ts +1 -0
  2. package/components/data-state/src/data-state.component.d.ts +41 -0
  3. package/components/import-excel/src/import-excel.component.d.ts +23 -1
  4. package/components/index.d.ts +1 -0
  5. package/components/quick-action/src/quick-action.component.d.ts +3 -1
  6. package/components/table/src/components/selector-action/selector-action.component.d.ts +11 -0
  7. package/components/table/src/table.component.d.ts +10 -1
  8. package/components/upload-file/src/upload-file.component.d.ts +5 -1
  9. package/fesm2022/sd-angular-core-components-button.mjs +2 -2
  10. package/fesm2022/sd-angular-core-components-button.mjs.map +1 -1
  11. package/fesm2022/sd-angular-core-components-data-state.mjs +72 -0
  12. package/fesm2022/sd-angular-core-components-data-state.mjs.map +1 -0
  13. package/fesm2022/sd-angular-core-components-form-generic.mjs +14 -14
  14. package/fesm2022/sd-angular-core-components-form-generic.mjs.map +1 -1
  15. package/fesm2022/sd-angular-core-components-import-excel.mjs +67 -18
  16. package/fesm2022/sd-angular-core-components-import-excel.mjs.map +1 -1
  17. package/fesm2022/sd-angular-core-components-inform.mjs +6 -6
  18. package/fesm2022/sd-angular-core-components-inform.mjs.map +1 -1
  19. package/fesm2022/sd-angular-core-components-query-bar.mjs +3 -3
  20. package/fesm2022/sd-angular-core-components-query-bar.mjs.map +1 -1
  21. package/fesm2022/sd-angular-core-components-query-builder.mjs +1 -1
  22. package/fesm2022/sd-angular-core-components-query-builder.mjs.map +1 -1
  23. package/fesm2022/sd-angular-core-components-quick-action.mjs +4 -2
  24. package/fesm2022/sd-angular-core-components-quick-action.mjs.map +1 -1
  25. package/fesm2022/sd-angular-core-components-stepper.mjs +2 -2
  26. package/fesm2022/sd-angular-core-components-stepper.mjs.map +1 -1
  27. package/fesm2022/sd-angular-core-components-table.mjs +119 -26
  28. package/fesm2022/sd-angular-core-components-table.mjs.map +1 -1
  29. package/fesm2022/sd-angular-core-components-tree.mjs +1 -1
  30. package/fesm2022/sd-angular-core-components-tree.mjs.map +1 -1
  31. package/fesm2022/sd-angular-core-components-upload-file.mjs +20 -6
  32. package/fesm2022/sd-angular-core-components-upload-file.mjs.map +1 -1
  33. package/fesm2022/sd-angular-core-components.mjs +1 -0
  34. package/fesm2022/sd-angular-core-components.mjs.map +1 -1
  35. package/fesm2022/sd-angular-core-forms-autocomplete.mjs +247 -59
  36. package/fesm2022/sd-angular-core-forms-autocomplete.mjs.map +1 -1
  37. package/fesm2022/sd-angular-core-forms-date-range.mjs +2 -2
  38. package/fesm2022/sd-angular-core-forms-date-range.mjs.map +1 -1
  39. package/fesm2022/sd-angular-core-forms-date.mjs +2 -2
  40. package/fesm2022/sd-angular-core-forms-date.mjs.map +1 -1
  41. package/fesm2022/sd-angular-core-forms-datetime.mjs +2 -2
  42. package/fesm2022/sd-angular-core-forms-datetime.mjs.map +1 -1
  43. package/fesm2022/sd-angular-core-forms-select.mjs +290 -67
  44. package/fesm2022/sd-angular-core-forms-select.mjs.map +1 -1
  45. package/fesm2022/sd-angular-core-i18n.mjs +155 -0
  46. package/fesm2022/sd-angular-core-i18n.mjs.map +1 -1
  47. package/fesm2022/sd-angular-core-modules-layout.mjs +6 -6
  48. package/fesm2022/sd-angular-core-modules-layout.mjs.map +1 -1
  49. package/fesm2022/sd-angular-core-services-confirm.mjs +43 -7
  50. package/fesm2022/sd-angular-core-services-confirm.mjs.map +1 -1
  51. package/fesm2022/sd-angular-core-services-notify.mjs +2 -2
  52. package/fesm2022/sd-angular-core-services-notify.mjs.map +1 -1
  53. package/fesm2022/sd-angular-core-utilities-read-state.mjs +87 -0
  54. package/fesm2022/sd-angular-core-utilities-read-state.mjs.map +1 -0
  55. package/fesm2022/sd-angular-core-utilities.mjs +1 -0
  56. package/fesm2022/sd-angular-core-utilities.mjs.map +1 -1
  57. package/forms/autocomplete/src/autocomplete.component.d.ts +13 -2
  58. package/forms/select/src/select.component.d.ts +11 -1
  59. package/i18n/src/en.d.ts +31 -0
  60. package/package.json +9 -1
  61. package/services/confirm/src/lib/components/dialog-confirm/dialog-confirm.component.d.ts +4 -0
  62. package/services/confirm/src/lib/confirm.service.d.ts +6 -0
  63. package/utilities/index.d.ts +1 -0
  64. package/utilities/read-state/index.d.ts +1 -0
  65. 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.pageIndex;
174
- const pageSize = this.#paginator.pageSize;
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.pageIndex = 0;
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', { value: item[column.field], list: column.values.join() });
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', { value: val ?? '', format: column.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', { value: val ?? '', format: column.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', { value: val ?? '', format: column.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', { value: val ?? '', format: column.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', { value: val ?? '', format: column.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.pageIndex = 0;
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(', ') || e.meta.warningMessages.join(', ') || this.#i18n.t('core.component.import-excel.success'))
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, TranslatePipe
562
- ], providers: [
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 }]