@masterteam/work-center 0.0.83 → 0.0.85
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/i18n/ar.json +5 -1
- package/assets/i18n/en.json +5 -1
- package/assets/work-center.css +1 -1
- package/fesm2022/masterteam-work-center-work-center-process-preview-qG9T9HoP.mjs +538 -0
- package/fesm2022/masterteam-work-center-work-center-process-preview-qG9T9HoP.mjs.map +1 -0
- package/fesm2022/masterteam-work-center.mjs +120 -599
- package/fesm2022/masterteam-work-center.mjs.map +1 -1
- package/package.json +5 -5
- package/types/masterteam-work-center.d.ts +54 -5
|
@@ -25,7 +25,6 @@ import { handleApiRequest } from '@masterteam/components';
|
|
|
25
25
|
import { ClientInstancePreview } from '@masterteam/client-components/client-instance-preview';
|
|
26
26
|
import { EntitiesPreview, EntityPreviewBody } from '@masterteam/components/entities';
|
|
27
27
|
import { Tabs } from '@masterteam/components/tabs';
|
|
28
|
-
import { DiscussionThread } from '@masterteam/discussion';
|
|
29
28
|
import { StructureBuilder } from '@masterteam/structure-builder';
|
|
30
29
|
import { Icon } from '@masterteam/icons';
|
|
31
30
|
|
|
@@ -1624,20 +1623,20 @@ class WorkCenterEscalationInstanceType {
|
|
|
1624
1623
|
});
|
|
1625
1624
|
}
|
|
1626
1625
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: WorkCenterEscalationInstanceType, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1627
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: WorkCenterEscalationInstanceType, isStandalone: true, selector: "mt-work-center-escalation-instance-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\
|
|
1628
|
-
}
|
|
1629
|
-
i0.ɵɵ
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1626
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: WorkCenterEscalationInstanceType, isStandalone: true, selector: "mt-work-center-escalation-instance-type", inputs: { details: { classPropertyName: "details", publicName: "details", isSignal: true, isRequired: true, transformFunction: null }, readOnly: { classPropertyName: "readOnly", publicName: "readOnly", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div\n class=\"mt-modal-content flex h-full min-h-0 flex-col gap-4 p-4 lg:overflow-hidden\"\n *transloco=\"let t; prefix: 'workCenter'\"\n>\n <div\n class=\"flex flex-1 min-h-0 flex-col gap-4 lg:grid lg:grid-cols-[13rem_minmax(0,1fr)] lg:gap-6\"\n >\n @if (tabOptions().length > 1) {\n <mt-tabs\n [(active)]=\"activeTab\"\n [options]=\"tabOptions()\"\n mode=\"vertical\"\n size=\"small\"\n />\n }\n\n <div class=\"min-w-0 flex-1 min-h-0 lg:overflow-hidden\">\n <div\n class=\"h-full min-h-0 lg:overflow-hidden\"\n [hidden]=\"activeTab() !== 'escalationDetails'\"\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 (canRenderDetails()) {\n @if (loading()) {\n <div\n class=\"flex min-h-[22rem] items-center justify-center rounded-lg border border-dashed border-surface-300 bg-surface-50 p-6\"\n >\n <p class=\"max-w-md text-center text-sm text-surface-500\">\n {{ t(\"loading.escalationDetails\") }}\n </p>\n </div>\n } @else if (error(); as error) {\n <div\n class=\"flex min-h-[22rem] items-center justify-center rounded-lg border border-dashed border-red-300 bg-red-50 p-6\"\n >\n <p class=\"max-w-md text-center text-sm text-red-600\">\n {{ error }}\n </p>\n </div>\n } @else if (preview()) {\n <mt-entities-preview [entities]=\"detailEntities()\" />\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.escalationDetailsUnavailable\") }}\n </p>\n </div>\n }\n </div>\n </div>\n\n @defer (when activeTab() === \"moduleDetails\") {\n <div\n class=\"h-full min-h-0 lg:overflow-hidden\"\n [hidden]=\"activeTab() !== 'moduleDetails'\"\n >\n @if (moduleDetailsConfig(); as moduleDetailsConfig) {\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]=\"moduleDetailsConfig\" />\n </div>\n }\n </div>\n }\n\n @defer (when activeTab() === \"schema\") {\n <div\n class=\"flex-1 min-h-0 overflow-auto\"\n [hidden]=\"activeTab() !== 'schema'\"\n >\n @if (canRenderDetails()) {\n @if (loading()) {\n <div\n class=\"flex min-h-[22rem] items-center justify-center rounded-lg border border-dashed border-surface-300 bg-surface-50 p-6\"\n >\n <p class=\"max-w-md text-center text-sm text-surface-500\">\n {{ t(\"loading.escalationSchema\") }}\n </p>\n </div>\n } @else if (error(); as error) {\n <div\n class=\"flex min-h-[22rem] items-center justify-center rounded-lg border border-dashed border-red-300 bg-red-50 p-6\"\n >\n <p class=\"max-w-md text-center text-sm text-red-600\">\n {{ error }}\n </p>\n </div>\n } @else if (hasSchema()) {\n <!--\n Only mount the flow once the schema tab is actually active. The\n structure-builder fits the diagram to the host size on load\n (fLoaded -> fitToScreen) and never re-fits, so mounting it inside\n the [hidden] (display:none, zero-size) container produces a blank\n canvas that doesn't recover when the tab is shown.\n -->\n @if (activeTab() === \"schema\") {\n <!--\n Use a concrete height (not lg:h-full). The diagram has no\n intrinsic height, and the tab's ancestor chain is block (not\n flex) on lg, so `h-full` collapses to 0 and the canvas renders\n blank. Mirrors the process preview's `h-[70vh]`.\n -->\n <div class=\"h-[70vh] min-h-[22rem] overflow-hidden rounded-lg\">\n <mt-structure-builder\n class=\"h-full\"\n [layoutDirection]=\"'LR'\"\n [readonly]=\"true\"\n [structureMode]=\"'workflow'\"\n [nodeFields]=\"previewNodeFields\"\n [nodes]=\"schemaNodes()\"\n [connections]=\"schemaConnections()\"\n />\n </div>\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.escalationSchemaUnavailable\") }}\n </p>\n </div>\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.escalationSchemaContextUnavailable\") }}\n </p>\n </div>\n }\n </div>\n }\n\n @if (!readOnly()) {\n @defer (when activeTab() === \"discussion\") {\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]=\"'EscalationInstance'\"\n [recordId]=\"resolvedInstanceId()\"\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.escalationDiscussionUnavailable\") }}\n </p>\n </div>\n }\n </div>\n }\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: EntitiesPreview, selector: "mt-entities-preview", inputs: ["entities", "attachmentShape", "stackOnNarrow", "clickableKeys", "activeKeys", "clickableTooltip", "activeTooltip"], outputs: ["entityClick"] }], deferBlockDependencies: [() => [ClientInstancePreview], () => [StructureBuilder], () => [import('@masterteam/discussion').then(m => m.DiscussionThread)]] });
|
|
1627
|
+
}
|
|
1628
|
+
i0.ɵɵngDeclareClassMetadataAsync({ minVersion: "18.0.0", version: "21.2.8", ngImport: i0, type: WorkCenterEscalationInstanceType, resolveDeferredDeps: () => [import('@masterteam/discussion').then(m => m.DiscussionThread)], resolveMetadata: DiscussionThread => ({ decorators: [{
|
|
1629
|
+
type: Component,
|
|
1630
|
+
args: [{ selector: 'mt-work-center-escalation-instance-type', standalone: true, imports: [
|
|
1631
|
+
CommonModule,
|
|
1632
|
+
TranslocoDirective,
|
|
1633
|
+
Tabs,
|
|
1634
|
+
DiscussionThread,
|
|
1635
|
+
EntitiesPreview,
|
|
1636
|
+
ClientInstancePreview,
|
|
1637
|
+
StructureBuilder,
|
|
1638
|
+
], 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() !== 'escalationDetails'\"\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 (canRenderDetails()) {\n @if (loading()) {\n <div\n class=\"flex min-h-[22rem] items-center justify-center rounded-lg border border-dashed border-surface-300 bg-surface-50 p-6\"\n >\n <p class=\"max-w-md text-center text-sm text-surface-500\">\n {{ t(\"loading.escalationDetails\") }}\n </p>\n </div>\n } @else if (error(); as error) {\n <div\n class=\"flex min-h-[22rem] items-center justify-center rounded-lg border border-dashed border-red-300 bg-red-50 p-6\"\n >\n <p class=\"max-w-md text-center text-sm text-red-600\">\n {{ error }}\n </p>\n </div>\n } @else if (preview()) {\n <mt-entities-preview [entities]=\"detailEntities()\" />\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.escalationDetailsUnavailable\") }}\n </p>\n </div>\n }\n </div>\n </div>\n\n @defer (when activeTab() === \"moduleDetails\") {\n <div\n class=\"h-full min-h-0 lg:overflow-hidden\"\n [hidden]=\"activeTab() !== 'moduleDetails'\"\n >\n @if (moduleDetailsConfig(); as moduleDetailsConfig) {\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]=\"moduleDetailsConfig\" />\n </div>\n }\n </div>\n }\n\n @defer (when activeTab() === \"schema\") {\n <div\n class=\"flex-1 min-h-0 overflow-auto\"\n [hidden]=\"activeTab() !== 'schema'\"\n >\n @if (canRenderDetails()) {\n @if (loading()) {\n <div\n class=\"flex min-h-[22rem] items-center justify-center rounded-lg border border-dashed border-surface-300 bg-surface-50 p-6\"\n >\n <p class=\"max-w-md text-center text-sm text-surface-500\">\n {{ t(\"loading.escalationSchema\") }}\n </p>\n </div>\n } @else if (error(); as error) {\n <div\n class=\"flex min-h-[22rem] items-center justify-center rounded-lg border border-dashed border-red-300 bg-red-50 p-6\"\n >\n <p class=\"max-w-md text-center text-sm text-red-600\">\n {{ error }}\n </p>\n </div>\n } @else if (hasSchema()) {\n <!--\n Only mount the flow once the schema tab is actually active. The\n structure-builder fits the diagram to the host size on load\n (fLoaded -> fitToScreen) and never re-fits, so mounting it inside\n the [hidden] (display:none, zero-size) container produces a blank\n canvas that doesn't recover when the tab is shown.\n -->\n @if (activeTab() === \"schema\") {\n <!--\n Use a concrete height (not lg:h-full). The diagram has no\n intrinsic height, and the tab's ancestor chain is block (not\n flex) on lg, so `h-full` collapses to 0 and the canvas renders\n blank. Mirrors the process preview's `h-[70vh]`.\n -->\n <div class=\"h-[70vh] min-h-[22rem] overflow-hidden rounded-lg\">\n <mt-structure-builder\n class=\"h-full\"\n [layoutDirection]=\"'LR'\"\n [readonly]=\"true\"\n [structureMode]=\"'workflow'\"\n [nodeFields]=\"previewNodeFields\"\n [nodes]=\"schemaNodes()\"\n [connections]=\"schemaConnections()\"\n />\n </div>\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.escalationSchemaUnavailable\") }}\n </p>\n </div>\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.escalationSchemaContextUnavailable\") }}\n </p>\n </div>\n }\n </div>\n }\n\n @if (!readOnly()) {\n @defer (when activeTab() === \"discussion\") {\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]=\"'EscalationInstance'\"\n [recordId]=\"resolvedInstanceId()\"\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.escalationDiscussionUnavailable\") }}\n </p>\n </div>\n }\n </div>\n }\n }\n </div>\n </div>\n</div>\n" }]
|
|
1639
|
+
}], ctorParameters: () => [], propDecorators: { details: [{ type: i0.Input, args: [{ isSignal: true, alias: "details", required: true }] }], readOnly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readOnly", required: false }] }] } }) });
|
|
1641
1640
|
// The endpoint returns the escalation payload directly, but tolerate the
|
|
1642
1641
|
// shared `{ data: ... }` envelope used by the other escalation endpoints.
|
|
1643
1642
|
function extractPreviewData(response) {
|
|
@@ -1786,546 +1785,18 @@ class WorkCenterModuleType {
|
|
|
1786
1785
|
this.actionContext?.setFormSource(null);
|
|
1787
1786
|
}
|
|
1788
1787
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: WorkCenterModuleType, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1789
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: WorkCenterModuleType, isStandalone: true, selector: "mt-work-center-module-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\
|
|
1790
|
-
}
|
|
1791
|
-
i0.ɵɵ
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
/**
|
|
1803
|
-
* Approval-history row projection for the process preview.
|
|
1804
|
-
*
|
|
1805
|
-
* The runtime stores one `step` record per *target user*, so a group step
|
|
1806
|
-
* fans out into one record per member, and every workflow attempt (return →
|
|
1807
|
-
* resubmit) creates a fresh fan-out for the same step schema. The table shows
|
|
1808
|
-
* one row per attempt: sibling member records collapse, but separate attempts
|
|
1809
|
-
* — and the completed history they carry — are preserved.
|
|
1810
|
-
*/
|
|
1811
|
-
const PENDING$1 = 'pending';
|
|
1812
|
-
const TERMINATED = 'terminated';
|
|
1813
|
-
/** Statuses whose actor is not the assigned group/role, so the actor wins. */
|
|
1814
|
-
const ACTOR_OVERRIDES_TARGET = new Set([TERMINATED]);
|
|
1815
|
-
function buildApprovalRows(steps, options) {
|
|
1816
|
-
const { resolveDisplayName } = options;
|
|
1817
|
-
const currentIds = new Set((options.currentStepIds ?? []).map((id) => String(id)));
|
|
1818
|
-
const rows = [];
|
|
1819
|
-
for (const run of splitIntoRuns(steps)) {
|
|
1820
|
-
const groupName = resolveGroupName(run[0].target, resolveDisplayName);
|
|
1821
|
-
if (groupName) {
|
|
1822
|
-
rows.push(buildGroupRow(run, groupName, currentIds, resolveDisplayName));
|
|
1823
|
-
continue;
|
|
1824
|
-
}
|
|
1825
|
-
// Non-group targets keep one row per record, minus the pending siblings
|
|
1826
|
-
// that were superseded when another member of the same fan-out acted.
|
|
1827
|
-
const runHasAction = run.some(hasAction);
|
|
1828
|
-
for (const step of run) {
|
|
1829
|
-
if (runHasAction &&
|
|
1830
|
-
run.length > 1 &&
|
|
1831
|
-
isSupersededPending(step, currentIds)) {
|
|
1832
|
-
continue;
|
|
1833
|
-
}
|
|
1834
|
-
rows.push({
|
|
1835
|
-
stepName: resolveStepName(step, resolveDisplayName),
|
|
1836
|
-
status: step.status,
|
|
1837
|
-
user: {
|
|
1838
|
-
kind: 'user',
|
|
1839
|
-
value: step.actionUserInfo ?? step.targetUser ?? null,
|
|
1840
|
-
},
|
|
1841
|
-
createdAt: step.createdAt ?? '',
|
|
1842
|
-
actionDate: step.actionDate ?? '',
|
|
1843
|
-
});
|
|
1844
|
-
}
|
|
1845
|
-
}
|
|
1846
|
-
return rows;
|
|
1847
|
-
}
|
|
1848
|
-
function buildGroupRow(run, groupName, currentIds, resolveDisplayName) {
|
|
1849
|
-
const pendingOnGroup = run.find((step) => isCurrent(step, currentIds));
|
|
1850
|
-
// While the attempt is open the row belongs to the group as a whole; once it
|
|
1851
|
-
// closes, the record that carries the outcome represents it.
|
|
1852
|
-
const repr = pendingOnGroup ?? resolveOutcome(run);
|
|
1853
|
-
const actor = repr.actionUserInfo;
|
|
1854
|
-
const showActor = !pendingOnGroup && !!actor && ACTOR_OVERRIDES_TARGET.has(statusKey(repr));
|
|
1855
|
-
return {
|
|
1856
|
-
stepName: resolveStepName(repr, resolveDisplayName),
|
|
1857
|
-
status: repr.status,
|
|
1858
|
-
user: showActor
|
|
1859
|
-
? { kind: 'user', value: actor }
|
|
1860
|
-
: { kind: 'text', value: groupName },
|
|
1861
|
-
createdAt: run[0].createdAt ?? '',
|
|
1862
|
-
actionDate: pendingOnGroup ? '' : (repr.actionDate ?? ''),
|
|
1863
|
-
};
|
|
1864
|
-
}
|
|
1865
|
-
/**
|
|
1866
|
-
* Consecutive records sharing a step schema + target belong to the same
|
|
1867
|
-
* attempt. The API orders steps by creation, so a later attempt on the same
|
|
1868
|
-
* step is always separated by the records that sent the request back.
|
|
1869
|
-
*/
|
|
1870
|
-
function splitIntoRuns(steps) {
|
|
1871
|
-
const runs = [];
|
|
1872
|
-
let currentKey = null;
|
|
1873
|
-
for (const step of steps) {
|
|
1874
|
-
const key = runKey(step);
|
|
1875
|
-
if (key !== currentKey || runs.length === 0) {
|
|
1876
|
-
runs.push([step]);
|
|
1877
|
-
currentKey = key;
|
|
1878
|
-
continue;
|
|
1879
|
-
}
|
|
1880
|
-
runs[runs.length - 1].push(step);
|
|
1881
|
-
}
|
|
1882
|
-
return runs;
|
|
1883
|
-
}
|
|
1884
|
-
function runKey(step) {
|
|
1885
|
-
return `${step.stepSchemaId ?? ''}|${step.target?.groupKey ?? ''}`;
|
|
1886
|
-
}
|
|
1887
|
-
/** The record holding the attempt's outcome (Returned/Rejected/Terminated…). */
|
|
1888
|
-
function resolveOutcome(run) {
|
|
1889
|
-
for (let index = run.length - 1; index >= 0; index -= 1) {
|
|
1890
|
-
const step = run[index];
|
|
1891
|
-
if (statusKey(step) !== PENDING$1 && statusKey(step) !== '') {
|
|
1892
|
-
return step;
|
|
1893
|
-
}
|
|
1894
|
-
}
|
|
1895
|
-
return findLast(run, hasAction) ?? run[run.length - 1];
|
|
1896
|
-
}
|
|
1897
|
-
function isCurrent(step, currentIds) {
|
|
1898
|
-
if (typeof step.isCurrent === 'boolean') {
|
|
1899
|
-
return step.isCurrent;
|
|
1900
|
-
}
|
|
1901
|
-
if (currentIds.size > 0) {
|
|
1902
|
-
return currentIds.has(String(step.stepId ?? ''));
|
|
1903
|
-
}
|
|
1904
|
-
return step.isActive !== false && statusKey(step) === PENDING$1;
|
|
1905
|
-
}
|
|
1906
|
-
/** A pending record left behind after a sibling acted — never actionable. */
|
|
1907
|
-
function isSupersededPending(step, currentIds) {
|
|
1908
|
-
return (statusKey(step) === PENDING$1 &&
|
|
1909
|
-
!hasAction(step) &&
|
|
1910
|
-
!isCurrent(step, currentIds));
|
|
1911
|
-
}
|
|
1912
|
-
function hasAction(step) {
|
|
1913
|
-
if (step.actionUserInfo) {
|
|
1914
|
-
return true;
|
|
1915
|
-
}
|
|
1916
|
-
const key = statusKey(step);
|
|
1917
|
-
return key !== '' && key !== PENDING$1;
|
|
1918
|
-
}
|
|
1919
|
-
/** Status comes off the lookup key, never the localized display text. */
|
|
1920
|
-
function statusKey(step) {
|
|
1921
|
-
const status = step.status;
|
|
1922
|
-
if (typeof status === 'string') {
|
|
1923
|
-
return status.trim().toLowerCase();
|
|
1924
|
-
}
|
|
1925
|
-
if (status && typeof status === 'object') {
|
|
1926
|
-
const key = status['key'];
|
|
1927
|
-
if (typeof key === 'string') {
|
|
1928
|
-
return key.trim().toLowerCase();
|
|
1929
|
-
}
|
|
1930
|
-
}
|
|
1931
|
-
return '';
|
|
1932
|
-
}
|
|
1933
|
-
function resolveStepName(step, resolveDisplayName) {
|
|
1934
|
-
return resolveDisplayName(step.stepName) || String(step.stepId ?? '--');
|
|
1935
|
-
}
|
|
1936
|
-
function resolveGroupName(target, resolveDisplayName) {
|
|
1937
|
-
if (!target || target.type !== 'Group' || !target.group) {
|
|
1938
|
-
return '';
|
|
1939
|
-
}
|
|
1940
|
-
return (resolveDisplayName(target.displayName) ||
|
|
1941
|
-
resolveDisplayName(target.group.name));
|
|
1942
|
-
}
|
|
1943
|
-
function findLast(items, predicate) {
|
|
1944
|
-
for (let index = items.length - 1; index >= 0; index -= 1) {
|
|
1945
|
-
if (predicate(items[index])) {
|
|
1946
|
-
return items[index];
|
|
1947
|
-
}
|
|
1948
|
-
}
|
|
1949
|
-
return undefined;
|
|
1950
|
-
}
|
|
1951
|
-
|
|
1952
|
-
/**
|
|
1953
|
-
* Status projection for the process-preview diagram.
|
|
1954
|
-
*
|
|
1955
|
-
* The schema view colors every step by where it stands in the run — the icon
|
|
1956
|
-
* and subtitle already say what *kind* of step it is, so color is free to
|
|
1957
|
-
* carry progress. One color per state, reusing the workflow badge tones from
|
|
1958
|
-
* `@masterteam/structure-builder`.
|
|
1959
|
-
*
|
|
1960
|
-
* The runtime stores one `step` record per target user, so a group step fans
|
|
1961
|
-
* out into several records and every attempt (return → resubmit) creates a
|
|
1962
|
-
* fresh fan-out. A schema node shows the state of its *latest* attempt.
|
|
1963
|
-
*/
|
|
1964
|
-
/** Node accent color per state — every outcome gets its own hue. */
|
|
1965
|
-
const PROCESS_STEP_STATUS_COLORS = {
|
|
1966
|
-
approved: '#059669', // green — approved
|
|
1967
|
-
current: '#d97706', // amber — running right now
|
|
1968
|
-
noAction: '#0891b2', // cyan — closed without a decision from this target
|
|
1969
|
-
notStarted: '#2563eb', // blue — not reached yet
|
|
1970
|
-
rejected: '#dc2626', // red — rejected
|
|
1971
|
-
resubmitted: '#7c3aed', // violet — sent back, then pushed forward again
|
|
1972
|
-
returned: '#6b7280', // gray — sent back
|
|
1973
|
-
terminated: '#334155', // slate — the run was killed outright
|
|
1974
|
-
};
|
|
1975
|
-
/**
|
|
1976
|
-
* `NodeBadgeTone` per state, so the pill matches the node color. The tones the
|
|
1977
|
-
* structure-builder names cover the original five; the rest use `accent`, which
|
|
1978
|
-
* has no tone rule of its own and therefore inherits the node color exactly.
|
|
1979
|
-
*/
|
|
1980
|
-
const PROCESS_STEP_STATUS_BADGE_TONES = {
|
|
1981
|
-
approved: 'final',
|
|
1982
|
-
current: 'current',
|
|
1983
|
-
noAction: 'accent',
|
|
1984
|
-
notStarted: 'initial',
|
|
1985
|
-
rejected: 'closed',
|
|
1986
|
-
resubmitted: 'accent',
|
|
1987
|
-
returned: 'neutral',
|
|
1988
|
-
terminated: 'accent',
|
|
1989
|
-
};
|
|
1990
|
-
const PENDING = 'pending';
|
|
1991
|
-
const CURRENT_VIEW = { status: 'current', label: '' };
|
|
1992
|
-
const NOT_STARTED_VIEW = {
|
|
1993
|
-
status: 'notStarted',
|
|
1994
|
-
label: '',
|
|
1995
|
-
};
|
|
1996
|
-
/**
|
|
1997
|
-
* Decided status keys → visual state. A decided status that isn't listed reads
|
|
1998
|
-
* as `noAction` — it closed, but not by a decision we can name, so the color
|
|
1999
|
-
* must not claim one. The badge still shows the backend's own wording.
|
|
2000
|
-
*/
|
|
2001
|
-
const DECIDED_STATUS_STATES = {
|
|
2002
|
-
approved: 'approved',
|
|
2003
|
-
noaction: 'noAction',
|
|
2004
|
-
rejected: 'rejected',
|
|
2005
|
-
resubmitted: 'resubmitted',
|
|
2006
|
-
returned: 'returned',
|
|
2007
|
-
terminated: 'terminated',
|
|
2008
|
-
};
|
|
2009
|
-
const UNCLASSIFIED_DECIDED_STATE = 'noAction';
|
|
2010
|
-
/**
|
|
2011
|
-
* Inside one attempt the strongest outcome represents the step, so a group
|
|
2012
|
-
* fan-out where one member rejected reads as rejected while the siblings who
|
|
2013
|
-
* never acted read as `noAction`.
|
|
2014
|
-
*/
|
|
2015
|
-
const OUTCOME_PRECEDENCE = [
|
|
2016
|
-
'terminated',
|
|
2017
|
-
'rejected',
|
|
2018
|
-
'returned',
|
|
2019
|
-
'resubmitted',
|
|
2020
|
-
'approved',
|
|
2021
|
-
'noAction',
|
|
2022
|
-
];
|
|
2023
|
-
/**
|
|
2024
|
-
* Maps every schema-step id the runtime has touched to its visual state.
|
|
2025
|
-
* Steps missing from the result were never reached — render them as
|
|
2026
|
-
* {@link NOT_STARTED_VIEW}.
|
|
2027
|
-
*/
|
|
2028
|
-
function buildSchemaStepStatuses(steps, options) {
|
|
2029
|
-
const currentIds = new Set((options.currentSchemaIds ?? []).map((id) => String(id)));
|
|
2030
|
-
const views = new Map();
|
|
2031
|
-
// Later attempts overwrite earlier ones, so the last run on a step wins.
|
|
2032
|
-
for (const run of splitBySchemaStep(steps)) {
|
|
2033
|
-
const schemaId = String(run[0].stepSchemaId ?? '');
|
|
2034
|
-
if (!schemaId) {
|
|
2035
|
-
continue;
|
|
2036
|
-
}
|
|
2037
|
-
views.set(schemaId, resolveRunStatus(run, currentIds, options));
|
|
2038
|
-
}
|
|
2039
|
-
// A step can be current before any record closes — the flag always wins.
|
|
2040
|
-
for (const id of currentIds) {
|
|
2041
|
-
views.set(id, CURRENT_VIEW);
|
|
2042
|
-
}
|
|
2043
|
-
return views;
|
|
2044
|
-
}
|
|
2045
|
-
/**
|
|
2046
|
-
* Reads a state straight off a schema step's own `status`, for schemas that
|
|
2047
|
-
* carry it without a matching runtime record.
|
|
2048
|
-
*/
|
|
2049
|
-
function resolveStatusView(status, resolveDisplayName) {
|
|
2050
|
-
const key = normalizeStatusKey(status);
|
|
2051
|
-
if (key === '' || key === PENDING) {
|
|
2052
|
-
return NOT_STARTED_VIEW;
|
|
2053
|
-
}
|
|
2054
|
-
return {
|
|
2055
|
-
status: DECIDED_STATUS_STATES[key] ?? UNCLASSIFIED_DECIDED_STATE,
|
|
2056
|
-
label: resolveStatusLabel(status, resolveDisplayName),
|
|
2057
|
-
};
|
|
2058
|
-
}
|
|
2059
|
-
function resolveRunStatus(run, currentIds, options) {
|
|
2060
|
-
if (currentIds.has(String(run[0].stepSchemaId ?? '')) ||
|
|
2061
|
-
run.some((step) => step.isCurrent === true)) {
|
|
2062
|
-
return CURRENT_VIEW;
|
|
2063
|
-
}
|
|
2064
|
-
const decided = run
|
|
2065
|
-
.map((step) => ({ step, state: decidedState(step) }))
|
|
2066
|
-
.filter((entry) => entry.state !== null);
|
|
2067
|
-
if (decided.length === 0) {
|
|
2068
|
-
return NOT_STARTED_VIEW;
|
|
2069
|
-
}
|
|
2070
|
-
const outcome = OUTCOME_PRECEDENCE.map((state) => decided.find((entry) => entry.state === state)).find((entry) => !!entry) ?? decided[decided.length - 1];
|
|
2071
|
-
return {
|
|
2072
|
-
status: outcome.state,
|
|
2073
|
-
label: resolveStatusLabel(outcome.step.status, options.resolveDisplayName),
|
|
2074
|
-
};
|
|
2075
|
-
}
|
|
2076
|
-
function decidedState(step) {
|
|
2077
|
-
const key = statusKey(step);
|
|
2078
|
-
if (key === '' || key === PENDING) {
|
|
2079
|
-
return null;
|
|
2080
|
-
}
|
|
2081
|
-
return DECIDED_STATUS_STATES[key] ?? UNCLASSIFIED_DECIDED_STATE;
|
|
2082
|
-
}
|
|
2083
|
-
/**
|
|
2084
|
-
* Consecutive records sharing a schema step belong to the same attempt. The
|
|
2085
|
-
* API orders steps by creation, so a later attempt is always separated by the
|
|
2086
|
-
* records that sent the request back.
|
|
2087
|
-
*/
|
|
2088
|
-
function splitBySchemaStep(steps) {
|
|
2089
|
-
const runs = [];
|
|
2090
|
-
let currentKey = null;
|
|
2091
|
-
for (const step of steps) {
|
|
2092
|
-
const key = String(step.stepSchemaId ?? '');
|
|
2093
|
-
if (key !== currentKey || runs.length === 0) {
|
|
2094
|
-
runs.push([step]);
|
|
2095
|
-
currentKey = key;
|
|
2096
|
-
continue;
|
|
2097
|
-
}
|
|
2098
|
-
runs[runs.length - 1].push(step);
|
|
2099
|
-
}
|
|
2100
|
-
return runs;
|
|
2101
|
-
}
|
|
2102
|
-
/** Status comes off the lookup key, never the localized display text. */
|
|
2103
|
-
function normalizeStatusKey(status) {
|
|
2104
|
-
if (typeof status === 'string') {
|
|
2105
|
-
return status.trim().toLowerCase();
|
|
2106
|
-
}
|
|
2107
|
-
if (status && typeof status === 'object') {
|
|
2108
|
-
const key = status['key'];
|
|
2109
|
-
if (typeof key === 'string') {
|
|
2110
|
-
return key.trim().toLowerCase();
|
|
2111
|
-
}
|
|
2112
|
-
}
|
|
2113
|
-
return '';
|
|
2114
|
-
}
|
|
2115
|
-
function resolveStatusLabel(status, resolveDisplayName) {
|
|
2116
|
-
return typeof status === 'string' ? status : resolveDisplayName(status);
|
|
2117
|
-
}
|
|
2118
|
-
|
|
2119
|
-
class WorkCenterProcessPreview {
|
|
2120
|
-
http = inject(HttpClient);
|
|
2121
|
-
transloco = inject(TranslocoService);
|
|
2122
|
-
loadSub;
|
|
2123
|
-
requestId = input(null, ...(ngDevMode ? [{ debugName: "requestId" }] : /* istanbul ignore next */ []));
|
|
2124
|
-
view = input('approvals', ...(ngDevMode ? [{ debugName: "view" }] : /* istanbul ignore next */ []));
|
|
2125
|
-
approvalColumns = computed(() => [
|
|
2126
|
-
{
|
|
2127
|
-
key: 'stepName',
|
|
2128
|
-
label: this.transloco.translate('workCenter.preview.name'),
|
|
2129
|
-
},
|
|
2130
|
-
{
|
|
2131
|
-
key: 'status',
|
|
2132
|
-
label: this.transloco.translate('workCenter.preview.status'),
|
|
2133
|
-
type: 'entity',
|
|
2134
|
-
},
|
|
2135
|
-
{
|
|
2136
|
-
key: 'user',
|
|
2137
|
-
label: this.transloco.translate('workCenter.preview.userOrGroup'),
|
|
2138
|
-
type: 'entity',
|
|
2139
|
-
},
|
|
2140
|
-
{
|
|
2141
|
-
key: 'createdAt',
|
|
2142
|
-
label: this.transloco.translate('workCenter.preview.initiationDate'),
|
|
2143
|
-
type: 'entity',
|
|
2144
|
-
},
|
|
2145
|
-
{
|
|
2146
|
-
key: 'actionDate',
|
|
2147
|
-
label: this.transloco.translate('workCenter.preview.actionDate'),
|
|
2148
|
-
type: 'entity',
|
|
2149
|
-
},
|
|
2150
|
-
], ...(ngDevMode ? [{ debugName: "approvalColumns" }] : /* istanbul ignore next */ []));
|
|
2151
|
-
loading = signal(false, ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
|
|
2152
|
-
error = signal(null, ...(ngDevMode ? [{ debugName: "error" }] : /* istanbul ignore next */ []));
|
|
2153
|
-
preview = signal(null, ...(ngDevMode ? [{ debugName: "preview" }] : /* istanbul ignore next */ []));
|
|
2154
|
-
canRenderPreview = computed(() => (this.requestId() ?? 0) > 0, ...(ngDevMode ? [{ debugName: "canRenderPreview" }] : /* istanbul ignore next */ []));
|
|
2155
|
-
approvalRows = computed(() => {
|
|
2156
|
-
const preview = this.preview();
|
|
2157
|
-
const rows = buildApprovalRows(preview?.steps ?? [], {
|
|
2158
|
-
currentStepIds: preview?.currentStepIds,
|
|
2159
|
-
resolveDisplayName: (value) => this.resolveDisplayName(value),
|
|
2160
|
-
});
|
|
2161
|
-
return rows.map((row) => ({
|
|
2162
|
-
stepName: row.stepName,
|
|
2163
|
-
status: buildEntity('Status', 'Status', row.status),
|
|
2164
|
-
user: row.user.kind === 'text'
|
|
2165
|
-
? buildEntity('User', 'Text', row.user.value)
|
|
2166
|
-
: buildEntity('User', 'User', row.user.value),
|
|
2167
|
-
createdAt: buildEntity('Initiation Date', 'DateTime', row.createdAt),
|
|
2168
|
-
actionDate: buildEntity('Action Date', 'DateTime', row.actionDate),
|
|
2169
|
-
}));
|
|
2170
|
-
}, ...(ngDevMode ? [{ debugName: "approvalRows" }] : /* istanbul ignore next */ []));
|
|
2171
|
-
hasApprovals = computed(() => this.approvalRows().length > 0, ...(ngDevMode ? [{ debugName: "hasApprovals" }] : /* istanbul ignore next */ []));
|
|
2172
|
-
schemaNodes = computed(() => {
|
|
2173
|
-
const preview = this.preview();
|
|
2174
|
-
const steps = preview?.schema?.stepsSchema ?? [];
|
|
2175
|
-
const connections = preview?.schema?.connections ?? [];
|
|
2176
|
-
const sourceIds = new Set(connections.map((c) => String(c.source)));
|
|
2177
|
-
// Where each step stands in the run — the color of a node is its state,
|
|
2178
|
-
// not its type (the icon and subtitle already carry the type).
|
|
2179
|
-
const statusViews = buildSchemaStepStatuses(preview?.steps ?? [], {
|
|
2180
|
-
currentSchemaIds: preview?.currentStepSchemaIds,
|
|
2181
|
-
resolveDisplayName: (value) => this.resolveDisplayName(value),
|
|
2182
|
-
});
|
|
2183
|
-
return steps.map((step) => {
|
|
2184
|
-
const isAppAction = step.type === 'AppAction';
|
|
2185
|
-
const isApprovalCommit = step.type === 'ApprovalCommit' || step.systemKind === 'ApprovalCommit';
|
|
2186
|
-
// Treat a step as final if explicitly flagged or if it has no outgoing
|
|
2187
|
-
// connection — the latter catches schemas where `isFinal` isn't set.
|
|
2188
|
-
const isFinal = !isApprovalCommit &&
|
|
2189
|
-
(step.isFinal === true || !sourceIds.has(String(step.id)));
|
|
2190
|
-
const stepStatus = statusViews.get(String(step.id)) ??
|
|
2191
|
-
resolveStatusView(step.status, (value) => this.resolveDisplayName(value));
|
|
2192
|
-
const isCurrent = stepStatus.status === 'current';
|
|
2193
|
-
return {
|
|
2194
|
-
id: String(step.id),
|
|
2195
|
-
name: isFinal
|
|
2196
|
-
? this.transloco.translate('workCenter.preview.end')
|
|
2197
|
-
: this.resolveDisplayName(step.name) || String(step.id),
|
|
2198
|
-
color: PROCESS_STEP_STATUS_COLORS[stepStatus.status],
|
|
2199
|
-
icon: isApprovalCommit
|
|
2200
|
-
? 'general.check-verified-01'
|
|
2201
|
-
: isAppAction
|
|
2202
|
-
? 'general.zap'
|
|
2203
|
-
: isFinal
|
|
2204
|
-
? 'map.flag-04'
|
|
2205
|
-
: step.isInitial
|
|
2206
|
-
? 'map.flag-04'
|
|
2207
|
-
: 'file.clipboard-check',
|
|
2208
|
-
subtitle: isApprovalCommit
|
|
2209
|
-
? this.transloco.translate('workCenter.preview.approvalCommitSubtitle')
|
|
2210
|
-
: step.isInitial
|
|
2211
|
-
? this.transloco.translate('workCenter.preview.start')
|
|
2212
|
-
: isFinal
|
|
2213
|
-
? this.transloco.translate('workCenter.preview.end')
|
|
2214
|
-
: isAppAction
|
|
2215
|
-
? this.transloco.translate('workCenter.preview.appAction')
|
|
2216
|
-
: this.transloco.translate('workCenter.preview.formStep'),
|
|
2217
|
-
// The badge names the state the color stands for, preferring the
|
|
2218
|
-
// backend's own wording. The current step keeps the built-in
|
|
2219
|
-
// "Current" pill instead of a second badge.
|
|
2220
|
-
badge: isCurrent
|
|
2221
|
-
? null
|
|
2222
|
-
: stepStatus.label ||
|
|
2223
|
-
this.transloco.translate(`workCenter.preview.stepStatus.${stepStatus.status}`),
|
|
2224
|
-
// Semantic color for the badge — every step state renders with the
|
|
2225
|
-
// same pill and is told apart by color alone.
|
|
2226
|
-
badgeTone: PROCESS_STEP_STATUS_BADGE_TONES[stepStatus.status],
|
|
2227
|
-
status: step.status ?? null,
|
|
2228
|
-
style: isAppAction || isApprovalCommit ? 'icon' : 'detail',
|
|
2229
|
-
current: isCurrent,
|
|
2230
|
-
};
|
|
2231
|
-
});
|
|
2232
|
-
}, ...(ngDevMode ? [{ debugName: "schemaNodes" }] : /* istanbul ignore next */ []));
|
|
2233
|
-
schemaConnections = computed(() => (this.preview()?.schema?.connections ?? []).map((connection, index) => ({
|
|
2234
|
-
id: String(connection.id ?? `${connection.source}-${connection.target}-${index}`),
|
|
2235
|
-
from: String(connection.source),
|
|
2236
|
-
to: String(connection.target),
|
|
2237
|
-
state: connection.state,
|
|
2238
|
-
})), ...(ngDevMode ? [{ debugName: "schemaConnections" }] : /* istanbul ignore next */ []));
|
|
2239
|
-
hasSchema = computed(() => this.schemaNodes().length > 0, ...(ngDevMode ? [{ debugName: "hasSchema" }] : /* istanbul ignore next */ []));
|
|
2240
|
-
previewNodeFields = {
|
|
2241
|
-
id: 'id',
|
|
2242
|
-
name: 'name',
|
|
2243
|
-
icon: 'icon',
|
|
2244
|
-
color: 'color',
|
|
2245
|
-
subtitle: 'subtitle',
|
|
2246
|
-
badge: 'badge',
|
|
2247
|
-
badgeTone: 'badgeTone',
|
|
2248
|
-
status: 'status',
|
|
2249
|
-
style: 'style',
|
|
2250
|
-
current: 'current',
|
|
2251
|
-
};
|
|
2252
|
-
constructor() {
|
|
2253
|
-
effect(() => {
|
|
2254
|
-
const requestId = this.requestId() ?? 0;
|
|
2255
|
-
if (requestId > 0) {
|
|
2256
|
-
untracked(() => this.loadPreview(requestId));
|
|
2257
|
-
return;
|
|
2258
|
-
}
|
|
2259
|
-
this.loadSub?.unsubscribe();
|
|
2260
|
-
this.loading.set(false);
|
|
2261
|
-
this.error.set(null);
|
|
2262
|
-
this.preview.set(null);
|
|
2263
|
-
});
|
|
2264
|
-
}
|
|
2265
|
-
ngOnDestroy() {
|
|
2266
|
-
this.loadSub?.unsubscribe();
|
|
2267
|
-
}
|
|
2268
|
-
resolveDisplayName(name) {
|
|
2269
|
-
if (typeof name === 'string') {
|
|
2270
|
-
return name;
|
|
2271
|
-
}
|
|
2272
|
-
if (typeof name === 'number' || typeof name === 'boolean') {
|
|
2273
|
-
return String(name);
|
|
2274
|
-
}
|
|
2275
|
-
if (!name || typeof name !== 'object') {
|
|
2276
|
-
return '';
|
|
2277
|
-
}
|
|
2278
|
-
const map = name;
|
|
2279
|
-
const locale = this.transloco.getActiveLang();
|
|
2280
|
-
const candidates = [map['display'], map[locale], map['en'], map['ar']];
|
|
2281
|
-
for (const candidate of candidates) {
|
|
2282
|
-
if (typeof candidate === 'string' && candidate) {
|
|
2283
|
-
return candidate;
|
|
2284
|
-
}
|
|
2285
|
-
if (typeof candidate === 'number' || typeof candidate === 'boolean') {
|
|
2286
|
-
return String(candidate);
|
|
2287
|
-
}
|
|
2288
|
-
}
|
|
2289
|
-
const fallback = Object.values(map).find((value) => typeof value === 'string' && value);
|
|
2290
|
-
return typeof fallback === 'string' ? fallback : '';
|
|
2291
|
-
}
|
|
2292
|
-
loadPreview(requestId) {
|
|
2293
|
-
this.loadSub?.unsubscribe();
|
|
2294
|
-
this.loading.set(true);
|
|
2295
|
-
this.error.set(null);
|
|
2296
|
-
this.loadSub = this.http
|
|
2297
|
-
.get(`processes/${requestId}/preview`)
|
|
2298
|
-
.subscribe({
|
|
2299
|
-
next: (response) => {
|
|
2300
|
-
this.loading.set(false);
|
|
2301
|
-
this.preview.set(response.data ?? null);
|
|
2302
|
-
},
|
|
2303
|
-
error: (error) => {
|
|
2304
|
-
this.loading.set(false);
|
|
2305
|
-
this.preview.set(null);
|
|
2306
|
-
this.error.set(error?.error?.message ??
|
|
2307
|
-
error?.message ??
|
|
2308
|
-
this.transloco.translate('workCenter.preview.loadFailed'));
|
|
2309
|
-
},
|
|
2310
|
-
});
|
|
2311
|
-
}
|
|
2312
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: WorkCenterProcessPreview, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2313
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: WorkCenterProcessPreview, isStandalone: true, selector: "mt-work-center-process-preview", inputs: { requestId: { classPropertyName: "requestId", publicName: "requestId", isSignal: true, isRequired: false, transformFunction: null }, view: { classPropertyName: "view", publicName: "view", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@if (!canRenderPreview()) {\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 Process preview is not available for this item yet.\r\n </p>\r\n </div>\r\n} @else 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=\"text-sm text-surface-500\">Loading process preview...</p>\r\n </div>\r\n} @else if (error()) {\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\">{{ error() }}</p>\r\n </div>\r\n} @else {\r\n @if (view() === \"schema\") {\r\n @if (hasSchema()) {\r\n <div class=\"wc-process-preview-schema h-[70vh] overflow-hidden\">\r\n <mt-structure-builder\r\n class=\"h-full\"\r\n [layoutDirection]=\"'LR'\"\r\n [readonly]=\"true\"\r\n [structureMode]=\"'workflow'\"\r\n [nodeFields]=\"previewNodeFields\"\r\n [nodes]=\"schemaNodes()\"\r\n [connections]=\"schemaConnections()\"\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 Process steps are not available for this item yet.\r\n </p>\r\n </div>\r\n }\r\n } @else if (hasApprovals()) {\r\n <div class=\"max-h-[70vh] overflow-y-auto\">\r\n <mt-table\r\n noCard\r\n [data]=\"approvalRows()\"\r\n [columns]=\"approvalColumns()\"\r\n storageKey=\"work-center-process-preview-table\"\r\n [showFilters]=\"false\"\r\n [generalSearch]=\"false\"\r\n [clickableRows]=\"false\"\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 Process approvals data is not available for this item yet.\r\n </p>\r\n </div>\r\n }\r\n}\r\n", styles: [":host ::ng-deep .wc-process-preview-schema .wf-detail-row{align-items:center}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: Table, selector: "mt-table", inputs: ["filters", "data", "columns", "rowActions", "size", "showGridlines", "stripedRows", "selectableRows", "clickableRows", "generalSearch", "lazyLocalSearch", "showFilters", "filterMode", "loading", "updating", "lazy", "lazyLocalSort", "lazyTotalRecords", "reorderableColumns", "reorderableRows", "dataKey", "storageKey", "storageMode", "persistStateExclude", "exportable", "printable", "groupable", "groupCountMap", "cellClickFilter", "freezeActions", "virtualScroll", "virtualScrollItemSize", "scrollHeight", "printTitle", "exportFilename", "actionShape", "rowActionsLoadingFn", "tableLayout", "noCard", "tabs", "tabsOptionLabel", "tabsOptionValue", "activeTab", "actions", "captionStartTemplate", "captionEndTemplate", "emptyTitle", "emptyDescription", "emptyActionLabel", "emptyActionIcon", "paginatorPosition", "alwaysShowPaginator", "rowsPerPageOptions", "pageSize", "currentPage", "first", "filterTerm", "groupBy", "sortField", "sortDirection"], outputs: ["selectionChange", "cellChange", "lazyLoad", "columnReorder", "rowReorder", "rowClick", "emptyAction", "rowActionsRequested", "filtersChange", "activeTabChange", "onTabChange", "pageSizeChange", "currentPageChange", "firstChange", "filterTermChange", "groupByChange", "sortFieldChange", "sortDirectionChange"] }, { kind: "component", type: StructureBuilder, selector: "mt-structure-builder", inputs: ["availableNodes", "availableNodesLabel", "nodeForm", "nodeDialogFooterConfig", "connectionForm", "connectionFormulaSchemaId", "connectionFormulaConfig", "connectionGuard", "nodeActions", "nodeFields", "isAutoLayout", "readonly", "structureMode", "nodeStyle", "addModalType", "updateModalType", "addModalStyleClass", "updateModalStyleClass", "addModalHeader", "updateModalHeader", "appendTo", "availableTabsClass", "layoutDirection", "nodes", "connections", "nodeTemplate"], outputs: ["nodeActionsEvent", "action", "nodesChange", "connectionsChange"] }] });
|
|
2314
|
-
}
|
|
2315
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: WorkCenterProcessPreview, decorators: [{
|
|
2316
|
-
type: Component,
|
|
2317
|
-
args: [{ selector: 'mt-work-center-process-preview', standalone: true, imports: [CommonModule, Table, StructureBuilder], template: "@if (!canRenderPreview()) {\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 Process preview is not available for this item yet.\r\n </p>\r\n </div>\r\n} @else 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=\"text-sm text-surface-500\">Loading process preview...</p>\r\n </div>\r\n} @else if (error()) {\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\">{{ error() }}</p>\r\n </div>\r\n} @else {\r\n @if (view() === \"schema\") {\r\n @if (hasSchema()) {\r\n <div class=\"wc-process-preview-schema h-[70vh] overflow-hidden\">\r\n <mt-structure-builder\r\n class=\"h-full\"\r\n [layoutDirection]=\"'LR'\"\r\n [readonly]=\"true\"\r\n [structureMode]=\"'workflow'\"\r\n [nodeFields]=\"previewNodeFields\"\r\n [nodes]=\"schemaNodes()\"\r\n [connections]=\"schemaConnections()\"\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 Process steps are not available for this item yet.\r\n </p>\r\n </div>\r\n }\r\n } @else if (hasApprovals()) {\r\n <div class=\"max-h-[70vh] overflow-y-auto\">\r\n <mt-table\r\n noCard\r\n [data]=\"approvalRows()\"\r\n [columns]=\"approvalColumns()\"\r\n storageKey=\"work-center-process-preview-table\"\r\n [showFilters]=\"false\"\r\n [generalSearch]=\"false\"\r\n [clickableRows]=\"false\"\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 Process approvals data is not available for this item yet.\r\n </p>\r\n </div>\r\n }\r\n}\r\n", styles: [":host ::ng-deep .wc-process-preview-schema .wf-detail-row{align-items:center}\n"] }]
|
|
2318
|
-
}], ctorParameters: () => [], propDecorators: { requestId: [{ type: i0.Input, args: [{ isSignal: true, alias: "requestId", required: false }] }], view: [{ type: i0.Input, args: [{ isSignal: true, alias: "view", required: false }] }] } });
|
|
2319
|
-
function buildEntity(name, viewType, value) {
|
|
2320
|
-
return {
|
|
2321
|
-
name,
|
|
2322
|
-
viewType,
|
|
2323
|
-
value: value ?? '',
|
|
2324
|
-
configuration: {
|
|
2325
|
-
hideName: true,
|
|
2326
|
-
},
|
|
2327
|
-
};
|
|
2328
|
-
}
|
|
1788
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: WorkCenterModuleType, isStandalone: true, selector: "mt-work-center-module-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() !== 'details'\"\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]=\"formModuleId()\"\n [levelId]=\"formLevelId()\"\n [levelDataId]=\"formLevelDataId()\"\n [moduleDataId]=\"moduleDataId()\"\n [readonly]=\"true\"\n [renderMode]=\"'sections'\"\n [lookups]=\"formLookups()\"\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.moduleFormUnavailable\") }}\n </p>\n </div>\n }\n </div>\n </div>\n\n @if (!readOnly()) {\n @defer (when activeTab() === \"discussion\") {\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]=\"discussionModuleType()\"\n [recordId]=\"resolvedInstanceId()\"\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.moduleDiscussionUnavailable\") }}\n </p>\n </div>\n }\n </div>\n }\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"] }], deferBlockDependencies: [() => [import('@masterteam/discussion').then(m => m.DiscussionThread)]] });
|
|
1789
|
+
}
|
|
1790
|
+
i0.ɵɵngDeclareClassMetadataAsync({ minVersion: "18.0.0", version: "21.2.8", ngImport: i0, type: WorkCenterModuleType, resolveDeferredDeps: () => [import('@masterteam/discussion').then(m => m.DiscussionThread)], resolveMetadata: DiscussionThread => ({ decorators: [{
|
|
1791
|
+
type: Component,
|
|
1792
|
+
args: [{ selector: 'mt-work-center-module-type', standalone: true, imports: [
|
|
1793
|
+
CommonModule,
|
|
1794
|
+
TranslocoDirective,
|
|
1795
|
+
Tabs,
|
|
1796
|
+
ClientForm,
|
|
1797
|
+
DiscussionThread,
|
|
1798
|
+
], 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() !== 'details'\"\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]=\"formModuleId()\"\n [levelId]=\"formLevelId()\"\n [levelDataId]=\"formLevelDataId()\"\n [moduleDataId]=\"moduleDataId()\"\n [readonly]=\"true\"\n [renderMode]=\"'sections'\"\n [lookups]=\"formLookups()\"\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.moduleFormUnavailable\") }}\n </p>\n </div>\n }\n </div>\n </div>\n\n @if (!readOnly()) {\n @defer (when activeTab() === \"discussion\") {\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]=\"discussionModuleType()\"\n [recordId]=\"resolvedInstanceId()\"\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.moduleDiscussionUnavailable\") }}\n </p>\n </div>\n }\n </div>\n }\n }\n </div>\n </div>\n</div>\n" }]
|
|
1799
|
+
}], 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 }] }] } }) });
|
|
2329
1800
|
|
|
2330
1801
|
class WorkCenterProcessRequestType {
|
|
2331
1802
|
facade = inject(WorkCenterFacade);
|
|
@@ -2448,19 +1919,19 @@ class WorkCenterProcessRequestType {
|
|
|
2448
1919
|
this.formStore?.setErrorHost(false);
|
|
2449
1920
|
}
|
|
2450
1921
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: WorkCenterProcessRequestType, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
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\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
|
|
2452
|
-
}
|
|
2453
|
-
i0.ɵɵ
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
1922
|
+
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 @defer (when activeTab() === \"approvals\") {\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\n @defer (when activeTab() === \"schema\") {\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\n @if (!readOnly()) {\n @defer (when activeTab() === \"discussion\") {\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 }\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"] }], deferBlockDependencies: [() => [import('./masterteam-work-center-work-center-process-preview-qG9T9HoP.mjs').then(m => m.WorkCenterProcessPreview)], () => [import('./masterteam-work-center-work-center-process-preview-qG9T9HoP.mjs').then(m => m.WorkCenterProcessPreview)], () => [import('@masterteam/discussion').then(m => m.DiscussionThread)]] });
|
|
1923
|
+
}
|
|
1924
|
+
i0.ɵɵngDeclareClassMetadataAsync({ minVersion: "18.0.0", version: "21.2.8", ngImport: i0, type: WorkCenterProcessRequestType, resolveDeferredDeps: () => [import('./masterteam-work-center-work-center-process-preview-qG9T9HoP.mjs').then(m => m.WorkCenterProcessPreview), import('@masterteam/discussion').then(m => m.DiscussionThread)], resolveMetadata: (WorkCenterProcessPreview, DiscussionThread) => ({ decorators: [{
|
|
1925
|
+
type: Component,
|
|
1926
|
+
args: [{ selector: 'mt-work-center-process-request-type', standalone: true, imports: [
|
|
1927
|
+
CommonModule,
|
|
1928
|
+
TranslocoDirective,
|
|
1929
|
+
Tabs,
|
|
1930
|
+
ClientForm,
|
|
1931
|
+
DiscussionThread,
|
|
1932
|
+
WorkCenterProcessPreview,
|
|
1933
|
+
], 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 @defer (when activeTab() === \"approvals\") {\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\n @defer (when activeTab() === \"schema\") {\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\n @if (!readOnly()) {\n @defer (when activeTab() === \"discussion\") {\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 }\n </div>\n </div>\n</div>\n" }]
|
|
1934
|
+
}], 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 }] }] } }) });
|
|
2464
1935
|
|
|
2465
1936
|
class WorkCenterProcessStepType {
|
|
2466
1937
|
facade = inject(WorkCenterFacade);
|
|
@@ -2623,20 +2094,20 @@ class WorkCenterProcessStepType {
|
|
|
2623
2094
|
this.formStore?.setErrorHost(false);
|
|
2624
2095
|
}
|
|
2625
2096
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: WorkCenterProcessStepType, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
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\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
|
|
2627
|
-
}
|
|
2628
|
-
i0.ɵɵ
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2097
|
+
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 @defer (when activeTab() === \"approvals\") {\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\n @defer (when activeTab() === \"schema\") {\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\n @if (!readOnly()) {\n @defer (when activeTab() === \"discussion\") {\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 }\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"] }], deferBlockDependencies: [() => [import('./masterteam-work-center-work-center-process-preview-qG9T9HoP.mjs').then(m => m.WorkCenterProcessPreview)], () => [import('./masterteam-work-center-work-center-process-preview-qG9T9HoP.mjs').then(m => m.WorkCenterProcessPreview)], () => [import('@masterteam/discussion').then(m => m.DiscussionThread)]] });
|
|
2098
|
+
}
|
|
2099
|
+
i0.ɵɵngDeclareClassMetadataAsync({ minVersion: "18.0.0", version: "21.2.8", ngImport: i0, type: WorkCenterProcessStepType, resolveDeferredDeps: () => [import('./masterteam-work-center-work-center-process-preview-qG9T9HoP.mjs').then(m => m.WorkCenterProcessPreview), import('@masterteam/discussion').then(m => m.DiscussionThread)], resolveMetadata: (WorkCenterProcessPreview, DiscussionThread) => ({ decorators: [{
|
|
2100
|
+
type: Component,
|
|
2101
|
+
args: [{ selector: 'mt-work-center-process-step-type', standalone: true, imports: [
|
|
2102
|
+
CommonModule,
|
|
2103
|
+
TranslocoDirective,
|
|
2104
|
+
Tabs,
|
|
2105
|
+
ClientForm,
|
|
2106
|
+
ClientInstancePreview,
|
|
2107
|
+
DiscussionThread,
|
|
2108
|
+
WorkCenterProcessPreview,
|
|
2109
|
+
], 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 @defer (when activeTab() === \"approvals\") {\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\n @defer (when activeTab() === \"schema\") {\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\n @if (!readOnly()) {\n @defer (when activeTab() === \"discussion\") {\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 }\n </div>\n </div>\n</div>\n" }]
|
|
2110
|
+
}], 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 }] }] } }) });
|
|
2640
2111
|
|
|
2641
2112
|
/**
|
|
2642
2113
|
* work-center-schedule-publish.rows.ts - Pure grouping, ordering, counting and
|
|
@@ -2668,6 +2139,10 @@ const SCHEDULE_PUBLISH_GROUP_ORDER = [
|
|
|
2668
2139
|
'changed',
|
|
2669
2140
|
'removed',
|
|
2670
2141
|
];
|
|
2142
|
+
/** The server's per-row baseline verdict says the published side also moved. */
|
|
2143
|
+
function isSchedulePublishConflictRow(row) {
|
|
2144
|
+
return row.draftBaselineState === 'conflict';
|
|
2145
|
+
}
|
|
2671
2146
|
function summarizeSchedulePublish(diff) {
|
|
2672
2147
|
const rows = diff?.rows ?? [];
|
|
2673
2148
|
// Counted from the rows rather than trusted from the envelope's own counters:
|
|
@@ -2675,18 +2150,30 @@ function summarizeSchedulePublish(diff) {
|
|
|
2675
2150
|
const addedCount = countByState(rows, 'added');
|
|
2676
2151
|
const changedCount = countByState(rows, 'changed');
|
|
2677
2152
|
const removedCount = countByState(rows, 'removed');
|
|
2153
|
+
const totalCount = addedCount + changedCount + removedCount;
|
|
2154
|
+
const conflictCount = rows.filter(isSchedulePublishConflictRow).length;
|
|
2678
2155
|
return {
|
|
2679
2156
|
hasSnapshot: diff?.hasSnapshot === true,
|
|
2680
2157
|
addedCount,
|
|
2681
2158
|
changedCount,
|
|
2682
2159
|
removedCount,
|
|
2683
|
-
totalCount
|
|
2684
|
-
|
|
2160
|
+
totalCount,
|
|
2161
|
+
conflictCount,
|
|
2162
|
+
isEmpty: totalCount === 0,
|
|
2163
|
+
needsAttention: totalCount > 0 || conflictCount > 0,
|
|
2685
2164
|
};
|
|
2686
2165
|
}
|
|
2687
2166
|
/**
|
|
2688
2167
|
* Group the comparison for display, optionally narrowed by a free-text search.
|
|
2689
2168
|
*
|
|
2169
|
+
* CONFLICTS LEAD, as their own section holding every conflicted row (whatever
|
|
2170
|
+
* its publish state - including the zero-diff `unchanged` convergences the
|
|
2171
|
+
* backend ships solely for this section). The approver has no resolution
|
|
2172
|
+
* control - the section is informational: the frozen plan they are approving
|
|
2173
|
+
* was captured while the live plan had moved under it. The remaining rows
|
|
2174
|
+
* group added → changed → removed exactly as before, with non-conflict
|
|
2175
|
+
* `unchanged` rows still dropped.
|
|
2176
|
+
*
|
|
2690
2177
|
* Search matches a row's NAME and its changed FIELD KEYS, so "start" finds every
|
|
2691
2178
|
* row whose planned start moved even though that text appears nowhere in a name.
|
|
2692
2179
|
* Empty groups are dropped rather than rendered as empty headers.
|
|
@@ -2695,8 +2182,22 @@ function groupSchedulePublishRows(diff, searchText = '') {
|
|
|
2695
2182
|
const rows = diff?.rows ?? [];
|
|
2696
2183
|
const needle = searchText.trim().toLowerCase();
|
|
2697
2184
|
const groups = [];
|
|
2185
|
+
const conflicts = rows.filter(isSchedulePublishConflictRow);
|
|
2186
|
+
if (conflicts.length > 0) {
|
|
2187
|
+
const matching = needle
|
|
2188
|
+
? conflicts.filter((row) => matchesSchedulePublishSearch(row, needle))
|
|
2189
|
+
: conflicts;
|
|
2190
|
+
if (matching.length > 0) {
|
|
2191
|
+
groups.push({
|
|
2192
|
+
state: 'conflict',
|
|
2193
|
+
rows: sortSchedulePublishRows(matching),
|
|
2194
|
+
totalCount: conflicts.length,
|
|
2195
|
+
});
|
|
2196
|
+
}
|
|
2197
|
+
}
|
|
2198
|
+
const rest = rows.filter((row) => !isSchedulePublishConflictRow(row));
|
|
2698
2199
|
for (const state of SCHEDULE_PUBLISH_GROUP_ORDER) {
|
|
2699
|
-
const all =
|
|
2200
|
+
const all = rest.filter((row) => row.state === state);
|
|
2700
2201
|
if (all.length === 0) {
|
|
2701
2202
|
continue;
|
|
2702
2203
|
}
|
|
@@ -2714,6 +2215,16 @@ function groupSchedulePublishRows(diff, searchText = '') {
|
|
|
2714
2215
|
}
|
|
2715
2216
|
return groups;
|
|
2716
2217
|
}
|
|
2218
|
+
/**
|
|
2219
|
+
* The changed-field keys a conflict row ships as one comma-joined string -
|
|
2220
|
+
* the same wire shape the schedule read envelope uses.
|
|
2221
|
+
*/
|
|
2222
|
+
function parseSchedulePublishChangedFields(changedFields) {
|
|
2223
|
+
return String(changedFields ?? '')
|
|
2224
|
+
.split(',')
|
|
2225
|
+
.map((field) => field.trim())
|
|
2226
|
+
.filter((field) => field.length > 0);
|
|
2227
|
+
}
|
|
2717
2228
|
/**
|
|
2718
2229
|
* Plan order, falling back to the row id.
|
|
2719
2230
|
*
|
|
@@ -2904,6 +2415,14 @@ class WorkCenterSchedulePublishType {
|
|
|
2904
2415
|
isExpandable(row) {
|
|
2905
2416
|
return row.state === 'changed' && this.changesFor(row).length > 0;
|
|
2906
2417
|
}
|
|
2418
|
+
/**
|
|
2419
|
+
* The published-side field keys of a conflict row, as human labels. Rendered
|
|
2420
|
+
* as an informational hint — the approver has no per-row resolution control;
|
|
2421
|
+
* the frozen plan they decide on was captured as-is.
|
|
2422
|
+
*/
|
|
2423
|
+
publishedMovedFields(row) {
|
|
2424
|
+
return parseSchedulePublishChangedFields(row.publishedChangedFields).map((field) => this.fieldLabel(field));
|
|
2425
|
+
}
|
|
2907
2426
|
groupBadgeClass(state) {
|
|
2908
2427
|
switch (state) {
|
|
2909
2428
|
case 'added':
|
|
@@ -2912,6 +2431,8 @@ class WorkCenterSchedulePublishType {
|
|
|
2912
2431
|
return 'bg-red-100 text-red-700';
|
|
2913
2432
|
case 'changed':
|
|
2914
2433
|
return 'bg-amber-100 text-amber-700';
|
|
2434
|
+
case 'conflict':
|
|
2435
|
+
return 'bg-orange-100 text-orange-700';
|
|
2915
2436
|
default:
|
|
2916
2437
|
return 'bg-surface-100 text-surface-700';
|
|
2917
2438
|
}
|
|
@@ -2985,19 +2506,19 @@ class WorkCenterSchedulePublishType {
|
|
|
2985
2506
|
});
|
|
2986
2507
|
}
|
|
2987
2508
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: WorkCenterSchedulePublishType, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2988
|
-
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"] }] });
|
|
2989
|
-
}
|
|
2990
|
-
i0.ɵɵ
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
], 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" }]
|
|
3000
|
-
|
|
2509
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: WorkCenterSchedulePublishType, isStandalone: true, selector: "mt-work-center-schedule-publish-type", inputs: { details: { classPropertyName: "details", publicName: "details", isSignal: true, isRequired: true, transformFunction: null }, readOnly: { classPropertyName: "readOnly", publicName: "readOnly", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div\n class=\"mt-modal-content flex h-full min-h-0 flex-col gap-4 p-4 lg:overflow-hidden\"\n *transloco=\"let t; prefix: 'workCenter'\"\n>\n <div\n class=\"flex flex-1 min-h-0 flex-col gap-4 lg:grid lg:grid-cols-[13rem_minmax(0,1fr)] lg:gap-6\"\n >\n @if (tabOptions().length > 1) {\n <mt-tabs\n [(active)]=\"activeTab\"\n [options]=\"tabOptions()\"\n mode=\"vertical\"\n size=\"small\"\n />\n }\n\n <div class=\"min-w-0 flex-1 min-h-0 lg:overflow-hidden\">\n <!-- Comparison -->\n <div\n class=\"h-full min-h-0 lg:overflow-hidden\"\n [hidden]=\"activeTab() !== 'comparison'\"\n >\n <div\n class=\"flex h-full min-h-0 min-w-0 flex-col gap-3 lg:overflow-hidden\"\n >\n @if (loading()) {\n <div\n class=\"flex min-h-[22rem] items-center justify-center rounded-lg border border-dashed border-surface-300 bg-surface-50 p-6\"\n >\n <p class=\"max-w-md text-center text-sm text-surface-500\">\n {{ t(\"loading.schedulePublish\") }}\n </p>\n </div>\n } @else if (error(); as loadError) {\n <div\n class=\"flex min-h-[22rem] items-center justify-center rounded-lg border border-dashed border-red-300 bg-red-50 p-6\"\n >\n <p class=\"max-w-md text-center text-sm text-red-600\">\n {{ loadError }}\n </p>\n </div>\n } @else if (!summary().hasSnapshot) {\n <!-- Never published: nothing has been announced, so nothing can\n differ from it. That is a different statement from \"no changes\". -->\n <div\n class=\"flex min-h-[22rem] items-center justify-center rounded-lg border border-dashed border-surface-300 bg-surface-50 p-6\"\n >\n <p class=\"max-w-md text-center text-sm text-surface-500\">\n {{ t(\"publish.neverPublished\") }}\n </p>\n </div>\n } @else if (!summary().needsAttention) {\n <!-- Not `isEmpty`: a request whose only rows are zero-diff conflict\n convergences is publish-empty but must still show its conflicts\n section rather than claim there is nothing here. -->\n <div\n class=\"flex min-h-[22rem] items-center justify-center rounded-lg border border-dashed border-surface-300 bg-surface-50 p-6\"\n >\n <p class=\"max-w-md text-center text-sm text-surface-500\">\n {{ t(\"publish.nothingToPublish\") }}\n </p>\n </div>\n } @else {\n <!-- Counts stay visible while searching, so a filtered view never\n reads as the whole request. -->\n <div class=\"flex flex-wrap items-center gap-2\">\n @if (summary().conflictCount > 0) {\n <span\n class=\"rounded-full px-2 py-0.5 text-xs font-medium\"\n [class]=\"groupBadgeClass('conflict')\"\n >{{\n t(\"publish.conflictCount\", {\n count: summary().conflictCount,\n })\n }}</span\n >\n }\n <span\n class=\"rounded-full px-2 py-0.5 text-xs font-medium\"\n [class]=\"groupBadgeClass('added')\"\n >{{\n t(\"publish.addedCount\", { count: summary().addedCount })\n }}</span\n >\n <span\n class=\"rounded-full px-2 py-0.5 text-xs font-medium\"\n [class]=\"groupBadgeClass('changed')\"\n >{{\n t(\"publish.changedCount\", { count: summary().changedCount })\n }}</span\n >\n <span\n class=\"rounded-full px-2 py-0.5 text-xs font-medium\"\n [class]=\"groupBadgeClass('removed')\"\n >{{\n t(\"publish.removedCount\", { count: summary().removedCount })\n }}</span\n >\n </div>\n\n <input\n type=\"search\"\n class=\"w-full rounded-md border border-surface-300 bg-surface-0 px-3 py-2 text-sm outline-none focus:border-primary-400\"\n [placeholder]=\"t('publish.searchPlaceholder')\"\n [value]=\"searchText()\"\n (input)=\"onSearch($any($event.target).value)\"\n />\n\n <div\n class=\"min-h-0 flex-1 lg:overflow-y-auto lg:overscroll-contain lg:pr-2\"\n >\n @if (searchFoundNothing()) {\n <div\n class=\"flex min-h-[12rem] items-center justify-center rounded-lg border border-dashed border-surface-300 bg-surface-50 p-6\"\n >\n <p class=\"text-sm text-surface-500\">\n {{ t(\"publish.noSearchResults\") }}\n </p>\n </div>\n }\n\n @for (group of groups(); track group.state) {\n <section class=\"mb-4\">\n <header class=\"mb-2 flex items-center gap-2\">\n <h4 class=\"text-sm font-semibold text-surface-700\">\n {{ groupLabel(group.state) }}\n </h4>\n <span\n class=\"rounded-full px-2 py-0.5 text-xs font-medium\"\n [class]=\"groupBadgeClass(group.state)\"\n >{{ group.totalCount }}</span\n >\n </header>\n\n @if (group.state === \"conflict\") {\n <!-- Informational only: unlike the planner's drawer there is\n no keep-mine / use-published control here. The frozen\n plan under approval was captured while the live plan had\n moved on these rows. -->\n <p class=\"mb-2 text-xs text-surface-500\">\n {{ t(\"publish.conflictHint\") }}\n </p>\n }\n\n <ul class=\"flex flex-col gap-1\">\n @for (row of visibleRows(group); track rowKey(row)) {\n <li\n class=\"rounded-md border border-surface-200 bg-surface-0\"\n >\n <button\n type=\"button\"\n class=\"flex w-full items-center gap-2 px-3 py-2 text-start\"\n [class.cursor-default]=\"!isExpandable(row)\"\n [attr.aria-expanded]=\"\n isExpandable(row) ? isExpanded(row) : null\n \"\n [disabled]=\"!isExpandable(row)\"\n (click)=\"toggleRow(row)\"\n >\n @if (isExpandable(row)) {\n <span\n class=\"shrink-0 text-xs text-surface-500\"\n aria-hidden=\"true\"\n >{{ isExpanded(row) ? \"\u25BE\" : \"\u25B8\" }}</span\n >\n } @else {\n <span\n class=\"w-3 shrink-0\"\n aria-hidden=\"true\"\n ></span>\n }\n <span\n class=\"min-w-0 flex-1 truncate text-sm text-surface-800\"\n >{{ row.name }}</span\n >\n @if (row.type) {\n <span class=\"shrink-0 text-xs text-surface-500\">{{\n row.type\n }}</span>\n }\n </button>\n\n @if (\n group.state === \"conflict\" &&\n publishedMovedFields(row).length > 0\n ) {\n <p\n class=\"border-t border-surface-200 px-3 py-1.5 text-xs text-orange-700\"\n >\n {{ t(\"publish.publishedSideMoved\") }}:\n {{ publishedMovedFields(row).join(\", \") }}\n </p>\n }\n\n @if (isExpandable(row) && isExpanded(row)) {\n <div\n class=\"flex flex-col gap-3 border-t border-surface-200 px-3 py-3\"\n >\n @for (\n change of changesFor(row);\n track change.field\n ) {\n <div class=\"flex min-w-0 flex-col gap-1\">\n <span\n class=\"text-xs font-medium text-surface-600\"\n >{{ fieldLabel(change.field) }}</span\n >\n <!-- Before -> After rendered locally rather than\n via mt-comparison-value, whose Before/After\n labels are hardcoded English. Each side still\n goes through mt-entity-preview-body, so a date\n renders as a date and a user as an avatar. -->\n <div\n class=\"flex w-full min-w-0 items-stretch gap-3\"\n >\n <div\n class=\"flex min-w-0 flex-1 flex-col gap-1 rounded-md border border-surface-200 bg-surface-50 px-3 py-2\"\n >\n <span\n class=\"text-xs font-medium uppercase tracking-wide text-muted-color\"\n >{{ t(\"publish.before\") }}</span\n >\n <mt-entity-preview-body\n [data]=\"sideEntity(change, 'published')\"\n />\n </div>\n <div\n class=\"flex shrink-0 items-center text-muted-color\"\n aria-hidden=\"true\"\n >\n →\n </div>\n <div\n class=\"flex min-w-0 flex-1 flex-col gap-1 rounded-md border border-primary-200 bg-primary-50 px-3 py-2\"\n >\n <span\n class=\"text-xs font-medium uppercase tracking-wide text-primary-600\"\n >{{ t(\"publish.after\") }}</span\n >\n <mt-entity-preview-body\n [data]=\"sideEntity(change, 'current')\"\n />\n </div>\n </div>\n </div>\n }\n </div>\n }\n </li>\n }\n </ul>\n\n @if (hiddenCount(group) > 0) {\n <!-- Said out loud: a silently truncated comparison reads as a\n complete one to whoever is approving it. -->\n <p class=\"mt-2 text-xs text-surface-500\">\n {{\n t(\"publish.hiddenRows\", { count: hiddenCount(group) })\n }}\n </p>\n }\n </section>\n }\n </div>\n }\n </div>\n </div>\n\n <!-- Workflow -->\n @if (canRenderPreview()) {\n @defer (when activeTab() === \"workflow\") {\n <div\n class=\"h-full min-h-0 lg:overflow-hidden\"\n [hidden]=\"activeTab() !== 'workflow'\"\n >\n <div\n class=\"min-w-0 h-full min-h-0 lg:overflow-y-auto lg:overscroll-contain lg:pr-2\"\n >\n <mt-work-center-process-preview\n [requestId]=\"requestId()\"\n [view]=\"'schema'\"\n />\n </div>\n </div>\n }\n }\n\n <!-- Discussion -->\n @if (!readOnly() && canRenderDiscussion()) {\n @defer (when activeTab() === \"discussion\") {\n <div\n class=\"h-full min-h-0 lg:overflow-hidden\"\n [hidden]=\"activeTab() !== 'discussion'\"\n >\n <div\n class=\"h-[32rem] min-h-[22rem] overflow-hidden rounded-lg border border-surface-200 bg-surface-50 lg:h-full lg:min-h-0\"\n >\n <mt-discussion-thread\n [moduleType]=\"'ProcessRequest'\"\n [recordId]=\"resolvedRequestId()\"\n [mentionSearchEndpoint]=\"'Identity/users'\"\n [mentionSearchParam]=\"'query'\"\n [mentionSearchDataPath]=\"'data'\"\n [uploadEndpoint]=\"'uploader'\"\n [attachmentDownloadEndpoint]=\"'uploader'\"\n [showParticipants]=\"true\"\n [autoMarkRead]=\"true\"\n [refreshIntervalMs]=\"0\"\n [styleClass]=\"'h-full'\"\n />\n </div>\n </div>\n }\n }\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: EntityPreviewBody, selector: "mt-entity-preview-body", inputs: ["data", "attachmentShape"] }], deferBlockDependencies: [() => [import('./masterteam-work-center-work-center-process-preview-qG9T9HoP.mjs').then(m => m.WorkCenterProcessPreview)], () => [import('@masterteam/discussion').then(m => m.DiscussionThread)]] });
|
|
2510
|
+
}
|
|
2511
|
+
i0.ɵɵngDeclareClassMetadataAsync({ minVersion: "18.0.0", version: "21.2.8", ngImport: i0, type: WorkCenterSchedulePublishType, resolveDeferredDeps: () => [import('./masterteam-work-center-work-center-process-preview-qG9T9HoP.mjs').then(m => m.WorkCenterProcessPreview), import('@masterteam/discussion').then(m => m.DiscussionThread)], resolveMetadata: (WorkCenterProcessPreview, DiscussionThread) => ({ decorators: [{
|
|
2512
|
+
type: Component,
|
|
2513
|
+
args: [{ selector: 'mt-work-center-schedule-publish-type', standalone: true, imports: [
|
|
2514
|
+
CommonModule,
|
|
2515
|
+
TranslocoDirective,
|
|
2516
|
+
Tabs,
|
|
2517
|
+
DiscussionThread,
|
|
2518
|
+
EntityPreviewBody,
|
|
2519
|
+
WorkCenterProcessPreview,
|
|
2520
|
+
], template: "<div\n class=\"mt-modal-content flex h-full min-h-0 flex-col gap-4 p-4 lg:overflow-hidden\"\n *transloco=\"let t; prefix: 'workCenter'\"\n>\n <div\n class=\"flex flex-1 min-h-0 flex-col gap-4 lg:grid lg:grid-cols-[13rem_minmax(0,1fr)] lg:gap-6\"\n >\n @if (tabOptions().length > 1) {\n <mt-tabs\n [(active)]=\"activeTab\"\n [options]=\"tabOptions()\"\n mode=\"vertical\"\n size=\"small\"\n />\n }\n\n <div class=\"min-w-0 flex-1 min-h-0 lg:overflow-hidden\">\n <!-- Comparison -->\n <div\n class=\"h-full min-h-0 lg:overflow-hidden\"\n [hidden]=\"activeTab() !== 'comparison'\"\n >\n <div\n class=\"flex h-full min-h-0 min-w-0 flex-col gap-3 lg:overflow-hidden\"\n >\n @if (loading()) {\n <div\n class=\"flex min-h-[22rem] items-center justify-center rounded-lg border border-dashed border-surface-300 bg-surface-50 p-6\"\n >\n <p class=\"max-w-md text-center text-sm text-surface-500\">\n {{ t(\"loading.schedulePublish\") }}\n </p>\n </div>\n } @else if (error(); as loadError) {\n <div\n class=\"flex min-h-[22rem] items-center justify-center rounded-lg border border-dashed border-red-300 bg-red-50 p-6\"\n >\n <p class=\"max-w-md text-center text-sm text-red-600\">\n {{ loadError }}\n </p>\n </div>\n } @else if (!summary().hasSnapshot) {\n <!-- Never published: nothing has been announced, so nothing can\n differ from it. That is a different statement from \"no changes\". -->\n <div\n class=\"flex min-h-[22rem] items-center justify-center rounded-lg border border-dashed border-surface-300 bg-surface-50 p-6\"\n >\n <p class=\"max-w-md text-center text-sm text-surface-500\">\n {{ t(\"publish.neverPublished\") }}\n </p>\n </div>\n } @else if (!summary().needsAttention) {\n <!-- Not `isEmpty`: a request whose only rows are zero-diff conflict\n convergences is publish-empty but must still show its conflicts\n section rather than claim there is nothing here. -->\n <div\n class=\"flex min-h-[22rem] items-center justify-center rounded-lg border border-dashed border-surface-300 bg-surface-50 p-6\"\n >\n <p class=\"max-w-md text-center text-sm text-surface-500\">\n {{ t(\"publish.nothingToPublish\") }}\n </p>\n </div>\n } @else {\n <!-- Counts stay visible while searching, so a filtered view never\n reads as the whole request. -->\n <div class=\"flex flex-wrap items-center gap-2\">\n @if (summary().conflictCount > 0) {\n <span\n class=\"rounded-full px-2 py-0.5 text-xs font-medium\"\n [class]=\"groupBadgeClass('conflict')\"\n >{{\n t(\"publish.conflictCount\", {\n count: summary().conflictCount,\n })\n }}</span\n >\n }\n <span\n class=\"rounded-full px-2 py-0.5 text-xs font-medium\"\n [class]=\"groupBadgeClass('added')\"\n >{{\n t(\"publish.addedCount\", { count: summary().addedCount })\n }}</span\n >\n <span\n class=\"rounded-full px-2 py-0.5 text-xs font-medium\"\n [class]=\"groupBadgeClass('changed')\"\n >{{\n t(\"publish.changedCount\", { count: summary().changedCount })\n }}</span\n >\n <span\n class=\"rounded-full px-2 py-0.5 text-xs font-medium\"\n [class]=\"groupBadgeClass('removed')\"\n >{{\n t(\"publish.removedCount\", { count: summary().removedCount })\n }}</span\n >\n </div>\n\n <input\n type=\"search\"\n class=\"w-full rounded-md border border-surface-300 bg-surface-0 px-3 py-2 text-sm outline-none focus:border-primary-400\"\n [placeholder]=\"t('publish.searchPlaceholder')\"\n [value]=\"searchText()\"\n (input)=\"onSearch($any($event.target).value)\"\n />\n\n <div\n class=\"min-h-0 flex-1 lg:overflow-y-auto lg:overscroll-contain lg:pr-2\"\n >\n @if (searchFoundNothing()) {\n <div\n class=\"flex min-h-[12rem] items-center justify-center rounded-lg border border-dashed border-surface-300 bg-surface-50 p-6\"\n >\n <p class=\"text-sm text-surface-500\">\n {{ t(\"publish.noSearchResults\") }}\n </p>\n </div>\n }\n\n @for (group of groups(); track group.state) {\n <section class=\"mb-4\">\n <header class=\"mb-2 flex items-center gap-2\">\n <h4 class=\"text-sm font-semibold text-surface-700\">\n {{ groupLabel(group.state) }}\n </h4>\n <span\n class=\"rounded-full px-2 py-0.5 text-xs font-medium\"\n [class]=\"groupBadgeClass(group.state)\"\n >{{ group.totalCount }}</span\n >\n </header>\n\n @if (group.state === \"conflict\") {\n <!-- Informational only: unlike the planner's drawer there is\n no keep-mine / use-published control here. The frozen\n plan under approval was captured while the live plan had\n moved on these rows. -->\n <p class=\"mb-2 text-xs text-surface-500\">\n {{ t(\"publish.conflictHint\") }}\n </p>\n }\n\n <ul class=\"flex flex-col gap-1\">\n @for (row of visibleRows(group); track rowKey(row)) {\n <li\n class=\"rounded-md border border-surface-200 bg-surface-0\"\n >\n <button\n type=\"button\"\n class=\"flex w-full items-center gap-2 px-3 py-2 text-start\"\n [class.cursor-default]=\"!isExpandable(row)\"\n [attr.aria-expanded]=\"\n isExpandable(row) ? isExpanded(row) : null\n \"\n [disabled]=\"!isExpandable(row)\"\n (click)=\"toggleRow(row)\"\n >\n @if (isExpandable(row)) {\n <span\n class=\"shrink-0 text-xs text-surface-500\"\n aria-hidden=\"true\"\n >{{ isExpanded(row) ? \"\u25BE\" : \"\u25B8\" }}</span\n >\n } @else {\n <span\n class=\"w-3 shrink-0\"\n aria-hidden=\"true\"\n ></span>\n }\n <span\n class=\"min-w-0 flex-1 truncate text-sm text-surface-800\"\n >{{ row.name }}</span\n >\n @if (row.type) {\n <span class=\"shrink-0 text-xs text-surface-500\">{{\n row.type\n }}</span>\n }\n </button>\n\n @if (\n group.state === \"conflict\" &&\n publishedMovedFields(row).length > 0\n ) {\n <p\n class=\"border-t border-surface-200 px-3 py-1.5 text-xs text-orange-700\"\n >\n {{ t(\"publish.publishedSideMoved\") }}:\n {{ publishedMovedFields(row).join(\", \") }}\n </p>\n }\n\n @if (isExpandable(row) && isExpanded(row)) {\n <div\n class=\"flex flex-col gap-3 border-t border-surface-200 px-3 py-3\"\n >\n @for (\n change of changesFor(row);\n track change.field\n ) {\n <div class=\"flex min-w-0 flex-col gap-1\">\n <span\n class=\"text-xs font-medium text-surface-600\"\n >{{ fieldLabel(change.field) }}</span\n >\n <!-- Before -> After rendered locally rather than\n via mt-comparison-value, whose Before/After\n labels are hardcoded English. Each side still\n goes through mt-entity-preview-body, so a date\n renders as a date and a user as an avatar. -->\n <div\n class=\"flex w-full min-w-0 items-stretch gap-3\"\n >\n <div\n class=\"flex min-w-0 flex-1 flex-col gap-1 rounded-md border border-surface-200 bg-surface-50 px-3 py-2\"\n >\n <span\n class=\"text-xs font-medium uppercase tracking-wide text-muted-color\"\n >{{ t(\"publish.before\") }}</span\n >\n <mt-entity-preview-body\n [data]=\"sideEntity(change, 'published')\"\n />\n </div>\n <div\n class=\"flex shrink-0 items-center text-muted-color\"\n aria-hidden=\"true\"\n >\n →\n </div>\n <div\n class=\"flex min-w-0 flex-1 flex-col gap-1 rounded-md border border-primary-200 bg-primary-50 px-3 py-2\"\n >\n <span\n class=\"text-xs font-medium uppercase tracking-wide text-primary-600\"\n >{{ t(\"publish.after\") }}</span\n >\n <mt-entity-preview-body\n [data]=\"sideEntity(change, 'current')\"\n />\n </div>\n </div>\n </div>\n }\n </div>\n }\n </li>\n }\n </ul>\n\n @if (hiddenCount(group) > 0) {\n <!-- Said out loud: a silently truncated comparison reads as a\n complete one to whoever is approving it. -->\n <p class=\"mt-2 text-xs text-surface-500\">\n {{\n t(\"publish.hiddenRows\", { count: hiddenCount(group) })\n }}\n </p>\n }\n </section>\n }\n </div>\n }\n </div>\n </div>\n\n <!-- Workflow -->\n @if (canRenderPreview()) {\n @defer (when activeTab() === \"workflow\") {\n <div\n class=\"h-full min-h-0 lg:overflow-hidden\"\n [hidden]=\"activeTab() !== 'workflow'\"\n >\n <div\n class=\"min-w-0 h-full min-h-0 lg:overflow-y-auto lg:overscroll-contain lg:pr-2\"\n >\n <mt-work-center-process-preview\n [requestId]=\"requestId()\"\n [view]=\"'schema'\"\n />\n </div>\n </div>\n }\n }\n\n <!-- Discussion -->\n @if (!readOnly() && canRenderDiscussion()) {\n @defer (when activeTab() === \"discussion\") {\n <div\n class=\"h-full min-h-0 lg:overflow-hidden\"\n [hidden]=\"activeTab() !== 'discussion'\"\n >\n <div\n class=\"h-[32rem] min-h-[22rem] overflow-hidden rounded-lg border border-surface-200 bg-surface-50 lg:h-full lg:min-h-0\"\n >\n <mt-discussion-thread\n [moduleType]=\"'ProcessRequest'\"\n [recordId]=\"resolvedRequestId()\"\n [mentionSearchEndpoint]=\"'Identity/users'\"\n [mentionSearchParam]=\"'query'\"\n [mentionSearchDataPath]=\"'data'\"\n [uploadEndpoint]=\"'uploader'\"\n [attachmentDownloadEndpoint]=\"'uploader'\"\n [showParticipants]=\"true\"\n [autoMarkRead]=\"true\"\n [refreshIntervalMs]=\"0\"\n [styleClass]=\"'h-full'\"\n />\n </div>\n </div>\n }\n }\n </div>\n </div>\n</div>\n" }]
|
|
2521
|
+
}], ctorParameters: () => [], propDecorators: { details: [{ type: i0.Input, args: [{ isSignal: true, alias: "details", required: true }] }], readOnly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readOnly", required: false }] }] } }) });
|
|
3001
2522
|
/**
|
|
3002
2523
|
* The comparison arrives on the response root, but several runtime endpoints
|
|
3003
2524
|
* wrap their payload in `{ data: ... }` - tolerate both rather than rendering an
|
|
@@ -4725,5 +4246,5 @@ const APP_STATES = [WorkCenterState];
|
|
|
4725
4246
|
* Generated bundle index. Do not edit.
|
|
4726
4247
|
*/
|
|
4727
4248
|
|
|
4728
|
-
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 };
|
|
4249
|
+
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 };
|
|
4729
4250
|
//# sourceMappingURL=masterteam-work-center.mjs.map
|