@masterteam/work-center 0.0.2 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,8 @@
1
1
  import { CommonModule } from '@angular/common';
2
2
  import * as i0 from '@angular/core';
3
- import { inject, Injectable, computed, input, output, Component } from '@angular/core';
3
+ import { inject, Injectable, computed, input, output, Component, signal, effect, untracked, viewChild } from '@angular/core';
4
4
  import { ClientPage } from '@masterteam/components/client-page';
5
+ import { ModalService } from '@masterteam/components/modal';
5
6
  import { PropertyFilterBuilder } from '@masterteam/components/property-filter-builder';
6
7
  import { StatisticCard } from '@masterteam/components/statistic-card';
7
8
  import { Table } from '@masterteam/components/table';
@@ -9,8 +10,21 @@ import * as i1 from 'primeng/skeleton';
9
10
  import { SkeletonModule } from 'primeng/skeleton';
10
11
  import { Action, Selector, State, Store, select } from '@ngxs/store';
11
12
  import { HttpClient } from '@angular/common/http';
12
- import { handleApiRequest } from '@masterteam/components';
13
- import { catchError, EMPTY, map } from 'rxjs';
13
+ import { handleApiRequest, ValidatorConfig } from '@masterteam/components';
14
+ import { catchError, EMPTY, map, take } from 'rxjs';
15
+ import { EntitiesPreview } from '@masterteam/components/entities';
16
+ import { Tabs } from '@masterteam/components/tabs';
17
+ import { DiscussionThread } from '@masterteam/discussion';
18
+ import { StructureBuilder } from '@masterteam/structure-builder';
19
+ import { ClientForm } from '@masterteam/forms/client-form';
20
+ import { Router, ActivatedRoute } from '@angular/router';
21
+ import { Drawer } from '@masterteam/components/drawer';
22
+ import { Button } from '@masterteam/components/button';
23
+ import { ToastService } from '@masterteam/components/toast';
24
+ import * as i1$1 from '@angular/forms';
25
+ import { FormControl, ReactiveFormsModule } from '@angular/forms';
26
+ import { ModalRef } from '@masterteam/components/dialog';
27
+ import { DynamicForm } from '@masterteam/forms/dynamic-form';
14
28
 
15
29
  class EnterArea {
16
30
  area;
@@ -150,26 +164,42 @@ function toLabel$1(value) {
150
164
  .trim()
151
165
  .replace(/\b\w/g, (char) => char.toUpperCase());
152
166
  }
153
- function toColumnType(key) {
154
- return key.toLowerCase().includes('date') ? 'date' : 'text';
167
+ function toViewType(viewType, key) {
168
+ if (viewType?.trim()) {
169
+ return viewType;
170
+ }
171
+ return key.toLowerCase().includes('date') ? 'Date' : 'Text';
172
+ }
173
+ function getValue(record, key) {
174
+ return key.split('.').reduce((current, part) => {
175
+ if (!current || typeof current !== 'object') {
176
+ return undefined;
177
+ }
178
+ return current[part];
179
+ }, record);
155
180
  }
156
- function mapViewTypeToColumnType(viewType, key) {
157
- const normalized = viewType?.toLowerCase();
158
- if (normalized === 'date' || key.toLowerCase().includes('date')) {
159
- return 'date';
181
+ function toDisplayValue(value, column) {
182
+ if (column.viewType !== 'Text') {
183
+ return value;
160
184
  }
161
- return 'text';
185
+ if (!value || typeof value !== 'object') {
186
+ return value;
187
+ }
188
+ const display = value['display'];
189
+ return typeof display === 'string' ? display : value;
162
190
  }
163
191
  function buildColumnsFromProperties(properties) {
164
192
  return properties
165
193
  .filter((property) => typeof property?.key === 'string' && property.key.length > 0)
166
194
  .map((property) => ({
195
+ ...property,
167
196
  key: property.key,
168
197
  label: property.name?.display ||
169
198
  property.name?.en ||
170
199
  property.name?.ar ||
171
200
  toLabel$1(property.key),
172
- type: mapViewTypeToColumnType(property.viewType, property.key),
201
+ type: 'entity',
202
+ viewType: toViewType(property.viewType, property.key),
173
203
  }));
174
204
  }
175
205
  function buildColumns(columnsConfig) {
@@ -181,9 +211,23 @@ function buildColumns(columnsConfig) {
181
211
  return keys.map((key) => ({
182
212
  key,
183
213
  label: toLabel$1(key),
184
- type: toColumnType(key),
214
+ type: 'entity',
215
+ viewType: toViewType(undefined, key),
185
216
  }));
186
217
  }
218
+ function toDisplayRows(rows, columns) {
219
+ return rows.map((row) => {
220
+ const displayRow = { ...row };
221
+ for (const column of columns) {
222
+ const value = getValue(row, column.key);
223
+ displayRow[column.key] = {
224
+ ...column,
225
+ value: toDisplayValue(value, column),
226
+ };
227
+ }
228
+ return displayRow;
229
+ });
230
+ }
187
231
  function buildMenuItems(cards) {
188
232
  const mapped = cards.map((card, index) => ({
189
233
  key: card.key,
@@ -270,10 +314,14 @@ function buildContextFromInputs(currentContext, area, inputs = {}) {
270
314
  templateId: parsedTemplateId ?? currentContext.templateId,
271
315
  sort: hasParamValue(inputs.sort)
272
316
  ? parseSortInput(inputs.sort)
273
- : currentContext.sort,
317
+ : inputs.sort === null
318
+ ? []
319
+ : currentContext.sort,
274
320
  runtimeFilters: hasParamValue(inputs.filters)
275
321
  ? parseFiltersInput(inputs.filters)
276
- : currentContext.runtimeFilters,
322
+ : inputs.filters === null
323
+ ? []
324
+ : currentContext.runtimeFilters,
277
325
  });
278
326
  }
279
327
  function isSameContext(left, right) {
@@ -288,7 +336,6 @@ function isSameContext(left, right) {
288
336
  }
289
337
  function mapRuntimeResponse(current, response) {
290
338
  const selectedCard = response.selectedCard;
291
- const rows = selectedCard?.items ?? [];
292
339
  const menuItems = buildMenuItems(response.cards);
293
340
  const requestedCardKey = selectedCard?.key ?? current.context.selectedCardKey ?? null;
294
341
  const selectedCardKey = requestedCardKey &&
@@ -300,6 +347,7 @@ function mapRuntimeResponse(current, response) {
300
347
  : null;
301
348
  const selectedMenuItem = menuItems.find((item) => item.key === selectedCardKey);
302
349
  const columns = buildColumns(selectedCard?.columnsConfig ?? selectedCardFromList?.columnsConfig);
350
+ const rows = toDisplayRows(selectedCard?.items ?? [], columns);
303
351
  const kpis = buildKpis(selectedCard?.stats ?? [], selectedMenuItem?.icon ?? null);
304
352
  return {
305
353
  ...current,
@@ -716,10 +764,14 @@ function mapAllowedOperators(schema) {
716
764
  }
717
765
  class WorkCenterPage {
718
766
  facade = inject(WorkCenterFacade);
767
+ modal = inject(ModalService);
719
768
  area = input('MyInbox', ...(ngDevMode ? [{ debugName: "area" }] : []));
720
769
  pageTitle = input('Work Center', ...(ngDevMode ? [{ debugName: "pageTitle" }] : []));
721
770
  menuIcon = input('communication.inbox-01', ...(ngDevMode ? [{ debugName: "menuIcon" }] : []));
771
+ openItemsInModal = input(true, ...(ngDevMode ? [{ debugName: "openItemsInModal" }] : []));
772
+ itemModal = input(null, ...(ngDevMode ? [{ debugName: "itemModal" }] : []));
722
773
  runtimeFiltersChanged = output();
774
+ itemClicked = output();
723
775
  context = this.facade.context;
724
776
  menuItems = this.facade.menuItems;
725
777
  rows = this.facade.rows;
@@ -759,27 +811,72 @@ class WorkCenterPage {
759
811
  allowedOperators: fallbackOperators,
760
812
  };
761
813
  }, ...(ngDevMode ? [{ debugName: "propertyFilterSchema" }] : []));
814
+ defaultModalStyleClass = '!w-[96vw] xl:!w-[calc(100%-8rem)] 2xl:!w-[calc(100%-12rem)] !absolute !shadow-none';
762
815
  onMenuItemClick(item) {
816
+ if (item.key === this.context().selectedCardKey) {
817
+ return;
818
+ }
763
819
  this.facade.selectCardAndLoad(this.area(), item.key);
764
820
  }
765
821
  onLazyLoad(event) {
766
822
  this.facade.applyTableLazyLoadAndLoad(this.area(), event);
767
823
  }
768
824
  onRuntimeFiltersApplied(filters) {
769
- this.runtimeFiltersChanged.emit(filters.map((filter) => ({
825
+ const mappedFilters = filters.map((filter) => ({
770
826
  field: filter.field,
771
827
  op: filter.op,
772
828
  value: filter.value,
773
829
  values: filter.values,
774
830
  from: filter.from,
775
831
  to: filter.to,
776
- })));
832
+ }));
833
+ this.facade.applyRuntimeFiltersAndLoad(this.area(), mappedFilters);
834
+ this.runtimeFiltersChanged.emit(mappedFilters);
777
835
  }
778
836
  onRuntimeFiltersCleared() {
837
+ this.facade.applyRuntimeFiltersAndLoad(this.area(), []);
779
838
  this.runtimeFiltersChanged.emit([]);
780
839
  }
840
+ onRowClick(row) {
841
+ const contextKey = this.resolveRowContextKey(row);
842
+ console.log('[work-center-debug] page row click', {
843
+ contextKey,
844
+ row,
845
+ openItemsInModal: this.openItemsInModal(),
846
+ hasModalComponent: !!this.itemModal()?.component,
847
+ });
848
+ if (!contextKey) {
849
+ return;
850
+ }
851
+ this.itemClicked.emit(contextKey);
852
+ if (!this.openItemsInModal()) {
853
+ return;
854
+ }
855
+ const modalOptions = this.itemModal();
856
+ if (!modalOptions?.component) {
857
+ return;
858
+ }
859
+ this.modal.openModal(modalOptions.component, 'drawer', {
860
+ header: modalOptions?.header ?? 'Item Details',
861
+ styleClass: modalOptions?.styleClass ?? this.defaultModalStyleClass,
862
+ position: modalOptions?.position ?? 'end',
863
+ dismissible: true,
864
+ modal: true,
865
+ ...(modalOptions?.appendTo ? { appendTo: modalOptions.appendTo } : {}),
866
+ inputValues: { contextKey },
867
+ });
868
+ }
869
+ rowsClickable = computed(() => this.openItemsInModal(), ...(ngDevMode ? [{ debugName: "rowsClickable" }] : []));
870
+ resolveRowContextKey(row) {
871
+ const value = row['contextKey'];
872
+ if (typeof value !== 'string') {
873
+ return null;
874
+ }
875
+ const normalized = value.trim();
876
+ return normalized.length ? normalized : null;
877
+ }
781
878
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: WorkCenterPage, deps: [], target: i0.ɵɵFactoryTarget.Component });
782
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", type: WorkCenterPage, isStandalone: true, selector: "mt-work-center-page", inputs: { area: { classPropertyName: "area", publicName: "area", isSignal: true, isRequired: false, transformFunction: null }, pageTitle: { classPropertyName: "pageTitle", publicName: "pageTitle", isSignal: true, isRequired: false, transformFunction: null }, menuIcon: { classPropertyName: "menuIcon", publicName: "menuIcon", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { runtimeFiltersChanged: "runtimeFiltersChanged" }, ngImport: i0, template: " <mt-client-page\r\n [menuIcon]=\"menuIcon()\"\r\n [menuTitle]=\"pageTitle()\"\r\n [menuItems]=\"menuItems()\"\r\n [menuItemsLoading]=\"loading() && !menuItems().length\"\r\n [activeItem]=\"context().selectedCardKey ?? undefined\"\r\n (menuItemClick)=\"onMenuItemClick($event)\"\r\n >\r\n <ng-template #headerClientPageEnd>\r\n <mt-property-filter-builder\r\n [schema]=\"propertyFilterSchema()\"\r\n [filters]=\"context().runtimeFilters\"\r\n (applied)=\"onRuntimeFiltersApplied($event)\"\r\n (cleared)=\"onRuntimeFiltersCleared()\"\r\n />\r\n </ng-template>\r\n\r\n <div class=\"flex flex-col gap-4\">\r\n @if (loading()) {\r\n <div class=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-3\">\r\n @for (_ of [1, 2, 3, 4]; track $index) {\r\n <p-skeleton height=\"6.5rem\" borderRadius=\"1rem\" />\r\n }\r\n </div>\r\n } @else if (kpis().length) {\r\n <div class=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-3\">\r\n @for (kpi of kpis(); track $index) {\r\n <mt-statistic-card [data]=\"kpi\" />\r\n }\r\n </div>\r\n }\r\n\r\n <mt-table\r\n [data]=\"rows()\"\r\n [columns]=\"columns()\"\r\n [lazy]=\"true\"\r\n [loading]=\"loading()\"\r\n [lazyTotalRecords]=\"totalCount()\"\r\n [showFilters]=\"false\"\r\n [generalSearch]=\"false\"\r\n [pageSize]=\"context().pageSize\"\r\n [currentPage]=\"tableCurrentPage()\"\r\n [first]=\"tableFirst()\"\r\n (lazyLoad)=\"onLazyLoad($event)\"\r\n />\r\n </div>\r\n </mt-client-page>\r\n\r\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: ClientPage, selector: "mt-client-page", inputs: ["menuIcon", "menuTitle", "menuItems", "menuItemsLoading", "activeItem", "collapsed"], outputs: ["collapsedChange", "menuItemClick"] }, { kind: "component", type: PropertyFilterBuilder, selector: "mt-property-filter-builder", inputs: ["schema", "filters", "title", "buttonLabel", "disabled"], outputs: ["filtersChange", "applied", "cleared"] }, { kind: "component", type: StatisticCard, selector: "mt-statistic-card", inputs: ["data"] }, { kind: "component", type: Table, selector: "mt-table", inputs: ["filters", "data", "columns", "rowActions", "size", "showGridlines", "stripedRows", "selectableRows", "generalSearch", "showFilters", "loading", "updating", "lazy", "lazyTotalRecords", "reorderableColumns", "reorderableRows", "dataKey", "exportable", "exportFilename", "tabs", "tabsOptionLabel", "tabsOptionValue", "activeTab", "actions", "paginatorPosition", "pageSize", "currentPage", "first", "filterTerm"], outputs: ["selectionChange", "cellChange", "lazyLoad", "columnReorder", "rowReorder", "filtersChange", "activeTabChange", "onTabChange", "pageSizeChange", "currentPageChange", "firstChange", "filterTermChange"] }, { kind: "ngmodule", type: SkeletonModule }, { kind: "component", type: i1.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "shape", "animation", "borderRadius", "size", "width", "height"] }] });
879
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", type: WorkCenterPage, isStandalone: true, selector: "mt-work-center-page", inputs: { area: { classPropertyName: "area", publicName: "area", isSignal: true, isRequired: false, transformFunction: null }, pageTitle: { classPropertyName: "pageTitle", publicName: "pageTitle", isSignal: true, isRequired: false, transformFunction: null }, menuIcon: { classPropertyName: "menuIcon", publicName: "menuIcon", isSignal: true, isRequired: false, transformFunction: null }, openItemsInModal: { classPropertyName: "openItemsInModal", publicName: "openItemsInModal", isSignal: true, isRequired: false, transformFunction: null }, itemModal: { classPropertyName: "itemModal", publicName: "itemModal", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { runtimeFiltersChanged: "runtimeFiltersChanged", itemClicked: "itemClicked" }, ngImport: i0, template: "<mt-client-page\r\n [menuIcon]=\"menuIcon()\"\r\n [menuTitle]=\"pageTitle()\"\r\n [menuItems]=\"menuItems()\"\r\n [menuItemsLoading]=\"loading() && !menuItems().length\"\r\n [activeItem]=\"context().selectedCardKey ?? undefined\"\r\n (menuItemClick)=\"onMenuItemClick($event)\"\r\n>\r\n <ng-template #headerClientPageEnd>\r\n <mt-property-filter-builder\r\n [schema]=\"propertyFilterSchema()\"\r\n [filters]=\"context().runtimeFilters\"\r\n (applied)=\"onRuntimeFiltersApplied($event)\"\r\n (cleared)=\"onRuntimeFiltersCleared()\"\r\n />\r\n </ng-template>\r\n\r\n <div class=\"flex flex-col gap-4\">\r\n @if (loading()) {\r\n <div\r\n class=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 xl:grid-cols-5 gap-3\"\r\n >\r\n @for (_ of [1, 2, 3, 4, 5]; track $index) {\r\n <p-skeleton height=\"6.5rem\" borderRadius=\"1rem\" />\r\n }\r\n </div>\r\n } @else if (kpis().length) {\r\n <div\r\n class=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 xl:grid-cols-5 gap-3\"\r\n >\r\n @for (kpi of kpis(); track $index) {\r\n <mt-statistic-card [data]=\"kpi\" />\r\n }\r\n </div>\r\n }\r\n\r\n <mt-table\r\n [data]=\"rows()\"\r\n [columns]=\"columns()\"\r\n [lazy]=\"true\"\r\n [clickableRows]=\"rowsClickable()\"\r\n [loading]=\"loading()\"\r\n [lazyTotalRecords]=\"totalCount()\"\r\n [showFilters]=\"false\"\r\n [generalSearch]=\"false\"\r\n [pageSize]=\"context().pageSize\"\r\n [currentPage]=\"tableCurrentPage()\"\r\n [first]=\"tableFirst()\"\r\n (lazyLoad)=\"onLazyLoad($event)\"\r\n (rowClick)=\"onRowClick($event)\"\r\n />\r\n </div>\r\n</mt-client-page>\r\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: ClientPage, selector: "mt-client-page", inputs: ["menuIcon", "menuTitle", "menuItems", "menuItemsLoading", "activeItem", "collapsed"], outputs: ["collapsedChange", "menuItemClick"] }, { kind: "component", type: PropertyFilterBuilder, selector: "mt-property-filter-builder", inputs: ["schema", "filters", "title", "buttonLabel", "disabled"], outputs: ["filtersChange", "applied", "cleared"] }, { kind: "component", type: StatisticCard, selector: "mt-statistic-card", inputs: ["data"] }, { kind: "component", type: Table, selector: "mt-table", inputs: ["filters", "data", "columns", "rowActions", "size", "showGridlines", "stripedRows", "selectableRows", "clickableRows", "generalSearch", "showFilters", "loading", "updating", "lazy", "lazyTotalRecords", "reorderableColumns", "reorderableRows", "dataKey", "exportable", "exportFilename", "tabs", "tabsOptionLabel", "tabsOptionValue", "activeTab", "actions", "paginatorPosition", "pageSize", "currentPage", "first", "filterTerm"], outputs: ["selectionChange", "cellChange", "lazyLoad", "columnReorder", "rowReorder", "rowClick", "filtersChange", "activeTabChange", "onTabChange", "pageSizeChange", "currentPageChange", "firstChange", "filterTermChange"] }, { kind: "ngmodule", type: SkeletonModule }, { kind: "component", type: i1.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "shape", "animation", "borderRadius", "size", "width", "height"] }] });
783
880
  }
784
881
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: WorkCenterPage, decorators: [{
785
882
  type: Component,
@@ -790,8 +887,958 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImpor
790
887
  StatisticCard,
791
888
  Table,
792
889
  SkeletonModule,
793
- ], template: " <mt-client-page\r\n [menuIcon]=\"menuIcon()\"\r\n [menuTitle]=\"pageTitle()\"\r\n [menuItems]=\"menuItems()\"\r\n [menuItemsLoading]=\"loading() && !menuItems().length\"\r\n [activeItem]=\"context().selectedCardKey ?? undefined\"\r\n (menuItemClick)=\"onMenuItemClick($event)\"\r\n >\r\n <ng-template #headerClientPageEnd>\r\n <mt-property-filter-builder\r\n [schema]=\"propertyFilterSchema()\"\r\n [filters]=\"context().runtimeFilters\"\r\n (applied)=\"onRuntimeFiltersApplied($event)\"\r\n (cleared)=\"onRuntimeFiltersCleared()\"\r\n />\r\n </ng-template>\r\n\r\n <div class=\"flex flex-col gap-4\">\r\n @if (loading()) {\r\n <div class=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-3\">\r\n @for (_ of [1, 2, 3, 4]; track $index) {\r\n <p-skeleton height=\"6.5rem\" borderRadius=\"1rem\" />\r\n }\r\n </div>\r\n } @else if (kpis().length) {\r\n <div class=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-3\">\r\n @for (kpi of kpis(); track $index) {\r\n <mt-statistic-card [data]=\"kpi\" />\r\n }\r\n </div>\r\n }\r\n\r\n <mt-table\r\n [data]=\"rows()\"\r\n [columns]=\"columns()\"\r\n [lazy]=\"true\"\r\n [loading]=\"loading()\"\r\n [lazyTotalRecords]=\"totalCount()\"\r\n [showFilters]=\"false\"\r\n [generalSearch]=\"false\"\r\n [pageSize]=\"context().pageSize\"\r\n [currentPage]=\"tableCurrentPage()\"\r\n [first]=\"tableFirst()\"\r\n (lazyLoad)=\"onLazyLoad($event)\"\r\n />\r\n </div>\r\n </mt-client-page>\r\n\r\n" }]
794
- }], propDecorators: { area: [{ type: i0.Input, args: [{ isSignal: true, alias: "area", required: false }] }], pageTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageTitle", required: false }] }], menuIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "menuIcon", required: false }] }], runtimeFiltersChanged: [{ type: i0.Output, args: ["runtimeFiltersChanged"] }] } });
890
+ ], template: "<mt-client-page\r\n [menuIcon]=\"menuIcon()\"\r\n [menuTitle]=\"pageTitle()\"\r\n [menuItems]=\"menuItems()\"\r\n [menuItemsLoading]=\"loading() && !menuItems().length\"\r\n [activeItem]=\"context().selectedCardKey ?? undefined\"\r\n (menuItemClick)=\"onMenuItemClick($event)\"\r\n>\r\n <ng-template #headerClientPageEnd>\r\n <mt-property-filter-builder\r\n [schema]=\"propertyFilterSchema()\"\r\n [filters]=\"context().runtimeFilters\"\r\n (applied)=\"onRuntimeFiltersApplied($event)\"\r\n (cleared)=\"onRuntimeFiltersCleared()\"\r\n />\r\n </ng-template>\r\n\r\n <div class=\"flex flex-col gap-4\">\r\n @if (loading()) {\r\n <div\r\n class=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 xl:grid-cols-5 gap-3\"\r\n >\r\n @for (_ of [1, 2, 3, 4, 5]; track $index) {\r\n <p-skeleton height=\"6.5rem\" borderRadius=\"1rem\" />\r\n }\r\n </div>\r\n } @else if (kpis().length) {\r\n <div\r\n class=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 xl:grid-cols-5 gap-3\"\r\n >\r\n @for (kpi of kpis(); track $index) {\r\n <mt-statistic-card [data]=\"kpi\" />\r\n }\r\n </div>\r\n }\r\n\r\n <mt-table\r\n [data]=\"rows()\"\r\n [columns]=\"columns()\"\r\n [lazy]=\"true\"\r\n [clickableRows]=\"rowsClickable()\"\r\n [loading]=\"loading()\"\r\n [lazyTotalRecords]=\"totalCount()\"\r\n [showFilters]=\"false\"\r\n [generalSearch]=\"false\"\r\n [pageSize]=\"context().pageSize\"\r\n [currentPage]=\"tableCurrentPage()\"\r\n [first]=\"tableFirst()\"\r\n (lazyLoad)=\"onLazyLoad($event)\"\r\n (rowClick)=\"onRowClick($event)\"\r\n />\r\n </div>\r\n</mt-client-page>\r\n" }]
891
+ }], propDecorators: { area: [{ type: i0.Input, args: [{ isSignal: true, alias: "area", required: false }] }], pageTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageTitle", required: false }] }], menuIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "menuIcon", required: false }] }], openItemsInModal: [{ type: i0.Input, args: [{ isSignal: true, alias: "openItemsInModal", required: false }] }], itemModal: [{ type: i0.Input, args: [{ isSignal: true, alias: "itemModal", required: false }] }], runtimeFiltersChanged: [{ type: i0.Output, args: ["runtimeFiltersChanged"] }], itemClicked: [{ type: i0.Output, args: ["itemClicked"] }] } });
892
+
893
+ function readItemContext(details) {
894
+ const item = details.item;
895
+ if (!item || typeof item !== 'object') {
896
+ return null;
897
+ }
898
+ const context = item['context'];
899
+ return context && typeof context === 'object'
900
+ ? context
901
+ : null;
902
+ }
903
+ function readNumber(source, key) {
904
+ const value = source?.[key];
905
+ if (typeof value === 'number') {
906
+ return Number.isFinite(value) ? value : undefined;
907
+ }
908
+ if (typeof value === 'string' && value.trim().length) {
909
+ const parsed = Number(value);
910
+ return Number.isFinite(parsed) ? parsed : undefined;
911
+ }
912
+ return undefined;
913
+ }
914
+ function readString$1(source, key) {
915
+ const value = source?.[key];
916
+ return typeof value === 'string' && value.trim().length ? value : undefined;
917
+ }
918
+
919
+ class WorkCenterEscalationInstanceType {
920
+ http = inject(HttpClient);
921
+ loadSub;
922
+ details = input.required(...(ngDevMode ? [{ debugName: "details" }] : []));
923
+ activeTab = signal('escalationDetails', ...(ngDevMode ? [{ debugName: "activeTab" }] : []));
924
+ currentUserId = signal('', ...(ngDevMode ? [{ debugName: "currentUserId" }] : []));
925
+ loading = signal(false, ...(ngDevMode ? [{ debugName: "loading" }] : []));
926
+ error = signal(null, ...(ngDevMode ? [{ debugName: "error" }] : []));
927
+ preview = signal(null, ...(ngDevMode ? [{ debugName: "preview" }] : []));
928
+ tabOptions = [
929
+ {
930
+ label: 'Escalation Details',
931
+ value: 'escalationDetails',
932
+ },
933
+ {
934
+ label: 'Discussion',
935
+ value: 'discussion',
936
+ },
937
+ {
938
+ label: 'Schema',
939
+ value: 'schema',
940
+ },
941
+ ];
942
+ detailEntities = computed(() => {
943
+ const preview = this.preview();
944
+ if (!preview) {
945
+ return [];
946
+ }
947
+ return [
948
+ {
949
+ key: 'initiatedBy',
950
+ name: 'Initiated By',
951
+ value: mapUserValue(preview.initiatedBy),
952
+ viewType: 'User',
953
+ order: 1,
954
+ configuration: { size: 12 },
955
+ },
956
+ {
957
+ key: 'startedAt',
958
+ name: 'Started At',
959
+ value: resolveDisplayValue(preview.startedAt),
960
+ rawValue: resolveRawValue(preview.startedAt),
961
+ viewType: 'DateTime',
962
+ order: 2,
963
+ configuration: { size: 12 },
964
+ },
965
+ {
966
+ key: 'reason',
967
+ name: 'Reason',
968
+ value: preview.reason ?? '',
969
+ viewType: 'LongText',
970
+ order: 3,
971
+ configuration: { size: 24 },
972
+ },
973
+ {
974
+ key: 'attachments',
975
+ name: 'Attachments',
976
+ value: preview.attachments ?? [],
977
+ viewType: 'Attachment',
978
+ order: 4,
979
+ configuration: { size: 24 },
980
+ },
981
+ ];
982
+ }, ...(ngDevMode ? [{ debugName: "detailEntities" }] : []));
983
+ schemaNodes = computed(() => resolveSchemaSteps(this.preview()).map((step) => ({
984
+ id: String(step.id),
985
+ name: resolveTranslatable(step.name) || `Step ${step.id}`,
986
+ color: step.isFinal ? '#059669' : step.isInitial ? '#2563eb' : '#0f172a',
987
+ })), ...(ngDevMode ? [{ debugName: "schemaNodes" }] : []));
988
+ schemaConnections = computed(() => (this.preview()?.schema?.connections ?? []).map((connection, index) => ({
989
+ id: String(connection.id ?? `${connection.source}-${connection.target}-${index}`),
990
+ from: String(connection.source),
991
+ to: String(connection.target),
992
+ })), ...(ngDevMode ? [{ debugName: "schemaConnections" }] : []));
993
+ hasSchema = computed(() => this.schemaNodes().length > 0, ...(ngDevMode ? [{ debugName: "hasSchema" }] : []));
994
+ itemContext = computed(() => readItemContext(this.details()), ...(ngDevMode ? [{ debugName: "itemContext" }] : []));
995
+ instanceId = computed(() => readNumber(this.itemContext(), 'escalationInstanceId') ??
996
+ readNumber(this.itemContext(), 'instanceId'), ...(ngDevMode ? [{ debugName: "instanceId" }] : []));
997
+ resolvedInstanceId = computed(() => this.instanceId() ?? 0, ...(ngDevMode ? [{ debugName: "resolvedInstanceId" }] : []));
998
+ canRenderDetails = computed(() => this.resolvedInstanceId() > 0, ...(ngDevMode ? [{ debugName: "canRenderDetails" }] : []));
999
+ canRenderDiscussion = computed(() => this.resolvedInstanceId() > 0, ...(ngDevMode ? [{ debugName: "canRenderDiscussion" }] : []));
1000
+ constructor() {
1001
+ effect(() => {
1002
+ const instanceId = this.resolvedInstanceId();
1003
+ if (instanceId > 0) {
1004
+ untracked(() => this.loadPreview(instanceId));
1005
+ return;
1006
+ }
1007
+ this.preview.set(null);
1008
+ this.error.set(null);
1009
+ });
1010
+ }
1011
+ ngOnDestroy() {
1012
+ this.loadSub?.unsubscribe();
1013
+ }
1014
+ onDiscussionReadStateChanged(state) {
1015
+ const resolvedUserId = state?.userId?.trim();
1016
+ if (resolvedUserId) {
1017
+ this.currentUserId.set(resolvedUserId);
1018
+ }
1019
+ }
1020
+ loadPreview(instanceId) {
1021
+ this.loadSub?.unsubscribe();
1022
+ this.loading.set(true);
1023
+ this.error.set(null);
1024
+ this.loadSub = this.http
1025
+ .get(`escalations/${instanceId}/preview`)
1026
+ .subscribe({
1027
+ next: (response) => {
1028
+ this.loading.set(false);
1029
+ this.preview.set(response.data ?? null);
1030
+ },
1031
+ error: (error) => {
1032
+ this.loading.set(false);
1033
+ this.preview.set(null);
1034
+ this.error.set(error?.error?.message ??
1035
+ error?.message ??
1036
+ 'Failed to load escalation preview');
1037
+ },
1038
+ });
1039
+ }
1040
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: WorkCenterEscalationInstanceType, deps: [], target: i0.ɵɵFactoryTarget.Component });
1041
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", type: WorkCenterEscalationInstanceType, isStandalone: true, selector: "mt-work-center-escalation-instance-type", inputs: { details: { classPropertyName: "details", publicName: "details", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<div class=\"mt-modal-content flex h-full flex-col gap-4 p-4\">\n <mt-tabs [(active)]=\"activeTab\" [options]=\"tabOptions\" />\n\n <div class=\"flex-1\" [hidden]=\"activeTab() !== 'escalationDetails'\">\n @if (canRenderDetails()) {\n @if (loading()) {\n <div\n class=\"flex min-h-[22rem] items-center justify-center rounded-2xl 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 Loading escalation details...\n </p>\n </div>\n } @else if (error(); as error) {\n <div\n class=\"flex min-h-[22rem] items-center justify-center rounded-2xl 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-2xl 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 Escalation details context is not available for this item yet.\n </p>\n </div>\n }\n </div>\n\n <div class=\"flex-1\" [hidden]=\"activeTab() !== 'discussion'\">\n @if (canRenderDiscussion()) {\n <div class=\"h-[70vh] min-h-[32rem]\">\n <mt-discussion-thread\n [moduleType]=\"'EscalationInstance'\"\n [recordId]=\"resolvedInstanceId()\"\n [currentUserId]=\"currentUserId()\"\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 (readStateChanged)=\"onDiscussionReadStateChanged($event)\"\n />\n </div>\n } @else {\n <div\n class=\"flex min-h-[22rem] items-center justify-center rounded-2xl 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 Discussion context is not available for this escalation yet.\n </p>\n </div>\n }\n </div>\n\n <div class=\"flex-1\" [hidden]=\"activeTab() !== 'schema'\">\n @if (canRenderDetails()) {\n @if (loading()) {\n <div\n class=\"flex min-h-[22rem] items-center justify-center rounded-2xl 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 Loading escalation schema...\n </p>\n </div>\n } @else if (error(); as error) {\n <div\n class=\"flex min-h-[22rem] items-center justify-center rounded-2xl 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 <div class=\"h-[70vh] overflow-hidden rounded-2xl\">\n <mt-structure-builder\n class=\"h-full\"\n [readonly]=\"true\"\n [nodes]=\"schemaNodes()\"\n [connections]=\"schemaConnections()\"\n />\n </div>\n } @else {\n <div\n class=\"flex min-h-[22rem] items-center justify-center rounded-2xl 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 Escalation schema is not available for this item yet.\n </p>\n </div>\n }\n } @else {\n <div\n class=\"flex min-h-[22rem] items-center justify-center rounded-2xl 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 Escalation schema context is not available for this item yet.\n </p>\n </div>\n }\n </div>\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: Tabs, selector: "mt-tabs", inputs: ["options", "optionLabel", "optionValue", "active", "size", "fluid", "disabled"], 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: EntitiesPreview, selector: "mt-entities-preview", inputs: ["entities"] }, { kind: "component", type: StructureBuilder, selector: "mt-structure-builder", inputs: ["availableNodes", "nodeForm", "connectionForm", "nodeActions", "nodeFields", "isAutoLayout", "readonly", "addModalType", "updateModalType", "addModalStyleClass", "updateModalStyleClass", "addModalHeader", "updateModalHeader", "appendTo", "availableTabsClass", "layoutDirection", "nodes", "connections", "nodeTemplate"], outputs: ["nodeActionsEvent", "action", "nodesChange", "connectionsChange"] }] });
1042
+ }
1043
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: WorkCenterEscalationInstanceType, decorators: [{
1044
+ type: Component,
1045
+ args: [{ selector: 'mt-work-center-escalation-instance-type', standalone: true, imports: [
1046
+ CommonModule,
1047
+ Tabs,
1048
+ DiscussionThread,
1049
+ EntitiesPreview,
1050
+ StructureBuilder,
1051
+ ], template: "<div class=\"mt-modal-content flex h-full flex-col gap-4 p-4\">\n <mt-tabs [(active)]=\"activeTab\" [options]=\"tabOptions\" />\n\n <div class=\"flex-1\" [hidden]=\"activeTab() !== 'escalationDetails'\">\n @if (canRenderDetails()) {\n @if (loading()) {\n <div\n class=\"flex min-h-[22rem] items-center justify-center rounded-2xl 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 Loading escalation details...\n </p>\n </div>\n } @else if (error(); as error) {\n <div\n class=\"flex min-h-[22rem] items-center justify-center rounded-2xl 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-2xl 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 Escalation details context is not available for this item yet.\n </p>\n </div>\n }\n </div>\n\n <div class=\"flex-1\" [hidden]=\"activeTab() !== 'discussion'\">\n @if (canRenderDiscussion()) {\n <div class=\"h-[70vh] min-h-[32rem]\">\n <mt-discussion-thread\n [moduleType]=\"'EscalationInstance'\"\n [recordId]=\"resolvedInstanceId()\"\n [currentUserId]=\"currentUserId()\"\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 (readStateChanged)=\"onDiscussionReadStateChanged($event)\"\n />\n </div>\n } @else {\n <div\n class=\"flex min-h-[22rem] items-center justify-center rounded-2xl 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 Discussion context is not available for this escalation yet.\n </p>\n </div>\n }\n </div>\n\n <div class=\"flex-1\" [hidden]=\"activeTab() !== 'schema'\">\n @if (canRenderDetails()) {\n @if (loading()) {\n <div\n class=\"flex min-h-[22rem] items-center justify-center rounded-2xl 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 Loading escalation schema...\n </p>\n </div>\n } @else if (error(); as error) {\n <div\n class=\"flex min-h-[22rem] items-center justify-center rounded-2xl 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 <div class=\"h-[70vh] overflow-hidden rounded-2xl\">\n <mt-structure-builder\n class=\"h-full\"\n [readonly]=\"true\"\n [nodes]=\"schemaNodes()\"\n [connections]=\"schemaConnections()\"\n />\n </div>\n } @else {\n <div\n class=\"flex min-h-[22rem] items-center justify-center rounded-2xl 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 Escalation schema is not available for this item yet.\n </p>\n </div>\n }\n } @else {\n <div\n class=\"flex min-h-[22rem] items-center justify-center rounded-2xl 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 Escalation schema context is not available for this item yet.\n </p>\n </div>\n }\n </div>\n</div>\n" }]
1052
+ }], ctorParameters: () => [], propDecorators: { details: [{ type: i0.Input, args: [{ isSignal: true, alias: "details", required: true }] }] } });
1053
+ function mapUserValue(value) {
1054
+ if (!value || typeof value !== 'object') {
1055
+ return {
1056
+ displayName: '',
1057
+ userName: '',
1058
+ };
1059
+ }
1060
+ return {
1061
+ id: readString(value, 'id'),
1062
+ userName: readString(value, 'userName'),
1063
+ displayName: readString(value, 'displayName') ??
1064
+ readString(value, 'fullName') ??
1065
+ readString(value, 'userName') ??
1066
+ '',
1067
+ photoUrl: readString(value, 'photoUrl'),
1068
+ phoneNumber: readString(value, 'phoneNumber') ?? readString(value, 'mobileNumber'),
1069
+ email: readString(value, 'email') ?? readString(value, 'emailAddress'),
1070
+ };
1071
+ }
1072
+ function resolveSchemaSteps(preview) {
1073
+ return preview?.schema?.stepsSchema ?? preview?.schemaSteps ?? [];
1074
+ }
1075
+ function resolveTranslatable(value) {
1076
+ if (!value || typeof value !== 'object') {
1077
+ return '';
1078
+ }
1079
+ return value.display ?? value.en ?? value.ar ?? '';
1080
+ }
1081
+ function resolveDisplayValue(value) {
1082
+ if (!value || typeof value !== 'object') {
1083
+ return typeof value === 'string' ? value : '';
1084
+ }
1085
+ return (readString(value, 'displayValue') ??
1086
+ readString(value, 'display') ??
1087
+ readString(value, 'actualValue') ??
1088
+ '');
1089
+ }
1090
+ function resolveRawValue(value) {
1091
+ if (!value || typeof value !== 'object') {
1092
+ return typeof value === 'string' ? value : undefined;
1093
+ }
1094
+ return (readString(value, 'actualValue') ??
1095
+ readString(value, 'displayValue') ??
1096
+ undefined);
1097
+ }
1098
+ function readString(value, key) {
1099
+ if (!value || typeof value !== 'object') {
1100
+ return undefined;
1101
+ }
1102
+ const result = value[key];
1103
+ return typeof result === 'string' && result.length ? result : undefined;
1104
+ }
1105
+
1106
+ class WorkCenterProcessPreviewJson {
1107
+ http = inject(HttpClient);
1108
+ loadSub;
1109
+ requestId = input(null, ...(ngDevMode ? [{ debugName: "requestId" }] : []));
1110
+ loading = signal(false, ...(ngDevMode ? [{ debugName: "loading" }] : []));
1111
+ error = signal(null, ...(ngDevMode ? [{ debugName: "error" }] : []));
1112
+ preview = signal(null, ...(ngDevMode ? [{ debugName: "preview" }] : []));
1113
+ canRenderPreview = computed(() => (this.requestId() ?? 0) > 0, ...(ngDevMode ? [{ debugName: "canRenderPreview" }] : []));
1114
+ previewJson = computed(() => {
1115
+ const preview = this.preview();
1116
+ return preview == null ? '' : JSON.stringify(preview, null, 2);
1117
+ }, ...(ngDevMode ? [{ debugName: "previewJson" }] : []));
1118
+ constructor() {
1119
+ effect(() => {
1120
+ const requestId = this.requestId() ?? 0;
1121
+ if (requestId > 0) {
1122
+ untracked(() => this.loadPreview(requestId));
1123
+ return;
1124
+ }
1125
+ this.loadSub?.unsubscribe();
1126
+ this.loading.set(false);
1127
+ this.error.set(null);
1128
+ this.preview.set(null);
1129
+ });
1130
+ }
1131
+ ngOnDestroy() {
1132
+ this.loadSub?.unsubscribe();
1133
+ }
1134
+ loadPreview(requestId) {
1135
+ this.loadSub?.unsubscribe();
1136
+ this.loading.set(true);
1137
+ this.error.set(null);
1138
+ this.loadSub = this.http
1139
+ .get(`processes/${requestId}/preview`)
1140
+ .subscribe({
1141
+ next: (response) => {
1142
+ this.loading.set(false);
1143
+ this.preview.set(response.data ?? null);
1144
+ },
1145
+ error: (error) => {
1146
+ this.loading.set(false);
1147
+ this.preview.set(null);
1148
+ this.error.set(error?.error?.message ??
1149
+ error?.message ??
1150
+ 'Failed to load process preview');
1151
+ },
1152
+ });
1153
+ }
1154
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: WorkCenterProcessPreviewJson, deps: [], target: i0.ɵɵFactoryTarget.Component });
1155
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", type: WorkCenterProcessPreviewJson, isStandalone: true, selector: "mt-work-center-process-preview-json", inputs: { requestId: { classPropertyName: "requestId", publicName: "requestId", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@if (!canRenderPreview()) {\n <div\n class=\"flex min-h-[22rem] items-center justify-center rounded-2xl 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 Process preview is not available for this item yet.\n </p>\n </div>\n} @else if (loading()) {\n <div\n class=\"flex min-h-[22rem] items-center justify-center rounded-2xl border border-dashed border-surface-300 bg-surface-50 p-6\"\n >\n <p class=\"text-sm text-surface-500\">Loading process preview...</p>\n </div>\n} @else if (error()) {\n <div\n class=\"flex min-h-[22rem] items-center justify-center rounded-2xl 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\">{{ error() }}</p>\n </div>\n} @else if (previewJson()) {\n <div class=\"overflow-hidden rounded-2xl border border-surface-200 bg-surface-50\">\n <pre\n class=\"max-h-[70vh] overflow-auto whitespace-pre-wrap break-all p-4 text-xs leading-6 text-surface-700\"\n ><code>{{ previewJson() }}</code></pre>\n </div>\n} @else {\n <div\n class=\"flex min-h-[22rem] items-center justify-center rounded-2xl 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 Process preview returned no data.\n </p>\n </div>\n}\n", dependencies: [{ kind: "ngmodule", type: CommonModule }] });
1156
+ }
1157
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: WorkCenterProcessPreviewJson, decorators: [{
1158
+ type: Component,
1159
+ args: [{ selector: 'mt-work-center-process-preview-json', standalone: true, imports: [CommonModule], template: "@if (!canRenderPreview()) {\n <div\n class=\"flex min-h-[22rem] items-center justify-center rounded-2xl 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 Process preview is not available for this item yet.\n </p>\n </div>\n} @else if (loading()) {\n <div\n class=\"flex min-h-[22rem] items-center justify-center rounded-2xl border border-dashed border-surface-300 bg-surface-50 p-6\"\n >\n <p class=\"text-sm text-surface-500\">Loading process preview...</p>\n </div>\n} @else if (error()) {\n <div\n class=\"flex min-h-[22rem] items-center justify-center rounded-2xl 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\">{{ error() }}</p>\n </div>\n} @else if (previewJson()) {\n <div class=\"overflow-hidden rounded-2xl border border-surface-200 bg-surface-50\">\n <pre\n class=\"max-h-[70vh] overflow-auto whitespace-pre-wrap break-all p-4 text-xs leading-6 text-surface-700\"\n ><code>{{ previewJson() }}</code></pre>\n </div>\n} @else {\n <div\n class=\"flex min-h-[22rem] items-center justify-center rounded-2xl 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 Process preview returned no data.\n </p>\n </div>\n}\n" }]
1160
+ }], ctorParameters: () => [], propDecorators: { requestId: [{ type: i0.Input, args: [{ isSignal: true, alias: "requestId", required: false }] }] } });
1161
+
1162
+ class WorkCenterProcessRequestType {
1163
+ details = input.required(...(ngDevMode ? [{ debugName: "details" }] : []));
1164
+ activeTab = signal('requestDetails', ...(ngDevMode ? [{ debugName: "activeTab" }] : []));
1165
+ currentUserId = signal('', ...(ngDevMode ? [{ debugName: "currentUserId" }] : []));
1166
+ tabOptions = [
1167
+ {
1168
+ label: 'Request Details',
1169
+ value: 'requestDetails',
1170
+ },
1171
+ {
1172
+ label: 'Discussion',
1173
+ value: 'discussion',
1174
+ },
1175
+ {
1176
+ label: 'Preview',
1177
+ value: 'preview',
1178
+ },
1179
+ ];
1180
+ itemContext = computed(() => readItemContext(this.details()), ...(ngDevMode ? [{ debugName: "itemContext" }] : []));
1181
+ requestId = computed(() => readNumber(this.itemContext(), 'requestId') ??
1182
+ readNumber(this.itemContext(), 'draftProcessId'), ...(ngDevMode ? [{ debugName: "requestId" }] : []));
1183
+ discussionRecordId = computed(() => this.requestId() ?? 0, ...(ngDevMode ? [{ debugName: "discussionRecordId" }] : []));
1184
+ canRenderDiscussion = computed(() => this.discussionRecordId() > 0, ...(ngDevMode ? [{ debugName: "canRenderDiscussion" }] : []));
1185
+ canRenderPreview = computed(() => (this.requestId() ?? 0) > 0, ...(ngDevMode ? [{ debugName: "canRenderPreview" }] : []));
1186
+ onDiscussionReadStateChanged(state) {
1187
+ const resolvedUserId = state?.userId?.trim();
1188
+ if (resolvedUserId) {
1189
+ this.currentUserId.set(resolvedUserId);
1190
+ }
1191
+ }
1192
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: WorkCenterProcessRequestType, deps: [], target: i0.ɵɵFactoryTarget.Component });
1193
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", type: WorkCenterProcessRequestType, isStandalone: true, selector: "mt-work-center-process-request-type", inputs: { details: { classPropertyName: "details", publicName: "details", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<div class=\"mt-modal-content flex h-full flex-col gap-4 p-4\">\n <mt-tabs [(active)]=\"activeTab\" [options]=\"tabOptions\" />\n\n <div class=\"flex-1\" [hidden]=\"activeTab() !== 'requestDetails'\">\n <div\n class=\"flex min-h-[22rem] items-center justify-center rounded-2xl 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 Process request details view will be added here.\n </p>\n </div>\n </div>\n\n <div class=\"flex-1\" [hidden]=\"activeTab() !== 'discussion'\">\n @if (canRenderDiscussion()) {\n <div class=\"h-[70vh] min-h-[32rem]\">\n <mt-discussion-thread\n [moduleType]=\"'ProcessRequest'\"\n [recordId]=\"discussionRecordId()\"\n [currentUserId]=\"currentUserId()\"\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 (readStateChanged)=\"onDiscussionReadStateChanged($event)\"\n />\n </div>\n } @else {\n <div\n class=\"flex min-h-[22rem] items-center justify-center rounded-2xl 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 Discussion context is not available for this process request yet.\n </p>\n </div>\n }\n </div>\n\n <div class=\"flex-1\" [hidden]=\"activeTab() !== 'preview'\">\n @if (canRenderPreview()) {\n <mt-work-center-process-preview-json [requestId]=\"requestId()\" />\n } @else {\n <div\n class=\"flex min-h-[22rem] items-center justify-center rounded-2xl 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 Process preview is not available for this process request yet.\n </p>\n </div>\n }\n </div>\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: Tabs, selector: "mt-tabs", inputs: ["options", "optionLabel", "optionValue", "active", "size", "fluid", "disabled"], 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: WorkCenterProcessPreviewJson, selector: "mt-work-center-process-preview-json", inputs: ["requestId"] }] });
1194
+ }
1195
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: WorkCenterProcessRequestType, decorators: [{
1196
+ type: Component,
1197
+ args: [{ selector: 'mt-work-center-process-request-type', standalone: true, imports: [CommonModule, Tabs, DiscussionThread, WorkCenterProcessPreviewJson], template: "<div class=\"mt-modal-content flex h-full flex-col gap-4 p-4\">\n <mt-tabs [(active)]=\"activeTab\" [options]=\"tabOptions\" />\n\n <div class=\"flex-1\" [hidden]=\"activeTab() !== 'requestDetails'\">\n <div\n class=\"flex min-h-[22rem] items-center justify-center rounded-2xl 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 Process request details view will be added here.\n </p>\n </div>\n </div>\n\n <div class=\"flex-1\" [hidden]=\"activeTab() !== 'discussion'\">\n @if (canRenderDiscussion()) {\n <div class=\"h-[70vh] min-h-[32rem]\">\n <mt-discussion-thread\n [moduleType]=\"'ProcessRequest'\"\n [recordId]=\"discussionRecordId()\"\n [currentUserId]=\"currentUserId()\"\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 (readStateChanged)=\"onDiscussionReadStateChanged($event)\"\n />\n </div>\n } @else {\n <div\n class=\"flex min-h-[22rem] items-center justify-center rounded-2xl 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 Discussion context is not available for this process request yet.\n </p>\n </div>\n }\n </div>\n\n <div class=\"flex-1\" [hidden]=\"activeTab() !== 'preview'\">\n @if (canRenderPreview()) {\n <mt-work-center-process-preview-json [requestId]=\"requestId()\" />\n } @else {\n <div\n class=\"flex min-h-[22rem] items-center justify-center rounded-2xl 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 Process preview is not available for this process request yet.\n </p>\n </div>\n }\n </div>\n</div>\n" }]
1198
+ }], propDecorators: { details: [{ type: i0.Input, args: [{ isSignal: true, alias: "details", required: true }] }] } });
1199
+
1200
+ class WorkCenterItemActionContextStore {
1201
+ clientForm = signal(null, ...(ngDevMode ? [{ debugName: "clientForm" }] : []));
1202
+ setClientForm(clientForm) {
1203
+ this.clientForm.set(clientForm);
1204
+ }
1205
+ resolveFormValues() {
1206
+ const values = this.clientForm()?.getSubmitValues();
1207
+ return Array.isArray(values)
1208
+ ? values.map((value) => ({
1209
+ requestPropertyId: value.requestPropertyId,
1210
+ propertyKey: value.propertyKey,
1211
+ value: value.value,
1212
+ }))
1213
+ : [];
1214
+ }
1215
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: WorkCenterItemActionContextStore, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1216
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: WorkCenterItemActionContextStore });
1217
+ }
1218
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: WorkCenterItemActionContextStore, decorators: [{
1219
+ type: Injectable
1220
+ }] });
1221
+
1222
+ class WorkCenterProcessStepType {
1223
+ actionContext = inject(WorkCenterItemActionContextStore, {
1224
+ optional: true,
1225
+ });
1226
+ details = input.required(...(ngDevMode ? [{ debugName: "details" }] : []));
1227
+ clientForm = viewChild(ClientForm, ...(ngDevMode ? [{ debugName: "clientForm" }] : []));
1228
+ activeTab = signal('form', ...(ngDevMode ? [{ debugName: "activeTab" }] : []));
1229
+ currentUserId = signal('', ...(ngDevMode ? [{ debugName: "currentUserId" }] : []));
1230
+ tabOptions = [
1231
+ {
1232
+ label: 'Form',
1233
+ value: 'form',
1234
+ },
1235
+ {
1236
+ label: 'Discussion',
1237
+ value: 'discussion',
1238
+ },
1239
+ {
1240
+ label: 'Preview',
1241
+ value: 'preview',
1242
+ },
1243
+ ];
1244
+ itemContext = computed(() => readItemContext(this.details()), ...(ngDevMode ? [{ debugName: "itemContext" }] : []));
1245
+ requestId = computed(() => readNumber(this.itemContext(), 'requestId') ??
1246
+ readNumber(this.itemContext(), 'draftProcessId'), ...(ngDevMode ? [{ debugName: "requestId" }] : []));
1247
+ moduleKey = computed(() => readString$1(this.itemContext(), 'moduleKey') ?? '', ...(ngDevMode ? [{ debugName: "moduleKey" }] : []));
1248
+ operationKey = computed(() => readString$1(this.itemContext(), 'operationKey') ?? '', ...(ngDevMode ? [{ debugName: "operationKey" }] : []));
1249
+ moduleId = computed(() => readNumber(this.itemContext(), 'moduleId'), ...(ngDevMode ? [{ debugName: "moduleId" }] : []));
1250
+ levelId = computed(() => readNumber(this.itemContext(), 'levelId'), ...(ngDevMode ? [{ debugName: "levelId" }] : []));
1251
+ levelDataId = computed(() => readNumber(this.itemContext(), 'levelDataId'), ...(ngDevMode ? [{ debugName: "levelDataId" }] : []));
1252
+ moduleDataId = computed(() => readNumber(this.itemContext(), 'moduleDataId'), ...(ngDevMode ? [{ debugName: "moduleDataId" }] : []));
1253
+ requestSchemaId = computed(() => readNumber(this.itemContext(), 'requestSchemaId'), ...(ngDevMode ? [{ debugName: "requestSchemaId" }] : []));
1254
+ draftProcessId = computed(() => readNumber(this.itemContext(), 'draftProcessId') ??
1255
+ readNumber(this.itemContext(), 'requestId'), ...(ngDevMode ? [{ debugName: "draftProcessId" }] : []));
1256
+ discussionRecordId = computed(() => this.requestId() ?? 0, ...(ngDevMode ? [{ debugName: "discussionRecordId" }] : []));
1257
+ canRenderForm = computed(() => !!this.moduleKey() && !!this.operationKey(), ...(ngDevMode ? [{ debugName: "canRenderForm" }] : []));
1258
+ canRenderDiscussion = computed(() => this.discussionRecordId() > 0, ...(ngDevMode ? [{ debugName: "canRenderDiscussion" }] : []));
1259
+ canRenderPreview = computed(() => (this.requestId() ?? 0) > 0, ...(ngDevMode ? [{ debugName: "canRenderPreview" }] : []));
1260
+ constructor() {
1261
+ effect(() => {
1262
+ this.actionContext?.setClientForm(this.clientForm() ?? null);
1263
+ });
1264
+ }
1265
+ onDiscussionReadStateChanged(state) {
1266
+ const resolvedUserId = state?.userId?.trim();
1267
+ if (resolvedUserId) {
1268
+ this.currentUserId.set(resolvedUserId);
1269
+ }
1270
+ }
1271
+ ngOnDestroy() {
1272
+ this.actionContext?.setClientForm(null);
1273
+ }
1274
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: WorkCenterProcessStepType, deps: [], target: i0.ɵɵFactoryTarget.Component });
1275
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", type: WorkCenterProcessStepType, isStandalone: true, selector: "mt-work-center-process-step-type", inputs: { details: { classPropertyName: "details", publicName: "details", isSignal: true, isRequired: true, transformFunction: null } }, viewQueries: [{ propertyName: "clientForm", first: true, predicate: ClientForm, descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"mt-modal-content flex h-full flex-col gap-4 p-4\">\n <mt-tabs [(active)]=\"activeTab\" [options]=\"tabOptions\" />\n\n <div class=\"flex-1 p-4\" [hidden]=\"activeTab() !== 'form'\">\n <div class=\"flex-1\">\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 />\n } @else {\n <div\n class=\"flex min-h-[22rem] items-center justify-center rounded-2xl 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 Process form context is not available for this item yet.\n </p>\n </div>\n }\n </div>\n </div>\n\n <div class=\"flex-1\" [hidden]=\"activeTab() !== 'discussion'\">\n @if (canRenderDiscussion()) {\n <div class=\"h-[70vh] min-h-[32rem]\">\n <mt-discussion-thread\n [moduleType]=\"'ProcessRequest'\"\n [recordId]=\"discussionRecordId()\"\n [currentUserId]=\"currentUserId()\"\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 (readStateChanged)=\"onDiscussionReadStateChanged($event)\"\n />\n </div>\n } @else {\n <div\n class=\"flex min-h-[22rem] items-center justify-center rounded-2xl 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 Discussion context is not available for this process item yet.\n </p>\n </div>\n }\n </div>\n\n <div class=\"flex-1\" [hidden]=\"activeTab() !== 'preview'\">\n @if (canRenderPreview()) {\n <mt-work-center-process-preview-json [requestId]=\"requestId()\" />\n } @else {\n <div\n class=\"flex min-h-[22rem] items-center justify-center rounded-2xl 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 Process preview is not available for this process item yet.\n </p>\n </div>\n }\n </div>\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: Tabs, selector: "mt-tabs", inputs: ["options", "optionLabel", "optionValue", "active", "size", "fluid", "disabled"], outputs: ["activeChange", "onChange"] }, { kind: "component", type: ClientForm, selector: "mt-client-form", inputs: ["moduleKey", "operationKey", "moduleId", "levelId", "levelDataId", "moduleDataId", "requestSchemaId", "draftProcessId", "preview", "returnUrl", "readonly", "autoLoad", "formMode", "renderMode", "showInternalStepActions", "lang", "lookups"], outputs: ["loaded", "submitted", "errored", "modeDetected", "formSourceDetected"] }, { 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: WorkCenterProcessPreviewJson, selector: "mt-work-center-process-preview-json", inputs: ["requestId"] }] });
1276
+ }
1277
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: WorkCenterProcessStepType, decorators: [{
1278
+ type: Component,
1279
+ args: [{ selector: 'mt-work-center-process-step-type', standalone: true, imports: [
1280
+ CommonModule,
1281
+ Tabs,
1282
+ ClientForm,
1283
+ DiscussionThread,
1284
+ WorkCenterProcessPreviewJson,
1285
+ ], template: "<div class=\"mt-modal-content flex h-full flex-col gap-4 p-4\">\n <mt-tabs [(active)]=\"activeTab\" [options]=\"tabOptions\" />\n\n <div class=\"flex-1 p-4\" [hidden]=\"activeTab() !== 'form'\">\n <div class=\"flex-1\">\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 />\n } @else {\n <div\n class=\"flex min-h-[22rem] items-center justify-center rounded-2xl 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 Process form context is not available for this item yet.\n </p>\n </div>\n }\n </div>\n </div>\n\n <div class=\"flex-1\" [hidden]=\"activeTab() !== 'discussion'\">\n @if (canRenderDiscussion()) {\n <div class=\"h-[70vh] min-h-[32rem]\">\n <mt-discussion-thread\n [moduleType]=\"'ProcessRequest'\"\n [recordId]=\"discussionRecordId()\"\n [currentUserId]=\"currentUserId()\"\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 (readStateChanged)=\"onDiscussionReadStateChanged($event)\"\n />\n </div>\n } @else {\n <div\n class=\"flex min-h-[22rem] items-center justify-center rounded-2xl 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 Discussion context is not available for this process item yet.\n </p>\n </div>\n }\n </div>\n\n <div class=\"flex-1\" [hidden]=\"activeTab() !== 'preview'\">\n @if (canRenderPreview()) {\n <mt-work-center-process-preview-json [requestId]=\"requestId()\" />\n } @else {\n <div\n class=\"flex min-h-[22rem] items-center justify-center rounded-2xl 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 Process preview is not available for this process item yet.\n </p>\n </div>\n }\n </div>\n</div>\n" }]
1286
+ }], ctorParameters: () => [], propDecorators: { details: [{ type: i0.Input, args: [{ isSignal: true, alias: "details", required: true }] }], clientForm: [{ type: i0.ViewChild, args: [i0.forwardRef(() => ClientForm), { isSignal: true }] }] } });
1287
+
1288
+ class WorkCenterItemModal {
1289
+ details = input.required(...(ngDevMode ? [{ debugName: "details" }] : []));
1290
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: WorkCenterItemModal, deps: [], target: i0.ɵɵFactoryTarget.Component });
1291
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", type: WorkCenterItemModal, isStandalone: true, selector: "mt-work-center-item-modal", inputs: { details: { classPropertyName: "details", publicName: "details", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "@if (details().type === \"ProcessStep\") {\n <mt-work-center-process-step-type [details]=\"details()\" />\n} @else if (details().type === \"ProcessRequest\") {\n <mt-work-center-process-request-type [details]=\"details()\" />\n} @else if (details().type === \"EscalationInstance\") {\n <mt-work-center-escalation-instance-type [details]=\"details()\" />\n} @else {\n <div class=\"mt-modal-content flex h-full items-center justify-center p-4\">\n <p class=\"text-sm text-surface-500\">This item type is not supported yet.</p>\n </div>\n}\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: WorkCenterProcessStepType, selector: "mt-work-center-process-step-type", inputs: ["details"] }, { kind: "component", type: WorkCenterProcessRequestType, selector: "mt-work-center-process-request-type", inputs: ["details"] }, { kind: "component", type: WorkCenterEscalationInstanceType, selector: "mt-work-center-escalation-instance-type", inputs: ["details"] }] });
1292
+ }
1293
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: WorkCenterItemModal, decorators: [{
1294
+ type: Component,
1295
+ args: [{ selector: 'mt-work-center-item-modal', standalone: true, imports: [
1296
+ CommonModule,
1297
+ WorkCenterProcessStepType,
1298
+ WorkCenterProcessRequestType,
1299
+ WorkCenterEscalationInstanceType,
1300
+ ], template: "@if (details().type === \"ProcessStep\") {\n <mt-work-center-process-step-type [details]=\"details()\" />\n} @else if (details().type === \"ProcessRequest\") {\n <mt-work-center-process-request-type [details]=\"details()\" />\n} @else if (details().type === \"EscalationInstance\") {\n <mt-work-center-escalation-instance-type [details]=\"details()\" />\n} @else {\n <div class=\"mt-modal-content flex h-full items-center justify-center p-4\">\n <p class=\"text-sm text-surface-500\">This item type is not supported yet.</p>\n </div>\n}\n" }]
1301
+ }], propDecorators: { details: [{ type: i0.Input, args: [{ isSignal: true, alias: "details", required: true }] }] } });
1302
+
1303
+ class WorkCenterItemActionConfirmDialog {
1304
+ ref = inject(ModalRef);
1305
+ modal = inject(ModalService);
1306
+ action = input.required(...(ngDevMode ? [{ debugName: "action" }] : []));
1307
+ formControl = new FormControl({});
1308
+ actionLabel = computed(() => this.action().actionName?.display || this.action().actionKey, ...(ngDevMode ? [{ debugName: "actionLabel" }] : []));
1309
+ formConfig = computed(() => ({
1310
+ sections: [
1311
+ {
1312
+ key: 'action-confirmation',
1313
+ type: 'none',
1314
+ fields: buildActionFields(this.action().payloadKeys, this.action().requiredPayloadKeys),
1315
+ },
1316
+ ],
1317
+ }), ...(ngDevMode ? [{ debugName: "formConfig" }] : []));
1318
+ hasFormFields = computed(() => this.formConfig().sections[0]?.fields.length > 0, ...(ngDevMode ? [{ debugName: "hasFormFields" }] : []));
1319
+ onCancel() {
1320
+ this.ref.close(null);
1321
+ }
1322
+ onConfirm() {
1323
+ if (this.formControl.invalid) {
1324
+ this.formControl.markAllAsTouched();
1325
+ return;
1326
+ }
1327
+ this.ref.close(this.formControl.getRawValue() ?? {});
1328
+ }
1329
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: WorkCenterItemActionConfirmDialog, deps: [], target: i0.ɵɵFactoryTarget.Component });
1330
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", type: WorkCenterItemActionConfirmDialog, isStandalone: true, selector: "mt-work-center-item-action-confirm-dialog", inputs: { action: { classPropertyName: "action", publicName: "action", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<div [class]=\"modal.contentClass +' p-4'\">\n @if (hasFormFields()) {\n <mt-dynamic-form [formConfig]=\"formConfig()\" [formControl]=\"formControl\" />\n } @else {\n <p class=\"text-sm text-surface-500\">\n Confirm {{ actionLabel() }} for this item.\n </p>\n }\n\n</div>\n\n<div [class]=\"modal.footerClass\">\n <mt-button label=\"Cancel\" severity=\"secondary\" variant=\"outlined\" (onClick)=\"onCancel()\" />\n <mt-button [label]=\"actionLabel()\" (onClick)=\"onConfirm()\" />\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "component", type: DynamicForm, selector: "mt-dynamic-form", inputs: ["formConfig", "forcedHiddenFieldKeys", "preserveForcedHiddenValues", "visibleSectionKeys"], outputs: ["runtimeMessagesChange"] }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }] });
1331
+ }
1332
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: WorkCenterItemActionConfirmDialog, decorators: [{
1333
+ type: Component,
1334
+ args: [{ selector: 'mt-work-center-item-action-confirm-dialog', standalone: true, imports: [CommonModule, ReactiveFormsModule, DynamicForm, Button], template: "<div [class]=\"modal.contentClass +' p-4'\">\n @if (hasFormFields()) {\n <mt-dynamic-form [formConfig]=\"formConfig()\" [formControl]=\"formControl\" />\n } @else {\n <p class=\"text-sm text-surface-500\">\n Confirm {{ actionLabel() }} for this item.\n </p>\n }\n\n</div>\n\n<div [class]=\"modal.footerClass\">\n <mt-button label=\"Cancel\" severity=\"secondary\" variant=\"outlined\" (onClick)=\"onCancel()\" />\n <mt-button [label]=\"actionLabel()\" (onClick)=\"onConfirm()\" />\n</div>\n" }]
1335
+ }], propDecorators: { action: [{ type: i0.Input, args: [{ isSignal: true, alias: "action", required: true }] }] } });
1336
+ function buildActionFields(payloadKeys, requiredPayloadKeys) {
1337
+ const requestedKeys = new Set(payloadKeys ?? []);
1338
+ const requiredKeys = new Set(requiredPayloadKeys ?? []);
1339
+ const fields = [];
1340
+ if (requestedKeys.has('delegatedUser')) {
1341
+ fields.push({
1342
+ key: 'delegatedUser',
1343
+ type: 'user-search',
1344
+ label: 'Delegated User',
1345
+ placeholder: 'Search user',
1346
+ apiUrl: 'Identity/users',
1347
+ optionLabel: 'displayName',
1348
+ optionValue: 'id',
1349
+ validators: buildRequiredValidators(requiredKeys.has('delegatedUser')),
1350
+ colSpan: 12,
1351
+ });
1352
+ }
1353
+ if (requestedKeys.has('reason')) {
1354
+ fields.push({
1355
+ key: 'reason',
1356
+ type: 'textarea',
1357
+ label: 'Reason',
1358
+ placeholder: 'Enter reason',
1359
+ rows: 4,
1360
+ validators: buildRequiredValidators(requiredKeys.has('reason')),
1361
+ colSpan: 12,
1362
+ });
1363
+ }
1364
+ if (requestedKeys.has('note')) {
1365
+ fields.push({
1366
+ key: 'note',
1367
+ type: 'textarea',
1368
+ label: 'Note',
1369
+ placeholder: 'Enter note',
1370
+ rows: 4,
1371
+ validators: buildRequiredValidators(requiredKeys.has('note')),
1372
+ colSpan: 12,
1373
+ });
1374
+ }
1375
+ if (requestedKeys.has('attachments')) {
1376
+ fields.push({
1377
+ key: 'attachments',
1378
+ type: 'upload-file',
1379
+ label: 'Attachments',
1380
+ endPoint: 'uploader',
1381
+ multiple: true,
1382
+ title: 'Upload Attachment',
1383
+ description: 'Click or drop a file to upload',
1384
+ validators: buildRequiredValidators(requiredKeys.has('attachments')),
1385
+ colSpan: 12,
1386
+ });
1387
+ }
1388
+ return fields;
1389
+ }
1390
+ function buildRequiredValidators(required) {
1391
+ return required ? [ValidatorConfig.required()] : [];
1392
+ }
1393
+
1394
+ class WorkCenterItemModalFooterActions {
1395
+ http = inject(HttpClient);
1396
+ modal = inject(ModalService);
1397
+ toast = inject(ToastService);
1398
+ actionContext = inject(WorkCenterItemActionContextStore);
1399
+ loadSub;
1400
+ executeSub;
1401
+ lastContextKey = null;
1402
+ contextKey = input(null, ...(ngDevMode ? [{ debugName: "contextKey" }] : []));
1403
+ actionExecuted = output();
1404
+ visibilityChange = output();
1405
+ reloadTick = signal(0, ...(ngDevMode ? [{ debugName: "reloadTick" }] : []));
1406
+ hasResolvedLoad = signal(false, ...(ngDevMode ? [{ debugName: "hasResolvedLoad" }] : []));
1407
+ loading = signal(false, ...(ngDevMode ? [{ debugName: "loading" }] : []));
1408
+ error = signal(null, ...(ngDevMode ? [{ debugName: "error" }] : []));
1409
+ actions = signal([], ...(ngDevMode ? [{ debugName: "actions" }] : []));
1410
+ pendingActionKey = signal(null, ...(ngDevMode ? [{ debugName: "pendingActionKey" }] : []));
1411
+ displayActions = computed(() => this.actions()
1412
+ .filter((action) => action.isAvailable !== false &&
1413
+ !!action.url &&
1414
+ !!normalizeMethod(action.httpMethod))
1415
+ .map((action, index) => ({
1416
+ ...action,
1417
+ label: action.actionName?.display || action.actionKey,
1418
+ severity: resolveActionSeverity(action.actionKey, index),
1419
+ variant: resolveActionVariant(action.actionKey, index),
1420
+ })), ...(ngDevMode ? [{ debugName: "displayActions" }] : []));
1421
+ shouldRender = computed(() => !!this.contextKey() &&
1422
+ (!this.hasResolvedLoad() ||
1423
+ this.loading() ||
1424
+ !!this.error() ||
1425
+ this.displayActions().length > 0 ||
1426
+ !!this.pendingActionKey()), ...(ngDevMode ? [{ debugName: "shouldRender" }] : []));
1427
+ constructor() {
1428
+ effect(() => {
1429
+ const contextKey = this.contextKey();
1430
+ this.reloadTick();
1431
+ this.error.set(null);
1432
+ if (!contextKey) {
1433
+ this.lastContextKey = null;
1434
+ this.actions.set([]);
1435
+ this.loading.set(false);
1436
+ this.hasResolvedLoad.set(false);
1437
+ return;
1438
+ }
1439
+ if (contextKey !== this.lastContextKey) {
1440
+ this.actions.set([]);
1441
+ this.lastContextKey = contextKey;
1442
+ }
1443
+ untracked(() => this.loadActions(contextKey));
1444
+ });
1445
+ effect(() => {
1446
+ this.visibilityChange.emit(this.shouldRender());
1447
+ });
1448
+ }
1449
+ ngOnDestroy() {
1450
+ this.loadSub?.unsubscribe();
1451
+ this.executeSub?.unsubscribe();
1452
+ }
1453
+ executeAction(action) {
1454
+ const contextKey = this.contextKey();
1455
+ const method = normalizeMethod(action.httpMethod);
1456
+ const url = action.url?.trim();
1457
+ if (!contextKey ||
1458
+ !method ||
1459
+ !url ||
1460
+ action.isAvailable === false ||
1461
+ !!this.pendingActionKey()) {
1462
+ return;
1463
+ }
1464
+ if (action.needConfirmation) {
1465
+ this.openConfirmationDialog(action);
1466
+ return;
1467
+ }
1468
+ const payload = this.buildActionPayload(action);
1469
+ if (!this.ensurePayloadIsReady(action, payload)) {
1470
+ return;
1471
+ }
1472
+ this.runActionRequest(action, method, url, payload);
1473
+ }
1474
+ openConfirmationDialog(action) {
1475
+ const ref = this.modal.openModal(WorkCenterItemActionConfirmDialog, 'dialog', {
1476
+ header: action.label,
1477
+ width: '40rem',
1478
+ dismissableMask: true,
1479
+ inputValues: {
1480
+ action,
1481
+ },
1482
+ });
1483
+ ref.onClose
1484
+ .pipe(take(1))
1485
+ .subscribe((formValue) => {
1486
+ if (formValue == null) {
1487
+ return;
1488
+ }
1489
+ const method = normalizeMethod(action.httpMethod);
1490
+ const url = action.url?.trim();
1491
+ if (!method || !url) {
1492
+ return;
1493
+ }
1494
+ const payload = this.buildActionPayload(action, formValue);
1495
+ if (!this.ensurePayloadIsReady(action, payload)) {
1496
+ return;
1497
+ }
1498
+ this.runActionRequest(action, method, url, payload);
1499
+ });
1500
+ }
1501
+ runActionRequest(action, method, url, payload) {
1502
+ this.error.set(null);
1503
+ this.pendingActionKey.set(action.actionKey);
1504
+ this.executeSub?.unsubscribe();
1505
+ this.executeSub = this.http
1506
+ .request(method, url, buildRequestOptions(method, payload))
1507
+ .pipe(map((response) => resolveActionSuccessMessage(response, `${action.label} completed successfully.`)))
1508
+ .subscribe({
1509
+ next: (message) => {
1510
+ this.pendingActionKey.set(null);
1511
+ this.toast.success(message);
1512
+ this.reloadTick.update((value) => value + 1);
1513
+ this.actionExecuted.emit();
1514
+ },
1515
+ error: (error) => {
1516
+ const message = resolveHttpErrorMessage(error, `Failed to execute ${action.label}.`);
1517
+ this.pendingActionKey.set(null);
1518
+ this.error.set(message);
1519
+ this.toast.error(message);
1520
+ },
1521
+ });
1522
+ }
1523
+ buildActionPayload(action, formValue) {
1524
+ const payloadKeys = action.payloadKeys ?? [];
1525
+ if (!payloadKeys.length) {
1526
+ return undefined;
1527
+ }
1528
+ const payload = {};
1529
+ for (const key of payloadKeys) {
1530
+ const formFieldValue = formValue?.[key];
1531
+ if (formFieldValue !== undefined) {
1532
+ payload[key] = normalizePayloadValue(key, formFieldValue);
1533
+ continue;
1534
+ }
1535
+ const externalValue = this.resolveExternalPayloadValue(key, action);
1536
+ if (externalValue !== undefined) {
1537
+ payload[key] = externalValue;
1538
+ continue;
1539
+ }
1540
+ const defaultValue = resolveDefaultPayloadValue(key);
1541
+ if (defaultValue !== undefined) {
1542
+ payload[key] = defaultValue;
1543
+ }
1544
+ }
1545
+ return Object.keys(payload).length ? payload : undefined;
1546
+ }
1547
+ ensurePayloadIsReady(action, payload) {
1548
+ const missingRequiredKeys = (action.requiredPayloadKeys ?? []).filter((key) => isMissingPayloadValue(payload?.[key]));
1549
+ if (!missingRequiredKeys.length) {
1550
+ return true;
1551
+ }
1552
+ const message = `Cannot execute ${action.label}. Missing: ${missingRequiredKeys.join(', ')}.`;
1553
+ this.error.set(message);
1554
+ this.toast.error(message);
1555
+ return false;
1556
+ }
1557
+ resolveExternalPayloadValue(key, action) {
1558
+ switch (key) {
1559
+ case 'stepId':
1560
+ return action.stepIds?.[0];
1561
+ case 'values':
1562
+ return this.actionContext.resolveFormValues();
1563
+ default:
1564
+ return undefined;
1565
+ }
1566
+ }
1567
+ loadActions(contextKey) {
1568
+ this.loadSub?.unsubscribe();
1569
+ this.loading.set(true);
1570
+ this.hasResolvedLoad.set(false);
1571
+ this.loadSub = this.http
1572
+ .get('workcenter/runtime/item-actions', {
1573
+ params: { contextKey },
1574
+ })
1575
+ .pipe(map((response) => {
1576
+ if (response.code === 1) {
1577
+ return response.data.actions ?? [];
1578
+ }
1579
+ throw new Error(resolveEnvelopeErrorMessage(response, 'Failed to load item actions.'));
1580
+ }))
1581
+ .subscribe({
1582
+ next: (actions) => {
1583
+ this.actions.set(actions);
1584
+ this.loading.set(false);
1585
+ this.hasResolvedLoad.set(true);
1586
+ },
1587
+ error: (error) => {
1588
+ this.actions.set([]);
1589
+ this.loading.set(false);
1590
+ this.hasResolvedLoad.set(true);
1591
+ this.error.set(resolveHttpErrorMessage(error, 'Failed to load item actions.'));
1592
+ },
1593
+ });
1594
+ }
1595
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: WorkCenterItemModalFooterActions, deps: [], target: i0.ɵɵFactoryTarget.Component });
1596
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", type: WorkCenterItemModalFooterActions, isStandalone: true, selector: "mt-work-center-item-modal-footer-actions", inputs: { contextKey: { classPropertyName: "contextKey", publicName: "contextKey", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { actionExecuted: "actionExecuted", visibilityChange: "visibilityChange" }, host: { classAttribute: "flex w-full flex-1 flex-wrap items-center justify-end gap-2" }, ngImport: i0, template: "@if (shouldRender()) {\n @if (error(); as error) {\n <span class=\"me-auto max-w-md text-sm text-red-600\">\n {{ error }}\n </span>\n } @else if (loading() && !displayActions().length) {\n <span class=\"me-auto text-sm text-surface-500\">Loading actions...</span>\n }\n\n @for (action of displayActions(); track action.actionKey + action.url) {\n <mt-button\n [label]=\"action.label\"\n [severity]=\"action.severity\"\n [variant]=\"action.variant\"\n [disabled]=\"loading() || !!pendingActionKey()\"\n [loading]=\"pendingActionKey() === action.actionKey\"\n (onClick)=\"executeAction(action)\"\n />\n }\n}\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }] });
1597
+ }
1598
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: WorkCenterItemModalFooterActions, decorators: [{
1599
+ type: Component,
1600
+ args: [{ selector: 'mt-work-center-item-modal-footer-actions', standalone: true, imports: [CommonModule, Button], host: {
1601
+ class: 'flex w-full flex-1 flex-wrap items-center justify-end gap-2',
1602
+ }, template: "@if (shouldRender()) {\n @if (error(); as error) {\n <span class=\"me-auto max-w-md text-sm text-red-600\">\n {{ error }}\n </span>\n } @else if (loading() && !displayActions().length) {\n <span class=\"me-auto text-sm text-surface-500\">Loading actions...</span>\n }\n\n @for (action of displayActions(); track action.actionKey + action.url) {\n <mt-button\n [label]=\"action.label\"\n [severity]=\"action.severity\"\n [variant]=\"action.variant\"\n [disabled]=\"loading() || !!pendingActionKey()\"\n [loading]=\"pendingActionKey() === action.actionKey\"\n (onClick)=\"executeAction(action)\"\n />\n }\n}\n" }]
1603
+ }], ctorParameters: () => [], propDecorators: { contextKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "contextKey", required: false }] }], actionExecuted: [{ type: i0.Output, args: ["actionExecuted"] }], visibilityChange: [{ type: i0.Output, args: ["visibilityChange"] }] } });
1604
+ function buildRequestOptions(method, payload) {
1605
+ if (!payload || !methodSupportsBody(method)) {
1606
+ return { observe: 'body' };
1607
+ }
1608
+ return {
1609
+ body: payload,
1610
+ observe: 'body',
1611
+ };
1612
+ }
1613
+ function methodSupportsBody(method) {
1614
+ return ['POST', 'PUT', 'PATCH', 'DELETE'].includes(method);
1615
+ }
1616
+ function normalizeMethod(value) {
1617
+ if (!value || typeof value !== 'string') {
1618
+ return null;
1619
+ }
1620
+ const method = value.trim().toUpperCase();
1621
+ return method.length ? method : null;
1622
+ }
1623
+ function normalizePayloadValue(key, value) {
1624
+ if (key === 'attachments') {
1625
+ if (value == null) {
1626
+ return [];
1627
+ }
1628
+ return Array.isArray(value) ? value : [value];
1629
+ }
1630
+ return value;
1631
+ }
1632
+ function resolveDefaultPayloadValue(key) {
1633
+ switch (key) {
1634
+ case 'attachments':
1635
+ return [];
1636
+ case 'delegatedUser':
1637
+ case 'note':
1638
+ case 'reason':
1639
+ return null;
1640
+ case 'values':
1641
+ return [];
1642
+ default:
1643
+ return undefined;
1644
+ }
1645
+ }
1646
+ function isMissingPayloadValue(value) {
1647
+ if (value == null) {
1648
+ return true;
1649
+ }
1650
+ if (typeof value === 'string') {
1651
+ return value.trim().length === 0;
1652
+ }
1653
+ if (Array.isArray(value)) {
1654
+ return value.length === 0;
1655
+ }
1656
+ return false;
1657
+ }
1658
+ function resolveActionSeverity(actionKey, index) {
1659
+ const normalized = actionKey.trim().toLowerCase();
1660
+ if (normalized === 'close' ||
1661
+ normalized === 'delete' ||
1662
+ normalized === 'reject' ||
1663
+ normalized === 'cancel') {
1664
+ return 'danger';
1665
+ }
1666
+ return index === 0 ? 'primary' : 'secondary';
1667
+ }
1668
+ function resolveActionVariant(actionKey, index) {
1669
+ const severity = resolveActionSeverity(actionKey, index);
1670
+ return severity === 'primary' ? undefined : 'outlined';
1671
+ }
1672
+ function resolveEnvelopeErrorMessage(response, fallback) {
1673
+ return (response?.errors?.message ??
1674
+ response?.message ??
1675
+ fallback);
1676
+ }
1677
+ function resolveActionSuccessMessage(response, fallback) {
1678
+ if (!response || typeof response !== 'object') {
1679
+ return fallback;
1680
+ }
1681
+ const apiResponse = response;
1682
+ if (typeof apiResponse.code === 'number' && apiResponse.code !== 1) {
1683
+ throw new Error(resolveEnvelopeErrorMessage(apiResponse, fallback));
1684
+ }
1685
+ return apiResponse.message?.trim() || fallback;
1686
+ }
1687
+ function resolveHttpErrorMessage(error, fallback) {
1688
+ if (!error || typeof error !== 'object') {
1689
+ return fallback;
1690
+ }
1691
+ const httpError = error;
1692
+ return httpError.error?.message ?? httpError.message ?? fallback;
1693
+ }
1694
+
1695
+ class WorkCenterItemModalRoute {
1696
+ http = inject(HttpClient);
1697
+ router = inject(Router);
1698
+ route = inject(ActivatedRoute);
1699
+ modal = inject(ModalService);
1700
+ contextKey = input(null, ...(ngDevMode ? [{ debugName: "contextKey" }] : []));
1701
+ drawerVisible = signal(true, ...(ngDevMode ? [{ debugName: "drawerVisible" }] : []));
1702
+ loading = signal(false, ...(ngDevMode ? [{ debugName: "loading" }] : []));
1703
+ error = signal(null, ...(ngDevMode ? [{ debugName: "error" }] : []));
1704
+ details = signal(null, ...(ngDevMode ? [{ debugName: "details" }] : []));
1705
+ detailsReloadTick = signal(0, ...(ngDevMode ? [{ debugName: "detailsReloadTick" }] : []));
1706
+ footerActionsMounted = signal(false, ...(ngDevMode ? [{ debugName: "footerActionsMounted" }] : []));
1707
+ resolvedContextKey = computed(() => this.decodeContextKey(this.contextKey()), ...(ngDevMode ? [{ debugName: "resolvedContextKey" }] : []));
1708
+ drawerTitle = computed(() => this.readDisplayField(this.details()?.item, 'title') ?? 'Item Details', ...(ngDevMode ? [{ debugName: "drawerTitle" }] : []));
1709
+ drawerSubtitle = computed(() => this.readDisplayField(this.details()?.item, 'subtitle') ?? '', ...(ngDevMode ? [{ debugName: "drawerSubtitle" }] : []));
1710
+ constructor() {
1711
+ effect(() => {
1712
+ this.footerActionsMounted.set(!!this.resolvedContextKey());
1713
+ });
1714
+ effect((onCleanup) => {
1715
+ const contextKey = this.resolvedContextKey();
1716
+ const reloadTick = this.detailsReloadTick();
1717
+ console.log('[work-center-debug] modal route load effect', {
1718
+ contextKey,
1719
+ reloadTick,
1720
+ });
1721
+ this.error.set(null);
1722
+ this.details.set(null);
1723
+ if (!contextKey) {
1724
+ this.loading.set(false);
1725
+ console.log('[work-center-debug] modal route load skipped, no context key');
1726
+ return;
1727
+ }
1728
+ this.loading.set(true);
1729
+ console.log('[work-center-debug] modal route fetch start', {
1730
+ endpoint: 'workcenter/runtime/item-basic-info',
1731
+ contextKey,
1732
+ });
1733
+ const subscription = this.http
1734
+ .get('workcenter/runtime/item-basic-info', {
1735
+ params: { contextKey },
1736
+ })
1737
+ .pipe(map((response) => {
1738
+ if (response.code === 1) {
1739
+ return response.data;
1740
+ }
1741
+ throw new Error(this.resolveErrorMessage(response));
1742
+ }))
1743
+ .subscribe({
1744
+ next: (details) => {
1745
+ console.log('[work-center-debug] modal route fetch success', {
1746
+ contextKey,
1747
+ details,
1748
+ });
1749
+ this.details.set(details);
1750
+ this.loading.set(false);
1751
+ },
1752
+ error: (error) => {
1753
+ console.error('[work-center-debug] modal route fetch failed', {
1754
+ contextKey,
1755
+ error,
1756
+ });
1757
+ this.error.set(error instanceof Error
1758
+ ? error.message
1759
+ : 'Failed to load item details.');
1760
+ this.loading.set(false);
1761
+ },
1762
+ });
1763
+ onCleanup(() => subscription.unsubscribe());
1764
+ });
1765
+ }
1766
+ onActionExecuted() {
1767
+ this.detailsReloadTick.update((value) => value + 1);
1768
+ }
1769
+ onFooterVisibilityChange(visible) {
1770
+ this.footerActionsMounted.set(visible);
1771
+ }
1772
+ async onVisibleChange(visible) {
1773
+ this.drawerVisible.set(visible);
1774
+ console.log('[work-center-debug] modal route visible change', { visible });
1775
+ if (visible) {
1776
+ return;
1777
+ }
1778
+ await this.closeRouteOutlet();
1779
+ }
1780
+ closeRouteOutlet() {
1781
+ console.log('[work-center-debug] modal route closing', {
1782
+ outlet: this.route.outlet || 'sidebar',
1783
+ parentUrl: this.route.parent?.snapshot.url.map((segment) => segment.path),
1784
+ });
1785
+ return this.router
1786
+ .navigate([{ outlets: { [this.route.outlet || 'sidebar']: null } }], {
1787
+ relativeTo: this.route.parent,
1788
+ queryParamsHandling: 'merge',
1789
+ replaceUrl: true,
1790
+ })
1791
+ .then((success) => {
1792
+ console.log('[work-center-debug] modal route close navigate result', {
1793
+ success,
1794
+ currentUrl: this.router.url,
1795
+ });
1796
+ return success;
1797
+ });
1798
+ }
1799
+ decodeContextKey(value) {
1800
+ if (!value) {
1801
+ return null;
1802
+ }
1803
+ try {
1804
+ return decodeURIComponent(value);
1805
+ }
1806
+ catch {
1807
+ return value;
1808
+ }
1809
+ }
1810
+ resolveErrorMessage(response) {
1811
+ const fallbackError = 'Failed to load item details.';
1812
+ return (response.errors?.message ??
1813
+ response.message ??
1814
+ fallbackError);
1815
+ }
1816
+ readDisplayField(item, key) {
1817
+ const field = item?.[key];
1818
+ if (typeof field === 'string' && field.trim().length) {
1819
+ return field;
1820
+ }
1821
+ if (!field || typeof field !== 'object') {
1822
+ return null;
1823
+ }
1824
+ const display = field.display;
1825
+ return typeof display === 'string' && display.trim().length
1826
+ ? display
1827
+ : null;
1828
+ }
1829
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: WorkCenterItemModalRoute, deps: [], target: i0.ɵɵFactoryTarget.Component });
1830
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", type: WorkCenterItemModalRoute, isStandalone: true, selector: "mt-work-center-item-modal-route", inputs: { contextKey: { classPropertyName: "contextKey", publicName: "contextKey", isSignal: true, isRequired: false, transformFunction: null } }, providers: [WorkCenterItemActionContextStore], ngImport: i0, template: "<mt-drawer\n [visible]=\"drawerVisible()\"\n [loadingHeader]=\"loading()\"\n [title]=\"drawerTitle()\"\n [subtitle]=\"drawerSubtitle()\"\n [appendTo]=\"'system-content'\"\n styleClass=\"!w-[90%] !absolute \"\n position=\"right\"\n (visibleChange)=\"onVisibleChange($event)\"\n>\n <ng-container content>\n @if (loading()) {\n <div class=\"p-4\" [class]=\"modal.contentClass\">\n <div class=\"flex min-h-[22rem] flex-col gap-5\">\n <div class=\"flex items-center gap-3\">\n <p-skeleton shape=\"circle\" size=\"3rem\" />\n <div class=\"flex flex-1 flex-col gap-2\">\n <p-skeleton width=\"12rem\" height=\"1rem\" />\n <p-skeleton width=\"8rem\" height=\"0.875rem\" />\n </div>\n </div>\n\n <div class=\"grid gap-3 md:grid-cols-2\">\n @for (_ of [1, 2, 3, 4]; track $index) {\n <p-skeleton height=\"5rem\" borderRadius=\"1rem\" />\n }\n </div>\n\n <p-skeleton height=\"16rem\" borderRadius=\"1rem\" />\n </div>\n </div>\n } @else if (error(); as errorMessage) {\n <div class=\"p-4\" [class]=\"modal.contentClass\">\n <div class=\"flex min-h-[22rem] items-center justify-center\">\n <p class=\"max-w-xl text-sm font-medium text-red-600\">\n {{ errorMessage }}\n </p>\n </div>\n </div>\n } @else if (details(); as details) {\n <mt-work-center-item-modal [details]=\"details\" />\n } @else {\n <div class=\"p-4\" [class]=\"modal.contentClass\">\n <div class=\"flex min-h-[22rem] items-center justify-center\">\n <p class=\"text-sm font-medium text-surface-500\">No item selected.</p>\n </div>\n </div>\n }\n </ng-container>\n\n @if (footerActionsMounted()) {\n <div footer [class]=\"modal.footerClass\">\n <mt-work-center-item-modal-footer-actions\n [contextKey]=\"resolvedContextKey()\"\n (actionExecuted)=\"onActionExecuted()\"\n (visibilityChange)=\"onFooterVisibilityChange($event)\"\n />\n </div>\n }\n</mt-drawer>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: Drawer, selector: "mt-drawer", inputs: ["visible", "position", "fullScreen", "closeOnEscape", "blockScroll", "dismissible", "title", "subtitle", "loadingHeader", "styleClass", "transitionOptions", "appendTo", "modal"], outputs: ["visibleChange", "onShow", "onHide"] }, { kind: "ngmodule", type: SkeletonModule }, { kind: "component", type: i1.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "shape", "animation", "borderRadius", "size", "width", "height"] }, { kind: "component", type: WorkCenterItemModal, selector: "mt-work-center-item-modal", inputs: ["details"] }, { kind: "component", type: WorkCenterItemModalFooterActions, selector: "mt-work-center-item-modal-footer-actions", inputs: ["contextKey"], outputs: ["actionExecuted", "visibilityChange"] }] });
1831
+ }
1832
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: WorkCenterItemModalRoute, decorators: [{
1833
+ type: Component,
1834
+ args: [{ selector: 'mt-work-center-item-modal-route', standalone: true, imports: [
1835
+ CommonModule,
1836
+ Drawer,
1837
+ SkeletonModule,
1838
+ WorkCenterItemModal,
1839
+ WorkCenterItemModalFooterActions,
1840
+ ], providers: [WorkCenterItemActionContextStore], template: "<mt-drawer\n [visible]=\"drawerVisible()\"\n [loadingHeader]=\"loading()\"\n [title]=\"drawerTitle()\"\n [subtitle]=\"drawerSubtitle()\"\n [appendTo]=\"'system-content'\"\n styleClass=\"!w-[90%] !absolute \"\n position=\"right\"\n (visibleChange)=\"onVisibleChange($event)\"\n>\n <ng-container content>\n @if (loading()) {\n <div class=\"p-4\" [class]=\"modal.contentClass\">\n <div class=\"flex min-h-[22rem] flex-col gap-5\">\n <div class=\"flex items-center gap-3\">\n <p-skeleton shape=\"circle\" size=\"3rem\" />\n <div class=\"flex flex-1 flex-col gap-2\">\n <p-skeleton width=\"12rem\" height=\"1rem\" />\n <p-skeleton width=\"8rem\" height=\"0.875rem\" />\n </div>\n </div>\n\n <div class=\"grid gap-3 md:grid-cols-2\">\n @for (_ of [1, 2, 3, 4]; track $index) {\n <p-skeleton height=\"5rem\" borderRadius=\"1rem\" />\n }\n </div>\n\n <p-skeleton height=\"16rem\" borderRadius=\"1rem\" />\n </div>\n </div>\n } @else if (error(); as errorMessage) {\n <div class=\"p-4\" [class]=\"modal.contentClass\">\n <div class=\"flex min-h-[22rem] items-center justify-center\">\n <p class=\"max-w-xl text-sm font-medium text-red-600\">\n {{ errorMessage }}\n </p>\n </div>\n </div>\n } @else if (details(); as details) {\n <mt-work-center-item-modal [details]=\"details\" />\n } @else {\n <div class=\"p-4\" [class]=\"modal.contentClass\">\n <div class=\"flex min-h-[22rem] items-center justify-center\">\n <p class=\"text-sm font-medium text-surface-500\">No item selected.</p>\n </div>\n </div>\n }\n </ng-container>\n\n @if (footerActionsMounted()) {\n <div footer [class]=\"modal.footerClass\">\n <mt-work-center-item-modal-footer-actions\n [contextKey]=\"resolvedContextKey()\"\n (actionExecuted)=\"onActionExecuted()\"\n (visibilityChange)=\"onFooterVisibilityChange($event)\"\n />\n </div>\n }\n</mt-drawer>\n" }]
1841
+ }], ctorParameters: () => [], propDecorators: { contextKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "contextKey", required: false }] }] } });
795
1842
 
796
1843
  const APP_STATES = [WorkCenterState];
797
1844
 
@@ -799,5 +1846,5 @@ const APP_STATES = [WorkCenterState];
799
1846
  * Generated bundle index. Do not edit.
800
1847
  */
801
1848
 
802
- export { APP_STATES, EnterArea, HydrateFromContext, LoadRuntime, SetParams, WORK_CENTER_MAX_FILTERS, WORK_CENTER_MAX_PAGE_SIZE, WORK_CENTER_MAX_SORT, WORK_CENTER_QUERY_VERSION, WorkCenterActionKey, WorkCenterFacade, WorkCenterPage, WorkCenterState };
1849
+ export { APP_STATES, EnterArea, HydrateFromContext, LoadRuntime, SetParams, WORK_CENTER_MAX_FILTERS, WORK_CENTER_MAX_PAGE_SIZE, WORK_CENTER_MAX_SORT, WORK_CENTER_QUERY_VERSION, WorkCenterActionKey, WorkCenterFacade, WorkCenterItemModal, WorkCenterItemModalRoute, WorkCenterPage, WorkCenterState };
803
1850
  //# sourceMappingURL=masterteam-work-center.mjs.map