@masterteam/delegations 0.0.20 → 0.0.22
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.
|
@@ -19,6 +19,7 @@ import { ToastService } from '@masterteam/components/toast';
|
|
|
19
19
|
import { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop';
|
|
20
20
|
import { Page } from '@masterteam/components/page';
|
|
21
21
|
import { Breadcrumb } from '@masterteam/components/breadcrumb';
|
|
22
|
+
import { Card } from '@masterteam/components/card';
|
|
22
23
|
import { Table } from '@masterteam/components/table';
|
|
23
24
|
import * as i1$1 from '@angular/forms';
|
|
24
25
|
import { FormControl, ReactiveFormsModule } from '@angular/forms';
|
|
@@ -384,6 +385,9 @@ function humanizeDelegationKey(value) {
|
|
|
384
385
|
}
|
|
385
386
|
return normalized.replace(/\b\w/g, (char) => char.toUpperCase());
|
|
386
387
|
}
|
|
388
|
+
function formatDelegationDisplayValue(value, preferredLanguage = 'en') {
|
|
389
|
+
return resolveDisplayValue(value, preferredLanguage) ?? '';
|
|
390
|
+
}
|
|
387
391
|
function formatDelegationTargetLabel(target, preferredLanguage = 'en') {
|
|
388
392
|
return resolveDisplayOrHumanizedKey(target, ['displayName', 'displayLabel', 'label'], target.targetKey, preferredLanguage);
|
|
389
393
|
}
|
|
@@ -397,7 +401,7 @@ function formatDelegationActionLabel(action, preferredLanguage = 'en') {
|
|
|
397
401
|
], action.operationKey, preferredLanguage);
|
|
398
402
|
}
|
|
399
403
|
function formatDelegationScopeSummary(value, preferredLanguage = 'en') {
|
|
400
|
-
return
|
|
404
|
+
return formatDelegationDisplayValue(value, preferredLanguage);
|
|
401
405
|
}
|
|
402
406
|
|
|
403
407
|
/**
|
|
@@ -457,6 +461,7 @@ class TopbarDelegationMenu {
|
|
|
457
461
|
/** Path to the management page, e.g. `/control-panel/delegations` or `/delegations`. */
|
|
458
462
|
managePath = input('/delegations', ...(ngDevMode ? [{ debugName: "managePath" }] : /* istanbul ignore next */ []));
|
|
459
463
|
compact = input(false, ...(ngDevMode ? [{ debugName: "compact" }] : /* istanbul ignore next */ []));
|
|
464
|
+
showManageLink = input(true, ...(ngDevMode ? [{ debugName: "showManageLink" }] : /* istanbul ignore next */ []));
|
|
460
465
|
facade = inject(DelegationSessionFacade);
|
|
461
466
|
modal = inject(ModalService);
|
|
462
467
|
transloco = inject(TranslocoService);
|
|
@@ -504,7 +509,7 @@ class TopbarDelegationMenu {
|
|
|
504
509
|
});
|
|
505
510
|
}
|
|
506
511
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: TopbarDelegationMenu, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
507
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: TopbarDelegationMenu, isStandalone: true, selector: "mt-topbar-delegation-menu", inputs: { managePath: { classPropertyName: "managePath", publicName: "managePath", isSignal: true, isRequired: false, transformFunction: null }, compact: { classPropertyName: "compact", publicName: "compact", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "popover", first: true, predicate: ["popover"], descendants: true, isSignal: true }], ngImport: i0, template: "<ng-container *transloco=\"let t\">\n @if (mode() !== \"hidden\") {\n <div class=\"mt-delegation-trigger flex items-center gap-2\">\n <button\n type=\"button\"\n class=\"mt-delegation-trigger__button flex min-w-0 max-w-full items-center gap-2 rounded-full px-2 py-1 hover:bg-surface-100 cursor-pointer\"\n [attr.aria-label]=\"t('delegations.title')\"\n [attr.title]=\"\n mode() === 'active'\n ? t('delegations.session.banner', {\n delegatorName: onBehalfOf()?.displayName,\n })\n : null\n \"\n (click)=\"togglePopover($event)\"\n >\n @if (mode() === \"active\") {\n <mt-icon\n icon=\"user.users-check\"\n styleClass=\"text-lg text-primary\"\n ></mt-icon>\n @if (!compact()) {\n <span class=\"mt-delegation-trigger__label text-sm font-medium\">\n {{\n t(\"delegations.session.banner\", {\n delegatorName: onBehalfOf()?.displayName,\n })\n }}\n </span>\n }\n <mt-delegation-status-chip\n status=\"Active\"\n ></mt-delegation-status-chip>\n } @else {\n <mt-icon\n icon=\"user.users-plus\"\n styleClass=\"text-lg text-primary\"\n ></mt-icon>\n @if (!compact()) {\n <span class=\"mt-delegation-trigger__label text-sm font-medium\">\n {{ t(\"delegations.session.availableTitle\") }}\n </span>\n }\n }\n </button>\n </div>\n\n <p-popover #popover [pt]=\"{ content: { class: 'p-0!' } }\">\n <div class=\"flex flex-col min-w-80 max-w-96 p-0\">\n @if (mode() === \"active\" && active(); as session) {\n <!-- STATE C \u2014 active session -->\n <div\n class=\"flex items-start gap-3 border-b border-gray-200 px-4 py-4\"\n >\n <mt-avatar\n icon=\"user.user-01\"\n styleClass=\"w-11! h-11! text-xl! text-gray-600! bg-surface-300!\"\n ></mt-avatar>\n <div class=\"flex flex-col min-w-0 flex-1\">\n <div class=\"text-xs text-gray-500\">\n {{ t(\"delegations.session.actingOnBehalfOf\") }}\n </div>\n <div class=\"text-sm font-medium text-gray-900 truncate\">\n {{ onBehalfOf()?.displayName }}\n </div>\n <div class=\"text-xs text-gray-500 mt-1\">\n {{\n t(\"delegations.session.executedBy\", {\n actualUserName: executedBy()?.displayName,\n })\n }}\n </div>\n @if (session.delegation.scopeSummary) {\n @let summary =\n formatScopeSummary(session.delegation.scopeSummary);\n <div\n class=\"text-xs text-gray-500 mt-1 truncate\"\n [title]=\"summary\"\n >\n {{ t(\"delegations.column.scopeSummary\") }}:\n {{ summary }}\n </div>\n }\n </div>\n </div>\n\n <div class=\"flex flex-col gap-1 px-2 py-2\">\n <mt-button\n variant=\"text\"\n icon=\"arrow.arrow-left\"\n [label]=\"t('delegations.action.endSession')\"\n styleClass=\"w-full justify-start\"\n (click)=\"endSession($event)\"\n ></mt-button>\n </div>\n\n @if (candidates().length > 1) {\n <div class=\"border-t border-gray-200 pt-2 px-2 pb-2\">\n <div class=\"text-xs text-gray-500 px-2 pb-1\">\n {{ t(\"delegations.session.switchToAnother\") }}\n </div>\n @for (cand of candidates(); track cand.delegationId) {\n @if (cand.delegationId !== session.delegation.delegationId) {\n <button\n type=\"button\"\n class=\"flex items-center justify-between gap-2 w-full px-2 py-2 rounded-md hover:bg-surface-100 cursor-pointer\"\n (click)=\"switchTo(cand, $event)\"\n >\n <div class=\"flex items-center gap-2 min-w-0\">\n <mt-avatar\n icon=\"user.user-01\"\n styleClass=\"w-7! h-7! text-sm! bg-surface-200!\"\n ></mt-avatar>\n <span class=\"text-sm text-gray-900 truncate\">\n {{ cand.delegator.displayName }}\n </span>\n </div>\n <mt-icon\n icon=\"arrow.switch-horizontal-01\"\n styleClass=\"text-base text-gray-400\"\n ></mt-icon>\n </button>\n }\n }\n </div>\n }\n } @else if (mode() === \"candidates\") {\n <!-- STATE B \u2014 candidates available -->\n <div class=\"border-b border-gray-200 px-4 py-3\">\n <div class=\"text-sm font-medium text-gray-900\">\n {{ t(\"delegations.session.youCanActAs\") }}\n </div>\n <div class=\"text-xs text-gray-500 mt-1\">\n {{ t(\"delegations.session.chooseDelegatorHint\") }}\n </div>\n </div>\n <div class=\"flex flex-col gap-1 px-2 py-2 max-h-72 overflow-y-auto\">\n @for (cand of candidates(); track cand.delegationId) {\n <button\n type=\"button\"\n class=\"flex items-center justify-between gap-2 w-full px-2 py-2 rounded-md hover:bg-surface-100 cursor-pointer text-start\"\n (click)=\"start(cand, $event)\"\n >\n <div class=\"flex items-center gap-2 min-w-0 flex-1\">\n <mt-avatar\n icon=\"user.user-01\"\n styleClass=\"w-9! h-9! text-base! bg-surface-200!\"\n ></mt-avatar>\n <div class=\"flex flex-col min-w-0\">\n <span class=\"text-sm font-medium text-gray-900 truncate\">\n {{ cand.delegator.displayName }}\n </span>\n @if (cand.scopeSummary) {\n @let summary = formatScopeSummary(cand.scopeSummary);\n <span\n class=\"text-xs text-gray-500 truncate\"\n [title]=\"summary\"\n >\n {{ summary }}\n </span>\n }\n </div>\n </div>\n <mt-icon\n icon=\"arrow.arrow-right\"\n styleClass=\"text-base text-gray-400\"\n ></mt-icon>\n </button>\n }\n </div>\n }\n\n <!-- Footer -->\n <div class=\"border-t border-gray-200 px-2 py-2\">\n <a\n [routerLink]=\"managePath()\"\n (click)=\"popover().hide()\"\n class=\"flex items-center gap-2 px-3 py-2 rounded-md text-gray-700 hover:bg-surface-100 text-sm cursor-pointer\"\n >\n <mt-icon\n icon=\"general.settings-02\"\n styleClass=\"text-base\"\n ></mt-icon>\n <span>{{ t(\"delegations.session.manage\") }}</span>\n </a>\n </div>\n </div>\n </p-popover>\n }\n</ng-container>\n", styles: [":host{display:inline-flex;align-items:center;min-width:0;max-width:100%}.mt-delegation-trigger{min-width:0;max-width:100%}.mt-delegation-trigger__button{transition:background-color .2s ease-out;min-width:0;max-width:100%}.mt-delegation-trigger__label{max-width:min(16rem,28vw);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}@media(max-width:1200px){.mt-delegation-trigger__label{max-width:10rem}}@media(max-width:1024px){.mt-delegation-trigger__button{gap:.375rem;padding-inline:.375rem}.mt-delegation-trigger__label{display:none}}:host-context(.dark) .mt-delegation-trigger__button:hover{background-color:#94a3b826}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: Avatar, selector: "mt-avatar", inputs: ["label", "icon", "image", "styleClass", "size", "shape", "badge", "badgeSize", "badgeSeverity"], outputs: ["onImageError"] }, { 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: Popover, selector: "p-popover", inputs: ["ariaLabel", "ariaLabelledBy", "dismissable", "style", "styleClass", "appendTo", "autoZIndex", "ariaCloseLabel", "baseZIndex", "focusOnShow", "showTransitionOptions", "hideTransitionOptions", "motionOptions"], outputs: ["onShow", "onHide"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "component", type: DelegationStatusChip, selector: "mt-delegation-status-chip", inputs: ["status"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
512
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: TopbarDelegationMenu, isStandalone: true, selector: "mt-topbar-delegation-menu", inputs: { managePath: { classPropertyName: "managePath", publicName: "managePath", isSignal: true, isRequired: false, transformFunction: null }, compact: { classPropertyName: "compact", publicName: "compact", isSignal: true, isRequired: false, transformFunction: null }, showManageLink: { classPropertyName: "showManageLink", publicName: "showManageLink", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "popover", first: true, predicate: ["popover"], descendants: true, isSignal: true }], ngImport: i0, template: "<ng-container *transloco=\"let t\">\n @if (mode() !== \"hidden\") {\n <div class=\"mt-delegation-trigger flex items-center gap-2\">\n <button\n type=\"button\"\n class=\"mt-delegation-trigger__button flex min-w-0 max-w-full items-center gap-2 rounded-full px-2 py-1 hover:bg-surface-100 cursor-pointer\"\n [attr.aria-label]=\"t('delegations.title')\"\n [attr.title]=\"\n mode() === 'active'\n ? t('delegations.session.banner', {\n delegatorName: onBehalfOf()?.displayName,\n })\n : null\n \"\n (click)=\"togglePopover($event)\"\n >\n @if (mode() === \"active\") {\n <mt-icon\n icon=\"user.users-check\"\n styleClass=\"text-lg text-primary\"\n ></mt-icon>\n @if (!compact()) {\n <span class=\"mt-delegation-trigger__label text-sm font-medium\">\n {{\n t(\"delegations.session.banner\", {\n delegatorName: onBehalfOf()?.displayName,\n })\n }}\n </span>\n }\n <mt-delegation-status-chip\n status=\"Active\"\n ></mt-delegation-status-chip>\n } @else {\n <mt-icon\n icon=\"user.users-plus\"\n styleClass=\"text-lg text-primary\"\n ></mt-icon>\n @if (!compact()) {\n <span class=\"mt-delegation-trigger__label text-sm font-medium\">\n {{ t(\"delegations.session.availableTitle\") }}\n </span>\n }\n }\n </button>\n </div>\n\n <p-popover #popover [pt]=\"{ content: { class: 'p-0!' } }\">\n <div class=\"flex flex-col min-w-80 max-w-96 p-0\">\n @if (mode() === \"active\" && active(); as session) {\n <!-- STATE C \u2014 active session -->\n <div\n class=\"flex items-start gap-3 border-b border-gray-200 px-4 py-4\"\n >\n <mt-avatar\n icon=\"user.user-01\"\n styleClass=\"w-11! h-11! text-xl! text-gray-600! bg-surface-300!\"\n ></mt-avatar>\n <div class=\"flex flex-col min-w-0 flex-1\">\n <div class=\"text-xs text-gray-500\">\n {{ t(\"delegations.session.actingOnBehalfOf\") }}\n </div>\n <div class=\"text-sm font-medium text-gray-900 truncate\">\n {{ onBehalfOf()?.displayName }}\n </div>\n <div class=\"text-xs text-gray-500 mt-1\">\n {{\n t(\"delegations.session.executedBy\", {\n actualUserName: executedBy()?.displayName,\n })\n }}\n </div>\n @if (session.delegation.scopeSummary) {\n @let summary =\n formatScopeSummary(session.delegation.scopeSummary);\n <div\n class=\"text-xs text-gray-500 mt-1 truncate\"\n [title]=\"summary\"\n >\n {{ t(\"delegations.column.scopeSummary\") }}:\n {{ summary }}\n </div>\n }\n </div>\n </div>\n\n <div class=\"flex flex-col gap-1 px-2 py-2\">\n <mt-button\n variant=\"text\"\n icon=\"arrow.arrow-left\"\n [label]=\"t('delegations.action.endSession')\"\n styleClass=\"w-full justify-start\"\n (click)=\"endSession($event)\"\n ></mt-button>\n </div>\n\n @if (candidates().length > 1) {\n <div class=\"border-t border-gray-200 pt-2 px-2 pb-2\">\n <div class=\"text-xs text-gray-500 px-2 pb-1\">\n {{ t(\"delegations.session.switchToAnother\") }}\n </div>\n @for (cand of candidates(); track cand.delegationId) {\n @if (cand.delegationId !== session.delegation.delegationId) {\n <button\n type=\"button\"\n class=\"flex items-center justify-between gap-2 w-full px-2 py-2 rounded-md hover:bg-surface-100 cursor-pointer\"\n (click)=\"switchTo(cand, $event)\"\n >\n <div class=\"flex items-center gap-2 min-w-0\">\n <mt-avatar\n icon=\"user.user-01\"\n styleClass=\"w-7! h-7! text-sm! bg-surface-200!\"\n ></mt-avatar>\n <span class=\"text-sm text-gray-900 truncate\">\n {{ cand.delegator.displayName }}\n </span>\n </div>\n <mt-icon\n icon=\"arrow.switch-horizontal-01\"\n styleClass=\"text-base text-gray-400\"\n ></mt-icon>\n </button>\n }\n }\n </div>\n }\n } @else if (mode() === \"candidates\") {\n <!-- STATE B \u2014 candidates available -->\n <div class=\"border-b border-gray-200 px-4 py-3\">\n <div class=\"text-sm font-medium text-gray-900\">\n {{ t(\"delegations.session.youCanActAs\") }}\n </div>\n <div class=\"text-xs text-gray-500 mt-1\">\n {{ t(\"delegations.session.chooseDelegatorHint\") }}\n </div>\n </div>\n <div class=\"flex flex-col gap-1 px-2 py-2 max-h-72 overflow-y-auto\">\n @for (cand of candidates(); track cand.delegationId) {\n <button\n type=\"button\"\n class=\"flex items-center justify-between gap-2 w-full px-2 py-2 rounded-md hover:bg-surface-100 cursor-pointer text-start\"\n (click)=\"start(cand, $event)\"\n >\n <div class=\"flex items-center gap-2 min-w-0 flex-1\">\n <mt-avatar\n icon=\"user.user-01\"\n styleClass=\"w-9! h-9! text-base! bg-surface-200!\"\n ></mt-avatar>\n <div class=\"flex flex-col min-w-0\">\n <span class=\"text-sm font-medium text-gray-900 truncate\">\n {{ cand.delegator.displayName }}\n </span>\n @if (cand.scopeSummary) {\n @let summary = formatScopeSummary(cand.scopeSummary);\n <span\n class=\"text-xs text-gray-500 truncate\"\n [title]=\"summary\"\n >\n {{ summary }}\n </span>\n }\n </div>\n </div>\n <mt-icon\n icon=\"arrow.arrow-right\"\n styleClass=\"text-base text-gray-400\"\n ></mt-icon>\n </button>\n }\n </div>\n }\n\n <!-- Footer -->\n @if (showManageLink()) {\n <div class=\"border-t border-gray-200 px-2 py-2\">\n <a\n [routerLink]=\"managePath()\"\n (click)=\"popover().hide()\"\n class=\"flex items-center gap-2 px-3 py-2 rounded-md text-gray-700 hover:bg-surface-100 text-sm cursor-pointer\"\n >\n <mt-icon\n icon=\"general.settings-02\"\n styleClass=\"text-base\"\n ></mt-icon>\n <span>{{ t(\"delegations.session.manage\") }}</span>\n </a>\n </div>\n }\n </div>\n </p-popover>\n }\n</ng-container>\n", styles: [":host{display:inline-flex;align-items:center;min-width:0;max-width:100%}.mt-delegation-trigger{min-width:0;max-width:100%}.mt-delegation-trigger__button{transition:background-color .2s ease-out;min-width:0;max-width:100%}.mt-delegation-trigger__label{max-width:min(16rem,28vw);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}@media(max-width:1200px){.mt-delegation-trigger__label{max-width:10rem}}@media(max-width:1024px){.mt-delegation-trigger__button{gap:.375rem;padding-inline:.375rem}.mt-delegation-trigger__label{display:none}}:host-context(.dark) .mt-delegation-trigger__button:hover{background-color:#94a3b826}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: Avatar, selector: "mt-avatar", inputs: ["label", "icon", "image", "styleClass", "size", "shape", "badge", "badgeSize", "badgeSeverity"], outputs: ["onImageError"] }, { 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: Popover, selector: "p-popover", inputs: ["ariaLabel", "ariaLabelledBy", "dismissable", "style", "styleClass", "appendTo", "autoZIndex", "ariaCloseLabel", "baseZIndex", "focusOnShow", "showTransitionOptions", "hideTransitionOptions", "motionOptions"], outputs: ["onShow", "onHide"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "component", type: DelegationStatusChip, selector: "mt-delegation-status-chip", inputs: ["status"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
508
513
|
}
|
|
509
514
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: TopbarDelegationMenu, decorators: [{
|
|
510
515
|
type: Component,
|
|
@@ -517,8 +522,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
|
|
|
517
522
|
RouterLink,
|
|
518
523
|
TranslocoDirective,
|
|
519
524
|
DelegationStatusChip,
|
|
520
|
-
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-container *transloco=\"let t\">\n @if (mode() !== \"hidden\") {\n <div class=\"mt-delegation-trigger flex items-center gap-2\">\n <button\n type=\"button\"\n class=\"mt-delegation-trigger__button flex min-w-0 max-w-full items-center gap-2 rounded-full px-2 py-1 hover:bg-surface-100 cursor-pointer\"\n [attr.aria-label]=\"t('delegations.title')\"\n [attr.title]=\"\n mode() === 'active'\n ? t('delegations.session.banner', {\n delegatorName: onBehalfOf()?.displayName,\n })\n : null\n \"\n (click)=\"togglePopover($event)\"\n >\n @if (mode() === \"active\") {\n <mt-icon\n icon=\"user.users-check\"\n styleClass=\"text-lg text-primary\"\n ></mt-icon>\n @if (!compact()) {\n <span class=\"mt-delegation-trigger__label text-sm font-medium\">\n {{\n t(\"delegations.session.banner\", {\n delegatorName: onBehalfOf()?.displayName,\n })\n }}\n </span>\n }\n <mt-delegation-status-chip\n status=\"Active\"\n ></mt-delegation-status-chip>\n } @else {\n <mt-icon\n icon=\"user.users-plus\"\n styleClass=\"text-lg text-primary\"\n ></mt-icon>\n @if (!compact()) {\n <span class=\"mt-delegation-trigger__label text-sm font-medium\">\n {{ t(\"delegations.session.availableTitle\") }}\n </span>\n }\n }\n </button>\n </div>\n\n <p-popover #popover [pt]=\"{ content: { class: 'p-0!' } }\">\n <div class=\"flex flex-col min-w-80 max-w-96 p-0\">\n @if (mode() === \"active\" && active(); as session) {\n <!-- STATE C \u2014 active session -->\n <div\n class=\"flex items-start gap-3 border-b border-gray-200 px-4 py-4\"\n >\n <mt-avatar\n icon=\"user.user-01\"\n styleClass=\"w-11! h-11! text-xl! text-gray-600! bg-surface-300!\"\n ></mt-avatar>\n <div class=\"flex flex-col min-w-0 flex-1\">\n <div class=\"text-xs text-gray-500\">\n {{ t(\"delegations.session.actingOnBehalfOf\") }}\n </div>\n <div class=\"text-sm font-medium text-gray-900 truncate\">\n {{ onBehalfOf()?.displayName }}\n </div>\n <div class=\"text-xs text-gray-500 mt-1\">\n {{\n t(\"delegations.session.executedBy\", {\n actualUserName: executedBy()?.displayName,\n })\n }}\n </div>\n @if (session.delegation.scopeSummary) {\n @let summary =\n formatScopeSummary(session.delegation.scopeSummary);\n <div\n class=\"text-xs text-gray-500 mt-1 truncate\"\n [title]=\"summary\"\n >\n {{ t(\"delegations.column.scopeSummary\") }}:\n {{ summary }}\n </div>\n }\n </div>\n </div>\n\n <div class=\"flex flex-col gap-1 px-2 py-2\">\n <mt-button\n variant=\"text\"\n icon=\"arrow.arrow-left\"\n [label]=\"t('delegations.action.endSession')\"\n styleClass=\"w-full justify-start\"\n (click)=\"endSession($event)\"\n ></mt-button>\n </div>\n\n @if (candidates().length > 1) {\n <div class=\"border-t border-gray-200 pt-2 px-2 pb-2\">\n <div class=\"text-xs text-gray-500 px-2 pb-1\">\n {{ t(\"delegations.session.switchToAnother\") }}\n </div>\n @for (cand of candidates(); track cand.delegationId) {\n @if (cand.delegationId !== session.delegation.delegationId) {\n <button\n type=\"button\"\n class=\"flex items-center justify-between gap-2 w-full px-2 py-2 rounded-md hover:bg-surface-100 cursor-pointer\"\n (click)=\"switchTo(cand, $event)\"\n >\n <div class=\"flex items-center gap-2 min-w-0\">\n <mt-avatar\n icon=\"user.user-01\"\n styleClass=\"w-7! h-7! text-sm! bg-surface-200!\"\n ></mt-avatar>\n <span class=\"text-sm text-gray-900 truncate\">\n {{ cand.delegator.displayName }}\n </span>\n </div>\n <mt-icon\n icon=\"arrow.switch-horizontal-01\"\n styleClass=\"text-base text-gray-400\"\n ></mt-icon>\n </button>\n }\n }\n </div>\n }\n } @else if (mode() === \"candidates\") {\n <!-- STATE B \u2014 candidates available -->\n <div class=\"border-b border-gray-200 px-4 py-3\">\n <div class=\"text-sm font-medium text-gray-900\">\n {{ t(\"delegations.session.youCanActAs\") }}\n </div>\n <div class=\"text-xs text-gray-500 mt-1\">\n {{ t(\"delegations.session.chooseDelegatorHint\") }}\n </div>\n </div>\n <div class=\"flex flex-col gap-1 px-2 py-2 max-h-72 overflow-y-auto\">\n @for (cand of candidates(); track cand.delegationId) {\n <button\n type=\"button\"\n class=\"flex items-center justify-between gap-2 w-full px-2 py-2 rounded-md hover:bg-surface-100 cursor-pointer text-start\"\n (click)=\"start(cand, $event)\"\n >\n <div class=\"flex items-center gap-2 min-w-0 flex-1\">\n <mt-avatar\n icon=\"user.user-01\"\n styleClass=\"w-9! h-9! text-base! bg-surface-200!\"\n ></mt-avatar>\n <div class=\"flex flex-col min-w-0\">\n <span class=\"text-sm font-medium text-gray-900 truncate\">\n {{ cand.delegator.displayName }}\n </span>\n @if (cand.scopeSummary) {\n @let summary = formatScopeSummary(cand.scopeSummary);\n <span\n class=\"text-xs text-gray-500 truncate\"\n [title]=\"summary\"\n >\n {{ summary }}\n </span>\n }\n </div>\n </div>\n <mt-icon\n icon=\"arrow.arrow-right\"\n styleClass=\"text-base text-gray-400\"\n ></mt-icon>\n </button>\n }\n </div>\n }\n\n <!-- Footer -->\n <div class=\"border-t border-gray-200 px-2 py-2\">\n
|
|
521
|
-
}], propDecorators: { managePath: [{ type: i0.Input, args: [{ isSignal: true, alias: "managePath", required: false }] }], compact: [{ type: i0.Input, args: [{ isSignal: true, alias: "compact", required: false }] }], popover: [{ type: i0.ViewChild, args: ['popover', { isSignal: true }] }] } });
|
|
525
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-container *transloco=\"let t\">\n @if (mode() !== \"hidden\") {\n <div class=\"mt-delegation-trigger flex items-center gap-2\">\n <button\n type=\"button\"\n class=\"mt-delegation-trigger__button flex min-w-0 max-w-full items-center gap-2 rounded-full px-2 py-1 hover:bg-surface-100 cursor-pointer\"\n [attr.aria-label]=\"t('delegations.title')\"\n [attr.title]=\"\n mode() === 'active'\n ? t('delegations.session.banner', {\n delegatorName: onBehalfOf()?.displayName,\n })\n : null\n \"\n (click)=\"togglePopover($event)\"\n >\n @if (mode() === \"active\") {\n <mt-icon\n icon=\"user.users-check\"\n styleClass=\"text-lg text-primary\"\n ></mt-icon>\n @if (!compact()) {\n <span class=\"mt-delegation-trigger__label text-sm font-medium\">\n {{\n t(\"delegations.session.banner\", {\n delegatorName: onBehalfOf()?.displayName,\n })\n }}\n </span>\n }\n <mt-delegation-status-chip\n status=\"Active\"\n ></mt-delegation-status-chip>\n } @else {\n <mt-icon\n icon=\"user.users-plus\"\n styleClass=\"text-lg text-primary\"\n ></mt-icon>\n @if (!compact()) {\n <span class=\"mt-delegation-trigger__label text-sm font-medium\">\n {{ t(\"delegations.session.availableTitle\") }}\n </span>\n }\n }\n </button>\n </div>\n\n <p-popover #popover [pt]=\"{ content: { class: 'p-0!' } }\">\n <div class=\"flex flex-col min-w-80 max-w-96 p-0\">\n @if (mode() === \"active\" && active(); as session) {\n <!-- STATE C \u2014 active session -->\n <div\n class=\"flex items-start gap-3 border-b border-gray-200 px-4 py-4\"\n >\n <mt-avatar\n icon=\"user.user-01\"\n styleClass=\"w-11! h-11! text-xl! text-gray-600! bg-surface-300!\"\n ></mt-avatar>\n <div class=\"flex flex-col min-w-0 flex-1\">\n <div class=\"text-xs text-gray-500\">\n {{ t(\"delegations.session.actingOnBehalfOf\") }}\n </div>\n <div class=\"text-sm font-medium text-gray-900 truncate\">\n {{ onBehalfOf()?.displayName }}\n </div>\n <div class=\"text-xs text-gray-500 mt-1\">\n {{\n t(\"delegations.session.executedBy\", {\n actualUserName: executedBy()?.displayName,\n })\n }}\n </div>\n @if (session.delegation.scopeSummary) {\n @let summary =\n formatScopeSummary(session.delegation.scopeSummary);\n <div\n class=\"text-xs text-gray-500 mt-1 truncate\"\n [title]=\"summary\"\n >\n {{ t(\"delegations.column.scopeSummary\") }}:\n {{ summary }}\n </div>\n }\n </div>\n </div>\n\n <div class=\"flex flex-col gap-1 px-2 py-2\">\n <mt-button\n variant=\"text\"\n icon=\"arrow.arrow-left\"\n [label]=\"t('delegations.action.endSession')\"\n styleClass=\"w-full justify-start\"\n (click)=\"endSession($event)\"\n ></mt-button>\n </div>\n\n @if (candidates().length > 1) {\n <div class=\"border-t border-gray-200 pt-2 px-2 pb-2\">\n <div class=\"text-xs text-gray-500 px-2 pb-1\">\n {{ t(\"delegations.session.switchToAnother\") }}\n </div>\n @for (cand of candidates(); track cand.delegationId) {\n @if (cand.delegationId !== session.delegation.delegationId) {\n <button\n type=\"button\"\n class=\"flex items-center justify-between gap-2 w-full px-2 py-2 rounded-md hover:bg-surface-100 cursor-pointer\"\n (click)=\"switchTo(cand, $event)\"\n >\n <div class=\"flex items-center gap-2 min-w-0\">\n <mt-avatar\n icon=\"user.user-01\"\n styleClass=\"w-7! h-7! text-sm! bg-surface-200!\"\n ></mt-avatar>\n <span class=\"text-sm text-gray-900 truncate\">\n {{ cand.delegator.displayName }}\n </span>\n </div>\n <mt-icon\n icon=\"arrow.switch-horizontal-01\"\n styleClass=\"text-base text-gray-400\"\n ></mt-icon>\n </button>\n }\n }\n </div>\n }\n } @else if (mode() === \"candidates\") {\n <!-- STATE B \u2014 candidates available -->\n <div class=\"border-b border-gray-200 px-4 py-3\">\n <div class=\"text-sm font-medium text-gray-900\">\n {{ t(\"delegations.session.youCanActAs\") }}\n </div>\n <div class=\"text-xs text-gray-500 mt-1\">\n {{ t(\"delegations.session.chooseDelegatorHint\") }}\n </div>\n </div>\n <div class=\"flex flex-col gap-1 px-2 py-2 max-h-72 overflow-y-auto\">\n @for (cand of candidates(); track cand.delegationId) {\n <button\n type=\"button\"\n class=\"flex items-center justify-between gap-2 w-full px-2 py-2 rounded-md hover:bg-surface-100 cursor-pointer text-start\"\n (click)=\"start(cand, $event)\"\n >\n <div class=\"flex items-center gap-2 min-w-0 flex-1\">\n <mt-avatar\n icon=\"user.user-01\"\n styleClass=\"w-9! h-9! text-base! bg-surface-200!\"\n ></mt-avatar>\n <div class=\"flex flex-col min-w-0\">\n <span class=\"text-sm font-medium text-gray-900 truncate\">\n {{ cand.delegator.displayName }}\n </span>\n @if (cand.scopeSummary) {\n @let summary = formatScopeSummary(cand.scopeSummary);\n <span\n class=\"text-xs text-gray-500 truncate\"\n [title]=\"summary\"\n >\n {{ summary }}\n </span>\n }\n </div>\n </div>\n <mt-icon\n icon=\"arrow.arrow-right\"\n styleClass=\"text-base text-gray-400\"\n ></mt-icon>\n </button>\n }\n </div>\n }\n\n <!-- Footer -->\n @if (showManageLink()) {\n <div class=\"border-t border-gray-200 px-2 py-2\">\n <a\n [routerLink]=\"managePath()\"\n (click)=\"popover().hide()\"\n class=\"flex items-center gap-2 px-3 py-2 rounded-md text-gray-700 hover:bg-surface-100 text-sm cursor-pointer\"\n >\n <mt-icon\n icon=\"general.settings-02\"\n styleClass=\"text-base\"\n ></mt-icon>\n <span>{{ t(\"delegations.session.manage\") }}</span>\n </a>\n </div>\n }\n </div>\n </p-popover>\n }\n</ng-container>\n", styles: [":host{display:inline-flex;align-items:center;min-width:0;max-width:100%}.mt-delegation-trigger{min-width:0;max-width:100%}.mt-delegation-trigger__button{transition:background-color .2s ease-out;min-width:0;max-width:100%}.mt-delegation-trigger__label{max-width:min(16rem,28vw);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}@media(max-width:1200px){.mt-delegation-trigger__label{max-width:10rem}}@media(max-width:1024px){.mt-delegation-trigger__button{gap:.375rem;padding-inline:.375rem}.mt-delegation-trigger__label{display:none}}:host-context(.dark) .mt-delegation-trigger__button:hover{background-color:#94a3b826}\n"] }]
|
|
526
|
+
}], propDecorators: { managePath: [{ type: i0.Input, args: [{ isSignal: true, alias: "managePath", required: false }] }], compact: [{ type: i0.Input, args: [{ isSignal: true, alias: "compact", required: false }] }], showManageLink: [{ type: i0.Input, args: [{ isSignal: true, alias: "showManageLink", required: false }] }], popover: [{ type: i0.ViewChild, args: ['popover', { isSignal: true }] }] } });
|
|
522
527
|
|
|
523
528
|
// ---------------------------------------------------------------------------
|
|
524
529
|
// Lists / detail
|
|
@@ -537,6 +542,13 @@ class GetAssignedDelegations {
|
|
|
537
542
|
this.query = query;
|
|
538
543
|
}
|
|
539
544
|
}
|
|
545
|
+
class GetAdminDelegations {
|
|
546
|
+
query;
|
|
547
|
+
static type = '[Delegations] Get Admin Delegations';
|
|
548
|
+
constructor(query = {}) {
|
|
549
|
+
this.query = query;
|
|
550
|
+
}
|
|
551
|
+
}
|
|
540
552
|
class GetActiveAssignedDelegations {
|
|
541
553
|
query;
|
|
542
554
|
static type = '[Delegations] Get Active Assigned Delegations';
|
|
@@ -649,6 +661,7 @@ var DelegationsActionKey;
|
|
|
649
661
|
(function (DelegationsActionKey) {
|
|
650
662
|
DelegationsActionKey["GetMy"] = "getMy";
|
|
651
663
|
DelegationsActionKey["GetAssigned"] = "getAssigned";
|
|
664
|
+
DelegationsActionKey["GetAdmin"] = "getAdmin";
|
|
652
665
|
DelegationsActionKey["GetActive"] = "getActive";
|
|
653
666
|
DelegationsActionKey["GetDetail"] = "getDetail";
|
|
654
667
|
DelegationsActionKey["GetScopeOptions"] = "getScopeOptions";
|
|
@@ -702,6 +715,9 @@ let DelegationsState = class DelegationsState {
|
|
|
702
715
|
static getAssigned(state) {
|
|
703
716
|
return state.assigned;
|
|
704
717
|
}
|
|
718
|
+
static getAdmin(state) {
|
|
719
|
+
return state.admin;
|
|
720
|
+
}
|
|
705
721
|
static getActive(state) {
|
|
706
722
|
return state.active;
|
|
707
723
|
}
|
|
@@ -743,6 +759,15 @@ let DelegationsState = class DelegationsState {
|
|
|
743
759
|
onSuccess: (response) => ({ assigned: response.data }),
|
|
744
760
|
});
|
|
745
761
|
}
|
|
762
|
+
getAdmin(ctx, { query }) {
|
|
763
|
+
const req$ = this.http.get(`${BASE}/All`, { params: buildListParams(query) });
|
|
764
|
+
return handleApiRequest({
|
|
765
|
+
ctx,
|
|
766
|
+
key: DelegationsActionKey.GetAdmin,
|
|
767
|
+
request$: req$,
|
|
768
|
+
onSuccess: (response) => ({ admin: response.data }),
|
|
769
|
+
});
|
|
770
|
+
}
|
|
746
771
|
getActive(ctx, { query }) {
|
|
747
772
|
const req$ = this.http.get(`${BASE}/user/activedelegations`, { params: buildListParams(query) });
|
|
748
773
|
return handleApiRequest({
|
|
@@ -873,6 +898,9 @@ __decorate([
|
|
|
873
898
|
__decorate([
|
|
874
899
|
Action(GetAssignedDelegations)
|
|
875
900
|
], DelegationsState.prototype, "getAssigned", null);
|
|
901
|
+
__decorate([
|
|
902
|
+
Action(GetAdminDelegations)
|
|
903
|
+
], DelegationsState.prototype, "getAdmin", null);
|
|
876
904
|
__decorate([
|
|
877
905
|
Action(GetActiveAssignedDelegations)
|
|
878
906
|
], DelegationsState.prototype, "getActive", null);
|
|
@@ -918,6 +946,9 @@ __decorate([
|
|
|
918
946
|
__decorate([
|
|
919
947
|
Selector()
|
|
920
948
|
], DelegationsState, "getAssigned", null);
|
|
949
|
+
__decorate([
|
|
950
|
+
Selector()
|
|
951
|
+
], DelegationsState, "getAdmin", null);
|
|
921
952
|
__decorate([
|
|
922
953
|
Selector()
|
|
923
954
|
], DelegationsState, "getActive", null);
|
|
@@ -942,6 +973,7 @@ DelegationsState = __decorate([
|
|
|
942
973
|
defaults: {
|
|
943
974
|
my: null,
|
|
944
975
|
assigned: null,
|
|
976
|
+
admin: null,
|
|
945
977
|
active: null,
|
|
946
978
|
detail: null,
|
|
947
979
|
scopeOptions: null,
|
|
@@ -953,7 +985,7 @@ DelegationsState = __decorate([
|
|
|
953
985
|
], DelegationsState);
|
|
954
986
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: DelegationsState, decorators: [{
|
|
955
987
|
type: Injectable
|
|
956
|
-
}], propDecorators: { getMy: [], getAssigned: [], getActive: [], getDetail: [], clearDetail: [], getScopeOptions: [], previewScope: [], clearScopePreview: [], createLegacy: [], createV2: [], updateLegacy: [], updateV2: [], approve: [], reject: [], cancel: [] } });
|
|
988
|
+
}], propDecorators: { getMy: [], getAssigned: [], getAdmin: [], getActive: [], getDetail: [], clearDetail: [], getScopeOptions: [], previewScope: [], clearScopePreview: [], createLegacy: [], createV2: [], updateLegacy: [], updateV2: [], approve: [], reject: [], cancel: [] } });
|
|
957
989
|
|
|
958
990
|
class DelegationsFacade {
|
|
959
991
|
store = inject(Store);
|
|
@@ -962,6 +994,7 @@ class DelegationsFacade {
|
|
|
962
994
|
// ---------------------------------------------------------------------------
|
|
963
995
|
myPage = select(DelegationsState.getMy);
|
|
964
996
|
assignedPage = select(DelegationsState.getAssigned);
|
|
997
|
+
adminPage = select(DelegationsState.getAdmin);
|
|
965
998
|
activePage = select(DelegationsState.getActive);
|
|
966
999
|
detail = select(DelegationsState.getDetail);
|
|
967
1000
|
scopeOptions = select(DelegationsState.getScopeOptions);
|
|
@@ -973,12 +1006,14 @@ class DelegationsFacade {
|
|
|
973
1006
|
// ---------------------------------------------------------------------------
|
|
974
1007
|
myItems = computed(() => this.myPage()?.items ?? [], ...(ngDevMode ? [{ debugName: "myItems" }] : /* istanbul ignore next */ []));
|
|
975
1008
|
assignedItems = computed(() => this.assignedPage()?.items ?? [], ...(ngDevMode ? [{ debugName: "assignedItems" }] : /* istanbul ignore next */ []));
|
|
1009
|
+
adminItems = computed(() => this.adminPage()?.items ?? [], ...(ngDevMode ? [{ debugName: "adminItems" }] : /* istanbul ignore next */ []));
|
|
976
1010
|
activeItems = computed(() => this.activePage()?.items ?? [], ...(ngDevMode ? [{ debugName: "activeItems" }] : /* istanbul ignore next */ []));
|
|
977
1011
|
// ---------------------------------------------------------------------------
|
|
978
1012
|
// Loading
|
|
979
1013
|
// ---------------------------------------------------------------------------
|
|
980
1014
|
isLoadingMy = computed(() => this.loadingActive().includes(DelegationsActionKey.GetMy), ...(ngDevMode ? [{ debugName: "isLoadingMy" }] : /* istanbul ignore next */ []));
|
|
981
1015
|
isLoadingAssigned = computed(() => this.loadingActive().includes(DelegationsActionKey.GetAssigned), ...(ngDevMode ? [{ debugName: "isLoadingAssigned" }] : /* istanbul ignore next */ []));
|
|
1016
|
+
isLoadingAdmin = computed(() => this.loadingActive().includes(DelegationsActionKey.GetAdmin), ...(ngDevMode ? [{ debugName: "isLoadingAdmin" }] : /* istanbul ignore next */ []));
|
|
982
1017
|
isLoadingActive = computed(() => this.loadingActive().includes(DelegationsActionKey.GetActive), ...(ngDevMode ? [{ debugName: "isLoadingActive" }] : /* istanbul ignore next */ []));
|
|
983
1018
|
isLoadingDetail = computed(() => this.loadingActive().includes(DelegationsActionKey.GetDetail), ...(ngDevMode ? [{ debugName: "isLoadingDetail" }] : /* istanbul ignore next */ []));
|
|
984
1019
|
isLoadingScopeOptions = computed(() => this.loadingActive().includes(DelegationsActionKey.GetScopeOptions), ...(ngDevMode ? [{ debugName: "isLoadingScopeOptions" }] : /* istanbul ignore next */ []));
|
|
@@ -993,6 +1028,7 @@ class DelegationsFacade {
|
|
|
993
1028
|
// ---------------------------------------------------------------------------
|
|
994
1029
|
errorMy = computed(() => this.errors()[DelegationsActionKey.GetMy] ?? null, ...(ngDevMode ? [{ debugName: "errorMy" }] : /* istanbul ignore next */ []));
|
|
995
1030
|
errorAssigned = computed(() => this.errors()[DelegationsActionKey.GetAssigned] ?? null, ...(ngDevMode ? [{ debugName: "errorAssigned" }] : /* istanbul ignore next */ []));
|
|
1031
|
+
errorAdmin = computed(() => this.errors()[DelegationsActionKey.GetAdmin] ?? null, ...(ngDevMode ? [{ debugName: "errorAdmin" }] : /* istanbul ignore next */ []));
|
|
996
1032
|
errorDetail = computed(() => this.errors()[DelegationsActionKey.GetDetail] ?? null, ...(ngDevMode ? [{ debugName: "errorDetail" }] : /* istanbul ignore next */ []));
|
|
997
1033
|
errorScopeOptions = computed(() => this.errors()[DelegationsActionKey.GetScopeOptions] ?? null, ...(ngDevMode ? [{ debugName: "errorScopeOptions" }] : /* istanbul ignore next */ []));
|
|
998
1034
|
errorSave = computed(() => this.errors()[DelegationsActionKey.Create] ??
|
|
@@ -1007,6 +1043,9 @@ class DelegationsFacade {
|
|
|
1007
1043
|
getAssigned(query = {}) {
|
|
1008
1044
|
return this.store.dispatch(new GetAssignedDelegations(query));
|
|
1009
1045
|
}
|
|
1046
|
+
getAdmin(query = {}) {
|
|
1047
|
+
return this.store.dispatch(new GetAdminDelegations(query));
|
|
1048
|
+
}
|
|
1010
1049
|
getActive(query = {}) {
|
|
1011
1050
|
return this.store.dispatch(new GetActiveAssignedDelegations(query));
|
|
1012
1051
|
}
|
|
@@ -1152,17 +1191,25 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
|
|
|
1152
1191
|
args: [{ selector: 'mt-delegations', imports: [Page, RouterOutlet, TranslocoDirective], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-container *transloco=\"let t\">\n <mt-page\n [title]=\"t('delegations.title')\"\n [avatarIcon]=\"'custom.hierarchy-structure'\"\n [tabs]=\"tabs()\"\n [activeTab]=\"activeTab()\"\n [contentClass]=\"'max-[1025px]:p-4 max-[640px]:p-3'\"\n [avatarStyle]=\"{\n '--p-avatar-background': 'var(--p-indigo-50)',\n '--p-avatar-color': 'var(--p-indigo-700)',\n }\"\n (tabChange)=\"onTabChange($event)\"\n (backButtonClick)=\"goBack()\"\n backButton\n >\n <router-outlet />\n </mt-page>\n</ng-container>\n" }]
|
|
1153
1192
|
}], ctorParameters: () => [] });
|
|
1154
1193
|
|
|
1194
|
+
const DEFAULT_SORT_ORDER = Number.MAX_SAFE_INTEGER;
|
|
1155
1195
|
function toArray(value) {
|
|
1156
1196
|
return Array.isArray(value) ? value : [];
|
|
1157
1197
|
}
|
|
1158
1198
|
function normalizeGrant$1(grant) {
|
|
1159
1199
|
return {
|
|
1160
1200
|
...grant,
|
|
1201
|
+
action: normalizeAction(grant.action),
|
|
1161
1202
|
accessibilities: toArray(grant.accessibilities),
|
|
1162
1203
|
dataFilters: toArray(grant.dataFilters),
|
|
1163
1204
|
constraints: toArray(grant.constraints),
|
|
1164
1205
|
};
|
|
1165
1206
|
}
|
|
1207
|
+
function normalizeAction(action) {
|
|
1208
|
+
return {
|
|
1209
|
+
...action,
|
|
1210
|
+
isDelegableSnapshot: action.isDelegableSnapshot ?? action.isDelegable ?? undefined,
|
|
1211
|
+
};
|
|
1212
|
+
}
|
|
1166
1213
|
function normalizeKey(value) {
|
|
1167
1214
|
if (typeof value === 'string') {
|
|
1168
1215
|
const trimmed = value.trim();
|
|
@@ -1173,21 +1220,36 @@ function normalizeKey(value) {
|
|
|
1173
1220
|
}
|
|
1174
1221
|
return null;
|
|
1175
1222
|
}
|
|
1176
|
-
function
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1223
|
+
function normalizeSortOrder(value) {
|
|
1224
|
+
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
1225
|
+
return value;
|
|
1226
|
+
}
|
|
1227
|
+
const numericValue = Number(value);
|
|
1228
|
+
return Number.isFinite(numericValue) ? numericValue : DEFAULT_SORT_ORDER;
|
|
1182
1229
|
}
|
|
1183
|
-
function
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1230
|
+
function compareBySortThenKey(left, right, leftKey, rightKey) {
|
|
1231
|
+
if (left !== right) {
|
|
1232
|
+
return left - right;
|
|
1233
|
+
}
|
|
1234
|
+
return leftKey.localeCompare(rightKey);
|
|
1235
|
+
}
|
|
1236
|
+
function targetIdentity(target) {
|
|
1237
|
+
return `${target.applicationKey}::${target.targetType}::${target.targetKey}`;
|
|
1238
|
+
}
|
|
1239
|
+
function groupIdentity(groupType, groupKey, fallbackTargetKey) {
|
|
1240
|
+
return `${groupType}::${groupKey || fallbackTargetKey || 'group'}`;
|
|
1241
|
+
}
|
|
1242
|
+
function buildHierarchyGrant(target, action) {
|
|
1243
|
+
return normalizeGrant$1({
|
|
1244
|
+
applicationKey: normalizeKey(target.applicationKey) ??
|
|
1245
|
+
normalizeKey(action.applicationKey) ??
|
|
1246
|
+
'',
|
|
1247
|
+
target,
|
|
1248
|
+
action: normalizeAction(action),
|
|
1249
|
+
accessibilities: [],
|
|
1250
|
+
dataFilters: [],
|
|
1251
|
+
constraints: [],
|
|
1252
|
+
});
|
|
1191
1253
|
}
|
|
1192
1254
|
function factLookupKeys(fact) {
|
|
1193
1255
|
const candidates = [
|
|
@@ -1245,6 +1307,22 @@ function ruleKeys(rule, preferredKeys) {
|
|
|
1245
1307
|
}
|
|
1246
1308
|
return null;
|
|
1247
1309
|
}
|
|
1310
|
+
function matchesTarget(target, rule) {
|
|
1311
|
+
return (target.targetType === rule.targetType &&
|
|
1312
|
+
target.targetKey === rule.targetKey &&
|
|
1313
|
+
(!rule.applicationKey ||
|
|
1314
|
+
!target.applicationKey ||
|
|
1315
|
+
target.applicationKey === rule.applicationKey));
|
|
1316
|
+
}
|
|
1317
|
+
function matchesAction(action, rule, applicationKey) {
|
|
1318
|
+
return (action.operationKey === rule.operationKey &&
|
|
1319
|
+
(!rule.applicationKey ||
|
|
1320
|
+
!action.applicationKey ||
|
|
1321
|
+
action.applicationKey === rule.applicationKey) &&
|
|
1322
|
+
(!applicationKey ||
|
|
1323
|
+
!action.applicationKey ||
|
|
1324
|
+
action.applicationKey === applicationKey));
|
|
1325
|
+
}
|
|
1248
1326
|
function grantIdentity(grant) {
|
|
1249
1327
|
return JSON.stringify({
|
|
1250
1328
|
app: grant.applicationKey,
|
|
@@ -1268,7 +1346,35 @@ function grantIdentity(grant) {
|
|
|
1268
1346
|
constraints: grant.constraints.map(factLookupKeys),
|
|
1269
1347
|
});
|
|
1270
1348
|
}
|
|
1271
|
-
function
|
|
1349
|
+
function buildGroupsFromHierarchy(hierarchy) {
|
|
1350
|
+
return hierarchy
|
|
1351
|
+
.map((group) => {
|
|
1352
|
+
const targets = toArray(group.targets)
|
|
1353
|
+
.map((targetNode) => {
|
|
1354
|
+
const grants = toArray(targetNode.operations)
|
|
1355
|
+
.map((operation) => buildHierarchyGrant(targetNode.target, operation))
|
|
1356
|
+
.sort((left, right) => compareBySortThenKey(normalizeSortOrder(left.action.sortOrder), normalizeSortOrder(right.action.sortOrder), left.action.operationKey, right.action.operationKey));
|
|
1357
|
+
return {
|
|
1358
|
+
key: targetIdentity(targetNode.target),
|
|
1359
|
+
target: targetNode.target,
|
|
1360
|
+
grants,
|
|
1361
|
+
sortOrder: normalizeSortOrder(targetNode.sortOrder ?? targetNode.target.levelModuleId),
|
|
1362
|
+
};
|
|
1363
|
+
})
|
|
1364
|
+
.filter((targetNode) => targetNode.grants.length > 0)
|
|
1365
|
+
.sort((left, right) => compareBySortThenKey(left.sortOrder, right.sortOrder, left.key, right.key));
|
|
1366
|
+
return {
|
|
1367
|
+
key: groupIdentity(group.groupType, group.groupKey),
|
|
1368
|
+
groupType: group.groupType,
|
|
1369
|
+
displayName: group.displayName,
|
|
1370
|
+
sortOrder: normalizeSortOrder(group.sortOrder ?? group.levelId),
|
|
1371
|
+
targets,
|
|
1372
|
+
};
|
|
1373
|
+
})
|
|
1374
|
+
.filter((group) => group.targets.length > 0)
|
|
1375
|
+
.sort((left, right) => compareBySortThenKey(left.sortOrder, right.sortOrder, left.key, right.key));
|
|
1376
|
+
}
|
|
1377
|
+
function getLegacyGrantableGrants(options) {
|
|
1272
1378
|
if (!options) {
|
|
1273
1379
|
return [];
|
|
1274
1380
|
}
|
|
@@ -1307,7 +1413,7 @@ function getDelegationGrantableGrants(options) {
|
|
|
1307
1413
|
normalizeKey(target.applicationKey) ??
|
|
1308
1414
|
'',
|
|
1309
1415
|
target,
|
|
1310
|
-
action,
|
|
1416
|
+
action: normalizeAction(action),
|
|
1311
1417
|
accessibilities: lookupFacts(ruleKeys(rule, ['accessibilityKeys', 'accessibilities']), accessibilityIndex),
|
|
1312
1418
|
dataFilters: lookupFacts(ruleKeys(rule, ['dataFilterKeys', 'dataFilters', 'filterKeys']), dataFilterIndex),
|
|
1313
1419
|
constraints: lookupFacts(ruleKeys(rule, ['constraintKeys', 'constraints']), constraintIndex),
|
|
@@ -1321,14 +1427,65 @@ function getDelegationGrantableGrants(options) {
|
|
|
1321
1427
|
}
|
|
1322
1428
|
return grants;
|
|
1323
1429
|
}
|
|
1430
|
+
function buildGroupsFromGrants(grants) {
|
|
1431
|
+
const groups = new Map();
|
|
1432
|
+
for (const grant of grants) {
|
|
1433
|
+
const parentGroupKey = normalizeKey(grant.target.parentTargetKey) ?? grant.target.targetKey;
|
|
1434
|
+
const parentGroupType = grant.target.parentTargetType ?? grant.target.targetType;
|
|
1435
|
+
const groupKey = groupIdentity(parentGroupType, parentGroupKey, grant.target.targetKey);
|
|
1436
|
+
let group = groups.get(groupKey);
|
|
1437
|
+
if (!group) {
|
|
1438
|
+
group = {
|
|
1439
|
+
groupType: parentGroupType,
|
|
1440
|
+
displayName: grant.target.parentDisplayName ?? grant.target.displayName ?? null,
|
|
1441
|
+
sortOrder: normalizeSortOrder(grant.target.parentLevelId ?? grant.target.levelId),
|
|
1442
|
+
targets: new Map(),
|
|
1443
|
+
};
|
|
1444
|
+
groups.set(groupKey, group);
|
|
1445
|
+
}
|
|
1446
|
+
const targetKey = targetIdentity(grant.target);
|
|
1447
|
+
const existingTarget = group.targets.get(targetKey);
|
|
1448
|
+
if (existingTarget) {
|
|
1449
|
+
existingTarget.grants.push(grant);
|
|
1450
|
+
continue;
|
|
1451
|
+
}
|
|
1452
|
+
group.targets.set(targetKey, {
|
|
1453
|
+
key: targetKey,
|
|
1454
|
+
target: grant.target,
|
|
1455
|
+
grants: [grant],
|
|
1456
|
+
sortOrder: normalizeSortOrder(grant.target.levelModuleId ?? grant.target.targetId),
|
|
1457
|
+
});
|
|
1458
|
+
}
|
|
1459
|
+
return Array.from(groups.entries())
|
|
1460
|
+
.map(([key, value]) => ({
|
|
1461
|
+
key,
|
|
1462
|
+
groupType: value.groupType,
|
|
1463
|
+
displayName: value.displayName,
|
|
1464
|
+
sortOrder: value.sortOrder,
|
|
1465
|
+
targets: Array.from(value.targets.values())
|
|
1466
|
+
.map((targetGroup) => ({
|
|
1467
|
+
...targetGroup,
|
|
1468
|
+
grants: [...targetGroup.grants].sort((left, right) => compareBySortThenKey(normalizeSortOrder(left.action.sortOrder), normalizeSortOrder(right.action.sortOrder), left.action.operationKey, right.action.operationKey)),
|
|
1469
|
+
}))
|
|
1470
|
+
.sort((left, right) => compareBySortThenKey(left.sortOrder, right.sortOrder, left.key, right.key)),
|
|
1471
|
+
}))
|
|
1472
|
+
.sort((left, right) => compareBySortThenKey(left.sortOrder, right.sortOrder, left.key, right.key));
|
|
1473
|
+
}
|
|
1474
|
+
function getDelegationGrantableGroups(options) {
|
|
1475
|
+
const hierarchyGroups = buildGroupsFromHierarchy(toArray(options?.hierarchy));
|
|
1476
|
+
if (hierarchyGroups.length > 0) {
|
|
1477
|
+
return hierarchyGroups;
|
|
1478
|
+
}
|
|
1479
|
+
return buildGroupsFromGrants(getLegacyGrantableGrants(options));
|
|
1480
|
+
}
|
|
1481
|
+
function getDelegationGrantableGrants(options) {
|
|
1482
|
+
return getDelegationGrantableGroups(options).flatMap((group) => group.targets.flatMap((targetGroup) => targetGroup.grants));
|
|
1483
|
+
}
|
|
1324
1484
|
function hasDelegationGrantableScope(options) {
|
|
1325
|
-
return
|
|
1485
|
+
return getDelegationGrantableGroups(options).length > 0;
|
|
1326
1486
|
}
|
|
1327
1487
|
|
|
1328
1488
|
const EMPTY_SCOPE$1 = { grants: [], metadata: {} };
|
|
1329
|
-
function targetKey(t) {
|
|
1330
|
-
return `${t.applicationKey}::${t.targetType}::${t.targetKey}`;
|
|
1331
|
-
}
|
|
1332
1489
|
/**
|
|
1333
1490
|
* Full grant identity. Two grants with the same target/action but different
|
|
1334
1491
|
* accessibilities, data filters, or constraints are DIFFERENT grants and must
|
|
@@ -1373,13 +1530,14 @@ function normalizeGrant(g) {
|
|
|
1373
1530
|
};
|
|
1374
1531
|
}
|
|
1375
1532
|
/**
|
|
1376
|
-
*
|
|
1377
|
-
* -
|
|
1378
|
-
* - lets the user toggle each grant,
|
|
1533
|
+
* Hierarchy-based scope picker (doc 03). Driven entirely by `scope/options`:
|
|
1534
|
+
* - renders backend-owned Level -> Level Module -> operations hierarchy,
|
|
1535
|
+
* - lets the user toggle each explicit grant,
|
|
1379
1536
|
* - calls `scope/preview` (debounced) and surfaces summary / warnings / denied.
|
|
1380
1537
|
*
|
|
1381
1538
|
* `[(scope)]` two-way binds a `DelegationScopeSelection`. The component never
|
|
1382
|
-
* invents permission metadata — it only echoes
|
|
1539
|
+
* invents permission metadata — it only echoes targets/actions returned by
|
|
1540
|
+
* options.
|
|
1383
1541
|
*/
|
|
1384
1542
|
class ScopePicker {
|
|
1385
1543
|
scope = model(EMPTY_SCOPE$1, ...(ngDevMode ? [{ debugName: "scope" }] : /* istanbul ignore next */ []));
|
|
@@ -1397,22 +1555,7 @@ class ScopePicker {
|
|
|
1397
1555
|
isLoadingOptions = this.facade.isLoadingScopeOptions;
|
|
1398
1556
|
isPreviewing = this.facade.isPreviewingScope;
|
|
1399
1557
|
errorOptions = this.facade.errorScopeOptions;
|
|
1400
|
-
|
|
1401
|
-
groups = computed(() => {
|
|
1402
|
-
const grants = getDelegationGrantableGrants(this.options());
|
|
1403
|
-
const map = new Map();
|
|
1404
|
-
for (const g of grants) {
|
|
1405
|
-
const key = targetKey(g.target);
|
|
1406
|
-
const existing = map.get(key);
|
|
1407
|
-
if (existing) {
|
|
1408
|
-
existing.grants.push(g);
|
|
1409
|
-
}
|
|
1410
|
-
else {
|
|
1411
|
-
map.set(key, { key, target: g.target, grants: [g] });
|
|
1412
|
-
}
|
|
1413
|
-
}
|
|
1414
|
-
return Array.from(map.values());
|
|
1415
|
-
}, ...(ngDevMode ? [{ debugName: "groups" }] : /* istanbul ignore next */ []));
|
|
1558
|
+
groups = computed(() => getDelegationGrantableGroups(this.options()), ...(ngDevMode ? [{ debugName: "groups" }] : /* istanbul ignore next */ []));
|
|
1416
1559
|
/** Selected grant keys for O(1) checkbox state. */
|
|
1417
1560
|
selectedKeys = computed(() => new Set(this.scope().grants.map(grantKey)), ...(ngDevMode ? [{ debugName: "selectedKeys" }] : /* istanbul ignore next */ []));
|
|
1418
1561
|
expanded = signal(new Set(), ...(ngDevMode ? [{ debugName: "expanded" }] : /* istanbul ignore next */ []));
|
|
@@ -1448,6 +1591,21 @@ class ScopePicker {
|
|
|
1448
1591
|
timer = setTimeout(() => this.facade.previewScope(s), 300);
|
|
1449
1592
|
});
|
|
1450
1593
|
});
|
|
1594
|
+
let lastGroupKeys = '';
|
|
1595
|
+
effect(() => {
|
|
1596
|
+
const groups = this.groups();
|
|
1597
|
+
const nextGroupKeys = groups.map((group) => group.key).join('|');
|
|
1598
|
+
if (!nextGroupKeys) {
|
|
1599
|
+
lastGroupKeys = '';
|
|
1600
|
+
this.expanded.set(new Set());
|
|
1601
|
+
return;
|
|
1602
|
+
}
|
|
1603
|
+
if (nextGroupKeys === lastGroupKeys) {
|
|
1604
|
+
return;
|
|
1605
|
+
}
|
|
1606
|
+
lastGroupKeys = nextGroupKeys;
|
|
1607
|
+
this.expanded.set(new Set(groups.map((group) => group.key)));
|
|
1608
|
+
});
|
|
1451
1609
|
}
|
|
1452
1610
|
isExpanded(group) {
|
|
1453
1611
|
return this.expanded().has(group.key);
|
|
@@ -1463,12 +1621,19 @@ class ScopePicker {
|
|
|
1463
1621
|
isSelected(grant) {
|
|
1464
1622
|
return this.selectedKeys().has(grantKey(grant));
|
|
1465
1623
|
}
|
|
1624
|
+
getGroupLabel(group) {
|
|
1625
|
+
return (formatDelegationDisplayValue(group.displayName, this.currentLanguage()) ||
|
|
1626
|
+
humanizeDelegationKey(group.groupType));
|
|
1627
|
+
}
|
|
1466
1628
|
getTargetLabel(target) {
|
|
1467
1629
|
return formatDelegationTargetLabel(target, this.currentLanguage());
|
|
1468
1630
|
}
|
|
1469
1631
|
getActionLabel(grant) {
|
|
1470
1632
|
return formatDelegationActionLabel(grant.action, this.currentLanguage());
|
|
1471
1633
|
}
|
|
1634
|
+
getTargetActions(targetGroup) {
|
|
1635
|
+
return targetGroup.grants;
|
|
1636
|
+
}
|
|
1472
1637
|
getPreviewSummary(summary) {
|
|
1473
1638
|
return formatDelegationScopeSummary(summary, this.currentLanguage());
|
|
1474
1639
|
}
|
|
@@ -1492,13 +1657,19 @@ class ScopePicker {
|
|
|
1492
1657
|
}
|
|
1493
1658
|
selectedCount(group) {
|
|
1494
1659
|
const keys = this.selectedKeys();
|
|
1495
|
-
return group.
|
|
1660
|
+
return group.targets.reduce((count, targetGroup) => count +
|
|
1661
|
+
targetGroup.grants.filter((grant) => keys.has(grantKey(grant))).length, 0);
|
|
1662
|
+
}
|
|
1663
|
+
selectedTargetCount(targetGroup) {
|
|
1664
|
+
const keys = this.selectedKeys();
|
|
1665
|
+
return targetGroup.grants.filter((grant) => keys.has(grantKey(grant)))
|
|
1666
|
+
.length;
|
|
1496
1667
|
}
|
|
1497
1668
|
currentLanguage() {
|
|
1498
1669
|
return this.transloco.getActiveLang();
|
|
1499
1670
|
}
|
|
1500
1671
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: ScopePicker, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1501
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: ScopePicker, isStandalone: true, selector: "mt-scope-picker", inputs: { scope: { classPropertyName: "scope", publicName: "scope", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, delegatorUserId: { classPropertyName: "delegatorUserId", publicName: "delegatorUserId", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { scope: "scopeChange" }, ngImport: i0, template: "<ng-container *transloco=\"let t\">\n <div class=\"mt-scope-picker flex flex-col gap-4\">\n @if (isLoadingOptions()) {\n <p-skeleton height=\"2rem\"></p-skeleton>\n <p-skeleton height=\"6rem\"></p-skeleton>\n <p-skeleton height=\"6rem\"></p-skeleton>\n } @else {\n <section class=\"flex flex-col gap-2\">\n
|
|
1672
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: ScopePicker, isStandalone: true, selector: "mt-scope-picker", inputs: { scope: { classPropertyName: "scope", publicName: "scope", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, delegatorUserId: { classPropertyName: "delegatorUserId", publicName: "delegatorUserId", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { scope: "scopeChange" }, ngImport: i0, template: "<ng-container *transloco=\"let t\">\n <div class=\"mt-scope-picker flex flex-col gap-4\">\n @if (isLoadingOptions()) {\n <p-skeleton height=\"2rem\"></p-skeleton>\n <p-skeleton height=\"6rem\"></p-skeleton>\n <p-skeleton height=\"6rem\"></p-skeleton>\n } @else {\n <section class=\"flex flex-col gap-2\">\n @if (errorOptions(); as errorMessage) {\n <div\n class=\"rounded-md border border-red-200 bg-red-50 px-3 py-2 text-xs text-red-700\"\n >\n {{ errorMessage }}\n </div>\n }\n\n @if (groups().length === 0 && !errorOptions()) {\n <p class=\"text-xs text-gray-500\">\n {{ t(\"delegations.scope.noGrantableOptions\") }}\n </p>\n }\n\n @for (group of groups(); track group.key) {\n <div class=\"overflow-hidden rounded-md border border-gray-200\">\n <button\n type=\"button\"\n class=\"flex w-full cursor-pointer items-center justify-between px-3 py-2 hover:bg-surface-50\"\n (click)=\"toggleAccordion(group)\"\n >\n <div class=\"flex min-w-0 items-center gap-2\">\n <mt-icon\n [icon]=\"\n isExpanded(group)\n ? 'arrow.chevron-down'\n : 'arrow.chevron-right'\n \"\n styleClass=\"text-base text-gray-400\"\n ></mt-icon>\n <span class=\"truncate text-sm text-gray-900\">\n {{ getGroupLabel(group) }}\n </span>\n </div>\n @if (selectedCount(group); as selectedGrantCount) {\n @if (selectedGrantCount > 0) {\n <span\n class=\"rounded-full bg-primary-50 px-2 py-0.5 text-xs font-medium text-primary\"\n >\n {{ selectedGrantCount }}\n </span>\n }\n }\n </button>\n\n @if (isExpanded(group)) {\n <div\n class=\"mt-scope-picker__group flex flex-col gap-3 bg-surface-50/40 px-3 py-3\"\n >\n @for (targetGroup of group.targets; track targetGroup.key) {\n <section\n class=\"rounded-md border border-surface-200 bg-white px-3 py-3\"\n >\n <div\n class=\"flex flex-wrap items-center justify-between gap-2\"\n >\n <div class=\"min-w-0\">\n <div class=\"truncate text-sm font-medium text-gray-900\">\n {{ getTargetLabel(targetGroup.target) }}\n </div>\n </div>\n @if (\n selectedTargetCount(targetGroup);\n as selectedTargetGrantCount\n ) {\n @if (selectedTargetGrantCount > 0) {\n <span\n class=\"rounded-full bg-primary-50 px-2 py-0.5 text-xs font-medium text-primary\"\n >\n {{ selectedTargetGrantCount }}\n </span>\n }\n }\n </div>\n\n <div\n class=\"mt-scope-picker__actions mt-3 flex flex-col gap-1\"\n >\n @for (\n grant of getTargetActions(targetGroup);\n track $index\n ) {\n <label\n class=\"flex cursor-pointer items-center gap-2 rounded-md px-2 py-1.5 hover:bg-surface-100\"\n >\n <input\n type=\"checkbox\"\n [checked]=\"isSelected(grant)\"\n [disabled]=\"readonly()\"\n (change)=\"toggleGrant(grant)\"\n />\n <span class=\"text-sm\">\n {{ getActionLabel(grant) }}\n </span>\n @if (grant.action.isHighRisk) {\n <mt-icon\n icon=\"alert.alert-triangle\"\n styleClass=\"text-sm text-amber-500\"\n [pTooltip]=\"t('delegations.scope.highRisk')\"\n tooltipPosition=\"top\"\n ></mt-icon>\n }\n </label>\n }\n </div>\n </section>\n } @empty {\n <div class=\"text-xs text-gray-500\">\n {{ t(\"delegations.scope.noGrantableOptions\") }}\n </div>\n }\n </div>\n }\n </div>\n }\n </section>\n\n <section\n class=\"flex flex-col gap-1 rounded-md border border-dashed border-gray-300 bg-surface-50 px-3 py-2\"\n >\n <div class=\"flex items-center justify-between gap-2\">\n <div class=\"text-xs font-medium text-gray-700\">\n {{ t(\"delegations.scope.previewTitle\") }}\n </div>\n @if (isPreviewing()) {\n <p-skeleton width=\"6rem\" height=\"0.75rem\"></p-skeleton>\n }\n </div>\n @if (preview(); as p) {\n @if (p.isValid) {\n <div class=\"text-sm text-gray-900\">\n {{\n getPreviewSummary(p.summary) ||\n t(\"delegations.column.scopeSummary\")\n }}\n </div>\n } @else {\n <div class=\"text-sm text-red-700\">\n {{ t(\"delegations.scope.previewInvalid\") }}\n </div>\n }\n @if (p.warnings.length > 0) {\n <ul class=\"list-inside list-disc text-xs text-amber-700\">\n @for (w of p.warnings; track w.message) {\n <li>{{ w.message }}</li>\n }\n </ul>\n }\n @if (p.deniedItems.length > 0) {\n <ul class=\"list-inside list-disc text-xs text-red-700\">\n @for (d of p.deniedItems; track d.operationKey) {\n <li>\n {{ formatDeniedTarget(d.targetKey) }} /\n {{ formatDeniedOperation(d.operationKey) }} -\n {{ d.reasonCode }}\n </li>\n }\n </ul>\n }\n } @else {\n <div class=\"text-xs text-gray-500\">\n {{ t(\"delegations.scope.noSelection\") }}\n </div>\n }\n </section>\n }\n </div>\n</ng-container>\n", styles: [":host{display:block}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: Icon, selector: "mt-icon", inputs: ["icon"] }, { kind: "ngmodule", type: SkeletonModule }, { kind: "component", type: i1.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "shape", "animation", "borderRadius", "size", "width", "height"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i2.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "showOnEllipsis", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip", "pTooltipPT", "pTooltipUnstyled"] }, { kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1502
1673
|
}
|
|
1503
1674
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: ScopePicker, decorators: [{
|
|
1504
1675
|
type: Component,
|
|
@@ -1508,10 +1679,53 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
|
|
|
1508
1679
|
SkeletonModule,
|
|
1509
1680
|
TooltipModule,
|
|
1510
1681
|
TranslocoDirective,
|
|
1511
|
-
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-container *transloco=\"let t\">\n <div class=\"mt-scope-picker flex flex-col gap-4\">\n @if (isLoadingOptions()) {\n <p-skeleton height=\"2rem\"></p-skeleton>\n <p-skeleton height=\"6rem\"></p-skeleton>\n <p-skeleton height=\"6rem\"></p-skeleton>\n } @else {\n <section class=\"flex flex-col gap-2\">\n
|
|
1682
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-container *transloco=\"let t\">\n <div class=\"mt-scope-picker flex flex-col gap-4\">\n @if (isLoadingOptions()) {\n <p-skeleton height=\"2rem\"></p-skeleton>\n <p-skeleton height=\"6rem\"></p-skeleton>\n <p-skeleton height=\"6rem\"></p-skeleton>\n } @else {\n <section class=\"flex flex-col gap-2\">\n @if (errorOptions(); as errorMessage) {\n <div\n class=\"rounded-md border border-red-200 bg-red-50 px-3 py-2 text-xs text-red-700\"\n >\n {{ errorMessage }}\n </div>\n }\n\n @if (groups().length === 0 && !errorOptions()) {\n <p class=\"text-xs text-gray-500\">\n {{ t(\"delegations.scope.noGrantableOptions\") }}\n </p>\n }\n\n @for (group of groups(); track group.key) {\n <div class=\"overflow-hidden rounded-md border border-gray-200\">\n <button\n type=\"button\"\n class=\"flex w-full cursor-pointer items-center justify-between px-3 py-2 hover:bg-surface-50\"\n (click)=\"toggleAccordion(group)\"\n >\n <div class=\"flex min-w-0 items-center gap-2\">\n <mt-icon\n [icon]=\"\n isExpanded(group)\n ? 'arrow.chevron-down'\n : 'arrow.chevron-right'\n \"\n styleClass=\"text-base text-gray-400\"\n ></mt-icon>\n <span class=\"truncate text-sm text-gray-900\">\n {{ getGroupLabel(group) }}\n </span>\n </div>\n @if (selectedCount(group); as selectedGrantCount) {\n @if (selectedGrantCount > 0) {\n <span\n class=\"rounded-full bg-primary-50 px-2 py-0.5 text-xs font-medium text-primary\"\n >\n {{ selectedGrantCount }}\n </span>\n }\n }\n </button>\n\n @if (isExpanded(group)) {\n <div\n class=\"mt-scope-picker__group flex flex-col gap-3 bg-surface-50/40 px-3 py-3\"\n >\n @for (targetGroup of group.targets; track targetGroup.key) {\n <section\n class=\"rounded-md border border-surface-200 bg-white px-3 py-3\"\n >\n <div\n class=\"flex flex-wrap items-center justify-between gap-2\"\n >\n <div class=\"min-w-0\">\n <div class=\"truncate text-sm font-medium text-gray-900\">\n {{ getTargetLabel(targetGroup.target) }}\n </div>\n </div>\n @if (\n selectedTargetCount(targetGroup);\n as selectedTargetGrantCount\n ) {\n @if (selectedTargetGrantCount > 0) {\n <span\n class=\"rounded-full bg-primary-50 px-2 py-0.5 text-xs font-medium text-primary\"\n >\n {{ selectedTargetGrantCount }}\n </span>\n }\n }\n </div>\n\n <div\n class=\"mt-scope-picker__actions mt-3 flex flex-col gap-1\"\n >\n @for (\n grant of getTargetActions(targetGroup);\n track $index\n ) {\n <label\n class=\"flex cursor-pointer items-center gap-2 rounded-md px-2 py-1.5 hover:bg-surface-100\"\n >\n <input\n type=\"checkbox\"\n [checked]=\"isSelected(grant)\"\n [disabled]=\"readonly()\"\n (change)=\"toggleGrant(grant)\"\n />\n <span class=\"text-sm\">\n {{ getActionLabel(grant) }}\n </span>\n @if (grant.action.isHighRisk) {\n <mt-icon\n icon=\"alert.alert-triangle\"\n styleClass=\"text-sm text-amber-500\"\n [pTooltip]=\"t('delegations.scope.highRisk')\"\n tooltipPosition=\"top\"\n ></mt-icon>\n }\n </label>\n }\n </div>\n </section>\n } @empty {\n <div class=\"text-xs text-gray-500\">\n {{ t(\"delegations.scope.noGrantableOptions\") }}\n </div>\n }\n </div>\n }\n </div>\n }\n </section>\n\n <section\n class=\"flex flex-col gap-1 rounded-md border border-dashed border-gray-300 bg-surface-50 px-3 py-2\"\n >\n <div class=\"flex items-center justify-between gap-2\">\n <div class=\"text-xs font-medium text-gray-700\">\n {{ t(\"delegations.scope.previewTitle\") }}\n </div>\n @if (isPreviewing()) {\n <p-skeleton width=\"6rem\" height=\"0.75rem\"></p-skeleton>\n }\n </div>\n @if (preview(); as p) {\n @if (p.isValid) {\n <div class=\"text-sm text-gray-900\">\n {{\n getPreviewSummary(p.summary) ||\n t(\"delegations.column.scopeSummary\")\n }}\n </div>\n } @else {\n <div class=\"text-sm text-red-700\">\n {{ t(\"delegations.scope.previewInvalid\") }}\n </div>\n }\n @if (p.warnings.length > 0) {\n <ul class=\"list-inside list-disc text-xs text-amber-700\">\n @for (w of p.warnings; track w.message) {\n <li>{{ w.message }}</li>\n }\n </ul>\n }\n @if (p.deniedItems.length > 0) {\n <ul class=\"list-inside list-disc text-xs text-red-700\">\n @for (d of p.deniedItems; track d.operationKey) {\n <li>\n {{ formatDeniedTarget(d.targetKey) }} /\n {{ formatDeniedOperation(d.operationKey) }} -\n {{ d.reasonCode }}\n </li>\n }\n </ul>\n }\n } @else {\n <div class=\"text-xs text-gray-500\">\n {{ t(\"delegations.scope.noSelection\") }}\n </div>\n }\n </section>\n }\n </div>\n</ng-container>\n", styles: [":host{display:block}\n"] }]
|
|
1512
1683
|
}], ctorParameters: () => [], propDecorators: { scope: [{ type: i0.Input, args: [{ isSignal: true, alias: "scope", required: false }] }, { type: i0.Output, args: ["scopeChange"] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], delegatorUserId: [{ type: i0.Input, args: [{ isSignal: true, alias: "delegatorUserId", required: false }] }] } });
|
|
1513
1684
|
|
|
1514
1685
|
const EMPTY_SCOPE = { grants: [], metadata: {} };
|
|
1686
|
+
function getCurrentDelegatorFromLocalStorage() {
|
|
1687
|
+
if (typeof localStorage === 'undefined') {
|
|
1688
|
+
return null;
|
|
1689
|
+
}
|
|
1690
|
+
try {
|
|
1691
|
+
const raw = localStorage.getItem('auth');
|
|
1692
|
+
if (!raw)
|
|
1693
|
+
return null;
|
|
1694
|
+
const parsed = JSON.parse(raw);
|
|
1695
|
+
if (!parsed || typeof parsed !== 'object')
|
|
1696
|
+
return null;
|
|
1697
|
+
const state = (parsed['state'] ?? parsed) ||
|
|
1698
|
+
null;
|
|
1699
|
+
const userWrapper = state?.['user'];
|
|
1700
|
+
const user = userWrapper?.['user'] ??
|
|
1701
|
+
userWrapper;
|
|
1702
|
+
if (!user || typeof user !== 'object')
|
|
1703
|
+
return null;
|
|
1704
|
+
const id = user['id'] ??
|
|
1705
|
+
user['userId'];
|
|
1706
|
+
if (!id)
|
|
1707
|
+
return null;
|
|
1708
|
+
const photo = user['photo'];
|
|
1709
|
+
const photoPath = typeof photo === 'string'
|
|
1710
|
+
? photo
|
|
1711
|
+
: (photo?.fileName ?? null);
|
|
1712
|
+
return {
|
|
1713
|
+
id,
|
|
1714
|
+
userId: id,
|
|
1715
|
+
displayName: user['displayName'] ??
|
|
1716
|
+
user['name'] ??
|
|
1717
|
+
user['userName'] ??
|
|
1718
|
+
'',
|
|
1719
|
+
userName: user['userName'] ?? '',
|
|
1720
|
+
email: user['email'] ?? null,
|
|
1721
|
+
photo: photoPath,
|
|
1722
|
+
isExternal: !!user['isExternal'],
|
|
1723
|
+
};
|
|
1724
|
+
}
|
|
1725
|
+
catch {
|
|
1726
|
+
return null;
|
|
1727
|
+
}
|
|
1728
|
+
}
|
|
1515
1729
|
function extractUserId(value) {
|
|
1516
1730
|
if (typeof value === 'string') {
|
|
1517
1731
|
return value.trim() || undefined;
|
|
@@ -1560,13 +1774,15 @@ class DelegationForm {
|
|
|
1560
1774
|
isSaving = this.facade.isSaving;
|
|
1561
1775
|
context = new HttpContext().set(REQUEST_CONTEXT, { useBaseUrl: false });
|
|
1562
1776
|
scope = signal(EMPTY_SCOPE, ...(ngDevMode ? [{ debugName: "scope" }] : /* istanbul ignore next */ []));
|
|
1563
|
-
|
|
1777
|
+
currentDelegator = signal(getCurrentDelegatorFromLocalStorage(), ...(ngDevMode ? [{ debugName: "currentDelegator" }] : /* istanbul ignore next */ []));
|
|
1778
|
+
/** Selected delegator id drives scope/options loading and the admin payload. */
|
|
1564
1779
|
selectedDelegatorId = computed(() => {
|
|
1565
|
-
if (
|
|
1566
|
-
return
|
|
1567
|
-
|
|
1780
|
+
if (this.adminMode()) {
|
|
1781
|
+
return extractUserId(this.formValue()?.delegateFrom);
|
|
1782
|
+
}
|
|
1783
|
+
return this.currentDelegator()?.id;
|
|
1568
1784
|
}, ...(ngDevMode ? [{ debugName: "selectedDelegatorId" }] : /* istanbul ignore next */ []));
|
|
1569
|
-
/**
|
|
1785
|
+
/** Admin mode waits for a chosen delegator; self-service always has one. */
|
|
1570
1786
|
showScopePicker = computed(() => !this.adminMode() || !!this.selectedDelegatorId(), ...(ngDevMode ? [{ debugName: "showScopePicker" }] : /* istanbul ignore next */ []));
|
|
1571
1787
|
specificDaysOptions = signal([
|
|
1572
1788
|
{ label: this.transloco.translate('delegations.days.sunday'), value: 0 },
|
|
@@ -1585,21 +1801,19 @@ class DelegationForm {
|
|
|
1585
1801
|
bodyClass: 'grid grid-cols-1 gap-4 items-start lg:grid-cols-12',
|
|
1586
1802
|
order: 1,
|
|
1587
1803
|
fields: [
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
]
|
|
1602
|
-
: []),
|
|
1804
|
+
new UserSearchFieldConfig({
|
|
1805
|
+
key: 'delegateFrom',
|
|
1806
|
+
label: this.transloco.translate('delegations.column.delegatorName'),
|
|
1807
|
+
apiUrl: 'Identity/users',
|
|
1808
|
+
context: this.context,
|
|
1809
|
+
validators: [ValidatorConfig.required()],
|
|
1810
|
+
cssClass: this.halfWidth,
|
|
1811
|
+
colSpan: 6,
|
|
1812
|
+
order: 0,
|
|
1813
|
+
disabled: this.readonly() ||
|
|
1814
|
+
!this.adminMode() ||
|
|
1815
|
+
!!this.delegationForEdit(),
|
|
1816
|
+
}),
|
|
1603
1817
|
new UserSearchFieldConfig({
|
|
1604
1818
|
key: 'delegateTo',
|
|
1605
1819
|
label: this.transloco.translate('delegations.column.delegatedTo'),
|
|
@@ -1699,6 +1913,7 @@ class DelegationForm {
|
|
|
1699
1913
|
const d = this.detail();
|
|
1700
1914
|
if (editing && d && d.row.delegationId === editing.delegationId) {
|
|
1701
1915
|
this.delegationFormControl.patchValue({
|
|
1916
|
+
delegateFrom: this.mapPartyToUserValue(d.row.delegator),
|
|
1702
1917
|
delegateTo: d.row.delegatedUser,
|
|
1703
1918
|
description: '',
|
|
1704
1919
|
delegateFromDateTime: d.row.startsAtUtc,
|
|
@@ -1714,12 +1929,26 @@ class DelegationForm {
|
|
|
1714
1929
|
}
|
|
1715
1930
|
else if (!editing) {
|
|
1716
1931
|
this.delegationFormControl.reset({
|
|
1932
|
+
delegateFrom: null,
|
|
1717
1933
|
delegationDaysType: 'FullRange',
|
|
1718
1934
|
requiresApproval: true,
|
|
1719
1935
|
});
|
|
1720
1936
|
this.scope.set(EMPTY_SCOPE);
|
|
1721
1937
|
}
|
|
1722
1938
|
});
|
|
1939
|
+
effect(() => {
|
|
1940
|
+
const currentDelegator = this.currentDelegator();
|
|
1941
|
+
if (this.adminMode() || this.delegationForEdit() || !currentDelegator) {
|
|
1942
|
+
return;
|
|
1943
|
+
}
|
|
1944
|
+
const existingDelegator = extractUserId(this.delegationFormControl.getRawValue()?.delegateFrom);
|
|
1945
|
+
if (existingDelegator === currentDelegator.id) {
|
|
1946
|
+
return;
|
|
1947
|
+
}
|
|
1948
|
+
this.delegationFormControl.patchValue({
|
|
1949
|
+
delegateFrom: currentDelegator,
|
|
1950
|
+
});
|
|
1951
|
+
});
|
|
1723
1952
|
}
|
|
1724
1953
|
ngOnInit() {
|
|
1725
1954
|
const editing = this.delegationForEdit();
|
|
@@ -1773,8 +2002,19 @@ class DelegationForm {
|
|
|
1773
2002
|
});
|
|
1774
2003
|
}
|
|
1775
2004
|
}
|
|
2005
|
+
mapPartyToUserValue(party) {
|
|
2006
|
+
return {
|
|
2007
|
+
id: party.userId,
|
|
2008
|
+
userId: party.userId,
|
|
2009
|
+
displayName: party.displayName,
|
|
2010
|
+
userName: party.email || party.displayName,
|
|
2011
|
+
email: party.email ?? null,
|
|
2012
|
+
photo: null,
|
|
2013
|
+
isExternal: false,
|
|
2014
|
+
};
|
|
2015
|
+
}
|
|
1776
2016
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: DelegationForm, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1777
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: DelegationForm, isStandalone: true, selector: "mt-delegation-form", inputs: { delegationForEdit: { classPropertyName: "delegationForEdit", publicName: "delegationForEdit", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, adminMode: { classPropertyName: "adminMode", publicName: "adminMode", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<ng-container *transloco=\"let t\">\n <div\n [class]=\"\n 'delegation-form-content flex min-w-0 flex-col gap-
|
|
2017
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: DelegationForm, isStandalone: true, selector: "mt-delegation-form", inputs: { delegationForEdit: { classPropertyName: "delegationForEdit", publicName: "delegationForEdit", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, adminMode: { classPropertyName: "adminMode", publicName: "adminMode", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<ng-container *transloco=\"let t\">\n <div\n [class]=\"\n 'delegation-form-content flex min-w-0 flex-col gap-4 overflow-x-hidden p-4 max-[640px]:p-3 ' +\n modal.contentClass\n \"\n >\n <section class=\"delegation-form-section\">\n <mt-dynamic-form\n [formConfig]=\"formConfig()\"\n [formControl]=\"delegationFormControl\"\n />\n </section>\n\n <section class=\"delegation-form-section delegation-form-section--scope\">\n <header class=\"delegation-form-section__header\">\n <h3 class=\"delegation-form-section__title\">\n {{ t(\"delegations.scope.permissionsTitle\") }}\n </h3>\n @if (adminMode() && !showScopePicker()) {\n <p class=\"delegation-form-section__subtitle\">\n {{ t(\"delegations.form.selectDelegatorFirst\") }}\n </p>\n }\n </header>\n\n @if (showScopePicker()) {\n <mt-scope-picker\n [(scope)]=\"scope\"\n [readonly]=\"readonly()\"\n [delegatorUserId]=\"selectedDelegatorId()\"\n ></mt-scope-picker>\n } @else {\n <div class=\"delegation-form-section__empty\">\n {{ t(\"delegations.form.selectDelegatorFirst\") }}\n </div>\n }\n </section>\n </div>\n\n <div\n [class]=\"\n 'delegation-form-footer ' +\n modal.footerClass +\n ' flex-col gap-2 sm:flex-row sm:items-center sm:justify-end'\n \"\n >\n <mt-button\n [label]=\"t('delegations.common.cancel')\"\n variant=\"outlined\"\n (click)=\"ref.close()\"\n styleClass=\"w-full sm:w-auto\"\n />\n @if (!readonly()) {\n <mt-button\n [label]=\"\n delegationForEdit()\n ? t('delegations.common.update')\n : t('delegations.common.create')\n \"\n [loading]=\"isSaving()\"\n [disabled]=\"!delegationFormControl.valid || !canSubmit()\"\n (click)=\"onSubmit()\"\n styleClass=\"w-full sm:w-auto\"\n />\n }\n </div>\n</ng-container>\n", styles: [":host{display:block}.delegation-form-section{display:flex;flex-direction:column;gap:1rem;border:1px solid var(--p-surface-200, #e5e7eb);border-radius:1rem;background:color-mix(in srgb,var(--p-surface-0, #ffffff) 92%,var(--p-primary-50, #eff6ff));padding:1rem}.delegation-form-section--scope{gap:.75rem}.delegation-form-section__header{display:flex;flex-direction:column;gap:.25rem}.delegation-form-section__title{margin:0;font-size:.95rem;font-weight:600;color:var(--p-surface-900, #111827)}.delegation-form-section__subtitle{margin:0;font-size:.8rem;color:var(--p-surface-500, #6b7280)}.delegation-form-section__empty{border:1px dashed var(--p-surface-300, #d1d5db);border-radius:.875rem;background:var(--p-surface-50, #f8fafc);padding:.875rem 1rem;font-size:.85rem;color:var(--p-surface-500, #6b7280)}@media(max-width:640px){.delegation-form-section{border-radius:.875rem;padding:.875rem}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: DynamicForm, selector: "mt-dynamic-form", inputs: ["formConfig", "forcedHiddenFieldKeys", "preserveForcedHiddenValues", "visibleSectionKeys"], outputs: ["runtimeMessagesChange"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "component", type: ScopePicker, selector: "mt-scope-picker", inputs: ["scope", "readonly", "delegatorUserId"], outputs: ["scopeChange"] }, { kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1778
2018
|
}
|
|
1779
2019
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: DelegationForm, decorators: [{
|
|
1780
2020
|
type: Component,
|
|
@@ -1785,7 +2025,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
|
|
|
1785
2025
|
ReactiveFormsModule,
|
|
1786
2026
|
ScopePicker,
|
|
1787
2027
|
TranslocoDirective,
|
|
1788
|
-
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-container *transloco=\"let t\">\n <div\n [class]=\"\n 'delegation-form-content flex min-w-0 flex-col gap-
|
|
2028
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-container *transloco=\"let t\">\n <div\n [class]=\"\n 'delegation-form-content flex min-w-0 flex-col gap-4 overflow-x-hidden p-4 max-[640px]:p-3 ' +\n modal.contentClass\n \"\n >\n <section class=\"delegation-form-section\">\n <mt-dynamic-form\n [formConfig]=\"formConfig()\"\n [formControl]=\"delegationFormControl\"\n />\n </section>\n\n <section class=\"delegation-form-section delegation-form-section--scope\">\n <header class=\"delegation-form-section__header\">\n <h3 class=\"delegation-form-section__title\">\n {{ t(\"delegations.scope.permissionsTitle\") }}\n </h3>\n @if (adminMode() && !showScopePicker()) {\n <p class=\"delegation-form-section__subtitle\">\n {{ t(\"delegations.form.selectDelegatorFirst\") }}\n </p>\n }\n </header>\n\n @if (showScopePicker()) {\n <mt-scope-picker\n [(scope)]=\"scope\"\n [readonly]=\"readonly()\"\n [delegatorUserId]=\"selectedDelegatorId()\"\n ></mt-scope-picker>\n } @else {\n <div class=\"delegation-form-section__empty\">\n {{ t(\"delegations.form.selectDelegatorFirst\") }}\n </div>\n }\n </section>\n </div>\n\n <div\n [class]=\"\n 'delegation-form-footer ' +\n modal.footerClass +\n ' flex-col gap-2 sm:flex-row sm:items-center sm:justify-end'\n \"\n >\n <mt-button\n [label]=\"t('delegations.common.cancel')\"\n variant=\"outlined\"\n (click)=\"ref.close()\"\n styleClass=\"w-full sm:w-auto\"\n />\n @if (!readonly()) {\n <mt-button\n [label]=\"\n delegationForEdit()\n ? t('delegations.common.update')\n : t('delegations.common.create')\n \"\n [loading]=\"isSaving()\"\n [disabled]=\"!delegationFormControl.valid || !canSubmit()\"\n (click)=\"onSubmit()\"\n styleClass=\"w-full sm:w-auto\"\n />\n }\n </div>\n</ng-container>\n", styles: [":host{display:block}.delegation-form-section{display:flex;flex-direction:column;gap:1rem;border:1px solid var(--p-surface-200, #e5e7eb);border-radius:1rem;background:color-mix(in srgb,var(--p-surface-0, #ffffff) 92%,var(--p-primary-50, #eff6ff));padding:1rem}.delegation-form-section--scope{gap:.75rem}.delegation-form-section__header{display:flex;flex-direction:column;gap:.25rem}.delegation-form-section__title{margin:0;font-size:.95rem;font-weight:600;color:var(--p-surface-900, #111827)}.delegation-form-section__subtitle{margin:0;font-size:.8rem;color:var(--p-surface-500, #6b7280)}.delegation-form-section__empty{border:1px dashed var(--p-surface-300, #d1d5db);border-radius:.875rem;background:var(--p-surface-50, #f8fafc);padding:.875rem 1rem;font-size:.85rem;color:var(--p-surface-500, #6b7280)}@media(max-width:640px){.delegation-form-section{border-radius:.875rem;padding:.875rem}}\n"] }]
|
|
1789
2029
|
}], ctorParameters: () => [], propDecorators: { delegationForEdit: [{ type: i0.Input, args: [{ isSignal: true, alias: "delegationForEdit", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], adminMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "adminMode", required: false }] }] } });
|
|
1790
2030
|
|
|
1791
2031
|
/**
|
|
@@ -1858,6 +2098,11 @@ class DelegationsList {
|
|
|
1858
2098
|
* `withComponentInputBinding()` — admin passes `data: { adminMode: true }`.
|
|
1859
2099
|
*/
|
|
1860
2100
|
adminMode = input(false, ...(ngDevMode ? [{ debugName: "adminMode" }] : /* istanbul ignore next */ []));
|
|
2101
|
+
surfaceTitleKey = input('delegations.title', ...(ngDevMode ? [{ debugName: "surfaceTitleKey" }] : /* istanbul ignore next */ []));
|
|
2102
|
+
assignedTabLabelKey = input('delegations.delegatedToMe', ...(ngDevMode ? [{ debugName: "assignedTabLabelKey" }] : /* istanbul ignore next */ []));
|
|
2103
|
+
assignedDecisionTab = input(false, ...(ngDevMode ? [{ debugName: "assignedDecisionTab" }] : /* istanbul ignore next */ []));
|
|
2104
|
+
assignedEmptyStateKey = input('delegations.empty.assigned', ...(ngDevMode ? [{ debugName: "assignedEmptyStateKey" }] : /* istanbul ignore next */ []));
|
|
2105
|
+
assignedStatus = input(null, ...(ngDevMode ? [{ debugName: "assignedStatus" }] : /* istanbul ignore next */ []));
|
|
1861
2106
|
facade = inject(DelegationsFacade);
|
|
1862
2107
|
modal = inject(ModalService);
|
|
1863
2108
|
transloco = inject(TranslocoService);
|
|
@@ -1876,25 +2121,39 @@ class DelegationsList {
|
|
|
1876
2121
|
},
|
|
1877
2122
|
], ...(ngDevMode ? [{ debugName: "breadcrumbItems" }] : /* istanbul ignore next */ []));
|
|
1878
2123
|
activeTab = signal('my', ...(ngDevMode ? [{ debugName: "activeTab" }] : /* istanbul ignore next */ []));
|
|
1879
|
-
|
|
2124
|
+
surfaceTitle = computed(() => this.transloco.translate(this.surfaceTitleKey()), ...(ngDevMode ? [{ debugName: "surfaceTitle" }] : /* istanbul ignore next */ []));
|
|
2125
|
+
tabs = computed(() => [
|
|
1880
2126
|
{
|
|
1881
2127
|
value: 'my',
|
|
1882
2128
|
label: this.transloco.translate('delegations.myDelegations'),
|
|
1883
2129
|
},
|
|
1884
2130
|
{
|
|
1885
2131
|
value: 'assigned',
|
|
1886
|
-
label: this.
|
|
2132
|
+
label: this.assignedDecisionTab()
|
|
2133
|
+
? `${this.transloco.translate('delegations.action.approve')} / ${this.transloco.translate('delegations.action.reject')}`
|
|
2134
|
+
: this.transloco.translate(this.assignedTabLabelKey()),
|
|
1887
2135
|
},
|
|
1888
2136
|
], ...(ngDevMode ? [{ debugName: "tabs" }] : /* istanbul ignore next */ []));
|
|
1889
|
-
isLoading = computed(() => this.
|
|
1890
|
-
? this.facade.
|
|
1891
|
-
: this.
|
|
2137
|
+
isLoading = computed(() => this.adminMode()
|
|
2138
|
+
? this.facade.isLoadingAdmin()
|
|
2139
|
+
: this.activeTab() === 'my'
|
|
2140
|
+
? this.facade.isLoadingMy()
|
|
2141
|
+
: this.facade.isLoadingAssigned(), ...(ngDevMode ? [{ debugName: "isLoading" }] : /* istanbul ignore next */ []));
|
|
1892
2142
|
scopeOptions = this.facade.scopeOptions;
|
|
1893
|
-
rows = computed(() => this.
|
|
1894
|
-
? this.facade.
|
|
1895
|
-
: this.
|
|
2143
|
+
rows = computed(() => this.adminMode()
|
|
2144
|
+
? this.facade.adminItems()
|
|
2145
|
+
: this.activeTab() === 'my'
|
|
2146
|
+
? this.facade.myItems()
|
|
2147
|
+
: this.facade.assignedItems(), ...(ngDevMode ? [{ debugName: "rows" }] : /* istanbul ignore next */ []));
|
|
2148
|
+
emptyStateKey = computed(() => this.adminMode()
|
|
2149
|
+
? 'components.table.no-data-found'
|
|
2150
|
+
: this.activeTab() === 'my'
|
|
2151
|
+
? 'delegations.empty.my'
|
|
2152
|
+
: this.assignedEmptyStateKey(), ...(ngDevMode ? [{ debugName: "emptyStateKey" }] : /* istanbul ignore next */ []));
|
|
1896
2153
|
statusCol = viewChild.required('statusCol');
|
|
1897
2154
|
userCol = viewChild.required('userCol');
|
|
2155
|
+
delegatorCol = viewChild.required('delegatorCol');
|
|
2156
|
+
delegatedUserCol = viewChild.required('delegatedUserCol');
|
|
1898
2157
|
scopeCol = viewChild.required('scopeCol');
|
|
1899
2158
|
daysCol = viewChild.required('daysCol');
|
|
1900
2159
|
canCreate = computed(() => {
|
|
@@ -1964,56 +2223,80 @@ class DelegationsList {
|
|
|
1964
2223
|
hidden: (row) => !this.has(row, 'StartSession'),
|
|
1965
2224
|
},
|
|
1966
2225
|
], ...(ngDevMode ? [{ debugName: "rowActions" }] : /* istanbul ignore next */ []));
|
|
1967
|
-
tableColumns = linkedSignal(() =>
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
: '
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
{
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2226
|
+
tableColumns = linkedSignal(() => {
|
|
2227
|
+
const baseColumns = [
|
|
2228
|
+
{
|
|
2229
|
+
key: 'effectiveStatus',
|
|
2230
|
+
label: this.transloco.translate('delegations.column.status'),
|
|
2231
|
+
type: 'custom',
|
|
2232
|
+
customCellTpl: this.statusCol(),
|
|
2233
|
+
width: '10rem',
|
|
2234
|
+
},
|
|
2235
|
+
{
|
|
2236
|
+
key: 'scopeSummary',
|
|
2237
|
+
label: this.transloco.translate('delegations.column.scopeSummary'),
|
|
2238
|
+
type: 'custom',
|
|
2239
|
+
customCellTpl: this.scopeCol(),
|
|
2240
|
+
width: '18rem',
|
|
2241
|
+
},
|
|
2242
|
+
{
|
|
2243
|
+
key: 'startsAtUtc',
|
|
2244
|
+
label: this.transloco.translate('delegations.column.startDate'),
|
|
2245
|
+
type: 'dateTime',
|
|
2246
|
+
width: '11rem',
|
|
2247
|
+
},
|
|
2248
|
+
{
|
|
2249
|
+
key: 'endsAtUtc',
|
|
2250
|
+
label: this.transloco.translate('delegations.column.endDate'),
|
|
2251
|
+
type: 'dateTime',
|
|
2252
|
+
width: '11rem',
|
|
2253
|
+
},
|
|
2254
|
+
{
|
|
2255
|
+
key: 'createdAtUtc',
|
|
2256
|
+
label: this.transloco.translate('delegations.column.createdDate'),
|
|
2257
|
+
type: 'dateTime',
|
|
2258
|
+
width: '11rem',
|
|
2259
|
+
},
|
|
2260
|
+
{
|
|
2261
|
+
key: 'dayRuleMode',
|
|
2262
|
+
label: this.transloco.translate('delegations.column.days'),
|
|
2263
|
+
type: 'custom',
|
|
2264
|
+
customCellTpl: this.daysCol(),
|
|
2265
|
+
width: '12rem',
|
|
2266
|
+
},
|
|
2267
|
+
];
|
|
2268
|
+
if (this.adminMode()) {
|
|
2269
|
+
return [
|
|
2270
|
+
{
|
|
2271
|
+
key: 'delegator',
|
|
2272
|
+
label: this.transloco.translate('delegations.column.delegatorName'),
|
|
2273
|
+
type: 'custom',
|
|
2274
|
+
customCellTpl: this.delegatorCol(),
|
|
2275
|
+
width: '16rem',
|
|
2276
|
+
},
|
|
2277
|
+
{
|
|
2278
|
+
key: 'delegatedUser',
|
|
2279
|
+
label: this.transloco.translate('delegations.column.delegatedTo'),
|
|
2280
|
+
type: 'custom',
|
|
2281
|
+
customCellTpl: this.delegatedUserCol(),
|
|
2282
|
+
width: '16rem',
|
|
2283
|
+
},
|
|
2284
|
+
...baseColumns,
|
|
2285
|
+
];
|
|
2286
|
+
}
|
|
2287
|
+
return [
|
|
2288
|
+
{
|
|
2289
|
+
key: this.activeTab() === 'my' ? 'delegatedUser' : 'delegator',
|
|
2290
|
+
label: this.transloco.translate(this.activeTab() === 'my'
|
|
2291
|
+
? 'delegations.column.delegatedTo'
|
|
2292
|
+
: 'delegations.column.delegatorName'),
|
|
2293
|
+
type: 'custom',
|
|
2294
|
+
customCellTpl: this.userCol(),
|
|
2295
|
+
width: '16rem',
|
|
2296
|
+
},
|
|
2297
|
+
...baseColumns,
|
|
2298
|
+
];
|
|
2299
|
+
}, ...(ngDevMode ? [{ debugName: "tableColumns" }] : /* istanbul ignore next */ []));
|
|
2017
2300
|
busyIds = signal([], ...(ngDevMode ? [{ debugName: "busyIds" }] : /* istanbul ignore next */ []));
|
|
2018
2301
|
weekdayKeys = [
|
|
2019
2302
|
'sunday',
|
|
@@ -2035,11 +2318,18 @@ class DelegationsList {
|
|
|
2035
2318
|
this.loadCurrentTab();
|
|
2036
2319
|
}
|
|
2037
2320
|
loadCurrentTab(query = { page: 1, pageSize: 25 }) {
|
|
2321
|
+
if (this.adminMode()) {
|
|
2322
|
+
this.facade.getAdmin(query);
|
|
2323
|
+
return;
|
|
2324
|
+
}
|
|
2038
2325
|
if (this.activeTab() === 'my') {
|
|
2039
2326
|
this.facade.getMy(query);
|
|
2040
2327
|
}
|
|
2041
2328
|
else {
|
|
2042
|
-
this.facade.getAssigned(
|
|
2329
|
+
this.facade.getAssigned({
|
|
2330
|
+
...query,
|
|
2331
|
+
status: query.status ?? this.assignedStatus() ?? undefined,
|
|
2332
|
+
});
|
|
2043
2333
|
}
|
|
2044
2334
|
}
|
|
2045
2335
|
reloadCurrentTab() {
|
|
@@ -2060,6 +2350,9 @@ class DelegationsList {
|
|
|
2060
2350
|
formatScopeSummary(value) {
|
|
2061
2351
|
return formatDelegationScopeSummary(value, this.transloco.getActiveLang());
|
|
2062
2352
|
}
|
|
2353
|
+
getListedParty(row) {
|
|
2354
|
+
return this.activeTab() === 'my' ? row.delegatedUser : row.delegator;
|
|
2355
|
+
}
|
|
2063
2356
|
viewDetails(row) {
|
|
2064
2357
|
this.modal.openModal(DelegationDetailDrawer, 'drawer', {
|
|
2065
2358
|
header: this.transloco.translate('delegations.action.view'),
|
|
@@ -2115,7 +2408,7 @@ class DelegationsList {
|
|
|
2115
2408
|
cancel(row) {
|
|
2116
2409
|
this.mark(`cancel:${row.delegationId}`, true);
|
|
2117
2410
|
this.facade
|
|
2118
|
-
.cancel(row.delegationId, row.rowVersion)
|
|
2411
|
+
.cancel(row.delegationId, row.rowVersion, this.adminMode())
|
|
2119
2412
|
.pipe(finalize(() => this.mark(`cancel:${row.delegationId}`, false)))
|
|
2120
2413
|
.subscribe({ next: () => this.reloadCurrentTab() });
|
|
2121
2414
|
}
|
|
@@ -2136,7 +2429,7 @@ class DelegationsList {
|
|
|
2136
2429
|
this.busyIds.update((ids) => on ? [...ids, key] : ids.filter((id) => id !== key));
|
|
2137
2430
|
}
|
|
2138
2431
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: DelegationsList, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2139
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: DelegationsList, isStandalone: true, selector: "mt-delegations-list", inputs: { showBreadcrumb: { classPropertyName: "showBreadcrumb", publicName: "showBreadcrumb", isSignal: true, isRequired: false, transformFunction: null }, adminMode: { classPropertyName: "adminMode", publicName: "adminMode", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "statusCol", first: true, predicate: ["statusCol"], descendants: true, isSignal: true }, { propertyName: "userCol", first: true, predicate: ["userCol"], descendants: true, isSignal: true }, { propertyName: "scopeCol", first: true, predicate: ["scopeCol"], descendants: true, isSignal: true }, { propertyName: "daysCol", first: true, predicate: ["daysCol"], descendants: true, isSignal: true }], ngImport: i0, template: "<ng-container *transloco=\"let t\">\n <div class=\"flex flex-col
|
|
2432
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: DelegationsList, isStandalone: true, selector: "mt-delegations-list", inputs: { showBreadcrumb: { classPropertyName: "showBreadcrumb", publicName: "showBreadcrumb", isSignal: true, isRequired: false, transformFunction: null }, adminMode: { classPropertyName: "adminMode", publicName: "adminMode", isSignal: true, isRequired: false, transformFunction: null }, surfaceTitleKey: { classPropertyName: "surfaceTitleKey", publicName: "surfaceTitleKey", isSignal: true, isRequired: false, transformFunction: null }, assignedTabLabelKey: { classPropertyName: "assignedTabLabelKey", publicName: "assignedTabLabelKey", isSignal: true, isRequired: false, transformFunction: null }, assignedDecisionTab: { classPropertyName: "assignedDecisionTab", publicName: "assignedDecisionTab", isSignal: true, isRequired: false, transformFunction: null }, assignedEmptyStateKey: { classPropertyName: "assignedEmptyStateKey", publicName: "assignedEmptyStateKey", isSignal: true, isRequired: false, transformFunction: null }, assignedStatus: { classPropertyName: "assignedStatus", publicName: "assignedStatus", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "statusCol", first: true, predicate: ["statusCol"], descendants: true, isSignal: true }, { propertyName: "userCol", first: true, predicate: ["userCol"], descendants: true, isSignal: true }, { propertyName: "delegatorCol", first: true, predicate: ["delegatorCol"], descendants: true, isSignal: true }, { propertyName: "delegatedUserCol", first: true, predicate: ["delegatedUserCol"], descendants: true, isSignal: true }, { propertyName: "scopeCol", first: true, predicate: ["scopeCol"], descendants: true, isSignal: true }, { propertyName: "daysCol", first: true, predicate: ["daysCol"], descendants: true, isSignal: true }], ngImport: i0, template: "<ng-container *transloco=\"let t\">\n <div class=\"flex h-full flex-col gap-4 p-4\">\n @if (showBreadcrumb()) {\n <mt-breadcrumb [items]=\"breadcrumbItems()\"></mt-breadcrumb>\n }\n\n <mt-card [title]=\"surfaceTitle()\">\n <div class=\"flex flex-col gap-4\">\n @if (!adminMode()) {\n <div class=\"flex items-center gap-2 border-b border-gray-200\">\n @for (tab of tabs(); track tab.value) {\n <button\n type=\"button\"\n class=\"px-4 py-2 -mb-px border-b-2 text-sm font-medium transition-colors\"\n [class.border-primary]=\"activeTab() === tab.value\"\n [class.text-primary]=\"activeTab() === tab.value\"\n [class.border-transparent]=\"activeTab() !== tab.value\"\n [class.text-gray-500]=\"activeTab() !== tab.value\"\n (click)=\"switchTab(tab.value)\"\n >\n {{ tab.label }}\n </button>\n }\n </div>\n }\n\n <mt-table\n [data]=\"rows()\"\n [columns]=\"tableColumns()\"\n [actions]=\"tableActions()\"\n [rowActions]=\"rowActions()\"\n [freezeActions]=\"true\"\n [loading]=\"isLoading()\"\n [noCard]=\"true\"\n >\n <ng-template #statusCol let-row>\n <mt-delegation-status-chip\n [status]=\"row.effectiveStatus\"\n ></mt-delegation-status-chip>\n </ng-template>\n\n <ng-template #userCol let-row>\n @let party = getListedParty(row);\n <div class=\"flex items-center gap-2\">\n <mt-avatar icon=\"user.user-01\" styleClass=\"w-8! h-8!\"></mt-avatar>\n <div class=\"flex min-w-0 flex-col\">\n <span class=\"truncate text-sm text-gray-900\">\n {{ party?.displayName }}\n </span>\n @if (party?.email) {\n <span class=\"truncate text-xs text-gray-500\">\n {{ party.email }}\n </span>\n }\n </div>\n </div>\n </ng-template>\n\n <ng-template #delegatorCol let-row>\n <div class=\"flex items-center gap-2\">\n <mt-avatar icon=\"user.user-01\" styleClass=\"w-8! h-8!\"></mt-avatar>\n <div class=\"flex min-w-0 flex-col\">\n <span class=\"truncate text-sm text-gray-900\">\n {{ row.delegator?.displayName }}\n </span>\n @if (row.delegator?.email) {\n <span class=\"truncate text-xs text-gray-500\">\n {{ row.delegator.email }}\n </span>\n }\n </div>\n </div>\n </ng-template>\n\n <ng-template #delegatedUserCol let-row>\n <div class=\"flex items-center gap-2\">\n <mt-avatar icon=\"user.user-01\" styleClass=\"w-8! h-8!\"></mt-avatar>\n <div class=\"flex min-w-0 flex-col\">\n <span class=\"truncate text-sm text-gray-900\">\n {{ row.delegatedUser?.displayName }}\n </span>\n @if (row.delegatedUser?.email) {\n <span class=\"truncate text-xs text-gray-500\">\n {{ row.delegatedUser.email }}\n </span>\n }\n </div>\n </div>\n </ng-template>\n\n <ng-template #scopeCol let-row>\n @let summary = formatScopeSummary(row.scopeSummary);\n <span class=\"text-sm text-gray-700\" [title]=\"summary\">\n {{ summary || \"--\" }}\n </span>\n </ng-template>\n\n <ng-template #daysCol let-row>\n <span class=\"text-sm text-gray-700\">{{ formatDays(row) }}</span>\n </ng-template>\n\n <ng-template #empty>\n <p class=\"text-sm text-surface-500\">\n {{ t(emptyStateKey()) }}\n </p>\n </ng-template>\n </mt-table>\n </div>\n </mt-card>\n </div>\n</ng-container>\n", styles: [":host{display:block;min-width:0}:host ::ng-deep mt-table .p-datatable-table{min-width:72rem}:host ::ng-deep mt-table .p-datatable-header>div,:host ::ng-deep mt-table .p-datatable-header>div>div{gap:.75rem;min-width:0;flex-wrap:wrap}:host ::ng-deep mt-table .p-datatable-header mt-tabs{display:block;max-width:100%;overflow-x:auto;padding-bottom:.25rem}:host ::ng-deep mt-table .p-datatable-header mt-text-field{display:block;flex:1 1 16rem;min-width:min(100%,16rem)}:host ::ng-deep mt-table .p-datatable-header .p-inputtext,:host ::ng-deep mt-table .p-datatable-header .p-inputwrapper,:host ::ng-deep mt-table .p-datatable-header .p-selectbutton{width:100%;max-width:100%}@media(max-width:1024px){:host ::ng-deep mt-table .p-datatable-header>div{flex-direction:column;align-items:stretch}:host ::ng-deep mt-table .p-datatable-header>div>div{width:100%;justify-content:flex-start}:host ::ng-deep mt-table .p-datatable-header mt-text-field{flex-basis:100%;min-width:0}}@media(max-width:768px){:host ::ng-deep mt-table .p-datatable-table{min-width:64rem}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: Avatar, selector: "mt-avatar", inputs: ["label", "icon", "image", "styleClass", "size", "shape", "badge", "badgeSize", "badgeSeverity"], outputs: ["onImageError"] }, { kind: "component", type: Breadcrumb, selector: "mt-breadcrumb", inputs: ["items", "styleClass"], outputs: ["onItemClick"] }, { 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", "lazyLocalSearch", "showFilters", "filterMode", "loading", "updating", "lazy", "lazyLocalSort", "lazyTotalRecords", "reorderableColumns", "reorderableRows", "dataKey", "storageKey", "storageMode", "exportable", "printable", "groupable", "cellClickFilter", "freezeActions", "printTitle", "exportFilename", "actionShape", "rowActionsLoadingFn", "tableLayout", "noCard", "tabs", "tabsOptionLabel", "tabsOptionValue", "activeTab", "actions", "paginatorPosition", "alwaysShowPaginator", "rowsPerPageOptions", "pageSize", "currentPage", "first", "filterTerm", "groupBy"], outputs: ["selectionChange", "cellChange", "lazyLoad", "columnReorder", "rowReorder", "rowClick", "rowActionsRequested", "filtersChange", "activeTabChange", "onTabChange", "pageSizeChange", "currentPageChange", "firstChange", "filterTermChange", "groupByChange"] }, { kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "component", type: DelegationStatusChip, selector: "mt-delegation-status-chip", inputs: ["status"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
2140
2433
|
}
|
|
2141
2434
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: DelegationsList, decorators: [{
|
|
2142
2435
|
type: Component,
|
|
@@ -2144,11 +2437,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
|
|
|
2144
2437
|
CommonModule,
|
|
2145
2438
|
Avatar,
|
|
2146
2439
|
Breadcrumb,
|
|
2440
|
+
Card,
|
|
2147
2441
|
Table,
|
|
2148
2442
|
TranslocoDirective,
|
|
2149
2443
|
DelegationStatusChip,
|
|
2150
|
-
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-container *transloco=\"let t\">\n <div class=\"flex flex-col
|
|
2151
|
-
}], propDecorators: { showBreadcrumb: [{ type: i0.Input, args: [{ isSignal: true, alias: "showBreadcrumb", required: false }] }], adminMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "adminMode", required: false }] }], statusCol: [{ type: i0.ViewChild, args: ['statusCol', { isSignal: true }] }], userCol: [{ type: i0.ViewChild, args: ['userCol', { isSignal: true }] }], scopeCol: [{ type: i0.ViewChild, args: ['scopeCol', { isSignal: true }] }], daysCol: [{ type: i0.ViewChild, args: ['daysCol', { isSignal: true }] }] } });
|
|
2444
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-container *transloco=\"let t\">\n <div class=\"flex h-full flex-col gap-4 p-4\">\n @if (showBreadcrumb()) {\n <mt-breadcrumb [items]=\"breadcrumbItems()\"></mt-breadcrumb>\n }\n\n <mt-card [title]=\"surfaceTitle()\">\n <div class=\"flex flex-col gap-4\">\n @if (!adminMode()) {\n <div class=\"flex items-center gap-2 border-b border-gray-200\">\n @for (tab of tabs(); track tab.value) {\n <button\n type=\"button\"\n class=\"px-4 py-2 -mb-px border-b-2 text-sm font-medium transition-colors\"\n [class.border-primary]=\"activeTab() === tab.value\"\n [class.text-primary]=\"activeTab() === tab.value\"\n [class.border-transparent]=\"activeTab() !== tab.value\"\n [class.text-gray-500]=\"activeTab() !== tab.value\"\n (click)=\"switchTab(tab.value)\"\n >\n {{ tab.label }}\n </button>\n }\n </div>\n }\n\n <mt-table\n [data]=\"rows()\"\n [columns]=\"tableColumns()\"\n [actions]=\"tableActions()\"\n [rowActions]=\"rowActions()\"\n [freezeActions]=\"true\"\n [loading]=\"isLoading()\"\n [noCard]=\"true\"\n >\n <ng-template #statusCol let-row>\n <mt-delegation-status-chip\n [status]=\"row.effectiveStatus\"\n ></mt-delegation-status-chip>\n </ng-template>\n\n <ng-template #userCol let-row>\n @let party = getListedParty(row);\n <div class=\"flex items-center gap-2\">\n <mt-avatar icon=\"user.user-01\" styleClass=\"w-8! h-8!\"></mt-avatar>\n <div class=\"flex min-w-0 flex-col\">\n <span class=\"truncate text-sm text-gray-900\">\n {{ party?.displayName }}\n </span>\n @if (party?.email) {\n <span class=\"truncate text-xs text-gray-500\">\n {{ party.email }}\n </span>\n }\n </div>\n </div>\n </ng-template>\n\n <ng-template #delegatorCol let-row>\n <div class=\"flex items-center gap-2\">\n <mt-avatar icon=\"user.user-01\" styleClass=\"w-8! h-8!\"></mt-avatar>\n <div class=\"flex min-w-0 flex-col\">\n <span class=\"truncate text-sm text-gray-900\">\n {{ row.delegator?.displayName }}\n </span>\n @if (row.delegator?.email) {\n <span class=\"truncate text-xs text-gray-500\">\n {{ row.delegator.email }}\n </span>\n }\n </div>\n </div>\n </ng-template>\n\n <ng-template #delegatedUserCol let-row>\n <div class=\"flex items-center gap-2\">\n <mt-avatar icon=\"user.user-01\" styleClass=\"w-8! h-8!\"></mt-avatar>\n <div class=\"flex min-w-0 flex-col\">\n <span class=\"truncate text-sm text-gray-900\">\n {{ row.delegatedUser?.displayName }}\n </span>\n @if (row.delegatedUser?.email) {\n <span class=\"truncate text-xs text-gray-500\">\n {{ row.delegatedUser.email }}\n </span>\n }\n </div>\n </div>\n </ng-template>\n\n <ng-template #scopeCol let-row>\n @let summary = formatScopeSummary(row.scopeSummary);\n <span class=\"text-sm text-gray-700\" [title]=\"summary\">\n {{ summary || \"--\" }}\n </span>\n </ng-template>\n\n <ng-template #daysCol let-row>\n <span class=\"text-sm text-gray-700\">{{ formatDays(row) }}</span>\n </ng-template>\n\n <ng-template #empty>\n <p class=\"text-sm text-surface-500\">\n {{ t(emptyStateKey()) }}\n </p>\n </ng-template>\n </mt-table>\n </div>\n </mt-card>\n </div>\n</ng-container>\n", styles: [":host{display:block;min-width:0}:host ::ng-deep mt-table .p-datatable-table{min-width:72rem}:host ::ng-deep mt-table .p-datatable-header>div,:host ::ng-deep mt-table .p-datatable-header>div>div{gap:.75rem;min-width:0;flex-wrap:wrap}:host ::ng-deep mt-table .p-datatable-header mt-tabs{display:block;max-width:100%;overflow-x:auto;padding-bottom:.25rem}:host ::ng-deep mt-table .p-datatable-header mt-text-field{display:block;flex:1 1 16rem;min-width:min(100%,16rem)}:host ::ng-deep mt-table .p-datatable-header .p-inputtext,:host ::ng-deep mt-table .p-datatable-header .p-inputwrapper,:host ::ng-deep mt-table .p-datatable-header .p-selectbutton{width:100%;max-width:100%}@media(max-width:1024px){:host ::ng-deep mt-table .p-datatable-header>div{flex-direction:column;align-items:stretch}:host ::ng-deep mt-table .p-datatable-header>div>div{width:100%;justify-content:flex-start}:host ::ng-deep mt-table .p-datatable-header mt-text-field{flex-basis:100%;min-width:0}}@media(max-width:768px){:host ::ng-deep mt-table .p-datatable-table{min-width:64rem}}\n"] }]
|
|
2445
|
+
}], propDecorators: { showBreadcrumb: [{ type: i0.Input, args: [{ isSignal: true, alias: "showBreadcrumb", required: false }] }], adminMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "adminMode", required: false }] }], surfaceTitleKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "surfaceTitleKey", required: false }] }], assignedTabLabelKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "assignedTabLabelKey", required: false }] }], assignedDecisionTab: [{ type: i0.Input, args: [{ isSignal: true, alias: "assignedDecisionTab", required: false }] }], assignedEmptyStateKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "assignedEmptyStateKey", required: false }] }], assignedStatus: [{ type: i0.Input, args: [{ isSignal: true, alias: "assignedStatus", required: false }] }], statusCol: [{ type: i0.ViewChild, args: ['statusCol', { isSignal: true }] }], userCol: [{ type: i0.ViewChild, args: ['userCol', { isSignal: true }] }], delegatorCol: [{ type: i0.ViewChild, args: ['delegatorCol', { isSignal: true }] }], delegatedUserCol: [{ type: i0.ViewChild, args: ['delegatedUserCol', { isSignal: true }] }], scopeCol: [{ type: i0.ViewChild, args: ['scopeCol', { isSignal: true }] }], daysCol: [{ type: i0.ViewChild, args: ['daysCol', { isSignal: true }] }] } });
|
|
2152
2446
|
|
|
2153
2447
|
/**
|
|
2154
2448
|
* Endpoints that must NEVER carry the `app-delegation` header.
|
|
@@ -2181,5 +2475,5 @@ const appDelegationInterceptor = (req, next) => {
|
|
|
2181
2475
|
* Generated bundle index. Do not edit.
|
|
2182
2476
|
*/
|
|
2183
2477
|
|
|
2184
|
-
export { ApproveDelegation, CancelDelegation, ClearDelegationDetail, ClearScopePreview, CreateDelegationLegacy, CreateDelegationV2, DelegationDetailDrawer, DelegationForm, DelegationSessionActionKey, DelegationSessionFacade, DelegationSessionState, DelegationStatusChip, Delegations, DelegationsActionKey, DelegationsFacade, DelegationsList, DelegationsState, EndDelegationSession, GetActiveAssignedDelegations, GetAssignedDelegations, GetDelegationDetail, GetMyDelegations, GetScopeOptions, LoadDelegationCandidates, PreviewScope, RejectDelegation, RejectDelegationDialog, ScopePicker, StartDelegationSession, StartSessionDialog, SwitchDelegationSession, TopbarDelegationMenu, UpdateDelegationLegacy, UpdateDelegationV2, appDelegationInterceptor };
|
|
2478
|
+
export { ApproveDelegation, CancelDelegation, ClearDelegationDetail, ClearScopePreview, CreateDelegationLegacy, CreateDelegationV2, DelegationDetailDrawer, DelegationForm, DelegationSessionActionKey, DelegationSessionFacade, DelegationSessionState, DelegationStatusChip, Delegations, DelegationsActionKey, DelegationsFacade, DelegationsList, DelegationsState, EndDelegationSession, GetActiveAssignedDelegations, GetAdminDelegations, GetAssignedDelegations, GetDelegationDetail, GetMyDelegations, GetScopeOptions, LoadDelegationCandidates, PreviewScope, RejectDelegation, RejectDelegationDialog, ScopePicker, StartDelegationSession, StartSessionDialog, SwitchDelegationSession, TopbarDelegationMenu, UpdateDelegationLegacy, UpdateDelegationV2, appDelegationInterceptor };
|
|
2185
2479
|
//# sourceMappingURL=masterteam-delegations.mjs.map
|