@masterteam/work-center 0.0.70 → 0.0.72
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import * as i1
|
|
2
|
-
import { CommonModule } from '@angular/common';
|
|
1
|
+
import * as i1 from '@angular/common';
|
|
2
|
+
import { CommonModule, NgComponentOutlet } from '@angular/common';
|
|
3
3
|
import * as i0 from '@angular/core';
|
|
4
|
-
import { inject, viewChild, input, computed, signal, Component, Injectable, DestroyRef, effect, untracked, output } from '@angular/core';
|
|
4
|
+
import { inject, viewChild, input, computed, signal, Component, Injectable, DestroyRef, effect, untracked, InjectionToken, output } from '@angular/core';
|
|
5
5
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
6
6
|
import { TranslocoService, TranslocoDirective } from '@jsverse/transloco';
|
|
7
7
|
import { Button } from '@masterteam/components/button';
|
|
@@ -10,7 +10,7 @@ import { ModalService } from '@masterteam/components/modal';
|
|
|
10
10
|
import { PropertyFilterBuilder } from '@masterteam/components/property-filter-builder';
|
|
11
11
|
import { StatisticCard } from '@masterteam/components/statistic-card';
|
|
12
12
|
import { Table } from '@masterteam/components/table';
|
|
13
|
-
import * as i1 from 'primeng/skeleton';
|
|
13
|
+
import * as i1$1 from 'primeng/skeleton';
|
|
14
14
|
import { SkeletonModule } from 'primeng/skeleton';
|
|
15
15
|
import { catchError, EMPTY, map, take } from 'rxjs';
|
|
16
16
|
import { ModalRef } from '@masterteam/components/dialog';
|
|
@@ -23,7 +23,7 @@ import { RuntimeActionContextStore, RuntimeActionRunner, RUNTIME_ACTION_CONFIRM_
|
|
|
23
23
|
import { Action, Selector, State, Store, select } from '@ngxs/store';
|
|
24
24
|
import { handleApiRequest } from '@masterteam/components';
|
|
25
25
|
import { ClientInstancePreview } from '@masterteam/client-components/client-instance-preview';
|
|
26
|
-
import { EntitiesPreview } from '@masterteam/components/entities';
|
|
26
|
+
import { EntitiesPreview, EntityPreviewBody } from '@masterteam/components/entities';
|
|
27
27
|
import { Tabs } from '@masterteam/components/tabs';
|
|
28
28
|
import { DiscussionThread } from '@masterteam/discussion';
|
|
29
29
|
import { StructureBuilder } from '@masterteam/structure-builder';
|
|
@@ -2288,6 +2288,7 @@ class WorkCenterProcessStepType {
|
|
|
2288
2288
|
contextKey: `level:${levelId}`,
|
|
2289
2289
|
instanceId: levelDataId,
|
|
2290
2290
|
displayAreas: ['preview'],
|
|
2291
|
+
processRequestId: this.requestId(),
|
|
2291
2292
|
};
|
|
2292
2293
|
}, ...(ngDevMode ? [{ debugName: "levelDetailsConfig" }] : /* istanbul ignore next */ []));
|
|
2293
2294
|
// The preview response carries the level name in its schema, so the tab is
|
|
@@ -2330,25 +2331,453 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
|
|
|
2330
2331
|
], template: "<div\r\n class=\"mt-modal-content flex h-full min-h-0 flex-col gap-4 p-4 lg:overflow-hidden\"\r\n *transloco=\"let t; prefix: 'workCenter'\"\r\n>\r\n <div\r\n class=\"flex flex-1 min-h-0 flex-col gap-4 lg:grid lg:grid-cols-[13rem_minmax(0,1fr)] lg:gap-6\"\r\n >\r\n @if (tabOptions().length > 1) {\r\n <mt-tabs\r\n [(active)]=\"activeTab\"\r\n [options]=\"tabOptions()\"\r\n mode=\"vertical\"\r\n size=\"small\"\r\n />\r\n }\r\n\r\n <div class=\"min-w-0 flex-1 min-h-0 lg:overflow-hidden\">\r\n <div\r\n class=\"h-full min-h-0 lg:overflow-hidden\"\r\n [hidden]=\"activeTab() !== 'form'\"\r\n >\r\n <div\r\n class=\"min-w-0 h-full min-h-0 lg:overflow-y-auto lg:overscroll-contain lg:pr-2\"\r\n >\r\n @if (canRenderForm()) {\r\n <mt-client-form\r\n [moduleKey]=\"moduleKey()\"\r\n [operationKey]=\"operationKey()\"\r\n [moduleId]=\"moduleId()\"\r\n [levelId]=\"levelId()\"\r\n [levelDataId]=\"levelDataId()\"\r\n [moduleDataId]=\"moduleDataId()\"\r\n [requestSchemaId]=\"requestSchemaId()\"\r\n [draftProcessId]=\"draftProcessId()\"\r\n [readonly]=\"readOnly()\"\r\n [lookups]=\"formLookups()\"\r\n />\r\n } @else {\r\n <div\r\n class=\"flex min-h-[22rem] items-center justify-center rounded-lg border border-dashed border-surface-300 bg-surface-50 p-6\"\r\n >\r\n <p class=\"max-w-md text-center text-sm text-surface-500\">\r\n {{ t(\"context.processFormUnavailable\") }}\r\n </p>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n\r\n <div\r\n class=\"h-full min-h-0 lg:overflow-hidden\"\r\n [hidden]=\"activeTab() !== 'levelDetails'\"\r\n >\r\n @if (levelDetailsConfig(); as levelDetailsConfig) {\r\n <div\r\n class=\"min-w-0 h-full min-h-0 lg:overflow-y-auto lg:overscroll-contain lg:pr-2\"\r\n >\r\n <mt-client-instance-preview [config]=\"levelDetailsConfig\" />\r\n </div>\r\n }\r\n </div>\r\n\r\n <div\r\n class=\"flex-1 min-h-0 overflow-auto\"\r\n [hidden]=\"activeTab() !== 'approvals'\"\r\n >\r\n @if (canRenderPreview()) {\r\n <mt-work-center-process-preview\r\n [requestId]=\"requestId()\"\r\n [view]=\"'approvals'\"\r\n />\r\n } @else {\r\n <div\r\n class=\"flex min-h-[22rem] items-center justify-center rounded-lg border border-dashed border-surface-300 bg-surface-50 p-6\"\r\n >\r\n <p class=\"max-w-md text-center text-sm text-surface-500\">\r\n {{ t(\"context.processPreviewUnavailable\") }}\r\n </p>\r\n </div>\r\n }\r\n </div>\r\n\r\n <div\r\n class=\"flex-1 min-h-0 overflow-auto\"\r\n [hidden]=\"activeTab() !== 'schema'\"\r\n >\r\n @if (canRenderPreview()) {\r\n <mt-work-center-process-preview\r\n [requestId]=\"requestId()\"\r\n [view]=\"'schema'\"\r\n />\r\n } @else {\r\n <div\r\n class=\"flex min-h-[22rem] items-center justify-center rounded-lg border border-dashed border-surface-300 bg-surface-50 p-6\"\r\n >\r\n <p class=\"max-w-md text-center text-sm text-surface-500\">\r\n {{ t(\"context.processPreviewUnavailable\") }}\r\n </p>\r\n </div>\r\n }\r\n </div>\r\n\r\n @if (!readOnly()) {\r\n <div\r\n class=\"h-full min-h-0 overflow-hidden\"\r\n [hidden]=\"activeTab() !== 'discussion'\"\r\n >\r\n @if (canRenderDiscussion()) {\r\n <div\r\n class=\"h-[32rem] min-h-[22rem] overflow-hidden rounded-lg border border-surface-200 bg-surface-50 lg:h-full lg:min-h-0\"\r\n >\r\n <mt-discussion-thread\r\n [moduleType]=\"'ProcessRequest'\"\r\n [recordId]=\"discussionRecordId()\"\r\n [mentionSearchEndpoint]=\"'Identity/users'\"\r\n [mentionSearchParam]=\"'query'\"\r\n [mentionSearchDataPath]=\"'data'\"\r\n [uploadEndpoint]=\"'uploader'\"\r\n [attachmentDownloadEndpoint]=\"'uploader'\"\r\n [showParticipants]=\"true\"\r\n [autoMarkRead]=\"true\"\r\n [refreshIntervalMs]=\"0\"\r\n [styleClass]=\"'h-full'\"\r\n />\r\n </div>\r\n } @else {\r\n <div\r\n class=\"flex min-h-[22rem] items-center justify-center rounded-lg border border-dashed border-surface-300 bg-surface-50 p-6\"\r\n >\r\n <p class=\"max-w-md text-center text-sm text-surface-500\">\r\n {{ t(\"context.processDiscussionUnavailable\") }}\r\n </p>\r\n </div>\r\n }\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n</div>\r\n" }]
|
|
2331
2332
|
}], ctorParameters: () => [], propDecorators: { details: [{ type: i0.Input, args: [{ isSignal: true, alias: "details", required: true }] }], lookups: [{ type: i0.Input, args: [{ isSignal: true, alias: "lookups", required: false }] }], readOnly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readOnly", required: false }] }], clientForm: [{ type: i0.ViewChild, args: [i0.forwardRef(() => ClientForm), { isSignal: true }] }], levelPreview: [{ type: i0.ViewChild, args: [i0.forwardRef(() => ClientInstancePreview), { isSignal: true }] }] } });
|
|
2332
2333
|
|
|
2334
|
+
/**
|
|
2335
|
+
* work-center-schedule-publish.rows.ts - Pure grouping, ordering, counting and
|
|
2336
|
+
* filtering rules for the approver's published-vs-draft comparison.
|
|
2337
|
+
*
|
|
2338
|
+
* THE DIFF ITSELF IS SERVER-SIDE. This module never decides what changed - only
|
|
2339
|
+
* how the answer is presented, so a 500-row plan stays scannable in a drawer.
|
|
2340
|
+
* Split out of the component so the rules are unit-testable without a browser or
|
|
2341
|
+
* TestBed, mirroring `work-center-process-preview.rows.ts` - this package has no
|
|
2342
|
+
* component TestBed specs, only pure-function ones.
|
|
2343
|
+
*
|
|
2344
|
+
* Intentionally a twin of `@masterteam/task-schedule`'s
|
|
2345
|
+
* `core/task-schedule-publish-diff.ts`: the planner and the approver must sort,
|
|
2346
|
+
* group and count the same payload identically, and duplicating ~100 lines of
|
|
2347
|
+
* pure rules is cheaper than a cross-package dependency between a schedule
|
|
2348
|
+
* runtime and a work-center drawer.
|
|
2349
|
+
*
|
|
2350
|
+
* Pure TypeScript - no Angular, no DOM.
|
|
2351
|
+
*/
|
|
2352
|
+
/**
|
|
2353
|
+
* The order groups are presented in, and the only states a group is built for.
|
|
2354
|
+
*
|
|
2355
|
+
* Added first because a new row is the biggest claim a publish makes; removed
|
|
2356
|
+
* last because it is the one an approver most needs to not miss, and a trailing
|
|
2357
|
+
* group is where the eye stops.
|
|
2358
|
+
*/
|
|
2359
|
+
const SCHEDULE_PUBLISH_GROUP_ORDER = [
|
|
2360
|
+
'added',
|
|
2361
|
+
'changed',
|
|
2362
|
+
'removed',
|
|
2363
|
+
];
|
|
2364
|
+
function summarizeSchedulePublish(diff) {
|
|
2365
|
+
const rows = diff?.rows ?? [];
|
|
2366
|
+
// Counted from the rows rather than trusted from the envelope's own counters:
|
|
2367
|
+
// the two cannot then disagree in front of someone about to approve.
|
|
2368
|
+
const addedCount = countByState(rows, 'added');
|
|
2369
|
+
const changedCount = countByState(rows, 'changed');
|
|
2370
|
+
const removedCount = countByState(rows, 'removed');
|
|
2371
|
+
return {
|
|
2372
|
+
hasSnapshot: diff?.hasSnapshot === true,
|
|
2373
|
+
addedCount,
|
|
2374
|
+
changedCount,
|
|
2375
|
+
removedCount,
|
|
2376
|
+
totalCount: addedCount + changedCount + removedCount,
|
|
2377
|
+
isEmpty: addedCount + changedCount + removedCount === 0,
|
|
2378
|
+
};
|
|
2379
|
+
}
|
|
2380
|
+
/**
|
|
2381
|
+
* Group the comparison for display, optionally narrowed by a free-text search.
|
|
2382
|
+
*
|
|
2383
|
+
* Search matches a row's NAME and its changed FIELD KEYS, so "start" finds every
|
|
2384
|
+
* row whose planned start moved even though that text appears nowhere in a name.
|
|
2385
|
+
* Empty groups are dropped rather than rendered as empty headers.
|
|
2386
|
+
*/
|
|
2387
|
+
function groupSchedulePublishRows(diff, searchText = '') {
|
|
2388
|
+
const rows = diff?.rows ?? [];
|
|
2389
|
+
const needle = searchText.trim().toLowerCase();
|
|
2390
|
+
const groups = [];
|
|
2391
|
+
for (const state of SCHEDULE_PUBLISH_GROUP_ORDER) {
|
|
2392
|
+
const all = rows.filter((row) => row.state === state);
|
|
2393
|
+
if (all.length === 0) {
|
|
2394
|
+
continue;
|
|
2395
|
+
}
|
|
2396
|
+
const matching = needle
|
|
2397
|
+
? all.filter((row) => matchesSchedulePublishSearch(row, needle))
|
|
2398
|
+
: all;
|
|
2399
|
+
if (matching.length === 0) {
|
|
2400
|
+
continue;
|
|
2401
|
+
}
|
|
2402
|
+
groups.push({
|
|
2403
|
+
state,
|
|
2404
|
+
rows: sortSchedulePublishRows(matching),
|
|
2405
|
+
totalCount: all.length,
|
|
2406
|
+
});
|
|
2407
|
+
}
|
|
2408
|
+
return groups;
|
|
2409
|
+
}
|
|
2410
|
+
/**
|
|
2411
|
+
* Plan order, falling back to the row id.
|
|
2412
|
+
*
|
|
2413
|
+
* Deliberately NOT alphabetical: an approver reads the comparison against the
|
|
2414
|
+
* plan as the planner built it, and re-sorting by name would scatter a phase's
|
|
2415
|
+
* rows across the list.
|
|
2416
|
+
*/
|
|
2417
|
+
function sortSchedulePublishRows(rows) {
|
|
2418
|
+
return [...rows].sort((left, right) => {
|
|
2419
|
+
const byOrder = (left.order ?? 0) - (right.order ?? 0);
|
|
2420
|
+
return byOrder !== 0 ? byOrder : left.scheduleItemId - right.scheduleItemId;
|
|
2421
|
+
});
|
|
2422
|
+
}
|
|
2423
|
+
function matchesSchedulePublishSearch(row, lowerNeedle) {
|
|
2424
|
+
if (!lowerNeedle) {
|
|
2425
|
+
return true;
|
|
2426
|
+
}
|
|
2427
|
+
if ((row.name ?? '').toLowerCase().includes(lowerNeedle)) {
|
|
2428
|
+
return true;
|
|
2429
|
+
}
|
|
2430
|
+
return (row.changes ?? []).some((change) => (change.field ?? '').toLowerCase().includes(lowerNeedle));
|
|
2431
|
+
}
|
|
2432
|
+
/**
|
|
2433
|
+
* A stable per-row key for `@for` tracking.
|
|
2434
|
+
*
|
|
2435
|
+
* Includes the state because the SAME schedule item id can legitimately appear
|
|
2436
|
+
* under two groups across a re-render (a row removed then re-added), and reusing
|
|
2437
|
+
* a DOM node across that transition would carry the old group's styling.
|
|
2438
|
+
*/
|
|
2439
|
+
function schedulePublishRowKey(row) {
|
|
2440
|
+
return `${row.state}:${row.scheduleItemId}`;
|
|
2441
|
+
}
|
|
2442
|
+
/**
|
|
2443
|
+
* Changed fields in the order the backend classified them, with blanks dropped.
|
|
2444
|
+
* Added and removed rows have no per-field list - the whole row is the change.
|
|
2445
|
+
*/
|
|
2446
|
+
function readSchedulePublishChanges(row) {
|
|
2447
|
+
return (row?.changes ?? []).filter((change) => !!change && (change.field ?? '').trim().length > 0);
|
|
2448
|
+
}
|
|
2449
|
+
/**
|
|
2450
|
+
* Windowing for a plan large enough to stall a drawer. Returns the first
|
|
2451
|
+
* `limit` rows and how many were withheld, so the caller can say so out loud
|
|
2452
|
+
* rather than silently truncating a comparison someone is approving.
|
|
2453
|
+
*/
|
|
2454
|
+
function windowSchedulePublishRows(rows, limit) {
|
|
2455
|
+
if (limit <= 0 || rows.length <= limit) {
|
|
2456
|
+
return { rows: [...rows], hiddenCount: 0 };
|
|
2457
|
+
}
|
|
2458
|
+
return {
|
|
2459
|
+
rows: rows.slice(0, limit),
|
|
2460
|
+
hiddenCount: rows.length - limit,
|
|
2461
|
+
};
|
|
2462
|
+
}
|
|
2463
|
+
function countByState(rows, state) {
|
|
2464
|
+
return rows.filter((row) => row.state === state).length;
|
|
2465
|
+
}
|
|
2466
|
+
|
|
2467
|
+
/**
|
|
2468
|
+
* The operation key this type renders for. Dispatch is by OPERATION KEY, not by
|
|
2469
|
+
* a new `type` value: the request arrives under the existing `ProcessStep` /
|
|
2470
|
+
* `ProcessRequest` types, so no published Work Center contract shifts.
|
|
2471
|
+
*/
|
|
2472
|
+
const SCHEDULE_PUBLISH_OPERATION_KEY = 'PublishSchedule';
|
|
2473
|
+
/**
|
|
2474
|
+
* Rows rendered per group before windowing kicks in. A publish can carry a
|
|
2475
|
+
* 500-row plan, and a drawer that renders all of it stalls before the approver
|
|
2476
|
+
* can read any of it. The withheld count is shown rather than hidden - a
|
|
2477
|
+
* silently truncated comparison reads as a complete one.
|
|
2478
|
+
*/
|
|
2479
|
+
const ROW_WINDOW_LIMIT = 100;
|
|
2480
|
+
/**
|
|
2481
|
+
* The approver's view of a `LevelData.PublishSchedule` request.
|
|
2482
|
+
*
|
|
2483
|
+
* Exists because the closest existing zero-field action renders a title, a due
|
|
2484
|
+
* date and three buttons: without this an approver would be asked to authorise a
|
|
2485
|
+
* change to the plan of record while being shown less than the planner who
|
|
2486
|
+
* requested it.
|
|
2487
|
+
*
|
|
2488
|
+
* The comparison is the diff FROZEN AT SUBMIT on the backend, not the live one,
|
|
2489
|
+
* so the plan cannot move under the approver while they read it.
|
|
2490
|
+
*/
|
|
2491
|
+
class WorkCenterSchedulePublishType {
|
|
2492
|
+
http = inject(HttpClient);
|
|
2493
|
+
transloco = inject(TranslocoService);
|
|
2494
|
+
destroyRef = inject(DestroyRef);
|
|
2495
|
+
loadSub;
|
|
2496
|
+
details = input.required(...(ngDevMode ? [{ debugName: "details" }] : /* istanbul ignore next */ []));
|
|
2497
|
+
readOnly = input(false, ...(ngDevMode ? [{ debugName: "readOnly" }] : /* istanbul ignore next */ []));
|
|
2498
|
+
activeTab = signal('comparison', ...(ngDevMode ? [{ debugName: "activeTab" }] : /* istanbul ignore next */ []));
|
|
2499
|
+
activeLang = signal(this.transloco.getActiveLang(), ...(ngDevMode ? [{ debugName: "activeLang" }] : /* istanbul ignore next */ []));
|
|
2500
|
+
loading = signal(false, ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
|
|
2501
|
+
error = signal(null, ...(ngDevMode ? [{ debugName: "error" }] : /* istanbul ignore next */ []));
|
|
2502
|
+
diff = signal(null, ...(ngDevMode ? [{ debugName: "diff" }] : /* istanbul ignore next */ []));
|
|
2503
|
+
searchText = signal('', ...(ngDevMode ? [{ debugName: "searchText" }] : /* istanbul ignore next */ []));
|
|
2504
|
+
expandedRowKeys = signal(new Set(), ...(ngDevMode ? [{ debugName: "expandedRowKeys" }] : /* istanbul ignore next */ []));
|
|
2505
|
+
itemContext = computed(() => readItemContext(this.details()), ...(ngDevMode ? [{ debugName: "itemContext" }] : /* istanbul ignore next */ []));
|
|
2506
|
+
requestId = computed(() => readNumber(this.itemContext(), 'requestId') ??
|
|
2507
|
+
readNumber(this.itemContext(), 'draftProcessId'), ...(ngDevMode ? [{ debugName: "requestId" }] : /* istanbul ignore next */ []));
|
|
2508
|
+
levelId = computed(() => readNumber(this.itemContext(), 'levelId'), ...(ngDevMode ? [{ debugName: "levelId" }] : /* istanbul ignore next */ []));
|
|
2509
|
+
levelDataId = computed(() => readNumber(this.itemContext(), 'levelDataId'), ...(ngDevMode ? [{ debugName: "levelDataId" }] : /* istanbul ignore next */ []));
|
|
2510
|
+
moduleKey = computed(() => readString$1(this.itemContext(), 'moduleKey') ?? 'LevelData', ...(ngDevMode ? [{ debugName: "moduleKey" }] : /* istanbul ignore next */ []));
|
|
2511
|
+
requestSchemaId = computed(() => readNumber(this.itemContext(), 'requestSchemaId'), ...(ngDevMode ? [{ debugName: "requestSchemaId" }] : /* istanbul ignore next */ []));
|
|
2512
|
+
summary = computed(() => summarizeSchedulePublish(this.diff()), ...(ngDevMode ? [{ debugName: "summary" }] : /* istanbul ignore next */ []));
|
|
2513
|
+
groups = computed(() => groupSchedulePublishRows(this.diff(), this.searchText()), ...(ngDevMode ? [{ debugName: "groups" }] : /* istanbul ignore next */ []));
|
|
2514
|
+
hasSearch = computed(() => this.searchText().trim().length > 0, ...(ngDevMode ? [{ debugName: "hasSearch" }] : /* istanbul ignore next */ []));
|
|
2515
|
+
/** True when the search hid everything, as opposed to there being nothing to publish. */
|
|
2516
|
+
searchFoundNothing = computed(() => this.hasSearch() && this.groups().length === 0 && !this.summary().isEmpty, ...(ngDevMode ? [{ debugName: "searchFoundNothing" }] : /* istanbul ignore next */ []));
|
|
2517
|
+
resolvedRequestId = computed(() => this.requestId() ?? 0, ...(ngDevMode ? [{ debugName: "resolvedRequestId" }] : /* istanbul ignore next */ []));
|
|
2518
|
+
canRenderPreview = computed(() => this.resolvedRequestId() > 0, ...(ngDevMode ? [{ debugName: "canRenderPreview" }] : /* istanbul ignore next */ []));
|
|
2519
|
+
canRenderDiscussion = computed(() => this.resolvedRequestId() > 0, ...(ngDevMode ? [{ debugName: "canRenderDiscussion" }] : /* istanbul ignore next */ []));
|
|
2520
|
+
tabOptions = computed(() => {
|
|
2521
|
+
this.activeLang();
|
|
2522
|
+
return [
|
|
2523
|
+
{
|
|
2524
|
+
label: this.transloco.translate('workCenter.tabs.publishComparison'),
|
|
2525
|
+
value: 'comparison',
|
|
2526
|
+
icon: 'custom.document-approval',
|
|
2527
|
+
},
|
|
2528
|
+
...(this.canRenderPreview()
|
|
2529
|
+
? [
|
|
2530
|
+
{
|
|
2531
|
+
label: this.transloco.translate('workCenter.tabs.steps'),
|
|
2532
|
+
value: 'workflow',
|
|
2533
|
+
icon: 'custom.flow-chart',
|
|
2534
|
+
},
|
|
2535
|
+
]
|
|
2536
|
+
: []),
|
|
2537
|
+
...(!this.readOnly() && this.canRenderDiscussion()
|
|
2538
|
+
? [
|
|
2539
|
+
{
|
|
2540
|
+
label: this.transloco.translate('workCenter.tabs.discussion'),
|
|
2541
|
+
value: 'discussion',
|
|
2542
|
+
icon: 'custom.discussion-area',
|
|
2543
|
+
},
|
|
2544
|
+
]
|
|
2545
|
+
: []),
|
|
2546
|
+
];
|
|
2547
|
+
}, ...(ngDevMode ? [{ debugName: "tabOptions" }] : /* istanbul ignore next */ []));
|
|
2548
|
+
constructor() {
|
|
2549
|
+
this.transloco.langChanges$
|
|
2550
|
+
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
2551
|
+
.subscribe((lang) => this.activeLang.set(lang));
|
|
2552
|
+
effect(() => {
|
|
2553
|
+
const levelDataId = this.levelDataId();
|
|
2554
|
+
const requestId = this.requestId();
|
|
2555
|
+
if ((levelDataId ?? 0) > 0) {
|
|
2556
|
+
untracked(() => this.loadComparison(levelDataId, requestId));
|
|
2557
|
+
return;
|
|
2558
|
+
}
|
|
2559
|
+
this.diff.set(null);
|
|
2560
|
+
this.error.set(null);
|
|
2561
|
+
});
|
|
2562
|
+
}
|
|
2563
|
+
ngOnDestroy() {
|
|
2564
|
+
this.loadSub?.unsubscribe();
|
|
2565
|
+
}
|
|
2566
|
+
onSearch(value) {
|
|
2567
|
+
this.searchText.set(value);
|
|
2568
|
+
}
|
|
2569
|
+
toggleRow(row) {
|
|
2570
|
+
const key = schedulePublishRowKey(row);
|
|
2571
|
+
const next = new Set(this.expandedRowKeys());
|
|
2572
|
+
if (!next.delete(key)) {
|
|
2573
|
+
next.add(key);
|
|
2574
|
+
}
|
|
2575
|
+
this.expandedRowKeys.set(next);
|
|
2576
|
+
}
|
|
2577
|
+
isExpanded(row) {
|
|
2578
|
+
return this.expandedRowKeys().has(schedulePublishRowKey(row));
|
|
2579
|
+
}
|
|
2580
|
+
rowKey(row) {
|
|
2581
|
+
return schedulePublishRowKey(row);
|
|
2582
|
+
}
|
|
2583
|
+
visibleRows(group) {
|
|
2584
|
+
return windowSchedulePublishRows(group.rows, ROW_WINDOW_LIMIT).rows;
|
|
2585
|
+
}
|
|
2586
|
+
hiddenCount(group) {
|
|
2587
|
+
return windowSchedulePublishRows(group.rows, ROW_WINDOW_LIMIT).hiddenCount;
|
|
2588
|
+
}
|
|
2589
|
+
changesFor(row) {
|
|
2590
|
+
return readSchedulePublishChanges(row);
|
|
2591
|
+
}
|
|
2592
|
+
/**
|
|
2593
|
+
* Only `changed` rows have per-field detail worth expanding. For added and
|
|
2594
|
+
* removed rows the whole row IS the change, so an expander would open onto
|
|
2595
|
+
* nothing.
|
|
2596
|
+
*/
|
|
2597
|
+
isExpandable(row) {
|
|
2598
|
+
return row.state === 'changed' && this.changesFor(row).length > 0;
|
|
2599
|
+
}
|
|
2600
|
+
groupBadgeClass(state) {
|
|
2601
|
+
switch (state) {
|
|
2602
|
+
case 'added':
|
|
2603
|
+
return 'bg-green-100 text-green-700';
|
|
2604
|
+
case 'removed':
|
|
2605
|
+
return 'bg-red-100 text-red-700';
|
|
2606
|
+
case 'changed':
|
|
2607
|
+
return 'bg-amber-100 text-amber-700';
|
|
2608
|
+
default:
|
|
2609
|
+
return 'bg-surface-100 text-surface-700';
|
|
2610
|
+
}
|
|
2611
|
+
}
|
|
2612
|
+
groupLabel(state) {
|
|
2613
|
+
this.activeLang();
|
|
2614
|
+
const translated = this.transloco.translate(`workCenter.publish.group.${state}`);
|
|
2615
|
+
// Transloco echoes the key back on a miss; showing the raw key to an
|
|
2616
|
+
// approver is worse than showing the untranslated state token.
|
|
2617
|
+
return translated === `workCenter.publish.group.${state}`
|
|
2618
|
+
? state
|
|
2619
|
+
: translated;
|
|
2620
|
+
}
|
|
2621
|
+
/**
|
|
2622
|
+
* The field key as a human label, falling back to the key itself on a missing
|
|
2623
|
+
* translation rather than rendering `workCenter.publish.field.plannedStart`.
|
|
2624
|
+
*/
|
|
2625
|
+
fieldLabel(field) {
|
|
2626
|
+
this.activeLang();
|
|
2627
|
+
const key = `workCenter.publish.field.${field}`;
|
|
2628
|
+
const translated = this.transloco.translate(key);
|
|
2629
|
+
return translated === key ? field : translated;
|
|
2630
|
+
}
|
|
2631
|
+
/**
|
|
2632
|
+
* One side of a field change, projected into an `EntityData` so it renders
|
|
2633
|
+
* through its NATIVE view type - a date as a date, a user as an avatar -
|
|
2634
|
+
* rather than as the stored string.
|
|
2635
|
+
*/
|
|
2636
|
+
sideEntity(change, side) {
|
|
2637
|
+
const raw = side === 'published' ? change.publishedValue : change.currentValue;
|
|
2638
|
+
return {
|
|
2639
|
+
key: `${change.field}:${side}`,
|
|
2640
|
+
name: this.fieldLabel(change.field),
|
|
2641
|
+
value: (raw ?? ''),
|
|
2642
|
+
rawValue: raw === null || raw === undefined ? undefined : String(raw),
|
|
2643
|
+
viewType: (change.viewType ?? 'Text'),
|
|
2644
|
+
order: 0,
|
|
2645
|
+
};
|
|
2646
|
+
}
|
|
2647
|
+
loadComparison(levelDataId, requestId) {
|
|
2648
|
+
this.loadSub?.unsubscribe();
|
|
2649
|
+
this.loading.set(true);
|
|
2650
|
+
this.error.set(null);
|
|
2651
|
+
// process-form-load is the seam: the backend returns the diff frozen at
|
|
2652
|
+
// submit for a pending request, and falls back to the live diff for the
|
|
2653
|
+
// pre-submit preview. Asking the schedule's own publish-diff endpoint
|
|
2654
|
+
// instead would show the approver a diff that keeps moving.
|
|
2655
|
+
this.loadSub = this.http
|
|
2656
|
+
.post('process-form-load', {
|
|
2657
|
+
moduleKey: this.moduleKey(),
|
|
2658
|
+
operationKey: SCHEDULE_PUBLISH_OPERATION_KEY,
|
|
2659
|
+
levelId: this.levelId(),
|
|
2660
|
+
levelDataId,
|
|
2661
|
+
recordId: levelDataId,
|
|
2662
|
+
requestSchemaId: this.requestSchemaId(),
|
|
2663
|
+
draftProcessId: requestId,
|
|
2664
|
+
preview: true,
|
|
2665
|
+
})
|
|
2666
|
+
.subscribe({
|
|
2667
|
+
next: (response) => {
|
|
2668
|
+
this.loading.set(false);
|
|
2669
|
+
this.diff.set(extractDiff(response));
|
|
2670
|
+
},
|
|
2671
|
+
error: (error) => {
|
|
2672
|
+
this.loading.set(false);
|
|
2673
|
+
this.diff.set(null);
|
|
2674
|
+
this.error.set(error?.error?.message ??
|
|
2675
|
+
error?.message ??
|
|
2676
|
+
this.transloco.translate('workCenter.error.failedLoadSchedulePublish'));
|
|
2677
|
+
},
|
|
2678
|
+
});
|
|
2679
|
+
}
|
|
2680
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: WorkCenterSchedulePublishType, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2681
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: WorkCenterSchedulePublishType, isStandalone: true, selector: "mt-work-center-schedule-publish-type", inputs: { details: { classPropertyName: "details", publicName: "details", isSignal: true, isRequired: true, transformFunction: null }, readOnly: { classPropertyName: "readOnly", publicName: "readOnly", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div\n class=\"mt-modal-content flex h-full min-h-0 flex-col gap-4 p-4 lg:overflow-hidden\"\n *transloco=\"let t; prefix: 'workCenter'\"\n>\n <div\n class=\"flex flex-1 min-h-0 flex-col gap-4 lg:grid lg:grid-cols-[13rem_minmax(0,1fr)] lg:gap-6\"\n >\n @if (tabOptions().length > 1) {\n <mt-tabs\n [(active)]=\"activeTab\"\n [options]=\"tabOptions()\"\n mode=\"vertical\"\n size=\"small\"\n />\n }\n\n <div class=\"min-w-0 flex-1 min-h-0 lg:overflow-hidden\">\n <!-- Comparison -->\n <div\n class=\"h-full min-h-0 lg:overflow-hidden\"\n [hidden]=\"activeTab() !== 'comparison'\"\n >\n <div\n class=\"flex h-full min-h-0 min-w-0 flex-col gap-3 lg:overflow-hidden\"\n >\n @if (loading()) {\n <div\n class=\"flex min-h-[22rem] items-center justify-center rounded-lg border border-dashed border-surface-300 bg-surface-50 p-6\"\n >\n <p class=\"max-w-md text-center text-sm text-surface-500\">\n {{ t(\"loading.schedulePublish\") }}\n </p>\n </div>\n } @else if (error(); as loadError) {\n <div\n class=\"flex min-h-[22rem] items-center justify-center rounded-lg border border-dashed border-red-300 bg-red-50 p-6\"\n >\n <p class=\"max-w-md text-center text-sm text-red-600\">\n {{ loadError }}\n </p>\n </div>\n } @else if (!summary().hasSnapshot) {\n <!-- Never published: nothing has been announced, so nothing can\n differ from it. That is a different statement from \"no changes\". -->\n <div\n class=\"flex min-h-[22rem] items-center justify-center rounded-lg border border-dashed border-surface-300 bg-surface-50 p-6\"\n >\n <p class=\"max-w-md text-center text-sm text-surface-500\">\n {{ t(\"publish.neverPublished\") }}\n </p>\n </div>\n } @else if (summary().isEmpty) {\n <div\n class=\"flex min-h-[22rem] items-center justify-center rounded-lg border border-dashed border-surface-300 bg-surface-50 p-6\"\n >\n <p class=\"max-w-md text-center text-sm text-surface-500\">\n {{ t(\"publish.nothingToPublish\") }}\n </p>\n </div>\n } @else {\n <!-- Counts stay visible while searching, so a filtered view never\n reads as the whole request. -->\n <div class=\"flex flex-wrap items-center gap-2\">\n <span\n class=\"rounded-full px-2 py-0.5 text-xs font-medium\"\n [class]=\"groupBadgeClass('added')\"\n >{{\n t(\"publish.addedCount\", { count: summary().addedCount })\n }}</span\n >\n <span\n class=\"rounded-full px-2 py-0.5 text-xs font-medium\"\n [class]=\"groupBadgeClass('changed')\"\n >{{\n t(\"publish.changedCount\", { count: summary().changedCount })\n }}</span\n >\n <span\n class=\"rounded-full px-2 py-0.5 text-xs font-medium\"\n [class]=\"groupBadgeClass('removed')\"\n >{{\n t(\"publish.removedCount\", { count: summary().removedCount })\n }}</span\n >\n </div>\n\n <input\n type=\"search\"\n class=\"w-full rounded-md border border-surface-300 bg-surface-0 px-3 py-2 text-sm outline-none focus:border-primary-400\"\n [placeholder]=\"t('publish.searchPlaceholder')\"\n [value]=\"searchText()\"\n (input)=\"onSearch($any($event.target).value)\"\n />\n\n <div\n class=\"min-h-0 flex-1 lg:overflow-y-auto lg:overscroll-contain lg:pr-2\"\n >\n @if (searchFoundNothing()) {\n <div\n class=\"flex min-h-[12rem] items-center justify-center rounded-lg border border-dashed border-surface-300 bg-surface-50 p-6\"\n >\n <p class=\"text-sm text-surface-500\">\n {{ t(\"publish.noSearchResults\") }}\n </p>\n </div>\n }\n\n @for (group of groups(); track group.state) {\n <section class=\"mb-4\">\n <header class=\"mb-2 flex items-center gap-2\">\n <h4 class=\"text-sm font-semibold text-surface-700\">\n {{ groupLabel(group.state) }}\n </h4>\n <span\n class=\"rounded-full px-2 py-0.5 text-xs font-medium\"\n [class]=\"groupBadgeClass(group.state)\"\n >{{ group.totalCount }}</span\n >\n </header>\n\n <ul class=\"flex flex-col gap-1\">\n @for (row of visibleRows(group); track rowKey(row)) {\n <li\n class=\"rounded-md border border-surface-200 bg-surface-0\"\n >\n <button\n type=\"button\"\n class=\"flex w-full items-center gap-2 px-3 py-2 text-start\"\n [class.cursor-default]=\"!isExpandable(row)\"\n [attr.aria-expanded]=\"\n isExpandable(row) ? isExpanded(row) : null\n \"\n [disabled]=\"!isExpandable(row)\"\n (click)=\"toggleRow(row)\"\n >\n @if (isExpandable(row)) {\n <span\n class=\"shrink-0 text-xs text-surface-500\"\n aria-hidden=\"true\"\n >{{ isExpanded(row) ? \"\u25BE\" : \"\u25B8\" }}</span\n >\n } @else {\n <span\n class=\"w-3 shrink-0\"\n aria-hidden=\"true\"\n ></span>\n }\n <span\n class=\"min-w-0 flex-1 truncate text-sm text-surface-800\"\n >{{ row.name }}</span\n >\n @if (row.type) {\n <span class=\"shrink-0 text-xs text-surface-500\">{{\n row.type\n }}</span>\n }\n </button>\n\n @if (isExpandable(row) && isExpanded(row)) {\n <div\n class=\"flex flex-col gap-3 border-t border-surface-200 px-3 py-3\"\n >\n @for (\n change of changesFor(row);\n track change.field\n ) {\n <div class=\"flex min-w-0 flex-col gap-1\">\n <span\n class=\"text-xs font-medium text-surface-600\"\n >{{ fieldLabel(change.field) }}</span\n >\n <!-- Before -> After rendered locally rather than\n via mt-comparison-value, whose Before/After\n labels are hardcoded English. Each side still\n goes through mt-entity-preview-body, so a date\n renders as a date and a user as an avatar. -->\n <div\n class=\"flex w-full min-w-0 items-stretch gap-3\"\n >\n <div\n class=\"flex min-w-0 flex-1 flex-col gap-1 rounded-md border border-surface-200 bg-surface-50 px-3 py-2\"\n >\n <span\n class=\"text-xs font-medium uppercase tracking-wide text-muted-color\"\n >{{ t(\"publish.before\") }}</span\n >\n <mt-entity-preview-body\n [data]=\"sideEntity(change, 'published')\"\n />\n </div>\n <div\n class=\"flex shrink-0 items-center text-muted-color\"\n aria-hidden=\"true\"\n >\n →\n </div>\n <div\n class=\"flex min-w-0 flex-1 flex-col gap-1 rounded-md border border-primary-200 bg-primary-50 px-3 py-2\"\n >\n <span\n class=\"text-xs font-medium uppercase tracking-wide text-primary-600\"\n >{{ t(\"publish.after\") }}</span\n >\n <mt-entity-preview-body\n [data]=\"sideEntity(change, 'current')\"\n />\n </div>\n </div>\n </div>\n }\n </div>\n }\n </li>\n }\n </ul>\n\n @if (hiddenCount(group) > 0) {\n <!-- Said out loud: a silently truncated comparison reads as a\n complete one to whoever is approving it. -->\n <p class=\"mt-2 text-xs text-surface-500\">\n {{\n t(\"publish.hiddenRows\", { count: hiddenCount(group) })\n }}\n </p>\n }\n </section>\n }\n </div>\n }\n </div>\n </div>\n\n <!-- Workflow -->\n @if (canRenderPreview()) {\n <div\n class=\"h-full min-h-0 lg:overflow-hidden\"\n [hidden]=\"activeTab() !== 'workflow'\"\n >\n <div\n class=\"min-w-0 h-full min-h-0 lg:overflow-y-auto lg:overscroll-contain lg:pr-2\"\n >\n <mt-work-center-process-preview\n [requestId]=\"requestId()\"\n [view]=\"'schema'\"\n />\n </div>\n </div>\n }\n\n <!-- Discussion -->\n @if (!readOnly() && canRenderDiscussion()) {\n <div\n class=\"h-full min-h-0 lg:overflow-hidden\"\n [hidden]=\"activeTab() !== 'discussion'\"\n >\n <div\n class=\"h-[32rem] min-h-[22rem] overflow-hidden rounded-lg border border-surface-200 bg-surface-50 lg:h-full lg:min-h-0\"\n >\n <mt-discussion-thread\n [moduleType]=\"'ProcessRequest'\"\n [recordId]=\"resolvedRequestId()\"\n [mentionSearchEndpoint]=\"'Identity/users'\"\n [mentionSearchParam]=\"'query'\"\n [mentionSearchDataPath]=\"'data'\"\n [uploadEndpoint]=\"'uploader'\"\n [attachmentDownloadEndpoint]=\"'uploader'\"\n [showParticipants]=\"true\"\n [autoMarkRead]=\"true\"\n [refreshIntervalMs]=\"0\"\n [styleClass]=\"'h-full'\"\n />\n </div>\n </div>\n }\n </div>\n </div>\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "component", type: Tabs, selector: "mt-tabs", inputs: ["options", "optionLabel", "optionValue", "active", "mode", "moreLabel", "defaultIcon", "size", "fluid", "disabled", "searchThreshold"], outputs: ["activeChange", "onChange"] }, { kind: "component", type: DiscussionThread, selector: "mt-discussion-thread", inputs: ["moduleType", "recordId", "title", "subtitle", "placeholder", "pageSize", "currentUserId", "requestContext", "mentionableUsers", "mentionSearchEndpoint", "mentionSearchParam", "mentionSearchDataPath", "allowAttachments", "uploadEndpoint", "attachmentDownloadEndpoint", "showParticipants", "autoMarkRead", "refreshIntervalMs", "styleClass", "disabled"], outputs: ["loaded", "errored", "commentCreated", "commentUpdated", "commentDeleted", "readStateChanged"] }, { kind: "component", type: EntityPreviewBody, selector: "mt-entity-preview-body", inputs: ["data", "attachmentShape"] }, { kind: "component", type: WorkCenterProcessPreview, selector: "mt-work-center-process-preview", inputs: ["requestId", "view"] }] });
|
|
2682
|
+
}
|
|
2683
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: WorkCenterSchedulePublishType, decorators: [{
|
|
2684
|
+
type: Component,
|
|
2685
|
+
args: [{ selector: 'mt-work-center-schedule-publish-type', standalone: true, imports: [
|
|
2686
|
+
CommonModule,
|
|
2687
|
+
TranslocoDirective,
|
|
2688
|
+
Tabs,
|
|
2689
|
+
DiscussionThread,
|
|
2690
|
+
EntityPreviewBody,
|
|
2691
|
+
WorkCenterProcessPreview,
|
|
2692
|
+
], template: "<div\n class=\"mt-modal-content flex h-full min-h-0 flex-col gap-4 p-4 lg:overflow-hidden\"\n *transloco=\"let t; prefix: 'workCenter'\"\n>\n <div\n class=\"flex flex-1 min-h-0 flex-col gap-4 lg:grid lg:grid-cols-[13rem_minmax(0,1fr)] lg:gap-6\"\n >\n @if (tabOptions().length > 1) {\n <mt-tabs\n [(active)]=\"activeTab\"\n [options]=\"tabOptions()\"\n mode=\"vertical\"\n size=\"small\"\n />\n }\n\n <div class=\"min-w-0 flex-1 min-h-0 lg:overflow-hidden\">\n <!-- Comparison -->\n <div\n class=\"h-full min-h-0 lg:overflow-hidden\"\n [hidden]=\"activeTab() !== 'comparison'\"\n >\n <div\n class=\"flex h-full min-h-0 min-w-0 flex-col gap-3 lg:overflow-hidden\"\n >\n @if (loading()) {\n <div\n class=\"flex min-h-[22rem] items-center justify-center rounded-lg border border-dashed border-surface-300 bg-surface-50 p-6\"\n >\n <p class=\"max-w-md text-center text-sm text-surface-500\">\n {{ t(\"loading.schedulePublish\") }}\n </p>\n </div>\n } @else if (error(); as loadError) {\n <div\n class=\"flex min-h-[22rem] items-center justify-center rounded-lg border border-dashed border-red-300 bg-red-50 p-6\"\n >\n <p class=\"max-w-md text-center text-sm text-red-600\">\n {{ loadError }}\n </p>\n </div>\n } @else if (!summary().hasSnapshot) {\n <!-- Never published: nothing has been announced, so nothing can\n differ from it. That is a different statement from \"no changes\". -->\n <div\n class=\"flex min-h-[22rem] items-center justify-center rounded-lg border border-dashed border-surface-300 bg-surface-50 p-6\"\n >\n <p class=\"max-w-md text-center text-sm text-surface-500\">\n {{ t(\"publish.neverPublished\") }}\n </p>\n </div>\n } @else if (summary().isEmpty) {\n <div\n class=\"flex min-h-[22rem] items-center justify-center rounded-lg border border-dashed border-surface-300 bg-surface-50 p-6\"\n >\n <p class=\"max-w-md text-center text-sm text-surface-500\">\n {{ t(\"publish.nothingToPublish\") }}\n </p>\n </div>\n } @else {\n <!-- Counts stay visible while searching, so a filtered view never\n reads as the whole request. -->\n <div class=\"flex flex-wrap items-center gap-2\">\n <span\n class=\"rounded-full px-2 py-0.5 text-xs font-medium\"\n [class]=\"groupBadgeClass('added')\"\n >{{\n t(\"publish.addedCount\", { count: summary().addedCount })\n }}</span\n >\n <span\n class=\"rounded-full px-2 py-0.5 text-xs font-medium\"\n [class]=\"groupBadgeClass('changed')\"\n >{{\n t(\"publish.changedCount\", { count: summary().changedCount })\n }}</span\n >\n <span\n class=\"rounded-full px-2 py-0.5 text-xs font-medium\"\n [class]=\"groupBadgeClass('removed')\"\n >{{\n t(\"publish.removedCount\", { count: summary().removedCount })\n }}</span\n >\n </div>\n\n <input\n type=\"search\"\n class=\"w-full rounded-md border border-surface-300 bg-surface-0 px-3 py-2 text-sm outline-none focus:border-primary-400\"\n [placeholder]=\"t('publish.searchPlaceholder')\"\n [value]=\"searchText()\"\n (input)=\"onSearch($any($event.target).value)\"\n />\n\n <div\n class=\"min-h-0 flex-1 lg:overflow-y-auto lg:overscroll-contain lg:pr-2\"\n >\n @if (searchFoundNothing()) {\n <div\n class=\"flex min-h-[12rem] items-center justify-center rounded-lg border border-dashed border-surface-300 bg-surface-50 p-6\"\n >\n <p class=\"text-sm text-surface-500\">\n {{ t(\"publish.noSearchResults\") }}\n </p>\n </div>\n }\n\n @for (group of groups(); track group.state) {\n <section class=\"mb-4\">\n <header class=\"mb-2 flex items-center gap-2\">\n <h4 class=\"text-sm font-semibold text-surface-700\">\n {{ groupLabel(group.state) }}\n </h4>\n <span\n class=\"rounded-full px-2 py-0.5 text-xs font-medium\"\n [class]=\"groupBadgeClass(group.state)\"\n >{{ group.totalCount }}</span\n >\n </header>\n\n <ul class=\"flex flex-col gap-1\">\n @for (row of visibleRows(group); track rowKey(row)) {\n <li\n class=\"rounded-md border border-surface-200 bg-surface-0\"\n >\n <button\n type=\"button\"\n class=\"flex w-full items-center gap-2 px-3 py-2 text-start\"\n [class.cursor-default]=\"!isExpandable(row)\"\n [attr.aria-expanded]=\"\n isExpandable(row) ? isExpanded(row) : null\n \"\n [disabled]=\"!isExpandable(row)\"\n (click)=\"toggleRow(row)\"\n >\n @if (isExpandable(row)) {\n <span\n class=\"shrink-0 text-xs text-surface-500\"\n aria-hidden=\"true\"\n >{{ isExpanded(row) ? \"\u25BE\" : \"\u25B8\" }}</span\n >\n } @else {\n <span\n class=\"w-3 shrink-0\"\n aria-hidden=\"true\"\n ></span>\n }\n <span\n class=\"min-w-0 flex-1 truncate text-sm text-surface-800\"\n >{{ row.name }}</span\n >\n @if (row.type) {\n <span class=\"shrink-0 text-xs text-surface-500\">{{\n row.type\n }}</span>\n }\n </button>\n\n @if (isExpandable(row) && isExpanded(row)) {\n <div\n class=\"flex flex-col gap-3 border-t border-surface-200 px-3 py-3\"\n >\n @for (\n change of changesFor(row);\n track change.field\n ) {\n <div class=\"flex min-w-0 flex-col gap-1\">\n <span\n class=\"text-xs font-medium text-surface-600\"\n >{{ fieldLabel(change.field) }}</span\n >\n <!-- Before -> After rendered locally rather than\n via mt-comparison-value, whose Before/After\n labels are hardcoded English. Each side still\n goes through mt-entity-preview-body, so a date\n renders as a date and a user as an avatar. -->\n <div\n class=\"flex w-full min-w-0 items-stretch gap-3\"\n >\n <div\n class=\"flex min-w-0 flex-1 flex-col gap-1 rounded-md border border-surface-200 bg-surface-50 px-3 py-2\"\n >\n <span\n class=\"text-xs font-medium uppercase tracking-wide text-muted-color\"\n >{{ t(\"publish.before\") }}</span\n >\n <mt-entity-preview-body\n [data]=\"sideEntity(change, 'published')\"\n />\n </div>\n <div\n class=\"flex shrink-0 items-center text-muted-color\"\n aria-hidden=\"true\"\n >\n →\n </div>\n <div\n class=\"flex min-w-0 flex-1 flex-col gap-1 rounded-md border border-primary-200 bg-primary-50 px-3 py-2\"\n >\n <span\n class=\"text-xs font-medium uppercase tracking-wide text-primary-600\"\n >{{ t(\"publish.after\") }}</span\n >\n <mt-entity-preview-body\n [data]=\"sideEntity(change, 'current')\"\n />\n </div>\n </div>\n </div>\n }\n </div>\n }\n </li>\n }\n </ul>\n\n @if (hiddenCount(group) > 0) {\n <!-- Said out loud: a silently truncated comparison reads as a\n complete one to whoever is approving it. -->\n <p class=\"mt-2 text-xs text-surface-500\">\n {{\n t(\"publish.hiddenRows\", { count: hiddenCount(group) })\n }}\n </p>\n }\n </section>\n }\n </div>\n }\n </div>\n </div>\n\n <!-- Workflow -->\n @if (canRenderPreview()) {\n <div\n class=\"h-full min-h-0 lg:overflow-hidden\"\n [hidden]=\"activeTab() !== 'workflow'\"\n >\n <div\n class=\"min-w-0 h-full min-h-0 lg:overflow-y-auto lg:overscroll-contain lg:pr-2\"\n >\n <mt-work-center-process-preview\n [requestId]=\"requestId()\"\n [view]=\"'schema'\"\n />\n </div>\n </div>\n }\n\n <!-- Discussion -->\n @if (!readOnly() && canRenderDiscussion()) {\n <div\n class=\"h-full min-h-0 lg:overflow-hidden\"\n [hidden]=\"activeTab() !== 'discussion'\"\n >\n <div\n class=\"h-[32rem] min-h-[22rem] overflow-hidden rounded-lg border border-surface-200 bg-surface-50 lg:h-full lg:min-h-0\"\n >\n <mt-discussion-thread\n [moduleType]=\"'ProcessRequest'\"\n [recordId]=\"resolvedRequestId()\"\n [mentionSearchEndpoint]=\"'Identity/users'\"\n [mentionSearchParam]=\"'query'\"\n [mentionSearchDataPath]=\"'data'\"\n [uploadEndpoint]=\"'uploader'\"\n [attachmentDownloadEndpoint]=\"'uploader'\"\n [showParticipants]=\"true\"\n [autoMarkRead]=\"true\"\n [refreshIntervalMs]=\"0\"\n [styleClass]=\"'h-full'\"\n />\n </div>\n </div>\n }\n </div>\n </div>\n</div>\n" }]
|
|
2693
|
+
}], ctorParameters: () => [], propDecorators: { details: [{ type: i0.Input, args: [{ isSignal: true, alias: "details", required: true }] }], readOnly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readOnly", required: false }] }] } });
|
|
2694
|
+
/**
|
|
2695
|
+
* The comparison arrives on the response root, but several runtime endpoints
|
|
2696
|
+
* wrap their payload in `{ data: ... }` - tolerate both rather than rendering an
|
|
2697
|
+
* empty approval screen because of an envelope.
|
|
2698
|
+
*/
|
|
2699
|
+
function extractDiff(response) {
|
|
2700
|
+
if (!response || typeof response !== 'object') {
|
|
2701
|
+
return null;
|
|
2702
|
+
}
|
|
2703
|
+
return response.schedulePublish ?? response.data?.schedulePublish ?? null;
|
|
2704
|
+
}
|
|
2705
|
+
|
|
2706
|
+
/**
|
|
2707
|
+
* Host-side registration for Work Center detail renderers.
|
|
2708
|
+
*
|
|
2709
|
+
* The built-in dispatch is a hardcoded `@if` chain, so every new request type
|
|
2710
|
+
* has meant a package release. This token is the seam that makes the NEXT one a
|
|
2711
|
+
* host registration instead. It is strictly additive: entries here are consulted
|
|
2712
|
+
* before the built-in chain, so an unregistered host behaves exactly as before.
|
|
2713
|
+
*
|
|
2714
|
+
* Key by `operationKey:<OperationKey>` when the request arrives under an
|
|
2715
|
+
* existing `type` - which is how `LevelData.PublishSchedule` is dispatched,
|
|
2716
|
+
* deliberately avoiding a shift in the published `type` contract.
|
|
2717
|
+
*
|
|
2718
|
+
* @example
|
|
2719
|
+
* providers: [
|
|
2720
|
+
* {
|
|
2721
|
+
* provide: WORK_CENTER_TYPE_COMPONENTS,
|
|
2722
|
+
* useValue: { 'operationKey:ApproveBudget': ApproveBudgetType },
|
|
2723
|
+
* },
|
|
2724
|
+
* ]
|
|
2725
|
+
*/
|
|
2726
|
+
const WORK_CENTER_TYPE_COMPONENTS = new InjectionToken('WORK_CENTER_TYPE_COMPONENTS');
|
|
2727
|
+
|
|
2333
2728
|
class WorkCenterItemModal {
|
|
2729
|
+
registry = inject(WORK_CENTER_TYPE_COMPONENTS, {
|
|
2730
|
+
optional: true,
|
|
2731
|
+
});
|
|
2334
2732
|
details = input.required(...(ngDevMode ? [{ debugName: "details" }] : /* istanbul ignore next */ []));
|
|
2335
2733
|
lookups = input(null, ...(ngDevMode ? [{ debugName: "lookups" }] : /* istanbul ignore next */ []));
|
|
2336
2734
|
readOnly = input(false, ...(ngDevMode ? [{ debugName: "readOnly" }] : /* istanbul ignore next */ []));
|
|
2735
|
+
/**
|
|
2736
|
+
* The request's operation key, read from the item context.
|
|
2737
|
+
*
|
|
2738
|
+
* `LevelData.PublishSchedule` is dispatched on THIS rather than on a new
|
|
2739
|
+
* `type` value: the request arrives under the existing `ProcessStep` /
|
|
2740
|
+
* `ProcessRequest` types, so no published Work Center contract shifts.
|
|
2741
|
+
*/
|
|
2742
|
+
operationKey = computed(() => readString$1(readItemContext(this.details()), 'operationKey') ?? '', ...(ngDevMode ? [{ debugName: "operationKey" }] : /* istanbul ignore next */ []));
|
|
2743
|
+
isSchedulePublish = computed(() => this.operationKey() === SCHEDULE_PUBLISH_OPERATION_KEY, ...(ngDevMode ? [{ debugName: "isSchedulePublish" }] : /* istanbul ignore next */ []));
|
|
2744
|
+
/**
|
|
2745
|
+
* A host-registered renderer for this item, if one is registered. Checked
|
|
2746
|
+
* before the built-in chain, keyed by `operationKey:<key>` first so a host can
|
|
2747
|
+
* specialise an existing type without replacing it wholesale.
|
|
2748
|
+
*/
|
|
2749
|
+
registeredComponent = computed(() => {
|
|
2750
|
+
const registry = this.registry;
|
|
2751
|
+
if (!registry) {
|
|
2752
|
+
return null;
|
|
2753
|
+
}
|
|
2754
|
+
const operationKey = this.operationKey();
|
|
2755
|
+
return ((operationKey ? registry[`operationKey:${operationKey}`] : undefined) ??
|
|
2756
|
+
registry[this.details().type ?? ''] ??
|
|
2757
|
+
null);
|
|
2758
|
+
}, ...(ngDevMode ? [{ debugName: "registeredComponent" }] : /* istanbul ignore next */ []));
|
|
2759
|
+
registeredInputs = computed(() => ({
|
|
2760
|
+
details: this.details(),
|
|
2761
|
+
lookups: this.lookups(),
|
|
2762
|
+
readOnly: this.readOnly(),
|
|
2763
|
+
}), ...(ngDevMode ? [{ debugName: "registeredInputs" }] : /* istanbul ignore next */ []));
|
|
2337
2764
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: WorkCenterItemModal, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2338
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: WorkCenterItemModal, isStandalone: true, selector: "mt-work-center-item-modal", inputs: { details: { classPropertyName: "details", publicName: "details", isSignal: true, isRequired: true, transformFunction: null }, lookups: { classPropertyName: "lookups", publicName: "lookups", isSignal: true, isRequired: false, transformFunction: null }, readOnly: { classPropertyName: "readOnly", publicName: "readOnly", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "block h-full min-h-0" }, ngImport: i0, template: "<ng-container *transloco=\"let t; prefix: 'workCenter'\">\
|
|
2765
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: WorkCenterItemModal, isStandalone: true, selector: "mt-work-center-item-modal", inputs: { details: { classPropertyName: "details", publicName: "details", isSignal: true, isRequired: true, transformFunction: null }, lookups: { classPropertyName: "lookups", publicName: "lookups", isSignal: true, isRequired: false, transformFunction: null }, readOnly: { classPropertyName: "readOnly", publicName: "readOnly", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "block h-full min-h-0" }, ngImport: i0, template: "<ng-container *transloco=\"let t; prefix: 'workCenter'\">\n @if (registeredComponent(); as registered) {\n <!-- Host registration wins, so a new request type no longer needs a package\n release. Nothing is registered by default, so the chain below is what\n every existing host still gets. -->\n <ng-container\n *ngComponentOutlet=\"registered; inputs: registeredInputs()\"\n ></ng-container>\n } @else if (isSchedulePublish()) {\n <!-- Dispatched on the OPERATION KEY, not on a new `type`: a publish request\n arrives as ProcessStep or ProcessRequest like any other, so the published\n Work Center type contract is unchanged. -->\n <mt-work-center-schedule-publish-type\n [details]=\"details()\"\n [readOnly]=\"readOnly()\"\n />\n } @else if (details().type === \"ProcessStep\") {\n <mt-work-center-process-step-type\n [details]=\"details()\"\n [lookups]=\"lookups()\"\n [readOnly]=\"readOnly()\"\n />\n } @else if (details().type === \"ProcessRequest\") {\n <mt-work-center-process-request-type\n [details]=\"details()\"\n [lookups]=\"lookups()\"\n [readOnly]=\"readOnly()\"\n />\n } @else if (details().type === \"EscalationInstance\") {\n <mt-work-center-escalation-instance-type\n [details]=\"details()\"\n [readOnly]=\"readOnly()\"\n />\n } @else if (details().type === \"Module\" || details().type === \"GeneralTask\") {\n <mt-work-center-module-type\n [details]=\"details()\"\n [lookups]=\"lookups()\"\n [readOnly]=\"readOnly()\"\n />\n } @else {\n <div class=\"mt-modal-content flex h-full items-center justify-center p-4\">\n <p class=\"text-sm text-surface-500\">{{ t(\"modal.unsupportedType\") }}</p>\n </div>\n }\n</ng-container>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "component", type: WorkCenterProcessStepType, selector: "mt-work-center-process-step-type", inputs: ["details", "lookups", "readOnly"] }, { kind: "component", type: WorkCenterProcessRequestType, selector: "mt-work-center-process-request-type", inputs: ["details", "lookups", "readOnly"] }, { kind: "component", type: WorkCenterEscalationInstanceType, selector: "mt-work-center-escalation-instance-type", inputs: ["details", "readOnly"] }, { kind: "component", type: WorkCenterModuleType, selector: "mt-work-center-module-type", inputs: ["details", "lookups", "readOnly"] }, { kind: "component", type: WorkCenterSchedulePublishType, selector: "mt-work-center-schedule-publish-type", inputs: ["details", "readOnly"] }] });
|
|
2339
2766
|
}
|
|
2340
2767
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: WorkCenterItemModal, decorators: [{
|
|
2341
2768
|
type: Component,
|
|
2342
2769
|
args: [{ selector: 'mt-work-center-item-modal', standalone: true, imports: [
|
|
2343
2770
|
CommonModule,
|
|
2771
|
+
NgComponentOutlet,
|
|
2344
2772
|
TranslocoDirective,
|
|
2345
2773
|
WorkCenterProcessStepType,
|
|
2346
2774
|
WorkCenterProcessRequestType,
|
|
2347
2775
|
WorkCenterEscalationInstanceType,
|
|
2348
2776
|
WorkCenterModuleType,
|
|
2777
|
+
WorkCenterSchedulePublishType,
|
|
2349
2778
|
], host: {
|
|
2350
2779
|
class: 'block h-full min-h-0',
|
|
2351
|
-
}, template: "<ng-container *transloco=\"let t; prefix: 'workCenter'\">\
|
|
2780
|
+
}, template: "<ng-container *transloco=\"let t; prefix: 'workCenter'\">\n @if (registeredComponent(); as registered) {\n <!-- Host registration wins, so a new request type no longer needs a package\n release. Nothing is registered by default, so the chain below is what\n every existing host still gets. -->\n <ng-container\n *ngComponentOutlet=\"registered; inputs: registeredInputs()\"\n ></ng-container>\n } @else if (isSchedulePublish()) {\n <!-- Dispatched on the OPERATION KEY, not on a new `type`: a publish request\n arrives as ProcessStep or ProcessRequest like any other, so the published\n Work Center type contract is unchanged. -->\n <mt-work-center-schedule-publish-type\n [details]=\"details()\"\n [readOnly]=\"readOnly()\"\n />\n } @else if (details().type === \"ProcessStep\") {\n <mt-work-center-process-step-type\n [details]=\"details()\"\n [lookups]=\"lookups()\"\n [readOnly]=\"readOnly()\"\n />\n } @else if (details().type === \"ProcessRequest\") {\n <mt-work-center-process-request-type\n [details]=\"details()\"\n [lookups]=\"lookups()\"\n [readOnly]=\"readOnly()\"\n />\n } @else if (details().type === \"EscalationInstance\") {\n <mt-work-center-escalation-instance-type\n [details]=\"details()\"\n [readOnly]=\"readOnly()\"\n />\n } @else if (details().type === \"Module\" || details().type === \"GeneralTask\") {\n <mt-work-center-module-type\n [details]=\"details()\"\n [lookups]=\"lookups()\"\n [readOnly]=\"readOnly()\"\n />\n } @else {\n <div class=\"mt-modal-content flex h-full items-center justify-center p-4\">\n <p class=\"text-sm text-surface-500\">{{ t(\"modal.unsupportedType\") }}</p>\n </div>\n }\n</ng-container>\n" }]
|
|
2352
2781
|
}], propDecorators: { details: [{ type: i0.Input, args: [{ isSignal: true, alias: "details", required: true }] }], lookups: [{ type: i0.Input, args: [{ isSignal: true, alias: "lookups", required: false }] }], readOnly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readOnly", required: false }] }] } });
|
|
2353
2782
|
|
|
2354
2783
|
const FORM_VALIDATION_WARNINGS = 'FormValidationWarnings';
|
|
@@ -3114,7 +3543,7 @@ class WorkCenterItemModalRoute {
|
|
|
3114
3543
|
};
|
|
3115
3544
|
}
|
|
3116
3545
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: WorkCenterItemModalRoute, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3117
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: WorkCenterItemModalRoute, isStandalone: true, selector: "mt-work-center-item-modal-route", inputs: { contextKey: { classPropertyName: "contextKey", publicName: "contextKey", isSignal: true, isRequired: false, transformFunction: null }, manageRouteOutlet: { classPropertyName: "manageRouteOutlet", publicName: "manageRouteOutlet", isSignal: true, isRequired: false, transformFunction: null }, showPreviewLevelAction: { classPropertyName: "showPreviewLevelAction", publicName: "showPreviewLevelAction", isSignal: true, isRequired: false, transformFunction: null }, readOnly: { classPropertyName: "readOnly", publicName: "readOnly", isSignal: true, isRequired: false, transformFunction: null }, appendTo: { classPropertyName: "appendTo", publicName: "appendTo", isSignal: true, isRequired: false, transformFunction: null }, drawerStyleClass: { classPropertyName: "drawerStyleClass", publicName: "drawerStyleClass", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { previewLevel: "previewLevel", closed: "closed" }, providers: [RuntimeActionContextStore], ngImport: i0, template: "<mt-drawer\r\n [visible]=\"drawerVisible()\"\r\n [loadingHeader]=\"loading()\"\r\n [title]=\"drawerTitle()\"\r\n [styleClass]=\"drawerStyleClass()\"\r\n [appendTo]=\"appendTo()\"\r\n position=\"right\"\r\n [modal]=\"true\"\r\n (visibleChange)=\"onVisibleChange($event)\"\r\n *transloco=\"let t; prefix: 'workCenter'\"\r\n>\r\n <ng-container content>\r\n @if (loading()) {\r\n <div class=\"p-4\" [class]=\"modal.contentClass\">\r\n <div class=\"flex min-h-[22rem] flex-col gap-5\">\r\n <div class=\"flex items-center gap-3\">\r\n <p-skeleton shape=\"circle\" size=\"3rem\" />\r\n <div class=\"flex flex-1 flex-col gap-2\">\r\n <p-skeleton width=\"12rem\" height=\"1rem\" />\r\n <p-skeleton width=\"8rem\" height=\"0.875rem\" />\r\n </div>\r\n </div>\r\n\r\n <div class=\"grid gap-3 md:grid-cols-2\">\r\n @for (_ of [1, 2, 3, 4]; track $index) {\r\n <p-skeleton height=\"5rem\" class=\"rounded-lg\" />\r\n }\r\n </div>\r\n\r\n <p-skeleton height=\"16rem\" class=\"rounded-lg\" />\r\n </div>\r\n </div>\r\n } @else if (error(); as errorMessage) {\r\n <div class=\"p-4\" [class]=\"modal.contentClass\">\r\n <div class=\"flex min-h-[22rem] items-center justify-center\">\r\n <p class=\"max-w-xl text-sm font-medium text-red-600\">\r\n {{ errorMessage }}\r\n </p>\r\n </div>\r\n </div>\r\n } @else if (details(); as details) {\r\n <div [class]=\"modal.contentClass + ' h-full min-h-0'\">\r\n <mt-work-center-item-modal\r\n [details]=\"details\"\r\n [lookups]=\"lookups()\"\r\n [readOnly]=\"readOnly()\"\r\n />\r\n </div>\r\n } @else {\r\n <div class=\"p-4\" [class]=\"modal.contentClass\">\r\n <div class=\"flex min-h-[22rem] items-center justify-center\">\r\n <p class=\"text-sm font-medium text-surface-500\">\r\n {{ t(\"modal.noItemSelected\") }}\r\n </p>\r\n </div>\r\n </div>\r\n }\r\n </ng-container>\r\n\r\n @if (!readOnly() && footerActionsMounted()) {\r\n <div footer [class]=\"modal.footerClass\">\r\n <mt-work-center-item-modal-footer-actions\r\n [contextKey]=\"resolvedContextKey()\"\r\n [previewLevelTarget]=\"displayedPreviewLevelTarget()\"\r\n (actionExecuted)=\"onActionExecuted($event)\"\r\n (previewLevelSelected)=\"onPreviewLevelSelected($event)\"\r\n (visibilityChange)=\"onFooterVisibilityChange($event)\"\r\n />\r\n </div>\r\n }\r\n</mt-drawer>\r\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "component", type: Drawer, selector: "mt-drawer", inputs: ["visible", "position", "fullScreen", "closeOnEscape", "blockScroll", "dismissible", "title", "subtitle", "loadingHeader", "styleClass", "transitionOptions", "appendTo", "modal", "showMaximizeToggle", "maximizeTooltip", "restoreTooltip", "maximized"], outputs: ["visibleChange", "onShow", "onHide", "maximizedChange"] }, { kind: "ngmodule", type: SkeletonModule }, { kind: "component", type: i1.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "shape", "animation", "borderRadius", "size", "width", "height"] }, { kind: "component", type: WorkCenterItemModal, selector: "mt-work-center-item-modal", inputs: ["details", "lookups", "readOnly"] }, { kind: "component", type: WorkCenterItemModalFooterActions, selector: "mt-work-center-item-modal-footer-actions", inputs: ["contextKey", "previewLevelTarget"], outputs: ["actionExecuted", "previewLevelSelected", "visibilityChange"] }] });
|
|
3546
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: WorkCenterItemModalRoute, isStandalone: true, selector: "mt-work-center-item-modal-route", inputs: { contextKey: { classPropertyName: "contextKey", publicName: "contextKey", isSignal: true, isRequired: false, transformFunction: null }, manageRouteOutlet: { classPropertyName: "manageRouteOutlet", publicName: "manageRouteOutlet", isSignal: true, isRequired: false, transformFunction: null }, showPreviewLevelAction: { classPropertyName: "showPreviewLevelAction", publicName: "showPreviewLevelAction", isSignal: true, isRequired: false, transformFunction: null }, readOnly: { classPropertyName: "readOnly", publicName: "readOnly", isSignal: true, isRequired: false, transformFunction: null }, appendTo: { classPropertyName: "appendTo", publicName: "appendTo", isSignal: true, isRequired: false, transformFunction: null }, drawerStyleClass: { classPropertyName: "drawerStyleClass", publicName: "drawerStyleClass", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { previewLevel: "previewLevel", closed: "closed" }, providers: [RuntimeActionContextStore], ngImport: i0, template: "<mt-drawer\r\n [visible]=\"drawerVisible()\"\r\n [loadingHeader]=\"loading()\"\r\n [title]=\"drawerTitle()\"\r\n [styleClass]=\"drawerStyleClass()\"\r\n [appendTo]=\"appendTo()\"\r\n position=\"right\"\r\n [modal]=\"true\"\r\n (visibleChange)=\"onVisibleChange($event)\"\r\n *transloco=\"let t; prefix: 'workCenter'\"\r\n>\r\n <ng-container content>\r\n @if (loading()) {\r\n <div class=\"p-4\" [class]=\"modal.contentClass\">\r\n <div class=\"flex min-h-[22rem] flex-col gap-5\">\r\n <div class=\"flex items-center gap-3\">\r\n <p-skeleton shape=\"circle\" size=\"3rem\" />\r\n <div class=\"flex flex-1 flex-col gap-2\">\r\n <p-skeleton width=\"12rem\" height=\"1rem\" />\r\n <p-skeleton width=\"8rem\" height=\"0.875rem\" />\r\n </div>\r\n </div>\r\n\r\n <div class=\"grid gap-3 md:grid-cols-2\">\r\n @for (_ of [1, 2, 3, 4]; track $index) {\r\n <p-skeleton height=\"5rem\" class=\"rounded-lg\" />\r\n }\r\n </div>\r\n\r\n <p-skeleton height=\"16rem\" class=\"rounded-lg\" />\r\n </div>\r\n </div>\r\n } @else if (error(); as errorMessage) {\r\n <div class=\"p-4\" [class]=\"modal.contentClass\">\r\n <div class=\"flex min-h-[22rem] items-center justify-center\">\r\n <p class=\"max-w-xl text-sm font-medium text-red-600\">\r\n {{ errorMessage }}\r\n </p>\r\n </div>\r\n </div>\r\n } @else if (details(); as details) {\r\n <div [class]=\"modal.contentClass + ' h-full min-h-0'\">\r\n <mt-work-center-item-modal\r\n [details]=\"details\"\r\n [lookups]=\"lookups()\"\r\n [readOnly]=\"readOnly()\"\r\n />\r\n </div>\r\n } @else {\r\n <div class=\"p-4\" [class]=\"modal.contentClass\">\r\n <div class=\"flex min-h-[22rem] items-center justify-center\">\r\n <p class=\"text-sm font-medium text-surface-500\">\r\n {{ t(\"modal.noItemSelected\") }}\r\n </p>\r\n </div>\r\n </div>\r\n }\r\n </ng-container>\r\n\r\n @if (!readOnly() && footerActionsMounted()) {\r\n <div footer [class]=\"modal.footerClass\">\r\n <mt-work-center-item-modal-footer-actions\r\n [contextKey]=\"resolvedContextKey()\"\r\n [previewLevelTarget]=\"displayedPreviewLevelTarget()\"\r\n (actionExecuted)=\"onActionExecuted($event)\"\r\n (previewLevelSelected)=\"onPreviewLevelSelected($event)\"\r\n (visibilityChange)=\"onFooterVisibilityChange($event)\"\r\n />\r\n </div>\r\n }\r\n</mt-drawer>\r\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "component", type: Drawer, selector: "mt-drawer", inputs: ["visible", "position", "fullScreen", "closeOnEscape", "blockScroll", "dismissible", "title", "subtitle", "loadingHeader", "styleClass", "transitionOptions", "appendTo", "modal", "showMaximizeToggle", "maximizeTooltip", "restoreTooltip", "maximized"], outputs: ["visibleChange", "onShow", "onHide", "maximizedChange"] }, { kind: "ngmodule", type: SkeletonModule }, { kind: "component", type: i1$1.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "shape", "animation", "borderRadius", "size", "width", "height"] }, { kind: "component", type: WorkCenterItemModal, selector: "mt-work-center-item-modal", inputs: ["details", "lookups", "readOnly"] }, { kind: "component", type: WorkCenterItemModalFooterActions, selector: "mt-work-center-item-modal-footer-actions", inputs: ["contextKey", "previewLevelTarget"], outputs: ["actionExecuted", "previewLevelSelected", "visibilityChange"] }] });
|
|
3118
3547
|
}
|
|
3119
3548
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: WorkCenterItemModalRoute, decorators: [{
|
|
3120
3549
|
type: Component,
|
|
@@ -3680,7 +4109,7 @@ class WorkCenterPage {
|
|
|
3680
4109
|
return normalized.length ? normalized : null;
|
|
3681
4110
|
}
|
|
3682
4111
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: WorkCenterPage, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3683
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: WorkCenterPage, isStandalone: true, selector: "mt-work-center-page", inputs: { area: { classPropertyName: "area", publicName: "area", isSignal: true, isRequired: false, transformFunction: null }, pageTitle: { classPropertyName: "pageTitle", publicName: "pageTitle", isSignal: true, isRequired: false, transformFunction: null }, menuIcon: { classPropertyName: "menuIcon", publicName: "menuIcon", isSignal: true, isRequired: false, transformFunction: null }, lookups: { classPropertyName: "lookups", publicName: "lookups", isSignal: true, isRequired: false, transformFunction: null }, showSidebar: { classPropertyName: "showSidebar", publicName: "showSidebar", isSignal: true, isRequired: false, transformFunction: null }, bootstrapInputs: { classPropertyName: "bootstrapInputs", publicName: "bootstrapInputs", isSignal: true, isRequired: false, transformFunction: null }, openItemDrawer: { classPropertyName: "openItemDrawer", publicName: "openItemDrawer", isSignal: true, isRequired: false, transformFunction: null }, itemReadOnly: { classPropertyName: "itemReadOnly", publicName: "itemReadOnly", isSignal: true, isRequired: false, transformFunction: null }, openItemsInModal: { classPropertyName: "openItemsInModal", publicName: "openItemsInModal", isSignal: true, isRequired: false, transformFunction: null }, itemModal: { classPropertyName: "itemModal", publicName: "itemModal", isSignal: true, isRequired: false, transformFunction: null }, generalTaskModal: { classPropertyName: "generalTaskModal", publicName: "generalTaskModal", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { runtimeFiltersChanged: "runtimeFiltersChanged", itemClicked: "itemClicked", cardSelected: "cardSelected", sortChanged: "sortChanged", groupChanged: "groupChanged" }, ngImport: i0, template: "<ng-template #headerActions>\r\n <div class=\"flex flex-wrap items-center justify-end gap-2\">\r\n <mt-property-filter-builder\r\n [schema]=\"propertyFilterSchema()\"\r\n [filters]=\"headerFilters()\"\r\n (applied)=\"onRuntimeFiltersApplied($event)\"\r\n (cleared)=\"onRuntimeFiltersCleared()\"\r\n />\r\n\r\n @if (showGeneralTaskCreateButton()) {\r\n <mt-button\r\n [label]=\"generalTaskButtonLabel()\"\r\n [icon]=\"generalTaskButtonIcon()\"\r\n size=\"small\"\r\n (onClick)=\"onGeneralTaskCreateClick()\"\r\n />\r\n }\r\n </div>\r\n</ng-template>\r\n\r\n<ng-template #pageContent>\r\n <div class=\"flex flex-col gap-8\">\r\n @if (loading()) {\r\n <div\r\n class=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 xl:grid-cols-5 gap-3\"\r\n >\r\n @for (_ of [1, 2, 3, 4, 5]; track $index) {\r\n <p-skeleton height=\"6.5rem\" class=\"rounded-lg\" />\r\n }\r\n </div>\r\n } @else if (kpis().length) {\r\n <div class=\"gap-3 flex flex-wrap\">\r\n @for (kpi of kpis(); track $index) {\r\n <mt-statistic-card [data]=\"kpi\" class=\"min-w-[200px]\" />\r\n }\r\n </div>\r\n }\r\n\r\n <mt-table\r\n noCard\r\n [data]=\"rows()\"\r\n [columns]=\"tableColumns()\"\r\n tableLayout=\"auto\"\r\n [clickableRows]=\"rowsClickable()\"\r\n [loading]=\"loading()\"\r\n [showFilters]=\"true\"\r\n filterMode=\"column\"\r\n [cellClickFilter]=\"true\"\r\n [generalSearch]=\"true\"\r\n [exportable]=\"true\"\r\n [printable]=\"true\"\r\n [groupable]=\"true\"\r\n [freezeActions]=\"true\"\r\n [lazy]=\"true\"\r\n [lazyLocalSearch]=\"true\"\r\n [lazyTotalRecords]=\"totalCount()\"\r\n [pageSize]=\"context().pageSize\"\r\n [currentPage]=\"context().page - 1\"\r\n [first]=\"(context().page - 1) * context().pageSize\"\r\n [rowsPerPageOptions]=\"[10, 20, 50, 100]\"\r\n [groupCountMap]=\"groupCountMap()\"\r\n [groupBy]=\"groupBy()\"\r\n [sortField]=\"sortField()\"\r\n [sortDirection]=\"sortDirection()\"\r\n storageKey=\"work-center-page-table\"\r\n [persistStateExclude]=\"persistStateExclude\"\r\n [filters]=\"tableFilters()\"\r\n (filtersChange)=\"tableFilters.set($event)\"\r\n (groupByChange)=\"onTableGroupByChange($event)\"\r\n (lazyLoad)=\"onTableLazyLoad($event)\"\r\n (rowClick)=\"onRowClick($event)\"\r\n />\r\n </div>\r\n</ng-template>\r\n\r\n@if (showSidebar()) {\r\n <mt-client-page\r\n [menuIcon]=\"menuIcon()\"\r\n [menuTitle]=\"resolvedPageTitle()\"\r\n [menuItems]=\"menuItems()\"\r\n [menuItemsLoading]=\"loading() && !menuItems().length\"\r\n [activeItem]=\"context().selectedCardKey ?? undefined\"\r\n storageKey=\"work-center-client-page\"\r\n (menuItemClick)=\"onMenuItemClick($event)\"\r\n >\r\n <ng-template #headerClientPageEnd>\r\n <ng-container [ngTemplateOutlet]=\"headerActions\" />\r\n </ng-template>\r\n\r\n <ng-container [ngTemplateOutlet]=\"pageContent\" />\r\n </mt-client-page>\r\n} @else {\r\n <div class=\"flex h-full min-h-0 flex-col bg-surface-0\">\r\n <div\r\n class=\"flex shrink-0 items-center justify-end border-b border-surface px-7 py-4\"\r\n >\r\n <ng-container [ngTemplateOutlet]=\"headerActions\" />\r\n </div>\r\n <div class=\"min-h-0 flex-1 overflow-y-auto px-7 py-5\">\r\n <ng-container [ngTemplateOutlet]=\"pageContent\" />\r\n </div>\r\n </div>\r\n}\r\n\r\n@if (selectedItemContextKey(); as contextKey) {\r\n <mt-work-center-item-modal-route\r\n [contextKey]=\"contextKey\"\r\n [manageRouteOutlet]=\"false\"\r\n [showPreviewLevelAction]=\"false\"\r\n [readOnly]=\"itemReadOnly()\"\r\n appendTo=\"body\"\r\n drawerStyleClass=\"mt-work-center-item-drawer !w-[90vw]\"\r\n (closed)=\"selectedItemContextKey.set(null)\"\r\n />\r\n}\r\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1
|
|
4112
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: WorkCenterPage, isStandalone: true, selector: "mt-work-center-page", inputs: { area: { classPropertyName: "area", publicName: "area", isSignal: true, isRequired: false, transformFunction: null }, pageTitle: { classPropertyName: "pageTitle", publicName: "pageTitle", isSignal: true, isRequired: false, transformFunction: null }, menuIcon: { classPropertyName: "menuIcon", publicName: "menuIcon", isSignal: true, isRequired: false, transformFunction: null }, lookups: { classPropertyName: "lookups", publicName: "lookups", isSignal: true, isRequired: false, transformFunction: null }, showSidebar: { classPropertyName: "showSidebar", publicName: "showSidebar", isSignal: true, isRequired: false, transformFunction: null }, bootstrapInputs: { classPropertyName: "bootstrapInputs", publicName: "bootstrapInputs", isSignal: true, isRequired: false, transformFunction: null }, openItemDrawer: { classPropertyName: "openItemDrawer", publicName: "openItemDrawer", isSignal: true, isRequired: false, transformFunction: null }, itemReadOnly: { classPropertyName: "itemReadOnly", publicName: "itemReadOnly", isSignal: true, isRequired: false, transformFunction: null }, openItemsInModal: { classPropertyName: "openItemsInModal", publicName: "openItemsInModal", isSignal: true, isRequired: false, transformFunction: null }, itemModal: { classPropertyName: "itemModal", publicName: "itemModal", isSignal: true, isRequired: false, transformFunction: null }, generalTaskModal: { classPropertyName: "generalTaskModal", publicName: "generalTaskModal", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { runtimeFiltersChanged: "runtimeFiltersChanged", itemClicked: "itemClicked", cardSelected: "cardSelected", sortChanged: "sortChanged", groupChanged: "groupChanged" }, ngImport: i0, template: "<ng-template #headerActions>\r\n <div class=\"flex flex-wrap items-center justify-end gap-2\">\r\n <mt-property-filter-builder\r\n [schema]=\"propertyFilterSchema()\"\r\n [filters]=\"headerFilters()\"\r\n (applied)=\"onRuntimeFiltersApplied($event)\"\r\n (cleared)=\"onRuntimeFiltersCleared()\"\r\n />\r\n\r\n @if (showGeneralTaskCreateButton()) {\r\n <mt-button\r\n [label]=\"generalTaskButtonLabel()\"\r\n [icon]=\"generalTaskButtonIcon()\"\r\n size=\"small\"\r\n (onClick)=\"onGeneralTaskCreateClick()\"\r\n />\r\n }\r\n </div>\r\n</ng-template>\r\n\r\n<ng-template #pageContent>\r\n <div class=\"flex flex-col gap-8\">\r\n @if (loading()) {\r\n <div\r\n class=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 xl:grid-cols-5 gap-3\"\r\n >\r\n @for (_ of [1, 2, 3, 4, 5]; track $index) {\r\n <p-skeleton height=\"6.5rem\" class=\"rounded-lg\" />\r\n }\r\n </div>\r\n } @else if (kpis().length) {\r\n <div class=\"gap-3 flex flex-wrap\">\r\n @for (kpi of kpis(); track $index) {\r\n <mt-statistic-card [data]=\"kpi\" class=\"min-w-[200px]\" />\r\n }\r\n </div>\r\n }\r\n\r\n <mt-table\r\n noCard\r\n [data]=\"rows()\"\r\n [columns]=\"tableColumns()\"\r\n tableLayout=\"auto\"\r\n [clickableRows]=\"rowsClickable()\"\r\n [loading]=\"loading()\"\r\n [showFilters]=\"true\"\r\n filterMode=\"column\"\r\n [cellClickFilter]=\"true\"\r\n [generalSearch]=\"true\"\r\n [exportable]=\"true\"\r\n [printable]=\"true\"\r\n [groupable]=\"true\"\r\n [freezeActions]=\"true\"\r\n [lazy]=\"true\"\r\n [lazyLocalSearch]=\"true\"\r\n [lazyTotalRecords]=\"totalCount()\"\r\n [pageSize]=\"context().pageSize\"\r\n [currentPage]=\"context().page - 1\"\r\n [first]=\"(context().page - 1) * context().pageSize\"\r\n [rowsPerPageOptions]=\"[10, 20, 50, 100]\"\r\n [groupCountMap]=\"groupCountMap()\"\r\n [groupBy]=\"groupBy()\"\r\n [sortField]=\"sortField()\"\r\n [sortDirection]=\"sortDirection()\"\r\n storageKey=\"work-center-page-table\"\r\n [persistStateExclude]=\"persistStateExclude\"\r\n [filters]=\"tableFilters()\"\r\n (filtersChange)=\"tableFilters.set($event)\"\r\n (groupByChange)=\"onTableGroupByChange($event)\"\r\n (lazyLoad)=\"onTableLazyLoad($event)\"\r\n (rowClick)=\"onRowClick($event)\"\r\n />\r\n </div>\r\n</ng-template>\r\n\r\n@if (showSidebar()) {\r\n <mt-client-page\r\n [menuIcon]=\"menuIcon()\"\r\n [menuTitle]=\"resolvedPageTitle()\"\r\n [menuItems]=\"menuItems()\"\r\n [menuItemsLoading]=\"loading() && !menuItems().length\"\r\n [activeItem]=\"context().selectedCardKey ?? undefined\"\r\n storageKey=\"work-center-client-page\"\r\n (menuItemClick)=\"onMenuItemClick($event)\"\r\n >\r\n <ng-template #headerClientPageEnd>\r\n <ng-container [ngTemplateOutlet]=\"headerActions\" />\r\n </ng-template>\r\n\r\n <ng-container [ngTemplateOutlet]=\"pageContent\" />\r\n </mt-client-page>\r\n} @else {\r\n <div class=\"flex h-full min-h-0 flex-col bg-surface-0\">\r\n <div\r\n class=\"flex shrink-0 items-center justify-end border-b border-surface px-7 py-4\"\r\n >\r\n <ng-container [ngTemplateOutlet]=\"headerActions\" />\r\n </div>\r\n <div class=\"min-h-0 flex-1 overflow-y-auto px-7 py-5\">\r\n <ng-container [ngTemplateOutlet]=\"pageContent\" />\r\n </div>\r\n </div>\r\n}\r\n\r\n@if (selectedItemContextKey(); as contextKey) {\r\n <mt-work-center-item-modal-route\r\n [contextKey]=\"contextKey\"\r\n [manageRouteOutlet]=\"false\"\r\n [showPreviewLevelAction]=\"false\"\r\n [readOnly]=\"itemReadOnly()\"\r\n appendTo=\"body\"\r\n drawerStyleClass=\"mt-work-center-item-drawer !w-[90vw]\"\r\n (closed)=\"selectedItemContextKey.set(null)\"\r\n />\r\n}\r\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { 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: ClientPage, selector: "mt-client-page", inputs: ["menuIcon", "menuTitle", "menuItems", "menuItemsLoading", "activeItem", "collapsed", "resizable", "storageKey", "storageMode", "minSidebarWidth", "maxSidebarWidth", "defaultSidebarWidth"], outputs: ["collapsedChange", "menuItemClick", "loadChildren"] }, { kind: "component", type: PropertyFilterBuilder, selector: "mt-property-filter-builder", inputs: ["schema", "filters", "title", "buttonLabel", "disabled"], outputs: ["filtersChange", "applied", "cleared"] }, { kind: "component", type: StatisticCard, selector: "mt-statistic-card", inputs: ["data", "cardClass"] }, { 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", "persistStateExclude", "exportable", "printable", "groupable", "groupCountMap", "cellClickFilter", "freezeActions", "virtualScroll", "virtualScrollItemSize", "scrollHeight", "printTitle", "exportFilename", "actionShape", "rowActionsLoadingFn", "tableLayout", "noCard", "tabs", "tabsOptionLabel", "tabsOptionValue", "activeTab", "actions", "paginatorPosition", "alwaysShowPaginator", "rowsPerPageOptions", "pageSize", "currentPage", "first", "filterTerm", "groupBy", "sortField", "sortDirection"], outputs: ["selectionChange", "cellChange", "lazyLoad", "columnReorder", "rowReorder", "rowClick", "rowActionsRequested", "filtersChange", "activeTabChange", "onTabChange", "pageSizeChange", "currentPageChange", "firstChange", "filterTermChange", "groupByChange", "sortFieldChange", "sortDirectionChange"] }, { kind: "component", type: WorkCenterItemModalRoute, selector: "mt-work-center-item-modal-route", inputs: ["contextKey", "manageRouteOutlet", "showPreviewLevelAction", "readOnly", "appendTo", "drawerStyleClass"], outputs: ["previewLevel", "closed"] }, { kind: "ngmodule", type: SkeletonModule }, { kind: "component", type: i1$1.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "shape", "animation", "borderRadius", "size", "width", "height"] }] });
|
|
3684
4113
|
}
|
|
3685
4114
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: WorkCenterPage, decorators: [{
|
|
3686
4115
|
type: Component,
|
|
@@ -3696,11 +4125,28 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
|
|
|
3696
4125
|
], template: "<ng-template #headerActions>\r\n <div class=\"flex flex-wrap items-center justify-end gap-2\">\r\n <mt-property-filter-builder\r\n [schema]=\"propertyFilterSchema()\"\r\n [filters]=\"headerFilters()\"\r\n (applied)=\"onRuntimeFiltersApplied($event)\"\r\n (cleared)=\"onRuntimeFiltersCleared()\"\r\n />\r\n\r\n @if (showGeneralTaskCreateButton()) {\r\n <mt-button\r\n [label]=\"generalTaskButtonLabel()\"\r\n [icon]=\"generalTaskButtonIcon()\"\r\n size=\"small\"\r\n (onClick)=\"onGeneralTaskCreateClick()\"\r\n />\r\n }\r\n </div>\r\n</ng-template>\r\n\r\n<ng-template #pageContent>\r\n <div class=\"flex flex-col gap-8\">\r\n @if (loading()) {\r\n <div\r\n class=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 xl:grid-cols-5 gap-3\"\r\n >\r\n @for (_ of [1, 2, 3, 4, 5]; track $index) {\r\n <p-skeleton height=\"6.5rem\" class=\"rounded-lg\" />\r\n }\r\n </div>\r\n } @else if (kpis().length) {\r\n <div class=\"gap-3 flex flex-wrap\">\r\n @for (kpi of kpis(); track $index) {\r\n <mt-statistic-card [data]=\"kpi\" class=\"min-w-[200px]\" />\r\n }\r\n </div>\r\n }\r\n\r\n <mt-table\r\n noCard\r\n [data]=\"rows()\"\r\n [columns]=\"tableColumns()\"\r\n tableLayout=\"auto\"\r\n [clickableRows]=\"rowsClickable()\"\r\n [loading]=\"loading()\"\r\n [showFilters]=\"true\"\r\n filterMode=\"column\"\r\n [cellClickFilter]=\"true\"\r\n [generalSearch]=\"true\"\r\n [exportable]=\"true\"\r\n [printable]=\"true\"\r\n [groupable]=\"true\"\r\n [freezeActions]=\"true\"\r\n [lazy]=\"true\"\r\n [lazyLocalSearch]=\"true\"\r\n [lazyTotalRecords]=\"totalCount()\"\r\n [pageSize]=\"context().pageSize\"\r\n [currentPage]=\"context().page - 1\"\r\n [first]=\"(context().page - 1) * context().pageSize\"\r\n [rowsPerPageOptions]=\"[10, 20, 50, 100]\"\r\n [groupCountMap]=\"groupCountMap()\"\r\n [groupBy]=\"groupBy()\"\r\n [sortField]=\"sortField()\"\r\n [sortDirection]=\"sortDirection()\"\r\n storageKey=\"work-center-page-table\"\r\n [persistStateExclude]=\"persistStateExclude\"\r\n [filters]=\"tableFilters()\"\r\n (filtersChange)=\"tableFilters.set($event)\"\r\n (groupByChange)=\"onTableGroupByChange($event)\"\r\n (lazyLoad)=\"onTableLazyLoad($event)\"\r\n (rowClick)=\"onRowClick($event)\"\r\n />\r\n </div>\r\n</ng-template>\r\n\r\n@if (showSidebar()) {\r\n <mt-client-page\r\n [menuIcon]=\"menuIcon()\"\r\n [menuTitle]=\"resolvedPageTitle()\"\r\n [menuItems]=\"menuItems()\"\r\n [menuItemsLoading]=\"loading() && !menuItems().length\"\r\n [activeItem]=\"context().selectedCardKey ?? undefined\"\r\n storageKey=\"work-center-client-page\"\r\n (menuItemClick)=\"onMenuItemClick($event)\"\r\n >\r\n <ng-template #headerClientPageEnd>\r\n <ng-container [ngTemplateOutlet]=\"headerActions\" />\r\n </ng-template>\r\n\r\n <ng-container [ngTemplateOutlet]=\"pageContent\" />\r\n </mt-client-page>\r\n} @else {\r\n <div class=\"flex h-full min-h-0 flex-col bg-surface-0\">\r\n <div\r\n class=\"flex shrink-0 items-center justify-end border-b border-surface px-7 py-4\"\r\n >\r\n <ng-container [ngTemplateOutlet]=\"headerActions\" />\r\n </div>\r\n <div class=\"min-h-0 flex-1 overflow-y-auto px-7 py-5\">\r\n <ng-container [ngTemplateOutlet]=\"pageContent\" />\r\n </div>\r\n </div>\r\n}\r\n\r\n@if (selectedItemContextKey(); as contextKey) {\r\n <mt-work-center-item-modal-route\r\n [contextKey]=\"contextKey\"\r\n [manageRouteOutlet]=\"false\"\r\n [showPreviewLevelAction]=\"false\"\r\n [readOnly]=\"itemReadOnly()\"\r\n appendTo=\"body\"\r\n drawerStyleClass=\"mt-work-center-item-drawer !w-[90vw]\"\r\n (closed)=\"selectedItemContextKey.set(null)\"\r\n />\r\n}\r\n" }]
|
|
3697
4126
|
}], ctorParameters: () => [], propDecorators: { area: [{ type: i0.Input, args: [{ isSignal: true, alias: "area", required: false }] }], pageTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageTitle", required: false }] }], menuIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "menuIcon", required: false }] }], lookups: [{ type: i0.Input, args: [{ isSignal: true, alias: "lookups", required: false }] }], showSidebar: [{ type: i0.Input, args: [{ isSignal: true, alias: "showSidebar", required: false }] }], bootstrapInputs: [{ type: i0.Input, args: [{ isSignal: true, alias: "bootstrapInputs", required: false }] }], openItemDrawer: [{ type: i0.Input, args: [{ isSignal: true, alias: "openItemDrawer", required: false }] }], itemReadOnly: [{ type: i0.Input, args: [{ isSignal: true, alias: "itemReadOnly", required: false }] }], openItemsInModal: [{ type: i0.Input, args: [{ isSignal: true, alias: "openItemsInModal", required: false }] }], itemModal: [{ type: i0.Input, args: [{ isSignal: true, alias: "itemModal", required: false }] }], generalTaskModal: [{ type: i0.Input, args: [{ isSignal: true, alias: "generalTaskModal", required: false }] }], runtimeFiltersChanged: [{ type: i0.Output, args: ["runtimeFiltersChanged"] }], itemClicked: [{ type: i0.Output, args: ["itemClicked"] }], cardSelected: [{ type: i0.Output, args: ["cardSelected"] }], sortChanged: [{ type: i0.Output, args: ["sortChanged"] }], groupChanged: [{ type: i0.Output, args: ["groupChanged"] }] } });
|
|
3698
4127
|
|
|
4128
|
+
/**
|
|
4129
|
+
* work-center-schedule-publish.models.ts - The wire shape of the published-vs-draft
|
|
4130
|
+
* comparison an approver acts on.
|
|
4131
|
+
*
|
|
4132
|
+
* Mirrors the backend's `SchedulePublishDiffDto`, which `process-form-load`
|
|
4133
|
+
* returns as a REQUEST-level `schedulePublish` block for
|
|
4134
|
+
* `LevelData.PublishSchedule`. It is request-level rather than hung off a form
|
|
4135
|
+
* field because a publish submits no fields - the plan IS the payload.
|
|
4136
|
+
*
|
|
4137
|
+
* The same vocabulary is used by the schedule read envelope and the Review &
|
|
4138
|
+
* Publish drawer in `@masterteam/task-schedule`, deliberately: one differ serves
|
|
4139
|
+
* all three, so the planner who requested the publish and the approver deciding
|
|
4140
|
+
* on it cannot be shown the same row described differently.
|
|
4141
|
+
*
|
|
4142
|
+
* Pure TypeScript - no Angular, no DOM.
|
|
4143
|
+
*/
|
|
4144
|
+
|
|
3699
4145
|
const APP_STATES = [WorkCenterState];
|
|
3700
4146
|
|
|
3701
4147
|
/**
|
|
3702
4148
|
* Generated bundle index. Do not edit.
|
|
3703
4149
|
*/
|
|
3704
4150
|
|
|
3705
|
-
export { APP_STATES, ApplyColumnFilters, ApplyHeaderFilters, ClearAllFilters, EnterArea, HydrateFromContext, LoadRuntime, SelectCard, SetGroupBy, SetLookups, SetParams, WORK_CENTER_EMPTY_LABEL, WORK_CENTER_GROUP_NONE, WORK_CENTER_LEVEL_GROUP_FIELD, WORK_CENTER_LEVEL_NAME_KEY, WORK_CENTER_MAX_FILTERS, WORK_CENTER_MAX_PAGE_SIZE, WORK_CENTER_MAX_SORT, WORK_CENTER_QUERY_VERSION, WorkCenterActionKey, WorkCenterClientFormModal, WorkCenterFacade, WorkCenterItemModal, WorkCenterItemModalRoute, WorkCenterPage, WorkCenterState, decodeWorkCenterRouteContextKey, encodeWorkCenterRouteContextKey };
|
|
4151
|
+
export { APP_STATES, ApplyColumnFilters, ApplyHeaderFilters, ClearAllFilters, EnterArea, HydrateFromContext, LoadRuntime, SCHEDULE_PUBLISH_GROUP_ORDER, SCHEDULE_PUBLISH_OPERATION_KEY, SelectCard, SetGroupBy, SetLookups, SetParams, WORK_CENTER_EMPTY_LABEL, WORK_CENTER_GROUP_NONE, WORK_CENTER_LEVEL_GROUP_FIELD, WORK_CENTER_LEVEL_NAME_KEY, WORK_CENTER_MAX_FILTERS, WORK_CENTER_MAX_PAGE_SIZE, WORK_CENTER_MAX_SORT, WORK_CENTER_QUERY_VERSION, WORK_CENTER_TYPE_COMPONENTS, WorkCenterActionKey, WorkCenterClientFormModal, WorkCenterFacade, WorkCenterItemModal, WorkCenterItemModalRoute, WorkCenterPage, WorkCenterSchedulePublishType, WorkCenterState, decodeWorkCenterRouteContextKey, encodeWorkCenterRouteContextKey, groupSchedulePublishRows, matchesSchedulePublishSearch, readSchedulePublishChanges, schedulePublishRowKey, sortSchedulePublishRows, summarizeSchedulePublish, windowSchedulePublishRows };
|
|
3706
4152
|
//# sourceMappingURL=masterteam-work-center.mjs.map
|