@masterteam/task-schedule 0.0.15 → 0.0.16

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.
@@ -3,7 +3,7 @@ import * as i0 from '@angular/core';
3
3
  import { inject, Injectable, viewChild, input, signal, computed, effect, Component, linkedSignal, 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
- import { switchMap, of, map, catchError, throwError, finalize, Subject, Subscription } from 'rxjs';
6
+ import { switchMap, of, map, timeout, catchError, throwError, finalize, Subject, Subscription } from 'rxjs';
7
7
  import { ModalService } from '@masterteam/components/modal';
8
8
  import { HttpClient } from '@angular/common/http';
9
9
  import { L10n, setCulture } from '@syncfusion/ej2-base';
@@ -20,7 +20,6 @@ import { CheckboxField } from '@masterteam/components/checkbox-field';
20
20
  import { DateField } from '@masterteam/components/date-field';
21
21
  import { EntityPreview } from '@masterteam/components/entities';
22
22
  import { NumberField } from '@masterteam/components/number-field';
23
- import { Table } from '@masterteam/components/table';
24
23
  import { TextField } from '@masterteam/components/text-field';
25
24
 
26
25
  function normalizeTaskScheduleModelType(modelType) {
@@ -109,6 +108,7 @@ const NATIVE_PROPERTY_KEYS = new Set([
109
108
  'predecessor',
110
109
  'order',
111
110
  'externalId',
111
+ 'externalParentId',
112
112
  'parentId',
113
113
  'phaseGate',
114
114
  'phaseGateId',
@@ -117,6 +117,21 @@ const NATIVE_PROPERTY_KEYS = new Set([
117
117
  'isSummary',
118
118
  ]);
119
119
  const NATIVE_PROPERTY_LOOKUP_KEYS = new Set(Array.from(NATIVE_PROPERTY_KEYS).map((key) => normalizeLookupKey(key)));
120
+ const NATIVE_PROPERTY_ALIAS_TARGETS = {
121
+ startdate: 'plannedStart',
122
+ plannedstartdate: 'plannedStart',
123
+ enddate: 'plannedFinish',
124
+ finishdate: 'plannedFinish',
125
+ plannedfinishdate: 'plannedFinish',
126
+ actualstartdate: 'actualStart',
127
+ actualfinishdate: 'actualFinish',
128
+ baselinestartdate: 'baselineStart',
129
+ baselinefinishdate: 'baselineFinish',
130
+ baselineenddate: 'baselineFinish',
131
+ taskname: 'name',
132
+ };
133
+ const DEFAULT_MPP_REQUEST_TIMEOUT_MS$1 = 300_000;
134
+ const DEFAULT_MPP_PREVIEW_MAX_ROWS = 400;
120
135
  class TaskScheduleFetchService {
121
136
  http = inject(HttpClient);
122
137
  load(modelType, context) {
@@ -140,12 +155,21 @@ class TaskScheduleFetchService {
140
155
  'levels/{levelId}/{levelDataId}/schedule/mpp', { levelId, levelDataId });
141
156
  const formData = new FormData();
142
157
  formData.append('file', file, file.name);
143
- return this.readData(this.http.post(endpoint, formData)).pipe(map((payload) => ({
158
+ return this.readData(this.http
159
+ .post(endpoint, formData)
160
+ .pipe(timeout(this.resolveMppRequestTimeoutMs(context)))).pipe(map((payload) => ({
144
161
  fileName: this.toNullableString(this.readObjectValue(payload, ['fileName', 'name'])) ?? file.name,
145
- tasks: this.mapLooseTasks(this.extractTaskRows(payload), [], context.langCode === 'ar' ? 'ar' : 'en'),
146
- raw: payload,
162
+ tasks: this.mapLooseTasks(this.extractTaskRows(payload), [], context.langCode === 'ar' ? 'ar' : 'en', new Map(), this.resolveMppPreviewMaxRows(context)),
163
+ raw: context.mppKeepRawPayload ? payload : undefined,
147
164
  })));
148
165
  }
166
+ exportTasks(context) {
167
+ const endpoint = this.levelEndpoint(context, this.readEndpoint(context, 'exportTasks') ??
168
+ 'levels/{levelId}/{levelDataId}/schedule/mpp', 'Export tasks requires levelId and levelDataId.');
169
+ return this.http
170
+ .get(endpoint, { responseType: 'blob' })
171
+ .pipe(timeout(this.resolveMppRequestTimeoutMs(context)));
172
+ }
149
173
  loadBaselineSnapshot(context, version = 'latest') {
150
174
  const endpoint = this.levelEndpoint(context, version === 'latest'
151
175
  ? 'levels/{levelId}/{levelDataId}/schedule/baselines/latest'
@@ -167,11 +191,12 @@ class TaskScheduleFetchService {
167
191
  modelType === 'custom'
168
192
  ? this.mapCustomProperties(payload.catalog?.properties ?? [])
169
193
  : [];
194
+ const propertyAliases = this.mapPropertyAliases(payload.catalog?.properties ?? []);
170
195
  const tasks = modelType === 'resources'
171
- ? this.mapGroupedTasks(payload.data?.groups ?? [], customProperties, langCode)
196
+ ? this.mapGroupedTasks(payload.data?.groups ?? [], customProperties, propertyAliases, langCode)
172
197
  : modelType === 'unscheduled'
173
- ? this.mapLooseTasks(payload.data?.items ?? [], customProperties, langCode)
174
- : this.mapQueryTasks(payload.data?.records ?? [], payload.schemas ?? [], customProperties, langCode);
198
+ ? this.mapLooseTasks(payload.data?.items ?? [], customProperties, langCode, propertyAliases)
199
+ : this.mapQueryTasks(payload.data?.records ?? [], payload.schemas ?? [], customProperties, propertyAliases, langCode);
175
200
  return this.resolveResources(context, modelType, tasks).pipe(map((resources) => ({
176
201
  tasks,
177
202
  resources,
@@ -229,20 +254,20 @@ class TaskScheduleFetchService {
229
254
  'levels/{levelId}/{levelDataId}/schedule/read', fallbackMessage);
230
255
  return this.readData(this.http.post(endpoint, body));
231
256
  }
232
- mapQueryTasks(records, schemas, customProperties, langCode) {
257
+ mapQueryTasks(records, schemas, customProperties, propertyAliases, langCode) {
233
258
  const schemaMap = new Map((schemas ?? [])
234
259
  .filter((schema) => schema.id !== null && schema.id !== undefined)
235
260
  .map((schema) => [String(schema.id), schema]));
236
261
  return sortTasksByOrder((records ?? [])
237
- .map((record, index) => this.mapTaskSource(record, index + 1, customProperties, langCode, schemaMap.get(String(record.schemaId ?? '')) ?? null, record.children ?? []))
262
+ .map((record, index) => this.mapTaskSource(record, index + 1, customProperties, langCode, propertyAliases, schemaMap.get(String(record.schemaId ?? '')) ?? null, record.children ?? []))
238
263
  .filter((task) => task !== null));
239
264
  }
240
- mapGroupedTasks(groups, customProperties, langCode) {
265
+ mapGroupedTasks(groups, customProperties, propertyAliases, langCode) {
241
266
  const byId = new Map();
242
267
  for (const group of groups ?? []) {
243
268
  const items = Array.isArray(group?.items) ? group.items : [];
244
269
  for (let index = 0; index < items.length; index += 1) {
245
- const task = this.mapTaskSource(items[index], index + 1, customProperties, langCode, null, []);
270
+ const task = this.mapTaskSource(items[index], index + 1, customProperties, langCode, propertyAliases, null, []);
246
271
  if (!task) {
247
272
  continue;
248
273
  }
@@ -258,26 +283,46 @@ class TaskScheduleFetchService {
258
283
  }
259
284
  return sortTasksByOrder(Array.from(byId.values()));
260
285
  }
261
- mapLooseTasks(rows, customProperties, langCode) {
262
- return sortTasksByOrder((rows ?? [])
263
- .filter((row) => this.isObject(row))
264
- .map((row, index) => this.mapTaskSource(row, index + 1, customProperties, langCode))
265
- .filter((task) => task !== null));
286
+ mapLooseTasks(rows, customProperties, langCode, propertyAliases = new Map(), maxTasks = Number.POSITIVE_INFINITY) {
287
+ const budget = Number.isFinite(maxTasks)
288
+ ? { remaining: Math.max(0, Math.floor(maxTasks)) }
289
+ : undefined;
290
+ const tasks = [];
291
+ for (let index = 0; index < (rows ?? []).length; index += 1) {
292
+ if (budget && budget.remaining <= 0) {
293
+ break;
294
+ }
295
+ const row = rows[index];
296
+ if (!this.isObject(row)) {
297
+ continue;
298
+ }
299
+ const task = this.mapTaskSource(row, index + 1, customProperties, langCode, propertyAliases, null, undefined, budget);
300
+ if (task) {
301
+ tasks.push(task);
302
+ }
303
+ }
304
+ return sortTasksByOrder(tasks);
266
305
  }
267
- mapTaskSource(source, fallbackOrder, customProperties, langCode, schema = null, explicitChildren) {
306
+ mapTaskSource(source, fallbackOrder, customProperties, langCode, propertyAliases = new Map(), schema = null, explicitChildren, budget) {
307
+ if (budget && budget.remaining <= 0) {
308
+ return null;
309
+ }
268
310
  const id = this.toTaskId(source['id'] ?? source['taskId'] ?? source['Id']);
269
311
  if (id === null) {
270
312
  return null;
271
313
  }
272
- const rawType = this.toNullableString(this.readSourceValue(source, 'type')) ??
314
+ if (budget) {
315
+ budget.remaining -= 1;
316
+ }
317
+ const rawType = this.toNullableString(this.readNativeValue(source, 'type', propertyAliases)) ??
273
318
  this.toNullableString(schema?.key) ??
274
319
  'Task';
275
320
  const type = this.normalizeTaskType(rawType);
276
321
  const title = this.toNullableString(source['title'] ??
277
322
  source['name'] ??
278
323
  source['Name'] ??
279
- this.readNestedValue(source, 'name')) ?? `Task ${String(id)}`;
280
- const resources = this.normalizeResources(this.readSourceValue(source, 'resources'), this.readSourceValue(source, 'assignedTo'));
324
+ this.readNativeValue(source, 'name', propertyAliases)) ?? `Task ${String(id)}`;
325
+ const resources = this.normalizeResources(this.readNativeValue(source, 'resources', propertyAliases), this.readNativeValue(source, 'assignedTo', propertyAliases));
281
326
  const customValues = this.mapCustomValues(source, customProperties);
282
327
  const childRows = explicitChildren ??
283
328
  (Array.isArray(source['children'])
@@ -285,6 +330,7 @@ class TaskScheduleFetchService {
285
330
  : Array.isArray(source['subtasks'])
286
331
  ? source['subtasks']
287
332
  : []);
333
+ const subtasks = this.mapTaskChildren(childRows, customProperties, langCode, propertyAliases, budget);
288
334
  const task = {
289
335
  ...source,
290
336
  id,
@@ -294,52 +340,56 @@ class TaskScheduleFetchService {
294
340
  title,
295
341
  name: title,
296
342
  guid: this.toNullableString(this.readNestedValue(source, 'externalId') ??
343
+ this.readNativeValue(source, 'externalId', propertyAliases) ??
297
344
  this.readNestedValue(source, 'guid') ??
298
345
  source['externalId'] ??
299
346
  source['guid'] ??
300
347
  source['Guid']) ?? `task-${String(id)}`,
301
348
  parentGuid: this.toNullableString(this.readNestedValue(source, 'parentId') ??
302
349
  this.readNestedValue(source, 'externalParentId') ??
350
+ this.readNativeValue(source, 'parentId', propertyAliases) ??
351
+ this.readNativeValue(source, 'externalParentId', propertyAliases) ??
303
352
  source['parentGuid'] ??
304
353
  source['ParentGuid'] ??
305
354
  source['externalParentId']),
306
- parentId: this.toTaskId(source['parentId'] ?? source['ParentId']),
307
- startDate: this.toDate(this.readSourceValue(source, 'plannedStart') ??
308
- this.readSourceValue(source, 'Planned_Start') ??
309
- this.readSourceValue(source, 'startDate')),
310
- finishDate: this.toDate(this.readSourceValue(source, 'plannedFinish') ??
311
- this.readSourceValue(source, 'Planned_Finish') ??
312
- this.readSourceValue(source, 'finishDate') ??
313
- this.readSourceValue(source, 'endDate')),
314
- baselineStartDate: this.toDate(this.readSourceValue(source, 'baselineStart')),
315
- baselineEndDate: this.toDate(this.readSourceValue(source, 'baselineFinish')),
316
- actualStartDate: this.toDate(this.readSourceValue(source, 'actualStart')),
317
- actualFinishDate: this.toDate(this.readSourceValue(source, 'actualFinish')),
318
- predecessor: this.toNullableString(this.readSourceValue(source, 'predecessor')),
319
- duration: this.normalizeDuration(this.readSourceValue(source, 'duration'), type),
320
- progress: this.normalizeProgress(this.readSourceValue(source, 'progress')),
321
- details: this.toNullableString(this.readSourceValue(source, 'details') ??
322
- this.readSourceValue(source, 'description')),
323
- status: this.readNamedString(this.readSourceValue(source, 'status')),
355
+ parentId: this.toTaskId(source['parentId'] ??
356
+ source['ParentId'] ??
357
+ this.readNativeValue(source, 'parentId', propertyAliases)),
358
+ startDate: this.toDate(this.readNativeRawValue(source, 'plannedStart', propertyAliases) ??
359
+ this.readNativeRawValue(source, 'startDate', propertyAliases)),
360
+ finishDate: this.toDate(this.readNativeRawValue(source, 'plannedFinish', propertyAliases) ??
361
+ this.readNativeRawValue(source, 'finishDate', propertyAliases) ??
362
+ this.readNativeRawValue(source, 'endDate', propertyAliases)),
363
+ baselineStartDate: this.toDate(this.readNativeRawValue(source, 'baselineStart', propertyAliases)),
364
+ baselineEndDate: this.toDate(this.readNativeRawValue(source, 'baselineFinish', propertyAliases)),
365
+ actualStart: this.toDate(this.readNativeRawValue(source, 'actualStart', propertyAliases)),
366
+ actualFinish: this.toDate(this.readNativeRawValue(source, 'actualFinish', propertyAliases)),
367
+ baselineStart: this.toDate(this.readNativeRawValue(source, 'baselineStart', propertyAliases)),
368
+ baselineFinish: this.toDate(this.readNativeRawValue(source, 'baselineFinish', propertyAliases)),
369
+ actualStartDate: this.toDate(this.readNativeRawValue(source, 'actualStart', propertyAliases)),
370
+ actualFinishDate: this.toDate(this.readNativeRawValue(source, 'actualFinish', propertyAliases)),
371
+ predecessor: this.toNullableString(this.readNativeValue(source, 'predecessor', propertyAliases)),
372
+ duration: this.normalizeDuration(this.readNativeValue(source, 'duration', propertyAliases), type),
373
+ progress: this.normalizeProgress(this.readNativeValue(source, 'progress', propertyAliases)),
374
+ details: this.toNullableString(this.readNativeValue(source, 'details', propertyAliases) ??
375
+ this.readNativeValue(source, 'description', propertyAliases)),
376
+ status: this.readNamedString(this.readNativeValue(source, 'status', propertyAliases)),
324
377
  type,
325
- typeLabel: this.toNullableString(this.readSourceValue(source, 'typeLabel') ??
326
- this.readSourceValue(source, 'typeLable') ??
378
+ typeLabel: this.toNullableString(this.readNativeValue(source, 'typeLabel', propertyAliases) ??
379
+ this.readNativeValue(source, 'typeLable', propertyAliases) ??
327
380
  schema?.name) ?? this.localizeTaskType(type, langCode),
328
- typeLable: this.toNullableString(this.readSourceValue(source, 'typeLable') ??
329
- this.readSourceValue(source, 'typeLabel') ??
381
+ typeLable: this.toNullableString(this.readNativeValue(source, 'typeLable', propertyAliases) ??
382
+ this.readNativeValue(source, 'typeLabel', propertyAliases) ??
330
383
  schema?.name) ?? this.localizeTaskType(type, langCode),
331
- assignedTo: this.resolveAssignedTo(resources, this.readSourceValue(source, 'assignedTo')),
384
+ assignedTo: this.resolveAssignedTo(resources, this.readNativeValue(source, 'assignedTo', propertyAliases)),
332
385
  resources,
333
- phaseGate: this.toNullableString(this.readSourceValue(source, 'phaseGate')),
334
- phaseGateId: this.toTaskId(this.readSourceValue(source, 'phaseGateId')),
335
- criticalPath: Boolean(this.readSourceValue(source, 'criticalPath')),
386
+ phaseGate: this.toNullableString(this.readNativeValue(source, 'phaseGate', propertyAliases)),
387
+ phaseGateId: this.toTaskId(this.readNativeValue(source, 'phaseGateId', propertyAliases)),
388
+ criticalPath: Boolean(this.readNativeValue(source, 'criticalPath', propertyAliases)),
336
389
  isMilestone: type === 'Milestone',
337
- order: this.toNullableNumber(this.readSourceValue(source, 'order')) ??
338
- fallbackOrder,
339
- subtasks: childRows
340
- .filter((row) => this.isObject(row))
341
- .map((row, index) => this.mapTaskSource(row, index + 1, customProperties, langCode))
342
- .filter((row) => row !== null),
390
+ order: this.toNullableNumber(this.readNativeValue(source, 'order', propertyAliases)) ?? fallbackOrder,
391
+ subtasks,
392
+ children: subtasks,
343
393
  props: customValues.props,
344
394
  };
345
395
  for (const [key, value] of Object.entries(customValues.values)) {
@@ -347,6 +397,23 @@ class TaskScheduleFetchService {
347
397
  }
348
398
  return task;
349
399
  }
400
+ mapTaskChildren(rows, customProperties, langCode, propertyAliases, budget) {
401
+ const tasks = [];
402
+ for (let index = 0; index < (rows ?? []).length; index += 1) {
403
+ if (budget && budget.remaining <= 0) {
404
+ break;
405
+ }
406
+ const row = rows[index];
407
+ if (!this.isObject(row)) {
408
+ continue;
409
+ }
410
+ const task = this.mapTaskSource(row, index + 1, customProperties, langCode, propertyAliases, null, undefined, budget);
411
+ if (task) {
412
+ tasks.push(task);
413
+ }
414
+ }
415
+ return sortTasksByOrder(tasks);
416
+ }
350
417
  mapCustomProperties(properties) {
351
418
  return [...(properties ?? [])]
352
419
  .filter((property) => {
@@ -363,6 +430,7 @@ class TaskScheduleFetchService {
363
430
  id: property.id,
364
431
  key: property.normalizedKey ?? property.key,
365
432
  normalizedKey: property.normalizedKey ?? property.key,
433
+ sourceKey: property.key,
366
434
  name: property.label ?? property.key,
367
435
  viewType: property.viewType ?? 'Text',
368
436
  configuration: property.configuration,
@@ -370,17 +438,59 @@ class TaskScheduleFetchService {
370
438
  isCalculated: !!property.isCalculated,
371
439
  }));
372
440
  }
441
+ mapPropertyAliases(properties) {
442
+ const aliases = new Map();
443
+ for (const property of properties ?? []) {
444
+ const canonicalKey = this.resolveNativePropertyKey(property.normalizedKey ?? property.key);
445
+ if (!canonicalKey) {
446
+ continue;
447
+ }
448
+ this.addPropertyAlias(aliases, canonicalKey, property.key);
449
+ this.addPropertyAlias(aliases, canonicalKey, property.normalizedKey);
450
+ }
451
+ return aliases;
452
+ }
453
+ resolveNativePropertyKey(value) {
454
+ const lookupKey = normalizeLookupKey(String(value ?? ''));
455
+ if (!lookupKey) {
456
+ return null;
457
+ }
458
+ const aliasTarget = NATIVE_PROPERTY_ALIAS_TARGETS[lookupKey];
459
+ if (aliasTarget) {
460
+ return aliasTarget;
461
+ }
462
+ for (const nativeKey of NATIVE_PROPERTY_KEYS) {
463
+ const nativeLookupKey = normalizeLookupKey(nativeKey);
464
+ if (lookupKey === nativeLookupKey ||
465
+ (nativeLookupKey.length >= 5 && lookupKey.endsWith(nativeLookupKey))) {
466
+ return nativeKey;
467
+ }
468
+ }
469
+ return null;
470
+ }
471
+ addPropertyAlias(aliases, canonicalKey, alias) {
472
+ const resolvedAlias = String(alias ?? '').trim();
473
+ if (!resolvedAlias || resolvedAlias === canonicalKey) {
474
+ return;
475
+ }
476
+ const currentAliases = aliases.get(canonicalKey) ?? [];
477
+ if (!currentAliases.includes(resolvedAlias)) {
478
+ aliases.set(canonicalKey, [...currentAliases, resolvedAlias]);
479
+ }
480
+ }
373
481
  mapCustomValues(source, customProperties) {
374
482
  const values = {};
375
483
  const props = [];
376
484
  for (const property of customProperties) {
377
- const raw = this.readSourceRawValue(source, property.key);
485
+ const raw = this.readSourceRawValue(source, property.key) ??
486
+ this.readSourceRawValue(source, property.sourceKey ?? '');
378
487
  if (raw === undefined) {
379
488
  continue;
380
489
  }
381
490
  const value = String(property.viewType ?? '').toLowerCase() === 'date'
382
491
  ? this.toDate(raw)
383
- : this.readSourceValue(source, property.key);
492
+ : (this.readSourceValue(source, property.key) ??
493
+ this.readSourceValue(source, property.sourceKey ?? ''));
384
494
  values[property.key] = value;
385
495
  props.push({
386
496
  id: 0,
@@ -617,6 +727,18 @@ class TaskScheduleFetchService {
617
727
  }
618
728
  return resolved;
619
729
  }
730
+ resolveMppRequestTimeoutMs(context) {
731
+ const value = Number(context.mppRequestTimeoutMs);
732
+ return Number.isFinite(value) && value > 0
733
+ ? value
734
+ : DEFAULT_MPP_REQUEST_TIMEOUT_MS$1;
735
+ }
736
+ resolveMppPreviewMaxRows(context) {
737
+ const value = Number(context.mppPreviewMaxRows);
738
+ return Number.isFinite(value) && value > 0
739
+ ? Math.floor(value) + 1
740
+ : DEFAULT_MPP_PREVIEW_MAX_ROWS + 1;
741
+ }
620
742
  readEndpoint(context, key) {
621
743
  const value = context.endpoints?.[key];
622
744
  return typeof value === 'string' && value.trim() ? value.trim() : null;
@@ -698,6 +820,32 @@ class TaskScheduleFetchService {
698
820
  }
699
821
  return undefined;
700
822
  }
823
+ readNativeValue(source, key, aliases) {
824
+ const direct = this.readSourceValue(source, key);
825
+ if (direct !== undefined) {
826
+ return direct;
827
+ }
828
+ for (const alias of aliases.get(key) ?? []) {
829
+ const value = this.readSourceValue(source, alias);
830
+ if (value !== undefined) {
831
+ return value;
832
+ }
833
+ }
834
+ return undefined;
835
+ }
836
+ readNativeRawValue(source, key, aliases) {
837
+ const direct = this.readSourceRawValue(source, key);
838
+ if (direct !== undefined) {
839
+ return direct;
840
+ }
841
+ for (const alias of aliases.get(key) ?? []) {
842
+ const value = this.readSourceRawValue(source, alias);
843
+ if (value !== undefined) {
844
+ return value;
845
+ }
846
+ }
847
+ return undefined;
848
+ }
701
849
  readSourceValue(source, key) {
702
850
  const direct = this.readObjectValueByKey(source, key);
703
851
  if (direct !== undefined) {
@@ -953,22 +1101,53 @@ class TaskScheduleFetchService {
953
1101
  if (value === null || value === undefined || value === '') {
954
1102
  return null;
955
1103
  }
956
- if (value instanceof Date || typeof value === 'string') {
957
- return value;
1104
+ if (value instanceof Date) {
1105
+ return Number.isNaN(value.getTime()) ? null : this.toLocalDate(value);
1106
+ }
1107
+ if (typeof value === 'string') {
1108
+ const raw = value.trim();
1109
+ if (!raw) {
1110
+ return null;
1111
+ }
1112
+ const dateOnly = this.parseDateOnly(raw);
1113
+ if (dateOnly) {
1114
+ return dateOnly;
1115
+ }
1116
+ const date = new Date(raw);
1117
+ return Number.isNaN(date.getTime()) ? value : this.toLocalDate(date);
958
1118
  }
959
1119
  if (typeof value === 'number') {
960
1120
  const date = new Date(value);
961
- return Number.isNaN(date.getTime()) ? null : date;
1121
+ return Number.isNaN(date.getTime()) ? null : this.toLocalDate(date);
962
1122
  }
963
1123
  if (!this.isObject(value)) {
964
1124
  return String(value);
965
1125
  }
966
1126
  const row = value;
967
- return (row['value'] ??
968
- row['raw'] ??
969
- row['actualValue'] ??
970
- row['displayValue'] ??
971
- null);
1127
+ for (const key of ['raw', 'actualValue', 'value', 'displayValue']) {
1128
+ if (!(key in row)) {
1129
+ continue;
1130
+ }
1131
+ const date = this.toDate(row[key]);
1132
+ if (date !== null) {
1133
+ return date;
1134
+ }
1135
+ }
1136
+ return null;
1137
+ }
1138
+ parseDateOnly(value) {
1139
+ const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value);
1140
+ if (!match) {
1141
+ return null;
1142
+ }
1143
+ const year = Number(match[1]);
1144
+ const month = Number(match[2]);
1145
+ const day = Number(match[3]);
1146
+ const date = new Date(year, month - 1, day);
1147
+ return Number.isNaN(date.getTime()) ? null : date;
1148
+ }
1149
+ toLocalDate(value) {
1150
+ return new Date(value.getFullYear(), value.getMonth(), value.getDate());
972
1151
  }
973
1152
  readLegacyHolidays() {
974
1153
  if (typeof localStorage === 'undefined') {
@@ -1060,6 +1239,7 @@ function normalizeLookupKey(value) {
1060
1239
  }
1061
1240
 
1062
1241
  const DEFAULT_TASK_MODULE_ID = 3;
1242
+ const DEFAULT_MPP_REQUEST_TIMEOUT_MS = 300_000;
1063
1243
  class TaskScheduleActionService {
1064
1244
  http = inject(HttpClient);
1065
1245
  buildTaskMutation(task, options = {}) {
@@ -1289,7 +1469,9 @@ class TaskScheduleActionService {
1289
1469
  'tasks/{levelId}/import', {
1290
1470
  levelId: this.requireId(context.levelId, 'Apply import requires levelId.'),
1291
1471
  });
1292
- return this.readData(this.http.patch(endpoint, payload));
1472
+ return this.readData(this.http
1473
+ .patch(endpoint, payload)
1474
+ .pipe(timeout(this.resolveMppRequestTimeoutMs(context))));
1293
1475
  }
1294
1476
  setBaseline(context, _payload = {}) {
1295
1477
  const request = {
@@ -1617,6 +1799,12 @@ class TaskScheduleActionService {
1617
1799
  }
1618
1800
  return resolved;
1619
1801
  }
1802
+ resolveMppRequestTimeoutMs(context) {
1803
+ const value = Number(context.mppRequestTimeoutMs);
1804
+ return Number.isFinite(value) && value > 0
1805
+ ? value
1806
+ : DEFAULT_MPP_REQUEST_TIMEOUT_MS;
1807
+ }
1620
1808
  requireProcessId(value) {
1621
1809
  const resolved = this.toTaskId(value);
1622
1810
  if (resolved === null) {
@@ -2539,7 +2727,8 @@ class TaskScheduleImportDialog {
2539
2727
  return output;
2540
2728
  }, ...(ngDevMode ? [{ debugName: "resourceOptions" }] : /* istanbul ignore next */ []));
2541
2729
  isTruncated = computed(() => {
2542
- const loadedCount = this.countTasks(this.importResult()?.tasks ?? []);
2730
+ const maxRows = Math.max(1, this.maxRows());
2731
+ const loadedCount = this.countTasks(this.importResult()?.tasks ?? [], maxRows + 1);
2543
2732
  return loadedCount > this.previewRows().length;
2544
2733
  }, ...(ngDevMode ? [{ debugName: "isTruncated" }] : /* istanbul ignore next */ []));
2545
2734
  allSelected = computed(() => {
@@ -2551,8 +2740,11 @@ class TaskScheduleImportDialog {
2551
2740
  return rows.every((row) => selected.has(row.key));
2552
2741
  }, ...(ngDevMode ? [{ debugName: "allSelected" }] : /* istanbul ignore next */ []));
2553
2742
  hasSelection = computed(() => this.selectedTaskKeys().size > 0, ...(ngDevMode ? [{ debugName: "hasSelection" }] : /* istanbul ignore next */ []));
2554
- canImport = computed(() => !!this.selectedFile() && !this.loadingImport(), ...(ngDevMode ? [{ debugName: "canImport" }] : /* istanbul ignore next */ []));
2555
- canApply = computed(() => !!this.importResult() && this.hasSelection() && !this.loadingApply(), ...(ngDevMode ? [{ debugName: "canApply" }] : /* istanbul ignore next */ []));
2743
+ canImport = computed(() => !!this.selectedFile() && !this.loadingImport() && !this.loadingApply(), ...(ngDevMode ? [{ debugName: "canImport" }] : /* istanbul ignore next */ []));
2744
+ canApply = computed(() => !!this.importResult() &&
2745
+ this.hasSelection() &&
2746
+ !this.loadingApply() &&
2747
+ !this.loadingImport(), ...(ngDevMode ? [{ debugName: "canApply" }] : /* istanbul ignore next */ []));
2556
2748
  previewPageSize = computed(() => Math.min(Math.max(this.previewRows().length, 1), 50), ...(ngDevMode ? [{ debugName: "previewPageSize" }] : /* istanbul ignore next */ []));
2557
2749
  previewColumns = linkedSignal(() => [
2558
2750
  {
@@ -2613,6 +2805,9 @@ class TaskScheduleImportDialog {
2613
2805
  this.errorMessage.set(null);
2614
2806
  }
2615
2807
  importFile() {
2808
+ if (this.loadingImport() || this.loadingApply()) {
2809
+ return;
2810
+ }
2616
2811
  const context = this.context();
2617
2812
  const file = this.selectedFile();
2618
2813
  if (!context) {
@@ -2629,8 +2824,12 @@ class TaskScheduleImportDialog {
2629
2824
  }
2630
2825
  this.errorMessage.set(null);
2631
2826
  this.loadingImport.set(true);
2827
+ const importContext = {
2828
+ ...context,
2829
+ mppPreviewMaxRows: Math.max(1, this.maxRows()),
2830
+ };
2632
2831
  this.fetchService
2633
- .importTasks(context, file)
2832
+ .importTasks(importContext, file)
2634
2833
  .pipe(finalize(() => this.loadingImport.set(false)))
2635
2834
  .subscribe({
2636
2835
  next: (result) => {
@@ -2655,13 +2854,16 @@ class TaskScheduleImportDialog {
2655
2854
  selectAllRows(checked) {
2656
2855
  const next = new Set();
2657
2856
  if (checked) {
2658
- this.collectTaskKeys(this.importResult()?.tasks ?? []).forEach((key) => {
2857
+ this.previewRows().forEach(({ key }) => {
2659
2858
  next.add(key);
2660
2859
  });
2661
2860
  }
2662
2861
  this.selectedTaskKeys.set(next);
2663
2862
  }
2664
2863
  applyImport(overrideCurrent) {
2864
+ if (this.loadingImport() || this.loadingApply()) {
2865
+ return;
2866
+ }
2665
2867
  const context = this.context();
2666
2868
  const importResult = this.importResult();
2667
2869
  if (!context || !importResult) {
@@ -2674,7 +2876,7 @@ class TaskScheduleImportDialog {
2674
2876
  }
2675
2877
  const payload = {
2676
2878
  overrideCurrent,
2677
- data: this.buildApplyPayloadData(importResult.tasks),
2879
+ data: this.buildApplyPayloadData(),
2678
2880
  };
2679
2881
  this.errorMessage.set(null);
2680
2882
  this.loadingApply.set(true);
@@ -2806,6 +3008,9 @@ class TaskScheduleImportDialog {
2806
3008
  if (!path) {
2807
3009
  return null;
2808
3010
  }
3011
+ return this.findTaskByPath(path);
3012
+ }
3013
+ findTaskByPath(path) {
2809
3014
  const steps = path.split('.').filter((segment) => segment !== '');
2810
3015
  if (!steps.length || steps[0] !== 'root') {
2811
3016
  return null;
@@ -2840,42 +3045,53 @@ class TaskScheduleImportDialog {
2840
3045
  tasks: [...result.tasks],
2841
3046
  });
2842
3047
  }
2843
- buildApplyPayloadData(tasks) {
3048
+ buildApplyPayloadData() {
2844
3049
  const selected = this.selectedTaskKeys();
2845
- const output = [];
2846
- const walk = (rows, parentGuid, pathPrefix) => {
2847
- rows.forEach((task, index) => {
2848
- const path = `${pathPrefix}.${index}`;
2849
- const key = this.resolveTaskKey(task, path);
2850
- const payloadTask = {
2851
- ...task,
2852
- name: String(task.title ?? task.name ?? ''),
2853
- predecessors: task.predecessor ?? task.predecessors ?? '',
2854
- parentGuid: task.parentGuid ?? parentGuid,
2855
- assignedTo: this.resolveAssignedTo(task),
2856
- isSelected: selected.has(key),
2857
- subtasks: [],
2858
- children: [],
2859
- };
2860
- payloadTask['startDate'] = this.toApiDate(task.startDate);
2861
- payloadTask['finishDate'] = this.toApiDate(task.finishDate);
2862
- payloadTask['baselineStart'] = this.toApiDate(task.baselineStartDate ?? task.baselineStart);
2863
- payloadTask['baselineFinish'] = this.toApiDate(task.baselineEndDate ?? task.baselineFinish);
2864
- payloadTask['actualStart'] = this.toApiDate(task.actualStartDate ?? task.actualStart);
2865
- payloadTask['actualFinish'] = this.toApiDate(task.actualFinishDate ?? task.actualFinish);
2866
- payloadTask['customProperties'] =
2867
- task.customProperties ?? task.props ?? [];
2868
- payloadTask['isMilestone'] =
2869
- String(task.type ?? task.typeLabel ?? task.typeLable ?? '').toLowerCase() === 'milestone';
2870
- output.push(payloadTask);
2871
- const children = this.resolveChildTasks(task);
2872
- if (children.length) {
2873
- walk(children, task.guid ?? String(task.id ?? ''), path);
2874
- }
2875
- });
3050
+ return this.previewRows()
3051
+ .filter((row) => selected.has(row.key))
3052
+ .map((row) => this.buildApplyPayloadTask(row));
3053
+ }
3054
+ buildApplyPayloadTask(row) {
3055
+ const task = row.task;
3056
+ const payloadTask = {
3057
+ ...task,
3058
+ name: String(task.title ?? task.name ?? ''),
3059
+ predecessors: task.predecessor ?? task.predecessors ?? '',
3060
+ parentGuid: task.parentGuid ?? this.resolveParentGuidForPreviewRow(row.key),
3061
+ assignedTo: this.resolveAssignedTo(task),
3062
+ isSelected: true,
3063
+ subtasks: [],
3064
+ children: [],
2876
3065
  };
2877
- walk(tasks, null, 'root');
2878
- return output;
3066
+ payloadTask['startDate'] = this.toApiDate(task.startDate);
3067
+ payloadTask['finishDate'] = this.toApiDate(task.finishDate);
3068
+ payloadTask['baselineStart'] = this.toApiDate(task.baselineStartDate ?? task.baselineStart);
3069
+ payloadTask['baselineFinish'] = this.toApiDate(task.baselineEndDate ?? task.baselineFinish);
3070
+ payloadTask['actualStart'] = this.toApiDate(task.actualStartDate ?? task.actualStart);
3071
+ payloadTask['actualFinish'] = this.toApiDate(task.actualFinishDate ?? task.actualFinish);
3072
+ payloadTask['customProperties'] =
3073
+ task.customProperties ?? task.props ?? [];
3074
+ payloadTask['isMilestone'] =
3075
+ String(task.type ?? task.typeLabel ?? task.typeLable ?? '').toLowerCase() === 'milestone';
3076
+ return payloadTask;
3077
+ }
3078
+ resolveParentGuidForPreviewRow(rowKey) {
3079
+ const path = this.readPathFromRowKey(rowKey);
3080
+ if (!path) {
3081
+ return null;
3082
+ }
3083
+ const steps = path.split('.').filter((segment) => segment !== '');
3084
+ if (steps.length <= 2) {
3085
+ return null;
3086
+ }
3087
+ const parent = this.findTaskByPath(steps.slice(0, -1).join('.'));
3088
+ if (!parent) {
3089
+ return null;
3090
+ }
3091
+ const parentGuid = parent.guid ?? parent.id ?? null;
3092
+ return parentGuid === null || parentGuid === undefined
3093
+ ? null
3094
+ : String(parentGuid);
2879
3095
  }
2880
3096
  resolveChildTasks(task) {
2881
3097
  const subtasks = Array.isArray(task.subtasks) ? task.subtasks : [];
@@ -2889,21 +3105,6 @@ class TaskScheduleImportDialog {
2889
3105
  const base = task.guid ?? task.id ?? path;
2890
3106
  return `${String(base)}::${path}`;
2891
3107
  }
2892
- collectTaskKeys(tasks) {
2893
- const keys = new Set();
2894
- const walk = (rows, pathPrefix) => {
2895
- rows.forEach((task, index) => {
2896
- const path = `${pathPrefix}.${index}`;
2897
- keys.add(this.resolveTaskKey(task, path));
2898
- const children = this.resolveChildTasks(task);
2899
- if (children.length) {
2900
- walk(children, path);
2901
- }
2902
- });
2903
- };
2904
- walk(tasks, 'root');
2905
- return keys;
2906
- }
2907
3108
  resolveAssignedTo(task) {
2908
3109
  const assignedTo = task.assignedTo;
2909
3110
  if ((typeof assignedTo === 'string' || typeof assignedTo === 'number') &&
@@ -3014,6 +3215,7 @@ class TaskScheduleImportDialog {
3014
3215
  if (typeof value === 'object') {
3015
3216
  const row = value;
3016
3217
  return (this.toApiDate(row['actualValue']) ??
3218
+ this.toApiDate(row['raw']) ??
3017
3219
  this.toApiDate(row['displayValue']) ??
3018
3220
  this.toApiDate(row['value']) ??
3019
3221
  null);
@@ -3026,16 +3228,19 @@ class TaskScheduleImportDialog {
3026
3228
  const day = String(date.getDate()).padStart(2, '0');
3027
3229
  return `${year}-${month}-${day}`;
3028
3230
  }
3029
- countTasks(tasks) {
3231
+ countTasks(tasks, maxCount) {
3030
3232
  let count = 0;
3031
3233
  const walk = (rows) => {
3032
- rows.forEach((task) => {
3234
+ for (const task of rows) {
3235
+ if (count >= maxCount) {
3236
+ return;
3237
+ }
3033
3238
  count += 1;
3034
3239
  const children = this.resolveChildTasks(task);
3035
3240
  if (children.length) {
3036
3241
  walk(children);
3037
3242
  }
3038
- });
3243
+ }
3039
3244
  };
3040
3245
  walk(tasks);
3041
3246
  return count;
@@ -3044,24 +3249,24 @@ class TaskScheduleImportDialog {
3044
3249
  this.errorMessage.set(message);
3045
3250
  }
3046
3251
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: TaskScheduleImportDialog, deps: [], target: i0.ɵɵFactoryTarget.Component });
3047
- 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\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-table\r\n [data]=\"previewRows()\"\r\n [columns]=\"previewColumns()\"\r\n dataKey=\"key\"\r\n storageKey=\"task-schedule-import-preview-table\"\r\n size=\"small\"\r\n [stripedRows]=\"true\"\r\n [showGridlines]=\"true\"\r\n [pageSize]=\"previewPageSize()\"\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\r\n<ng-template #selectionCellTpl let-row>\r\n <div class=\"flex items-center justify-center\">\r\n <mt-checkbox-field\r\n [ngModel]=\"selectedTaskKeys().has(row.key)\"\r\n (ngModelChange)=\"toggleRowSelection(row.key, !!$event)\"\r\n />\r\n </div>\r\n</ng-template>\r\n\r\n<ng-template #taskCellTpl let-row>\r\n <div class=\"min-w-0\" [style.paddingInlineStart.px]=\"row.depth * 18\">\r\n <mt-text-field\r\n [ngModel]=\"row.task.title || row.task.name || ''\"\r\n (ngModelChange)=\"onTaskTitleChanged(row.key, $event)\"\r\n />\r\n </div>\r\n</ng-template>\r\n\r\n<ng-template #startDateCellTpl let-row>\r\n <mt-date-field\r\n [ngModel]=\"toDateFieldValue(row.task.startDate)\"\r\n [showClear]=\"true\"\r\n (ngModelChange)=\"onTaskDateChanged(row.key, 'startDate', $event)\"\r\n />\r\n</ng-template>\r\n\r\n<ng-template #finishDateCellTpl let-row>\r\n <mt-date-field\r\n [ngModel]=\"toDateFieldValue(row.task.finishDate)\"\r\n [showClear]=\"true\"\r\n (ngModelChange)=\"onTaskDateChanged(row.key, 'finishDate', $event)\"\r\n />\r\n</ng-template>\r\n\r\n<ng-template #actualStartDateCellTpl let-row>\r\n <mt-date-field\r\n [ngModel]=\"\r\n toDateFieldValue(row.task.actualStartDate || row.task.actualStart)\r\n \"\r\n [showClear]=\"true\"\r\n (ngModelChange)=\"onTaskDateChanged(row.key, 'actualStartDate', $event)\"\r\n />\r\n</ng-template>\r\n\r\n<ng-template #actualFinishDateCellTpl let-row>\r\n <mt-date-field\r\n [ngModel]=\"\r\n toDateFieldValue(row.task.actualFinishDate || row.task.actualFinish)\r\n \"\r\n [showClear]=\"true\"\r\n (ngModelChange)=\"onTaskDateChanged(row.key, 'actualFinishDate', $event)\"\r\n />\r\n</ng-template>\r\n\r\n<ng-template #assignedToCellTpl let-row>\r\n <div class=\"min-w-0\">\r\n @if (resourceOptions().length) {\r\n <div class=\"space-y-2\">\r\n <mt-select-field\r\n [ngModel]=\"resolveAssignedValue(row.task)\"\r\n [options]=\"resourceOptions()\"\r\n optionLabel=\"label\"\r\n optionValue=\"id\"\r\n [showClear]=\"true\"\r\n [hasPlaceholderPrefix]=\"false\"\r\n [placeholder]=\"labels().assignedTo\"\r\n (ngModelChange)=\"onTaskAssignedToChanged(row.key, $event)\"\r\n />\r\n @if (row.task.assignedTo) {\r\n <mt-entity-preview [data]=\"toAssignedEntity(row.task)\" />\r\n }\r\n </div>\r\n } @else if (row.task.assignedTo) {\r\n <mt-entity-preview [data]=\"toAssignedEntity(row.task)\" />\r\n } @else {\r\n <span class=\"text-slate-400\">-</span>\r\n }\r\n </div>\r\n</ng-template>\r\n\r\n<ng-template #progressCellTpl let-row>\r\n <div class=\"space-y-2\">\r\n <mt-number-field\r\n [ngModel]=\"normalizeProgress(row.task.progress)\"\r\n [min]=\"0\"\r\n [max]=\"100\"\r\n [useGrouping]=\"false\"\r\n [maxFractionDigits]=\"0\"\r\n (ngModelChange)=\"onTaskProgressChanged(row.key, $event)\"\r\n />\r\n <mt-entity-preview [data]=\"toProgressEntity(row.task)\" />\r\n </div>\r\n</ng-template>\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: 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: Table, selector: "mt-table", inputs: ["filters", "data", "columns", "rowActions", "size", "showGridlines", "stripedRows", "selectableRows", "clickableRows", "generalSearch", "lazyLocalSearch", "showFilters", "filterMode", "loading", "updating", "lazy", "lazyLocalSort", "lazyTotalRecords", "reorderableColumns", "reorderableRows", "dataKey", "storageKey", "storageMode", "exportable", "printable", "groupable", "cellClickFilter", "freezeActions", "printTitle", "exportFilename", "actionShape", "rowActionsLoadingFn", "tableLayout", "noCard", "tabs", "tabsOptionLabel", "tabsOptionValue", "activeTab", "actions", "paginatorPosition", "alwaysShowPaginator", "rowsPerPageOptions", "pageSize", "currentPage", "first", "filterTerm", "groupBy"], outputs: ["selectionChange", "cellChange", "lazyLoad", "columnReorder", "rowReorder", "rowClick", "rowActionsRequested", "filtersChange", "activeTabChange", "onTabChange", "pageSizeChange", "currentPageChange", "firstChange", "filterTermChange", "groupByChange"] }, { 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 }] });
3252
+ 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)]] });
3048
3253
  }
3049
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: TaskScheduleImportDialog, decorators: [{
3050
- type: Component,
3051
- args: [{ selector: 'mt-task-schedule-import-dialog', standalone: true, imports: [
3052
- CommonModule,
3053
- FormsModule,
3054
- Button,
3055
- CheckboxField,
3056
- DateField,
3057
- EntityPreview,
3058
- NumberField,
3059
- SelectField,
3060
- Table,
3061
- TextField,
3062
- TranslocoModule,
3063
- ], 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-table\r\n [data]=\"previewRows()\"\r\n [columns]=\"previewColumns()\"\r\n dataKey=\"key\"\r\n storageKey=\"task-schedule-import-preview-table\"\r\n size=\"small\"\r\n [stripedRows]=\"true\"\r\n [showGridlines]=\"true\"\r\n [pageSize]=\"previewPageSize()\"\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\r\n<ng-template #selectionCellTpl let-row>\r\n <div class=\"flex items-center justify-center\">\r\n <mt-checkbox-field\r\n [ngModel]=\"selectedTaskKeys().has(row.key)\"\r\n (ngModelChange)=\"toggleRowSelection(row.key, !!$event)\"\r\n />\r\n </div>\r\n</ng-template>\r\n\r\n<ng-template #taskCellTpl let-row>\r\n <div class=\"min-w-0\" [style.paddingInlineStart.px]=\"row.depth * 18\">\r\n <mt-text-field\r\n [ngModel]=\"row.task.title || row.task.name || ''\"\r\n (ngModelChange)=\"onTaskTitleChanged(row.key, $event)\"\r\n />\r\n </div>\r\n</ng-template>\r\n\r\n<ng-template #startDateCellTpl let-row>\r\n <mt-date-field\r\n [ngModel]=\"toDateFieldValue(row.task.startDate)\"\r\n [showClear]=\"true\"\r\n (ngModelChange)=\"onTaskDateChanged(row.key, 'startDate', $event)\"\r\n />\r\n</ng-template>\r\n\r\n<ng-template #finishDateCellTpl let-row>\r\n <mt-date-field\r\n [ngModel]=\"toDateFieldValue(row.task.finishDate)\"\r\n [showClear]=\"true\"\r\n (ngModelChange)=\"onTaskDateChanged(row.key, 'finishDate', $event)\"\r\n />\r\n</ng-template>\r\n\r\n<ng-template #actualStartDateCellTpl let-row>\r\n <mt-date-field\r\n [ngModel]=\"\r\n toDateFieldValue(row.task.actualStartDate || row.task.actualStart)\r\n \"\r\n [showClear]=\"true\"\r\n (ngModelChange)=\"onTaskDateChanged(row.key, 'actualStartDate', $event)\"\r\n />\r\n</ng-template>\r\n\r\n<ng-template #actualFinishDateCellTpl let-row>\r\n <mt-date-field\r\n [ngModel]=\"\r\n toDateFieldValue(row.task.actualFinishDate || row.task.actualFinish)\r\n \"\r\n [showClear]=\"true\"\r\n (ngModelChange)=\"onTaskDateChanged(row.key, 'actualFinishDate', $event)\"\r\n />\r\n</ng-template>\r\n\r\n<ng-template #assignedToCellTpl let-row>\r\n <div class=\"min-w-0\">\r\n @if (resourceOptions().length) {\r\n <div class=\"space-y-2\">\r\n <mt-select-field\r\n [ngModel]=\"resolveAssignedValue(row.task)\"\r\n [options]=\"resourceOptions()\"\r\n optionLabel=\"label\"\r\n optionValue=\"id\"\r\n [showClear]=\"true\"\r\n [hasPlaceholderPrefix]=\"false\"\r\n [placeholder]=\"labels().assignedTo\"\r\n (ngModelChange)=\"onTaskAssignedToChanged(row.key, $event)\"\r\n />\r\n @if (row.task.assignedTo) {\r\n <mt-entity-preview [data]=\"toAssignedEntity(row.task)\" />\r\n }\r\n </div>\r\n } @else if (row.task.assignedTo) {\r\n <mt-entity-preview [data]=\"toAssignedEntity(row.task)\" />\r\n } @else {\r\n <span class=\"text-slate-400\">-</span>\r\n }\r\n </div>\r\n</ng-template>\r\n\r\n<ng-template #progressCellTpl let-row>\r\n <div class=\"space-y-2\">\r\n <mt-number-field\r\n [ngModel]=\"normalizeProgress(row.task.progress)\"\r\n [min]=\"0\"\r\n [max]=\"100\"\r\n [useGrouping]=\"false\"\r\n [maxFractionDigits]=\"0\"\r\n (ngModelChange)=\"onTaskProgressChanged(row.key, $event)\"\r\n />\r\n <mt-entity-preview [data]=\"toProgressEntity(row.task)\" />\r\n </div>\r\n</ng-template>\r\n" }]
3064
- }], 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 }] }] } });
3254
+ 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: [{
3255
+ type: Component,
3256
+ args: [{ selector: 'mt-task-schedule-import-dialog', standalone: true, imports: [
3257
+ CommonModule,
3258
+ FormsModule,
3259
+ Button,
3260
+ CheckboxField,
3261
+ DateField,
3262
+ EntityPreview,
3263
+ NumberField,
3264
+ SelectField,
3265
+ Table,
3266
+ TextField,
3267
+ TranslocoModule,
3268
+ ], 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" }]
3269
+ }], 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 }] }] } }) });
3065
3270
 
3066
3271
  class TaskScheduleQueueService {
3067
3272
  basicOrderColumns = new Map();