@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
|
|
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
|
-
|
|
263
|
-
.
|
|
264
|
-
|
|
265
|
-
|
|
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
|
-
|
|
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.
|
|
280
|
-
const resources = this.normalizeResources(this.
|
|
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'] ??
|
|
307
|
-
|
|
308
|
-
this.
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
this.
|
|
313
|
-
this.
|
|
314
|
-
baselineStartDate: this.toDate(this.
|
|
315
|
-
baselineEndDate: this.toDate(this.
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
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.
|
|
326
|
-
this.
|
|
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.
|
|
329
|
-
this.
|
|
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.
|
|
384
|
+
assignedTo: this.resolveAssignedTo(resources, this.readNativeValue(source, 'assignedTo', propertyAliases)),
|
|
332
385
|
resources,
|
|
333
|
-
phaseGate: this.toNullableString(this.
|
|
334
|
-
phaseGateId: this.toTaskId(this.
|
|
335
|
-
criticalPath: Boolean(this.
|
|
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.
|
|
338
|
-
|
|
339
|
-
|
|
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
|
|
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
|
-
|
|
968
|
-
row
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
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
|
|
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
|
|
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() &&
|
|
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(
|
|
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.
|
|
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(
|
|
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(
|
|
3048
|
+
buildApplyPayloadData() {
|
|
2844
3049
|
const selected = this.selectedTaskKeys();
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
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
|
-
|
|
2878
|
-
|
|
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
|
-
|
|
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.ɵɵ
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
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();
|