@masterteam/work-center 0.0.82 → 0.0.84
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.
|
@@ -15,8 +15,8 @@ import { SkeletonModule } from 'primeng/skeleton';
|
|
|
15
15
|
import { catchError, EMPTY, map, take, firstValueFrom, of } from 'rxjs';
|
|
16
16
|
import { ModalRef } from '@masterteam/components/dialog';
|
|
17
17
|
import { DrawerController } from '@masterteam/components/dynamic-drawer';
|
|
18
|
-
import { ClientForm, ClientFormWarningsDialog
|
|
19
|
-
import { HttpClient } from '@angular/common/http';
|
|
18
|
+
import { ClientForm, ClientFormWarningsDialog } from '@masterteam/forms/client-form';
|
|
19
|
+
import { HttpClient, HttpHeaders } from '@angular/common/http';
|
|
20
20
|
import { Router, ActivatedRoute } from '@angular/router';
|
|
21
21
|
import { Drawer } from '@masterteam/components/drawer';
|
|
22
22
|
import { RuntimeActionContextStore, RuntimeActionRunner, RUNTIME_ACTION_CONFIRM_DIALOG, hasActionFormFields, buildActionPayload, resolveActionLabel } from '@masterteam/components/runtime-action';
|
|
@@ -1322,20 +1322,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
|
|
|
1322
1322
|
|
|
1323
1323
|
/**
|
|
1324
1324
|
* The channel between the client form in the modal body and the action footer,
|
|
1325
|
-
* route-scoped alongside `RuntimeActionContextStore`. It carries
|
|
1325
|
+
* route-scoped alongside `RuntimeActionContextStore`. It carries two things
|
|
1326
1326
|
* in both directions:
|
|
1327
1327
|
*
|
|
1328
|
-
* - the form's submit context, for the validate pre-flight;
|
|
1329
1328
|
* - whether the form is locally complete, so the footer can gate its actions;
|
|
1330
1329
|
* - the failure messages of the last action, so they can be rendered above the
|
|
1331
1330
|
* form instead of squeezed beside the buttons.
|
|
1332
1331
|
*/
|
|
1333
1332
|
class WorkCenterActionFormStore {
|
|
1334
|
-
formContext = signal(null, ...(ngDevMode ? [{ debugName: "formContext" }] : /* istanbul ignore next */ []));
|
|
1335
1333
|
formIncomplete = signal(false, ...(ngDevMode ? [{ debugName: "formIncomplete" }] : /* istanbul ignore next */ []));
|
|
1336
1334
|
actionErrors = signal([], ...(ngDevMode ? [{ debugName: "actionErrors" }] : /* istanbul ignore next */ []));
|
|
1337
1335
|
errorHosted = signal(false, ...(ngDevMode ? [{ debugName: "errorHosted" }] : /* istanbul ignore next */ []));
|
|
1338
|
-
context = this.formContext.asReadonly();
|
|
1339
1336
|
/**
|
|
1340
1337
|
* True while the on-screen form fails its own local validation — a required
|
|
1341
1338
|
* field left empty, a formula error. The footer disables the actions that
|
|
@@ -1353,9 +1350,6 @@ class WorkCenterActionFormStore {
|
|
|
1353
1350
|
* claims them, so an item type with no form still reports a failure.
|
|
1354
1351
|
*/
|
|
1355
1352
|
hasErrorHost = this.errorHosted.asReadonly();
|
|
1356
|
-
setContext(context) {
|
|
1357
|
-
this.formContext.set(context);
|
|
1358
|
-
}
|
|
1359
1353
|
setIncomplete(incomplete) {
|
|
1360
1354
|
this.formIncomplete.set(incomplete);
|
|
1361
1355
|
}
|
|
@@ -2421,22 +2415,6 @@ class WorkCenterProcessRequestType {
|
|
|
2421
2415
|
effect(() => {
|
|
2422
2416
|
this.actionContext?.setFormSource(this.readOnly() ? null : (this.clientForm() ?? null));
|
|
2423
2417
|
});
|
|
2424
|
-
// See the equivalent effect in `WorkCenterProcessStepType`. Gated on
|
|
2425
|
-
// `isReadonly` rather than `readOnly`, since the request form is only
|
|
2426
|
-
// editable while the request sits in `Returned`.
|
|
2427
|
-
effect(() => {
|
|
2428
|
-
this.formStore?.setContext(this.isReadonly() || !this.canRenderForm()
|
|
2429
|
-
? null
|
|
2430
|
-
: {
|
|
2431
|
-
moduleKey: this.moduleKey(),
|
|
2432
|
-
operationKey: this.operationKey(),
|
|
2433
|
-
moduleId: this.moduleId(),
|
|
2434
|
-
levelId: this.levelId(),
|
|
2435
|
-
levelDataId: this.levelDataId(),
|
|
2436
|
-
moduleDataId: this.moduleDataId(),
|
|
2437
|
-
requestSchemaId: this.requestSchemaId(),
|
|
2438
|
-
});
|
|
2439
|
-
});
|
|
2440
2418
|
// `formControl.status` is not a signal, so mirror it into one — without
|
|
2441
2419
|
// this the footer would keep its stale enabled/disabled state while the
|
|
2442
2420
|
// approver fills the form in.
|
|
@@ -2466,12 +2444,11 @@ class WorkCenterProcessRequestType {
|
|
|
2466
2444
|
}
|
|
2467
2445
|
ngOnDestroy() {
|
|
2468
2446
|
this.actionContext?.setFormSource(null);
|
|
2469
|
-
this.formStore?.setContext(null);
|
|
2470
2447
|
this.formStore?.setIncomplete(false);
|
|
2471
2448
|
this.formStore?.setErrorHost(false);
|
|
2472
2449
|
}
|
|
2473
2450
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: WorkCenterProcessRequestType, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2474
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: WorkCenterProcessRequestType, isStandalone: true, selector: "mt-work-center-process-request-type", 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 } }, viewQueries: [{ propertyName: "clientForm", first: true, predicate: ClientForm, descendants: true, isSignal: true }], 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 <div\n class=\"h-full min-h-0 lg:overflow-hidden\"\n [hidden]=\"activeTab() !== 'requestDetails'\"\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 @if (canRenderForm()) {\n <mt-client-form\n [moduleKey]=\"moduleKey()\"\n [operationKey]=\"operationKey()\"\n [moduleId]=\"moduleId()\"\n [levelId]=\"levelId()\"\n [levelDataId]=\"levelDataId()\"\n [moduleDataId]=\"moduleDataId()\"\n [requestSchemaId]=\"requestSchemaId()\"\n [draftProcessId]=\"draftProcessId()\"\n [readonly]=\"isReadonly()\"\n [renderMode]=\"'sections'\"\n [lookups]=\"formLookups()\"\n [hostMessages]=\"actionMessages()\"\n />\n } @else {\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(\"context.requestFormUnavailable\") }}\n </p>\n </div>\n }\n </div>\n </div>\n\n <div\n class=\"flex-1 min-h-0 overflow-auto\"\n [hidden]=\"activeTab() !== 'approvals'\"\n >\n @if (canRenderPreview()) {\n <mt-work-center-process-preview\n [requestId]=\"requestId()\"\n [view]=\"'approvals'\"\n />\n } @else {\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(\"context.requestPreviewUnavailable\") }}\n </p>\n </div>\n }\n </div>\n\n <div\n class=\"flex-1 min-h-0 overflow-auto\"\n [hidden]=\"activeTab() !== 'schema'\"\n >\n @if (canRenderPreview()) {\n <mt-work-center-process-preview\n [requestId]=\"requestId()\"\n [view]=\"'schema'\"\n />\n } @else {\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(\"context.requestPreviewUnavailable\") }}\n </p>\n </div>\n }\n </div>\n\n @if (!readOnly()) {\n <div\n class=\"h-full min-h-0 overflow-hidden\"\n [hidden]=\"activeTab() !== 'discussion'\"\n >\n @if (canRenderDiscussion()) {\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]=\"discussionRecordId()\"\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 } @else {\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(\"context.requestDiscussionUnavailable\") }}\n </p>\n </div>\n }\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: ClientForm, selector: "mt-client-form", inputs: ["moduleKey", "operationKey", "moduleId", "levelId", "levelDataId", "moduleDataId", "requestSchemaId", "draftProcessId", "templateId", "returnRecord", "preview", "forceOriginalForm", "returnUrl", "defaultValues", "submitRequestMapper", "submitTransport", "readonly", "autoLoad", "formMode", "renderMode", "showInternalStepActions", "confirmWarningsOnSubmit", "confirmWarningsOnStepChange", "silentMessages", "hostMessages", "readonlyFieldDisplayMode", "lookups", "statuses", "ignoredFieldKeys", "disabledFieldKeys", "allowedFieldKeys"], outputs: ["loaded", "submitted", "errored", "modeDetected", "formSourceDetected", "footerStateChanged"] }, { 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: WorkCenterProcessPreview, selector: "mt-work-center-process-preview", inputs: ["requestId", "view"] }] });
|
|
2451
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: WorkCenterProcessRequestType, isStandalone: true, selector: "mt-work-center-process-request-type", 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 } }, viewQueries: [{ propertyName: "clientForm", first: true, predicate: ClientForm, descendants: true, isSignal: true }], ngImport: i0, 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() !== 'requestDetails'\"\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]=\"isReadonly()\"\r\n [renderMode]=\"'sections'\"\r\n [lookups]=\"formLookups()\"\r\n [hostMessages]=\"actionMessages()\"\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.requestFormUnavailable\") }}\r\n </p>\r\n </div>\r\n }\r\n </div>\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.requestPreviewUnavailable\") }}\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.requestPreviewUnavailable\") }}\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.requestDiscussionUnavailable\") }}\r\n </p>\r\n </div>\r\n }\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n</div>\r\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: ClientForm, selector: "mt-client-form", inputs: ["moduleKey", "operationKey", "moduleId", "levelId", "levelDataId", "moduleDataId", "requestSchemaId", "draftProcessId", "templateId", "returnRecord", "preview", "forceOriginalForm", "returnUrl", "defaultValues", "submitRequestMapper", "submitTransport", "readonly", "autoLoad", "formMode", "renderMode", "showInternalStepActions", "confirmWarningsOnSubmit", "confirmWarningsOnStepChange", "silentMessages", "hostMessages", "readonlyFieldDisplayMode", "lookups", "statuses", "ignoredFieldKeys", "disabledFieldKeys", "allowedFieldKeys"], outputs: ["loaded", "submitted", "errored", "modeDetected", "formSourceDetected", "footerStateChanged"] }, { 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: WorkCenterProcessPreview, selector: "mt-work-center-process-preview", inputs: ["requestId", "view"] }] });
|
|
2475
2452
|
}
|
|
2476
2453
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: WorkCenterProcessRequestType, decorators: [{
|
|
2477
2454
|
type: Component,
|
|
@@ -2482,7 +2459,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
|
|
|
2482
2459
|
ClientForm,
|
|
2483
2460
|
DiscussionThread,
|
|
2484
2461
|
WorkCenterProcessPreview,
|
|
2485
|
-
], 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 <div\n class=\"h-full min-h-0 lg:overflow-hidden\"\n [hidden]=\"activeTab() !== 'requestDetails'\"\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 @if (canRenderForm()) {\n <mt-client-form\n [moduleKey]=\"moduleKey()\"\n [operationKey]=\"operationKey()\"\n [moduleId]=\"moduleId()\"\n [levelId]=\"levelId()\"\n [levelDataId]=\"levelDataId()\"\n [moduleDataId]=\"moduleDataId()\"\n [requestSchemaId]=\"requestSchemaId()\"\n [draftProcessId]=\"draftProcessId()\"\n [readonly]=\"isReadonly()\"\n [renderMode]=\"'sections'\"\n [lookups]=\"formLookups()\"\n [hostMessages]=\"actionMessages()\"\n />\n } @else {\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(\"context.requestFormUnavailable\") }}\n </p>\n </div>\n }\n </div>\n </div>\n\n <div\n class=\"flex-1 min-h-0 overflow-auto\"\n [hidden]=\"activeTab() !== 'approvals'\"\n >\n @if (canRenderPreview()) {\n <mt-work-center-process-preview\n [requestId]=\"requestId()\"\n [view]=\"'approvals'\"\n />\n } @else {\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(\"context.requestPreviewUnavailable\") }}\n </p>\n </div>\n }\n </div>\n\n <div\n class=\"flex-1 min-h-0 overflow-auto\"\n [hidden]=\"activeTab() !== 'schema'\"\n >\n @if (canRenderPreview()) {\n <mt-work-center-process-preview\n [requestId]=\"requestId()\"\n [view]=\"'schema'\"\n />\n } @else {\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(\"context.requestPreviewUnavailable\") }}\n </p>\n </div>\n }\n </div>\n\n @if (!readOnly()) {\n <div\n class=\"h-full min-h-0 overflow-hidden\"\n [hidden]=\"activeTab() !== 'discussion'\"\n >\n @if (canRenderDiscussion()) {\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]=\"discussionRecordId()\"\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 } @else {\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(\"context.requestDiscussionUnavailable\") }}\n </p>\n </div>\n }\n </div>\n }\n </div>\n </div>\n</div>\n" }]
|
|
2462
|
+
], 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() !== 'requestDetails'\"\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]=\"isReadonly()\"\r\n [renderMode]=\"'sections'\"\r\n [lookups]=\"formLookups()\"\r\n [hostMessages]=\"actionMessages()\"\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.requestFormUnavailable\") }}\r\n </p>\r\n </div>\r\n }\r\n </div>\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.requestPreviewUnavailable\") }}\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.requestPreviewUnavailable\") }}\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.requestDiscussionUnavailable\") }}\r\n </p>\r\n </div>\r\n }\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n</div>\r\n" }]
|
|
2486
2463
|
}], 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 }] }] } });
|
|
2487
2464
|
|
|
2488
2465
|
class WorkCenterProcessStepType {
|
|
@@ -2613,23 +2590,6 @@ class WorkCenterProcessStepType {
|
|
|
2613
2590
|
effect(() => {
|
|
2614
2591
|
this.actionContext?.setFormSource(this.readOnly() ? null : (this.clientForm() ?? null));
|
|
2615
2592
|
});
|
|
2616
|
-
// Publishes what the footer's `process-submit/validate` pre-flight needs.
|
|
2617
|
-
// Only the editable case is published: on a read-only form the approver
|
|
2618
|
-
// cannot fix anything, so blocking them on a rule failure would strand the
|
|
2619
|
-
// action instead of helping.
|
|
2620
|
-
effect(() => {
|
|
2621
|
-
this.formStore?.setContext(this.readOnly() || !this.canRenderForm()
|
|
2622
|
-
? null
|
|
2623
|
-
: {
|
|
2624
|
-
moduleKey: this.moduleKey(),
|
|
2625
|
-
operationKey: this.operationKey(),
|
|
2626
|
-
moduleId: this.moduleId(),
|
|
2627
|
-
levelId: this.levelId(),
|
|
2628
|
-
levelDataId: this.levelDataId(),
|
|
2629
|
-
moduleDataId: this.moduleDataId(),
|
|
2630
|
-
requestSchemaId: this.requestSchemaId(),
|
|
2631
|
-
});
|
|
2632
|
-
});
|
|
2633
2593
|
// `formControl.status` is not a signal, so mirror it into one — without
|
|
2634
2594
|
// this the footer would keep its stale enabled/disabled state while the
|
|
2635
2595
|
// approver fills the form in.
|
|
@@ -2659,12 +2619,11 @@ class WorkCenterProcessStepType {
|
|
|
2659
2619
|
}
|
|
2660
2620
|
ngOnDestroy() {
|
|
2661
2621
|
this.actionContext?.setFormSource(null);
|
|
2662
|
-
this.formStore?.setContext(null);
|
|
2663
2622
|
this.formStore?.setIncomplete(false);
|
|
2664
2623
|
this.formStore?.setErrorHost(false);
|
|
2665
2624
|
}
|
|
2666
2625
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: WorkCenterProcessStepType, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2667
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: WorkCenterProcessStepType, isStandalone: true, selector: "mt-work-center-process-step-type", 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 } }, viewQueries: [{ propertyName: "clientForm", first: true, predicate: ClientForm, descendants: true, isSignal: true }, { propertyName: "levelPreview", first: true, predicate: ClientInstancePreview, descendants: true, isSignal: true }], 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 <div\n class=\"h-full min-h-0 lg:overflow-hidden\"\n [hidden]=\"activeTab() !== 'form'\"\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 @if (canRenderForm()) {\n <mt-client-form\n [moduleKey]=\"moduleKey()\"\n [operationKey]=\"operationKey()\"\n [moduleId]=\"moduleId()\"\n [levelId]=\"levelId()\"\n [levelDataId]=\"levelDataId()\"\n [moduleDataId]=\"moduleDataId()\"\n [requestSchemaId]=\"requestSchemaId()\"\n [draftProcessId]=\"draftProcessId()\"\n [readonly]=\"readOnly()\"\n [renderMode]=\"'sections'\"\n [lookups]=\"formLookups()\"\n [hostMessages]=\"actionMessages()\"\n />\n } @else {\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(\"context.processFormUnavailable\") }}\n </p>\n </div>\n }\n </div>\n </div>\n\n <div\n class=\"h-full min-h-0 lg:overflow-hidden\"\n [hidden]=\"activeTab() !== 'levelDetails'\"\n >\n @if (levelDetailsConfig(); as levelDetailsConfig) {\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-client-instance-preview [config]=\"levelDetailsConfig\" />\n </div>\n }\n </div>\n\n <div\n class=\"flex-1 min-h-0 overflow-auto\"\n [hidden]=\"activeTab() !== 'approvals'\"\n >\n @if (canRenderPreview()) {\n <mt-work-center-process-preview\n [requestId]=\"requestId()\"\n [view]=\"'approvals'\"\n />\n } @else {\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(\"context.processPreviewUnavailable\") }}\n </p>\n </div>\n }\n </div>\n\n <div\n class=\"flex-1 min-h-0 overflow-auto\"\n [hidden]=\"activeTab() !== 'schema'\"\n >\n @if (canRenderPreview()) {\n <mt-work-center-process-preview\n [requestId]=\"requestId()\"\n [view]=\"'schema'\"\n />\n } @else {\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(\"context.processPreviewUnavailable\") }}\n </p>\n </div>\n }\n </div>\n\n @if (!readOnly()) {\n <div\n class=\"h-full min-h-0 overflow-hidden\"\n [hidden]=\"activeTab() !== 'discussion'\"\n >\n @if (canRenderDiscussion()) {\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]=\"discussionRecordId()\"\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 } @else {\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(\"context.processDiscussionUnavailable\") }}\n </p>\n </div>\n }\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: ClientForm, selector: "mt-client-form", inputs: ["moduleKey", "operationKey", "moduleId", "levelId", "levelDataId", "moduleDataId", "requestSchemaId", "draftProcessId", "templateId", "returnRecord", "preview", "forceOriginalForm", "returnUrl", "defaultValues", "submitRequestMapper", "submitTransport", "readonly", "autoLoad", "formMode", "renderMode", "showInternalStepActions", "confirmWarningsOnSubmit", "confirmWarningsOnStepChange", "silentMessages", "hostMessages", "readonlyFieldDisplayMode", "lookups", "statuses", "ignoredFieldKeys", "disabledFieldKeys", "allowedFieldKeys"], outputs: ["loaded", "submitted", "errored", "modeDetected", "formSourceDetected", "footerStateChanged"] }, { kind: "component", type: ClientInstancePreview, selector: "mt-client-instance-preview", inputs: ["config", "hideEmptyState", "stackOnNarrow"] }, { 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: WorkCenterProcessPreview, selector: "mt-work-center-process-preview", inputs: ["requestId", "view"] }] });
|
|
2626
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: WorkCenterProcessStepType, isStandalone: true, selector: "mt-work-center-process-step-type", 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 } }, viewQueries: [{ propertyName: "clientForm", first: true, predicate: ClientForm, descendants: true, isSignal: true }, { propertyName: "levelPreview", first: true, predicate: ClientInstancePreview, descendants: true, isSignal: true }], ngImport: i0, 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 [renderMode]=\"'sections'\"\r\n [lookups]=\"formLookups()\"\r\n [hostMessages]=\"actionMessages()\"\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", 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: ClientForm, selector: "mt-client-form", inputs: ["moduleKey", "operationKey", "moduleId", "levelId", "levelDataId", "moduleDataId", "requestSchemaId", "draftProcessId", "templateId", "returnRecord", "preview", "forceOriginalForm", "returnUrl", "defaultValues", "submitRequestMapper", "submitTransport", "readonly", "autoLoad", "formMode", "renderMode", "showInternalStepActions", "confirmWarningsOnSubmit", "confirmWarningsOnStepChange", "silentMessages", "hostMessages", "readonlyFieldDisplayMode", "lookups", "statuses", "ignoredFieldKeys", "disabledFieldKeys", "allowedFieldKeys"], outputs: ["loaded", "submitted", "errored", "modeDetected", "formSourceDetected", "footerStateChanged"] }, { kind: "component", type: ClientInstancePreview, selector: "mt-client-instance-preview", inputs: ["config", "hideEmptyState", "stackOnNarrow"] }, { 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: WorkCenterProcessPreview, selector: "mt-work-center-process-preview", inputs: ["requestId", "view"] }] });
|
|
2668
2627
|
}
|
|
2669
2628
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: WorkCenterProcessStepType, decorators: [{
|
|
2670
2629
|
type: Component,
|
|
@@ -2676,7 +2635,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
|
|
|
2676
2635
|
ClientInstancePreview,
|
|
2677
2636
|
DiscussionThread,
|
|
2678
2637
|
WorkCenterProcessPreview,
|
|
2679
|
-
], 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 <div\n class=\"h-full min-h-0 lg:overflow-hidden\"\n [hidden]=\"activeTab() !== 'form'\"\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 @if (canRenderForm()) {\n <mt-client-form\n [moduleKey]=\"moduleKey()\"\n [operationKey]=\"operationKey()\"\n [moduleId]=\"moduleId()\"\n [levelId]=\"levelId()\"\n [levelDataId]=\"levelDataId()\"\n [moduleDataId]=\"moduleDataId()\"\n [requestSchemaId]=\"requestSchemaId()\"\n [draftProcessId]=\"draftProcessId()\"\n [readonly]=\"readOnly()\"\n [renderMode]=\"'sections'\"\n [lookups]=\"formLookups()\"\n [hostMessages]=\"actionMessages()\"\n />\n } @else {\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(\"context.processFormUnavailable\") }}\n </p>\n </div>\n }\n </div>\n </div>\n\n <div\n class=\"h-full min-h-0 lg:overflow-hidden\"\n [hidden]=\"activeTab() !== 'levelDetails'\"\n >\n @if (levelDetailsConfig(); as levelDetailsConfig) {\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-client-instance-preview [config]=\"levelDetailsConfig\" />\n </div>\n }\n </div>\n\n <div\n class=\"flex-1 min-h-0 overflow-auto\"\n [hidden]=\"activeTab() !== 'approvals'\"\n >\n @if (canRenderPreview()) {\n <mt-work-center-process-preview\n [requestId]=\"requestId()\"\n [view]=\"'approvals'\"\n />\n } @else {\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(\"context.processPreviewUnavailable\") }}\n </p>\n </div>\n }\n </div>\n\n <div\n class=\"flex-1 min-h-0 overflow-auto\"\n [hidden]=\"activeTab() !== 'schema'\"\n >\n @if (canRenderPreview()) {\n <mt-work-center-process-preview\n [requestId]=\"requestId()\"\n [view]=\"'schema'\"\n />\n } @else {\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(\"context.processPreviewUnavailable\") }}\n </p>\n </div>\n }\n </div>\n\n @if (!readOnly()) {\n <div\n class=\"h-full min-h-0 overflow-hidden\"\n [hidden]=\"activeTab() !== 'discussion'\"\n >\n @if (canRenderDiscussion()) {\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]=\"discussionRecordId()\"\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 } @else {\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(\"context.processDiscussionUnavailable\") }}\n </p>\n </div>\n }\n </div>\n }\n </div>\n </div>\n</div>\n" }]
|
|
2638
|
+
], 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 [renderMode]=\"'sections'\"\r\n [lookups]=\"formLookups()\"\r\n [hostMessages]=\"actionMessages()\"\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" }]
|
|
2680
2639
|
}], 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 }] }] } });
|
|
2681
2640
|
|
|
2682
2641
|
/**
|
|
@@ -2709,6 +2668,10 @@ const SCHEDULE_PUBLISH_GROUP_ORDER = [
|
|
|
2709
2668
|
'changed',
|
|
2710
2669
|
'removed',
|
|
2711
2670
|
];
|
|
2671
|
+
/** The server's per-row baseline verdict says the published side also moved. */
|
|
2672
|
+
function isSchedulePublishConflictRow(row) {
|
|
2673
|
+
return row.draftBaselineState === 'conflict';
|
|
2674
|
+
}
|
|
2712
2675
|
function summarizeSchedulePublish(diff) {
|
|
2713
2676
|
const rows = diff?.rows ?? [];
|
|
2714
2677
|
// Counted from the rows rather than trusted from the envelope's own counters:
|
|
@@ -2716,18 +2679,30 @@ function summarizeSchedulePublish(diff) {
|
|
|
2716
2679
|
const addedCount = countByState(rows, 'added');
|
|
2717
2680
|
const changedCount = countByState(rows, 'changed');
|
|
2718
2681
|
const removedCount = countByState(rows, 'removed');
|
|
2682
|
+
const totalCount = addedCount + changedCount + removedCount;
|
|
2683
|
+
const conflictCount = rows.filter(isSchedulePublishConflictRow).length;
|
|
2719
2684
|
return {
|
|
2720
2685
|
hasSnapshot: diff?.hasSnapshot === true,
|
|
2721
2686
|
addedCount,
|
|
2722
2687
|
changedCount,
|
|
2723
2688
|
removedCount,
|
|
2724
|
-
totalCount
|
|
2725
|
-
|
|
2689
|
+
totalCount,
|
|
2690
|
+
conflictCount,
|
|
2691
|
+
isEmpty: totalCount === 0,
|
|
2692
|
+
needsAttention: totalCount > 0 || conflictCount > 0,
|
|
2726
2693
|
};
|
|
2727
2694
|
}
|
|
2728
2695
|
/**
|
|
2729
2696
|
* Group the comparison for display, optionally narrowed by a free-text search.
|
|
2730
2697
|
*
|
|
2698
|
+
* CONFLICTS LEAD, as their own section holding every conflicted row (whatever
|
|
2699
|
+
* its publish state - including the zero-diff `unchanged` convergences the
|
|
2700
|
+
* backend ships solely for this section). The approver has no resolution
|
|
2701
|
+
* control - the section is informational: the frozen plan they are approving
|
|
2702
|
+
* was captured while the live plan had moved under it. The remaining rows
|
|
2703
|
+
* group added → changed → removed exactly as before, with non-conflict
|
|
2704
|
+
* `unchanged` rows still dropped.
|
|
2705
|
+
*
|
|
2731
2706
|
* Search matches a row's NAME and its changed FIELD KEYS, so "start" finds every
|
|
2732
2707
|
* row whose planned start moved even though that text appears nowhere in a name.
|
|
2733
2708
|
* Empty groups are dropped rather than rendered as empty headers.
|
|
@@ -2736,8 +2711,22 @@ function groupSchedulePublishRows(diff, searchText = '') {
|
|
|
2736
2711
|
const rows = diff?.rows ?? [];
|
|
2737
2712
|
const needle = searchText.trim().toLowerCase();
|
|
2738
2713
|
const groups = [];
|
|
2714
|
+
const conflicts = rows.filter(isSchedulePublishConflictRow);
|
|
2715
|
+
if (conflicts.length > 0) {
|
|
2716
|
+
const matching = needle
|
|
2717
|
+
? conflicts.filter((row) => matchesSchedulePublishSearch(row, needle))
|
|
2718
|
+
: conflicts;
|
|
2719
|
+
if (matching.length > 0) {
|
|
2720
|
+
groups.push({
|
|
2721
|
+
state: 'conflict',
|
|
2722
|
+
rows: sortSchedulePublishRows(matching),
|
|
2723
|
+
totalCount: conflicts.length,
|
|
2724
|
+
});
|
|
2725
|
+
}
|
|
2726
|
+
}
|
|
2727
|
+
const rest = rows.filter((row) => !isSchedulePublishConflictRow(row));
|
|
2739
2728
|
for (const state of SCHEDULE_PUBLISH_GROUP_ORDER) {
|
|
2740
|
-
const all =
|
|
2729
|
+
const all = rest.filter((row) => row.state === state);
|
|
2741
2730
|
if (all.length === 0) {
|
|
2742
2731
|
continue;
|
|
2743
2732
|
}
|
|
@@ -2755,6 +2744,16 @@ function groupSchedulePublishRows(diff, searchText = '') {
|
|
|
2755
2744
|
}
|
|
2756
2745
|
return groups;
|
|
2757
2746
|
}
|
|
2747
|
+
/**
|
|
2748
|
+
* The changed-field keys a conflict row ships as one comma-joined string -
|
|
2749
|
+
* the same wire shape the schedule read envelope uses.
|
|
2750
|
+
*/
|
|
2751
|
+
function parseSchedulePublishChangedFields(changedFields) {
|
|
2752
|
+
return String(changedFields ?? '')
|
|
2753
|
+
.split(',')
|
|
2754
|
+
.map((field) => field.trim())
|
|
2755
|
+
.filter((field) => field.length > 0);
|
|
2756
|
+
}
|
|
2758
2757
|
/**
|
|
2759
2758
|
* Plan order, falling back to the row id.
|
|
2760
2759
|
*
|
|
@@ -2945,6 +2944,14 @@ class WorkCenterSchedulePublishType {
|
|
|
2945
2944
|
isExpandable(row) {
|
|
2946
2945
|
return row.state === 'changed' && this.changesFor(row).length > 0;
|
|
2947
2946
|
}
|
|
2947
|
+
/**
|
|
2948
|
+
* The published-side field keys of a conflict row, as human labels. Rendered
|
|
2949
|
+
* as an informational hint — the approver has no per-row resolution control;
|
|
2950
|
+
* the frozen plan they decide on was captured as-is.
|
|
2951
|
+
*/
|
|
2952
|
+
publishedMovedFields(row) {
|
|
2953
|
+
return parseSchedulePublishChangedFields(row.publishedChangedFields).map((field) => this.fieldLabel(field));
|
|
2954
|
+
}
|
|
2948
2955
|
groupBadgeClass(state) {
|
|
2949
2956
|
switch (state) {
|
|
2950
2957
|
case 'added':
|
|
@@ -2953,6 +2960,8 @@ class WorkCenterSchedulePublishType {
|
|
|
2953
2960
|
return 'bg-red-100 text-red-700';
|
|
2954
2961
|
case 'changed':
|
|
2955
2962
|
return 'bg-amber-100 text-amber-700';
|
|
2963
|
+
case 'conflict':
|
|
2964
|
+
return 'bg-orange-100 text-orange-700';
|
|
2956
2965
|
default:
|
|
2957
2966
|
return 'bg-surface-100 text-surface-700';
|
|
2958
2967
|
}
|
|
@@ -3026,7 +3035,7 @@ class WorkCenterSchedulePublishType {
|
|
|
3026
3035
|
});
|
|
3027
3036
|
}
|
|
3028
3037
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: WorkCenterSchedulePublishType, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3029
|
-
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\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 <!-- Comparison -->\r\n <div\r\n class=\"h-full min-h-0 lg:overflow-hidden\"\r\n [hidden]=\"activeTab() !== 'comparison'\"\r\n >\r\n <div\r\n class=\"flex h-full min-h-0 min-w-0 flex-col gap-3 lg:overflow-hidden\"\r\n >\r\n @if (loading()) {\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(\"loading.schedulePublish\") }}\r\n </p>\r\n </div>\r\n } @else if (error(); as loadError) {\r\n <div\r\n class=\"flex min-h-[22rem] items-center justify-center rounded-lg border border-dashed border-red-300 bg-red-50 p-6\"\r\n >\r\n <p class=\"max-w-md text-center text-sm text-red-600\">\r\n {{ loadError }}\r\n </p>\r\n </div>\r\n } @else if (!summary().hasSnapshot) {\r\n <!-- Never published: nothing has been announced, so nothing can\r\n differ from it. That is a different statement from \"no changes\". -->\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(\"publish.neverPublished\") }}\r\n </p>\r\n </div>\r\n } @else if (summary().isEmpty) {\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(\"publish.nothingToPublish\") }}\r\n </p>\r\n </div>\r\n } @else {\r\n <!-- Counts stay visible while searching, so a filtered view never\r\n reads as the whole request. -->\r\n <div class=\"flex flex-wrap items-center gap-2\">\r\n <span\r\n class=\"rounded-full px-2 py-0.5 text-xs font-medium\"\r\n [class]=\"groupBadgeClass('added')\"\r\n >{{\r\n t(\"publish.addedCount\", { count: summary().addedCount })\r\n }}</span\r\n >\r\n <span\r\n class=\"rounded-full px-2 py-0.5 text-xs font-medium\"\r\n [class]=\"groupBadgeClass('changed')\"\r\n >{{\r\n t(\"publish.changedCount\", { count: summary().changedCount })\r\n }}</span\r\n >\r\n <span\r\n class=\"rounded-full px-2 py-0.5 text-xs font-medium\"\r\n [class]=\"groupBadgeClass('removed')\"\r\n >{{\r\n t(\"publish.removedCount\", { count: summary().removedCount })\r\n }}</span\r\n >\r\n </div>\r\n\r\n <input\r\n type=\"search\"\r\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\"\r\n [placeholder]=\"t('publish.searchPlaceholder')\"\r\n [value]=\"searchText()\"\r\n (input)=\"onSearch($any($event.target).value)\"\r\n />\r\n\r\n <div\r\n class=\"min-h-0 flex-1 lg:overflow-y-auto lg:overscroll-contain lg:pr-2\"\r\n >\r\n @if (searchFoundNothing()) {\r\n <div\r\n class=\"flex min-h-[12rem] items-center justify-center rounded-lg border border-dashed border-surface-300 bg-surface-50 p-6\"\r\n >\r\n <p class=\"text-sm text-surface-500\">\r\n {{ t(\"publish.noSearchResults\") }}\r\n </p>\r\n </div>\r\n }\r\n\r\n @for (group of groups(); track group.state) {\r\n <section class=\"mb-4\">\r\n <header class=\"mb-2 flex items-center gap-2\">\r\n <h4 class=\"text-sm font-semibold text-surface-700\">\r\n {{ groupLabel(group.state) }}\r\n </h4>\r\n <span\r\n class=\"rounded-full px-2 py-0.5 text-xs font-medium\"\r\n [class]=\"groupBadgeClass(group.state)\"\r\n >{{ group.totalCount }}</span\r\n >\r\n </header>\r\n\r\n <ul class=\"flex flex-col gap-1\">\r\n @for (row of visibleRows(group); track rowKey(row)) {\r\n <li\r\n class=\"rounded-md border border-surface-200 bg-surface-0\"\r\n >\r\n <button\r\n type=\"button\"\r\n class=\"flex w-full items-center gap-2 px-3 py-2 text-start\"\r\n [class.cursor-default]=\"!isExpandable(row)\"\r\n [attr.aria-expanded]=\"\r\n isExpandable(row) ? isExpanded(row) : null\r\n \"\r\n [disabled]=\"!isExpandable(row)\"\r\n (click)=\"toggleRow(row)\"\r\n >\r\n @if (isExpandable(row)) {\r\n <span\r\n class=\"shrink-0 text-xs text-surface-500\"\r\n aria-hidden=\"true\"\r\n >{{ isExpanded(row) ? \"\u25BE\" : \"\u25B8\" }}</span\r\n >\r\n } @else {\r\n <span\r\n class=\"w-3 shrink-0\"\r\n aria-hidden=\"true\"\r\n ></span>\r\n }\r\n <span\r\n class=\"min-w-0 flex-1 truncate text-sm text-surface-800\"\r\n >{{ row.name }}</span\r\n >\r\n @if (row.type) {\r\n <span class=\"shrink-0 text-xs text-surface-500\">{{\r\n row.type\r\n }}</span>\r\n }\r\n </button>\r\n\r\n @if (isExpandable(row) && isExpanded(row)) {\r\n <div\r\n class=\"flex flex-col gap-3 border-t border-surface-200 px-3 py-3\"\r\n >\r\n @for (\r\n change of changesFor(row);\r\n track change.field\r\n ) {\r\n <div class=\"flex min-w-0 flex-col gap-1\">\r\n <span\r\n class=\"text-xs font-medium text-surface-600\"\r\n >{{ fieldLabel(change.field) }}</span\r\n >\r\n <!-- Before -> After rendered locally rather than\r\n via mt-comparison-value, whose Before/After\r\n labels are hardcoded English. Each side still\r\n goes through mt-entity-preview-body, so a date\r\n renders as a date and a user as an avatar. -->\r\n <div\r\n class=\"flex w-full min-w-0 items-stretch gap-3\"\r\n >\r\n <div\r\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\"\r\n >\r\n <span\r\n class=\"text-xs font-medium uppercase tracking-wide text-muted-color\"\r\n >{{ t(\"publish.before\") }}</span\r\n >\r\n <mt-entity-preview-body\r\n [data]=\"sideEntity(change, 'published')\"\r\n />\r\n </div>\r\n <div\r\n class=\"flex shrink-0 items-center text-muted-color\"\r\n aria-hidden=\"true\"\r\n >\r\n →\r\n </div>\r\n <div\r\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\"\r\n >\r\n <span\r\n class=\"text-xs font-medium uppercase tracking-wide text-primary-600\"\r\n >{{ t(\"publish.after\") }}</span\r\n >\r\n <mt-entity-preview-body\r\n [data]=\"sideEntity(change, 'current')\"\r\n />\r\n </div>\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n }\r\n </li>\r\n }\r\n </ul>\r\n\r\n @if (hiddenCount(group) > 0) {\r\n <!-- Said out loud: a silently truncated comparison reads as a\r\n complete one to whoever is approving it. -->\r\n <p class=\"mt-2 text-xs text-surface-500\">\r\n {{\r\n t(\"publish.hiddenRows\", { count: hiddenCount(group) })\r\n }}\r\n </p>\r\n }\r\n </section>\r\n }\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n\r\n <!-- Workflow -->\r\n @if (canRenderPreview()) {\r\n <div\r\n class=\"h-full min-h-0 lg:overflow-hidden\"\r\n [hidden]=\"activeTab() !== 'workflow'\"\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 <mt-work-center-process-preview\r\n [requestId]=\"requestId()\"\r\n [view]=\"'schema'\"\r\n />\r\n </div>\r\n </div>\r\n }\r\n\r\n <!-- Discussion -->\r\n @if (!readOnly() && canRenderDiscussion()) {\r\n <div\r\n class=\"h-full min-h-0 lg:overflow-hidden\"\r\n [hidden]=\"activeTab() !== 'discussion'\"\r\n >\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]=\"resolvedRequestId()\"\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 </div>\r\n }\r\n </div>\r\n </div>\r\n</div>\r\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"] }] });
|
|
3038
|
+
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\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 <!-- Comparison -->\r\n <div\r\n class=\"h-full min-h-0 lg:overflow-hidden\"\r\n [hidden]=\"activeTab() !== 'comparison'\"\r\n >\r\n <div\r\n class=\"flex h-full min-h-0 min-w-0 flex-col gap-3 lg:overflow-hidden\"\r\n >\r\n @if (loading()) {\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(\"loading.schedulePublish\") }}\r\n </p>\r\n </div>\r\n } @else if (error(); as loadError) {\r\n <div\r\n class=\"flex min-h-[22rem] items-center justify-center rounded-lg border border-dashed border-red-300 bg-red-50 p-6\"\r\n >\r\n <p class=\"max-w-md text-center text-sm text-red-600\">\r\n {{ loadError }}\r\n </p>\r\n </div>\r\n } @else if (!summary().hasSnapshot) {\r\n <!-- Never published: nothing has been announced, so nothing can\r\n differ from it. That is a different statement from \"no changes\". -->\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(\"publish.neverPublished\") }}\r\n </p>\r\n </div>\r\n } @else if (!summary().needsAttention) {\r\n <!-- Not `isEmpty`: a request whose only rows are zero-diff conflict\r\n convergences is publish-empty but must still show its conflicts\r\n section rather than claim there is nothing here. -->\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(\"publish.nothingToPublish\") }}\r\n </p>\r\n </div>\r\n } @else {\r\n <!-- Counts stay visible while searching, so a filtered view never\r\n reads as the whole request. -->\r\n <div class=\"flex flex-wrap items-center gap-2\">\r\n @if (summary().conflictCount > 0) {\r\n <span\r\n class=\"rounded-full px-2 py-0.5 text-xs font-medium\"\r\n [class]=\"groupBadgeClass('conflict')\"\r\n >{{\r\n t(\"publish.conflictCount\", {\r\n count: summary().conflictCount,\r\n })\r\n }}</span\r\n >\r\n }\r\n <span\r\n class=\"rounded-full px-2 py-0.5 text-xs font-medium\"\r\n [class]=\"groupBadgeClass('added')\"\r\n >{{\r\n t(\"publish.addedCount\", { count: summary().addedCount })\r\n }}</span\r\n >\r\n <span\r\n class=\"rounded-full px-2 py-0.5 text-xs font-medium\"\r\n [class]=\"groupBadgeClass('changed')\"\r\n >{{\r\n t(\"publish.changedCount\", { count: summary().changedCount })\r\n }}</span\r\n >\r\n <span\r\n class=\"rounded-full px-2 py-0.5 text-xs font-medium\"\r\n [class]=\"groupBadgeClass('removed')\"\r\n >{{\r\n t(\"publish.removedCount\", { count: summary().removedCount })\r\n }}</span\r\n >\r\n </div>\r\n\r\n <input\r\n type=\"search\"\r\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\"\r\n [placeholder]=\"t('publish.searchPlaceholder')\"\r\n [value]=\"searchText()\"\r\n (input)=\"onSearch($any($event.target).value)\"\r\n />\r\n\r\n <div\r\n class=\"min-h-0 flex-1 lg:overflow-y-auto lg:overscroll-contain lg:pr-2\"\r\n >\r\n @if (searchFoundNothing()) {\r\n <div\r\n class=\"flex min-h-[12rem] items-center justify-center rounded-lg border border-dashed border-surface-300 bg-surface-50 p-6\"\r\n >\r\n <p class=\"text-sm text-surface-500\">\r\n {{ t(\"publish.noSearchResults\") }}\r\n </p>\r\n </div>\r\n }\r\n\r\n @for (group of groups(); track group.state) {\r\n <section class=\"mb-4\">\r\n <header class=\"mb-2 flex items-center gap-2\">\r\n <h4 class=\"text-sm font-semibold text-surface-700\">\r\n {{ groupLabel(group.state) }}\r\n </h4>\r\n <span\r\n class=\"rounded-full px-2 py-0.5 text-xs font-medium\"\r\n [class]=\"groupBadgeClass(group.state)\"\r\n >{{ group.totalCount }}</span\r\n >\r\n </header>\r\n\r\n @if (group.state === \"conflict\") {\r\n <!-- Informational only: unlike the planner's drawer there is\r\n no keep-mine / use-published control here. The frozen\r\n plan under approval was captured while the live plan had\r\n moved on these rows. -->\r\n <p class=\"mb-2 text-xs text-surface-500\">\r\n {{ t(\"publish.conflictHint\") }}\r\n </p>\r\n }\r\n\r\n <ul class=\"flex flex-col gap-1\">\r\n @for (row of visibleRows(group); track rowKey(row)) {\r\n <li\r\n class=\"rounded-md border border-surface-200 bg-surface-0\"\r\n >\r\n <button\r\n type=\"button\"\r\n class=\"flex w-full items-center gap-2 px-3 py-2 text-start\"\r\n [class.cursor-default]=\"!isExpandable(row)\"\r\n [attr.aria-expanded]=\"\r\n isExpandable(row) ? isExpanded(row) : null\r\n \"\r\n [disabled]=\"!isExpandable(row)\"\r\n (click)=\"toggleRow(row)\"\r\n >\r\n @if (isExpandable(row)) {\r\n <span\r\n class=\"shrink-0 text-xs text-surface-500\"\r\n aria-hidden=\"true\"\r\n >{{ isExpanded(row) ? \"\u25BE\" : \"\u25B8\" }}</span\r\n >\r\n } @else {\r\n <span\r\n class=\"w-3 shrink-0\"\r\n aria-hidden=\"true\"\r\n ></span>\r\n }\r\n <span\r\n class=\"min-w-0 flex-1 truncate text-sm text-surface-800\"\r\n >{{ row.name }}</span\r\n >\r\n @if (row.type) {\r\n <span class=\"shrink-0 text-xs text-surface-500\">{{\r\n row.type\r\n }}</span>\r\n }\r\n </button>\r\n\r\n @if (\r\n group.state === \"conflict\" &&\r\n publishedMovedFields(row).length > 0\r\n ) {\r\n <p\r\n class=\"border-t border-surface-200 px-3 py-1.5 text-xs text-orange-700\"\r\n >\r\n {{ t(\"publish.publishedSideMoved\") }}:\r\n {{ publishedMovedFields(row).join(\", \") }}\r\n </p>\r\n }\r\n\r\n @if (isExpandable(row) && isExpanded(row)) {\r\n <div\r\n class=\"flex flex-col gap-3 border-t border-surface-200 px-3 py-3\"\r\n >\r\n @for (\r\n change of changesFor(row);\r\n track change.field\r\n ) {\r\n <div class=\"flex min-w-0 flex-col gap-1\">\r\n <span\r\n class=\"text-xs font-medium text-surface-600\"\r\n >{{ fieldLabel(change.field) }}</span\r\n >\r\n <!-- Before -> After rendered locally rather than\r\n via mt-comparison-value, whose Before/After\r\n labels are hardcoded English. Each side still\r\n goes through mt-entity-preview-body, so a date\r\n renders as a date and a user as an avatar. -->\r\n <div\r\n class=\"flex w-full min-w-0 items-stretch gap-3\"\r\n >\r\n <div\r\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\"\r\n >\r\n <span\r\n class=\"text-xs font-medium uppercase tracking-wide text-muted-color\"\r\n >{{ t(\"publish.before\") }}</span\r\n >\r\n <mt-entity-preview-body\r\n [data]=\"sideEntity(change, 'published')\"\r\n />\r\n </div>\r\n <div\r\n class=\"flex shrink-0 items-center text-muted-color\"\r\n aria-hidden=\"true\"\r\n >\r\n →\r\n </div>\r\n <div\r\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\"\r\n >\r\n <span\r\n class=\"text-xs font-medium uppercase tracking-wide text-primary-600\"\r\n >{{ t(\"publish.after\") }}</span\r\n >\r\n <mt-entity-preview-body\r\n [data]=\"sideEntity(change, 'current')\"\r\n />\r\n </div>\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n }\r\n </li>\r\n }\r\n </ul>\r\n\r\n @if (hiddenCount(group) > 0) {\r\n <!-- Said out loud: a silently truncated comparison reads as a\r\n complete one to whoever is approving it. -->\r\n <p class=\"mt-2 text-xs text-surface-500\">\r\n {{\r\n t(\"publish.hiddenRows\", { count: hiddenCount(group) })\r\n }}\r\n </p>\r\n }\r\n </section>\r\n }\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n\r\n <!-- Workflow -->\r\n @if (canRenderPreview()) {\r\n <div\r\n class=\"h-full min-h-0 lg:overflow-hidden\"\r\n [hidden]=\"activeTab() !== 'workflow'\"\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 <mt-work-center-process-preview\r\n [requestId]=\"requestId()\"\r\n [view]=\"'schema'\"\r\n />\r\n </div>\r\n </div>\r\n }\r\n\r\n <!-- Discussion -->\r\n @if (!readOnly() && canRenderDiscussion()) {\r\n <div\r\n class=\"h-full min-h-0 lg:overflow-hidden\"\r\n [hidden]=\"activeTab() !== 'discussion'\"\r\n >\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]=\"resolvedRequestId()\"\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 </div>\r\n }\r\n </div>\r\n </div>\r\n</div>\r\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"] }] });
|
|
3030
3039
|
}
|
|
3031
3040
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: WorkCenterSchedulePublishType, decorators: [{
|
|
3032
3041
|
type: Component,
|
|
@@ -3037,7 +3046,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
|
|
|
3037
3046
|
DiscussionThread,
|
|
3038
3047
|
EntityPreviewBody,
|
|
3039
3048
|
WorkCenterProcessPreview,
|
|
3040
|
-
], 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 <!-- Comparison -->\r\n <div\r\n class=\"h-full min-h-0 lg:overflow-hidden\"\r\n [hidden]=\"activeTab() !== 'comparison'\"\r\n >\r\n <div\r\n class=\"flex h-full min-h-0 min-w-0 flex-col gap-3 lg:overflow-hidden\"\r\n >\r\n @if (loading()) {\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(\"loading.schedulePublish\") }}\r\n </p>\r\n </div>\r\n } @else if (error(); as loadError) {\r\n <div\r\n class=\"flex min-h-[22rem] items-center justify-center rounded-lg border border-dashed border-red-300 bg-red-50 p-6\"\r\n >\r\n <p class=\"max-w-md text-center text-sm text-red-600\">\r\n {{ loadError }}\r\n </p>\r\n </div>\r\n } @else if (!summary().hasSnapshot) {\r\n <!-- Never published: nothing has been announced, so nothing can\r\n differ from it. That is a different statement from \"no changes\". -->\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(\"publish.neverPublished\") }}\r\n </p>\r\n </div>\r\n } @else if (summary().isEmpty) {\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(\"publish.nothingToPublish\") }}\r\n </p>\r\n </div>\r\n } @else {\r\n <!-- Counts stay visible while searching, so a filtered view never\r\n reads as the whole request. -->\r\n <div class=\"flex flex-wrap items-center gap-2\">\r\n <span\r\n class=\"rounded-full px-2 py-0.5 text-xs font-medium\"\r\n [class]=\"groupBadgeClass('added')\"\r\n >{{\r\n t(\"publish.addedCount\", { count: summary().addedCount })\r\n }}</span\r\n >\r\n <span\r\n class=\"rounded-full px-2 py-0.5 text-xs font-medium\"\r\n [class]=\"groupBadgeClass('changed')\"\r\n >{{\r\n t(\"publish.changedCount\", { count: summary().changedCount })\r\n }}</span\r\n >\r\n <span\r\n class=\"rounded-full px-2 py-0.5 text-xs font-medium\"\r\n [class]=\"groupBadgeClass('removed')\"\r\n >{{\r\n t(\"publish.removedCount\", { count: summary().removedCount })\r\n }}</span\r\n >\r\n </div>\r\n\r\n <input\r\n type=\"search\"\r\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\"\r\n [placeholder]=\"t('publish.searchPlaceholder')\"\r\n [value]=\"searchText()\"\r\n (input)=\"onSearch($any($event.target).value)\"\r\n />\r\n\r\n <div\r\n class=\"min-h-0 flex-1 lg:overflow-y-auto lg:overscroll-contain lg:pr-2\"\r\n >\r\n @if (searchFoundNothing()) {\r\n <div\r\n class=\"flex min-h-[12rem] items-center justify-center rounded-lg border border-dashed border-surface-300 bg-surface-50 p-6\"\r\n >\r\n <p class=\"text-sm text-surface-500\">\r\n {{ t(\"publish.noSearchResults\") }}\r\n </p>\r\n </div>\r\n }\r\n\r\n @for (group of groups(); track group.state) {\r\n <section class=\"mb-4\">\r\n <header class=\"mb-2 flex items-center gap-2\">\r\n <h4 class=\"text-sm font-semibold text-surface-700\">\r\n {{ groupLabel(group.state) }}\r\n </h4>\r\n <span\r\n class=\"rounded-full px-2 py-0.5 text-xs font-medium\"\r\n [class]=\"groupBadgeClass(group.state)\"\r\n >{{ group.totalCount }}</span\r\n >\r\n </header>\r\n\r\n <ul class=\"flex flex-col gap-1\">\r\n @for (row of visibleRows(group); track rowKey(row)) {\r\n <li\r\n class=\"rounded-md border border-surface-200 bg-surface-0\"\r\n >\r\n <button\r\n type=\"button\"\r\n class=\"flex w-full items-center gap-2 px-3 py-2 text-start\"\r\n [class.cursor-default]=\"!isExpandable(row)\"\r\n [attr.aria-expanded]=\"\r\n isExpandable(row) ? isExpanded(row) : null\r\n \"\r\n [disabled]=\"!isExpandable(row)\"\r\n (click)=\"toggleRow(row)\"\r\n >\r\n @if (isExpandable(row)) {\r\n <span\r\n class=\"shrink-0 text-xs text-surface-500\"\r\n aria-hidden=\"true\"\r\n >{{ isExpanded(row) ? \"\u25BE\" : \"\u25B8\" }}</span\r\n >\r\n } @else {\r\n <span\r\n class=\"w-3 shrink-0\"\r\n aria-hidden=\"true\"\r\n ></span>\r\n }\r\n <span\r\n class=\"min-w-0 flex-1 truncate text-sm text-surface-800\"\r\n >{{ row.name }}</span\r\n >\r\n @if (row.type) {\r\n <span class=\"shrink-0 text-xs text-surface-500\">{{\r\n row.type\r\n }}</span>\r\n }\r\n </button>\r\n\r\n @if (isExpandable(row) && isExpanded(row)) {\r\n <div\r\n class=\"flex flex-col gap-3 border-t border-surface-200 px-3 py-3\"\r\n >\r\n @for (\r\n change of changesFor(row);\r\n track change.field\r\n ) {\r\n <div class=\"flex min-w-0 flex-col gap-1\">\r\n <span\r\n class=\"text-xs font-medium text-surface-600\"\r\n >{{ fieldLabel(change.field) }}</span\r\n >\r\n <!-- Before -> After rendered locally rather than\r\n via mt-comparison-value, whose Before/After\r\n labels are hardcoded English. Each side still\r\n goes through mt-entity-preview-body, so a date\r\n renders as a date and a user as an avatar. -->\r\n <div\r\n class=\"flex w-full min-w-0 items-stretch gap-3\"\r\n >\r\n <div\r\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\"\r\n >\r\n <span\r\n class=\"text-xs font-medium uppercase tracking-wide text-muted-color\"\r\n >{{ t(\"publish.before\") }}</span\r\n >\r\n <mt-entity-preview-body\r\n [data]=\"sideEntity(change, 'published')\"\r\n />\r\n </div>\r\n <div\r\n class=\"flex shrink-0 items-center text-muted-color\"\r\n aria-hidden=\"true\"\r\n >\r\n →\r\n </div>\r\n <div\r\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\"\r\n >\r\n <span\r\n class=\"text-xs font-medium uppercase tracking-wide text-primary-600\"\r\n >{{ t(\"publish.after\") }}</span\r\n >\r\n <mt-entity-preview-body\r\n [data]=\"sideEntity(change, 'current')\"\r\n />\r\n </div>\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n }\r\n </li>\r\n }\r\n </ul>\r\n\r\n @if (hiddenCount(group) > 0) {\r\n <!-- Said out loud: a silently truncated comparison reads as a\r\n complete one to whoever is approving it. -->\r\n <p class=\"mt-2 text-xs text-surface-500\">\r\n {{\r\n t(\"publish.hiddenRows\", { count: hiddenCount(group) })\r\n }}\r\n </p>\r\n }\r\n </section>\r\n }\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n\r\n <!-- Workflow -->\r\n @if (canRenderPreview()) {\r\n <div\r\n class=\"h-full min-h-0 lg:overflow-hidden\"\r\n [hidden]=\"activeTab() !== 'workflow'\"\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 <mt-work-center-process-preview\r\n [requestId]=\"requestId()\"\r\n [view]=\"'schema'\"\r\n />\r\n </div>\r\n </div>\r\n }\r\n\r\n <!-- Discussion -->\r\n @if (!readOnly() && canRenderDiscussion()) {\r\n <div\r\n class=\"h-full min-h-0 lg:overflow-hidden\"\r\n [hidden]=\"activeTab() !== 'discussion'\"\r\n >\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]=\"resolvedRequestId()\"\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 </div>\r\n }\r\n </div>\r\n </div>\r\n</div>\r\n" }]
|
|
3049
|
+
], 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 <!-- Comparison -->\r\n <div\r\n class=\"h-full min-h-0 lg:overflow-hidden\"\r\n [hidden]=\"activeTab() !== 'comparison'\"\r\n >\r\n <div\r\n class=\"flex h-full min-h-0 min-w-0 flex-col gap-3 lg:overflow-hidden\"\r\n >\r\n @if (loading()) {\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(\"loading.schedulePublish\") }}\r\n </p>\r\n </div>\r\n } @else if (error(); as loadError) {\r\n <div\r\n class=\"flex min-h-[22rem] items-center justify-center rounded-lg border border-dashed border-red-300 bg-red-50 p-6\"\r\n >\r\n <p class=\"max-w-md text-center text-sm text-red-600\">\r\n {{ loadError }}\r\n </p>\r\n </div>\r\n } @else if (!summary().hasSnapshot) {\r\n <!-- Never published: nothing has been announced, so nothing can\r\n differ from it. That is a different statement from \"no changes\". -->\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(\"publish.neverPublished\") }}\r\n </p>\r\n </div>\r\n } @else if (!summary().needsAttention) {\r\n <!-- Not `isEmpty`: a request whose only rows are zero-diff conflict\r\n convergences is publish-empty but must still show its conflicts\r\n section rather than claim there is nothing here. -->\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(\"publish.nothingToPublish\") }}\r\n </p>\r\n </div>\r\n } @else {\r\n <!-- Counts stay visible while searching, so a filtered view never\r\n reads as the whole request. -->\r\n <div class=\"flex flex-wrap items-center gap-2\">\r\n @if (summary().conflictCount > 0) {\r\n <span\r\n class=\"rounded-full px-2 py-0.5 text-xs font-medium\"\r\n [class]=\"groupBadgeClass('conflict')\"\r\n >{{\r\n t(\"publish.conflictCount\", {\r\n count: summary().conflictCount,\r\n })\r\n }}</span\r\n >\r\n }\r\n <span\r\n class=\"rounded-full px-2 py-0.5 text-xs font-medium\"\r\n [class]=\"groupBadgeClass('added')\"\r\n >{{\r\n t(\"publish.addedCount\", { count: summary().addedCount })\r\n }}</span\r\n >\r\n <span\r\n class=\"rounded-full px-2 py-0.5 text-xs font-medium\"\r\n [class]=\"groupBadgeClass('changed')\"\r\n >{{\r\n t(\"publish.changedCount\", { count: summary().changedCount })\r\n }}</span\r\n >\r\n <span\r\n class=\"rounded-full px-2 py-0.5 text-xs font-medium\"\r\n [class]=\"groupBadgeClass('removed')\"\r\n >{{\r\n t(\"publish.removedCount\", { count: summary().removedCount })\r\n }}</span\r\n >\r\n </div>\r\n\r\n <input\r\n type=\"search\"\r\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\"\r\n [placeholder]=\"t('publish.searchPlaceholder')\"\r\n [value]=\"searchText()\"\r\n (input)=\"onSearch($any($event.target).value)\"\r\n />\r\n\r\n <div\r\n class=\"min-h-0 flex-1 lg:overflow-y-auto lg:overscroll-contain lg:pr-2\"\r\n >\r\n @if (searchFoundNothing()) {\r\n <div\r\n class=\"flex min-h-[12rem] items-center justify-center rounded-lg border border-dashed border-surface-300 bg-surface-50 p-6\"\r\n >\r\n <p class=\"text-sm text-surface-500\">\r\n {{ t(\"publish.noSearchResults\") }}\r\n </p>\r\n </div>\r\n }\r\n\r\n @for (group of groups(); track group.state) {\r\n <section class=\"mb-4\">\r\n <header class=\"mb-2 flex items-center gap-2\">\r\n <h4 class=\"text-sm font-semibold text-surface-700\">\r\n {{ groupLabel(group.state) }}\r\n </h4>\r\n <span\r\n class=\"rounded-full px-2 py-0.5 text-xs font-medium\"\r\n [class]=\"groupBadgeClass(group.state)\"\r\n >{{ group.totalCount }}</span\r\n >\r\n </header>\r\n\r\n @if (group.state === \"conflict\") {\r\n <!-- Informational only: unlike the planner's drawer there is\r\n no keep-mine / use-published control here. The frozen\r\n plan under approval was captured while the live plan had\r\n moved on these rows. -->\r\n <p class=\"mb-2 text-xs text-surface-500\">\r\n {{ t(\"publish.conflictHint\") }}\r\n </p>\r\n }\r\n\r\n <ul class=\"flex flex-col gap-1\">\r\n @for (row of visibleRows(group); track rowKey(row)) {\r\n <li\r\n class=\"rounded-md border border-surface-200 bg-surface-0\"\r\n >\r\n <button\r\n type=\"button\"\r\n class=\"flex w-full items-center gap-2 px-3 py-2 text-start\"\r\n [class.cursor-default]=\"!isExpandable(row)\"\r\n [attr.aria-expanded]=\"\r\n isExpandable(row) ? isExpanded(row) : null\r\n \"\r\n [disabled]=\"!isExpandable(row)\"\r\n (click)=\"toggleRow(row)\"\r\n >\r\n @if (isExpandable(row)) {\r\n <span\r\n class=\"shrink-0 text-xs text-surface-500\"\r\n aria-hidden=\"true\"\r\n >{{ isExpanded(row) ? \"\u25BE\" : \"\u25B8\" }}</span\r\n >\r\n } @else {\r\n <span\r\n class=\"w-3 shrink-0\"\r\n aria-hidden=\"true\"\r\n ></span>\r\n }\r\n <span\r\n class=\"min-w-0 flex-1 truncate text-sm text-surface-800\"\r\n >{{ row.name }}</span\r\n >\r\n @if (row.type) {\r\n <span class=\"shrink-0 text-xs text-surface-500\">{{\r\n row.type\r\n }}</span>\r\n }\r\n </button>\r\n\r\n @if (\r\n group.state === \"conflict\" &&\r\n publishedMovedFields(row).length > 0\r\n ) {\r\n <p\r\n class=\"border-t border-surface-200 px-3 py-1.5 text-xs text-orange-700\"\r\n >\r\n {{ t(\"publish.publishedSideMoved\") }}:\r\n {{ publishedMovedFields(row).join(\", \") }}\r\n </p>\r\n }\r\n\r\n @if (isExpandable(row) && isExpanded(row)) {\r\n <div\r\n class=\"flex flex-col gap-3 border-t border-surface-200 px-3 py-3\"\r\n >\r\n @for (\r\n change of changesFor(row);\r\n track change.field\r\n ) {\r\n <div class=\"flex min-w-0 flex-col gap-1\">\r\n <span\r\n class=\"text-xs font-medium text-surface-600\"\r\n >{{ fieldLabel(change.field) }}</span\r\n >\r\n <!-- Before -> After rendered locally rather than\r\n via mt-comparison-value, whose Before/After\r\n labels are hardcoded English. Each side still\r\n goes through mt-entity-preview-body, so a date\r\n renders as a date and a user as an avatar. -->\r\n <div\r\n class=\"flex w-full min-w-0 items-stretch gap-3\"\r\n >\r\n <div\r\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\"\r\n >\r\n <span\r\n class=\"text-xs font-medium uppercase tracking-wide text-muted-color\"\r\n >{{ t(\"publish.before\") }}</span\r\n >\r\n <mt-entity-preview-body\r\n [data]=\"sideEntity(change, 'published')\"\r\n />\r\n </div>\r\n <div\r\n class=\"flex shrink-0 items-center text-muted-color\"\r\n aria-hidden=\"true\"\r\n >\r\n →\r\n </div>\r\n <div\r\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\"\r\n >\r\n <span\r\n class=\"text-xs font-medium uppercase tracking-wide text-primary-600\"\r\n >{{ t(\"publish.after\") }}</span\r\n >\r\n <mt-entity-preview-body\r\n [data]=\"sideEntity(change, 'current')\"\r\n />\r\n </div>\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n }\r\n </li>\r\n }\r\n </ul>\r\n\r\n @if (hiddenCount(group) > 0) {\r\n <!-- Said out loud: a silently truncated comparison reads as a\r\n complete one to whoever is approving it. -->\r\n <p class=\"mt-2 text-xs text-surface-500\">\r\n {{\r\n t(\"publish.hiddenRows\", { count: hiddenCount(group) })\r\n }}\r\n </p>\r\n }\r\n </section>\r\n }\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n\r\n <!-- Workflow -->\r\n @if (canRenderPreview()) {\r\n <div\r\n class=\"h-full min-h-0 lg:overflow-hidden\"\r\n [hidden]=\"activeTab() !== 'workflow'\"\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 <mt-work-center-process-preview\r\n [requestId]=\"requestId()\"\r\n [view]=\"'schema'\"\r\n />\r\n </div>\r\n </div>\r\n }\r\n\r\n <!-- Discussion -->\r\n @if (!readOnly() && canRenderDiscussion()) {\r\n <div\r\n class=\"h-full min-h-0 lg:overflow-hidden\"\r\n [hidden]=\"activeTab() !== 'discussion'\"\r\n >\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]=\"resolvedRequestId()\"\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 </div>\r\n }\r\n </div>\r\n </div>\r\n</div>\r\n" }]
|
|
3041
3050
|
}], ctorParameters: () => [], propDecorators: { details: [{ type: i0.Input, args: [{ isSignal: true, alias: "details", required: true }] }], readOnly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readOnly", required: false }] }] } });
|
|
3042
3051
|
/**
|
|
3043
3052
|
* The comparison arrives on the response root, but several runtime endpoints
|
|
@@ -3142,18 +3151,65 @@ class WorkCenterFormValidationError extends Error {
|
|
|
3142
3151
|
}
|
|
3143
3152
|
}
|
|
3144
3153
|
|
|
3154
|
+
const FORM_VALIDATION_WARNINGS = 'FormValidationWarnings';
|
|
3145
3155
|
/**
|
|
3146
|
-
* Renders what a process action's validation has to say, before it runs
|
|
3147
|
-
* after it fails
|
|
3148
|
-
*
|
|
3149
|
-
* Actions do not negotiate over warnings. A Work Center action is one HTTP
|
|
3150
|
-
* request: the `process-submit/validate` pre-flight asks about non-blocking
|
|
3151
|
-
* failures up front (`confirmWarnings`), and whatever the action itself answers
|
|
3152
|
-
* is final — a failure is reported, never acknowledged and retried.
|
|
3156
|
+
* Renders what a process action's validation has to say, before it runs
|
|
3157
|
+
* (`confirmWarnings`, driven by the pre-flight) and after it fails
|
|
3158
|
+
* (`resolveErrorMessages`, `recover`).
|
|
3153
3159
|
*/
|
|
3154
3160
|
class WorkCenterActionValidationHandler {
|
|
3155
3161
|
modal = inject(ModalService);
|
|
3162
|
+
runner = inject(RuntimeActionRunner);
|
|
3156
3163
|
transloco = inject(TranslocoService);
|
|
3164
|
+
/**
|
|
3165
|
+
* Recovers the backend's `FormValidationWarnings` fallback.
|
|
3166
|
+
*
|
|
3167
|
+
* The backend revalidates immediately before it changes workflow state, so a
|
|
3168
|
+
* clean pre-flight is not a promise: configuration or persisted values may
|
|
3169
|
+
* have moved, the workflow may have advanced, or the receipt may have gone
|
|
3170
|
+
* stale. When that happens the action is refused with `400` and a
|
|
3171
|
+
* **replacement** token.
|
|
3172
|
+
*
|
|
3173
|
+
* These are warnings the user has not seen — that is the whole reason the
|
|
3174
|
+
* pre-flight's answer no longer holds — so they are shown and confirmed like
|
|
3175
|
+
* any other, and only then does the action go out again carrying the new
|
|
3176
|
+
* token. Declining reports the action as `cancelled`, and the workflow stays
|
|
3177
|
+
* where it was.
|
|
3178
|
+
*
|
|
3179
|
+
* `FormValidationErrors` — the blocking kind — are never recovered here.
|
|
3180
|
+
*/
|
|
3181
|
+
async recover(action, error, submittedPayload, defaultAfterSuccess) {
|
|
3182
|
+
const validationError = readProcessActionValidationError(error);
|
|
3183
|
+
if (validationError?.kind !== FORM_VALIDATION_WARNINGS ||
|
|
3184
|
+
!validationError.requiresAcknowledgement ||
|
|
3185
|
+
!validationError.validationWarningsToken ||
|
|
3186
|
+
validationError.validationWarningsToken ===
|
|
3187
|
+
submittedPayload?.['validationWarningsToken']) {
|
|
3188
|
+
return null;
|
|
3189
|
+
}
|
|
3190
|
+
const confirmed = await this.confirmWarnings(this.resolveValidationMessages(validationError));
|
|
3191
|
+
if (!confirmed) {
|
|
3192
|
+
return {
|
|
3193
|
+
status: 'cancelled',
|
|
3194
|
+
actionKey: action.actionKey,
|
|
3195
|
+
afterSuccess: null,
|
|
3196
|
+
};
|
|
3197
|
+
}
|
|
3198
|
+
const retryPayload = {
|
|
3199
|
+
...(submittedPayload ?? {}),
|
|
3200
|
+
validationWarningsToken: validationError.validationWarningsToken,
|
|
3201
|
+
};
|
|
3202
|
+
// This descriptor is intentionally ephemeral, and the retry is a new
|
|
3203
|
+
// request with its own idempotency key. A later action rebuilds its payload
|
|
3204
|
+
// from the current form, so a token can never outlive the values it was
|
|
3205
|
+
// issued for.
|
|
3206
|
+
return this.runner.execute({
|
|
3207
|
+
...action,
|
|
3208
|
+
needConfirmation: false,
|
|
3209
|
+
payloadKeys: Object.keys(retryPayload),
|
|
3210
|
+
payloadTemplate: retryPayload,
|
|
3211
|
+
}, { defaultAfterSuccess, silent: true });
|
|
3212
|
+
}
|
|
3157
3213
|
/**
|
|
3158
3214
|
* One entry per failing rule, so the footer can list them instead of
|
|
3159
3215
|
* rendering one newline-joined blob. A single-entry array is the normal
|
|
@@ -3192,10 +3248,10 @@ class WorkCenterActionValidationHandler {
|
|
|
3192
3248
|
return messages;
|
|
3193
3249
|
}
|
|
3194
3250
|
/**
|
|
3195
|
-
*
|
|
3196
|
-
* `
|
|
3197
|
-
*
|
|
3198
|
-
*
|
|
3251
|
+
* Shared by the `actions/validate` pre-flight in
|
|
3252
|
+
* `WorkCenterActionFormValidator` and by the `recover` fallback above. In the
|
|
3253
|
+
* normal flow only the pre-flight raises it; a second prompt means the
|
|
3254
|
+
* backend came back with warnings the pre-flight had not seen.
|
|
3199
3255
|
*
|
|
3200
3256
|
* Uses the client form's own warnings dialog so an approver sees the exact
|
|
3201
3257
|
* same prompt the normal submit path raises — same layout, same wording
|
|
@@ -3243,6 +3299,8 @@ function readProcessActionValidationError(error) {
|
|
|
3243
3299
|
: [];
|
|
3244
3300
|
return {
|
|
3245
3301
|
kind,
|
|
3302
|
+
requiresAcknowledgement: details?.['requiresAcknowledgement'] === true,
|
|
3303
|
+
validationWarningsToken: readNonEmptyString$1(details?.['validationWarningsToken']),
|
|
3246
3304
|
message: readNonEmptyString$1(errors?.['message']),
|
|
3247
3305
|
results,
|
|
3248
3306
|
};
|
|
@@ -3289,75 +3347,108 @@ function readNonEmptyString$1(value) {
|
|
|
3289
3347
|
return normalized.length ? normalized : null;
|
|
3290
3348
|
}
|
|
3291
3349
|
|
|
3292
|
-
const PROCEED = {
|
|
3293
|
-
|
|
3294
|
-
|
|
3350
|
+
const PROCEED = {
|
|
3351
|
+
status: 'proceed',
|
|
3352
|
+
validationWarningsToken: null,
|
|
3353
|
+
};
|
|
3295
3354
|
/**
|
|
3296
|
-
*
|
|
3297
|
-
*
|
|
3355
|
+
* The backend action names this endpoint accepts, keyed by the runtime
|
|
3356
|
+
* descriptor's `actionKey`. Reject / Return / Terminate are deliberately
|
|
3357
|
+
* absent: they are not validated against the approval form and must never be
|
|
3358
|
+
* pre-flighted.
|
|
3359
|
+
*/
|
|
3360
|
+
const PREFLIGHT_ACTIONS = {
|
|
3361
|
+
approve: 'Approve',
|
|
3362
|
+
resubmit: 'Resubmit',
|
|
3363
|
+
};
|
|
3364
|
+
const SILENT_MESSAGE_HEADERS = new HttpHeaders({ noMessage: 'true' });
|
|
3365
|
+
/**
|
|
3366
|
+
* Runs the action-aware validation pre-flight before Approve and Resubmit.
|
|
3367
|
+
*
|
|
3368
|
+
* The endpoint is `POST processes/{requestId}/actions/validate`, a sibling of
|
|
3369
|
+
* the action itself. It is deliberately **not** `process-submit/validate`:
|
|
3370
|
+
* that one answers for a create/edit submit and carries no active request,
|
|
3371
|
+
* step, action or effective approval form, so it validates the wrong scope for
|
|
3372
|
+
* an approval.
|
|
3373
|
+
*
|
|
3374
|
+
* Three outcomes, all HTTP `200`:
|
|
3298
3375
|
*
|
|
3299
|
-
*
|
|
3300
|
-
*
|
|
3301
|
-
*
|
|
3302
|
-
*
|
|
3303
|
-
* raise the acknowledge-and-continue prompt — without submitting anything.
|
|
3376
|
+
* - `Blocked` — the action is never sent, and the messages go to the footer;
|
|
3377
|
+
* - `Warning` — the messages are shown, and a confirmed prompt hands the
|
|
3378
|
+
* `validationWarningsToken` to the action that follows;
|
|
3379
|
+
* - `Valid` — straight through, no token.
|
|
3304
3380
|
*
|
|
3305
|
-
*
|
|
3306
|
-
*
|
|
3307
|
-
*
|
|
3381
|
+
* The action endpoint stays authoritative and revalidates immediately before it
|
|
3382
|
+
* changes workflow state, so a pre-flight that cannot be reached lets the action
|
|
3383
|
+
* through rather than stranding it. See
|
|
3384
|
+
* `WorkCenterActionValidationHandler.recover` for what happens when that second
|
|
3385
|
+
* opinion disagrees.
|
|
3308
3386
|
*/
|
|
3309
3387
|
class WorkCenterActionFormValidator {
|
|
3310
|
-
|
|
3388
|
+
http = inject(HttpClient);
|
|
3311
3389
|
actionContext = inject(RuntimeActionContextStore);
|
|
3312
|
-
contextStore = inject(WorkCenterActionFormStore);
|
|
3313
3390
|
warnings = inject(WorkCenterActionValidationHandler);
|
|
3314
3391
|
transloco = inject(TranslocoService);
|
|
3315
3392
|
async validate(action) {
|
|
3316
|
-
const
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
}
|
|
3320
|
-
const fields = this.actionContext.resolveFormValues();
|
|
3321
|
-
if (!fields.length) {
|
|
3393
|
+
const actionName = PREFLIGHT_ACTIONS[normalizeActionKey(action.actionKey)];
|
|
3394
|
+
const url = resolveValidateUrl(action);
|
|
3395
|
+
if (!actionName || !url) {
|
|
3322
3396
|
return PROCEED;
|
|
3323
3397
|
}
|
|
3324
|
-
const validation = await this.runValidation(
|
|
3398
|
+
const validation = await this.runValidation(url, {
|
|
3399
|
+
stepId: resolveStepId(action),
|
|
3400
|
+
action: actionName,
|
|
3401
|
+
// An action form with no editable fields sends `[]`, which the backend
|
|
3402
|
+
// accepts — a read-only approver is still validated.
|
|
3403
|
+
values: this.actionContext.resolveFormValues(),
|
|
3404
|
+
});
|
|
3325
3405
|
// A transport failure must not strand an otherwise valid action: the
|
|
3326
3406
|
// pre-flight is advisory and the action endpoint validates again anyway.
|
|
3327
3407
|
if (!validation) {
|
|
3328
3408
|
return PROCEED;
|
|
3329
3409
|
}
|
|
3330
|
-
const failures = (validation.results ?? []).filter(
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3410
|
+
const failures = (validation.validation?.results ?? []).filter((result) => !result.passed);
|
|
3411
|
+
if (validation.status === 'Blocked' ||
|
|
3412
|
+
!validation.canProceed ||
|
|
3413
|
+
validation.validation?.hasBlockingFailures) {
|
|
3334
3414
|
return {
|
|
3335
3415
|
status: 'blocked',
|
|
3336
|
-
error:
|
|
3337
|
-
? messages
|
|
3338
|
-
: [
|
|
3339
|
-
this.transloco.translate('workCenter.actions.validationBlocked'),
|
|
3340
|
-
]),
|
|
3416
|
+
error: this.blockedError(validation, failures),
|
|
3341
3417
|
};
|
|
3342
3418
|
}
|
|
3343
|
-
if (
|
|
3419
|
+
if (validation.status !== 'Warning' &&
|
|
3420
|
+
!validation.requiresAcknowledgement) {
|
|
3344
3421
|
return PROCEED;
|
|
3345
3422
|
}
|
|
3346
3423
|
const confirmed = await this.warnings.confirmWarnings(this.resolveMessages(failures));
|
|
3347
|
-
return confirmed
|
|
3424
|
+
return confirmed
|
|
3425
|
+
? {
|
|
3426
|
+
status: 'proceed',
|
|
3427
|
+
validationWarningsToken: readNonEmptyString(validation.validationWarningsToken),
|
|
3428
|
+
}
|
|
3429
|
+
: { status: 'cancelled' };
|
|
3348
3430
|
}
|
|
3349
|
-
async runValidation(
|
|
3350
|
-
const response = await firstValueFrom(this.
|
|
3351
|
-
.
|
|
3431
|
+
async runValidation(url, request) {
|
|
3432
|
+
const response = await firstValueFrom(this.http
|
|
3433
|
+
.post(url, request, { headers: SILENT_MESSAGE_HEADERS })
|
|
3352
3434
|
.pipe(catchError(() => of(null))));
|
|
3353
|
-
return response?.data ?? null;
|
|
3435
|
+
return response?.code === 1 ? (response.data ?? null) : null;
|
|
3436
|
+
}
|
|
3437
|
+
blockedError(validation, failures) {
|
|
3438
|
+
const messages = this.resolveMessages(failures);
|
|
3439
|
+
if (messages.length) {
|
|
3440
|
+
return new WorkCenterFormValidationError(messages);
|
|
3441
|
+
}
|
|
3442
|
+
return new WorkCenterFormValidationError([
|
|
3443
|
+
readNonEmptyString(validation.message) ??
|
|
3444
|
+
this.transloco.translate('workCenter.actions.validationBlocked'),
|
|
3445
|
+
]);
|
|
3354
3446
|
}
|
|
3355
3447
|
resolveMessages(results) {
|
|
3356
3448
|
const messages = [];
|
|
3357
3449
|
const seen = new Set();
|
|
3358
3450
|
for (const result of results) {
|
|
3359
|
-
const message = resolveLocalizedText(result.message, this.transloco.getActiveLang())
|
|
3360
|
-
readNonEmptyString(result.errorMessage);
|
|
3451
|
+
const message = resolveLocalizedText(result.message, this.transloco.getActiveLang());
|
|
3361
3452
|
if (!message || seen.has(message))
|
|
3362
3453
|
continue;
|
|
3363
3454
|
seen.add(message);
|
|
@@ -3372,51 +3463,31 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
|
|
|
3372
3463
|
type: Injectable
|
|
3373
3464
|
}] });
|
|
3374
3465
|
/**
|
|
3375
|
-
*
|
|
3376
|
-
*
|
|
3377
|
-
*
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
const request = {
|
|
3381
|
-
moduleKey: context.moduleKey,
|
|
3382
|
-
operationKey: context.operationKey,
|
|
3383
|
-
fields,
|
|
3384
|
-
};
|
|
3385
|
-
if (context.moduleId != null)
|
|
3386
|
-
request.moduleId = context.moduleId;
|
|
3387
|
-
if (context.levelId != null)
|
|
3388
|
-
request.levelId = context.levelId;
|
|
3389
|
-
if (context.levelDataId != null)
|
|
3390
|
-
request.levelDataId = context.levelDataId;
|
|
3391
|
-
if (context.moduleDataId != null)
|
|
3392
|
-
request.moduleDataId = context.moduleDataId;
|
|
3393
|
-
if (context.requestSchemaId != null) {
|
|
3394
|
-
request.requestSchemaId = context.requestSchemaId;
|
|
3395
|
-
}
|
|
3396
|
-
const recordId = context.moduleDataId ??
|
|
3397
|
-
(context.moduleKey === 'LevelData' && context.operationKey !== 'Create'
|
|
3398
|
-
? context.levelDataId
|
|
3399
|
-
: undefined);
|
|
3400
|
-
if (recordId != null)
|
|
3401
|
-
request.recordId = recordId;
|
|
3402
|
-
return request;
|
|
3403
|
-
}
|
|
3404
|
-
/**
|
|
3405
|
-
* Only actions that carry the edited form values are worth validating —
|
|
3406
|
-
* Delete, Close and friends submit nothing the rules can read.
|
|
3466
|
+
* The pre-flight is the action's own sibling route, so it is derived from the
|
|
3467
|
+
* descriptor's URL rather than rebuilt from a `requestId` the FE would have to
|
|
3468
|
+
* source somewhere else — `.../processes/{requestId}/actions/approve` becomes
|
|
3469
|
+
* `.../processes/{requestId}/actions/validate`. That also keeps both calls on
|
|
3470
|
+
* whatever base the backend handed out.
|
|
3407
3471
|
*/
|
|
3408
|
-
function
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3472
|
+
function resolveValidateUrl(action) {
|
|
3473
|
+
const url = action.url?.trim().split(/[?#]/)[0].replace(/\/+$/, '');
|
|
3474
|
+
if (!url) {
|
|
3475
|
+
return null;
|
|
3476
|
+
}
|
|
3477
|
+
const lastSlash = url.lastIndexOf('/');
|
|
3478
|
+
return lastSlash > 0 ? `${url.slice(0, lastSlash)}/validate` : null;
|
|
3412
3479
|
}
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3480
|
+
/** The same resolution the action payload uses for its own `stepId`. */
|
|
3481
|
+
function resolveStepId(action) {
|
|
3482
|
+
const templateStepId = action.payloadTemplate?.['stepId'];
|
|
3483
|
+
if (typeof templateStepId === 'number' ||
|
|
3484
|
+
typeof templateStepId === 'string') {
|
|
3485
|
+
return templateStepId;
|
|
3486
|
+
}
|
|
3487
|
+
return action.stepIds?.[0] ?? null;
|
|
3417
3488
|
}
|
|
3418
|
-
function
|
|
3419
|
-
return
|
|
3489
|
+
function normalizeActionKey(value) {
|
|
3490
|
+
return typeof value === 'string' ? value.trim().toLowerCase() : '';
|
|
3420
3491
|
}
|
|
3421
3492
|
function readNonEmptyString(value) {
|
|
3422
3493
|
if (typeof value !== 'string')
|
|
@@ -3426,18 +3497,20 @@ function readNonEmptyString(value) {
|
|
|
3426
3497
|
}
|
|
3427
3498
|
|
|
3428
3499
|
/**
|
|
3429
|
-
* Runs a Work Center footer action as
|
|
3500
|
+
* Runs a Work Center footer action, normally as a single HTTP request.
|
|
3430
3501
|
*
|
|
3431
|
-
* Everything negotiable happens before
|
|
3432
|
-
*
|
|
3433
|
-
*
|
|
3434
|
-
* the
|
|
3435
|
-
*
|
|
3502
|
+
* Everything negotiable happens before it: the `actions/validate` pre-flight
|
|
3503
|
+
* blocks the action or takes the warning acknowledgement, then the reason/note
|
|
3504
|
+
* dialog collects its fields, and the acknowledgement receipt rides along on
|
|
3505
|
+
* the request itself. A second request happens only when the backend's own
|
|
3506
|
+
* revalidation disagrees with the pre-flight — see
|
|
3507
|
+
* `WorkCenterActionValidationHandler.recover`.
|
|
3436
3508
|
*/
|
|
3437
3509
|
class WorkCenterRuntimeActionExecutor {
|
|
3438
3510
|
runner = inject(RuntimeActionRunner);
|
|
3439
3511
|
actionContext = inject(RuntimeActionContextStore);
|
|
3440
3512
|
modal = inject(ModalService);
|
|
3513
|
+
validation = inject(WorkCenterActionValidationHandler);
|
|
3441
3514
|
formValidation = inject(WorkCenterActionFormValidator);
|
|
3442
3515
|
confirmDialogClass = inject(RUNTIME_ACTION_CONFIRM_DIALOG, {
|
|
3443
3516
|
optional: true,
|
|
@@ -3462,20 +3535,33 @@ class WorkCenterRuntimeActionExecutor {
|
|
|
3462
3535
|
error: preflight.error,
|
|
3463
3536
|
};
|
|
3464
3537
|
}
|
|
3538
|
+
// The receipt for the warnings the user just acknowledged. It goes out with
|
|
3539
|
+
// the action itself, so an acknowledged Approve is a single request.
|
|
3540
|
+
const warningsToken = preflight.validationWarningsToken;
|
|
3465
3541
|
if (action.needConfirmation &&
|
|
3466
3542
|
hasActionFormFields(action) &&
|
|
3467
3543
|
this.confirmDialogClass) {
|
|
3468
|
-
return this.executeFormAction(action, defaultAfterSuccess);
|
|
3544
|
+
return this.executeFormAction(action, defaultAfterSuccess, warningsToken);
|
|
3469
3545
|
}
|
|
3470
|
-
|
|
3546
|
+
let submittedPayload;
|
|
3547
|
+
let result = await this.runner.execute(action, {
|
|
3471
3548
|
defaultAfterSuccess,
|
|
3472
3549
|
silent: true,
|
|
3473
3550
|
externalPayloadResolvers: {
|
|
3474
3551
|
values: () => this.actionContext.resolveFormValues(),
|
|
3475
3552
|
},
|
|
3553
|
+
transformPayload: (payload) => {
|
|
3554
|
+
submittedPayload = withWarningsToken(payload, warningsToken);
|
|
3555
|
+
return submittedPayload;
|
|
3556
|
+
},
|
|
3476
3557
|
});
|
|
3558
|
+
if (result.status === 'error') {
|
|
3559
|
+
result =
|
|
3560
|
+
(await this.validation.recover(action, result.error, submittedPayload, defaultAfterSuccess)) ?? result;
|
|
3561
|
+
}
|
|
3562
|
+
return result;
|
|
3477
3563
|
}
|
|
3478
|
-
executeFormAction(action, defaultAfterSuccess) {
|
|
3564
|
+
executeFormAction(action, defaultAfterSuccess, warningsToken) {
|
|
3479
3565
|
return new Promise((resolve) => {
|
|
3480
3566
|
let pendingRun = null;
|
|
3481
3567
|
let finalResult = null;
|
|
@@ -3486,19 +3572,27 @@ class WorkCenterRuntimeActionExecutor {
|
|
|
3486
3572
|
inputValues: {
|
|
3487
3573
|
action,
|
|
3488
3574
|
onSubmit: async (formValue) => {
|
|
3489
|
-
const
|
|
3575
|
+
const submittedPayload = withWarningsToken(buildActionPayload(action, formValue, {
|
|
3490
3576
|
values: () => this.actionContext.resolveFormValues(),
|
|
3491
|
-
})
|
|
3577
|
+
}), warningsToken);
|
|
3578
|
+
const executablePayload = submittedPayload ?? {};
|
|
3492
3579
|
const executableAction = {
|
|
3493
3580
|
...action,
|
|
3494
3581
|
needConfirmation: false,
|
|
3495
3582
|
payloadKeys: Object.keys(executablePayload),
|
|
3496
3583
|
payloadTemplate: executablePayload,
|
|
3497
3584
|
};
|
|
3498
|
-
pendingRun =
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3585
|
+
pendingRun = (async () => {
|
|
3586
|
+
let result = await this.runner.execute(executableAction, {
|
|
3587
|
+
defaultAfterSuccess,
|
|
3588
|
+
silent: true,
|
|
3589
|
+
});
|
|
3590
|
+
if (result.status === 'error') {
|
|
3591
|
+
result =
|
|
3592
|
+
(await this.validation.recover(action, result.error, submittedPayload, defaultAfterSuccess)) ?? result;
|
|
3593
|
+
}
|
|
3594
|
+
return result;
|
|
3595
|
+
})();
|
|
3502
3596
|
finalResult = await pendingRun;
|
|
3503
3597
|
// Every backend outcome leaves the action dialog. The footer then
|
|
3504
3598
|
// refreshes on success or displays the actionable validation error.
|
|
@@ -3539,6 +3633,17 @@ class WorkCenterRuntimeActionExecutor {
|
|
|
3539
3633
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: WorkCenterRuntimeActionExecutor, decorators: [{
|
|
3540
3634
|
type: Injectable
|
|
3541
3635
|
}] });
|
|
3636
|
+
/**
|
|
3637
|
+
* Attaches the pre-flight's acknowledgement receipt to the payload the action
|
|
3638
|
+
* is about to send. Without a receipt — a `Valid` pre-flight, or one that never
|
|
3639
|
+
* ran — the payload goes out exactly as built.
|
|
3640
|
+
*/
|
|
3641
|
+
function withWarningsToken(payload, warningsToken) {
|
|
3642
|
+
if (!warningsToken || !payload) {
|
|
3643
|
+
return payload;
|
|
3644
|
+
}
|
|
3645
|
+
return { ...payload, validationWarningsToken: warningsToken };
|
|
3646
|
+
}
|
|
3542
3647
|
|
|
3543
3648
|
const DEFAULT_GENERAL_TASK_MODAL_STYLE_CLASS = '!w-[70vw] !h-[90vh]';
|
|
3544
3649
|
class WorkCenterItemModalFooterActions {
|
|
@@ -4670,5 +4775,5 @@ const APP_STATES = [WorkCenterState];
|
|
|
4670
4775
|
* Generated bundle index. Do not edit.
|
|
4671
4776
|
*/
|
|
4672
4777
|
|
|
4673
|
-
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 };
|
|
4778
|
+
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, isSchedulePublishConflictRow, matchesSchedulePublishSearch, parseSchedulePublishChangedFields, readSchedulePublishChanges, schedulePublishRowKey, sortSchedulePublishRows, summarizeSchedulePublish, windowSchedulePublishRows };
|
|
4674
4779
|
//# sourceMappingURL=masterteam-work-center.mjs.map
|