@masterteam/client-components 0.0.8 → 0.0.10

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.
@@ -3,7 +3,8 @@ import { inject, Injectable, signal, computed, input, output, Component, effect,
3
3
  import * as i1 from '@angular/common';
4
4
  import { CommonModule } from '@angular/common';
5
5
  import { Button } from '@masterteam/components/button';
6
- import { HttpClient, HttpParams } from '@angular/common/http';
6
+ import { HttpClient } from '@angular/common/http';
7
+ import { switchMap, of, map } from 'rxjs';
7
8
  import { Card } from '@masterteam/components/card';
8
9
  import { Table } from '@masterteam/components/table';
9
10
  import * as i2 from 'primeng/skeleton';
@@ -13,34 +14,81 @@ import { DashboardViewer } from '@masterteam/dashboard-builder';
13
14
 
14
15
  class ClientListApiService {
15
16
  http = inject(HttpClient);
16
- tablesBaseUrl = 'data/tables';
17
- cardsBaseUrl = 'data/modules/cards';
17
+ runtimeFetchBaseUrl = 'fetch/query';
18
+ tableSettingsBaseUrl = 'table-display-settings';
18
19
  informativeBaseUrl = 'Modules';
19
- getRows(levelId, levelDataId, moduleId, query) {
20
- const endpoint = `${this.tablesBaseUrl}/level/${levelId}/level-data/${levelDataId}/module/${moduleId}/rows`;
21
- let params = new HttpParams()
22
- .set('mode', query.mode)
23
- .set('skip', query.skip)
24
- .set('take', query.take);
25
- (query.columnKeys ?? []).forEach((columnKey) => {
26
- params = params.append('columnKeys', columnKey);
27
- });
28
- return this.http.get(endpoint, {
29
- params,
30
- });
20
+ getRows(contextKey, instanceId, query) {
21
+ return this.resolveTablePropertyKeys(contextKey, query).pipe(switchMap((propertyKeys) => this.queryRuntime({
22
+ contextKey,
23
+ projection: 'Table',
24
+ surfaceKey: 'table',
25
+ propertyKeys: propertyKeys?.length ? propertyKeys : undefined,
26
+ filters: this.buildInstanceFilters(instanceId),
27
+ page: this.toPage(query.skip, query.take),
28
+ pageSize: query.take,
29
+ })));
31
30
  }
32
- getCards(levelDataId, moduleId) {
33
- const params = new HttpParams()
34
- .set('moduleId', moduleId)
35
- .set('levelDataId', levelDataId)
36
- .append('displayAreas', 'card');
37
- return this.http.get(this.cardsBaseUrl, {
38
- params,
31
+ getCards(contextKey, instanceId) {
32
+ return this.queryRuntime({
33
+ contextKey,
34
+ projection: 'Card',
35
+ filters: instanceId != null ? this.buildInstanceFilters(instanceId) : undefined,
36
+ display: { areas: ['card'] },
39
37
  });
40
38
  }
41
39
  getInformativeDashboard(moduleId) {
42
40
  return this.http.get(`${this.informativeBaseUrl}/${moduleId}/dashboard`);
43
41
  }
42
+ queryRuntime(payload) {
43
+ return this.http.post(this.runtimeFetchBaseUrl, payload);
44
+ }
45
+ resolveTablePropertyKeys(contextKey, query) {
46
+ if (query.columnKeys?.length) {
47
+ return of(query.columnKeys);
48
+ }
49
+ const context = this.readTableSettingsContext(contextKey);
50
+ if (!context) {
51
+ return of(undefined);
52
+ }
53
+ return this.http
54
+ .get(`${this.tableSettingsBaseUrl}/level/${context.levelId}/module/${context.moduleId}/catalog`)
55
+ .pipe(map((response) => this.toEffectiveColumnKeys(response.data?.effectiveColumns ?? [])));
56
+ }
57
+ buildInstanceFilters(instanceId) {
58
+ return [
59
+ {
60
+ key: 'levelDataId',
61
+ operator: 'Equals',
62
+ value: String(instanceId),
63
+ },
64
+ ];
65
+ }
66
+ toPage(skip, take) {
67
+ if (take <= 0) {
68
+ return 1;
69
+ }
70
+ return Math.floor(skip / take) + 1;
71
+ }
72
+ toEffectiveColumnKeys(columns) {
73
+ const keys = (columns ?? [])
74
+ .filter((column) => typeof column === 'string' ? true : column.visible !== false)
75
+ .sort((a, b) => (typeof a === 'string' ? 0 : (a.order ?? 0)) -
76
+ (typeof b === 'string' ? 0 : (b.order ?? 0)))
77
+ .map((column) => typeof column === 'string'
78
+ ? column
79
+ : (column.key ?? column.propertyKey ?? ''))
80
+ .filter((key) => key.length > 0);
81
+ return keys.length ? keys : undefined;
82
+ }
83
+ readTableSettingsContext(contextKey) {
84
+ const [levelPart, modulePart] = contextKey.split('/');
85
+ const levelId = levelPart?.split(':')[1];
86
+ const moduleId = modulePart?.split(':')[1];
87
+ if (!levelId || !moduleId) {
88
+ return null;
89
+ }
90
+ return { levelId, moduleId };
91
+ }
44
92
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: ClientListApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
45
93
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: ClientListApiService, providedIn: 'root' });
46
94
  }
@@ -52,7 +100,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImpor
52
100
  class ClientListStateService {
53
101
  itemsByKey = signal({}, ...(ngDevMode ? [{ debugName: "itemsByKey" }] : []));
54
102
  items = computed(() => Object.values(this.itemsByKey()).sort((a, b) => a.config.moduleId === b.config.moduleId
55
- ? (a.config.levelDataId ?? 0) - (b.config.levelDataId ?? 0)
103
+ ? (a.config.instanceId ?? 0) - (b.config.instanceId ?? 0)
56
104
  : a.config.moduleId - b.config.moduleId), ...(ngDevMode ? [{ debugName: "items" }] : []));
57
105
  setConfigs(configs) {
58
106
  const currentItems = this.itemsByKey();
@@ -93,10 +141,31 @@ class ClientListStateService {
93
141
  target.areaType !== 'table') {
94
142
  return;
95
143
  }
96
- const visibleColumns = this.getVisibleColumns(response.columns);
97
- const columns = this.toColumnDefs(visibleColumns);
98
- const rows = this.toDisplayRows(response.rows ?? [], visibleColumns);
99
- const title = (config.title ?? '').trim() || response.moduleKey;
144
+ const columns = (response.projectionMeta?.columns ?? [])
145
+ .filter((column) => column.visible)
146
+ .sort((a, b) => a.order - b.order)
147
+ .map((column) => ({
148
+ key: column.key,
149
+ label: column.label,
150
+ type: 'entity',
151
+ viewType: column.viewType,
152
+ order: column.order,
153
+ visible: column.visible,
154
+ configuration: column.configuration ?? null,
155
+ }));
156
+ const propertiesByKey = new Map((response.catalog?.properties ?? []).map((property) => [
157
+ property.normalizedKey,
158
+ property,
159
+ ]));
160
+ const rows = (response.records ?? []).map((record) => {
161
+ const row = { Id: record.id };
162
+ columns.forEach((column) => {
163
+ row[column.key] = this.toEntityData(column.key, record.values?.[column.key], propertiesByKey.get(column.key), column.viewType, column.label, column.configuration, record.id);
164
+ });
165
+ return row;
166
+ });
167
+ const schema = response.schemas?.find((item) => item.id === response.records?.[0]?.schemaId);
168
+ const title = (config.title ?? '').trim() || schema?.name || schema?.key || '';
100
169
  const totalCount = config.isPaginated === false
101
170
  ? rows.length
102
171
  : (response.pagination?.totalCount ?? rows.length);
@@ -105,7 +174,7 @@ class ClientListStateService {
105
174
  [key]: {
106
175
  ...target,
107
176
  title,
108
- moduleKey: response.moduleKey ?? '',
177
+ moduleKey: schema?.key ?? '',
109
178
  columns,
110
179
  rows,
111
180
  cards: [],
@@ -125,10 +194,10 @@ class ClientListStateService {
125
194
  return;
126
195
  }
127
196
  const cards = this.toCards(response);
128
- const firstModule = response.details?.[0]?.module;
129
- const moduleKey = firstModule?.key ?? target.moduleKey ?? '';
197
+ const firstCardModule = cards[0]?.module;
198
+ const moduleKey = firstCardModule?.key ?? target.moduleKey ?? '';
130
199
  const title = (config.title ?? '').trim() ||
131
- firstModule?.name ||
200
+ firstCardModule?.name ||
132
201
  moduleKey ||
133
202
  target.title;
134
203
  this.itemsByKey.set({
@@ -157,7 +226,6 @@ class ClientListStateService {
157
226
  const dashboardData = response
158
227
  ? this.toDashboardData(response, title)
159
228
  : null;
160
- const chartsCount = dashboardData?.charts?.length ?? 0;
161
229
  this.itemsByKey.set({
162
230
  ...this.itemsByKey(),
163
231
  [key]: {
@@ -169,7 +237,7 @@ class ClientListStateService {
169
237
  cards: [],
170
238
  skip: 0,
171
239
  take: 0,
172
- totalCount: chartsCount,
240
+ totalCount: dashboardData?.charts?.length ?? 0,
173
241
  dashboardData,
174
242
  error: null,
175
243
  rawData: response,
@@ -211,48 +279,92 @@ class ClientListStateService {
211
279
  rawData: existing.rawData,
212
280
  };
213
281
  }
214
- getVisibleColumns(columns) {
215
- return (columns ?? [])
216
- .filter((column) => column.isVisible)
217
- .sort((a, b) => a.order - b.order);
218
- }
219
- toColumnDefs(columns) {
220
- return columns.map((column) => ({
221
- ...column,
222
- key: column.columnKey,
223
- label: column.name || this.toLabel(column.columnKey),
224
- runtimeType: column.type,
225
- type: 'entity',
226
- }));
282
+ toCards(data) {
283
+ const records = this.orderRecords(data?.records ?? [], data?.projectionMeta?.groups ?? []);
284
+ const propertiesByKey = new Map((data?.catalog?.properties ?? []).map((property) => [
285
+ property.normalizedKey,
286
+ property,
287
+ ]));
288
+ const displayOrder = [...(data?.projectionMeta?.displayOrder ?? [])].sort((a, b) => a.order - b.order);
289
+ return records.map((record) => {
290
+ const schema = data?.schemas?.find((item) => item.id === record.schemaId);
291
+ const propertyKeys = displayOrder.length
292
+ ? displayOrder.map((item) => item.propertyKey)
293
+ : (data?.catalog?.properties ?? [])
294
+ .filter((property) => record.values?.[property.normalizedKey] !== undefined)
295
+ .sort((a, b) => a.order - b.order)
296
+ .map((property) => property.normalizedKey);
297
+ const keys = propertyKeys.length
298
+ ? propertyKeys
299
+ : Object.keys(record.values ?? {});
300
+ const properties = keys.map((propertyKey) => {
301
+ const property = propertiesByKey.get(propertyKey);
302
+ const displayConfig = displayOrder.find((item) => item.propertyKey === propertyKey);
303
+ return this.toEntityData(propertyKey, record.values?.[propertyKey], property, property?.viewType, property?.label, displayConfig?.configuration ?? property?.configuration ?? null, record.id);
304
+ });
305
+ return {
306
+ id: record.id,
307
+ name: record.name ?? `Record ${record.id}`,
308
+ module: schema
309
+ ? {
310
+ id: schema.id,
311
+ key: schema.key,
312
+ name: schema.name,
313
+ order: schema.order,
314
+ typeGroup: schema.typeGroup,
315
+ }
316
+ : undefined,
317
+ instanceId: record.id,
318
+ properties,
319
+ entities: properties,
320
+ };
321
+ });
227
322
  }
228
- /**
229
- * Enriches each flat API row so that every cell value becomes a
230
- * RuntimeTableCell carrying both the original value and the full
231
- * column metadata. The `Id` key is preserved as-is.
232
- */
233
- toDisplayRows(apiRows, columns) {
234
- return apiRows.map((row) => {
235
- const displayRow = { Id: row['Id'] };
236
- for (const col of columns) {
237
- displayRow[col.columnKey] = this.toCell(row[col.columnKey], col);
323
+ orderRecords(records, groups) {
324
+ if (!groups.length) {
325
+ return records;
326
+ }
327
+ const recordsById = new Map(records.map((record) => [record.id, record]));
328
+ const ordered = [];
329
+ const seen = new Set();
330
+ groups.forEach((group) => {
331
+ group.recordIds.forEach((recordId) => {
332
+ const record = recordsById.get(recordId);
333
+ if (!record || seen.has(recordId)) {
334
+ return;
335
+ }
336
+ ordered.push(record);
337
+ seen.add(recordId);
338
+ });
339
+ });
340
+ records.forEach((record) => {
341
+ if (!seen.has(record.id)) {
342
+ ordered.push(record);
238
343
  }
239
- return displayRow;
240
344
  });
345
+ return ordered;
241
346
  }
242
- toCell(value, column) {
243
- return { ...column, value };
244
- }
245
- toCards(data) {
246
- const details = data?.details ?? [];
247
- if (details.length === 0) {
248
- return [];
249
- }
250
- const configurationByKey = this.buildDisplayConfigurationLookup(data?.displayConfigurations ?? []);
251
- return details.flatMap((detailsBlock) => (detailsBlock.data ?? []).map((card) => ({
252
- ...card,
253
- module: card.module ?? detailsBlock.module,
254
- entities: this.mapEntities(card.properties ?? [], configurationByKey),
255
- })));
347
+ toEntityData(propertyKey, cell, property, viewType, label, configuration, recordId) {
348
+ const resolvedViewType = (viewType ?? 'Text');
349
+ return {
350
+ id: recordId,
351
+ propertyId: property?.id,
352
+ key: property?.key ?? propertyKey,
353
+ normalizedKey: property?.normalizedKey ?? propertyKey,
354
+ name: label ?? property?.label ?? propertyKey,
355
+ rawValue: cell?.raw === null || cell?.raw === undefined
356
+ ? undefined
357
+ : String(cell.raw),
358
+ value: resolvedViewType === 'Status' ||
359
+ resolvedViewType === 'Lookup' ||
360
+ resolvedViewType === 'User' ||
361
+ resolvedViewType === 'Attachment'
362
+ ? cell?.value
363
+ : String(cell?.display ?? cell?.value ?? cell?.raw ?? ''),
364
+ viewType: resolvedViewType,
365
+ type: property?.source,
366
+ configuration: configuration,
367
+ };
256
368
  }
257
369
  toDashboardData(data, title) {
258
370
  const charts = (data.chartLinks ?? []).map((link) => {
@@ -280,55 +392,6 @@ class ClientListStateService {
280
392
  filters: [],
281
393
  };
282
394
  }
283
- buildDisplayConfigurationLookup(displayConfigurations) {
284
- const configurationsByProperty = new Map();
285
- for (const displayConfiguration of displayConfigurations) {
286
- for (const propertyConfiguration of displayConfiguration.properties ??
287
- []) {
288
- if (propertyConfiguration.areaKey !== 'card' ||
289
- !propertyConfiguration.propertyKey) {
290
- continue;
291
- }
292
- configurationsByProperty.set(propertyConfiguration.propertyKey.toLowerCase(), propertyConfiguration);
293
- }
294
- }
295
- return configurationsByProperty;
296
- }
297
- mapEntities(properties, configurationByKey) {
298
- return properties
299
- .map((property, index) => {
300
- const propertyConfiguration = this.getPropertyConfiguration(property, configurationByKey);
301
- return {
302
- ...property,
303
- rawValue: property.rawValue ?? undefined,
304
- order: propertyConfiguration?.order ?? index + 1,
305
- configuration: propertyConfiguration?.configuration ?? { size: 4 },
306
- };
307
- })
308
- .sort((first, second) => (first.order ?? 0) - (second.order ?? 0));
309
- }
310
- getPropertyConfiguration(property, configurationByKey) {
311
- const propertyKeys = [property.normalizedKey, property.key, property.name];
312
- for (const propertyKey of propertyKeys) {
313
- if (!propertyKey) {
314
- continue;
315
- }
316
- const configuration = configurationByKey.get(propertyKey.toLowerCase());
317
- if (configuration) {
318
- return configuration;
319
- }
320
- }
321
- return undefined;
322
- }
323
- toLabel(key) {
324
- return key
325
- .replace(/([a-z])([A-Z])/g, '$1 $2')
326
- .replace(/[_-]+/g, ' ')
327
- .split(' ')
328
- .filter(Boolean)
329
- .map((word) => word[0].toUpperCase() + word.slice(1))
330
- .join(' ');
331
- }
332
395
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: ClientListStateService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
333
396
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: ClientListStateService });
334
397
  }
@@ -341,6 +404,7 @@ class ClientListTableView {
341
404
  state = input.required(...(ngDevMode ? [{ debugName: "state" }] : []));
342
405
  rowActions = input([], ...(ngDevMode ? [{ debugName: "rowActions" }] : []));
343
406
  lazyLoad = output();
407
+ rowClick = output();
344
408
  table = computed(() => {
345
409
  return this.state();
346
410
  }, ...(ngDevMode ? [{ debugName: "table" }] : []));
@@ -359,23 +423,24 @@ class ClientListTableView {
359
423
  };
360
424
  }
361
425
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: ClientListTableView, deps: [], target: i0.ɵɵFactoryTarget.Component });
362
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", type: ClientListTableView, isStandalone: true, selector: "mt-client-list-table-view", inputs: { state: { classPropertyName: "state", publicName: "state", isSignal: true, isRequired: true, transformFunction: null }, rowActions: { classPropertyName: "rowActions", publicName: "rowActions", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { lazyLoad: "lazyLoad" }, ngImport: i0, template: "<div class=\"grid gap-4\" [style.gridTemplateColumns]=\"gridTemplateColumns\">\n @if (table().config.contentStart) {\n <div [style.gridColumn]=\"slotGridSpan(table().config.layout.startSpan)\">\n <ng-container\n [ngTemplateOutlet]=\"table().config.contentStart\"\n [ngTemplateOutletContext]=\"templateContext(table())\"\n />\n </div>\n }\n\n <div [style.gridColumn]=\"slotGridSpan(table().config.layout.tableSpan)\">\n <mt-card>\n @if (table().loading && table().rows.length === 0) {\n <div class=\"flex flex-col gap-3 py-3\">\n <p-skeleton height=\"3rem\" />\n <p-skeleton height=\"3rem\" />\n <p-skeleton height=\"3rem\" />\n </div>\n } @else if (table().error) {\n <div\n class=\"rounded-lg border border-red-200 bg-red-50 p-3 text-sm text-red-700\"\n >\n {{ table().error }}\n </div>\n } @else {\n <mt-table\n [data]=\"table().rows\"\n [columns]=\"table().columns\"\n [rowActions]=\"rowActions()\"\n [loading]=\"table().loading\"\n [lazy]=\"table().config.isPaginated\"\n [lazyTotalRecords]=\"table().totalCount\"\n [pageSize]=\"table().config.isPaginated ? table().take : 10\"\n (lazyLoad)=\"lazyLoad.emit($event)\"\n />\n }\n </mt-card>\n </div>\n\n @if (table().config.contentEnd) {\n <div [style.gridColumn]=\"slotGridSpan(table().config.layout.endSpan)\">\n <ng-container\n [ngTemplateOutlet]=\"table().config.contentEnd\"\n [ngTemplateOutletContext]=\"templateContext(table())\"\n />\n </div>\n }\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: Card, selector: "mt-card", inputs: ["class", "title", "paddingless"] }, { 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: i2.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "shape", "animation", "borderRadius", "size", "width", "height"] }] });
426
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", type: ClientListTableView, isStandalone: true, selector: "mt-client-list-table-view", inputs: { state: { classPropertyName: "state", publicName: "state", isSignal: true, isRequired: true, transformFunction: null }, rowActions: { classPropertyName: "rowActions", publicName: "rowActions", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { lazyLoad: "lazyLoad", rowClick: "rowClick" }, ngImport: i0, template: "<div class=\"grid gap-4\" [style.gridTemplateColumns]=\"gridTemplateColumns\">\r\n @if (table().config.contentStart) {\r\n <div [style.gridColumn]=\"slotGridSpan(table().config.layout.startSpan)\">\r\n <ng-container\r\n [ngTemplateOutlet]=\"table().config.contentStart\"\r\n [ngTemplateOutletContext]=\"templateContext(table())\"\r\n />\r\n </div>\r\n }\r\n\r\n <div [style.gridColumn]=\"slotGridSpan(table().config.layout.tableSpan)\">\r\n <mt-card>\r\n @if (table().loading && table().rows.length === 0) {\r\n <div class=\"flex flex-col gap-3 py-3\">\r\n <p-skeleton height=\"3rem\" />\r\n <p-skeleton height=\"3rem\" />\r\n <p-skeleton height=\"3rem\" />\r\n </div>\r\n } @else if (table().error) {\r\n <div\r\n class=\"rounded-lg border border-red-200 bg-red-50 p-3 text-sm text-red-700\"\r\n >\r\n {{ table().error }}\r\n </div>\r\n } @else {\r\n <mt-table\r\n [data]=\"table().rows\"\r\n [columns]=\"table().columns\"\r\n [rowActions]=\"rowActions()\"\r\n [loading]=\"table().loading\"\r\n [clickableRows]=\"true\"\r\n [lazy]=\"table().config.isPaginated\"\r\n [lazyTotalRecords]=\"table().totalCount\"\r\n [pageSize]=\"table().config.isPaginated ? table().take : 10\"\r\n (lazyLoad)=\"lazyLoad.emit($event)\"\r\n (rowClick)=\"rowClick.emit($event)\"\r\n />\r\n }\r\n </mt-card>\r\n </div>\r\n\r\n @if (table().config.contentEnd) {\r\n <div [style.gridColumn]=\"slotGridSpan(table().config.layout.endSpan)\">\r\n <ng-container\r\n [ngTemplateOutlet]=\"table().config.contentEnd\"\r\n [ngTemplateOutletContext]=\"templateContext(table())\"\r\n />\r\n </div>\r\n }\r\n</div>\r\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: Card, selector: "mt-card", inputs: ["class", "title", "paddingless"] }, { 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: i2.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "shape", "animation", "borderRadius", "size", "width", "height"] }] });
363
427
  }
364
428
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: ClientListTableView, decorators: [{
365
429
  type: Component,
366
- args: [{ selector: 'mt-client-list-table-view', standalone: true, imports: [CommonModule, Card, Table, SkeletonModule], template: "<div class=\"grid gap-4\" [style.gridTemplateColumns]=\"gridTemplateColumns\">\n @if (table().config.contentStart) {\n <div [style.gridColumn]=\"slotGridSpan(table().config.layout.startSpan)\">\n <ng-container\n [ngTemplateOutlet]=\"table().config.contentStart\"\n [ngTemplateOutletContext]=\"templateContext(table())\"\n />\n </div>\n }\n\n <div [style.gridColumn]=\"slotGridSpan(table().config.layout.tableSpan)\">\n <mt-card>\n @if (table().loading && table().rows.length === 0) {\n <div class=\"flex flex-col gap-3 py-3\">\n <p-skeleton height=\"3rem\" />\n <p-skeleton height=\"3rem\" />\n <p-skeleton height=\"3rem\" />\n </div>\n } @else if (table().error) {\n <div\n class=\"rounded-lg border border-red-200 bg-red-50 p-3 text-sm text-red-700\"\n >\n {{ table().error }}\n </div>\n } @else {\n <mt-table\n [data]=\"table().rows\"\n [columns]=\"table().columns\"\n [rowActions]=\"rowActions()\"\n [loading]=\"table().loading\"\n [lazy]=\"table().config.isPaginated\"\n [lazyTotalRecords]=\"table().totalCount\"\n [pageSize]=\"table().config.isPaginated ? table().take : 10\"\n (lazyLoad)=\"lazyLoad.emit($event)\"\n />\n }\n </mt-card>\n </div>\n\n @if (table().config.contentEnd) {\n <div [style.gridColumn]=\"slotGridSpan(table().config.layout.endSpan)\">\n <ng-container\n [ngTemplateOutlet]=\"table().config.contentEnd\"\n [ngTemplateOutletContext]=\"templateContext(table())\"\n />\n </div>\n }\n</div>\n" }]
367
- }], propDecorators: { state: [{ type: i0.Input, args: [{ isSignal: true, alias: "state", required: true }] }], rowActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowActions", required: false }] }], lazyLoad: [{ type: i0.Output, args: ["lazyLoad"] }] } });
430
+ args: [{ selector: 'mt-client-list-table-view', standalone: true, imports: [CommonModule, Card, Table, SkeletonModule], template: "<div class=\"grid gap-4\" [style.gridTemplateColumns]=\"gridTemplateColumns\">\r\n @if (table().config.contentStart) {\r\n <div [style.gridColumn]=\"slotGridSpan(table().config.layout.startSpan)\">\r\n <ng-container\r\n [ngTemplateOutlet]=\"table().config.contentStart\"\r\n [ngTemplateOutletContext]=\"templateContext(table())\"\r\n />\r\n </div>\r\n }\r\n\r\n <div [style.gridColumn]=\"slotGridSpan(table().config.layout.tableSpan)\">\r\n <mt-card>\r\n @if (table().loading && table().rows.length === 0) {\r\n <div class=\"flex flex-col gap-3 py-3\">\r\n <p-skeleton height=\"3rem\" />\r\n <p-skeleton height=\"3rem\" />\r\n <p-skeleton height=\"3rem\" />\r\n </div>\r\n } @else if (table().error) {\r\n <div\r\n class=\"rounded-lg border border-red-200 bg-red-50 p-3 text-sm text-red-700\"\r\n >\r\n {{ table().error }}\r\n </div>\r\n } @else {\r\n <mt-table\r\n [data]=\"table().rows\"\r\n [columns]=\"table().columns\"\r\n [rowActions]=\"rowActions()\"\r\n [loading]=\"table().loading\"\r\n [clickableRows]=\"true\"\r\n [lazy]=\"table().config.isPaginated\"\r\n [lazyTotalRecords]=\"table().totalCount\"\r\n [pageSize]=\"table().config.isPaginated ? table().take : 10\"\r\n (lazyLoad)=\"lazyLoad.emit($event)\"\r\n (rowClick)=\"rowClick.emit($event)\"\r\n />\r\n }\r\n </mt-card>\r\n </div>\r\n\r\n @if (table().config.contentEnd) {\r\n <div [style.gridColumn]=\"slotGridSpan(table().config.layout.endSpan)\">\r\n <ng-container\r\n [ngTemplateOutlet]=\"table().config.contentEnd\"\r\n [ngTemplateOutletContext]=\"templateContext(table())\"\r\n />\r\n </div>\r\n }\r\n</div>\r\n" }]
431
+ }], propDecorators: { state: [{ type: i0.Input, args: [{ isSignal: true, alias: "state", required: true }] }], rowActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowActions", required: false }] }], lazyLoad: [{ type: i0.Output, args: ["lazyLoad"] }], rowClick: [{ type: i0.Output, args: ["rowClick"] }] } });
368
432
 
369
433
  class ClientListCardsView {
370
434
  state = input.required(...(ngDevMode ? [{ debugName: "state" }] : []));
435
+ cardClick = output();
371
436
  cardsState = computed(() => this.state(), ...(ngDevMode ? [{ debugName: "cardsState" }] : []));
372
437
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: ClientListCardsView, deps: [], target: i0.ɵɵFactoryTarget.Component });
373
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", type: ClientListCardsView, isStandalone: true, selector: "mt-client-list-cards-view", inputs: { state: { classPropertyName: "state", publicName: "state", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "@if (cardsState().loading && cardsState().cards.length === 0) {\r\n <div class=\"grid grid-cols-1 gap-6 md:grid-cols-2 xl:grid-cols-3\">\r\n @for (i of [1, 2, 3, 4, 5, 6]; track i) {\r\n <p-skeleton height=\"20rem\" borderRadius=\"1rem\" />\r\n }\r\n </div>\r\n} @else if (cardsState().error) {\r\n <div class=\"rounded-lg border border-red-200 bg-red-50 p-6 text-red-600\">\r\n {{ cardsState().error }}\r\n </div>\r\n} @else if (cardsState().cards.length === 0) {\r\n <div class=\"p-6 text-center text-gray-400\">No cards found.</div>\r\n} @else {\r\n <div class=\"grid grid-cols-1 gap-5 md:grid-cols-2 xl:grid-cols-3\">\r\n @for (card of cardsState().cards; track card.id) {\r\n <mt-card class=\"cursor-pointer p-3\">\r\n <ng-template #headless>\r\n <mt-entities-preview [entities]=\"card.entities\" />\r\n </ng-template>\r\n </mt-card>\r\n }\r\n </div>\r\n}\r\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: Card, selector: "mt-card", inputs: ["class", "title", "paddingless"] }, { kind: "component", type: EntitiesPreview, selector: "mt-entities-preview", inputs: ["entities"] }, { kind: "ngmodule", type: SkeletonModule }, { kind: "component", type: i2.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "shape", "animation", "borderRadius", "size", "width", "height"] }] });
438
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", type: ClientListCardsView, isStandalone: true, selector: "mt-client-list-cards-view", inputs: { state: { classPropertyName: "state", publicName: "state", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { cardClick: "cardClick" }, ngImport: i0, template: "@if (cardsState().loading && cardsState().cards.length === 0) {\r\n <div class=\"grid grid-cols-1 gap-6 md:grid-cols-2 xl:grid-cols-3\">\r\n @for (i of [1, 2, 3, 4, 5, 6]; track i) {\r\n <p-skeleton height=\"20rem\" borderRadius=\"1rem\" />\r\n }\r\n </div>\r\n} @else if (cardsState().error) {\r\n <div class=\"rounded-lg border border-red-200 bg-red-50 p-6 text-red-600\">\r\n {{ cardsState().error }}\r\n </div>\r\n} @else if (cardsState().cards.length === 0) {\r\n <div class=\"p-6 text-center text-gray-400\">No cards found.</div>\r\n} @else {\n <div class=\"grid grid-cols-1 gap-5 md:grid-cols-2 xl:grid-cols-3\">\n @for (card of cardsState().cards; track card.id) {\n <mt-card class=\"cursor-pointer p-3\" (click)=\"cardClick.emit(card)\">\n <ng-template #headless>\n <mt-entities-preview [entities]=\"card.entities\" />\n </ng-template>\n </mt-card>\n }\n </div>\n}\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: Card, selector: "mt-card", inputs: ["class", "title", "paddingless"] }, { kind: "component", type: EntitiesPreview, selector: "mt-entities-preview", inputs: ["entities"] }, { kind: "ngmodule", type: SkeletonModule }, { kind: "component", type: i2.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "shape", "animation", "borderRadius", "size", "width", "height"] }] });
374
439
  }
375
440
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: ClientListCardsView, decorators: [{
376
441
  type: Component,
377
- args: [{ selector: 'mt-client-list-cards-view', standalone: true, imports: [CommonModule, Card, EntitiesPreview, SkeletonModule], template: "@if (cardsState().loading && cardsState().cards.length === 0) {\r\n <div class=\"grid grid-cols-1 gap-6 md:grid-cols-2 xl:grid-cols-3\">\r\n @for (i of [1, 2, 3, 4, 5, 6]; track i) {\r\n <p-skeleton height=\"20rem\" borderRadius=\"1rem\" />\r\n }\r\n </div>\r\n} @else if (cardsState().error) {\r\n <div class=\"rounded-lg border border-red-200 bg-red-50 p-6 text-red-600\">\r\n {{ cardsState().error }}\r\n </div>\r\n} @else if (cardsState().cards.length === 0) {\r\n <div class=\"p-6 text-center text-gray-400\">No cards found.</div>\r\n} @else {\r\n <div class=\"grid grid-cols-1 gap-5 md:grid-cols-2 xl:grid-cols-3\">\r\n @for (card of cardsState().cards; track card.id) {\r\n <mt-card class=\"cursor-pointer p-3\">\r\n <ng-template #headless>\r\n <mt-entities-preview [entities]=\"card.entities\" />\r\n </ng-template>\r\n </mt-card>\r\n }\r\n </div>\r\n}\r\n" }]
378
- }], propDecorators: { state: [{ type: i0.Input, args: [{ isSignal: true, alias: "state", required: true }] }] } });
442
+ args: [{ selector: 'mt-client-list-cards-view', standalone: true, imports: [CommonModule, Card, EntitiesPreview, SkeletonModule], template: "@if (cardsState().loading && cardsState().cards.length === 0) {\r\n <div class=\"grid grid-cols-1 gap-6 md:grid-cols-2 xl:grid-cols-3\">\r\n @for (i of [1, 2, 3, 4, 5, 6]; track i) {\r\n <p-skeleton height=\"20rem\" borderRadius=\"1rem\" />\r\n }\r\n </div>\r\n} @else if (cardsState().error) {\r\n <div class=\"rounded-lg border border-red-200 bg-red-50 p-6 text-red-600\">\r\n {{ cardsState().error }}\r\n </div>\r\n} @else if (cardsState().cards.length === 0) {\r\n <div class=\"p-6 text-center text-gray-400\">No cards found.</div>\r\n} @else {\n <div class=\"grid grid-cols-1 gap-5 md:grid-cols-2 xl:grid-cols-3\">\n @for (card of cardsState().cards; track card.id) {\n <mt-card class=\"cursor-pointer p-3\" (click)=\"cardClick.emit(card)\">\n <ng-template #headless>\n <mt-entities-preview [entities]=\"card.entities\" />\n </ng-template>\n </mt-card>\n }\n </div>\n}\n" }]
443
+ }], propDecorators: { state: [{ type: i0.Input, args: [{ isSignal: true, alias: "state", required: true }] }], cardClick: [{ type: i0.Output, args: ["cardClick"] }] } });
379
444
 
380
445
  const DEFAULT_GRID_COLUMNS$1 = 12;
381
446
  class ClientListInformativeView {
@@ -396,11 +461,11 @@ class ClientListInformativeView {
396
461
  };
397
462
  }
398
463
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: ClientListInformativeView, deps: [], target: i0.ɵɵFactoryTarget.Component });
399
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", type: ClientListInformativeView, isStandalone: true, selector: "mt-client-list-informative-view", inputs: { state: { classPropertyName: "state", publicName: "state", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<div class=\"grid gap-4\" [style.gridTemplateColumns]=\"gridTemplateColumns\">\n @if (informativeState().config.contentStart) {\n <div\n [style.gridColumn]=\"\n slotGridSpan(informativeState().config.layout.startSpan)\n \"\n >\n <ng-container\n [ngTemplateOutlet]=\"informativeState().config.contentStart\"\n [ngTemplateOutletContext]=\"templateContext(informativeState())\"\n />\n </div>\n }\n\n <div\n [style.gridColumn]=\"\n slotGridSpan(informativeState().config.layout.tableSpan)\n \"\n >\n <mt-card>\n @if (\n informativeState().loading &&\n !informativeState().dashboardData?.charts?.length\n ) {\n <div class=\"flex flex-col gap-3 py-3\">\n <p-skeleton height=\"6rem\" />\n <p-skeleton height=\"12rem\" />\n <p-skeleton height=\"12rem\" />\n </div>\n } @else if (informativeState().error) {\n <div\n class=\"rounded-lg border border-red-200 bg-red-50 p-3 text-sm text-red-700\"\n >\n {{ informativeState().error }}\n </div>\n } @else if (!informativeState().dashboardData?.charts?.length) {\n <div class=\"p-6 text-center text-gray-400\">\n No informative dashboard found.\n </div>\n } @else {\n <div class=\"min-h-[420px]\">\n <mt-dashboard-viewer\n [isPage]=\"false\"\n [showFilters]=\"false\"\n [dashboardData]=\"informativeState().dashboardData\"\n />\n </div>\n }\n </mt-card>\n </div>\n\n @if (informativeState().config.contentEnd) {\n <div\n [style.gridColumn]=\"\n slotGridSpan(informativeState().config.layout.endSpan)\n \"\n >\n <ng-container\n [ngTemplateOutlet]=\"informativeState().config.contentEnd\"\n [ngTemplateOutletContext]=\"templateContext(informativeState())\"\n />\n </div>\n }\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: Card, selector: "mt-card", inputs: ["class", "title", "paddingless"] }, { kind: "component", type: DashboardViewer, selector: "mt-dashboard-viewer", inputs: ["isPage", "pageTitle", "backButton", "pageId", "dashboardData", "chartsData", "dialogsData", "filtersData", "showFilters"], outputs: ["pageLoaded", "onBack", "chartClick"] }, { kind: "ngmodule", type: SkeletonModule }, { kind: "component", type: i2.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "shape", "animation", "borderRadius", "size", "width", "height"] }] });
464
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", type: ClientListInformativeView, isStandalone: true, selector: "mt-client-list-informative-view", inputs: { state: { classPropertyName: "state", publicName: "state", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<div class=\"grid gap-4\" [style.gridTemplateColumns]=\"gridTemplateColumns\">\r\n @if (informativeState().config.contentStart) {\r\n <div\r\n [style.gridColumn]=\"\r\n slotGridSpan(informativeState().config.layout.startSpan)\r\n \"\r\n >\r\n <ng-container\r\n [ngTemplateOutlet]=\"informativeState().config.contentStart\"\r\n [ngTemplateOutletContext]=\"templateContext(informativeState())\"\r\n />\r\n </div>\r\n }\r\n\r\n <div\r\n [style.gridColumn]=\"\r\n slotGridSpan(informativeState().config.layout.tableSpan)\r\n \"\r\n >\r\n <mt-card>\r\n @if (\r\n informativeState().loading &&\r\n !informativeState().dashboardData?.charts?.length\r\n ) {\r\n <div class=\"flex flex-col gap-3 py-3\">\r\n <p-skeleton height=\"6rem\" />\r\n <p-skeleton height=\"12rem\" />\r\n <p-skeleton height=\"12rem\" />\r\n </div>\r\n } @else if (informativeState().error) {\r\n <div\r\n class=\"rounded-lg border border-red-200 bg-red-50 p-3 text-sm text-red-700\"\r\n >\r\n {{ informativeState().error }}\r\n </div>\r\n } @else if (!informativeState().dashboardData?.charts?.length) {\r\n <div class=\"p-6 text-center text-gray-400\">\r\n No informative dashboard found.\r\n </div>\r\n } @else {\r\n <div class=\"min-h-[420px]\">\r\n <mt-dashboard-viewer\r\n [isPage]=\"false\"\r\n [showFilters]=\"false\"\r\n [dashboardData]=\"informativeState().dashboardData\"\r\n />\r\n </div>\r\n }\r\n </mt-card>\r\n </div>\r\n\r\n @if (informativeState().config.contentEnd) {\r\n <div\r\n [style.gridColumn]=\"\r\n slotGridSpan(informativeState().config.layout.endSpan)\r\n \"\r\n >\r\n <ng-container\r\n [ngTemplateOutlet]=\"informativeState().config.contentEnd\"\r\n [ngTemplateOutletContext]=\"templateContext(informativeState())\"\r\n />\r\n </div>\r\n }\r\n</div>\r\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: Card, selector: "mt-card", inputs: ["class", "title", "paddingless"] }, { kind: "component", type: DashboardViewer, selector: "mt-dashboard-viewer", inputs: ["isPage", "pageTitle", "backButton", "pageId", "dashboardData", "chartsData", "dialogsData", "filtersData", "showFilters"], outputs: ["pageLoaded", "onBack", "chartClick"] }, { kind: "ngmodule", type: SkeletonModule }, { kind: "component", type: i2.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "shape", "animation", "borderRadius", "size", "width", "height"] }] });
400
465
  }
401
466
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: ClientListInformativeView, decorators: [{
402
467
  type: Component,
403
- args: [{ selector: 'mt-client-list-informative-view', standalone: true, imports: [CommonModule, Card, DashboardViewer, SkeletonModule], template: "<div class=\"grid gap-4\" [style.gridTemplateColumns]=\"gridTemplateColumns\">\n @if (informativeState().config.contentStart) {\n <div\n [style.gridColumn]=\"\n slotGridSpan(informativeState().config.layout.startSpan)\n \"\n >\n <ng-container\n [ngTemplateOutlet]=\"informativeState().config.contentStart\"\n [ngTemplateOutletContext]=\"templateContext(informativeState())\"\n />\n </div>\n }\n\n <div\n [style.gridColumn]=\"\n slotGridSpan(informativeState().config.layout.tableSpan)\n \"\n >\n <mt-card>\n @if (\n informativeState().loading &&\n !informativeState().dashboardData?.charts?.length\n ) {\n <div class=\"flex flex-col gap-3 py-3\">\n <p-skeleton height=\"6rem\" />\n <p-skeleton height=\"12rem\" />\n <p-skeleton height=\"12rem\" />\n </div>\n } @else if (informativeState().error) {\n <div\n class=\"rounded-lg border border-red-200 bg-red-50 p-3 text-sm text-red-700\"\n >\n {{ informativeState().error }}\n </div>\n } @else if (!informativeState().dashboardData?.charts?.length) {\n <div class=\"p-6 text-center text-gray-400\">\n No informative dashboard found.\n </div>\n } @else {\n <div class=\"min-h-[420px]\">\n <mt-dashboard-viewer\n [isPage]=\"false\"\n [showFilters]=\"false\"\n [dashboardData]=\"informativeState().dashboardData\"\n />\n </div>\n }\n </mt-card>\n </div>\n\n @if (informativeState().config.contentEnd) {\n <div\n [style.gridColumn]=\"\n slotGridSpan(informativeState().config.layout.endSpan)\n \"\n >\n <ng-container\n [ngTemplateOutlet]=\"informativeState().config.contentEnd\"\n [ngTemplateOutletContext]=\"templateContext(informativeState())\"\n />\n </div>\n }\n</div>\n" }]
468
+ args: [{ selector: 'mt-client-list-informative-view', standalone: true, imports: [CommonModule, Card, DashboardViewer, SkeletonModule], template: "<div class=\"grid gap-4\" [style.gridTemplateColumns]=\"gridTemplateColumns\">\r\n @if (informativeState().config.contentStart) {\r\n <div\r\n [style.gridColumn]=\"\r\n slotGridSpan(informativeState().config.layout.startSpan)\r\n \"\r\n >\r\n <ng-container\r\n [ngTemplateOutlet]=\"informativeState().config.contentStart\"\r\n [ngTemplateOutletContext]=\"templateContext(informativeState())\"\r\n />\r\n </div>\r\n }\r\n\r\n <div\r\n [style.gridColumn]=\"\r\n slotGridSpan(informativeState().config.layout.tableSpan)\r\n \"\r\n >\r\n <mt-card>\r\n @if (\r\n informativeState().loading &&\r\n !informativeState().dashboardData?.charts?.length\r\n ) {\r\n <div class=\"flex flex-col gap-3 py-3\">\r\n <p-skeleton height=\"6rem\" />\r\n <p-skeleton height=\"12rem\" />\r\n <p-skeleton height=\"12rem\" />\r\n </div>\r\n } @else if (informativeState().error) {\r\n <div\r\n class=\"rounded-lg border border-red-200 bg-red-50 p-3 text-sm text-red-700\"\r\n >\r\n {{ informativeState().error }}\r\n </div>\r\n } @else if (!informativeState().dashboardData?.charts?.length) {\r\n <div class=\"p-6 text-center text-gray-400\">\r\n No informative dashboard found.\r\n </div>\r\n } @else {\r\n <div class=\"min-h-[420px]\">\r\n <mt-dashboard-viewer\r\n [isPage]=\"false\"\r\n [showFilters]=\"false\"\r\n [dashboardData]=\"informativeState().dashboardData\"\r\n />\r\n </div>\r\n }\r\n </mt-card>\r\n </div>\r\n\r\n @if (informativeState().config.contentEnd) {\r\n <div\r\n [style.gridColumn]=\"\r\n slotGridSpan(informativeState().config.layout.endSpan)\r\n \"\r\n >\r\n <ng-container\r\n [ngTemplateOutlet]=\"informativeState().config.contentEnd\"\r\n [ngTemplateOutletContext]=\"templateContext(informativeState())\"\r\n />\r\n </div>\r\n }\r\n</div>\r\n" }]
404
469
  }], propDecorators: { state: [{ type: i0.Input, args: [{ isSignal: true, alias: "state", required: true }] }] } });
405
470
 
406
471
  const DEFAULT_AREA_TYPE = 'table';
@@ -419,6 +484,7 @@ class ClientList {
419
484
  defaultTake = input(DEFAULT_SERVER_PAGE_SIZE, ...(ngDevMode ? [{ debugName: "defaultTake" }] : []));
420
485
  loaded = output();
421
486
  errored = output();
487
+ itemClicked = output();
422
488
  items = this.state.items;
423
489
  subscriptions = new Map();
424
490
  inFlightRequestSignatures = new Map();
@@ -464,6 +530,22 @@ class ClientList {
464
530
  toggleExpanded(key) {
465
531
  this.state.toggleExpanded(key);
466
532
  }
533
+ onTableRowClick(item, row) {
534
+ this.itemClicked.emit({
535
+ key: item.key,
536
+ areaType: 'table',
537
+ item: row,
538
+ state: item,
539
+ });
540
+ }
541
+ onCardClick(item, card) {
542
+ this.itemClicked.emit({
543
+ key: item.key,
544
+ areaType: 'cards',
545
+ item: card,
546
+ state: item,
547
+ });
548
+ }
467
549
  templateContext(item) {
468
550
  return {
469
551
  $implicit: item,
@@ -488,28 +570,31 @@ class ClientList {
488
570
  const previousItems = this.state.itemsByKey();
489
571
  const hasMultipleConfigs = (configs ?? []).length > 1;
490
572
  const normalized = (configs ?? []).map((config, index) => {
491
- const type = this.resolveType(config.type);
573
+ const type = this.resolveType(config);
492
574
  const isInformative = type === 'informative';
575
+ const formConfig = isInformative ? null : this.asFormConfig(config);
493
576
  const areaType = isInformative
494
577
  ? DEFAULT_AREA_TYPE
495
- : this.resolveAreaType(config.areaType);
578
+ : this.resolveAreaType(formConfig?.areaType);
496
579
  const mode = isInformative
497
580
  ? DEFAULT_MODE
498
- : this.resolveMode(config.mode, config.columnKeys);
581
+ : this.resolveMode(formConfig?.mode, formConfig?.columnKeys);
499
582
  const isPaginated = !isInformative &&
500
583
  areaType === 'table' &&
501
- (config.isPaginated ?? true);
584
+ (formConfig?.isPaginated ?? true);
585
+ const showHeader = config.showHeader ?? true;
502
586
  const take = !isInformative && areaType === 'table'
503
587
  ? isPaginated
504
- ? (config.take ?? this.defaultTake())
588
+ ? (formConfig?.take ?? this.defaultTake())
505
589
  : LOCAL_PAGE_SIZE_SOURCE
506
590
  : 0;
507
- const collapseEnabled = (config.collapse?.enabled ?? true) && hasMultipleConfigs;
591
+ const collapseEnabled = showHeader &&
592
+ (config.collapse?.enabled ?? true) &&
593
+ hasMultipleConfigs;
508
594
  const layout = this.resolveLayout(config);
509
- const key = config.key ??
510
- `${config.levelId ?? 'x'}-${config.levelDataId ?? 'x'}-${config.moduleId ?? 'x'}-${index}`;
595
+ const key = config.key ?? this.createItemKey(config, index);
511
596
  const existing = previousItems[key];
512
- const normalizedConfig = this.toNormalizedConfig(config, type, areaType, mode, isPaginated, take, collapseEnabled, layout);
597
+ const normalizedConfig = this.toNormalizedConfig(config, type, areaType, mode, isPaginated, take, showHeader, collapseEnabled, layout);
513
598
  if (isInformative) {
514
599
  return {
515
600
  key,
@@ -607,7 +692,7 @@ class ClientList {
607
692
  this.state.setLoading(key, true);
608
693
  this.state.setError(key, null);
609
694
  const sub = this.api
610
- .getRows(config.levelId, config.levelDataId, config.moduleId, query)
695
+ .getRows(config.contextKey, config.instanceId, query)
611
696
  .subscribe({
612
697
  next: (response) => {
613
698
  let hasLoadedData = false;
@@ -658,7 +743,7 @@ class ClientList {
658
743
  this.state.setLoading(key, true);
659
744
  this.state.setError(key, null);
660
745
  const sub = this.api
661
- .getCards(config.levelDataId, config.moduleId)
746
+ .getCards(config.contextKey, config.instanceId)
662
747
  .subscribe({
663
748
  next: (response) => {
664
749
  let hasLoadedData = false;
@@ -741,26 +826,29 @@ class ClientList {
741
826
  resolveAreaType(areaType) {
742
827
  return areaType === 'cards' ? 'cards' : DEFAULT_AREA_TYPE;
743
828
  }
744
- resolveType(type) {
745
- return type === 'informative' ? 'informative' : DEFAULT_TYPE;
829
+ resolveType(config) {
830
+ return config.type === 'informative' ? 'informative' : DEFAULT_TYPE;
746
831
  }
747
832
  resolveMode(mode, columnKeys) {
748
833
  if (mode !== 'override')
749
834
  return DEFAULT_MODE;
750
835
  return (columnKeys ?? []).length > 0 ? 'override' : DEFAULT_MODE;
751
836
  }
752
- toNormalizedConfig(config, type, areaType, mode, isPaginated, take, collapseEnabled, layout) {
837
+ toNormalizedConfig(config, type, areaType, mode, isPaginated, take, showHeader, collapseEnabled, layout) {
838
+ const informativeConfig = this.asInformativeConfig(config);
839
+ const formConfig = this.asFormConfig(config);
753
840
  return {
754
- levelId: config.levelId,
755
- levelDataId: config.levelDataId,
756
- moduleId: config.moduleId,
841
+ contextKey: formConfig?.contextKey ?? null,
842
+ instanceId: formConfig?.instanceId ?? null,
843
+ moduleId: informativeConfig?.moduleId ?? 0,
757
844
  type,
758
845
  areaType,
759
846
  mode,
760
847
  isPaginated,
761
848
  take,
762
849
  title: config.title,
763
- columnKeys: config.columnKeys ?? [],
850
+ showHeader,
851
+ columnKeys: formConfig?.columnKeys ?? [],
764
852
  headerStart: config.headerStart,
765
853
  headerEnd: config.headerEnd,
766
854
  contentStart: config.contentStart,
@@ -777,6 +865,17 @@ class ClientList {
777
865
  layout,
778
866
  };
779
867
  }
868
+ createItemKey(config, index) {
869
+ if (this.isInformativeConfig(config)) {
870
+ return `informative-${config.moduleId}-${index}`;
871
+ }
872
+ return [
873
+ config.contextKey,
874
+ config.instanceId ?? 'scope',
875
+ config.areaType ?? DEFAULT_AREA_TYPE,
876
+ index,
877
+ ].join('-');
878
+ }
780
879
  resolveLayout(config) {
781
880
  const hasStart = !!config.contentStart;
782
881
  const hasEnd = !!config.contentEnd;
@@ -832,9 +931,8 @@ class ClientList {
832
931
  return [
833
932
  config.type,
834
933
  config.areaType,
835
- config.levelId,
836
- config.levelDataId,
837
- config.moduleId,
934
+ config.contextKey ?? 'x',
935
+ config.instanceId ?? 'scope',
838
936
  query.mode,
839
937
  query.skip,
840
938
  query.take,
@@ -845,9 +943,9 @@ class ClientList {
845
943
  return [
846
944
  config.type,
847
945
  config.areaType,
848
- config.levelDataId,
849
- config.moduleId,
850
- 'card',
946
+ config.contextKey ?? 'x',
947
+ config.instanceId ?? 'scope',
948
+ 'Card',
851
949
  ].join('|');
852
950
  }
853
951
  createInformativeRequestSignature(config) {
@@ -857,27 +955,34 @@ class ClientList {
857
955
  if (config.type === 'informative') {
858
956
  return Number.isFinite(config.moduleId) && config.moduleId > 0;
859
957
  }
860
- const hasLevelDataId = Number.isFinite(config.levelDataId) && config.levelDataId > 0;
861
- const hasModuleId = Number.isFinite(config.moduleId) && config.moduleId > 0;
958
+ if (!config.contextKey?.trim()) {
959
+ return false;
960
+ }
862
961
  if (config.areaType === 'cards') {
863
- return hasLevelDataId && hasModuleId;
962
+ return true;
864
963
  }
865
- return (Number.isFinite(config.levelId) &&
866
- config.levelId > 0 &&
867
- hasLevelDataId &&
868
- hasModuleId);
964
+ const instanceId = config.instanceId;
965
+ return instanceId != null && Number.isFinite(instanceId) && instanceId > 0;
869
966
  }
870
967
  getMissingIdentifiersMessage(config) {
871
968
  if (config.type === 'informative') {
872
969
  return 'Missing identifiers: moduleId is required';
873
970
  }
874
- if (config.areaType === 'cards') {
875
- return 'Missing identifiers: levelDataId and moduleId are required';
876
- }
877
- return 'Missing identifiers: levelId, levelDataId and moduleId are required';
971
+ return config.areaType === 'cards'
972
+ ? 'Missing identifiers: contextKey is required'
973
+ : 'Missing identifiers: contextKey and instanceId are required';
974
+ }
975
+ isInformativeConfig(config) {
976
+ return config.type === 'informative';
977
+ }
978
+ asInformativeConfig(config) {
979
+ return this.isInformativeConfig(config) ? config : null;
980
+ }
981
+ asFormConfig(config) {
982
+ return this.isInformativeConfig(config) ? null : config;
878
983
  }
879
984
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: ClientList, deps: [], target: i0.ɵɵFactoryTarget.Component });
880
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", type: ClientList, isStandalone: true, selector: "mt-client-list", inputs: { configurations: { classPropertyName: "configurations", publicName: "configurations", isSignal: true, isRequired: true, transformFunction: null }, defaultTake: { classPropertyName: "defaultTake", publicName: "defaultTake", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { loaded: "loaded", errored: "errored" }, providers: [ClientListStateService], ngImport: i0, template: "<div class=\"flex flex-col gap-4\">\n @for (item of items(); track item.key) {\n <section class=\"flex flex-col gap-4\">\n <div class=\"flex w-full items-center gap-2\">\n <div class=\"flex min-w-0 flex-1 items-center gap-2\">\n @if (item.config.collapse.enabled) {\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n [icon]=\"\n item.expanded\n ? item.config.collapse.collapseIcon\n : item.config.collapse.expandIcon\n \"\n (onClick)=\"toggleExpanded(item.key)\"\n />\n }\n <h3 class=\"m-0 text-lg font-bold\">\n {{ item.title || item.moduleKey || defaultTitle(item) }}\n </h3>\n @if (item.config.headerStart) {\n <div class=\"flex items-center gap-2\">\n <ng-container\n [ngTemplateOutlet]=\"item.config.headerStart\"\n [ngTemplateOutletContext]=\"templateContext(item)\"\n />\n </div>\n }\n </div>\n @if (item.config.headerEnd) {\n <div class=\"ml-auto flex shrink-0 items-center gap-2\">\n <ng-container\n [ngTemplateOutlet]=\"item.config.headerEnd\"\n [ngTemplateOutletContext]=\"templateContext(item)\"\n />\n </div>\n }\n </div>\n\n @if (item.expanded || !item.config.collapse.enabled) {\n @if (item.config.templateContent) {\n <ng-container\n [ngTemplateOutlet]=\"item.config.templateContent\"\n [ngTemplateOutletContext]=\"templateContext(item)\"\n />\n } @else if (item.type === \"informative\") {\n <mt-client-list-informative-view [state]=\"item\" />\n } @else if (item.areaType === \"table\") {\n <mt-client-list-table-view\n [state]=\"item\"\n [rowActions]=\"item.config.rowActions\"\n (lazyLoad)=\"onLazyLoad(item.key, $event)\"\n />\n } @else {\n <mt-client-list-cards-view [state]=\"item\" />\n }\n }\n </section>\n }\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: ClientListTableView, selector: "mt-client-list-table-view", inputs: ["state", "rowActions"], outputs: ["lazyLoad"] }, { kind: "component", type: ClientListCardsView, selector: "mt-client-list-cards-view", inputs: ["state"] }, { kind: "component", type: ClientListInformativeView, selector: "mt-client-list-informative-view", inputs: ["state"] }] });
985
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", type: ClientList, isStandalone: true, selector: "mt-client-list", inputs: { configurations: { classPropertyName: "configurations", publicName: "configurations", isSignal: true, isRequired: true, transformFunction: null }, defaultTake: { classPropertyName: "defaultTake", publicName: "defaultTake", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { loaded: "loaded", errored: "errored", itemClicked: "itemClicked" }, providers: [ClientListStateService], ngImport: i0, template: "<div class=\"flex flex-col gap-4\">\r\n @for (item of items(); track item.key) {\r\n <section class=\"flex flex-col gap-4\">\r\n @if (item.config.showHeader) {\r\n <div class=\"flex w-full items-center gap-2\">\r\n <div class=\"flex min-w-0 flex-1 items-center gap-2\">\r\n @if (item.config.collapse.enabled) {\r\n <mt-button\r\n variant=\"text\"\r\n severity=\"secondary\"\r\n [icon]=\"\r\n item.expanded\r\n ? item.config.collapse.collapseIcon\r\n : item.config.collapse.expandIcon\r\n \"\r\n (onClick)=\"toggleExpanded(item.key)\"\r\n />\r\n }\r\n <h3 class=\"m-0 text-lg font-bold\">\r\n {{ item.title || item.moduleKey || defaultTitle(item) }}\r\n </h3>\r\n @if (item.config.headerStart) {\r\n <div class=\"flex items-center gap-2\">\r\n <ng-container\r\n [ngTemplateOutlet]=\"item.config.headerStart\"\r\n [ngTemplateOutletContext]=\"templateContext(item)\"\r\n />\r\n </div>\r\n }\r\n </div>\r\n @if (item.config.headerEnd) {\r\n <div class=\"ml-auto flex shrink-0 items-center gap-2\">\r\n <ng-container\r\n [ngTemplateOutlet]=\"item.config.headerEnd\"\r\n [ngTemplateOutletContext]=\"templateContext(item)\"\r\n />\r\n </div>\r\n }\r\n </div>\r\n }\r\n\r\n @if (item.expanded || !item.config.collapse.enabled) {\r\n @if (item.config.templateContent) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"item.config.templateContent\"\r\n [ngTemplateOutletContext]=\"templateContext(item)\"\r\n />\r\n } @else if (item.type === \"informative\") {\r\n <mt-client-list-informative-view [state]=\"item\" />\r\n } @else if (item.areaType === \"table\") {\r\n <mt-client-list-table-view\r\n [state]=\"item\"\r\n [rowActions]=\"item.config.rowActions\"\r\n (lazyLoad)=\"onLazyLoad(item.key, $event)\"\r\n (rowClick)=\"onTableRowClick(item, $event)\"\r\n />\r\n } @else {\r\n <mt-client-list-cards-view\r\n [state]=\"item\"\r\n (cardClick)=\"onCardClick(item, $event)\"\r\n />\r\n }\r\n }\r\n </section>\r\n }\r\n</div>\r\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: ClientListTableView, selector: "mt-client-list-table-view", inputs: ["state", "rowActions"], outputs: ["lazyLoad", "rowClick"] }, { kind: "component", type: ClientListCardsView, selector: "mt-client-list-cards-view", inputs: ["state"], outputs: ["cardClick"] }, { kind: "component", type: ClientListInformativeView, selector: "mt-client-list-informative-view", inputs: ["state"] }] });
881
986
  }
882
987
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: ClientList, decorators: [{
883
988
  type: Component,
@@ -887,8 +992,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImpor
887
992
  ClientListTableView,
888
993
  ClientListCardsView,
889
994
  ClientListInformativeView,
890
- ], providers: [ClientListStateService], template: "<div class=\"flex flex-col gap-4\">\n @for (item of items(); track item.key) {\n <section class=\"flex flex-col gap-4\">\n <div class=\"flex w-full items-center gap-2\">\n <div class=\"flex min-w-0 flex-1 items-center gap-2\">\n @if (item.config.collapse.enabled) {\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n [icon]=\"\n item.expanded\n ? item.config.collapse.collapseIcon\n : item.config.collapse.expandIcon\n \"\n (onClick)=\"toggleExpanded(item.key)\"\n />\n }\n <h3 class=\"m-0 text-lg font-bold\">\n {{ item.title || item.moduleKey || defaultTitle(item) }}\n </h3>\n @if (item.config.headerStart) {\n <div class=\"flex items-center gap-2\">\n <ng-container\n [ngTemplateOutlet]=\"item.config.headerStart\"\n [ngTemplateOutletContext]=\"templateContext(item)\"\n />\n </div>\n }\n </div>\n @if (item.config.headerEnd) {\n <div class=\"ml-auto flex shrink-0 items-center gap-2\">\n <ng-container\n [ngTemplateOutlet]=\"item.config.headerEnd\"\n [ngTemplateOutletContext]=\"templateContext(item)\"\n />\n </div>\n }\n </div>\n\n @if (item.expanded || !item.config.collapse.enabled) {\n @if (item.config.templateContent) {\n <ng-container\n [ngTemplateOutlet]=\"item.config.templateContent\"\n [ngTemplateOutletContext]=\"templateContext(item)\"\n />\n } @else if (item.type === \"informative\") {\n <mt-client-list-informative-view [state]=\"item\" />\n } @else if (item.areaType === \"table\") {\n <mt-client-list-table-view\n [state]=\"item\"\n [rowActions]=\"item.config.rowActions\"\n (lazyLoad)=\"onLazyLoad(item.key, $event)\"\n />\n } @else {\n <mt-client-list-cards-view [state]=\"item\" />\n }\n }\n </section>\n }\n</div>\n" }]
891
- }], ctorParameters: () => [], propDecorators: { configurations: [{ type: i0.Input, args: [{ isSignal: true, alias: "configurations", required: true }] }], defaultTake: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultTake", required: false }] }], loaded: [{ type: i0.Output, args: ["loaded"] }], errored: [{ type: i0.Output, args: ["errored"] }] } });
995
+ ], providers: [ClientListStateService], template: "<div class=\"flex flex-col gap-4\">\r\n @for (item of items(); track item.key) {\r\n <section class=\"flex flex-col gap-4\">\r\n @if (item.config.showHeader) {\r\n <div class=\"flex w-full items-center gap-2\">\r\n <div class=\"flex min-w-0 flex-1 items-center gap-2\">\r\n @if (item.config.collapse.enabled) {\r\n <mt-button\r\n variant=\"text\"\r\n severity=\"secondary\"\r\n [icon]=\"\r\n item.expanded\r\n ? item.config.collapse.collapseIcon\r\n : item.config.collapse.expandIcon\r\n \"\r\n (onClick)=\"toggleExpanded(item.key)\"\r\n />\r\n }\r\n <h3 class=\"m-0 text-lg font-bold\">\r\n {{ item.title || item.moduleKey || defaultTitle(item) }}\r\n </h3>\r\n @if (item.config.headerStart) {\r\n <div class=\"flex items-center gap-2\">\r\n <ng-container\r\n [ngTemplateOutlet]=\"item.config.headerStart\"\r\n [ngTemplateOutletContext]=\"templateContext(item)\"\r\n />\r\n </div>\r\n }\r\n </div>\r\n @if (item.config.headerEnd) {\r\n <div class=\"ml-auto flex shrink-0 items-center gap-2\">\r\n <ng-container\r\n [ngTemplateOutlet]=\"item.config.headerEnd\"\r\n [ngTemplateOutletContext]=\"templateContext(item)\"\r\n />\r\n </div>\r\n }\r\n </div>\r\n }\r\n\r\n @if (item.expanded || !item.config.collapse.enabled) {\r\n @if (item.config.templateContent) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"item.config.templateContent\"\r\n [ngTemplateOutletContext]=\"templateContext(item)\"\r\n />\r\n } @else if (item.type === \"informative\") {\r\n <mt-client-list-informative-view [state]=\"item\" />\r\n } @else if (item.areaType === \"table\") {\r\n <mt-client-list-table-view\r\n [state]=\"item\"\r\n [rowActions]=\"item.config.rowActions\"\r\n (lazyLoad)=\"onLazyLoad(item.key, $event)\"\r\n (rowClick)=\"onTableRowClick(item, $event)\"\r\n />\r\n } @else {\r\n <mt-client-list-cards-view\r\n [state]=\"item\"\r\n (cardClick)=\"onCardClick(item, $event)\"\r\n />\r\n }\r\n }\r\n </section>\r\n }\r\n</div>\r\n" }]
996
+ }], ctorParameters: () => [], propDecorators: { configurations: [{ type: i0.Input, args: [{ isSignal: true, alias: "configurations", required: true }] }], defaultTake: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultTake", required: false }] }], loaded: [{ type: i0.Output, args: ["loaded"] }], errored: [{ type: i0.Output, args: ["errored"] }], itemClicked: [{ type: i0.Output, args: ["itemClicked"] }] } });
892
997
 
893
998
  /**
894
999
  * Generated bundle index. Do not edit.