@masterteam/client-components 0.0.61 → 0.0.63
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/client-components.css +1 -1
- package/fesm2022/masterteam-client-components-client-instance-preview.mjs +40 -6
- package/fesm2022/masterteam-client-components-client-instance-preview.mjs.map +1 -1
- package/fesm2022/masterteam-client-components-client-list.mjs +103 -60
- package/fesm2022/masterteam-client-components-client-list.mjs.map +1 -1
- package/package.json +4 -4
- package/types/masterteam-client-components-client-list.d.ts +3 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { inject, Injectable, signal, computed, input, output, Component, effect,
|
|
2
|
+
import { inject, Injectable, signal, computed, input, output, Component, effect, untracked } from '@angular/core';
|
|
3
3
|
import * as i1 from '@angular/common';
|
|
4
|
-
import { CommonModule
|
|
4
|
+
import { CommonModule } from '@angular/common';
|
|
5
5
|
import * as i4 from '@jsverse/transloco';
|
|
6
6
|
import { TranslocoModule, TranslocoPipe, TranslocoService } from '@jsverse/transloco';
|
|
7
7
|
import { Button } from '@masterteam/components/button';
|
|
@@ -18,7 +18,6 @@ import { ConfirmationService } from '@masterteam/components/confirmation';
|
|
|
18
18
|
import { MTDateFormatPipe } from '@masterteam/components';
|
|
19
19
|
import * as i3 from 'primeng/popover';
|
|
20
20
|
import { PopoverModule } from 'primeng/popover';
|
|
21
|
-
import { Scroller } from 'primeng/scroller';
|
|
22
21
|
import { DashboardViewer } from '@masterteam/dashboard-builder';
|
|
23
22
|
|
|
24
23
|
const DEFAULT_INCLUDE_STATE = ['escalation'];
|
|
@@ -831,36 +830,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
|
|
|
831
830
|
args: [{ selector: 'mt-client-list-table-view', standalone: true, imports: [CommonModule, 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 @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 [noCard]=\"true\"\r\n [data]=\"table().rows\"\r\n [columns]=\"table().columns\"\r\n dataKey=\"Id\"\r\n [storageKey]=\"tableStorageKey()\"\r\n [persistStateExclude]=\"table().config.table.persistStateExclude\"\r\n [rowActions]=\"rowActions()\"\r\n [actionShape]=\"actionShape()\"\r\n [rowActionsLoadingFn]=\"rowActionsLoadingFn()\"\r\n (rowActionsRequested)=\"rowActionsRequested.emit($event)\"\r\n [generalSearch]=\"table().config.table.searchable\"\r\n [showFilters]=\"true\"\r\n filterMode=\"column\"\r\n [printable]=\"true\"\r\n [groupable]=\"true\"\r\n [cellClickFilter]=\"true\"\r\n [freezeActions]=\"true\"\r\n [exportable]=\"table().config.table.exportable\"\r\n [loading]=\"table().loading\"\r\n [clickableRows]=\"true\"\r\n [lazy]=\"table().config.isPaginated\"\r\n [lazyTotalRecords]=\"table().totalCount\"\r\n [virtualScroll]=\"!table().config.isPaginated\"\r\n [virtualScrollItemSize]=\"56\"\r\n scrollHeight=\"520px\"\r\n [pageSize]=\"pageSize()\"\r\n [filters]=\"bucket().filters()\"\r\n (filtersChange)=\"bucket().filters.set($event)\"\r\n [filterTerm]=\"bucket().filterTerm()\"\r\n (filterTermChange)=\"bucket().filterTerm.set($event)\"\r\n [groupBy]=\"bucket().groupBy()\"\r\n (groupByChange)=\"bucket().groupBy.set($event)\"\r\n [activeTab]=\"bucket().activeTab()\"\r\n (activeTabChange)=\"bucket().activeTab.set($event)\"\r\n (lazyLoad)=\"lazyLoad.emit($event)\"\r\n (rowClick)=\"rowClick.emit($event)\"\r\n />\r\n }\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" }]
|
|
832
831
|
}], propDecorators: { state: [{ type: i0.Input, args: [{ isSignal: true, alias: "state", required: true }] }], rowActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowActions", required: false }] }], actionShape: [{ type: i0.Input, args: [{ isSignal: true, alias: "actionShape", required: false }] }], rowActionsLoadingFn: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowActionsLoadingFn", required: false }] }], lazyLoad: [{ type: i0.Output, args: ["lazyLoad"] }], rowClick: [{ type: i0.Output, args: ["rowClick"] }], rowActionsRequested: [{ type: i0.Output, args: ["rowActionsRequested"] }] } });
|
|
833
832
|
|
|
834
|
-
const CLIENT_LIST_CARD_VIRTUALIZATION_THRESHOLD = 60;
|
|
835
|
-
function clientListCardsPerRow(viewportWidth) {
|
|
836
|
-
if (viewportWidth >= 1280)
|
|
837
|
-
return 3;
|
|
838
|
-
if (viewportWidth >= 768)
|
|
839
|
-
return 2;
|
|
840
|
-
return 1;
|
|
841
|
-
}
|
|
842
|
-
function chunkClientListCards(cards, cardsPerRow) {
|
|
843
|
-
const width = Math.max(1, Math.floor(cardsPerRow));
|
|
844
|
-
const rows = [];
|
|
845
|
-
for (let index = 0; index < cards.length; index += width) {
|
|
846
|
-
rows.push(cards.slice(index, index + width));
|
|
847
|
-
}
|
|
848
|
-
return rows;
|
|
849
|
-
}
|
|
850
|
-
|
|
851
833
|
const EMPTY_KEY_SET = new Set();
|
|
852
|
-
// Client-components can be tested against an already-published components
|
|
853
|
-
// declaration whose EntityViewType predates the Section layout marker.
|
|
854
|
-
const isSectionEntity = (entity) => entity.viewType === 'Section';
|
|
855
834
|
class ClientListCardsView {
|
|
856
835
|
toolbar = inject(ClientListToolbarService);
|
|
857
836
|
exportService = inject(TableExportService);
|
|
858
837
|
dateFormat = inject(MTDateFormatPipe);
|
|
859
838
|
confirmationService = inject(ConfirmationService);
|
|
860
|
-
document = inject(DOCUMENT);
|
|
861
|
-
viewportWidth = signal(this.document.defaultView?.innerWidth ?? 1280, ...(ngDevMode ? [{ debugName: "viewportWidth" }] : /* istanbul ignore next */ []));
|
|
862
|
-
virtualRowHeight = 340;
|
|
863
|
-
virtualScrollHeight = 'max(24rem, calc(100dvh - 18rem))';
|
|
864
839
|
state = input.required(...(ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []));
|
|
865
840
|
rowActions = input([], ...(ngDevMode ? [{ debugName: "rowActions" }] : /* istanbul ignore next */ []));
|
|
866
841
|
/** Per-card loading predicate — drives the popover skeleton while runtime
|
|
@@ -892,7 +867,7 @@ class ClientListCardsView {
|
|
|
892
867
|
return [];
|
|
893
868
|
// Section markers are card-layout only — never table columns.
|
|
894
869
|
return sampleCard.entities
|
|
895
|
-
.filter((entity) =>
|
|
870
|
+
.filter((entity) => entity.viewType !== 'Section')
|
|
896
871
|
.map((entity) => {
|
|
897
872
|
const raw = entity;
|
|
898
873
|
const key = (typeof raw['key'] === 'string' && raw['key']) ||
|
|
@@ -924,7 +899,7 @@ class ClientListCardsView {
|
|
|
924
899
|
}
|
|
925
900
|
: { Id: card.id, [CLIENT_LIST_RECORD_STATE_KEY]: card.state ?? null };
|
|
926
901
|
for (const entity of card.entities ?? []) {
|
|
927
|
-
if (
|
|
902
|
+
if (entity.viewType === 'Section')
|
|
928
903
|
continue;
|
|
929
904
|
const e = entity;
|
|
930
905
|
const key = (typeof e['key'] === 'string' && e['key']) ||
|
|
@@ -1081,14 +1056,6 @@ class ClientListCardsView {
|
|
|
1081
1056
|
});
|
|
1082
1057
|
});
|
|
1083
1058
|
}, ...(ngDevMode ? [{ debugName: "visibleCards" }] : /* istanbul ignore next */ []));
|
|
1084
|
-
shouldVirtualizeCards = computed(() => this.visibleCards().length > CLIENT_LIST_CARD_VIRTUALIZATION_THRESHOLD, ...(ngDevMode ? [{ debugName: "shouldVirtualizeCards" }] : /* istanbul ignore next */ []));
|
|
1085
|
-
virtualCardRows = computed(() => chunkClientListCards(this.visibleCards(), clientListCardsPerRow(this.viewportWidth())), ...(ngDevMode ? [{ debugName: "virtualCardRows" }] : /* istanbul ignore next */ []));
|
|
1086
|
-
trackVirtualRow = (_index, row) => row.map((card) => card.id).join('|');
|
|
1087
|
-
updateViewportWidth() {
|
|
1088
|
-
const width = this.document.defaultView?.innerWidth;
|
|
1089
|
-
if (width)
|
|
1090
|
-
this.viewportWidth.set(width);
|
|
1091
|
-
}
|
|
1092
1059
|
/**
|
|
1093
1060
|
* Grouped cards. Reuses TableGroupingController helpers so the accent color,
|
|
1094
1061
|
* label, and count rendering match the table's subheader exactly.
|
|
@@ -1302,7 +1269,8 @@ class ClientListCardsView {
|
|
|
1302
1269
|
});
|
|
1303
1270
|
}
|
|
1304
1271
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: ClientListCardsView, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1305
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: ClientListCardsView, isStandalone: true, selector: "mt-client-list-cards-view", inputs: { state: { classPropertyName: "state", publicName: "state", isSignal: true, isRequired: true, transformFunction: null }, rowActions: { classPropertyName: "rowActions", publicName: "rowActions", isSignal: true, isRequired: false, transformFunction: null }, rowActionsLoadingFn: { classPropertyName: "rowActionsLoadingFn", publicName: "rowActionsLoadingFn", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { lazyLoad: "lazyLoad", cardClick: "cardClick", rowActionsRequested: "rowActionsRequested" }, host: { listeners: { "window:resize": "updateViewportWidth()" } }, providers: [MTDateFormatPipe], ngImport: i0, template: "<mt-table-caption\n [generalSearch]=\"true\"\n [showFilters]=\"true\"\n filterMode=\"popover\"\n [printable]=\"true\"\n [groupable]=\"true\"\n [exportable]=\"true\"\n [columns]=\"columns()\"\n [data]=\"rowRepresentations()\"\n [groupColumns]=\"grouping.groupableColumns()\"\n [filters]=\"bucket().filters()\"\n (filtersChange)=\"bucket().filters.set($event)\"\n [filterTerm]=\"bucket().filterTerm()\"\n (filterTermChange)=\"bucket().filterTerm.set($event)\"\n [groupBy]=\"bucket().groupBy()\"\n (groupByChange)=\"bucket().groupBy.set($event)\"\n [activeTab]=\"bucket().activeTab()\"\n (activeTabChange)=\"bucket().activeTab.set($event)\"\n (exportRequested)=\"exportExcel()\"\n (printRequested)=\"printPdf()\"\n/>\n\n@if (cardsState().loading && cardsState().cards.length === 0) {\n <div class=\"grid grid-cols-1 gap-6 md:grid-cols-2 xl:grid-cols-3\">\n @for (i of [1, 2, 3, 4, 5, 6]; track i) {\n <p-skeleton height=\"20rem\" class=\"rounded-lg\" />\n }\n </div>\n} @else if (cardsState().error) {\n <div class=\"rounded-lg border border-red-200 bg-red-50 p-6 text-red-600\">\n {{ cardsState().error }}\n </div>\n} @else if (visibleCards().length === 0) {\n <div class=\"p-6 text-center text-gray-400\">\n {{ \"components.clientComponents.list.noCardsFound\" | transloco }}\n </div>\n} @else if (grouping.groupingActive()) {\n <div class=\"flex flex-col gap-6\">\n @for (group of groupedCards(); track group.key) {\n <section class=\"flex flex-col gap-3\">\n <header\n class=\"flex items-center gap-3 px-2 py-2 bg-surface-50 dark:bg-surface-900 border-y border-surface-200 dark:border-surface-700 rounded\"\n [style.--mt-group-accent]=\"group.accent ?? 'var(--p-primary-color)'\"\n >\n <span\n class=\"inline-block w-1 h-5 rounded-full\"\n [style.background]=\"'var(--mt-group-accent)'\"\n ></span>\n <span\n class=\"text-xs uppercase tracking-wide text-gray-500 dark:text-gray-400\"\n >\n {{ group.columnLabel }}\n </span>\n <span class=\"text-sm font-semibold text-gray-900 dark:text-gray-100\">\n {{ group.label }}\n </span>\n <span\n class=\"ml-auto inline-flex items-center gap-1 text-xs px-2 py-0.5 rounded-full bg-primary-50 text-primary-700 dark:bg-primary-950 dark:text-primary-300\"\n >\n {{ group.count }}\n </span>\n </header>\n <div class=\"grid grid-cols-1 gap-5 md:grid-cols-2 xl:grid-cols-3\">\n @for (card of group.cards; track card.id) {\n <mt-card\n class=\"relative cursor-pointer p-5 shadow-sm transition-shadow hover:shadow-md\"\n (click)=\"cardClick.emit(card)\"\n >\n <ng-template #headless>\n <ng-container\n [ngTemplateOutlet]=\"cardActionsMenu\"\n [ngTemplateOutletContext]=\"{ $implicit: card }\"\n />\n <mt-entities-preview\n [entities]=\"card.entities\"\n attachmentShape=\"compact\"\n [stackOnNarrow]=\"false\"\n [clickableKeys]=\"clickableEntityKeys()\"\n [activeKeys]=\"activeKeysForCard(card)\"\n [clickableTooltip]=\"\n 'components.table.filterByThis' | transloco\n \"\n [activeTooltip]=\"'components.table.clearFilter' | transloco\"\n (entityClick)=\"onEntityFilterClick(card, $event)\"\n />\n </ng-template>\n </mt-card>\n }\n </div>\n </section>\n }\n </div>\n} @else {\n @if (shouldVirtualizeCards()) {\n <p-scroller\n [items]=\"virtualCardRows()\"\n [itemSize]=\"virtualRowHeight\"\n [scrollHeight]=\"virtualScrollHeight\"\n [numToleratedItems]=\"2\"\n [trackBy]=\"trackVirtualRow\"\n styleClass=\"w-full\"\n >\n <ng-template #item let-row>\n <div\n class=\"grid grid-cols-1 gap-5 pb-5 md:grid-cols-2 xl:grid-cols-3\"\n [style.height.px]=\"virtualRowHeight\"\n >\n @for (card of row; track card.id) {\n <div class=\"h-80 overflow-auto rounded-lg\">\n <mt-card\n class=\"relative h-full cursor-pointer p-5 shadow-sm transition-shadow hover:shadow-md\"\n (click)=\"cardClick.emit(card)\"\n >\n <ng-template #headless>\n <ng-container\n [ngTemplateOutlet]=\"cardActionsMenu\"\n [ngTemplateOutletContext]=\"{ $implicit: card }\"\n />\n <mt-entities-preview\n [entities]=\"card.entities\"\n attachmentShape=\"compact\"\n [stackOnNarrow]=\"false\"\n [clickableKeys]=\"clickableEntityKeys()\"\n [activeKeys]=\"activeKeysForCard(card)\"\n [clickableTooltip]=\"\n 'components.table.filterByThis' | transloco\n \"\n [activeTooltip]=\"'components.table.clearFilter' | transloco\"\n (entityClick)=\"onEntityFilterClick(card, $event)\"\n />\n </ng-template>\n </mt-card>\n </div>\n }\n </div>\n </ng-template>\n </p-scroller>\n } @else {\n <div class=\"grid grid-cols-1 gap-5 md:grid-cols-2 xl:grid-cols-3\">\n @for (card of visibleCards(); track card.id) {\n <mt-card\n class=\"relative cursor-pointer p-5 shadow-sm transition-shadow hover:shadow-md\"\n (click)=\"cardClick.emit(card)\"\n >\n <ng-template #headless>\n <ng-container\n [ngTemplateOutlet]=\"cardActionsMenu\"\n [ngTemplateOutletContext]=\"{ $implicit: card }\"\n />\n <mt-entities-preview\n [entities]=\"card.entities\"\n attachmentShape=\"compact\"\n [stackOnNarrow]=\"false\"\n [clickableKeys]=\"clickableEntityKeys()\"\n [activeKeys]=\"activeKeysForCard(card)\"\n [clickableTooltip]=\"'components.table.filterByThis' | transloco\"\n [activeTooltip]=\"'components.table.clearFilter' | transloco\"\n (entityClick)=\"onEntityFilterClick(card, $event)\"\n />\n </ng-template>\n </mt-card>\n }\n </div>\n }\n}\n\n<!--\n Per-card 3-dots actions menu \u2014 same contract as the table view's popover\n row-actions. Rendered into each card's top-end corner; clicks stop at the\n wrapper so the card's own click (cardClick) doesn't fire.\n-->\n<ng-template #cardActionsMenu let-card>\n @if (rowActions().length > 0) {\n <div\n class=\"absolute end-2 top-2 z-10 flex items-center gap-1\"\n (click)=\"$event.stopPropagation()\"\n >\n @for (action of inlineActionsForCard(card); track $index) {\n <mt-button\n [icon]=\"action.icon\"\n [severity]=\"action.color\"\n [variant]=\"action.variant\"\n [size]=\"actionSize(action) || 'small'\"\n [tooltip]=\"action.tooltip\"\n [label]=\"action.label\"\n [loading]=\"resolveActionLoading(action, card)\"\n (onClick)=\"runCardAction($event, action, card)\"\n />\n }\n @if (showCardActionsMenu(card)) {\n <mt-button\n icon=\"general.dots-vertical\"\n severity=\"secondary\"\n variant=\"text\"\n [tooltip]=\"'components.table.actions' | transloco\"\n (onClick)=\"onCardActionsToggle($event, cardPopover, card)\"\n />\n <p-popover #cardPopover appendTo=\"body\">\n <div class=\"flex min-w-44 flex-col gap-1 p-1\">\n @if (isCardActionsLoading(card)) {\n <div class=\"flex flex-col gap-2 p-1\">\n <p-skeleton height=\"1.75rem\" />\n <p-skeleton height=\"1.75rem\" />\n <p-skeleton height=\"1.75rem\" />\n </div>\n } @else {\n @for (action of menuActionsForCard(card); track $index) {\n <button\n type=\"button\"\n class=\"flex cursor-pointer items-center gap-2 rounded px-2 py-2 text-start text-sm transition-colors\"\n [class]=\"\n action.color === 'danger'\n ? 'text-red-600 hover:bg-red-50 dark:hover:bg-red-950'\n : 'text-gray-700 hover:bg-gray-100 dark:text-gray-200 dark:hover:bg-surface-700'\n \"\n (click)=\"\n runCardAction($event, action, card); cardPopover.hide()\n \"\n >\n @if (action.icon) {\n <mt-icon [icon]=\"action.icon\" class=\"text-base\" />\n }\n <span>{{ action.label || action.tooltip }}</span>\n </button>\n }\n }\n </div>\n </p-popover>\n }\n </div>\n }\n</ng-template>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: TranslocoModule }, { kind: "component", type: Card, selector: "mt-card", inputs: ["class", "title", "paddingless"] }, { 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: Icon, selector: "mt-icon", inputs: ["icon"] }, { kind: "component", type: EntitiesPreview, selector: "mt-entities-preview", inputs: ["entities", "attachmentShape", "stackOnNarrow", "clickableKeys", "activeKeys", "clickableTooltip", "activeTooltip"], outputs: ["entityClick"] }, { kind: "ngmodule", type: SkeletonModule }, { kind: "component", type: i2.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "shape", "animation", "borderRadius", "size", "width", "height"] }, { kind: "ngmodule", type: PopoverModule }, { kind: "component", type: i3.Popover, selector: "p-popover", inputs: ["ariaLabel", "ariaLabelledBy", "dismissable", "style", "styleClass", "appendTo", "autoZIndex", "ariaCloseLabel", "baseZIndex", "focusOnShow", "showTransitionOptions", "hideTransitionOptions", "motionOptions"], outputs: ["onShow", "onHide"] }, { kind: "component", type: Scroller, selector: "p-scroller, p-virtualscroller, p-virtual-scroller, p-virtualScroller", inputs: ["hostName", "id", "style", "styleClass", "tabindex", "items", "itemSize", "scrollHeight", "scrollWidth", "orientation", "step", "delay", "resizeDelay", "appendOnly", "inline", "lazy", "disabled", "loaderDisabled", "columns", "showSpacer", "showLoader", "numToleratedItems", "loading", "autoSize", "trackBy", "options"], outputs: ["onLazyLoad", "onScroll", "onScrollIndexChange"] }, { kind: "component", type: TableCaption, selector: "mt-table-caption", inputs: ["generalSearch", "showFilters", "filterMode", "exportable", "printable", "groupable", "columns", "data", "groupColumns", "tabs", "tabsOptionLabel", "tabsOptionValue", "actions", "captionStart", "captionEnd", "activeTab", "filters", "filterTerm", "groupBy"], outputs: ["activeTabChange", "filtersChange", "filterTermChange", "groupByChange", "exportRequested", "printRequested", "onTabChange", "searchChange", "filterApplied", "filterReset"] }, { kind: "pipe", type: i4.TranslocoPipe, name: "transloco" }] });
|
|
1272
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: ClientListCardsView, isStandalone: true, selector: "mt-client-list-cards-view", inputs: { state: { classPropertyName: "state", publicName: "state", isSignal: true, isRequired: true, transformFunction: null }, rowActions: { classPropertyName: "rowActions", publicName: "rowActions", isSignal: true, isRequired: false, transformFunction: null }, rowActionsLoadingFn: { classPropertyName: "rowActionsLoadingFn", publicName: "rowActionsLoadingFn", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { lazyLoad: "lazyLoad", cardClick: "cardClick", rowActionsRequested: "rowActionsRequested" }, providers: [MTDateFormatPipe], ngImport: i0, template: "<mt-table-caption\r\n [generalSearch]=\"true\"\r\n [showFilters]=\"true\"\r\n filterMode=\"popover\"\r\n [printable]=\"true\"\r\n [groupable]=\"true\"\r\n [exportable]=\"true\"\r\n [columns]=\"columns()\"\r\n [data]=\"rowRepresentations()\"\r\n [groupColumns]=\"grouping.groupableColumns()\"\r\n [filters]=\"bucket().filters()\"\r\n (filtersChange)=\"bucket().filters.set($event)\"\r\n [filterTerm]=\"bucket().filterTerm()\"\r\n (filterTermChange)=\"bucket().filterTerm.set($event)\"\r\n [groupBy]=\"bucket().groupBy()\"\r\n (groupByChange)=\"bucket().groupBy.set($event)\"\r\n [activeTab]=\"bucket().activeTab()\"\r\n (activeTabChange)=\"bucket().activeTab.set($event)\"\r\n (exportRequested)=\"exportExcel()\"\r\n (printRequested)=\"printPdf()\"\r\n/>\r\n\r\n@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\" class=\"rounded-lg\" />\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 (visibleCards().length === 0) {\r\n <div class=\"p-6 text-center text-gray-400\">\r\n {{ \"components.clientComponents.list.noCardsFound\" | transloco }}\r\n </div>\r\n} @else if (grouping.groupingActive()) {\r\n <div class=\"flex flex-col gap-6\">\r\n @for (group of groupedCards(); track group.key) {\r\n <section class=\"flex flex-col gap-3\">\r\n <header\r\n class=\"flex items-center gap-3 px-2 py-2 bg-surface-50 dark:bg-surface-900 border-y border-surface-200 dark:border-surface-700 rounded\"\r\n [style.--mt-group-accent]=\"group.accent ?? 'var(--p-primary-color)'\"\r\n >\r\n <span\r\n class=\"inline-block w-1 h-5 rounded-full\"\r\n [style.background]=\"'var(--mt-group-accent)'\"\r\n ></span>\r\n <span\r\n class=\"text-xs uppercase tracking-wide text-gray-500 dark:text-gray-400\"\r\n >\r\n {{ group.columnLabel }}\r\n </span>\r\n <span class=\"text-sm font-semibold text-gray-900 dark:text-gray-100\">\r\n {{ group.label }}\r\n </span>\r\n <span\r\n class=\"ml-auto inline-flex items-center gap-1 text-xs px-2 py-0.5 rounded-full bg-primary-50 text-primary-700 dark:bg-primary-950 dark:text-primary-300\"\r\n >\r\n {{ group.count }}\r\n </span>\r\n </header>\r\n <div class=\"grid grid-cols-1 gap-5 md:grid-cols-2 xl:grid-cols-3\">\r\n @for (card of group.cards; track card.id) {\r\n <mt-card\r\n class=\"relative cursor-pointer p-5 shadow-sm transition-shadow hover:shadow-md\"\r\n (click)=\"cardClick.emit(card)\"\r\n >\r\n <ng-template #headless>\r\n <ng-container\r\n [ngTemplateOutlet]=\"cardActionsMenu\"\r\n [ngTemplateOutletContext]=\"{ $implicit: card }\"\r\n />\r\n <mt-entities-preview\r\n [entities]=\"card.entities\"\r\n attachmentShape=\"compact\"\r\n [stackOnNarrow]=\"false\"\r\n [clickableKeys]=\"clickableEntityKeys()\"\r\n [activeKeys]=\"activeKeysForCard(card)\"\r\n [clickableTooltip]=\"\r\n 'components.table.filterByThis' | transloco\r\n \"\r\n [activeTooltip]=\"'components.table.clearFilter' | transloco\"\r\n (entityClick)=\"onEntityFilterClick(card, $event)\"\r\n />\r\n </ng-template>\r\n </mt-card>\r\n }\r\n </div>\r\n </section>\r\n }\r\n </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 visibleCards(); track card.id) {\r\n @defer (on viewport) {\r\n <mt-card\r\n class=\"relative cursor-pointer p-5 shadow-sm transition-shadow hover:shadow-md\"\r\n (click)=\"cardClick.emit(card)\"\r\n >\r\n <ng-template #headless>\r\n <ng-container\r\n [ngTemplateOutlet]=\"cardActionsMenu\"\r\n [ngTemplateOutletContext]=\"{ $implicit: card }\"\r\n />\r\n <mt-entities-preview\r\n [entities]=\"card.entities\"\r\n attachmentShape=\"compact\"\r\n [stackOnNarrow]=\"false\"\r\n [clickableKeys]=\"clickableEntityKeys()\"\r\n [activeKeys]=\"activeKeysForCard(card)\"\r\n [clickableTooltip]=\"'components.table.filterByThis' | transloco\"\r\n [activeTooltip]=\"'components.table.clearFilter' | transloco\"\r\n (entityClick)=\"onEntityFilterClick(card, $event)\"\r\n />\r\n </ng-template>\r\n </mt-card>\r\n } @placeholder {\r\n <div class=\"min-h-[20rem]\">\r\n <p-skeleton height=\"20rem\" class=\"rounded-lg\" />\r\n </div>\r\n }\r\n }\r\n </div>\r\n}\r\n\r\n<!--\r\n Per-card 3-dots actions menu \u2014 same contract as the table view's popover\r\n row-actions. Rendered into each card's top-end corner; clicks stop at the\r\n wrapper so the card's own click (cardClick) doesn't fire.\r\n-->\r\n<ng-template #cardActionsMenu let-card>\r\n @if (rowActions().length > 0) {\r\n <div\r\n class=\"absolute end-2 top-2 z-10 flex items-center gap-1\"\r\n (click)=\"$event.stopPropagation()\"\r\n >\r\n @for (action of inlineActionsForCard(card); track $index) {\r\n <mt-button\r\n [icon]=\"action.icon\"\r\n [severity]=\"action.color\"\r\n [variant]=\"action.variant\"\r\n [size]=\"actionSize(action) || 'small'\"\r\n [tooltip]=\"action.tooltip\"\r\n [label]=\"action.label\"\r\n [loading]=\"resolveActionLoading(action, card)\"\r\n (onClick)=\"runCardAction($event, action, card)\"\r\n />\r\n }\r\n @if (showCardActionsMenu(card)) {\r\n <mt-button\r\n icon=\"general.dots-vertical\"\r\n severity=\"secondary\"\r\n variant=\"text\"\r\n [tooltip]=\"'components.table.actions' | transloco\"\r\n (onClick)=\"onCardActionsToggle($event, cardPopover, card)\"\r\n />\r\n <p-popover #cardPopover appendTo=\"body\">\r\n <div class=\"flex min-w-44 flex-col gap-1 p-1\">\r\n @if (isCardActionsLoading(card)) {\r\n <div class=\"flex flex-col gap-2 p-1\">\r\n <p-skeleton height=\"1.75rem\" />\r\n <p-skeleton height=\"1.75rem\" />\r\n <p-skeleton height=\"1.75rem\" />\r\n </div>\r\n } @else {\r\n @for (action of menuActionsForCard(card); track $index) {\r\n <button\r\n type=\"button\"\r\n class=\"flex cursor-pointer items-center gap-2 rounded px-2 py-2 text-start text-sm transition-colors\"\r\n [class]=\"\r\n action.color === 'danger'\r\n ? 'text-red-600 hover:bg-red-50 dark:hover:bg-red-950'\r\n : 'text-gray-700 hover:bg-gray-100 dark:text-gray-200 dark:hover:bg-surface-700'\r\n \"\r\n (click)=\"\r\n runCardAction($event, action, card); cardPopover.hide()\r\n \"\r\n >\r\n @if (action.icon) {\r\n <mt-icon [icon]=\"action.icon\" class=\"text-base\" />\r\n }\r\n <span>{{ action.label || action.tooltip }}</span>\r\n </button>\r\n }\r\n }\r\n </div>\r\n </p-popover>\r\n }\r\n </div>\r\n }\r\n</ng-template>\r\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: TranslocoModule }, { kind: "component", type: Card, selector: "mt-card", inputs: ["class", "title", "paddingless"] }, { 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: Icon, selector: "mt-icon", inputs: ["icon"] }, { kind: "component", type: EntitiesPreview, selector: "mt-entities-preview", inputs: ["entities", "attachmentShape", "stackOnNarrow", "clickableKeys", "activeKeys", "clickableTooltip", "activeTooltip"], outputs: ["entityClick"] }, { kind: "ngmodule", type: SkeletonModule }, { kind: "component", type: i2.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "shape", "animation", "borderRadius", "size", "width", "height"] }, { kind: "ngmodule", type: PopoverModule }, { kind: "component", type: i3.Popover, selector: "p-popover", inputs: ["ariaLabel", "ariaLabelledBy", "dismissable", "style", "styleClass", "appendTo", "autoZIndex", "ariaCloseLabel", "baseZIndex", "focusOnShow", "showTransitionOptions", "hideTransitionOptions", "motionOptions"], outputs: ["onShow", "onHide"] }, { kind: "component", type: TableCaption, selector: "mt-table-caption", inputs: ["generalSearch", "showFilters", "filterMode", "exportable", "printable", "groupable", "columns", "data", "groupColumns", "tabs", "tabsOptionLabel", "tabsOptionValue", "actions", "captionStart", "captionEnd", "activeTab", "filters", "filterTerm", "groupBy"], outputs: ["activeTabChange", "filtersChange", "filterTermChange", "groupByChange", "exportRequested", "printRequested", "onTabChange", "searchChange", "filterApplied", "filterReset"] }, { kind: "pipe", type: i4.TranslocoPipe, name: "transloco" }], deferBlockDependencies: [() => [i1.NgTemplateOutlet, Card,
|
|
1273
|
+
EntitiesPreview, i4.TranslocoPipe]] });
|
|
1306
1274
|
}
|
|
1307
1275
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: ClientListCardsView, decorators: [{
|
|
1308
1276
|
type: Component,
|
|
@@ -1315,13 +1283,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
|
|
|
1315
1283
|
EntitiesPreview,
|
|
1316
1284
|
SkeletonModule,
|
|
1317
1285
|
PopoverModule,
|
|
1318
|
-
Scroller,
|
|
1319
1286
|
TableCaption,
|
|
1320
|
-
], providers: [MTDateFormatPipe], template: "<mt-table-caption\n [generalSearch]=\"true\"\n [showFilters]=\"true\"\n filterMode=\"popover\"\n [printable]=\"true\"\n [groupable]=\"true\"\n [exportable]=\"true\"\n [columns]=\"columns()\"\n [data]=\"rowRepresentations()\"\n [groupColumns]=\"grouping.groupableColumns()\"\n [filters]=\"bucket().filters()\"\n (filtersChange)=\"bucket().filters.set($event)\"\n [filterTerm]=\"bucket().filterTerm()\"\n (filterTermChange)=\"bucket().filterTerm.set($event)\"\n [groupBy]=\"bucket().groupBy()\"\n (groupByChange)=\"bucket().groupBy.set($event)\"\n [activeTab]=\"bucket().activeTab()\"\n (activeTabChange)=\"bucket().activeTab.set($event)\"\n (exportRequested)=\"exportExcel()\"\n (printRequested)=\"printPdf()\"\n/>\n\n@if (cardsState().loading && cardsState().cards.length === 0) {\n <div class=\"grid grid-cols-1 gap-6 md:grid-cols-2 xl:grid-cols-3\">\n @for (i of [1, 2, 3, 4, 5, 6]; track i) {\n <p-skeleton height=\"20rem\" class=\"rounded-lg\" />\n }\n </div>\n} @else if (cardsState().error) {\n <div class=\"rounded-lg border border-red-200 bg-red-50 p-6 text-red-600\">\n {{ cardsState().error }}\n </div>\n} @else if (visibleCards().length === 0) {\n <div class=\"p-6 text-center text-gray-400\">\n {{ \"components.clientComponents.list.noCardsFound\" | transloco }}\n </div>\n} @else if (grouping.groupingActive()) {\n <div class=\"flex flex-col gap-6\">\n @for (group of groupedCards(); track group.key) {\n <section class=\"flex flex-col gap-3\">\n <header\n class=\"flex items-center gap-3 px-2 py-2 bg-surface-50 dark:bg-surface-900 border-y border-surface-200 dark:border-surface-700 rounded\"\n [style.--mt-group-accent]=\"group.accent ?? 'var(--p-primary-color)'\"\n >\n <span\n class=\"inline-block w-1 h-5 rounded-full\"\n [style.background]=\"'var(--mt-group-accent)'\"\n ></span>\n <span\n class=\"text-xs uppercase tracking-wide text-gray-500 dark:text-gray-400\"\n >\n {{ group.columnLabel }}\n </span>\n <span class=\"text-sm font-semibold text-gray-900 dark:text-gray-100\">\n {{ group.label }}\n </span>\n <span\n class=\"ml-auto inline-flex items-center gap-1 text-xs px-2 py-0.5 rounded-full bg-primary-50 text-primary-700 dark:bg-primary-950 dark:text-primary-300\"\n >\n {{ group.count }}\n </span>\n </header>\n <div class=\"grid grid-cols-1 gap-5 md:grid-cols-2 xl:grid-cols-3\">\n @for (card of group.cards; track card.id) {\n <mt-card\n class=\"relative cursor-pointer p-5 shadow-sm transition-shadow hover:shadow-md\"\n (click)=\"cardClick.emit(card)\"\n >\n <ng-template #headless>\n <ng-container\n [ngTemplateOutlet]=\"cardActionsMenu\"\n [ngTemplateOutletContext]=\"{ $implicit: card }\"\n />\n <mt-entities-preview\n [entities]=\"card.entities\"\n attachmentShape=\"compact\"\n [stackOnNarrow]=\"false\"\n [clickableKeys]=\"clickableEntityKeys()\"\n [activeKeys]=\"activeKeysForCard(card)\"\n [clickableTooltip]=\"\n 'components.table.filterByThis' | transloco\n \"\n [activeTooltip]=\"'components.table.clearFilter' | transloco\"\n (entityClick)=\"onEntityFilterClick(card, $event)\"\n />\n </ng-template>\n </mt-card>\n }\n </div>\n </section>\n }\n </div>\n} @else {\n
|
|
1321
|
-
}], ctorParameters: () => [], propDecorators: { state: [{ type: i0.Input, args: [{ isSignal: true, alias: "state", required: true }] }], rowActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowActions", required: false }] }], rowActionsLoadingFn: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowActionsLoadingFn", required: false }] }], lazyLoad: [{ type: i0.Output, args: ["lazyLoad"] }], cardClick: [{ type: i0.Output, args: ["cardClick"] }], rowActionsRequested: [{ type: i0.Output, args: ["rowActionsRequested"] }]
|
|
1322
|
-
type: HostListener,
|
|
1323
|
-
args: ['window:resize']
|
|
1324
|
-
}] } });
|
|
1287
|
+
], providers: [MTDateFormatPipe], template: "<mt-table-caption\r\n [generalSearch]=\"true\"\r\n [showFilters]=\"true\"\r\n filterMode=\"popover\"\r\n [printable]=\"true\"\r\n [groupable]=\"true\"\r\n [exportable]=\"true\"\r\n [columns]=\"columns()\"\r\n [data]=\"rowRepresentations()\"\r\n [groupColumns]=\"grouping.groupableColumns()\"\r\n [filters]=\"bucket().filters()\"\r\n (filtersChange)=\"bucket().filters.set($event)\"\r\n [filterTerm]=\"bucket().filterTerm()\"\r\n (filterTermChange)=\"bucket().filterTerm.set($event)\"\r\n [groupBy]=\"bucket().groupBy()\"\r\n (groupByChange)=\"bucket().groupBy.set($event)\"\r\n [activeTab]=\"bucket().activeTab()\"\r\n (activeTabChange)=\"bucket().activeTab.set($event)\"\r\n (exportRequested)=\"exportExcel()\"\r\n (printRequested)=\"printPdf()\"\r\n/>\r\n\r\n@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\" class=\"rounded-lg\" />\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 (visibleCards().length === 0) {\r\n <div class=\"p-6 text-center text-gray-400\">\r\n {{ \"components.clientComponents.list.noCardsFound\" | transloco }}\r\n </div>\r\n} @else if (grouping.groupingActive()) {\r\n <div class=\"flex flex-col gap-6\">\r\n @for (group of groupedCards(); track group.key) {\r\n <section class=\"flex flex-col gap-3\">\r\n <header\r\n class=\"flex items-center gap-3 px-2 py-2 bg-surface-50 dark:bg-surface-900 border-y border-surface-200 dark:border-surface-700 rounded\"\r\n [style.--mt-group-accent]=\"group.accent ?? 'var(--p-primary-color)'\"\r\n >\r\n <span\r\n class=\"inline-block w-1 h-5 rounded-full\"\r\n [style.background]=\"'var(--mt-group-accent)'\"\r\n ></span>\r\n <span\r\n class=\"text-xs uppercase tracking-wide text-gray-500 dark:text-gray-400\"\r\n >\r\n {{ group.columnLabel }}\r\n </span>\r\n <span class=\"text-sm font-semibold text-gray-900 dark:text-gray-100\">\r\n {{ group.label }}\r\n </span>\r\n <span\r\n class=\"ml-auto inline-flex items-center gap-1 text-xs px-2 py-0.5 rounded-full bg-primary-50 text-primary-700 dark:bg-primary-950 dark:text-primary-300\"\r\n >\r\n {{ group.count }}\r\n </span>\r\n </header>\r\n <div class=\"grid grid-cols-1 gap-5 md:grid-cols-2 xl:grid-cols-3\">\r\n @for (card of group.cards; track card.id) {\r\n <mt-card\r\n class=\"relative cursor-pointer p-5 shadow-sm transition-shadow hover:shadow-md\"\r\n (click)=\"cardClick.emit(card)\"\r\n >\r\n <ng-template #headless>\r\n <ng-container\r\n [ngTemplateOutlet]=\"cardActionsMenu\"\r\n [ngTemplateOutletContext]=\"{ $implicit: card }\"\r\n />\r\n <mt-entities-preview\r\n [entities]=\"card.entities\"\r\n attachmentShape=\"compact\"\r\n [stackOnNarrow]=\"false\"\r\n [clickableKeys]=\"clickableEntityKeys()\"\r\n [activeKeys]=\"activeKeysForCard(card)\"\r\n [clickableTooltip]=\"\r\n 'components.table.filterByThis' | transloco\r\n \"\r\n [activeTooltip]=\"'components.table.clearFilter' | transloco\"\r\n (entityClick)=\"onEntityFilterClick(card, $event)\"\r\n />\r\n </ng-template>\r\n </mt-card>\r\n }\r\n </div>\r\n </section>\r\n }\r\n </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 visibleCards(); track card.id) {\r\n @defer (on viewport) {\r\n <mt-card\r\n class=\"relative cursor-pointer p-5 shadow-sm transition-shadow hover:shadow-md\"\r\n (click)=\"cardClick.emit(card)\"\r\n >\r\n <ng-template #headless>\r\n <ng-container\r\n [ngTemplateOutlet]=\"cardActionsMenu\"\r\n [ngTemplateOutletContext]=\"{ $implicit: card }\"\r\n />\r\n <mt-entities-preview\r\n [entities]=\"card.entities\"\r\n attachmentShape=\"compact\"\r\n [stackOnNarrow]=\"false\"\r\n [clickableKeys]=\"clickableEntityKeys()\"\r\n [activeKeys]=\"activeKeysForCard(card)\"\r\n [clickableTooltip]=\"'components.table.filterByThis' | transloco\"\r\n [activeTooltip]=\"'components.table.clearFilter' | transloco\"\r\n (entityClick)=\"onEntityFilterClick(card, $event)\"\r\n />\r\n </ng-template>\r\n </mt-card>\r\n } @placeholder {\r\n <div class=\"min-h-[20rem]\">\r\n <p-skeleton height=\"20rem\" class=\"rounded-lg\" />\r\n </div>\r\n }\r\n }\r\n </div>\r\n}\r\n\r\n<!--\r\n Per-card 3-dots actions menu \u2014 same contract as the table view's popover\r\n row-actions. Rendered into each card's top-end corner; clicks stop at the\r\n wrapper so the card's own click (cardClick) doesn't fire.\r\n-->\r\n<ng-template #cardActionsMenu let-card>\r\n @if (rowActions().length > 0) {\r\n <div\r\n class=\"absolute end-2 top-2 z-10 flex items-center gap-1\"\r\n (click)=\"$event.stopPropagation()\"\r\n >\r\n @for (action of inlineActionsForCard(card); track $index) {\r\n <mt-button\r\n [icon]=\"action.icon\"\r\n [severity]=\"action.color\"\r\n [variant]=\"action.variant\"\r\n [size]=\"actionSize(action) || 'small'\"\r\n [tooltip]=\"action.tooltip\"\r\n [label]=\"action.label\"\r\n [loading]=\"resolveActionLoading(action, card)\"\r\n (onClick)=\"runCardAction($event, action, card)\"\r\n />\r\n }\r\n @if (showCardActionsMenu(card)) {\r\n <mt-button\r\n icon=\"general.dots-vertical\"\r\n severity=\"secondary\"\r\n variant=\"text\"\r\n [tooltip]=\"'components.table.actions' | transloco\"\r\n (onClick)=\"onCardActionsToggle($event, cardPopover, card)\"\r\n />\r\n <p-popover #cardPopover appendTo=\"body\">\r\n <div class=\"flex min-w-44 flex-col gap-1 p-1\">\r\n @if (isCardActionsLoading(card)) {\r\n <div class=\"flex flex-col gap-2 p-1\">\r\n <p-skeleton height=\"1.75rem\" />\r\n <p-skeleton height=\"1.75rem\" />\r\n <p-skeleton height=\"1.75rem\" />\r\n </div>\r\n } @else {\r\n @for (action of menuActionsForCard(card); track $index) {\r\n <button\r\n type=\"button\"\r\n class=\"flex cursor-pointer items-center gap-2 rounded px-2 py-2 text-start text-sm transition-colors\"\r\n [class]=\"\r\n action.color === 'danger'\r\n ? 'text-red-600 hover:bg-red-50 dark:hover:bg-red-950'\r\n : 'text-gray-700 hover:bg-gray-100 dark:text-gray-200 dark:hover:bg-surface-700'\r\n \"\r\n (click)=\"\r\n runCardAction($event, action, card); cardPopover.hide()\r\n \"\r\n >\r\n @if (action.icon) {\r\n <mt-icon [icon]=\"action.icon\" class=\"text-base\" />\r\n }\r\n <span>{{ action.label || action.tooltip }}</span>\r\n </button>\r\n }\r\n }\r\n </div>\r\n </p-popover>\r\n }\r\n </div>\r\n }\r\n</ng-template>\r\n" }]
|
|
1288
|
+
}], ctorParameters: () => [], propDecorators: { state: [{ type: i0.Input, args: [{ isSignal: true, alias: "state", required: true }] }], rowActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowActions", required: false }] }], rowActionsLoadingFn: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowActionsLoadingFn", required: false }] }], lazyLoad: [{ type: i0.Output, args: ["lazyLoad"] }], cardClick: [{ type: i0.Output, args: ["cardClick"] }], rowActionsRequested: [{ type: i0.Output, args: ["rowActionsRequested"] }] } });
|
|
1325
1289
|
|
|
1326
1290
|
function toDashboardExtraFilters(filters = []) {
|
|
1327
1291
|
const extraFilters = {};
|
|
@@ -1363,6 +1327,76 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
|
|
|
1363
1327
|
args: [{ selector: 'mt-client-list-informative-view', standalone: true, imports: [CommonModule, Card, DashboardViewer, SkeletonModule, TranslocoPipe], 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 {{\r\n \"components.clientComponents.list.noInformativeDashboard\"\r\n | transloco\r\n }}\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 [extraFilters]=\"dashboardExtraFilters()\"\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" }]
|
|
1364
1328
|
}], propDecorators: { state: [{ type: i0.Input, args: [{ isSignal: true, alias: "state", required: true }] }] } });
|
|
1365
1329
|
|
|
1330
|
+
function mergeRuntimeFilters(configured = [], runtime = {}, columns = []) {
|
|
1331
|
+
const filters = [...configured];
|
|
1332
|
+
for (const [rawKey, rawValue] of Object.entries(runtime)) {
|
|
1333
|
+
const key = rawKey === 'generalSearch' ? 'Name' : rawKey;
|
|
1334
|
+
const column = columns.find((item) => item.key === rawKey);
|
|
1335
|
+
const filterType = column
|
|
1336
|
+
? TableValueResolver.getColumnFilterType(column)
|
|
1337
|
+
: null;
|
|
1338
|
+
if (isRange(rawValue)) {
|
|
1339
|
+
appendRange(filters, key, rawValue, filterType === 'date');
|
|
1340
|
+
continue;
|
|
1341
|
+
}
|
|
1342
|
+
const scalar = toScalar(rawValue);
|
|
1343
|
+
const value = rawKey === 'generalSearch' && typeof scalar === 'string'
|
|
1344
|
+
? scalar.trim()
|
|
1345
|
+
: scalar;
|
|
1346
|
+
if (value === null)
|
|
1347
|
+
continue;
|
|
1348
|
+
filters.push({
|
|
1349
|
+
key,
|
|
1350
|
+
operator: rawKey === 'generalSearch' || filterType === 'text'
|
|
1351
|
+
? 'Contains'
|
|
1352
|
+
: 'Equals',
|
|
1353
|
+
value,
|
|
1354
|
+
});
|
|
1355
|
+
}
|
|
1356
|
+
return filters;
|
|
1357
|
+
}
|
|
1358
|
+
function appendRange(filters, key, range, isDate) {
|
|
1359
|
+
const from = toRangeValue(range.from, isDate);
|
|
1360
|
+
const to = toRangeValue(range.to, isDate);
|
|
1361
|
+
if (from !== null) {
|
|
1362
|
+
filters.push({ key, operator: 'GreaterThanOrEqual', value: from });
|
|
1363
|
+
}
|
|
1364
|
+
if (to !== null) {
|
|
1365
|
+
filters.push({ key, operator: 'LessThanOrEqual', value: to });
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
function isRange(value) {
|
|
1369
|
+
return (!!value && typeof value === 'object' && ('from' in value || 'to' in value));
|
|
1370
|
+
}
|
|
1371
|
+
function toRangeValue(value, isDate) {
|
|
1372
|
+
if (value === null || value === undefined || value === '')
|
|
1373
|
+
return null;
|
|
1374
|
+
if (!isDate)
|
|
1375
|
+
return value;
|
|
1376
|
+
const date = value instanceof Date ? value : new Date(String(value));
|
|
1377
|
+
if (Number.isNaN(date.getTime()))
|
|
1378
|
+
return null;
|
|
1379
|
+
const year = date.getFullYear();
|
|
1380
|
+
const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
1381
|
+
const day = String(date.getDate()).padStart(2, '0');
|
|
1382
|
+
return `${year}-${month}-${day}`;
|
|
1383
|
+
}
|
|
1384
|
+
function toScalar(value) {
|
|
1385
|
+
if (Array.isArray(value)) {
|
|
1386
|
+
return value.length === 1 ? toScalar(value[0]) : null;
|
|
1387
|
+
}
|
|
1388
|
+
if (value === null || value === undefined || value === '')
|
|
1389
|
+
return null;
|
|
1390
|
+
if (typeof value !== 'object')
|
|
1391
|
+
return value;
|
|
1392
|
+
const item = value;
|
|
1393
|
+
return toScalar(item['value'] ??
|
|
1394
|
+
item['id'] ??
|
|
1395
|
+
item['key'] ??
|
|
1396
|
+
item['userName'] ??
|
|
1397
|
+
item['displayName']);
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1366
1400
|
const DEFAULT_AREA_TYPE = 'table';
|
|
1367
1401
|
const DEFAULT_MODE = 'auto';
|
|
1368
1402
|
const DEFAULT_TYPE = 'form';
|
|
@@ -1393,6 +1427,7 @@ class ClientList {
|
|
|
1393
1427
|
subscriptions = new Map();
|
|
1394
1428
|
inFlightRequestSignatures = new Map();
|
|
1395
1429
|
fulfilledRequestSignatures = new Map();
|
|
1430
|
+
runtimeFilters = new Map();
|
|
1396
1431
|
constructor() {
|
|
1397
1432
|
effect(() => {
|
|
1398
1433
|
const configs = this.configurations();
|
|
@@ -1406,10 +1441,12 @@ class ClientList {
|
|
|
1406
1441
|
}
|
|
1407
1442
|
onLazyLoad(itemKey, event) {
|
|
1408
1443
|
const item = this.state.itemsByKey()[itemKey];
|
|
1409
|
-
if (!item ||
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1444
|
+
if (!item || item.type === 'informative' || !item.config.isPaginated) {
|
|
1445
|
+
return;
|
|
1446
|
+
}
|
|
1447
|
+
this.runtimeFilters.set(item.key, event.filters ?? {});
|
|
1448
|
+
if (item.areaType === 'cards') {
|
|
1449
|
+
this.loadCards(item.key, item.config);
|
|
1413
1450
|
return;
|
|
1414
1451
|
}
|
|
1415
1452
|
const take = this.resolvePositiveInteger(event.pageSize) ?? item.take;
|
|
@@ -1841,7 +1878,8 @@ class ClientList {
|
|
|
1841
1878
|
return;
|
|
1842
1879
|
}
|
|
1843
1880
|
const query = this.toTableQuery(config, skip, take);
|
|
1844
|
-
const
|
|
1881
|
+
const filters = this.resolveFetchFilters(key, config);
|
|
1882
|
+
const requestSignature = this.createTableRequestSignature(config, query, filters);
|
|
1845
1883
|
if (force) {
|
|
1846
1884
|
this.inFlightRequestSignatures.delete(key);
|
|
1847
1885
|
this.fulfilledRequestSignatures.delete(key);
|
|
@@ -1854,9 +1892,7 @@ class ClientList {
|
|
|
1854
1892
|
this.inFlightRequestSignatures.set(key, requestSignature);
|
|
1855
1893
|
this.state.setLoading(key, true);
|
|
1856
1894
|
this.state.setError(key, null);
|
|
1857
|
-
const sub = this.api
|
|
1858
|
-
.getRows(config.contextKey, query, config.filters)
|
|
1859
|
-
.subscribe({
|
|
1895
|
+
const sub = this.api.getRows(config.contextKey, query, filters).subscribe({
|
|
1860
1896
|
next: (response) => {
|
|
1861
1897
|
let hasLoadedData = false;
|
|
1862
1898
|
if (response.data) {
|
|
@@ -1895,7 +1931,8 @@ class ClientList {
|
|
|
1895
1931
|
this.state.setError(key, this.getMissingIdentifiersMessage(config));
|
|
1896
1932
|
return;
|
|
1897
1933
|
}
|
|
1898
|
-
const
|
|
1934
|
+
const filters = this.resolveFetchFilters(key, config);
|
|
1935
|
+
const requestSignature = this.createCardsRequestSignature(config, filters);
|
|
1899
1936
|
if (force) {
|
|
1900
1937
|
this.inFlightRequestSignatures.delete(key);
|
|
1901
1938
|
this.fulfilledRequestSignatures.delete(key);
|
|
@@ -1908,9 +1945,7 @@ class ClientList {
|
|
|
1908
1945
|
this.inFlightRequestSignatures.set(key, requestSignature);
|
|
1909
1946
|
this.state.setLoading(key, true);
|
|
1910
1947
|
this.state.setError(key, null);
|
|
1911
|
-
const sub = this.api
|
|
1912
|
-
.getCards(config.contextKey, config.filters)
|
|
1913
|
-
.subscribe({
|
|
1948
|
+
const sub = this.api.getCards(config.contextKey, filters).subscribe({
|
|
1914
1949
|
next: (response) => {
|
|
1915
1950
|
let hasLoadedData = false;
|
|
1916
1951
|
if (response.data) {
|
|
@@ -2127,6 +2162,11 @@ class ClientList {
|
|
|
2127
2162
|
this.fulfilledRequestSignatures.delete(key);
|
|
2128
2163
|
}
|
|
2129
2164
|
}
|
|
2165
|
+
for (const key of this.runtimeFilters.keys()) {
|
|
2166
|
+
if (!activeKeys.has(key)) {
|
|
2167
|
+
this.runtimeFilters.delete(key);
|
|
2168
|
+
}
|
|
2169
|
+
}
|
|
2130
2170
|
for (const key of this.rowActionsLoadingFnCache.keys()) {
|
|
2131
2171
|
if (!activeKeys.has(key)) {
|
|
2132
2172
|
this.rowActionsLoadingFnCache.delete(key);
|
|
@@ -2164,24 +2204,24 @@ class ClientList {
|
|
|
2164
2204
|
take: queryTake,
|
|
2165
2205
|
};
|
|
2166
2206
|
}
|
|
2167
|
-
createTableRequestSignature(config, query) {
|
|
2207
|
+
createTableRequestSignature(config, query, filters) {
|
|
2168
2208
|
return [
|
|
2169
2209
|
config.type,
|
|
2170
2210
|
config.areaType,
|
|
2171
2211
|
config.contextKey ?? 'x',
|
|
2172
|
-
this.serializeFilters(
|
|
2212
|
+
this.serializeFilters(filters),
|
|
2173
2213
|
query.mode,
|
|
2174
2214
|
query.skip,
|
|
2175
2215
|
query.take,
|
|
2176
2216
|
...(query.columnKeys ?? []),
|
|
2177
2217
|
].join('|');
|
|
2178
2218
|
}
|
|
2179
|
-
createCardsRequestSignature(config) {
|
|
2219
|
+
createCardsRequestSignature(config, filters) {
|
|
2180
2220
|
return [
|
|
2181
2221
|
config.type,
|
|
2182
2222
|
config.areaType,
|
|
2183
2223
|
config.contextKey ?? 'x',
|
|
2184
|
-
this.serializeFilters(
|
|
2224
|
+
this.serializeFilters(filters),
|
|
2185
2225
|
'Card',
|
|
2186
2226
|
].join('|');
|
|
2187
2227
|
}
|
|
@@ -2222,6 +2262,9 @@ class ClientList {
|
|
|
2222
2262
|
serializeFilters(filters) {
|
|
2223
2263
|
return JSON.stringify(filters ?? []);
|
|
2224
2264
|
}
|
|
2265
|
+
resolveFetchFilters(key, config) {
|
|
2266
|
+
return mergeRuntimeFilters(config.filters, this.runtimeFilters.get(key), this.state.itemsByKey()[key]?.columns);
|
|
2267
|
+
}
|
|
2225
2268
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: ClientList, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2226
2269
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", 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: [
|
|
2227
2270
|
ClientListStateService,
|