@mmlogic/components 0.2.0 → 0.3.0

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.
@@ -22,7 +22,10 @@ export class MrdTable {
22
22
  this.outsideClickHandler = null;
23
23
  this.keydownHandler = null;
24
24
  // ── Props ──────────────────────────────────────────────────────────────────
25
- this.columns = [];
25
+ /** The VIEW or RELATED_VIEW layout item. Contains view config, dataClass, fromClass, actions etc. */
26
+ this.item = null;
27
+ /** Parent record id — required for RELATED_VIEW to build /{fromClass}/{parentId}/{dataClass}. */
28
+ this.parentId = '';
26
29
  /** Direct rows (non-paginated mode, used when totalElements === 0). */
27
30
  this.rows = [];
28
31
  this.locale = navigator.language;
@@ -34,16 +37,9 @@ export class MrdTable {
34
37
  this.rowHeight = 36;
35
38
  /** Height of the scroll container in px. */
36
39
  this.tableHeight = 500;
37
- /** Initial sort applied on load, e.g. "timestamp,desc" or "name".
38
- * Parsed by init() into sortField + sortDir. */
39
- this.defaultSort = '';
40
- /** Toolbar action buttons rendered above the table. */
41
- this.actions = [];
42
- /** Display label of the current view — shown in the toolbar center as a view picker trigger. */
43
- this.viewLabel = '';
44
- /** Alternative views available for this table; renders a dropdown when non-empty. */
45
- this.alternativeViews = [];
46
40
  // ── Internal state ─────────────────────────────────────────────────────────
41
+ /** Index into allViews[] for the currently displayed view. 0 = primary, 1+ = alternatives. */
42
+ this.activeViewIdx = 0;
47
43
  this.loadedPages = new Map();
48
44
  this.requestedPages = new Set();
49
45
  this.renderStart = 0;
@@ -86,13 +82,16 @@ export class MrdTable {
86
82
  totalElementsChanged(newVal) {
87
83
  this.renderEnd = Math.min(this.renderEnd, Math.max(0, newVal - 1));
88
84
  }
89
- /** Apply defaultSort when the prop changes (e.g. after a view switch). */
90
- defaultSortChanged(newVal) {
91
- this.applyDefaultSort(newVal);
85
+ /** Reset to primary view when the item prop is replaced from outside. */
86
+ itemChanged(newVal) {
87
+ var _a, _b;
88
+ this.activeViewIdx = 0;
89
+ this.applyDefaultSort((_b = (_a = newVal === null || newVal === void 0 ? void 0 : newVal.view) === null || _a === void 0 ? void 0 : _a.defaultSort) !== null && _b !== void 0 ? _b : '');
92
90
  }
93
91
  // ── Lifecycle ──────────────────────────────────────────────────────────────
94
92
  componentWillLoad() {
95
- this.applyDefaultSort(this.defaultSort);
93
+ var _a, _b, _c;
94
+ this.applyDefaultSort((_c = (_b = (_a = this.item) === null || _a === void 0 ? void 0 : _a.view) === null || _b === void 0 ? void 0 : _b.defaultSort) !== null && _c !== void 0 ? _c : '');
96
95
  }
97
96
  // ── Helpers ────────────────────────────────────────────────────────────────
98
97
  applyDefaultSort(defaultSort) {
@@ -124,14 +123,17 @@ export class MrdTable {
124
123
  this.colWidths = [];
125
124
  this.scrollTop = 0;
126
125
  this.renderStart = 0;
127
- // No BUFFER on init — only request what fits the visible area (page 0).
128
- // BUFFER is applied during scroll to pre-fetch the next page proactively.
129
- this.renderEnd = Math.max(0, Math.min(this.visibleCount() - 1, this.totalElements - 1));
126
+ // Always fill the visible viewport on init — totalElements may be stale from a
127
+ // previous view. setPage() clamps renderEnd when the page is shorter than pageSize.
128
+ this.renderEnd = this.visibleCount() - 1;
130
129
  const scroller = this.el.querySelector('.mrd-table__scroll');
131
130
  if (scroller)
132
131
  scroller.scrollTop = 0;
133
132
  this.aggregations = null;
134
133
  this.emitLoadAggregations();
134
+ // Always request page 0 — totalElements may be unknown (0) on first load.
135
+ this.mrdLoadPage.emit({ page: 0, sort: this.sortParam(), path: this.buildDataPath(), qs: this.buildQueryParams(0) });
136
+ this.requestedPages = new Set([0]);
135
137
  }
136
138
  /**
137
139
  * Inject the rows for a given page (0-based).
@@ -184,6 +186,147 @@ export class MrdTable {
184
186
  return '';
185
187
  return this.sortDir === 'desc' ? `${this.sortField},desc` : this.sortField;
186
188
  }
189
+ /** Stable ordered list: primary view first, then alternatives (from the item prop). */
190
+ get allViews() {
191
+ var _a, _b, _c, _d, _e, _f, _g;
192
+ if (!this.item)
193
+ return [];
194
+ const it = this.item;
195
+ return [
196
+ { label: (_e = (_d = (_c = (_a = it.label) !== null && _a !== void 0 ? _a : (_b = it.view) === null || _b === void 0 ? void 0 : _b.pluralLabel) !== null && _c !== void 0 ? _c : it.dataClass) !== null && _d !== void 0 ? _d : it.relatedClass) !== null && _e !== void 0 ? _e : '', dataClass: (_f = it.dataClass) !== null && _f !== void 0 ? _f : it.relatedClass, fromClass: it.fromClass, filterClass: it.filterClass, view: it.view },
197
+ ...((_g = it.alternativeViews) !== null && _g !== void 0 ? _g : []).map(av => {
198
+ var _a, _b, _c, _d;
199
+ return ({
200
+ label: (_d = (_c = (_a = av.label) !== null && _a !== void 0 ? _a : (_b = av.view) === null || _b === void 0 ? void 0 : _b.pluralLabel) !== null && _c !== void 0 ? _c : av.dataClass) !== null && _d !== void 0 ? _d : '',
201
+ dataClass: av.dataClass,
202
+ fromClass: av.fromClass,
203
+ filterClass: av.filterClass,
204
+ view: av.view,
205
+ });
206
+ }),
207
+ ];
208
+ }
209
+ /** Relative excel export path for the current view.
210
+ * VIEW: /excel/{dataClass}
211
+ * RELATED_VIEW: /excel/{fromClass}/{parentId}/{dataClass} */
212
+ buildExcelPath() {
213
+ var _a, _b, _c, _d;
214
+ const v = this.allViews[this.activeViewIdx];
215
+ if (!v)
216
+ return '';
217
+ if (((_a = this.item) === null || _a === void 0 ? void 0 : _a.type) === 'RELATED_VIEW') {
218
+ return `/excel/${(_b = v.fromClass) !== null && _b !== void 0 ? _b : ''}/${this.parentId}/${(_c = v.dataClass) !== null && _c !== void 0 ? _c : ''}`;
219
+ }
220
+ return `/excel/${(_d = v.dataClass) !== null && _d !== void 0 ? _d : ''}`;
221
+ }
222
+ buildActionDetail(action) {
223
+ var _a, _b, _c;
224
+ if (action === 'export') {
225
+ return { action, path: this.buildExcelPath(), qs: this.buildQueryParams(0) };
226
+ }
227
+ if (action === 'create') {
228
+ const v = this.allViews[this.activeViewIdx];
229
+ const parentPath = ((_a = this.item) === null || _a === void 0 ? void 0 : _a.type) === 'RELATED_VIEW'
230
+ ? `/${(_b = v === null || v === void 0 ? void 0 : v.fromClass) !== null && _b !== void 0 ? _b : ''}/${this.parentId}`
231
+ : null;
232
+ return { action, dataClass: (_c = v === null || v === void 0 ? void 0 : v.dataClass) !== null && _c !== void 0 ? _c : '', parentPath };
233
+ }
234
+ return { action };
235
+ }
236
+ /** Relative data path for the current view, without query string.
237
+ * VIEW: /{dataClass}
238
+ * RELATED_VIEW: /{fromClass}/{parentId}/{dataClass} */
239
+ buildDataPath() {
240
+ var _a, _b, _c, _d;
241
+ const v = this.allViews[this.activeViewIdx];
242
+ if (!v)
243
+ return '';
244
+ if (((_a = this.item) === null || _a === void 0 ? void 0 : _a.type) === 'RELATED_VIEW') {
245
+ return `/${(_b = v.fromClass) !== null && _b !== void 0 ? _b : ''}/${this.parentId}/${(_c = v.dataClass) !== null && _c !== void 0 ? _c : ''}`;
246
+ }
247
+ return `/${(_d = v.dataClass) !== null && _d !== void 0 ? _d : ''}`;
248
+ }
249
+ /** Build query params for a page request from current sort, view filters, filterClass and active column filters. */
250
+ buildQueryParams(page) {
251
+ var _a, _b, _c, _d, _e, _f, _g;
252
+ const v = this.allViews[this.activeViewIdx];
253
+ const p = new URLSearchParams();
254
+ if (page > 0)
255
+ p.set('page', String(page));
256
+ const sort = this.sortParam();
257
+ if (sort)
258
+ p.set('sort', sort);
259
+ const filterClass = v === null || v === void 0 ? void 0 : v.filterClass;
260
+ if (filterClass)
261
+ p.set('type', filterClass);
262
+ for (const f of ((_b = (_a = v === null || v === void 0 ? void 0 : v.view) === null || _a === void 0 ? void 0 : _a.filter) !== null && _b !== void 0 ? _b : [])) {
263
+ if (!f.name)
264
+ continue;
265
+ if (f.operator === 'EMPTY') {
266
+ p.set(f.name, '');
267
+ continue;
268
+ }
269
+ if (f.operator === 'NOT_EMPTY') {
270
+ p.set(f.name + '_notempty', 'true');
271
+ continue;
272
+ }
273
+ if (f.operator === 'STARTS_WITH') {
274
+ p.set(f.name + '_startswith', String((_c = f.value) !== null && _c !== void 0 ? _c : ''));
275
+ continue;
276
+ }
277
+ if (f.operator === 'FROM') {
278
+ p.set(f.name + '_from', String((_d = f.value) !== null && _d !== void 0 ? _d : ''));
279
+ continue;
280
+ }
281
+ if (f.operator === 'TO') {
282
+ p.set(f.name + '_to', String((_e = f.value) !== null && _e !== void 0 ? _e : ''));
283
+ continue;
284
+ }
285
+ if (f.value != null) {
286
+ p.set(f.name, String(f.value));
287
+ }
288
+ }
289
+ for (const f of this.activeFilters.values()) {
290
+ if (f.operator === 'isEmpty') {
291
+ p.set(f.field, '');
292
+ continue;
293
+ }
294
+ if (f.operator === 'isNotEmpty') {
295
+ p.set(f.field + '_notempty', 'true');
296
+ continue;
297
+ }
298
+ if (f.operator === 'startsWith') {
299
+ p.set(f.field + '_startswith', String((_f = f.value) !== null && _f !== void 0 ? _f : ''));
300
+ continue;
301
+ }
302
+ if ((_g = f.values) === null || _g === void 0 ? void 0 : _g.length) {
303
+ p.set(f.field, f.values.join(','));
304
+ continue;
305
+ }
306
+ if (f.value != null)
307
+ p.set(f.field, String(f.value));
308
+ if (f.from != null)
309
+ p.set(f.field + '_from', String(f.from));
310
+ if (f.to != null)
311
+ p.set(f.field + '_to', String(f.to));
312
+ }
313
+ return p.toString();
314
+ }
315
+ get columns() {
316
+ var _a, _b, _c;
317
+ return ((_c = (_b = (_a = this.allViews[this.activeViewIdx]) === null || _a === void 0 ? void 0 : _a.view) === null || _b === void 0 ? void 0 : _b.values) !== null && _c !== void 0 ? _c : []);
318
+ }
319
+ get tableActions() {
320
+ var _a, _b;
321
+ const raw = (_b = (_a = this.item) === null || _a === void 0 ? void 0 : _a.actions) !== null && _b !== void 0 ? _b : [];
322
+ return (raw !== null && raw !== void 0 ? raw : []).reduce((acc, a) => {
323
+ if (a === 'NEW')
324
+ acc.push({ action: 'create', label: t('table_new_record', this.locale), icon: 'assets/sprites.svg#icon-plus', variant: 'primary' });
325
+ if (a === 'EXPORT')
326
+ acc.push({ action: 'export', label: t('table_export_excel', this.locale), icon: 'assets/sprites.svg#icon-file-excel' });
327
+ return acc;
328
+ }, []);
329
+ }
187
330
  colName(col) {
188
331
  var _a;
189
332
  return (_a = col.name) !== null && _a !== void 0 ? _a : '';
@@ -215,9 +358,9 @@ export class MrdTable {
215
358
  return Object.keys(params).length > 0 ? params : null;
216
359
  }
217
360
  emitLoadAggregations() {
218
- const params = this.buildAggregationParams();
219
- if (params)
220
- this.mrdLoadAggregations.emit(params);
361
+ const aggParams = this.buildAggregationParams();
362
+ if (aggParams)
363
+ this.mrdLoadAggregations.emit(Object.assign(Object.assign({}, aggParams), { path: this.buildDataPath(), qs: this.buildQueryParams(0) }));
221
364
  }
222
365
  renderAggregationValue(col) {
223
366
  var _a, _b;
@@ -282,7 +425,7 @@ export class MrdTable {
282
425
  for (let p = firstPage; p <= lastPage; p++) {
283
426
  if (!this.loadedPages.has(p) && !next.has(p)) {
284
427
  next.add(p);
285
- this.mrdLoadPage.emit({ page: p, sort: this.sortParam() });
428
+ this.mrdLoadPage.emit({ page: p, sort: this.sortParam(), path: this.buildDataPath(), qs: this.buildQueryParams(p) });
286
429
  changed = true;
287
430
  }
288
431
  }
@@ -324,7 +467,7 @@ export class MrdTable {
324
467
  if (pageEnd < this.renderStart || pageStart > this.renderEnd)
325
468
  continue;
326
469
  next.add(page);
327
- this.mrdLoadPage.emit({ page, sort: this.sortParam() });
470
+ this.mrdLoadPage.emit({ page, sort: this.sortParam(), path: this.buildDataPath(), qs: this.buildQueryParams(page) });
328
471
  changed = true;
329
472
  }
330
473
  this.pendingPages.clear();
@@ -516,7 +659,6 @@ export class MrdTable {
516
659
  }
517
660
  this.activeFilters = next;
518
661
  this.closeFilterPopup();
519
- this.mrdFilter.emit({ filters: Array.from(this.activeFilters.values()) });
520
662
  this.aggregations = null;
521
663
  this.emitLoadAggregations();
522
664
  if (this.totalElements > 0) {
@@ -531,7 +673,6 @@ export class MrdTable {
531
673
  next.delete(name);
532
674
  this.activeFilters = next;
533
675
  this.closeFilterPopup();
534
- this.mrdFilter.emit({ filters: Array.from(this.activeFilters.values()) });
535
676
  this.aggregations = null;
536
677
  this.emitLoadAggregations();
537
678
  if (this.totalElements > 0) {
@@ -541,7 +682,6 @@ export class MrdTable {
541
682
  }
542
683
  clearAllFilters() {
543
684
  this.activeFilters = new Map();
544
- this.mrdFilter.emit({ filters: [] });
545
685
  this.aggregations = null;
546
686
  this.emitLoadAggregations();
547
687
  if (this.totalElements > 0) {
@@ -550,28 +690,30 @@ export class MrdTable {
550
690
  }
551
691
  }
552
692
  // ── View switcher ──────────────────────────────────────────────────────────
553
- handleViewSwitch(view) {
554
- this.mrdSwitchView.emit({ name: view.name, class: view.class });
693
+ handleViewSwitch(targetIdx) {
694
+ var _a, _b;
695
+ const target = this.allViews[targetIdx];
696
+ if (!(target === null || target === void 0 ? void 0 : target.view))
697
+ return;
698
+ this.activeViewIdx = targetIdx;
699
+ this.applyDefaultSort((_b = (_a = target.view) === null || _a === void 0 ? void 0 : _a.defaultSort) !== null && _b !== void 0 ? _b : '');
700
+ this.activeFilters = new Map();
701
+ this.init();
555
702
  }
556
703
  // ── Render: toolbar ────────────────────────────────────────────────────────
557
704
  renderToolbar() {
558
- var _a, _b;
559
705
  const filterCount = this.activeFilters.size;
560
- const hasActions = ((_a = this.actions) === null || _a === void 0 ? void 0 : _a.length) > 0;
561
- const hasViewSwitcher = !!this.viewLabel && ((_b = this.alternativeViews) === null || _b === void 0 ? void 0 : _b.length) > 0;
706
+ const actions = this.tableActions;
707
+ const allViews = this.allViews;
708
+ const hasActions = actions.length > 0;
709
+ const hasViewSwitcher = allViews.length > 1;
562
710
  return (h("div", { class: "mrd-table__toolbar" }, h("div", { class: "mrd-table__toolbar-left" }, h("button", { class: `mrd-table__action mrd-table__action--secondary mrd-table__filter-toggle${this.filterMode ? ' mrd-table__filter-toggle--active' : ''}`, onClick: () => this.handleFilterToggle() }, h("svg", { class: "mrd-table__action-icon", viewBox: "0 0 24 24", "aria-hidden": "true" }, h("path", { fill: "currentColor", d: "M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z" })), filterCount > 0 && h("span", { class: "mrd-table__filter-badge" }, filterCount), h("span", { class: "mrd-table__action-tooltip" }, this.filterMode ? t('table_filter_hide', this.locale) : t('table_filter', this.locale), filterCount > 0 ? ` (${filterCount} ${t('table_filter_active', this.locale)})` : '')), filterCount > 0 && (h("button", { class: "mrd-table__action mrd-table__action--secondary", onClick: () => this.clearAllFilters() }, h("svg", { class: "mrd-table__action-icon", viewBox: "0 0 24 24", "aria-hidden": "true" }, h("path", { fill: "currentColor", d: "M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" })), h("span", { class: "mrd-table__action-tooltip" }, t('table_filter_clear_all', this.locale))))), hasViewSwitcher && (h("div", { class: "mrd-table__toolbar-center" }, h("select", { class: "mrd-table__view-select", onChange: (e) => {
563
- const sel = e.target;
564
- const view = this.alternativeViews.find(v => v.name === sel.value);
565
- if (view) {
566
- sel.selectedIndex = 0;
567
- this.handleViewSwitch(view);
568
- }
569
- } }, h("option", { value: "" }, this.viewLabel), this.alternativeViews.map(v => {
711
+ const idx = parseInt(e.target.value, 10);
712
+ if (!isNaN(idx) && idx !== this.activeViewIdx)
713
+ this.handleViewSwitch(idx);
714
+ } }, allViews.map((v, i) => (h("option", { value: String(i), selected: i === this.activeViewIdx }, v.label)))))), hasActions && (h("div", { class: "mrd-table__toolbar-right" }, actions.map(a => {
570
715
  var _a;
571
- return (h("option", { value: v.name }, (_a = v.label) !== null && _a !== void 0 ? _a : v.name));
572
- })))), hasActions && (h("div", { class: "mrd-table__toolbar-right" }, this.actions.map(a => {
573
- var _a;
574
- return (h("button", { class: `mrd-table__action mrd-table__action--${(_a = a.variant) !== null && _a !== void 0 ? _a : 'secondary'}`, disabled: a.disabled, onClick: () => this.mrdAction.emit({ action: a.action }) }, a.icon
716
+ return (h("button", { class: `mrd-table__action mrd-table__action--${(_a = a.variant) !== null && _a !== void 0 ? _a : 'secondary'}`, disabled: a.disabled, onClick: () => this.mrdAction.emit(this.buildActionDetail(a.action)) }, a.icon
575
717
  ? h("svg", { class: "mrd-table__action-icon", "aria-hidden": "true" }, h("use", { href: a.icon }))
576
718
  : a.label, h("span", { class: "mrd-table__action-tooltip" }, a.label)));
577
719
  })))));
@@ -826,18 +968,18 @@ export class MrdTable {
826
968
  }
827
969
  static get properties() {
828
970
  return {
829
- "columns": {
971
+ "item": {
830
972
  "type": "unknown",
831
973
  "mutable": false,
832
974
  "complexType": {
833
- "original": "TableColumn[]",
834
- "resolved": "ClientLayoutItem[]",
975
+ "original": "ClientLayoutItem | null",
976
+ "resolved": "ClientLayoutItem | null",
835
977
  "references": {
836
- "TableColumn": {
978
+ "ClientLayoutItem": {
837
979
  "location": "import",
838
- "path": "../../utils/cell-renderer",
839
- "id": "src/utils/cell-renderer.ts::TableColumn",
840
- "referenceLocation": "TableColumn"
980
+ "path": "../../types/client-layout",
981
+ "id": "src/types/client-layout.ts::ClientLayoutItem",
982
+ "referenceLocation": "ClientLayoutItem"
841
983
  }
842
984
  }
843
985
  },
@@ -845,11 +987,31 @@ export class MrdTable {
845
987
  "optional": false,
846
988
  "docs": {
847
989
  "tags": [],
848
- "text": ""
990
+ "text": "The VIEW or RELATED_VIEW layout item. Contains view config, dataClass, fromClass, actions etc."
849
991
  },
850
992
  "getter": false,
851
993
  "setter": false,
852
- "defaultValue": "[]"
994
+ "defaultValue": "null"
995
+ },
996
+ "parentId": {
997
+ "type": "string",
998
+ "mutable": false,
999
+ "complexType": {
1000
+ "original": "string",
1001
+ "resolved": "string",
1002
+ "references": {}
1003
+ },
1004
+ "required": false,
1005
+ "optional": false,
1006
+ "docs": {
1007
+ "tags": [],
1008
+ "text": "Parent record id \u2014 required for RELATED_VIEW to build /{fromClass}/{parentId}/{dataClass}."
1009
+ },
1010
+ "getter": false,
1011
+ "setter": false,
1012
+ "reflect": false,
1013
+ "attribute": "parent-id",
1014
+ "defaultValue": "''"
853
1015
  },
854
1016
  "rows": {
855
1017
  "type": "unknown",
@@ -973,101 +1135,12 @@ export class MrdTable {
973
1135
  "reflect": false,
974
1136
  "attribute": "table-height",
975
1137
  "defaultValue": "500"
976
- },
977
- "defaultSort": {
978
- "type": "string",
979
- "mutable": false,
980
- "complexType": {
981
- "original": "string",
982
- "resolved": "string",
983
- "references": {}
984
- },
985
- "required": false,
986
- "optional": false,
987
- "docs": {
988
- "tags": [],
989
- "text": "Initial sort applied on load, e.g. \"timestamp,desc\" or \"name\".\nParsed by init() into sortField + sortDir."
990
- },
991
- "getter": false,
992
- "setter": false,
993
- "reflect": false,
994
- "attribute": "default-sort",
995
- "defaultValue": "''"
996
- },
997
- "actions": {
998
- "type": "unknown",
999
- "mutable": false,
1000
- "complexType": {
1001
- "original": "TableAction[]",
1002
- "resolved": "TableAction[]",
1003
- "references": {
1004
- "TableAction": {
1005
- "location": "import",
1006
- "path": "../../utils/cell-renderer",
1007
- "id": "src/utils/cell-renderer.ts::TableAction",
1008
- "referenceLocation": "TableAction"
1009
- }
1010
- }
1011
- },
1012
- "required": false,
1013
- "optional": false,
1014
- "docs": {
1015
- "tags": [],
1016
- "text": "Toolbar action buttons rendered above the table."
1017
- },
1018
- "getter": false,
1019
- "setter": false,
1020
- "defaultValue": "[]"
1021
- },
1022
- "viewLabel": {
1023
- "type": "string",
1024
- "mutable": false,
1025
- "complexType": {
1026
- "original": "string",
1027
- "resolved": "string",
1028
- "references": {}
1029
- },
1030
- "required": false,
1031
- "optional": false,
1032
- "docs": {
1033
- "tags": [],
1034
- "text": "Display label of the current view \u2014 shown in the toolbar center as a view picker trigger."
1035
- },
1036
- "getter": false,
1037
- "setter": false,
1038
- "reflect": false,
1039
- "attribute": "view-label",
1040
- "defaultValue": "''"
1041
- },
1042
- "alternativeViews": {
1043
- "type": "unknown",
1044
- "mutable": false,
1045
- "complexType": {
1046
- "original": "AlternativeView[]",
1047
- "resolved": "AlternativeView[]",
1048
- "references": {
1049
- "AlternativeView": {
1050
- "location": "import",
1051
- "path": "../../utils/cell-renderer",
1052
- "id": "src/utils/cell-renderer.ts::AlternativeView",
1053
- "referenceLocation": "AlternativeView"
1054
- }
1055
- }
1056
- },
1057
- "required": false,
1058
- "optional": false,
1059
- "docs": {
1060
- "tags": [],
1061
- "text": "Alternative views available for this table; renders a dropdown when non-empty."
1062
- },
1063
- "getter": false,
1064
- "setter": false,
1065
- "defaultValue": "[]"
1066
1138
  }
1067
1139
  };
1068
1140
  }
1069
1141
  static get states() {
1070
1142
  return {
1143
+ "activeViewIdx": {},
1071
1144
  "loadedPages": {},
1072
1145
  "requestedPages": {},
1073
1146
  "renderStart": {},
@@ -1098,8 +1171,8 @@ export class MrdTable {
1098
1171
  "text": "Fired when a page needs to be fetched. Host fetches and calls setPage().\n`sort` is the raw query-param value, e.g. \"name\" or \"name,desc\"."
1099
1172
  },
1100
1173
  "complexType": {
1101
- "original": "{ page: number; sort: string }",
1102
- "resolved": "{ page: number; sort: string; }",
1174
+ "original": "{ page: number; sort: string; path: string; qs: string }",
1175
+ "resolved": "{ page: number; sort: string; path: string; qs: string; }",
1103
1176
  "references": {}
1104
1177
  }
1105
1178
  }, {
@@ -1130,35 +1203,13 @@ export class MrdTable {
1130
1203
  "composed": true,
1131
1204
  "docs": {
1132
1205
  "tags": [],
1133
- "text": "Fired when a toolbar action button is clicked. Detail contains the action identifier."
1206
+ "text": "Fired when a toolbar action button is clicked.\nFor 'export': includes `path` (relative excel path) and `qs` (current sort+filter params).\nFor 'create': includes `dataClass` (target type) and `parentPath` (e.g. /buyers/123 for RELATED_VIEW)."
1134
1207
  },
1135
1208
  "complexType": {
1136
- "original": "{ action: string }",
1137
- "resolved": "{ action: string; }",
1209
+ "original": "{ action: string; path?: string; qs?: string; dataClass?: string; parentPath?: string | null }",
1210
+ "resolved": "{ action: string; path?: string | undefined; qs?: string | undefined; dataClass?: string | undefined; parentPath?: string | null | undefined; }",
1138
1211
  "references": {}
1139
1212
  }
1140
- }, {
1141
- "method": "mrdFilter",
1142
- "name": "mrdFilter",
1143
- "bubbles": true,
1144
- "cancelable": true,
1145
- "composed": true,
1146
- "docs": {
1147
- "tags": [],
1148
- "text": "Fired when active filters change. Host translates filters to API query params."
1149
- },
1150
- "complexType": {
1151
- "original": "{ filters: ColumnFilter[] }",
1152
- "resolved": "{ filters: ColumnFilter[]; }",
1153
- "references": {
1154
- "ColumnFilter": {
1155
- "location": "import",
1156
- "path": "../../utils/cell-renderer",
1157
- "id": "src/utils/cell-renderer.ts::ColumnFilter",
1158
- "referenceLocation": "ColumnFilter"
1159
- }
1160
- }
1161
- }
1162
1213
  }, {
1163
1214
  "method": "mrdDownload",
1164
1215
  "name": "mrdDownload",
@@ -1174,21 +1225,6 @@ export class MrdTable {
1174
1225
  "resolved": "{ href: string; fileName: string; }",
1175
1226
  "references": {}
1176
1227
  }
1177
- }, {
1178
- "method": "mrdSwitchView",
1179
- "name": "mrdSwitchView",
1180
- "bubbles": true,
1181
- "cancelable": true,
1182
- "composed": true,
1183
- "docs": {
1184
- "tags": [],
1185
- "text": "Fired when the user selects an alternative view from the view switcher dropdown."
1186
- },
1187
- "complexType": {
1188
- "original": "{ name: string; class?: string }",
1189
- "resolved": "{ name: string; class?: string | undefined; }",
1190
- "references": {}
1191
- }
1192
1228
  }, {
1193
1229
  "method": "mrdLoadAggregations",
1194
1230
  "name": "mrdLoadAggregations",
@@ -1200,8 +1236,8 @@ export class MrdTable {
1200
1236
  "text": "Fired when aggregation totals need to be (re-)fetched.\nDetail contains the fields grouped by aggregate function.\nHost calls the /aggregations endpoint and passes the result to setAggregations()."
1201
1237
  },
1202
1238
  "complexType": {
1203
- "original": "{ sum?: string[]; avg?: string[]; count?: string[] }",
1204
- "resolved": "{ sum?: string[] | undefined; avg?: string[] | undefined; count?: string[] | undefined; }",
1239
+ "original": "{ sum?: string[]; avg?: string[]; count?: string[]; path: string; qs: string }",
1240
+ "resolved": "{ sum?: string[] | undefined; avg?: string[] | undefined; count?: string[] | undefined; path: string; qs: string; }",
1205
1241
  "references": {}
1206
1242
  }
1207
1243
  }];
@@ -1293,8 +1329,8 @@ export class MrdTable {
1293
1329
  "propName": "totalElements",
1294
1330
  "methodName": "totalElementsChanged"
1295
1331
  }, {
1296
- "propName": "defaultSort",
1297
- "methodName": "defaultSortChanged"
1332
+ "propName": "item",
1333
+ "methodName": "itemChanged"
1298
1334
  }];
1299
1335
  }
1300
1336
  }
@@ -51,14 +51,31 @@ async function apiFetchDashboard(token, tenantCode, pluralName) {
51
51
  return body; // { layouts, views, _links }
52
52
  }
53
53
 
54
- function applyViewFilter(href, viewFilter) {
55
- if (!viewFilter?.length) return href;
56
- const params = new URLSearchParams();
57
- viewFilter.forEach(f => { if (f.name && f.value != null) params.set(f.name, String(f.value)); });
58
- const qs = params.toString();
59
- return qs ? `${href}?${qs}` : href;
54
+ async function apiFetchClassDashboard(token, tenantCode, pluralName, name) {
55
+ const qs = new URLSearchParams({ language: navigator.language });
56
+ if (name) qs.set('name', name);
57
+ const { ok, status, body } = await apiRequest('GET', `/metadata/${tenantCode}/dashboard/${pluralName}?${qs}`, token);
58
+ if (!ok) throw new Error(`${status}: ${typeof body === 'string' ? body : JSON.stringify(body)}`);
59
+ return body;
60
+ }
61
+
62
+ async function apiFetchGeneralDashboard(token, tenantCode, name) {
63
+ const qs = new URLSearchParams({ language: navigator.language });
64
+ if (name) qs.set('name', name);
65
+ const { ok, status, body } = await apiRequest('GET', `/metadata/${tenantCode}/dashboard?${qs}`, token);
66
+ if (!ok) throw new Error(`${status}: ${typeof body === 'string' ? body : JSON.stringify(body)}`);
67
+ return body;
60
68
  }
61
69
 
70
+ async function apiFetchNavigationPane(token, tenantCode, name) {
71
+ const qs = new URLSearchParams({ language: navigator.language });
72
+ if (name) qs.set('name', name);
73
+ const { ok, status, body } = await apiRequest('GET', `/metadata/${tenantCode}/navigationPane?${qs}`, token);
74
+ if (!ok) throw new Error(`${status}: ${typeof body === 'string' ? body : JSON.stringify(body)}`);
75
+ return body;
76
+ }
77
+
78
+
62
79
  async function apiFetchPage(token, baseHref, pageNumber, sort = '') {
63
80
  const sep = baseHref.includes('?') ? '&' : '?';
64
81
  let url = `${baseHref}${sep}page=${pageNumber}`;