@masterteam/delegations 0.0.14 → 0.0.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as i2$1 from '@angular/common';
|
|
2
2
|
import { CommonModule } from '@angular/common';
|
|
3
3
|
import * as i0 from '@angular/core';
|
|
4
|
-
import { input, computed, Component, inject, Injectable, ChangeDetectionStrategy, viewChild,
|
|
4
|
+
import { input, computed, Component, inject, Injectable, ChangeDetectionStrategy, viewChild, signal, model, effect, untracked, linkedSignal } from '@angular/core';
|
|
5
5
|
import { RouterLink, Router, RouterOutlet } from '@angular/router';
|
|
6
6
|
import { TranslocoDirective, TranslocoService } from '@jsverse/transloco';
|
|
7
7
|
import { Avatar } from '@masterteam/components/avatar';
|
|
@@ -395,19 +395,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
|
|
|
395
395
|
], 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 items-center gap-2 px-2 py-1 rounded-full cursor-pointer hover:bg-surface-100\"\n [attr.aria-label]=\"t('delegations.title')\"\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 <div\n class=\"text-xs text-gray-500 mt-1 truncate\"\n [title]=\"session.delegation.scopeSummary\"\n >\n {{ t(\"delegations.column.scopeSummary\") }}:\n {{ session.delegation.scopeSummary }}\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 <span\n class=\"text-xs text-gray-500 truncate\"\n [title]=\"cand.scopeSummary\"\n >\n {{ cand.scopeSummary }}\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}.mt-delegation-trigger__button{transition:background-color .2s ease-out}.mt-delegation-trigger__label{max-width:16rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}:host-context(.dark) .mt-delegation-trigger__button:hover{background-color:#94a3b826}\n"] }]
|
|
396
396
|
}], 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 }] }] } });
|
|
397
397
|
|
|
398
|
-
class Delegations {
|
|
399
|
-
router = inject(Router);
|
|
400
|
-
goBack() {
|
|
401
|
-
this.router.navigate(['control-panel/product-settings']);
|
|
402
|
-
}
|
|
403
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: Delegations, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
404
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.8", type: Delegations, isStandalone: true, selector: "mt-delegations", ngImport: i0, template: "<ng-container *transloco=\"let t\">\n <mt-page\n [title]=\"t('delegations.title')\"\n [avatarIcon]=\"'custom.hierarchy-structure'\"\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 (backButtonClick)=\"goBack()\"\n backButton\n >\n <router-outlet />\n </mt-page>\n</ng-container>\n", styles: [""], dependencies: [{ kind: "component", type: Page, selector: "mt-page", inputs: ["backButton", "backButtonIcon", "avatarIcon", "avatarStyle", "avatarShape", "title", "tabs", "activeTab", "contentClass", "contentId"], outputs: ["backButtonClick", "tabChange"] }, { kind: "directive", type: RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }, { kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }] });
|
|
405
|
-
}
|
|
406
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: Delegations, decorators: [{
|
|
407
|
-
type: Component,
|
|
408
|
-
args: [{ selector: 'mt-delegations', imports: [Page, RouterOutlet, TranslocoDirective], template: "<ng-container *transloco=\"let t\">\n <mt-page\n [title]=\"t('delegations.title')\"\n [avatarIcon]=\"'custom.hierarchy-structure'\"\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 (backButtonClick)=\"goBack()\"\n backButton\n >\n <router-outlet />\n </mt-page>\n</ng-container>\n" }]
|
|
409
|
-
}] });
|
|
410
|
-
|
|
411
398
|
// ---------------------------------------------------------------------------
|
|
412
399
|
// Lists / detail
|
|
413
400
|
// ---------------------------------------------------------------------------
|
|
@@ -940,12 +927,102 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
|
|
|
940
927
|
args: [{ providedIn: 'root' }]
|
|
941
928
|
}] });
|
|
942
929
|
|
|
930
|
+
/**
|
|
931
|
+
* Collects a required rejection reason before calling
|
|
932
|
+
* POST identity/delegations/{id}/reject (doc 04 — reject requires a non-empty
|
|
933
|
+
* reason for audit/governance). Closes with `true` on success so the caller
|
|
934
|
+
* can reload the row.
|
|
935
|
+
*/
|
|
936
|
+
class RejectDelegationDialog {
|
|
937
|
+
delegation = input.required(...(ngDevMode ? [{ debugName: "delegation" }] : /* istanbul ignore next */ []));
|
|
938
|
+
ref = inject(ModalRef);
|
|
939
|
+
facade = inject(DelegationsFacade);
|
|
940
|
+
reason = signal('', ...(ngDevMode ? [{ debugName: "reason" }] : /* istanbul ignore next */ []));
|
|
941
|
+
submitted = signal(false, ...(ngDevMode ? [{ debugName: "submitted" }] : /* istanbul ignore next */ []));
|
|
942
|
+
isBusy = this.facade.isRejecting;
|
|
943
|
+
isValid = computed(() => this.reason().trim().length > 0, ...(ngDevMode ? [{ debugName: "isValid" }] : /* istanbul ignore next */ []));
|
|
944
|
+
onReasonInput(event) {
|
|
945
|
+
this.reason.set(event.target.value);
|
|
946
|
+
}
|
|
947
|
+
confirm() {
|
|
948
|
+
this.submitted.set(true);
|
|
949
|
+
if (!this.isValid())
|
|
950
|
+
return;
|
|
951
|
+
const row = this.delegation();
|
|
952
|
+
this.facade
|
|
953
|
+
.reject(row.delegationId, {
|
|
954
|
+
rowVersion: row.rowVersion,
|
|
955
|
+
reason: this.reason().trim(),
|
|
956
|
+
})
|
|
957
|
+
.subscribe({
|
|
958
|
+
next: () => this.ref.close(true),
|
|
959
|
+
error: () => {
|
|
960
|
+
/* error surfaced via shared toast pipeline; keep dialog open */
|
|
961
|
+
},
|
|
962
|
+
});
|
|
963
|
+
}
|
|
964
|
+
cancel() {
|
|
965
|
+
this.ref.close(false);
|
|
966
|
+
}
|
|
967
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: RejectDelegationDialog, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
968
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: RejectDelegationDialog, isStandalone: true, selector: "mt-reject-delegation-dialog", inputs: { delegation: { classPropertyName: "delegation", publicName: "delegation", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<ng-container *transloco=\"let t\">\n <div class=\"flex flex-col gap-4 p-2\">\n <div class=\"flex flex-col gap-1\">\n <div class=\"text-sm text-gray-700\">\n {{ t(\"delegations.confirm.reject\") }}\n </div>\n <div class=\"text-xs text-gray-500\">\n {{ delegation().delegator.displayName }}\n </div>\n </div>\n\n <div class=\"flex flex-col gap-1\">\n <label class=\"text-sm font-medium text-gray-800\" for=\"mt-reject-reason\">\n {{ t(\"delegations.form.rejectionReason\") }}\n <span class=\"text-red-600\">*</span>\n </label>\n <textarea\n id=\"mt-reject-reason\"\n rows=\"3\"\n class=\"w-full rounded-md border border-gray-300 px-3 py-2 text-sm focus:border-primary focus:outline-none\"\n [class.border-red-400]=\"submitted() && !isValid()\"\n [value]=\"reason()\"\n (input)=\"onReasonInput($event)\"\n [attr.aria-invalid]=\"submitted() && !isValid()\"\n [placeholder]=\"t('delegations.form.rejectionReason')\"\n ></textarea>\n @if (submitted() && !isValid()) {\n <span class=\"text-xs text-red-600\">\n {{ t(\"delegations.form.rejectReasonRequired\") }}\n </span>\n }\n </div>\n\n <div class=\"flex justify-end gap-2 pt-2 border-t border-gray-200\">\n <mt-button\n variant=\"outlined\"\n color=\"secondary\"\n [label]=\"t('delegations.common.cancel')\"\n [disabled]=\"isBusy()\"\n (click)=\"cancel()\"\n ></mt-button>\n <mt-button\n color=\"danger\"\n icon=\"general.x-close\"\n [label]=\"t('delegations.action.reject')\"\n [loading]=\"isBusy()\"\n [disabled]=\"!isValid()\"\n (click)=\"confirm()\"\n ></mt-button>\n </div>\n </div>\n</ng-container>\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: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
969
|
+
}
|
|
970
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: RejectDelegationDialog, decorators: [{
|
|
971
|
+
type: Component,
|
|
972
|
+
args: [{ selector: 'mt-reject-delegation-dialog', standalone: true, imports: [CommonModule, Button, TranslocoDirective], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-container *transloco=\"let t\">\n <div class=\"flex flex-col gap-4 p-2\">\n <div class=\"flex flex-col gap-1\">\n <div class=\"text-sm text-gray-700\">\n {{ t(\"delegations.confirm.reject\") }}\n </div>\n <div class=\"text-xs text-gray-500\">\n {{ delegation().delegator.displayName }}\n </div>\n </div>\n\n <div class=\"flex flex-col gap-1\">\n <label class=\"text-sm font-medium text-gray-800\" for=\"mt-reject-reason\">\n {{ t(\"delegations.form.rejectionReason\") }}\n <span class=\"text-red-600\">*</span>\n </label>\n <textarea\n id=\"mt-reject-reason\"\n rows=\"3\"\n class=\"w-full rounded-md border border-gray-300 px-3 py-2 text-sm focus:border-primary focus:outline-none\"\n [class.border-red-400]=\"submitted() && !isValid()\"\n [value]=\"reason()\"\n (input)=\"onReasonInput($event)\"\n [attr.aria-invalid]=\"submitted() && !isValid()\"\n [placeholder]=\"t('delegations.form.rejectionReason')\"\n ></textarea>\n @if (submitted() && !isValid()) {\n <span class=\"text-xs text-red-600\">\n {{ t(\"delegations.form.rejectReasonRequired\") }}\n </span>\n }\n </div>\n\n <div class=\"flex justify-end gap-2 pt-2 border-t border-gray-200\">\n <mt-button\n variant=\"outlined\"\n color=\"secondary\"\n [label]=\"t('delegations.common.cancel')\"\n [disabled]=\"isBusy()\"\n (click)=\"cancel()\"\n ></mt-button>\n <mt-button\n color=\"danger\"\n icon=\"general.x-close\"\n [label]=\"t('delegations.action.reject')\"\n [loading]=\"isBusy()\"\n [disabled]=\"!isValid()\"\n (click)=\"confirm()\"\n ></mt-button>\n </div>\n </div>\n</ng-container>\n" }]
|
|
973
|
+
}], propDecorators: { delegation: [{ type: i0.Input, args: [{ isSignal: true, alias: "delegation", required: true }] }] } });
|
|
974
|
+
|
|
975
|
+
class Delegations {
|
|
976
|
+
router = inject(Router);
|
|
977
|
+
goBack() {
|
|
978
|
+
this.router.navigate(['control-panel/product-settings']);
|
|
979
|
+
}
|
|
980
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: Delegations, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
981
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.8", type: Delegations, isStandalone: true, selector: "mt-delegations", ngImport: i0, template: "<ng-container *transloco=\"let t\">\n <mt-page\n [title]=\"t('delegations.title')\"\n [avatarIcon]=\"'custom.hierarchy-structure'\"\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 (backButtonClick)=\"goBack()\"\n backButton\n >\n <router-outlet />\n </mt-page>\n</ng-container>\n", styles: [""], dependencies: [{ kind: "component", type: Page, selector: "mt-page", inputs: ["backButton", "backButtonIcon", "avatarIcon", "avatarStyle", "avatarShape", "title", "tabs", "activeTab", "contentClass", "contentId"], outputs: ["backButtonClick", "tabChange"] }, { kind: "directive", type: RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }, { kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }] });
|
|
982
|
+
}
|
|
983
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: Delegations, decorators: [{
|
|
984
|
+
type: Component,
|
|
985
|
+
args: [{ selector: 'mt-delegations', imports: [Page, RouterOutlet, TranslocoDirective], template: "<ng-container *transloco=\"let t\">\n <mt-page\n [title]=\"t('delegations.title')\"\n [avatarIcon]=\"'custom.hierarchy-structure'\"\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 (backButtonClick)=\"goBack()\"\n backButton\n >\n <router-outlet />\n </mt-page>\n</ng-container>\n" }]
|
|
986
|
+
}] });
|
|
987
|
+
|
|
943
988
|
const EMPTY_SCOPE$1 = { grants: [], metadata: {} };
|
|
944
989
|
function targetKey(t) {
|
|
945
990
|
return `${t.applicationKey}::${t.targetType}::${t.targetKey}`;
|
|
946
991
|
}
|
|
992
|
+
/**
|
|
993
|
+
* Full grant identity. Two grants with the same target/action but different
|
|
994
|
+
* accessibilities, data filters, or constraints are DIFFERENT grants and must
|
|
995
|
+
* not collapse (backend scope contract). Prefer a backend-provided stable
|
|
996
|
+
* `key`; otherwise hash the complete grant shape.
|
|
997
|
+
*/
|
|
947
998
|
function grantKey(g) {
|
|
948
|
-
|
|
999
|
+
if (g.key)
|
|
1000
|
+
return g.key;
|
|
1001
|
+
return JSON.stringify({
|
|
1002
|
+
app: g.applicationKey,
|
|
1003
|
+
target: [
|
|
1004
|
+
g.target.applicationKey,
|
|
1005
|
+
g.target.targetType,
|
|
1006
|
+
g.target.targetKey,
|
|
1007
|
+
g.target.permissionModuleType ?? null,
|
|
1008
|
+
g.target.permissionTargetId ?? null,
|
|
1009
|
+
g.target.legacyModuleId ?? null,
|
|
1010
|
+
g.target.levelId ?? null,
|
|
1011
|
+
g.target.levelModuleId ?? null,
|
|
1012
|
+
g.target.domainModuleId ?? null,
|
|
1013
|
+
g.target.moduleKey ?? null,
|
|
1014
|
+
],
|
|
1015
|
+
action: [
|
|
1016
|
+
g.action.applicationKey,
|
|
1017
|
+
g.action.operationKey,
|
|
1018
|
+
g.action.operationKind,
|
|
1019
|
+
g.action.permissionCommand ?? null,
|
|
1020
|
+
g.action.businessActionCode ?? null,
|
|
1021
|
+
],
|
|
1022
|
+
accessibilities: g.accessibilities ?? [],
|
|
1023
|
+
dataFilters: g.dataFilters ?? [],
|
|
1024
|
+
constraints: g.constraints ?? [],
|
|
1025
|
+
});
|
|
949
1026
|
}
|
|
950
1027
|
function normalizeGrant(g) {
|
|
951
1028
|
return {
|
|
@@ -967,6 +1044,11 @@ function normalizeGrant(g) {
|
|
|
967
1044
|
class ScopePicker {
|
|
968
1045
|
scope = model(EMPTY_SCOPE$1, ...(ngDevMode ? [{ debugName: "scope" }] : /* istanbul ignore next */ []));
|
|
969
1046
|
readonly = input(false, ...(ngDevMode ? [{ debugName: "readonly" }] : /* istanbul ignore next */ []));
|
|
1047
|
+
/**
|
|
1048
|
+
* Delegator whose grantable scope is loaded. Undefined = current user
|
|
1049
|
+
* (self-service). In admin mode the host rebinds this as the selected
|
|
1050
|
+
* delegator changes, and options reload reactively.
|
|
1051
|
+
*/
|
|
970
1052
|
delegatorUserId = input(undefined, ...(ngDevMode ? [{ debugName: "delegatorUserId" }] : /* istanbul ignore next */ []));
|
|
971
1053
|
facade = inject(DelegationsFacade);
|
|
972
1054
|
options = this.facade.scopeOptions;
|
|
@@ -992,10 +1074,12 @@ class ScopePicker {
|
|
|
992
1074
|
/** Selected grant keys for O(1) checkbox state. */
|
|
993
1075
|
selectedKeys = computed(() => new Set(this.scope().grants.map(grantKey)), ...(ngDevMode ? [{ debugName: "selectedKeys" }] : /* istanbul ignore next */ []));
|
|
994
1076
|
expanded = signal(new Set(), ...(ngDevMode ? [{ debugName: "expanded" }] : /* istanbul ignore next */ []));
|
|
995
|
-
ngOnInit() {
|
|
996
|
-
this.facade.loadScopeOptions(this.delegatorUserId());
|
|
997
|
-
}
|
|
998
1077
|
constructor() {
|
|
1078
|
+
// Load (and reload) grantable options whenever the delegator changes.
|
|
1079
|
+
effect(() => {
|
|
1080
|
+
const delegator = this.delegatorUserId();
|
|
1081
|
+
untracked(() => this.facade.loadScopeOptions(delegator));
|
|
1082
|
+
});
|
|
999
1083
|
let timer = null;
|
|
1000
1084
|
effect(() => {
|
|
1001
1085
|
const s = this.scope();
|
|
@@ -1041,7 +1125,7 @@ class ScopePicker {
|
|
|
1041
1125
|
return group.grants.filter((g) => keys.has(grantKey(g))).length;
|
|
1042
1126
|
}
|
|
1043
1127
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: ScopePicker, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1044
|
-
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() && !options()) {\n <p-skeleton height=\"2rem\"></p-skeleton>\n <p-skeleton height=\"6rem\"></p-skeleton>\n <p-skeleton height=\"6rem\"></p-skeleton>\n } @else if (options()) {\n <!-- Targets + actions -->\n <section class=\"flex flex-col gap-2\">\n <h4 class=\"text-sm font-medium text-gray-800\">\n {{ t(\"delegations.scope.permissionsTitle\") }}\n </h4>\n\n @if (groups().length === 0) {\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=\"border border-gray-200 rounded-md overflow-hidden\">\n <button\n type=\"button\"\n class=\"flex items-center justify-between w-full px-3 py-2 hover:bg-surface-50 cursor-pointer\"\n (click)=\"toggleAccordion(group)\"\n >\n <div class=\"flex items-center gap-2 min-w-0\">\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=\"text-sm text-gray-900 truncate\">\n {{ group.target.displayName || group.target.targetKey }}\n </span>\n <span class=\"text-xs text-gray-400\">\n {{ group.target.targetType }}\n </span>\n </div>\n @if (selectedCount(group); as count) {\n @if (count > 0) {\n <span\n class=\"text-xs text-primary font-medium px-2 py-0.5 rounded-full bg-primary-50\"\n >\n {{ count }}\n </span>\n }\n }\n </button>\n\n @if (isExpanded(group)) {\n <div class=\"flex flex-col gap-1 px-3 py-2 bg-surface-50/40\">\n @for (grant of group.grants; track
|
|
1128
|
+
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() && !options()) {\n <p-skeleton height=\"2rem\"></p-skeleton>\n <p-skeleton height=\"6rem\"></p-skeleton>\n <p-skeleton height=\"6rem\"></p-skeleton>\n } @else if (options()) {\n <!-- Targets + actions -->\n <section class=\"flex flex-col gap-2\">\n <h4 class=\"text-sm font-medium text-gray-800\">\n {{ t(\"delegations.scope.permissionsTitle\") }}\n </h4>\n\n @if (groups().length === 0) {\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=\"border border-gray-200 rounded-md overflow-hidden\">\n <button\n type=\"button\"\n class=\"flex items-center justify-between w-full px-3 py-2 hover:bg-surface-50 cursor-pointer\"\n (click)=\"toggleAccordion(group)\"\n >\n <div class=\"flex items-center gap-2 min-w-0\">\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=\"text-sm text-gray-900 truncate\">\n {{ group.target.displayName || group.target.targetKey }}\n </span>\n <span class=\"text-xs text-gray-400\">\n {{ group.target.targetType }}\n </span>\n </div>\n @if (selectedCount(group); as count) {\n @if (count > 0) {\n <span\n class=\"text-xs text-primary font-medium px-2 py-0.5 rounded-full bg-primary-50\"\n >\n {{ count }}\n </span>\n }\n }\n </button>\n\n @if (isExpanded(group)) {\n <div class=\"flex flex-col gap-1 px-3 py-2 bg-surface-50/40\">\n @for (grant of group.grants; track $index) {\n <label\n class=\"flex items-center gap-2 px-2 py-1.5 rounded-md cursor-pointer 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 {{ grant.action.operationKey }}\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 }\n </div>\n }\n </section>\n\n <!-- Preview -->\n <section\n class=\"flex flex-col gap-1 px-3 py-2 rounded-md border border-dashed border-gray-300 bg-surface-50\"\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 {{ p.summary || t(\"delegations.column.scopeSummary\") }}\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=\"text-xs text-amber-700 list-disc list-inside\">\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=\"text-xs text-red-700 list-disc list-inside\">\n @for (d of p.deniedItems; track d.operationKey) {\n <li>\n {{ d.targetKey }} / {{ d.operationKey }} \u2014\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}.mt-scope-picker input[type=checkbox]{accent-color:var(--p-primary-color, currentColor)}\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 });
|
|
1045
1129
|
}
|
|
1046
1130
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: ScopePicker, decorators: [{
|
|
1047
1131
|
type: Component,
|
|
@@ -1051,7 +1135,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
|
|
|
1051
1135
|
SkeletonModule,
|
|
1052
1136
|
TooltipModule,
|
|
1053
1137
|
TranslocoDirective,
|
|
1054
|
-
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-container *transloco=\"let t\">\n <div class=\"mt-scope-picker flex flex-col gap-4\">\n @if (isLoadingOptions() && !options()) {\n <p-skeleton height=\"2rem\"></p-skeleton>\n <p-skeleton height=\"6rem\"></p-skeleton>\n <p-skeleton height=\"6rem\"></p-skeleton>\n } @else if (options()) {\n <!-- Targets + actions -->\n <section class=\"flex flex-col gap-2\">\n <h4 class=\"text-sm font-medium text-gray-800\">\n {{ t(\"delegations.scope.permissionsTitle\") }}\n </h4>\n\n @if (groups().length === 0) {\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=\"border border-gray-200 rounded-md overflow-hidden\">\n <button\n type=\"button\"\n class=\"flex items-center justify-between w-full px-3 py-2 hover:bg-surface-50 cursor-pointer\"\n (click)=\"toggleAccordion(group)\"\n >\n <div class=\"flex items-center gap-2 min-w-0\">\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=\"text-sm text-gray-900 truncate\">\n {{ group.target.displayName || group.target.targetKey }}\n </span>\n <span class=\"text-xs text-gray-400\">\n {{ group.target.targetType }}\n </span>\n </div>\n @if (selectedCount(group); as count) {\n @if (count > 0) {\n <span\n class=\"text-xs text-primary font-medium px-2 py-0.5 rounded-full bg-primary-50\"\n >\n {{ count }}\n </span>\n }\n }\n </button>\n\n @if (isExpanded(group)) {\n <div class=\"flex flex-col gap-1 px-3 py-2 bg-surface-50/40\">\n @for (grant of group.grants; track
|
|
1138
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-container *transloco=\"let t\">\n <div class=\"mt-scope-picker flex flex-col gap-4\">\n @if (isLoadingOptions() && !options()) {\n <p-skeleton height=\"2rem\"></p-skeleton>\n <p-skeleton height=\"6rem\"></p-skeleton>\n <p-skeleton height=\"6rem\"></p-skeleton>\n } @else if (options()) {\n <!-- Targets + actions -->\n <section class=\"flex flex-col gap-2\">\n <h4 class=\"text-sm font-medium text-gray-800\">\n {{ t(\"delegations.scope.permissionsTitle\") }}\n </h4>\n\n @if (groups().length === 0) {\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=\"border border-gray-200 rounded-md overflow-hidden\">\n <button\n type=\"button\"\n class=\"flex items-center justify-between w-full px-3 py-2 hover:bg-surface-50 cursor-pointer\"\n (click)=\"toggleAccordion(group)\"\n >\n <div class=\"flex items-center gap-2 min-w-0\">\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=\"text-sm text-gray-900 truncate\">\n {{ group.target.displayName || group.target.targetKey }}\n </span>\n <span class=\"text-xs text-gray-400\">\n {{ group.target.targetType }}\n </span>\n </div>\n @if (selectedCount(group); as count) {\n @if (count > 0) {\n <span\n class=\"text-xs text-primary font-medium px-2 py-0.5 rounded-full bg-primary-50\"\n >\n {{ count }}\n </span>\n }\n }\n </button>\n\n @if (isExpanded(group)) {\n <div class=\"flex flex-col gap-1 px-3 py-2 bg-surface-50/40\">\n @for (grant of group.grants; track $index) {\n <label\n class=\"flex items-center gap-2 px-2 py-1.5 rounded-md cursor-pointer 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 {{ grant.action.operationKey }}\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 }\n </div>\n }\n </section>\n\n <!-- Preview -->\n <section\n class=\"flex flex-col gap-1 px-3 py-2 rounded-md border border-dashed border-gray-300 bg-surface-50\"\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 {{ p.summary || t(\"delegations.column.scopeSummary\") }}\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=\"text-xs text-amber-700 list-disc list-inside\">\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=\"text-xs text-red-700 list-disc list-inside\">\n @for (d of p.deniedItems; track d.operationKey) {\n <li>\n {{ d.targetKey }} / {{ d.operationKey }} \u2014\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}.mt-scope-picker input[type=checkbox]{accent-color:var(--p-primary-color, currentColor)}\n"] }]
|
|
1055
1139
|
}], 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 }] }] } });
|
|
1056
1140
|
|
|
1057
1141
|
const EMPTY_SCOPE = { grants: [], metadata: {} };
|
|
@@ -1066,6 +1150,12 @@ const EMPTY_SCOPE = { grants: [], metadata: {} };
|
|
|
1066
1150
|
class DelegationForm {
|
|
1067
1151
|
delegationForEdit = input(null, ...(ngDevMode ? [{ debugName: "delegationForEdit" }] : /* istanbul ignore next */ []));
|
|
1068
1152
|
readonly = input(false, ...(ngDevMode ? [{ debugName: "readonly" }] : /* istanbul ignore next */ []));
|
|
1153
|
+
/**
|
|
1154
|
+
* Admin-managed mode: also collect the Delegator / From user, so an admin can
|
|
1155
|
+
* create a delegation between two selected users (Ahmed → Sara). Self-service
|
|
1156
|
+
* leaves this false; the delegator defaults to the current user server-side.
|
|
1157
|
+
*/
|
|
1158
|
+
adminMode = input(false, ...(ngDevMode ? [{ debugName: "adminMode" }] : /* istanbul ignore next */ []));
|
|
1069
1159
|
halfWidth = 'flex flex-col gap-1 lg:col-span-6';
|
|
1070
1160
|
fullWidth = 'flex flex-col gap-1 lg:col-span-12';
|
|
1071
1161
|
modal = inject(ModalService);
|
|
@@ -1079,6 +1169,18 @@ class DelegationForm {
|
|
|
1079
1169
|
isSaving = this.facade.isSaving;
|
|
1080
1170
|
context = new HttpContext().set(REQUEST_CONTEXT, { useBaseUrl: false });
|
|
1081
1171
|
scope = signal(EMPTY_SCOPE, ...(ngDevMode ? [{ debugName: "scope" }] : /* istanbul ignore next */ []));
|
|
1172
|
+
/** Selected delegator id (admin mode only); drives scope/options + create payload. */
|
|
1173
|
+
selectedDelegatorId = computed(() => {
|
|
1174
|
+
if (!this.adminMode())
|
|
1175
|
+
return undefined;
|
|
1176
|
+
const v = this.formValue();
|
|
1177
|
+
return (v?.delegateFrom?.userId ??
|
|
1178
|
+
v?.delegateFrom?.id ??
|
|
1179
|
+
v?.delegateFrom ??
|
|
1180
|
+
undefined);
|
|
1181
|
+
}, ...(ngDevMode ? [{ debugName: "selectedDelegatorId" }] : /* istanbul ignore next */ []));
|
|
1182
|
+
/** In admin mode the scope picker only loads once a delegator is chosen. */
|
|
1183
|
+
showScopePicker = computed(() => !this.adminMode() || !!this.selectedDelegatorId(), ...(ngDevMode ? [{ debugName: "showScopePicker" }] : /* istanbul ignore next */ []));
|
|
1082
1184
|
specificDaysOptions = signal([
|
|
1083
1185
|
{ label: this.transloco.translate('delegations.days.sunday'), value: 0 },
|
|
1084
1186
|
{ label: this.transloco.translate('delegations.days.monday'), value: 1 },
|
|
@@ -1096,6 +1198,21 @@ class DelegationForm {
|
|
|
1096
1198
|
bodyClass: 'grid grid-cols-1 gap-4 items-start lg:grid-cols-12',
|
|
1097
1199
|
order: 1,
|
|
1098
1200
|
fields: [
|
|
1201
|
+
...(this.adminMode()
|
|
1202
|
+
? [
|
|
1203
|
+
new UserSearchFieldConfig({
|
|
1204
|
+
key: 'delegateFrom',
|
|
1205
|
+
label: this.transloco.translate('delegations.column.delegatorName'),
|
|
1206
|
+
apiUrl: 'Identity/users',
|
|
1207
|
+
context: this.context,
|
|
1208
|
+
validators: [ValidatorConfig.required()],
|
|
1209
|
+
cssClass: this.halfWidth,
|
|
1210
|
+
colSpan: 6,
|
|
1211
|
+
order: 0,
|
|
1212
|
+
disabled: this.readonly() || !!this.delegationForEdit(),
|
|
1213
|
+
}),
|
|
1214
|
+
]
|
|
1215
|
+
: []),
|
|
1099
1216
|
new UserSearchFieldConfig({
|
|
1100
1217
|
key: 'delegateTo',
|
|
1101
1218
|
label: this.transloco.translate('delegations.column.delegatedTo'),
|
|
@@ -1231,6 +1348,11 @@ class DelegationForm {
|
|
|
1231
1348
|
return;
|
|
1232
1349
|
const v = this.delegationFormControl.value;
|
|
1233
1350
|
const base = {
|
|
1351
|
+
// Admin mode sends the chosen delegator; self-service omits it so the
|
|
1352
|
+
// backend defaults delegateFrom to the current user (doc 04).
|
|
1353
|
+
delegateFrom: this.adminMode()
|
|
1354
|
+
? (this.selectedDelegatorId() ?? null)
|
|
1355
|
+
: null,
|
|
1234
1356
|
delegateTo: v?.delegateTo?.userId ?? v?.delegateTo?.id ?? v?.delegateTo,
|
|
1235
1357
|
description: v?.description,
|
|
1236
1358
|
delegateFromDateTime: v?.delegateFromDateTime,
|
|
@@ -1242,9 +1364,12 @@ class DelegationForm {
|
|
|
1242
1364
|
};
|
|
1243
1365
|
const editing = this.delegationForEdit();
|
|
1244
1366
|
if (editing) {
|
|
1367
|
+
// Prefer the freshest rowVersion from the loaded detail (the form loads
|
|
1368
|
+
// detail on open); fall back to the list row only if detail is absent.
|
|
1369
|
+
const latestRowVersion = this.detail()?.row?.rowVersion ?? editing.rowVersion;
|
|
1245
1370
|
const req = {
|
|
1246
1371
|
...base,
|
|
1247
|
-
rowVersion:
|
|
1372
|
+
rowVersion: latestRowVersion,
|
|
1248
1373
|
};
|
|
1249
1374
|
this.facade.updateV2(editing.delegationId, req).subscribe({
|
|
1250
1375
|
next: () => this.ref.close(true),
|
|
@@ -1258,7 +1383,7 @@ class DelegationForm {
|
|
|
1258
1383
|
}
|
|
1259
1384
|
}
|
|
1260
1385
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: DelegationForm, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1261
|
-
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 } }, ngImport: i0, template: "<ng-container *transloco=\"let t\">\n <div\n [class]=\"\n 'delegation-form-content flex min-w-0 flex-col gap-3 overflow-x-hidden p-4 max-[640px]:p-3 ' +\n modal.contentClass\n \"\n >\n <mt-dynamic-form\n [formConfig]=\"formConfig()\"\n [formControl]=\"delegationFormControl\"\n />\n\n <div class=\"mt-2 border-t border-gray-200 pt-3 flex flex-col gap-2\">\n <h3 class=\"text-sm font-semibold text-gray-800\">\n {{ t(\"delegations.scope.permissionsTitle\") }}\n </h3>\n <mt-scope-picker\n
|
|
1386
|
+
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-3 overflow-x-hidden p-4 max-[640px]:p-3 ' +\n modal.contentClass\n \"\n >\n <mt-dynamic-form\n [formConfig]=\"formConfig()\"\n [formControl]=\"delegationFormControl\"\n />\n\n <div class=\"mt-2 border-t border-gray-200 pt-3 flex flex-col gap-2\">\n <h3 class=\"text-sm font-semibold text-gray-800\">\n {{ t(\"delegations.scope.permissionsTitle\") }}\n </h3>\n @if (showScopePicker()) {\n <mt-scope-picker\n [(scope)]=\"scope\"\n [readonly]=\"readonly()\"\n [delegatorUserId]=\"selectedDelegatorId()\"\n ></mt-scope-picker>\n } @else {\n <p class=\"text-xs text-gray-500\">\n {{ t(\"delegations.form.selectDelegatorFirst\") }}\n </p>\n }\n </div>\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: [""], 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 });
|
|
1262
1387
|
}
|
|
1263
1388
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: DelegationForm, decorators: [{
|
|
1264
1389
|
type: Component,
|
|
@@ -1269,8 +1394,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
|
|
|
1269
1394
|
ReactiveFormsModule,
|
|
1270
1395
|
ScopePicker,
|
|
1271
1396
|
TranslocoDirective,
|
|
1272
|
-
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-container *transloco=\"let t\">\n <div\n [class]=\"\n 'delegation-form-content flex min-w-0 flex-col gap-3 overflow-x-hidden p-4 max-[640px]:p-3 ' +\n modal.contentClass\n \"\n >\n <mt-dynamic-form\n [formConfig]=\"formConfig()\"\n [formControl]=\"delegationFormControl\"\n />\n\n <div class=\"mt-2 border-t border-gray-200 pt-3 flex flex-col gap-2\">\n <h3 class=\"text-sm font-semibold text-gray-800\">\n {{ t(\"delegations.scope.permissionsTitle\") }}\n </h3>\n <mt-scope-picker\n
|
|
1273
|
-
}], ctorParameters: () => [], propDecorators: { delegationForEdit: [{ type: i0.Input, args: [{ isSignal: true, alias: "delegationForEdit", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }] } });
|
|
1397
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-container *transloco=\"let t\">\n <div\n [class]=\"\n 'delegation-form-content flex min-w-0 flex-col gap-3 overflow-x-hidden p-4 max-[640px]:p-3 ' +\n modal.contentClass\n \"\n >\n <mt-dynamic-form\n [formConfig]=\"formConfig()\"\n [formControl]=\"delegationFormControl\"\n />\n\n <div class=\"mt-2 border-t border-gray-200 pt-3 flex flex-col gap-2\">\n <h3 class=\"text-sm font-semibold text-gray-800\">\n {{ t(\"delegations.scope.permissionsTitle\") }}\n </h3>\n @if (showScopePicker()) {\n <mt-scope-picker\n [(scope)]=\"scope\"\n [readonly]=\"readonly()\"\n [delegatorUserId]=\"selectedDelegatorId()\"\n ></mt-scope-picker>\n } @else {\n <p class=\"text-xs text-gray-500\">\n {{ t(\"delegations.form.selectDelegatorFirst\") }}\n </p>\n }\n </div>\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" }]
|
|
1398
|
+
}], 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 }] }] } });
|
|
1274
1399
|
|
|
1275
1400
|
/**
|
|
1276
1401
|
* Read-only drawer that shows the full `DelegationDetail` ({ row, scope, status }).
|
|
@@ -1326,6 +1451,13 @@ class DelegationsList {
|
|
|
1326
1451
|
* `data: { showBreadcrumb: false }`.
|
|
1327
1452
|
*/
|
|
1328
1453
|
showBreadcrumb = input(true, ...(ngDevMode ? [{ debugName: "showBreadcrumb" }] : /* istanbul ignore next */ []));
|
|
1454
|
+
/**
|
|
1455
|
+
* Admin-managed surface: the create/edit form also collects the Delegator /
|
|
1456
|
+
* From user (admin creates a delegation between two users). Client
|
|
1457
|
+
* self-service leaves this false. Bound from route data via
|
|
1458
|
+
* `withComponentInputBinding()` — admin passes `data: { adminMode: true }`.
|
|
1459
|
+
*/
|
|
1460
|
+
adminMode = input(false, ...(ngDevMode ? [{ debugName: "adminMode" }] : /* istanbul ignore next */ []));
|
|
1329
1461
|
facade = inject(DelegationsFacade);
|
|
1330
1462
|
modal = inject(ModalService);
|
|
1331
1463
|
transloco = inject(TranslocoService);
|
|
@@ -1402,10 +1534,9 @@ class DelegationsList {
|
|
|
1402
1534
|
tooltip: this.transloco.translate('delegations.action.reject'),
|
|
1403
1535
|
color: 'danger',
|
|
1404
1536
|
variant: 'outlined',
|
|
1537
|
+
// Opens a dialog that collects a required reason (no inline popup confirm).
|
|
1405
1538
|
action: (row) => this.reject(row),
|
|
1406
1539
|
hidden: (row) => !this.has(row, 'Reject'),
|
|
1407
|
-
loading: (row) => this.busyIds().includes(`reject:${row.delegationId}`),
|
|
1408
|
-
confirmation: { type: 'popup' },
|
|
1409
1540
|
},
|
|
1410
1541
|
{
|
|
1411
1542
|
icon: 'general.trash-01',
|
|
@@ -1529,7 +1660,11 @@ class DelegationsList {
|
|
|
1529
1660
|
appendTo: row ? 'page-content' : 'body',
|
|
1530
1661
|
dismissableMask: true,
|
|
1531
1662
|
dismissible: true,
|
|
1532
|
-
inputValues: {
|
|
1663
|
+
inputValues: {
|
|
1664
|
+
delegationForEdit: row,
|
|
1665
|
+
readonly: false,
|
|
1666
|
+
adminMode: this.adminMode(),
|
|
1667
|
+
},
|
|
1533
1668
|
});
|
|
1534
1669
|
ref.onClose.subscribe((saved) => {
|
|
1535
1670
|
if (saved)
|
|
@@ -1544,14 +1679,17 @@ class DelegationsList {
|
|
|
1544
1679
|
.subscribe({ next: () => this.reloadCurrentTab() });
|
|
1545
1680
|
}
|
|
1546
1681
|
reject(row) {
|
|
1547
|
-
this.
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1682
|
+
const ref = this.modal.openModal(RejectDelegationDialog, 'dialog', {
|
|
1683
|
+
header: this.transloco.translate('delegations.action.reject'),
|
|
1684
|
+
styleClass: '!w-[min(96vw,30rem)] !max-w-[96vw]',
|
|
1685
|
+
dismissableMask: true,
|
|
1686
|
+
dismissible: true,
|
|
1687
|
+
inputValues: { delegation: row },
|
|
1688
|
+
});
|
|
1689
|
+
ref.onClose.subscribe((rejected) => {
|
|
1690
|
+
if (rejected)
|
|
1691
|
+
this.reloadCurrentTab();
|
|
1692
|
+
});
|
|
1555
1693
|
}
|
|
1556
1694
|
cancel(row) {
|
|
1557
1695
|
this.mark(`cancel:${row.delegationId}`, true);
|
|
@@ -1573,7 +1711,7 @@ class DelegationsList {
|
|
|
1573
1711
|
this.busyIds.update((ids) => on ? [...ids, key] : ids.filter((id) => id !== key));
|
|
1574
1712
|
}
|
|
1575
1713
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: DelegationsList, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1576
|
-
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 } }, 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 h-full gap-2 p-4\">\n @if (showBreadcrumb()) {\n <mt-breadcrumb [items]=\"breadcrumbItems()\"></mt-breadcrumb>\n }\n\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 <mt-table\n [data]=\"rows()\"\n [columns]=\"tableColumns()\"\n [actions]=\"tableActions()\"\n [rowActions]=\"rowActions()\"\n [loading]=\"isLoading()\"\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 = activeTab() === \"my\" ? row.delegatedUser : row.delegator;\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 flex-col min-w-0\">\n <span class=\"text-sm text-gray-900 truncate\">\n {{ party?.displayName }}\n </span>\n @if (party?.email) {\n <span class=\"text-xs text-gray-500 truncate\">\n {{ party.email }}\n </span>\n }\n </div>\n </div>\n </ng-template>\n\n <ng-template #scopeCol let-row>\n <span class=\"text-sm text-gray-700\" [title]=\"row.scopeSummary\">\n {{ row.scopeSummary || \"\u2014\" }}\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 </mt-table>\n </div>\n</ng-container>\n", styles: [":host{display:block;min-width:0}: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}}\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: 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 });
|
|
1714
|
+
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 h-full gap-2 p-4\">\n @if (showBreadcrumb()) {\n <mt-breadcrumb [items]=\"breadcrumbItems()\"></mt-breadcrumb>\n }\n\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 <mt-table\n [data]=\"rows()\"\n [columns]=\"tableColumns()\"\n [actions]=\"tableActions()\"\n [rowActions]=\"rowActions()\"\n [loading]=\"isLoading()\"\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 = activeTab() === \"my\" ? row.delegatedUser : row.delegator;\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 flex-col min-w-0\">\n <span class=\"text-sm text-gray-900 truncate\">\n {{ party?.displayName }}\n </span>\n @if (party?.email) {\n <span class=\"text-xs text-gray-500 truncate\">\n {{ party.email }}\n </span>\n }\n </div>\n </div>\n </ng-template>\n\n <ng-template #scopeCol let-row>\n <span class=\"text-sm text-gray-700\" [title]=\"row.scopeSummary\">\n {{ row.scopeSummary || \"\u2014\" }}\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 </mt-table>\n </div>\n</ng-container>\n", styles: [":host{display:block;min-width:0}: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}}\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: 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 });
|
|
1577
1715
|
}
|
|
1578
1716
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: DelegationsList, decorators: [{
|
|
1579
1717
|
type: Component,
|
|
@@ -1585,7 +1723,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
|
|
|
1585
1723
|
TranslocoDirective,
|
|
1586
1724
|
DelegationStatusChip,
|
|
1587
1725
|
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-container *transloco=\"let t\">\n <div class=\"flex flex-col h-full gap-2 p-4\">\n @if (showBreadcrumb()) {\n <mt-breadcrumb [items]=\"breadcrumbItems()\"></mt-breadcrumb>\n }\n\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 <mt-table\n [data]=\"rows()\"\n [columns]=\"tableColumns()\"\n [actions]=\"tableActions()\"\n [rowActions]=\"rowActions()\"\n [loading]=\"isLoading()\"\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 = activeTab() === \"my\" ? row.delegatedUser : row.delegator;\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 flex-col min-w-0\">\n <span class=\"text-sm text-gray-900 truncate\">\n {{ party?.displayName }}\n </span>\n @if (party?.email) {\n <span class=\"text-xs text-gray-500 truncate\">\n {{ party.email }}\n </span>\n }\n </div>\n </div>\n </ng-template>\n\n <ng-template #scopeCol let-row>\n <span class=\"text-sm text-gray-700\" [title]=\"row.scopeSummary\">\n {{ row.scopeSummary || \"\u2014\" }}\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 </mt-table>\n </div>\n</ng-container>\n", styles: [":host{display:block;min-width:0}: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}}\n"] }]
|
|
1588
|
-
}], propDecorators: { showBreadcrumb: [{ type: i0.Input, args: [{ isSignal: true, alias: "showBreadcrumb", 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 }] }] } });
|
|
1726
|
+
}], 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 }] }] } });
|
|
1589
1727
|
|
|
1590
1728
|
/**
|
|
1591
1729
|
* Endpoints that must NEVER carry the `app-delegation` header.
|
|
@@ -1618,5 +1756,5 @@ const appDelegationInterceptor = (req, next) => {
|
|
|
1618
1756
|
* Generated bundle index. Do not edit.
|
|
1619
1757
|
*/
|
|
1620
1758
|
|
|
1621
|
-
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, ScopePicker, StartDelegationSession, StartSessionDialog, SwitchDelegationSession, TopbarDelegationMenu, UpdateDelegationLegacy, UpdateDelegationV2, appDelegationInterceptor };
|
|
1759
|
+
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 };
|
|
1622
1760
|
//# sourceMappingURL=masterteam-delegations.mjs.map
|