@masterteam/work-center 0.0.71 → 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.
- package/assets/i18n/ar.json +197 -160
- package/assets/i18n/en.json +197 -160
- package/assets/work-center.css +1 -1
- package/fesm2022/masterteam-work-center.mjs +457 -12
- package/fesm2022/masterteam-work-center.mjs.map +1 -1
- package/package.json +6 -6
- package/types/masterteam-work-center.d.ts +300 -3
|
@@ -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';
|
|
@@ -2331,25 +2331,453 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
|
|
|
2331
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" }]
|
|
2332
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 }] }] } });
|
|
2333
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
|
+
|
|
2334
2728
|
class WorkCenterItemModal {
|
|
2729
|
+
registry = inject(WORK_CENTER_TYPE_COMPONENTS, {
|
|
2730
|
+
optional: true,
|
|
2731
|
+
});
|
|
2335
2732
|
details = input.required(...(ngDevMode ? [{ debugName: "details" }] : /* istanbul ignore next */ []));
|
|
2336
2733
|
lookups = input(null, ...(ngDevMode ? [{ debugName: "lookups" }] : /* istanbul ignore next */ []));
|
|
2337
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 */ []));
|
|
2338
2764
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: WorkCenterItemModal, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2339
|
-
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"] }] });
|
|
2340
2766
|
}
|
|
2341
2767
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: WorkCenterItemModal, decorators: [{
|
|
2342
2768
|
type: Component,
|
|
2343
2769
|
args: [{ selector: 'mt-work-center-item-modal', standalone: true, imports: [
|
|
2344
2770
|
CommonModule,
|
|
2771
|
+
NgComponentOutlet,
|
|
2345
2772
|
TranslocoDirective,
|
|
2346
2773
|
WorkCenterProcessStepType,
|
|
2347
2774
|
WorkCenterProcessRequestType,
|
|
2348
2775
|
WorkCenterEscalationInstanceType,
|
|
2349
2776
|
WorkCenterModuleType,
|
|
2777
|
+
WorkCenterSchedulePublishType,
|
|
2350
2778
|
], host: {
|
|
2351
2779
|
class: 'block h-full min-h-0',
|
|
2352
|
-
}, 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" }]
|
|
2353
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 }] }] } });
|
|
2354
2782
|
|
|
2355
2783
|
const FORM_VALIDATION_WARNINGS = 'FormValidationWarnings';
|
|
@@ -2825,13 +3253,13 @@ class WorkCenterItemModalFooterActions {
|
|
|
2825
3253
|
};
|
|
2826
3254
|
}
|
|
2827
3255
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: WorkCenterItemModalFooterActions, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2828
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: WorkCenterItemModalFooterActions, isStandalone: true, selector: "mt-work-center-item-modal-footer-actions", inputs: { contextKey: { classPropertyName: "contextKey", publicName: "contextKey", isSignal: true, isRequired: false, transformFunction: null }, previewLevelTarget: { classPropertyName: "previewLevelTarget", publicName: "previewLevelTarget", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { actionExecuted: "actionExecuted", previewLevelSelected: "previewLevelSelected", visibilityChange: "visibilityChange" }, host: { classAttribute: "flex w-full flex-1 flex-wrap items-center justify-end gap-2" }, providers: [WorkCenterRuntimeActionExecutor], ngImport: i0, template: "<ng-container *transloco=\"let t; prefix: 'workCenter'\">\
|
|
3256
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: WorkCenterItemModalFooterActions, isStandalone: true, selector: "mt-work-center-item-modal-footer-actions", inputs: { contextKey: { classPropertyName: "contextKey", publicName: "contextKey", isSignal: true, isRequired: false, transformFunction: null }, previewLevelTarget: { classPropertyName: "previewLevelTarget", publicName: "previewLevelTarget", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { actionExecuted: "actionExecuted", previewLevelSelected: "previewLevelSelected", visibilityChange: "visibilityChange" }, host: { classAttribute: "flex w-full flex-1 flex-wrap items-center justify-end gap-2" }, providers: [WorkCenterRuntimeActionExecutor], ngImport: i0, template: "<ng-container *transloco=\"let t; prefix: 'workCenter'\">\n @if (shouldRender()) {\n @if (error(); as error) {\n <span class=\"me-auto max-w-md whitespace-pre-line text-sm text-red-600\">\n {{ error }}\n </span>\n } @else if (loading() && !displayActions().length) {\n <span class=\"me-auto text-sm text-surface-500\">{{\n t(\"modal.loadingActions\")\n }}</span>\n }\n\n @if (previewLevelTarget(); as previewLevelTarget) {\n <mt-button\n [label]=\"previewLevelLabel(previewLevelTarget, t)\"\n severity=\"secondary\"\n variant=\"outlined\"\n [disabled]=\"loading() || !!pendingActionKey()\"\n (onClick)=\"previewLevelSelected.emit(previewLevelTarget)\"\n />\n }\n\n @for (action of displayActions(); track action.actionKey + action.url) {\n <mt-button\n [label]=\"action.label\"\n [severity]=\"action.severity\"\n [variant]=\"action.variant\"\n [disabled]=\"loading() || !!pendingActionKey()\"\n [loading]=\"pendingActionKey() === action.actionKey\"\n (onClick)=\"executeAction(action)\"\n />\n }\n }\n</ng-container>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { 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"] }] });
|
|
2829
3257
|
}
|
|
2830
3258
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: WorkCenterItemModalFooterActions, decorators: [{
|
|
2831
3259
|
type: Component,
|
|
2832
3260
|
args: [{ selector: 'mt-work-center-item-modal-footer-actions', standalone: true, imports: [CommonModule, TranslocoDirective, Button], host: {
|
|
2833
3261
|
class: 'flex w-full flex-1 flex-wrap items-center justify-end gap-2',
|
|
2834
|
-
}, providers: [WorkCenterRuntimeActionExecutor], template: "<ng-container *transloco=\"let t; prefix: 'workCenter'\">\
|
|
3262
|
+
}, providers: [WorkCenterRuntimeActionExecutor], template: "<ng-container *transloco=\"let t; prefix: 'workCenter'\">\n @if (shouldRender()) {\n @if (error(); as error) {\n <span class=\"me-auto max-w-md whitespace-pre-line text-sm text-red-600\">\n {{ error }}\n </span>\n } @else if (loading() && !displayActions().length) {\n <span class=\"me-auto text-sm text-surface-500\">{{\n t(\"modal.loadingActions\")\n }}</span>\n }\n\n @if (previewLevelTarget(); as previewLevelTarget) {\n <mt-button\n [label]=\"previewLevelLabel(previewLevelTarget, t)\"\n severity=\"secondary\"\n variant=\"outlined\"\n [disabled]=\"loading() || !!pendingActionKey()\"\n (onClick)=\"previewLevelSelected.emit(previewLevelTarget)\"\n />\n }\n\n @for (action of displayActions(); track action.actionKey + action.url) {\n <mt-button\n [label]=\"action.label\"\n [severity]=\"action.severity\"\n [variant]=\"action.variant\"\n [disabled]=\"loading() || !!pendingActionKey()\"\n [loading]=\"pendingActionKey() === action.actionKey\"\n (onClick)=\"executeAction(action)\"\n />\n }\n }\n</ng-container>\n" }]
|
|
2835
3263
|
}], ctorParameters: () => [], propDecorators: { contextKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "contextKey", required: false }] }], previewLevelTarget: [{ type: i0.Input, args: [{ isSignal: true, alias: "previewLevelTarget", required: false }] }], actionExecuted: [{ type: i0.Output, args: ["actionExecuted"] }], previewLevelSelected: [{ type: i0.Output, args: ["previewLevelSelected"] }], visibilityChange: [{ type: i0.Output, args: ["visibilityChange"] }] } });
|
|
2836
3264
|
function normalizeMethod(value) {
|
|
2837
3265
|
if (!value || typeof value !== 'string') {
|
|
@@ -3115,7 +3543,7 @@ class WorkCenterItemModalRoute {
|
|
|
3115
3543
|
};
|
|
3116
3544
|
}
|
|
3117
3545
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: WorkCenterItemModalRoute, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3118
|
-
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"] }] });
|
|
3119
3547
|
}
|
|
3120
3548
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: WorkCenterItemModalRoute, decorators: [{
|
|
3121
3549
|
type: Component,
|
|
@@ -3681,7 +4109,7 @@ class WorkCenterPage {
|
|
|
3681
4109
|
return normalized.length ? normalized : null;
|
|
3682
4110
|
}
|
|
3683
4111
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: WorkCenterPage, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3684
|
-
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"] }] });
|
|
3685
4113
|
}
|
|
3686
4114
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: WorkCenterPage, decorators: [{
|
|
3687
4115
|
type: Component,
|
|
@@ -3697,11 +4125,28 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
|
|
|
3697
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" }]
|
|
3698
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"] }] } });
|
|
3699
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
|
+
|
|
3700
4145
|
const APP_STATES = [WorkCenterState];
|
|
3701
4146
|
|
|
3702
4147
|
/**
|
|
3703
4148
|
* Generated bundle index. Do not edit.
|
|
3704
4149
|
*/
|
|
3705
4150
|
|
|
3706
|
-
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 };
|
|
3707
4152
|
//# sourceMappingURL=masterteam-work-center.mjs.map
|