@masterteam/task-schedule 0.0.17 → 0.0.19

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.
@@ -1,6 +1,6 @@
1
1
  import { CommonModule } from '@angular/common';
2
2
  import * as i0 from '@angular/core';
3
- import { inject, Injectable, viewChild, input, signal, computed, effect, Component, linkedSignal, Injector, output, ViewChild } from '@angular/core';
3
+ import { inject, Injectable, viewChild, input, signal, computed, effect, Component, Injector, output, ViewChild } from '@angular/core';
4
4
  import * as i1$1 from '@syncfusion/ej2-angular-gantt';
5
5
  import { GanttModule, DayMarkersService, EditService, ExcelExportService, FilterService, CriticalPathService, PdfExportService, RowDDService, SelectionService, SortService, ToolbarService, ContextMenuService, ResizeService } from '@syncfusion/ej2-angular-gantt';
6
6
  import { switchMap, of, map, timeout, catchError, throwError, finalize, Subject, Subscription } from 'rxjs';
@@ -18,10 +18,8 @@ import { SelectField } from '@masterteam/components/select-field';
18
18
  import { ClientForm } from '@masterteam/forms/client-form';
19
19
  import { toSignal } from '@angular/core/rxjs-interop';
20
20
  import { CheckboxField } from '@masterteam/components/checkbox-field';
21
- import { DateField } from '@masterteam/components/date-field';
22
- import { EntityPreview } from '@masterteam/components/entities';
23
- import { NumberField } from '@masterteam/components/number-field';
24
21
  import { TextField } from '@masterteam/components/text-field';
22
+ import { Tree } from '@masterteam/components/tree';
25
23
 
26
24
  function normalizeTaskScheduleModelType(modelType) {
27
25
  const normalized = String(modelType ?? '')
@@ -133,6 +131,7 @@ const NATIVE_PROPERTY_ALIAS_TARGETS = {
133
131
  };
134
132
  const DEFAULT_MPP_REQUEST_TIMEOUT_MS$1 = 300_000;
135
133
  const DEFAULT_MPP_PREVIEW_MAX_ROWS = 400;
134
+ const DEFAULT_MPP_PREVIEW_MAX_RESPONSE_BYTES = 5_000_000;
136
135
  class TaskScheduleFetchService {
137
136
  http = inject(HttpClient);
138
137
  load(modelType, context) {
@@ -156,10 +155,11 @@ class TaskScheduleFetchService {
156
155
  'levels/{levelId}/{levelDataId}/schedule/mpp', { levelId, levelDataId });
157
156
  const formData = new FormData();
158
157
  formData.append('file', file, file.name);
159
- return this.readData(this.http
160
- .post(endpoint, formData)
161
- .pipe(timeout(this.resolveMppRequestTimeoutMs(context)))).pipe(map((payload) => ({
158
+ return this.http.post(endpoint, formData, { responseType: 'text' }).pipe(timeout(this.resolveMppRequestTimeoutMs(context)), map((responseText) => this.parseImportResponseText(responseText, context, file.name)), map((payload) => ({
162
159
  fileName: this.toNullableString(this.readObjectValue(payload, ['fileName', 'name'])) ?? file.name,
160
+ applied: payload === null ||
161
+ payload === undefined ||
162
+ this.isImportAppliedMarker(payload),
163
163
  tasks: this.mapLooseTasks(this.extractTaskRows(payload), [], context.langCode === 'ar' ? 'ar' : 'en', new Map(), this.resolveMppPreviewMaxRows(context)),
164
164
  raw: context.mppKeepRawPayload ? payload : undefined,
165
165
  })));
@@ -708,7 +708,12 @@ class TaskScheduleFetchService {
708
708
  '');
709
709
  }
710
710
  readData(request$) {
711
- return request$.pipe(map((response) => response.data), catchError((error) => {
711
+ return request$.pipe(map((response) => {
712
+ if (response === null || response === undefined) {
713
+ return response;
714
+ }
715
+ return response.data;
716
+ }), catchError((error) => {
712
717
  const message = error?.error?.message ??
713
718
  error?.message ??
714
719
  error?.statusText ??
@@ -740,6 +745,42 @@ class TaskScheduleFetchService {
740
745
  ? Math.floor(value) + 1
741
746
  : DEFAULT_MPP_PREVIEW_MAX_ROWS + 1;
742
747
  }
748
+ resolveMppPreviewMaxResponseBytes(context) {
749
+ const value = Number(context.mppPreviewMaxResponseBytes);
750
+ return Number.isFinite(value) && value > 0
751
+ ? Math.floor(value)
752
+ : DEFAULT_MPP_PREVIEW_MAX_RESPONSE_BYTES;
753
+ }
754
+ parseImportResponseText(responseText, context, fileName) {
755
+ const text = String(responseText ?? '').trim();
756
+ if (!text) {
757
+ return null;
758
+ }
759
+ if (text.length > this.resolveMppPreviewMaxResponseBytes(context)) {
760
+ return {
761
+ fileName,
762
+ applied: true,
763
+ };
764
+ }
765
+ try {
766
+ const parsed = JSON.parse(text);
767
+ if (this.isObject(parsed) &&
768
+ Object.prototype.hasOwnProperty.call(parsed, 'data')) {
769
+ return parsed['data'];
770
+ }
771
+ return parsed;
772
+ }
773
+ catch {
774
+ return {
775
+ fileName,
776
+ applied: true,
777
+ };
778
+ }
779
+ }
780
+ isImportAppliedMarker(payload) {
781
+ return (this.isObject(payload) &&
782
+ (payload['applied'] === true || payload['imported'] === true));
783
+ }
743
784
  readEndpoint(context, key) {
744
785
  const value = context.endpoints?.[key];
745
786
  return typeof value === 'string' && value.trim() ? value.trim() : null;
@@ -1762,7 +1803,12 @@ class TaskScheduleActionService {
1762
1803
  return assigneeId === null ? [] : [{ resourceId: assigneeId }];
1763
1804
  }
1764
1805
  readData(request$) {
1765
- return request$.pipe(map((response) => response.data), catchError((error) => {
1806
+ return request$.pipe(map((response) => {
1807
+ if (response === null || response === undefined) {
1808
+ return response;
1809
+ }
1810
+ return response.data;
1811
+ }), catchError((error) => {
1766
1812
  const message = error?.error?.message ??
1767
1813
  error?.message ??
1768
1814
  error?.statusText ??
@@ -2630,14 +2676,6 @@ class TaskScheduleImportDialog {
2630
2676
  fetchService = inject(TaskScheduleFetchService);
2631
2677
  actionService = inject(TaskScheduleActionService);
2632
2678
  transloco = inject(TranslocoService);
2633
- selectionCellTpl = viewChild.required('selectionCellTpl');
2634
- taskCellTpl = viewChild.required('taskCellTpl');
2635
- startDateCellTpl = viewChild.required('startDateCellTpl');
2636
- finishDateCellTpl = viewChild.required('finishDateCellTpl');
2637
- actualStartDateCellTpl = viewChild.required('actualStartDateCellTpl');
2638
- actualFinishDateCellTpl = viewChild.required('actualFinishDateCellTpl');
2639
- assignedToCellTpl = viewChild.required('assignedToCellTpl');
2640
- progressCellTpl = viewChild.required('progressCellTpl');
2641
2679
  context = input(null, ...(ngDevMode ? [{ debugName: "context" }] : /* istanbul ignore next */ []));
2642
2680
  maxRows = input(400, ...(ngDevMode ? [{ debugName: "maxRows" }] : /* istanbul ignore next */ []));
2643
2681
  selectedFile = signal(null, ...(ngDevMode ? [{ debugName: "selectedFile" }] : /* istanbul ignore next */ []));
@@ -2645,6 +2683,7 @@ class TaskScheduleImportDialog {
2645
2683
  loadingApply = signal(false, ...(ngDevMode ? [{ debugName: "loadingApply" }] : /* istanbul ignore next */ []));
2646
2684
  importResult = signal(null, ...(ngDevMode ? [{ debugName: "importResult" }] : /* istanbul ignore next */ []));
2647
2685
  selectedTaskKeys = signal(new Set(), ...(ngDevMode ? [{ debugName: "selectedTaskKeys" }] : /* istanbul ignore next */ []));
2686
+ treeSelection = signal([], ...(ngDevMode ? [{ debugName: "treeSelection" }] : /* istanbul ignore next */ []));
2648
2687
  errorMessage = signal(null, ...(ngDevMode ? [{ debugName: "errorMessage" }] : /* istanbul ignore next */ []));
2649
2688
  activeLang = toSignal(this.transloco.langChanges$, {
2650
2689
  initialValue: this.transloco.getActiveLang(),
@@ -2747,62 +2786,58 @@ class TaskScheduleImportDialog {
2747
2786
  !this.loadingApply() &&
2748
2787
  !this.loadingImport(), ...(ngDevMode ? [{ debugName: "canApply" }] : /* istanbul ignore next */ []));
2749
2788
  previewPageSize = computed(() => Math.min(Math.max(this.previewRows().length, 1), 50), ...(ngDevMode ? [{ debugName: "previewPageSize" }] : /* istanbul ignore next */ []));
2750
- previewColumns = linkedSignal(() => [
2751
- {
2752
- key: 'selected',
2753
- label: '',
2754
- type: 'custom',
2755
- customCellTpl: this.selectionCellTpl(),
2756
- },
2757
- {
2758
- key: 'task',
2759
- label: this.labels().task,
2760
- type: 'custom',
2761
- customCellTpl: this.taskCellTpl(),
2762
- },
2763
- {
2764
- key: 'startDate',
2765
- label: this.labels().startDate,
2766
- type: 'custom',
2767
- customCellTpl: this.startDateCellTpl(),
2768
- },
2769
- {
2770
- key: 'finishDate',
2771
- label: this.labels().finishDate,
2772
- type: 'custom',
2773
- customCellTpl: this.finishDateCellTpl(),
2774
- },
2775
- {
2776
- key: 'actualStartDate',
2777
- label: this.labels().actualStartDate,
2778
- type: 'custom',
2779
- customCellTpl: this.actualStartDateCellTpl(),
2780
- },
2781
- {
2782
- key: 'actualFinishDate',
2783
- label: this.labels().actualFinishDate,
2784
- type: 'custom',
2785
- customCellTpl: this.actualFinishDateCellTpl(),
2786
- },
2787
- {
2788
- key: 'assignedTo',
2789
- label: this.labels().assignedTo,
2790
- type: 'custom',
2791
- customCellTpl: this.assignedToCellTpl(),
2792
- },
2793
- {
2794
- key: 'progress',
2795
- label: this.labels().progress,
2796
- type: 'custom',
2797
- customCellTpl: this.progressCellTpl(),
2798
- },
2799
- ], ...(ngDevMode ? [{ debugName: "previewColumns" }] : /* istanbul ignore next */ []));
2789
+ previewTreeNodes = computed(() => {
2790
+ const maxRows = Math.max(1, this.maxRows());
2791
+ let remaining = maxRows;
2792
+ const mapNode = (task, depth, path) => {
2793
+ if (remaining <= 0) {
2794
+ return null;
2795
+ }
2796
+ remaining -= 1;
2797
+ const key = this.resolveTaskKey(task, path);
2798
+ const children = [];
2799
+ const childTasks = this.resolveChildTasks(task);
2800
+ for (let index = 0; index < childTasks.length; index += 1) {
2801
+ const child = mapNode(childTasks[index], depth + 1, `${path}.${index}`);
2802
+ if (child) {
2803
+ children.push(child);
2804
+ }
2805
+ if (remaining <= 0) {
2806
+ break;
2807
+ }
2808
+ }
2809
+ return {
2810
+ key,
2811
+ label: this.toTreeNodeLabel(task),
2812
+ icon: this.resolveTreeNodeIcon(task),
2813
+ expanded: depth < 2,
2814
+ data: {
2815
+ rowKey: key,
2816
+ task,
2817
+ },
2818
+ children,
2819
+ };
2820
+ };
2821
+ const nodes = [];
2822
+ const tasks = this.importResult()?.tasks ?? [];
2823
+ for (let index = 0; index < tasks.length; index += 1) {
2824
+ const node = mapNode(tasks[index], 0, `root.${index}`);
2825
+ if (node) {
2826
+ nodes.push(node);
2827
+ }
2828
+ if (remaining <= 0) {
2829
+ break;
2830
+ }
2831
+ }
2832
+ return nodes;
2833
+ }, ...(ngDevMode ? [{ debugName: "previewTreeNodes" }] : /* istanbul ignore next */ []));
2800
2834
  onFileChanged(event) {
2801
2835
  const input = event.target;
2802
2836
  const file = input?.files?.item(0) ?? null;
2803
2837
  this.selectedFile.set(file);
2804
2838
  this.importResult.set(null);
2805
2839
  this.selectedTaskKeys.set(new Set());
2840
+ this.treeSelection.set([]);
2806
2841
  this.errorMessage.set(null);
2807
2842
  }
2808
2843
  importFile() {
@@ -2834,6 +2869,10 @@ class TaskScheduleImportDialog {
2834
2869
  .pipe(finalize(() => this.loadingImport.set(false)))
2835
2870
  .subscribe({
2836
2871
  next: (result) => {
2872
+ if (result.applied) {
2873
+ this.ref.close(true);
2874
+ return;
2875
+ }
2837
2876
  this.importResult.set(result);
2838
2877
  this.selectAllRows(true);
2839
2878
  },
@@ -2854,12 +2893,33 @@ class TaskScheduleImportDialog {
2854
2893
  }
2855
2894
  selectAllRows(checked) {
2856
2895
  const next = new Set();
2896
+ const nextSelection = [];
2857
2897
  if (checked) {
2858
- this.previewRows().forEach(({ key }) => {
2859
- next.add(key);
2898
+ this.collectTreeNodes(this.previewTreeNodes()).forEach((node) => {
2899
+ if (node.key) {
2900
+ next.add(node.key);
2901
+ nextSelection.push(node);
2902
+ }
2860
2903
  });
2861
2904
  }
2862
2905
  this.selectedTaskKeys.set(next);
2906
+ this.treeSelection.set(nextSelection);
2907
+ }
2908
+ onTreeSelectionChange(selection) {
2909
+ const nodes = Array.isArray(selection)
2910
+ ? selection
2911
+ : selection
2912
+ ? [selection]
2913
+ : [];
2914
+ const next = new Set();
2915
+ nodes.forEach((node) => {
2916
+ const rowKey = this.readRowKeyFromTreeNode(node);
2917
+ if (rowKey) {
2918
+ next.add(rowKey);
2919
+ }
2920
+ });
2921
+ this.treeSelection.set(nodes);
2922
+ this.selectedTaskKeys.set(next);
2863
2923
  }
2864
2924
  applyImport(overrideCurrent) {
2865
2925
  if (this.loadingImport() || this.loadingApply()) {
@@ -3106,6 +3166,44 @@ class TaskScheduleImportDialog {
3106
3166
  const base = task.guid ?? task.id ?? path;
3107
3167
  return `${String(base)}::${path}`;
3108
3168
  }
3169
+ collectTreeNodes(nodes) {
3170
+ const output = [];
3171
+ const walk = (items) => {
3172
+ items.forEach((node) => {
3173
+ output.push(node);
3174
+ if (node.children?.length) {
3175
+ walk(node.children);
3176
+ }
3177
+ });
3178
+ };
3179
+ walk(nodes);
3180
+ return output;
3181
+ }
3182
+ readRowKeyFromTreeNode(node) {
3183
+ const data = node.data;
3184
+ return typeof data?.rowKey === 'string' && data.rowKey.trim()
3185
+ ? data.rowKey
3186
+ : null;
3187
+ }
3188
+ toTreeNodeLabel(task) {
3189
+ const title = String(task.title ?? task.name ?? '').trim();
3190
+ const type = String(task.type ?? task.typeLabel ?? task.typeLable ?? '').trim() ||
3191
+ (task.isMilestone ? 'Milestone' : task['isSummary'] ? 'Summary' : 'Task');
3192
+ return `${title || type}`;
3193
+ }
3194
+ resolveTreeNodeIcon(task) {
3195
+ if (task.isMilestone) {
3196
+ return 'map.flag-02';
3197
+ }
3198
+ if (task['isSummary'] || this.resolveChildTasks(task).length) {
3199
+ return 'files.folder-01';
3200
+ }
3201
+ return 'custom.task';
3202
+ }
3203
+ formatPreviewDate(value) {
3204
+ const normalized = this.toApiDate(value);
3205
+ return normalized ?? '';
3206
+ }
3109
3207
  resolveAssignedTo(task) {
3110
3208
  const assignedTo = task.assignedTo;
3111
3209
  if ((typeof assignedTo === 'string' || typeof assignedTo === 'number') &&
@@ -3250,24 +3348,20 @@ class TaskScheduleImportDialog {
3250
3348
  this.errorMessage.set(message);
3251
3349
  }
3252
3350
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: TaskScheduleImportDialog, deps: [], target: i0.ɵɵFactoryTarget.Component });
3253
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: TaskScheduleImportDialog, isStandalone: true, selector: "mt-task-schedule-import-dialog", inputs: { context: { classPropertyName: "context", publicName: "context", isSignal: true, isRequired: false, transformFunction: null }, maxRows: { classPropertyName: "maxRows", publicName: "maxRows", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "selectionCellTpl", first: true, predicate: ["selectionCellTpl"], descendants: true, isSignal: true }, { propertyName: "taskCellTpl", first: true, predicate: ["taskCellTpl"], descendants: true, isSignal: true }, { propertyName: "startDateCellTpl", first: true, predicate: ["startDateCellTpl"], descendants: true, isSignal: true }, { propertyName: "finishDateCellTpl", first: true, predicate: ["finishDateCellTpl"], descendants: true, isSignal: true }, { propertyName: "actualStartDateCellTpl", first: true, predicate: ["actualStartDateCellTpl"], descendants: true, isSignal: true }, { propertyName: "actualFinishDateCellTpl", first: true, predicate: ["actualFinishDateCellTpl"], descendants: true, isSignal: true }, { propertyName: "assignedToCellTpl", first: true, predicate: ["assignedToCellTpl"], descendants: true, isSignal: true }, { propertyName: "progressCellTpl", first: true, predicate: ["progressCellTpl"], descendants: true, isSignal: true }], ngImport: i0, template: "<div\n [class]=\"modal.contentClass\"\n [attr.dir]=\"isRtl() ? 'rtl' : 'ltr'\"\n class=\"flex h-full min-h-0 flex-col gap-4 !overflow-y-hidden p-5\"\n>\n <div class=\"rounded-lg border border-surface-200 bg-surface-50 p-4\">\n <div class=\"grid gap-3 lg:grid-cols-[minmax(0,1fr)_auto_auto] lg:items-end\">\n <input\n #fileInput\n class=\"hidden\"\n type=\"file\"\n accept=\".mpp,.xer\"\n (change)=\"onFileChanged($event)\"\n />\n\n <mt-text-field\n [ngModel]=\"selectedFile()?.name ?? ''\"\n [readonly]=\"true\"\n [placeholder]=\"labels().selectFile\"\n />\n\n <mt-button\n [label]=\"labels().selectFile\"\n severity=\"secondary\"\n variant=\"outlined\"\n [disabled]=\"loadingImport() || loadingApply()\"\n (onClick)=\"fileInput.click()\"\n />\n\n <mt-button\n [label]=\"labels().startImport\"\n [disabled]=\"!canImport()\"\n [loading]=\"loadingImport()\"\n (onClick)=\"importFile()\"\n />\n </div>\n </div>\n\n @if (errorMessage()) {\n <div\n class=\"rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700\"\n >\n {{ errorMessage() }}\n </div>\n }\n\n <div class=\"min-h-0 flex-1 overflow-y-auto\">\n @if (importResult()) {\n @if (!previewRows().length) {\n <div\n class=\"rounded-lg border border-amber-200 bg-amber-50 px-4 py-3 text-sm text-amber-800\"\n >\n {{ labels().noTasks }}\n </div>\n } @else {\n <div class=\"rounded-lg border border-surface-200 bg-surface-0\">\n <div\n class=\"flex flex-wrap items-center justify-between gap-3 border-b border-surface-200 bg-surface-50 px-4 py-3\"\n >\n <div class=\"flex items-center gap-3\">\n <mt-checkbox-field\n [ngModel]=\"allSelected()\"\n (ngModelChange)=\"selectAllRows(!!$event)\"\n />\n <span class=\"text-sm text-surface-600\">\n {{ selectedTaskKeys().size }} / {{ previewRows().length }}\n </span>\n </div>\n\n @if (isTruncated()) {\n <p class=\"text-xs text-surface-500\">\n {{ labels().rowsLimited }}\n </p>\n }\n </div>\n\n <div class=\"px-2 py-2\">\n @defer (on idle) {\n <mt-table\n [data]=\"previewRows()\"\n [columns]=\"previewColumns()\"\n dataKey=\"key\"\n storageKey=\"task-schedule-import-preview-table\"\n size=\"small\"\n [stripedRows]=\"true\"\n [showGridlines]=\"true\"\n [pageSize]=\"previewPageSize()\"\n />\n } @placeholder {\n <div class=\"space-y-2 p-2\">\n <div class=\"h-9 rounded bg-surface-100\"></div>\n <div class=\"h-9 rounded bg-surface-100\"></div>\n <div class=\"h-9 rounded bg-surface-100\"></div>\n </div>\n }\n </div>\n </div>\n }\n }\n </div>\n</div>\n\n<div [class]=\"modal.footerClass\">\n <mt-button\n [label]=\"labels().cancel\"\n variant=\"outlined\"\n [disabled]=\"loadingImport() || loadingApply()\"\n (onClick)=\"close()\"\n />\n\n <mt-button\n [label]=\"labels().replace\"\n [disabled]=\"!canApply()\"\n [loading]=\"loadingApply()\"\n (onClick)=\"applyImport(true)\"\n />\n\n <mt-button\n [label]=\"labels().append\"\n severity=\"secondary\"\n [disabled]=\"!canApply()\"\n [loading]=\"loadingApply()\"\n (onClick)=\"applyImport(false)\"\n />\n</div>\n\n<ng-template #selectionCellTpl let-row>\n <div class=\"flex items-center justify-center\">\n <mt-checkbox-field\n [ngModel]=\"selectedTaskKeys().has(row.key)\"\n (ngModelChange)=\"toggleRowSelection(row.key, !!$event)\"\n />\n </div>\n</ng-template>\n\n<ng-template #taskCellTpl let-row>\n <div class=\"min-w-0\" [style.paddingInlineStart.px]=\"row.depth * 18\">\n <mt-text-field\n [ngModel]=\"row.task.title || row.task.name || ''\"\n (ngModelChange)=\"onTaskTitleChanged(row.key, $event)\"\n />\n </div>\n</ng-template>\n\n<ng-template #startDateCellTpl let-row>\n <mt-date-field\n [ngModel]=\"toDateFieldValue(row.task.startDate)\"\n [showClear]=\"true\"\n (ngModelChange)=\"onTaskDateChanged(row.key, 'startDate', $event)\"\n />\n</ng-template>\n\n<ng-template #finishDateCellTpl let-row>\n <mt-date-field\n [ngModel]=\"toDateFieldValue(row.task.finishDate)\"\n [showClear]=\"true\"\n (ngModelChange)=\"onTaskDateChanged(row.key, 'finishDate', $event)\"\n />\n</ng-template>\n\n<ng-template #actualStartDateCellTpl let-row>\n <mt-date-field\n [ngModel]=\"\n toDateFieldValue(row.task.actualStartDate || row.task.actualStart)\n \"\n [showClear]=\"true\"\n (ngModelChange)=\"onTaskDateChanged(row.key, 'actualStartDate', $event)\"\n />\n</ng-template>\n\n<ng-template #actualFinishDateCellTpl let-row>\n <mt-date-field\n [ngModel]=\"\n toDateFieldValue(row.task.actualFinishDate || row.task.actualFinish)\n \"\n [showClear]=\"true\"\n (ngModelChange)=\"onTaskDateChanged(row.key, 'actualFinishDate', $event)\"\n />\n</ng-template>\n\n<ng-template #assignedToCellTpl let-row>\n <div class=\"min-w-0\">\n @if (resourceOptions().length) {\n <div class=\"space-y-2\">\n <mt-select-field\n [ngModel]=\"resolveAssignedValue(row.task)\"\n [options]=\"resourceOptions()\"\n optionLabel=\"label\"\n optionValue=\"id\"\n [showClear]=\"true\"\n [hasPlaceholderPrefix]=\"false\"\n [placeholder]=\"labels().assignedTo\"\n (ngModelChange)=\"onTaskAssignedToChanged(row.key, $event)\"\n />\n @if (row.task.assignedTo) {\n <mt-entity-preview [data]=\"toAssignedEntity(row.task)\" />\n }\n </div>\n } @else if (row.task.assignedTo) {\n <mt-entity-preview [data]=\"toAssignedEntity(row.task)\" />\n } @else {\n <span class=\"text-slate-400\">-</span>\n }\n </div>\n</ng-template>\n\n<ng-template #progressCellTpl let-row>\n <div class=\"space-y-2\">\n <mt-number-field\n [ngModel]=\"normalizeProgress(row.task.progress)\"\n [min]=\"0\"\n [max]=\"100\"\n [useGrouping]=\"false\"\n [maxFractionDigits]=\"0\"\n (ngModelChange)=\"onTaskProgressChanged(row.key, $event)\"\n />\n <mt-entity-preview [data]=\"toProgressEntity(row.task)\" />\n </div>\n</ng-template>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: CheckboxField, selector: "mt-checkbox-field", inputs: ["label", "labelPosition", "placeholder", "readonly", "pInputs", "required"], outputs: ["onChange"] }, { kind: "component", type: DateField, selector: "mt-date-field", inputs: ["field", "hint", "label", "placeholder", "class", "readonly", "showIcon", "showClear", "showTime", "pInputs", "required"] }, { kind: "component", type: EntityPreview, selector: "mt-entity-preview", inputs: ["data", "attachmentShape"] }, { kind: "component", type: NumberField, selector: "mt-number-field", inputs: ["field", "hint", "label", "placeholder", "class", "readonly", "pInputs", "format", "useGrouping", "maxFractionDigits", "min", "max", "required"] }, { kind: "component", type: SelectField, selector: "mt-select-field", inputs: ["field", "hint", "label", "placeholder", "hasPlaceholderPrefix", "class", "readonly", "pInputs", "options", "optionValue", "optionLabel", "filter", "filterBy", "dataKey", "showClear", "clearAfterSelect", "required", "group", "size", "optionGroupLabel", "optionGroupChildren", "loading", "optionIcon", "optionIconColor", "optionIconShape", "optionAvatarShape", "optionGroupIcon", "optionGroupIconColor", "optionGroupIconShape", "optionGroupAvatarShape", "markCurrentUser"], outputs: ["onChange"] }, { kind: "component", type: TextField, selector: "mt-text-field", inputs: ["field", "hint", "label", "placeholder", "class", "type", "readonly", "pInputs", "required", "maxLength", "icon", "iconPosition"] }, { kind: "ngmodule", type: TranslocoModule }], deferBlockDependencies: [() => [import('@masterteam/components/table').then(m => m.Table)]] });
3351
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: TaskScheduleImportDialog, isStandalone: true, selector: "mt-task-schedule-import-dialog", inputs: { context: { classPropertyName: "context", publicName: "context", isSignal: true, isRequired: false, transformFunction: null }, maxRows: { classPropertyName: "maxRows", publicName: "maxRows", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div\r\n [class]=\"modal.contentClass\"\r\n [attr.dir]=\"isRtl() ? 'rtl' : 'ltr'\"\r\n class=\"flex h-full min-h-0 flex-col gap-4 !overflow-y-hidden p-5\"\r\n>\r\n <div class=\"rounded-lg border border-surface-200 bg-surface-50 p-4\">\r\n <div class=\"grid gap-3 lg:grid-cols-[minmax(0,1fr)_auto_auto] lg:items-end\">\r\n <input\r\n #fileInput\r\n class=\"hidden\"\r\n type=\"file\"\r\n accept=\".mpp,.xer\"\r\n (change)=\"onFileChanged($event)\"\r\n />\r\n\r\n <mt-text-field\r\n [ngModel]=\"selectedFile()?.name ?? ''\"\r\n [readonly]=\"true\"\r\n [placeholder]=\"labels().selectFile\"\r\n />\r\n\r\n <mt-button\r\n [label]=\"labels().selectFile\"\r\n severity=\"secondary\"\r\n variant=\"outlined\"\r\n [disabled]=\"loadingImport() || loadingApply()\"\r\n (onClick)=\"fileInput.click()\"\r\n />\r\n\r\n <mt-button\r\n [label]=\"labels().startImport\"\r\n [disabled]=\"!canImport()\"\r\n [loading]=\"loadingImport()\"\r\n (onClick)=\"importFile()\"\r\n />\r\n </div>\r\n </div>\r\n\r\n @if (errorMessage()) {\r\n <div\r\n class=\"rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700\"\r\n >\r\n {{ errorMessage() }}\r\n </div>\r\n }\r\n\r\n <div class=\"min-h-0 flex-1 overflow-y-auto\">\r\n @if (importResult()) {\r\n @if (!previewRows().length) {\r\n <div\r\n class=\"rounded-lg border border-amber-200 bg-amber-50 px-4 py-3 text-sm text-amber-800\"\r\n >\r\n {{ labels().noTasks }}\r\n </div>\r\n } @else {\r\n <div class=\"rounded-lg border border-surface-200 bg-surface-0\">\r\n <div\r\n class=\"flex flex-wrap items-center justify-between gap-3 border-b border-surface-200 bg-surface-50 px-4 py-3\"\r\n >\r\n <div class=\"flex items-center gap-3\">\r\n <mt-checkbox-field\r\n [ngModel]=\"allSelected()\"\r\n (ngModelChange)=\"selectAllRows(!!$event)\"\r\n />\r\n <span class=\"text-sm text-surface-600\">\r\n {{ selectedTaskKeys().size }} / {{ previewRows().length }}\r\n </span>\r\n </div>\r\n\r\n @if (isTruncated()) {\r\n <p class=\"text-xs text-surface-500\">\r\n {{ labels().rowsLimited }}\r\n </p>\r\n }\r\n </div>\r\n\r\n <div class=\"px-2 py-2\">\r\n <mt-tree\r\n [value]=\"previewTreeNodes()\"\r\n [selection]=\"treeSelection()\"\r\n selectionMode=\"checkbox\"\r\n [propagateSelectionUp]=\"false\"\r\n [propagateSelectionDown]=\"false\"\r\n [filter]=\"true\"\r\n scrollHeight=\"28rem\"\r\n styleClass=\"task-schedule-import-tree\"\r\n (selectionChange)=\"onTreeSelectionChange($event)\"\r\n />\r\n </div>\r\n </div>\r\n }\r\n }\r\n </div>\r\n</div>\r\n\r\n<div [class]=\"modal.footerClass\">\r\n <mt-button\r\n [label]=\"labels().cancel\"\r\n variant=\"outlined\"\r\n [disabled]=\"loadingImport() || loadingApply()\"\r\n (onClick)=\"close()\"\r\n />\r\n\r\n <mt-button\r\n [label]=\"labels().replace\"\r\n [disabled]=\"!canApply()\"\r\n [loading]=\"loadingApply()\"\r\n (onClick)=\"applyImport(true)\"\r\n />\r\n\r\n <mt-button\r\n [label]=\"labels().append\"\r\n severity=\"secondary\"\r\n [disabled]=\"!canApply()\"\r\n [loading]=\"loadingApply()\"\r\n (onClick)=\"applyImport(false)\"\r\n />\r\n</div>\r\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: CheckboxField, selector: "mt-checkbox-field", inputs: ["label", "labelPosition", "placeholder", "readonly", "pInputs", "required"], outputs: ["onChange"] }, { kind: "component", type: TextField, selector: "mt-text-field", inputs: ["field", "hint", "label", "placeholder", "class", "type", "readonly", "pInputs", "required", "maxLength", "icon", "iconPosition"] }, { kind: "component", type: Tree, selector: "mt-tree", inputs: ["value", "selection", "selectionMode", "nodeIcon", "propagateSelectionUp", "propagateSelectionDown", "checkAllChildren", "loading", "emptyMessage", "checkAllLabel", "filterPlaceholder", "dataKey", "filter", "filterMode", "virtualScroll", "virtualScrollItemSize", "scrollHeight", "styleClass", "style", "pInputs", "nodeActions", "nodeContextmenuActions", "contextMenuSelection"], outputs: ["selectionChange", "contextMenuSelectionChange", "action"] }, { kind: "ngmodule", type: TranslocoModule }] });
3254
3352
  }
3255
- i0.ɵɵngDeclareClassMetadataAsync({ minVersion: "18.0.0", version: "21.2.8", ngImport: i0, type: TaskScheduleImportDialog, resolveDeferredDeps: () => [import('@masterteam/components/table').then(m => m.Table)], resolveMetadata: Table => ({ decorators: [{
3256
- type: Component,
3257
- args: [{ selector: 'mt-task-schedule-import-dialog', standalone: true, imports: [
3258
- CommonModule,
3259
- FormsModule,
3260
- Button,
3261
- CheckboxField,
3262
- DateField,
3263
- EntityPreview,
3264
- NumberField,
3265
- SelectField,
3266
- Table,
3267
- TextField,
3268
- TranslocoModule,
3269
- ], template: "<div\n [class]=\"modal.contentClass\"\n [attr.dir]=\"isRtl() ? 'rtl' : 'ltr'\"\n class=\"flex h-full min-h-0 flex-col gap-4 !overflow-y-hidden p-5\"\n>\n <div class=\"rounded-lg border border-surface-200 bg-surface-50 p-4\">\n <div class=\"grid gap-3 lg:grid-cols-[minmax(0,1fr)_auto_auto] lg:items-end\">\n <input\n #fileInput\n class=\"hidden\"\n type=\"file\"\n accept=\".mpp,.xer\"\n (change)=\"onFileChanged($event)\"\n />\n\n <mt-text-field\n [ngModel]=\"selectedFile()?.name ?? ''\"\n [readonly]=\"true\"\n [placeholder]=\"labels().selectFile\"\n />\n\n <mt-button\n [label]=\"labels().selectFile\"\n severity=\"secondary\"\n variant=\"outlined\"\n [disabled]=\"loadingImport() || loadingApply()\"\n (onClick)=\"fileInput.click()\"\n />\n\n <mt-button\n [label]=\"labels().startImport\"\n [disabled]=\"!canImport()\"\n [loading]=\"loadingImport()\"\n (onClick)=\"importFile()\"\n />\n </div>\n </div>\n\n @if (errorMessage()) {\n <div\n class=\"rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700\"\n >\n {{ errorMessage() }}\n </div>\n }\n\n <div class=\"min-h-0 flex-1 overflow-y-auto\">\n @if (importResult()) {\n @if (!previewRows().length) {\n <div\n class=\"rounded-lg border border-amber-200 bg-amber-50 px-4 py-3 text-sm text-amber-800\"\n >\n {{ labels().noTasks }}\n </div>\n } @else {\n <div class=\"rounded-lg border border-surface-200 bg-surface-0\">\n <div\n class=\"flex flex-wrap items-center justify-between gap-3 border-b border-surface-200 bg-surface-50 px-4 py-3\"\n >\n <div class=\"flex items-center gap-3\">\n <mt-checkbox-field\n [ngModel]=\"allSelected()\"\n (ngModelChange)=\"selectAllRows(!!$event)\"\n />\n <span class=\"text-sm text-surface-600\">\n {{ selectedTaskKeys().size }} / {{ previewRows().length }}\n </span>\n </div>\n\n @if (isTruncated()) {\n <p class=\"text-xs text-surface-500\">\n {{ labels().rowsLimited }}\n </p>\n }\n </div>\n\n <div class=\"px-2 py-2\">\n @defer (on idle) {\n <mt-table\n [data]=\"previewRows()\"\n [columns]=\"previewColumns()\"\n dataKey=\"key\"\n storageKey=\"task-schedule-import-preview-table\"\n size=\"small\"\n [stripedRows]=\"true\"\n [showGridlines]=\"true\"\n [pageSize]=\"previewPageSize()\"\n />\n } @placeholder {\n <div class=\"space-y-2 p-2\">\n <div class=\"h-9 rounded bg-surface-100\"></div>\n <div class=\"h-9 rounded bg-surface-100\"></div>\n <div class=\"h-9 rounded bg-surface-100\"></div>\n </div>\n }\n </div>\n </div>\n }\n }\n </div>\n</div>\n\n<div [class]=\"modal.footerClass\">\n <mt-button\n [label]=\"labels().cancel\"\n variant=\"outlined\"\n [disabled]=\"loadingImport() || loadingApply()\"\n (onClick)=\"close()\"\n />\n\n <mt-button\n [label]=\"labels().replace\"\n [disabled]=\"!canApply()\"\n [loading]=\"loadingApply()\"\n (onClick)=\"applyImport(true)\"\n />\n\n <mt-button\n [label]=\"labels().append\"\n severity=\"secondary\"\n [disabled]=\"!canApply()\"\n [loading]=\"loadingApply()\"\n (onClick)=\"applyImport(false)\"\n />\n</div>\n\n<ng-template #selectionCellTpl let-row>\n <div class=\"flex items-center justify-center\">\n <mt-checkbox-field\n [ngModel]=\"selectedTaskKeys().has(row.key)\"\n (ngModelChange)=\"toggleRowSelection(row.key, !!$event)\"\n />\n </div>\n</ng-template>\n\n<ng-template #taskCellTpl let-row>\n <div class=\"min-w-0\" [style.paddingInlineStart.px]=\"row.depth * 18\">\n <mt-text-field\n [ngModel]=\"row.task.title || row.task.name || ''\"\n (ngModelChange)=\"onTaskTitleChanged(row.key, $event)\"\n />\n </div>\n</ng-template>\n\n<ng-template #startDateCellTpl let-row>\n <mt-date-field\n [ngModel]=\"toDateFieldValue(row.task.startDate)\"\n [showClear]=\"true\"\n (ngModelChange)=\"onTaskDateChanged(row.key, 'startDate', $event)\"\n />\n</ng-template>\n\n<ng-template #finishDateCellTpl let-row>\n <mt-date-field\n [ngModel]=\"toDateFieldValue(row.task.finishDate)\"\n [showClear]=\"true\"\n (ngModelChange)=\"onTaskDateChanged(row.key, 'finishDate', $event)\"\n />\n</ng-template>\n\n<ng-template #actualStartDateCellTpl let-row>\n <mt-date-field\n [ngModel]=\"\n toDateFieldValue(row.task.actualStartDate || row.task.actualStart)\n \"\n [showClear]=\"true\"\n (ngModelChange)=\"onTaskDateChanged(row.key, 'actualStartDate', $event)\"\n />\n</ng-template>\n\n<ng-template #actualFinishDateCellTpl let-row>\n <mt-date-field\n [ngModel]=\"\n toDateFieldValue(row.task.actualFinishDate || row.task.actualFinish)\n \"\n [showClear]=\"true\"\n (ngModelChange)=\"onTaskDateChanged(row.key, 'actualFinishDate', $event)\"\n />\n</ng-template>\n\n<ng-template #assignedToCellTpl let-row>\n <div class=\"min-w-0\">\n @if (resourceOptions().length) {\n <div class=\"space-y-2\">\n <mt-select-field\n [ngModel]=\"resolveAssignedValue(row.task)\"\n [options]=\"resourceOptions()\"\n optionLabel=\"label\"\n optionValue=\"id\"\n [showClear]=\"true\"\n [hasPlaceholderPrefix]=\"false\"\n [placeholder]=\"labels().assignedTo\"\n (ngModelChange)=\"onTaskAssignedToChanged(row.key, $event)\"\n />\n @if (row.task.assignedTo) {\n <mt-entity-preview [data]=\"toAssignedEntity(row.task)\" />\n }\n </div>\n } @else if (row.task.assignedTo) {\n <mt-entity-preview [data]=\"toAssignedEntity(row.task)\" />\n } @else {\n <span class=\"text-slate-400\">-</span>\n }\n </div>\n</ng-template>\n\n<ng-template #progressCellTpl let-row>\n <div class=\"space-y-2\">\n <mt-number-field\n [ngModel]=\"normalizeProgress(row.task.progress)\"\n [min]=\"0\"\n [max]=\"100\"\n [useGrouping]=\"false\"\n [maxFractionDigits]=\"0\"\n (ngModelChange)=\"onTaskProgressChanged(row.key, $event)\"\n />\n <mt-entity-preview [data]=\"toProgressEntity(row.task)\" />\n </div>\n</ng-template>\n" }]
3270
- }], ctorParameters: null, propDecorators: { selectionCellTpl: [{ type: i0.ViewChild, args: ['selectionCellTpl', { isSignal: true }] }], taskCellTpl: [{ type: i0.ViewChild, args: ['taskCellTpl', { isSignal: true }] }], startDateCellTpl: [{ type: i0.ViewChild, args: ['startDateCellTpl', { isSignal: true }] }], finishDateCellTpl: [{ type: i0.ViewChild, args: ['finishDateCellTpl', { isSignal: true }] }], actualStartDateCellTpl: [{ type: i0.ViewChild, args: ['actualStartDateCellTpl', { isSignal: true }] }], actualFinishDateCellTpl: [{ type: i0.ViewChild, args: ['actualFinishDateCellTpl', { isSignal: true }] }], assignedToCellTpl: [{ type: i0.ViewChild, args: ['assignedToCellTpl', { isSignal: true }] }], progressCellTpl: [{ type: i0.ViewChild, args: ['progressCellTpl', { isSignal: true }] }], context: [{ type: i0.Input, args: [{ isSignal: true, alias: "context", required: false }] }], maxRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxRows", required: false }] }] } }) });
3353
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: TaskScheduleImportDialog, decorators: [{
3354
+ type: Component,
3355
+ args: [{ selector: 'mt-task-schedule-import-dialog', standalone: true, imports: [
3356
+ CommonModule,
3357
+ FormsModule,
3358
+ Button,
3359
+ CheckboxField,
3360
+ TextField,
3361
+ Tree,
3362
+ TranslocoModule,
3363
+ ], template: "<div\r\n [class]=\"modal.contentClass\"\r\n [attr.dir]=\"isRtl() ? 'rtl' : 'ltr'\"\r\n class=\"flex h-full min-h-0 flex-col gap-4 !overflow-y-hidden p-5\"\r\n>\r\n <div class=\"rounded-lg border border-surface-200 bg-surface-50 p-4\">\r\n <div class=\"grid gap-3 lg:grid-cols-[minmax(0,1fr)_auto_auto] lg:items-end\">\r\n <input\r\n #fileInput\r\n class=\"hidden\"\r\n type=\"file\"\r\n accept=\".mpp,.xer\"\r\n (change)=\"onFileChanged($event)\"\r\n />\r\n\r\n <mt-text-field\r\n [ngModel]=\"selectedFile()?.name ?? ''\"\r\n [readonly]=\"true\"\r\n [placeholder]=\"labels().selectFile\"\r\n />\r\n\r\n <mt-button\r\n [label]=\"labels().selectFile\"\r\n severity=\"secondary\"\r\n variant=\"outlined\"\r\n [disabled]=\"loadingImport() || loadingApply()\"\r\n (onClick)=\"fileInput.click()\"\r\n />\r\n\r\n <mt-button\r\n [label]=\"labels().startImport\"\r\n [disabled]=\"!canImport()\"\r\n [loading]=\"loadingImport()\"\r\n (onClick)=\"importFile()\"\r\n />\r\n </div>\r\n </div>\r\n\r\n @if (errorMessage()) {\r\n <div\r\n class=\"rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700\"\r\n >\r\n {{ errorMessage() }}\r\n </div>\r\n }\r\n\r\n <div class=\"min-h-0 flex-1 overflow-y-auto\">\r\n @if (importResult()) {\r\n @if (!previewRows().length) {\r\n <div\r\n class=\"rounded-lg border border-amber-200 bg-amber-50 px-4 py-3 text-sm text-amber-800\"\r\n >\r\n {{ labels().noTasks }}\r\n </div>\r\n } @else {\r\n <div class=\"rounded-lg border border-surface-200 bg-surface-0\">\r\n <div\r\n class=\"flex flex-wrap items-center justify-between gap-3 border-b border-surface-200 bg-surface-50 px-4 py-3\"\r\n >\r\n <div class=\"flex items-center gap-3\">\r\n <mt-checkbox-field\r\n [ngModel]=\"allSelected()\"\r\n (ngModelChange)=\"selectAllRows(!!$event)\"\r\n />\r\n <span class=\"text-sm text-surface-600\">\r\n {{ selectedTaskKeys().size }} / {{ previewRows().length }}\r\n </span>\r\n </div>\r\n\r\n @if (isTruncated()) {\r\n <p class=\"text-xs text-surface-500\">\r\n {{ labels().rowsLimited }}\r\n </p>\r\n }\r\n </div>\r\n\r\n <div class=\"px-2 py-2\">\r\n <mt-tree\r\n [value]=\"previewTreeNodes()\"\r\n [selection]=\"treeSelection()\"\r\n selectionMode=\"checkbox\"\r\n [propagateSelectionUp]=\"false\"\r\n [propagateSelectionDown]=\"false\"\r\n [filter]=\"true\"\r\n scrollHeight=\"28rem\"\r\n styleClass=\"task-schedule-import-tree\"\r\n (selectionChange)=\"onTreeSelectionChange($event)\"\r\n />\r\n </div>\r\n </div>\r\n }\r\n }\r\n </div>\r\n</div>\r\n\r\n<div [class]=\"modal.footerClass\">\r\n <mt-button\r\n [label]=\"labels().cancel\"\r\n variant=\"outlined\"\r\n [disabled]=\"loadingImport() || loadingApply()\"\r\n (onClick)=\"close()\"\r\n />\r\n\r\n <mt-button\r\n [label]=\"labels().replace\"\r\n [disabled]=\"!canApply()\"\r\n [loading]=\"loadingApply()\"\r\n (onClick)=\"applyImport(true)\"\r\n />\r\n\r\n <mt-button\r\n [label]=\"labels().append\"\r\n severity=\"secondary\"\r\n [disabled]=\"!canApply()\"\r\n [loading]=\"loadingApply()\"\r\n (onClick)=\"applyImport(false)\"\r\n />\r\n</div>\r\n" }]
3364
+ }], propDecorators: { context: [{ type: i0.Input, args: [{ isSignal: true, alias: "context", required: false }] }], maxRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxRows", required: false }] }] } });
3271
3365
 
3272
3366
  class TaskScheduleQueueService {
3273
3367
  basicOrderColumns = new Map();