@masterteam/client-components 0.0.3 → 0.0.5

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,19 +1,23 @@
1
1
  import * as i0 from '@angular/core';
2
- import { inject, Injectable, signal, computed, input, output, effect, untracked, Component } from '@angular/core';
2
+ import { inject, Injectable, signal, computed, input, output, Component, effect, untracked } from '@angular/core';
3
3
  import * as i1 from '@angular/common';
4
4
  import { CommonModule } from '@angular/common';
5
- import { Table } from '@masterteam/components/table';
6
5
  import { Button } from '@masterteam/components/button';
7
- import * as i2 from 'primeng/skeleton';
8
- import { SkeletonModule } from 'primeng/skeleton';
9
6
  import { HttpClient, HttpParams } from '@angular/common/http';
10
7
  import { Card } from '@masterteam/components/card';
8
+ import { Table } from '@masterteam/components/table';
9
+ import * as i2 from 'primeng/skeleton';
10
+ import { SkeletonModule } from 'primeng/skeleton';
11
+ import { EntitiesPreview } from '@masterteam/components/entities';
12
+ import { DashboardViewer } from '@masterteam/dashboard-builder';
11
13
 
12
14
  class ClientListApiService {
13
15
  http = inject(HttpClient);
14
- baseUrl = 'data/tables';
16
+ tablesBaseUrl = 'data/tables';
17
+ cardsBaseUrl = 'data/modules/cards';
18
+ informativeBaseUrl = 'Modules';
15
19
  getRows(levelId, levelDataId, moduleId, query) {
16
- const endpoint = `${this.baseUrl}/level/${levelId}/level-data/${levelDataId}/module/${moduleId}/rows`;
20
+ const endpoint = `${this.tablesBaseUrl}/level/${levelId}/level-data/${levelDataId}/module/${moduleId}/rows`;
17
21
  let params = new HttpParams()
18
22
  .set('mode', query.mode)
19
23
  .set('skip', query.skip)
@@ -25,6 +29,18 @@ class ClientListApiService {
25
29
  params,
26
30
  });
27
31
  }
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,
39
+ });
40
+ }
41
+ getInformativeDashboard(moduleId) {
42
+ return this.http.get(`${this.informativeBaseUrl}/${moduleId}/dashboard`);
43
+ }
28
44
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: ClientListApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
29
45
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: ClientListApiService, providedIn: 'root' });
30
46
  }
@@ -34,36 +50,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImpor
34
50
  }] });
35
51
 
36
52
  class ClientListStateService {
37
- tablesByKey = signal({}, ...(ngDevMode ? [{ debugName: "tablesByKey" }] : []));
38
- tables = computed(() => Object.values(this.tablesByKey()).sort((a, b) => a.config.moduleId === b.config.moduleId
39
- ? a.config.levelDataId - b.config.levelDataId
40
- : a.config.moduleId - b.config.moduleId), ...(ngDevMode ? [{ debugName: "tables" }] : []));
53
+ itemsByKey = signal({}, ...(ngDevMode ? [{ debugName: "itemsByKey" }] : []));
54
+ items = computed(() => Object.values(this.itemsByKey()).sort((a, b) => a.config.moduleId === b.config.moduleId
55
+ ? (a.config.levelDataId ?? 0) - (b.config.levelDataId ?? 0)
56
+ : a.config.moduleId - b.config.moduleId), ...(ngDevMode ? [{ debugName: "items" }] : []));
41
57
  setConfigs(configs) {
58
+ const currentItems = this.itemsByKey();
42
59
  const next = {};
43
- configs.forEach((table) => {
44
- const current = this.tablesByKey()[table.key];
45
- next[table.key] = {
46
- ...table,
47
- loading: current?.loading ?? false,
48
- error: current?.error ?? null,
49
- title: current?.title ?? table.title,
50
- moduleKey: current?.moduleKey ?? table.moduleKey,
51
- columns: current?.columns ?? table.columns,
52
- rows: current?.rows ?? table.rows,
53
- skip: current?.skip ?? table.skip,
54
- take: current?.take ?? table.take,
55
- totalCount: current?.totalCount ?? table.totalCount,
56
- expanded: current?.expanded ?? table.expanded,
57
- };
60
+ configs.forEach((item) => {
61
+ next[item.key] = this.mergeItemState(item, currentItems[item.key]);
58
62
  });
59
- this.tablesByKey.set(next);
63
+ this.itemsByKey.set(next);
60
64
  }
61
65
  setLoading(key, loading) {
62
- const target = this.tablesByKey()[key];
66
+ const target = this.itemsByKey()[key];
63
67
  if (!target)
64
68
  return;
65
- this.tablesByKey.set({
66
- ...this.tablesByKey(),
69
+ this.itemsByKey.set({
70
+ ...this.itemsByKey(),
67
71
  [key]: {
68
72
  ...target,
69
73
  loading,
@@ -71,11 +75,11 @@ class ClientListStateService {
71
75
  });
72
76
  }
73
77
  setError(key, message) {
74
- const target = this.tablesByKey()[key];
78
+ const target = this.itemsByKey()[key];
75
79
  if (!target)
76
80
  return;
77
- this.tablesByKey.set({
78
- ...this.tablesByKey(),
81
+ this.itemsByKey.set({
82
+ ...this.itemsByKey(),
79
83
  [key]: {
80
84
  ...target,
81
85
  error: message,
@@ -83,23 +87,28 @@ class ClientListStateService {
83
87
  });
84
88
  }
85
89
  setRowsResult(key, response, config, skip, take) {
86
- const target = this.tablesByKey()[key];
87
- if (!target)
90
+ const target = this.itemsByKey()[key];
91
+ if (!target ||
92
+ target.type === 'informative' ||
93
+ target.areaType !== 'table') {
88
94
  return;
89
- const columns = this.toColumnDefs(response.columns);
90
- const rows = response.rows ?? [];
95
+ }
96
+ const visibleColumns = this.getVisibleColumns(response.columns);
97
+ const columns = this.toColumnDefs(visibleColumns);
98
+ const rows = this.toDisplayRows(response.rows ?? [], visibleColumns);
91
99
  const title = (config.title ?? '').trim() || response.moduleKey;
92
100
  const totalCount = config.isPaginated === false
93
101
  ? rows.length
94
102
  : (response.pagination?.totalCount ?? rows.length);
95
- this.tablesByKey.set({
96
- ...this.tablesByKey(),
103
+ this.itemsByKey.set({
104
+ ...this.itemsByKey(),
97
105
  [key]: {
98
106
  ...target,
99
107
  title,
100
108
  moduleKey: response.moduleKey ?? '',
101
109
  columns,
102
110
  rows,
111
+ cards: [],
103
112
  skip,
104
113
  take,
105
114
  totalCount,
@@ -107,27 +116,206 @@ class ClientListStateService {
107
116
  },
108
117
  });
109
118
  }
119
+ setCardsResult(key, response, config) {
120
+ const target = this.itemsByKey()[key];
121
+ if (!target ||
122
+ target.type === 'informative' ||
123
+ target.areaType !== 'cards') {
124
+ return;
125
+ }
126
+ const cards = this.toCards(response);
127
+ const firstModule = response.details?.[0]?.module;
128
+ const moduleKey = firstModule?.key ?? target.moduleKey ?? '';
129
+ const title = (config.title ?? '').trim() ||
130
+ firstModule?.name ||
131
+ moduleKey ||
132
+ target.title;
133
+ this.itemsByKey.set({
134
+ ...this.itemsByKey(),
135
+ [key]: {
136
+ ...target,
137
+ title,
138
+ moduleKey,
139
+ columns: [],
140
+ rows: [],
141
+ cards,
142
+ skip: 0,
143
+ take: 0,
144
+ totalCount: cards.length,
145
+ error: null,
146
+ },
147
+ });
148
+ }
149
+ setInformativeResult(key, response, config) {
150
+ const target = this.itemsByKey()[key];
151
+ if (!target || target.type !== 'informative')
152
+ return;
153
+ const moduleKey = target.moduleKey || `Module ${config.moduleId}`;
154
+ const title = (config.title ?? '').trim() || target.title || 'Informative Dashboard';
155
+ const dashboardData = response
156
+ ? this.toDashboardData(response, title)
157
+ : null;
158
+ const chartsCount = dashboardData?.charts?.length ?? 0;
159
+ this.itemsByKey.set({
160
+ ...this.itemsByKey(),
161
+ [key]: {
162
+ ...target,
163
+ title,
164
+ moduleKey,
165
+ columns: [],
166
+ rows: [],
167
+ cards: [],
168
+ skip: 0,
169
+ take: 0,
170
+ totalCount: chartsCount,
171
+ dashboardData,
172
+ error: null,
173
+ },
174
+ });
175
+ }
110
176
  toggleExpanded(key) {
111
- const target = this.tablesByKey()[key];
177
+ const target = this.itemsByKey()[key];
112
178
  if (!target)
113
179
  return;
114
- this.tablesByKey.set({
115
- ...this.tablesByKey(),
180
+ this.itemsByKey.set({
181
+ ...this.itemsByKey(),
116
182
  [key]: {
117
183
  ...target,
118
184
  expanded: !target.expanded,
119
185
  },
120
186
  });
121
187
  }
122
- toColumnDefs(columns) {
188
+ mergeItemState(item, existing) {
189
+ if (!existing ||
190
+ existing.type !== item.type ||
191
+ existing.areaType !== item.areaType) {
192
+ return item;
193
+ }
194
+ return {
195
+ ...item,
196
+ loading: existing.loading,
197
+ error: existing.error,
198
+ title: item.title || existing.title,
199
+ moduleKey: existing.moduleKey || item.moduleKey,
200
+ totalCount: existing.totalCount,
201
+ columns: existing.columns,
202
+ rows: existing.rows,
203
+ cards: existing.cards,
204
+ skip: existing.skip,
205
+ take: existing.take,
206
+ expanded: existing.expanded,
207
+ dashboardData: existing.dashboardData,
208
+ };
209
+ }
210
+ getVisibleColumns(columns) {
123
211
  return (columns ?? [])
124
212
  .filter((column) => column.isVisible)
125
- .sort((a, b) => a.order - b.order)
126
- .map((column) => ({
213
+ .sort((a, b) => a.order - b.order);
214
+ }
215
+ toColumnDefs(columns) {
216
+ return columns.map((column) => ({
217
+ ...column,
127
218
  key: column.columnKey,
128
- label: this.toLabel(column.columnKey),
219
+ label: column.name || this.toLabel(column.columnKey),
220
+ runtimeType: column.type,
221
+ type: 'entity',
129
222
  }));
130
223
  }
224
+ /**
225
+ * Enriches each flat API row so that every cell value becomes a
226
+ * RuntimeTableCell carrying both the original value and the full
227
+ * column metadata. The `Id` key is preserved as-is.
228
+ */
229
+ toDisplayRows(apiRows, columns) {
230
+ return apiRows.map((row) => {
231
+ const displayRow = { Id: row['Id'] };
232
+ for (const col of columns) {
233
+ displayRow[col.columnKey] = this.toCell(row[col.columnKey], col);
234
+ }
235
+ return displayRow;
236
+ });
237
+ }
238
+ toCell(value, column) {
239
+ return { ...column, value };
240
+ }
241
+ toCards(data) {
242
+ const details = data?.details ?? [];
243
+ if (details.length === 0) {
244
+ return [];
245
+ }
246
+ const configurationByKey = this.buildDisplayConfigurationLookup(data?.displayConfigurations ?? []);
247
+ return details.flatMap((detailsBlock) => (detailsBlock.data ?? []).map((card) => ({
248
+ ...card,
249
+ module: card.module ?? detailsBlock.module,
250
+ entities: this.mapEntities(card.properties ?? [], configurationByKey),
251
+ })));
252
+ }
253
+ toDashboardData(data, title) {
254
+ const charts = (data.chartLinks ?? []).map((link) => {
255
+ const configuration = link.configuration ?? {};
256
+ return {
257
+ ...configuration,
258
+ id: link.id,
259
+ chartComponentId: link.chartComponentId,
260
+ };
261
+ });
262
+ return {
263
+ page: {
264
+ id: data.moduleId,
265
+ name: { en: title, ar: title },
266
+ type: 'Dashboard',
267
+ dashboardConfig: {
268
+ ignoreQueryFilter: data.ignoreQueryFilter,
269
+ filters: data.filters,
270
+ versionNumber: data.versionNumber,
271
+ extraInfo: data.extraInfo,
272
+ },
273
+ },
274
+ charts,
275
+ dialogs: [],
276
+ filters: [],
277
+ };
278
+ }
279
+ buildDisplayConfigurationLookup(displayConfigurations) {
280
+ const configurationsByProperty = new Map();
281
+ for (const displayConfiguration of displayConfigurations) {
282
+ for (const propertyConfiguration of displayConfiguration.properties ??
283
+ []) {
284
+ if (propertyConfiguration.areaKey !== 'card' ||
285
+ !propertyConfiguration.propertyKey) {
286
+ continue;
287
+ }
288
+ configurationsByProperty.set(propertyConfiguration.propertyKey.toLowerCase(), propertyConfiguration);
289
+ }
290
+ }
291
+ return configurationsByProperty;
292
+ }
293
+ mapEntities(properties, configurationByKey) {
294
+ return properties
295
+ .map((property, index) => {
296
+ const propertyConfiguration = this.getPropertyConfiguration(property, configurationByKey);
297
+ return {
298
+ ...property,
299
+ rawValue: property.rawValue ?? undefined,
300
+ order: propertyConfiguration?.order ?? index + 1,
301
+ configuration: propertyConfiguration?.configuration ?? { size: 4 },
302
+ };
303
+ })
304
+ .sort((first, second) => (first.order ?? 0) - (second.order ?? 0));
305
+ }
306
+ getPropertyConfiguration(property, configurationByKey) {
307
+ const propertyKeys = [property.normalizedKey, property.key, property.name];
308
+ for (const propertyKey of propertyKeys) {
309
+ if (!propertyKey) {
310
+ continue;
311
+ }
312
+ const configuration = configurationByKey.get(propertyKey.toLowerCase());
313
+ if (configuration) {
314
+ return configuration;
315
+ }
316
+ }
317
+ return undefined;
318
+ }
131
319
  toLabel(key) {
132
320
  return key
133
321
  .replace(/([a-z])([A-Z])/g, '$1 $2')
@@ -144,7 +332,55 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImpor
144
332
  type: Injectable
145
333
  }] });
146
334
 
335
+ const DEFAULT_GRID_COLUMNS$2 = 12;
336
+ class ClientListTableView {
337
+ state = input.required(...(ngDevMode ? [{ debugName: "state" }] : []));
338
+ lazyLoad = output();
339
+ table = computed(() => {
340
+ return this.state();
341
+ }, ...(ngDevMode ? [{ debugName: "table" }] : []));
342
+ gridTemplateColumns = `repeat(${DEFAULT_GRID_COLUMNS$2}, minmax(0, 1fr))`;
343
+ slotGridSpan(span) {
344
+ return `span ${span} / span ${span}`;
345
+ }
346
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: ClientListTableView, deps: [], target: i0.ɵɵFactoryTarget.Component });
347
+ 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 } }, outputs: { lazyLoad: "lazyLoad" }, 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]=\"{\r\n $implicit: table(),\r\n table: table(),\r\n }\"\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 [loading]=\"table().loading\"\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 />\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]=\"{\r\n $implicit: table(),\r\n table: table(),\r\n }\"\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"] }] });
348
+ }
349
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: ClientListTableView, decorators: [{
350
+ type: Component,
351
+ 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]=\"{\r\n $implicit: table(),\r\n table: table(),\r\n }\"\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 [loading]=\"table().loading\"\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 />\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]=\"{\r\n $implicit: table(),\r\n table: table(),\r\n }\"\r\n />\r\n </div>\r\n }\r\n</div>\r\n" }]
352
+ }], propDecorators: { state: [{ type: i0.Input, args: [{ isSignal: true, alias: "state", required: true }] }], lazyLoad: [{ type: i0.Output, args: ["lazyLoad"] }] } });
353
+
354
+ class ClientListCardsView {
355
+ state = input.required(...(ngDevMode ? [{ debugName: "state" }] : []));
356
+ cardsState = computed(() => this.state(), ...(ngDevMode ? [{ debugName: "cardsState" }] : []));
357
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: ClientListCardsView, deps: [], target: i0.ɵɵFactoryTarget.Component });
358
+ 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"] }] });
359
+ }
360
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: ClientListCardsView, decorators: [{
361
+ type: Component,
362
+ 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" }]
363
+ }], propDecorators: { state: [{ type: i0.Input, args: [{ isSignal: true, alias: "state", required: true }] }] } });
364
+
365
+ const DEFAULT_GRID_COLUMNS$1 = 12;
366
+ class ClientListInformativeView {
367
+ state = input.required(...(ngDevMode ? [{ debugName: "state" }] : []));
368
+ informativeState = computed(() => this.state(), ...(ngDevMode ? [{ debugName: "informativeState" }] : []));
369
+ gridTemplateColumns = `repeat(${DEFAULT_GRID_COLUMNS$1}, minmax(0, 1fr))`;
370
+ slotGridSpan(span) {
371
+ return `span ${span} / span ${span}`;
372
+ }
373
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: ClientListInformativeView, deps: [], target: i0.ɵɵFactoryTarget.Component });
374
+ 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]=\"{\r\n $implicit: informativeState(),\r\n table: informativeState(),\r\n }\"\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]=\"{\r\n $implicit: informativeState(),\r\n table: informativeState(),\r\n }\"\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"] }] });
375
+ }
376
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: ClientListInformativeView, decorators: [{
377
+ type: Component,
378
+ 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]=\"{\r\n $implicit: informativeState(),\r\n table: informativeState(),\r\n }\"\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]=\"{\r\n $implicit: informativeState(),\r\n table: informativeState(),\r\n }\"\r\n />\r\n </div>\r\n }\r\n</div>\r\n" }]
379
+ }], propDecorators: { state: [{ type: i0.Input, args: [{ isSignal: true, alias: "state", required: true }] }] } });
380
+
381
+ const DEFAULT_AREA_TYPE = 'table';
147
382
  const DEFAULT_MODE = 'auto';
383
+ const DEFAULT_TYPE = 'form';
148
384
  const DEFAULT_SERVER_PAGE_SIZE = 50;
149
385
  const LOCAL_PAGE_SIZE_SOURCE = 100000;
150
386
  const DEFAULT_COLLAPSE_ICON = 'arrow.chevron-up';
@@ -158,101 +394,174 @@ class ClientList {
158
394
  defaultTake = input(DEFAULT_SERVER_PAGE_SIZE, ...(ngDevMode ? [{ debugName: "defaultTake" }] : []));
159
395
  loaded = output();
160
396
  errored = output();
161
- tables = this.state.tables;
162
- gridTemplateColumns = `repeat(${DEFAULT_GRID_COLUMNS}, minmax(0, 1fr))`;
397
+ items = this.state.items;
163
398
  subscriptions = new Map();
164
399
  inFlightRequestSignatures = new Map();
165
400
  fulfilledRequestSignatures = new Map();
166
401
  constructor() {
167
402
  effect(() => {
168
403
  const configs = this.configurations();
169
- untracked(() => this.configureTables(configs));
404
+ untracked(() => this.configureItems(configs));
170
405
  untracked(() => {
171
- this.state.tables().forEach((table) => {
172
- this.loadTable(table.key, table.config, table.skip, table.take);
406
+ this.state.items().forEach((item) => {
407
+ this.loadItem(item);
173
408
  });
174
409
  });
175
410
  });
176
411
  }
177
- onLazyLoad(tableKey, event) {
178
- const table = this.state.tablesByKey()[tableKey];
179
- if (!table || !table.config.isPaginated)
412
+ onLazyLoad(itemKey, event) {
413
+ const item = this.state.itemsByKey()[itemKey];
414
+ if (!item ||
415
+ item.type === 'informative' ||
416
+ item.areaType !== 'table' ||
417
+ !item.config.isPaginated) {
180
418
  return;
181
- const take = Number(event.pageSize ?? table.take);
419
+ }
420
+ const take = Number(event.pageSize ?? item.take);
182
421
  const skip = Number(event.first ?? 0);
183
- this.loadTable(table.key, table.config, skip, take);
422
+ this.loadTable(item.key, item.config, skip, take);
184
423
  }
185
- reload(tableKey) {
186
- if (tableKey) {
187
- this.reloadByKey(tableKey);
424
+ reload(itemKey) {
425
+ if (itemKey) {
426
+ this.reloadByKey(itemKey);
188
427
  return;
189
428
  }
190
- this.tables().forEach((table) => {
191
- this.loadTable(table.key, table.config, table.skip, table.take, true);
429
+ this.items().forEach((item) => {
430
+ this.loadItem(item, true);
192
431
  });
193
432
  }
194
- reloadByKey(tableKey) {
195
- const table = this.state.tablesByKey()[tableKey];
196
- if (!table)
433
+ reloadByKey(itemKey) {
434
+ const item = this.state.itemsByKey()[itemKey];
435
+ if (!item)
197
436
  return;
198
- this.loadTable(table.key, table.config, table.skip, table.take, true);
437
+ this.loadItem(item, true);
199
438
  }
200
439
  toggleExpanded(key) {
201
440
  this.state.toggleExpanded(key);
202
441
  }
203
- slotGridSpan(span) {
204
- return `span ${span} / span ${span}`;
205
- }
206
- templateContext(table) {
442
+ templateContext(item) {
207
443
  return {
208
- $implicit: table,
209
- table,
444
+ $implicit: item,
445
+ table: item,
210
446
  };
211
447
  }
448
+ defaultTitle(item) {
449
+ if (item.type === 'informative') {
450
+ return 'Informative Dashboard';
451
+ }
452
+ return item.areaType === 'cards' ? 'Cards' : 'Table';
453
+ }
212
454
  ngOnDestroy() {
213
455
  this.subscriptions.forEach((sub) => sub.unsubscribe());
214
456
  this.subscriptions.clear();
215
457
  }
216
- configureTables(configs) {
217
- const previousTables = this.state.tablesByKey();
458
+ configureItems(configs) {
459
+ const previousItems = this.state.itemsByKey();
218
460
  const hasMultipleConfigs = (configs ?? []).length > 1;
219
461
  const normalized = (configs ?? []).map((config, index) => {
220
- const mode = this.resolveMode(config.mode, config.columnKeys);
221
- const isPaginated = config.isPaginated ?? true;
222
- const take = isPaginated
223
- ? (config.take ?? this.defaultTake())
224
- : LOCAL_PAGE_SIZE_SOURCE;
462
+ const type = this.resolveType(config.type);
463
+ const isInformative = type === 'informative';
464
+ const areaType = isInformative
465
+ ? DEFAULT_AREA_TYPE
466
+ : this.resolveAreaType(config.areaType);
467
+ const mode = isInformative
468
+ ? DEFAULT_MODE
469
+ : this.resolveMode(config.mode, config.columnKeys);
470
+ const isPaginated = !isInformative &&
471
+ areaType === 'table' &&
472
+ (config.isPaginated ?? true);
473
+ const take = !isInformative && areaType === 'table'
474
+ ? isPaginated
475
+ ? (config.take ?? this.defaultTake())
476
+ : LOCAL_PAGE_SIZE_SOURCE
477
+ : 0;
225
478
  const collapseEnabled = (config.collapse?.enabled ?? true) && hasMultipleConfigs;
226
479
  const layout = this.resolveLayout(config);
227
480
  const key = config.key ??
228
481
  `${config.levelId ?? 'x'}-${config.levelDataId ?? 'x'}-${config.moduleId ?? 'x'}-${index}`;
229
- const current = previousTables[key];
482
+ const existing = previousItems[key];
483
+ const normalizedConfig = this.toNormalizedConfig(config, type, areaType, mode, isPaginated, take, collapseEnabled, layout);
484
+ if (isInformative) {
485
+ return {
486
+ key,
487
+ config: normalizedConfig,
488
+ type: 'informative',
489
+ areaType: 'table',
490
+ loading: false,
491
+ error: null,
492
+ title: config.title?.trim() || '',
493
+ moduleKey: '',
494
+ totalCount: 0,
495
+ columns: [],
496
+ rows: [],
497
+ cards: [],
498
+ skip: 0,
499
+ take: 0,
500
+ expanded: existing?.expanded ?? config.collapse?.expandedByDefault ?? true,
501
+ dashboardData: existing?.dashboardData ?? null,
502
+ };
503
+ }
504
+ if (areaType === 'cards') {
505
+ return {
506
+ key,
507
+ config: normalizedConfig,
508
+ type: 'form',
509
+ areaType: 'cards',
510
+ loading: false,
511
+ error: null,
512
+ title: config.title?.trim() || '',
513
+ moduleKey: '',
514
+ totalCount: 0,
515
+ columns: [],
516
+ rows: [],
517
+ cards: [],
518
+ skip: 0,
519
+ take: 0,
520
+ expanded: existing?.expanded ?? config.collapse?.expandedByDefault ?? true,
521
+ dashboardData: null,
522
+ };
523
+ }
230
524
  return {
231
525
  key,
232
- config: this.toNormalizedConfig(config, mode, isPaginated, take, collapseEnabled, layout),
526
+ config: normalizedConfig,
527
+ type: 'form',
528
+ areaType: 'table',
233
529
  loading: false,
234
530
  error: null,
235
531
  title: config.title?.trim() || '',
236
532
  moduleKey: '',
533
+ totalCount: 0,
237
534
  columns: [],
238
535
  rows: [],
239
- skip: current?.skip ?? 0,
536
+ cards: [],
537
+ skip: existing?.areaType === 'table' ? existing.skip : 0,
240
538
  take,
241
- totalCount: 0,
242
- expanded: config.collapse?.expandedByDefault ?? true,
539
+ expanded: existing?.expanded ?? config.collapse?.expandedByDefault ?? true,
540
+ dashboardData: null,
243
541
  };
244
542
  });
245
543
  this.state.setConfigs(normalized);
246
- this.cleanupStaleResources(new Set(normalized.map((table) => table.key)));
544
+ this.cleanupStaleResources(new Set(normalized.map((item) => item.key)));
545
+ }
546
+ loadItem(item, force = false) {
547
+ if (item.type === 'informative') {
548
+ this.loadInformative(item.key, item.config, force);
549
+ return;
550
+ }
551
+ if (item.areaType === 'cards') {
552
+ this.loadCards(item.key, item.config, force);
553
+ return;
554
+ }
555
+ this.loadTable(item.key, item.config, item.skip, item.take, force);
247
556
  }
248
557
  loadTable(key, config, skip, take, force = false) {
249
558
  if (!this.hasValidIdentifiers(config)) {
250
559
  this.state.setLoading(key, false);
251
- this.state.setError(key, 'Missing identifiers: levelId, levelDataId and moduleId are required');
560
+ this.state.setError(key, this.getMissingIdentifiersMessage(config));
252
561
  return;
253
562
  }
254
- const query = this.toQuery(config, skip, take);
255
- const requestSignature = this.createRequestSignature(config, query);
563
+ const query = this.toTableQuery(config, skip, take);
564
+ const requestSignature = this.createTableRequestSignature(config, query);
256
565
  if (force) {
257
566
  this.inFlightRequestSignatures.delete(key);
258
567
  this.fulfilledRequestSignatures.delete(key);
@@ -292,16 +601,117 @@ class ClientList {
292
601
  });
293
602
  this.subscriptions.set(key, sub);
294
603
  }
604
+ loadCards(key, config, force = false) {
605
+ if (!this.hasValidIdentifiers(config)) {
606
+ this.state.setLoading(key, false);
607
+ this.state.setError(key, this.getMissingIdentifiersMessage(config));
608
+ return;
609
+ }
610
+ const requestSignature = this.createCardsRequestSignature(config);
611
+ if (force) {
612
+ this.inFlightRequestSignatures.delete(key);
613
+ this.fulfilledRequestSignatures.delete(key);
614
+ }
615
+ else if (this.inFlightRequestSignatures.get(key) === requestSignature ||
616
+ this.fulfilledRequestSignatures.get(key) === requestSignature) {
617
+ return;
618
+ }
619
+ this.subscriptions.get(key)?.unsubscribe();
620
+ this.inFlightRequestSignatures.set(key, requestSignature);
621
+ this.state.setLoading(key, true);
622
+ this.state.setError(key, null);
623
+ const sub = this.api
624
+ .getCards(config.levelDataId, config.moduleId)
625
+ .subscribe({
626
+ next: (response) => {
627
+ if (response.data) {
628
+ this.state.setCardsResult(key, response.data, config);
629
+ this.fulfilledRequestSignatures.set(key, requestSignature);
630
+ this.loaded.emit(key);
631
+ }
632
+ else {
633
+ const message = response.message ?? 'Failed to load cards';
634
+ this.state.setError(key, message);
635
+ this.errored.emit({ key, message });
636
+ }
637
+ this.state.setLoading(key, false);
638
+ this.inFlightRequestSignatures.delete(key);
639
+ },
640
+ error: (error) => {
641
+ const message = error?.error?.message ?? error?.message ?? 'Failed to load cards';
642
+ this.state.setError(key, message);
643
+ this.state.setLoading(key, false);
644
+ this.inFlightRequestSignatures.delete(key);
645
+ this.errored.emit({ key, message });
646
+ },
647
+ });
648
+ this.subscriptions.set(key, sub);
649
+ }
650
+ loadInformative(key, config, force = false) {
651
+ if (!this.hasValidIdentifiers(config)) {
652
+ this.state.setLoading(key, false);
653
+ this.state.setError(key, this.getMissingIdentifiersMessage(config));
654
+ return;
655
+ }
656
+ const requestSignature = this.createInformativeRequestSignature(config);
657
+ if (force) {
658
+ this.inFlightRequestSignatures.delete(key);
659
+ this.fulfilledRequestSignatures.delete(key);
660
+ }
661
+ else if (this.inFlightRequestSignatures.get(key) === requestSignature ||
662
+ this.fulfilledRequestSignatures.get(key) === requestSignature) {
663
+ return;
664
+ }
665
+ this.subscriptions.get(key)?.unsubscribe();
666
+ this.inFlightRequestSignatures.set(key, requestSignature);
667
+ this.state.setLoading(key, true);
668
+ this.state.setError(key, null);
669
+ const sub = this.api.getInformativeDashboard(config.moduleId).subscribe({
670
+ next: (response) => {
671
+ this.state.setInformativeResult(key, response.data ?? null, config);
672
+ this.fulfilledRequestSignatures.set(key, requestSignature);
673
+ this.loaded.emit(key);
674
+ this.state.setLoading(key, false);
675
+ this.inFlightRequestSignatures.delete(key);
676
+ },
677
+ error: (error) => {
678
+ if (error?.status === 404) {
679
+ this.state.setInformativeResult(key, null, config);
680
+ this.fulfilledRequestSignatures.set(key, requestSignature);
681
+ this.loaded.emit(key);
682
+ this.state.setLoading(key, false);
683
+ this.inFlightRequestSignatures.delete(key);
684
+ return;
685
+ }
686
+ const message = error?.error?.message ??
687
+ error?.message ??
688
+ 'Failed to load informative dashboard';
689
+ this.state.setError(key, message);
690
+ this.state.setLoading(key, false);
691
+ this.inFlightRequestSignatures.delete(key);
692
+ this.errored.emit({ key, message });
693
+ },
694
+ });
695
+ this.subscriptions.set(key, sub);
696
+ }
697
+ resolveAreaType(areaType) {
698
+ return areaType === 'cards' ? 'cards' : DEFAULT_AREA_TYPE;
699
+ }
700
+ resolveType(type) {
701
+ return type === 'informative' ? 'informative' : DEFAULT_TYPE;
702
+ }
295
703
  resolveMode(mode, columnKeys) {
296
704
  if (mode !== 'override')
297
705
  return DEFAULT_MODE;
298
706
  return (columnKeys ?? []).length > 0 ? 'override' : DEFAULT_MODE;
299
707
  }
300
- toNormalizedConfig(config, mode, isPaginated, take, collapseEnabled, layout) {
708
+ toNormalizedConfig(config, type, areaType, mode, isPaginated, take, collapseEnabled, layout) {
301
709
  return {
302
710
  levelId: config.levelId,
303
711
  levelDataId: config.levelDataId,
304
712
  moduleId: config.moduleId,
713
+ type,
714
+ areaType,
305
715
  mode,
306
716
  isPaginated,
307
717
  take,
@@ -356,7 +766,7 @@ class ClientList {
356
766
  }
357
767
  }
358
768
  }
359
- toQuery(config, skip, take) {
769
+ toTableQuery(config, skip, take) {
360
770
  return {
361
771
  mode: config.mode,
362
772
  columnKeys: config.mode === 'override' ? config.columnKeys : undefined,
@@ -364,8 +774,10 @@ class ClientList {
364
774
  take: config.isPaginated ? take : LOCAL_PAGE_SIZE_SOURCE,
365
775
  };
366
776
  }
367
- createRequestSignature(config, query) {
777
+ createTableRequestSignature(config, query) {
368
778
  return [
779
+ config.type,
780
+ config.areaType,
369
781
  config.levelId,
370
782
  config.levelDataId,
371
783
  config.moduleId,
@@ -375,20 +787,53 @@ class ClientList {
375
787
  ...(query.columnKeys ?? []),
376
788
  ].join('|');
377
789
  }
790
+ createCardsRequestSignature(config) {
791
+ return [
792
+ config.type,
793
+ config.areaType,
794
+ config.levelDataId,
795
+ config.moduleId,
796
+ 'card',
797
+ ].join('|');
798
+ }
799
+ createInformativeRequestSignature(config) {
800
+ return [config.type, config.moduleId, 'dashboard'].join('|');
801
+ }
378
802
  hasValidIdentifiers(config) {
803
+ if (config.type === 'informative') {
804
+ return Number.isFinite(config.moduleId) && config.moduleId > 0;
805
+ }
806
+ const hasLevelDataId = Number.isFinite(config.levelDataId) && config.levelDataId > 0;
807
+ const hasModuleId = Number.isFinite(config.moduleId) && config.moduleId > 0;
808
+ if (config.areaType === 'cards') {
809
+ return hasLevelDataId && hasModuleId;
810
+ }
379
811
  return (Number.isFinite(config.levelId) &&
380
812
  config.levelId > 0 &&
381
- Number.isFinite(config.levelDataId) &&
382
- config.levelDataId > 0 &&
383
- Number.isFinite(config.moduleId) &&
384
- config.moduleId > 0);
813
+ hasLevelDataId &&
814
+ hasModuleId);
815
+ }
816
+ getMissingIdentifiersMessage(config) {
817
+ if (config.type === 'informative') {
818
+ return 'Missing identifiers: moduleId is required';
819
+ }
820
+ if (config.areaType === 'cards') {
821
+ return 'Missing identifiers: levelDataId and moduleId are required';
822
+ }
823
+ return 'Missing identifiers: levelId, levelDataId and moduleId are required';
385
824
  }
386
825
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: ClientList, deps: [], target: i0.ɵɵFactoryTarget.Component });
387
- 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\">\r\n @for (table of tables(); track table.key) {\r\n <section class=\"flex flex-col gap-4\">\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 (table.config.collapse.enabled) {\r\n <mt-button\r\n variant=\"text\"\r\n severity=\"secondary\"\r\n [icon]=\"\r\n table.expanded\r\n ? table.config.collapse.collapseIcon\r\n : table.config.collapse.expandIcon\r\n \"\r\n (onClick)=\"toggleExpanded(table.key)\"\r\n />\r\n }\r\n <h3 class=\"m-0 text-lg font-bold\">\r\n {{ table.title || table.moduleKey || \"Table\" }}\r\n </h3>\r\n @if (table.config.headerStart) {\r\n <div class=\"flex items-center gap-2\">\r\n <ng-container\r\n [ngTemplateOutlet]=\"table.config.headerStart\"\r\n [ngTemplateOutletContext]=\"templateContext(table)\"\r\n />\r\n </div>\r\n }\r\n </div>\r\n @if (table.config.headerEnd) {\r\n <div class=\"ml-auto flex shrink-0 items-center gap-2\">\r\n <ng-container\r\n [ngTemplateOutlet]=\"table.config.headerEnd\"\r\n [ngTemplateOutletContext]=\"templateContext(table)\"\r\n />\r\n </div>\r\n }\r\n </div>\r\n\r\n @if (table.expanded || !table.config.collapse.enabled) {\r\n <div\r\n class=\"grid gap-4\"\r\n [style.gridTemplateColumns]=\"gridTemplateColumns\"\r\n >\r\n @if (table.config.contentStart) {\r\n <div\r\n [style.gridColumn]=\"slotGridSpan(table.config.layout.startSpan)\"\r\n >\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=\"p-3 rounded-lg bg-red-50 text-red-700 border border-red-200 text-sm\"\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 [loading]=\"table.loading\"\r\n [lazy]=\"table.config.isPaginated\"\r\n [lazyTotalRecords]=\"table.totalCount\"\r\n [pageSize]=\"table.config.isPaginated ? table.take : 10\"\r\n (lazyLoad)=\"onLazyLoad(table.key, $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 }\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: Card, selector: "mt-card", inputs: ["class", "title", "paddingless"] }, { 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: "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: "ngmodule", type: SkeletonModule }, { kind: "component", type: i2.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "shape", "animation", "borderRadius", "size", "width", "height"] }] });
826
+ 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\">\r\n @for (item of items(); track item.key) {\r\n <section class=\"flex flex-col gap-4\">\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 @if (item.expanded || !item.config.collapse.enabled) {\r\n @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 (lazyLoad)=\"onLazyLoad(item.key, $event)\"\r\n />\r\n } @else {\r\n <mt-client-list-cards-view [state]=\"item\" />\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"], 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"] }] });
388
827
  }
389
828
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: ClientList, decorators: [{
390
829
  type: Component,
391
- args: [{ selector: 'mt-client-list', standalone: true, imports: [CommonModule, Card, Table, Button, SkeletonModule], providers: [ClientListStateService], template: "<div class=\"flex flex-col gap-4\">\r\n @for (table of tables(); track table.key) {\r\n <section class=\"flex flex-col gap-4\">\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 (table.config.collapse.enabled) {\r\n <mt-button\r\n variant=\"text\"\r\n severity=\"secondary\"\r\n [icon]=\"\r\n table.expanded\r\n ? table.config.collapse.collapseIcon\r\n : table.config.collapse.expandIcon\r\n \"\r\n (onClick)=\"toggleExpanded(table.key)\"\r\n />\r\n }\r\n <h3 class=\"m-0 text-lg font-bold\">\r\n {{ table.title || table.moduleKey || \"Table\" }}\r\n </h3>\r\n @if (table.config.headerStart) {\r\n <div class=\"flex items-center gap-2\">\r\n <ng-container\r\n [ngTemplateOutlet]=\"table.config.headerStart\"\r\n [ngTemplateOutletContext]=\"templateContext(table)\"\r\n />\r\n </div>\r\n }\r\n </div>\r\n @if (table.config.headerEnd) {\r\n <div class=\"ml-auto flex shrink-0 items-center gap-2\">\r\n <ng-container\r\n [ngTemplateOutlet]=\"table.config.headerEnd\"\r\n [ngTemplateOutletContext]=\"templateContext(table)\"\r\n />\r\n </div>\r\n }\r\n </div>\r\n\r\n @if (table.expanded || !table.config.collapse.enabled) {\r\n <div\r\n class=\"grid gap-4\"\r\n [style.gridTemplateColumns]=\"gridTemplateColumns\"\r\n >\r\n @if (table.config.contentStart) {\r\n <div\r\n [style.gridColumn]=\"slotGridSpan(table.config.layout.startSpan)\"\r\n >\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=\"p-3 rounded-lg bg-red-50 text-red-700 border border-red-200 text-sm\"\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 [loading]=\"table.loading\"\r\n [lazy]=\"table.config.isPaginated\"\r\n [lazyTotalRecords]=\"table.totalCount\"\r\n [pageSize]=\"table.config.isPaginated ? table.take : 10\"\r\n (lazyLoad)=\"onLazyLoad(table.key, $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 }\r\n </section>\r\n }\r\n</div>\r\n" }]
830
+ args: [{ selector: 'mt-client-list', standalone: true, imports: [
831
+ CommonModule,
832
+ Button,
833
+ ClientListTableView,
834
+ ClientListCardsView,
835
+ ClientListInformativeView,
836
+ ], 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 <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 @if (item.expanded || !item.config.collapse.enabled) {\r\n @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 (lazyLoad)=\"onLazyLoad(item.key, $event)\"\r\n />\r\n } @else {\r\n <mt-client-list-cards-view [state]=\"item\" />\r\n }\r\n }\r\n </section>\r\n }\r\n</div>\r\n" }]
392
837
  }], 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"] }] } });
393
838
 
394
839
  /**