@masterteam/delegations 0.0.22 → 0.0.24
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,5 +1,5 @@
|
|
|
1
|
-
import * as i2
|
|
2
|
-
import { CommonModule } from '@angular/common';
|
|
1
|
+
import * as i2 from '@angular/common';
|
|
2
|
+
import { CommonModule, Location } from '@angular/common';
|
|
3
3
|
import * as i0 from '@angular/core';
|
|
4
4
|
import { input, computed, Component, inject, Injectable, ChangeDetectionStrategy, viewChild, signal, model, effect, untracked, linkedSignal } from '@angular/core';
|
|
5
5
|
import { RouterLink, ActivatedRoute, Router, NavigationEnd, RouterOutlet } from '@angular/router';
|
|
@@ -24,10 +24,9 @@ import { Table } from '@masterteam/components/table';
|
|
|
24
24
|
import * as i1$1 from '@angular/forms';
|
|
25
25
|
import { FormControl, ReactiveFormsModule } from '@angular/forms';
|
|
26
26
|
import { DynamicForm } from '@masterteam/forms/dynamic-form';
|
|
27
|
+
import { Tree } from '@masterteam/components/tree';
|
|
27
28
|
import * as i1 from 'primeng/skeleton';
|
|
28
29
|
import { SkeletonModule } from 'primeng/skeleton';
|
|
29
|
-
import * as i2 from 'primeng/tooltip';
|
|
30
|
-
import { TooltipModule } from 'primeng/tooltip';
|
|
31
30
|
|
|
32
31
|
const STATUS_VISUAL = {
|
|
33
32
|
Active: {
|
|
@@ -558,9 +557,11 @@ class GetActiveAssignedDelegations {
|
|
|
558
557
|
}
|
|
559
558
|
class GetDelegationDetail {
|
|
560
559
|
id;
|
|
560
|
+
asAdmin;
|
|
561
561
|
static type = '[Delegations] Get Delegation Detail';
|
|
562
|
-
constructor(id) {
|
|
562
|
+
constructor(id, asAdmin = false) {
|
|
563
563
|
this.id = id;
|
|
564
|
+
this.asAdmin = asAdmin;
|
|
564
565
|
}
|
|
565
566
|
}
|
|
566
567
|
class ClearDelegationDetail {
|
|
@@ -571,16 +572,20 @@ class ClearDelegationDetail {
|
|
|
571
572
|
// ---------------------------------------------------------------------------
|
|
572
573
|
class GetScopeOptions {
|
|
573
574
|
delegatorUserId;
|
|
575
|
+
asAdmin;
|
|
574
576
|
static type = '[Delegations] Get Scope Options';
|
|
575
|
-
constructor(delegatorUserId) {
|
|
577
|
+
constructor(delegatorUserId, asAdmin = false) {
|
|
576
578
|
this.delegatorUserId = delegatorUserId;
|
|
579
|
+
this.asAdmin = asAdmin;
|
|
577
580
|
}
|
|
578
581
|
}
|
|
579
582
|
class PreviewScope {
|
|
580
|
-
|
|
583
|
+
request;
|
|
584
|
+
asAdmin;
|
|
581
585
|
static type = '[Delegations] Preview Scope';
|
|
582
|
-
constructor(
|
|
583
|
-
this.
|
|
586
|
+
constructor(request, asAdmin = false) {
|
|
587
|
+
this.request = request;
|
|
588
|
+
this.asAdmin = asAdmin;
|
|
584
589
|
}
|
|
585
590
|
}
|
|
586
591
|
class ClearScopePreview {
|
|
@@ -598,9 +603,11 @@ class CreateDelegationLegacy {
|
|
|
598
603
|
}
|
|
599
604
|
class CreateDelegationV2 {
|
|
600
605
|
request;
|
|
606
|
+
asAdmin;
|
|
601
607
|
static type = '[Delegations] Create Delegation (v2)';
|
|
602
|
-
constructor(request) {
|
|
608
|
+
constructor(request, asAdmin = false) {
|
|
603
609
|
this.request = request;
|
|
610
|
+
this.asAdmin = asAdmin;
|
|
604
611
|
}
|
|
605
612
|
}
|
|
606
613
|
class UpdateDelegationLegacy {
|
|
@@ -615,10 +622,12 @@ class UpdateDelegationLegacy {
|
|
|
615
622
|
class UpdateDelegationV2 {
|
|
616
623
|
id;
|
|
617
624
|
request;
|
|
625
|
+
asAdmin;
|
|
618
626
|
static type = '[Delegations] Update Delegation (v2)';
|
|
619
|
-
constructor(id, request) {
|
|
627
|
+
constructor(id, request, asAdmin = false) {
|
|
620
628
|
this.id = id;
|
|
621
629
|
this.request = request;
|
|
630
|
+
this.asAdmin = asAdmin;
|
|
622
631
|
}
|
|
623
632
|
}
|
|
624
633
|
// ---------------------------------------------------------------------------
|
|
@@ -777,8 +786,9 @@ let DelegationsState = class DelegationsState {
|
|
|
777
786
|
onSuccess: (response) => ({ active: response.data }),
|
|
778
787
|
});
|
|
779
788
|
}
|
|
780
|
-
getDetail(ctx, { id }) {
|
|
781
|
-
const
|
|
789
|
+
getDetail(ctx, { id, asAdmin }) {
|
|
790
|
+
const path = asAdmin ? `${BASE}/Admin/${id}` : `${BASE}/${id}`;
|
|
791
|
+
const req$ = this.http.get(path);
|
|
782
792
|
return handleApiRequest({
|
|
783
793
|
ctx,
|
|
784
794
|
key: DelegationsActionKey.GetDetail,
|
|
@@ -792,12 +802,17 @@ let DelegationsState = class DelegationsState {
|
|
|
792
802
|
// ============================================================================
|
|
793
803
|
// Scope
|
|
794
804
|
// ============================================================================
|
|
795
|
-
getScopeOptions(ctx, { delegatorUserId }) {
|
|
805
|
+
getScopeOptions(ctx, { delegatorUserId, asAdmin }) {
|
|
796
806
|
ctx.patchState({ scopeOptions: null, scopePreview: null });
|
|
797
807
|
let params = new HttpParams();
|
|
798
808
|
if (delegatorUserId)
|
|
799
809
|
params = params.set('delegatorUserId', delegatorUserId);
|
|
800
|
-
const
|
|
810
|
+
const path = asAdmin
|
|
811
|
+
? `${BASE}/Admin/scope/options`
|
|
812
|
+
: `${BASE}/scope/options`;
|
|
813
|
+
const req$ = this.http.get(path, {
|
|
814
|
+
params,
|
|
815
|
+
});
|
|
801
816
|
return handleApiRequest({
|
|
802
817
|
ctx,
|
|
803
818
|
key: DelegationsActionKey.GetScopeOptions,
|
|
@@ -805,8 +820,11 @@ let DelegationsState = class DelegationsState {
|
|
|
805
820
|
onSuccess: (response) => ({ scopeOptions: response.data ?? null }),
|
|
806
821
|
});
|
|
807
822
|
}
|
|
808
|
-
previewScope(ctx, {
|
|
809
|
-
const
|
|
823
|
+
previewScope(ctx, { request, asAdmin }) {
|
|
824
|
+
const path = asAdmin
|
|
825
|
+
? `${BASE}/Admin/scope/preview`
|
|
826
|
+
: `${BASE}/scope/preview`;
|
|
827
|
+
const req$ = this.http.post(path, this.buildScopePreviewRequest(request, asAdmin));
|
|
810
828
|
return handleApiRequest({
|
|
811
829
|
ctx,
|
|
812
830
|
key: DelegationsActionKey.PreviewScope,
|
|
@@ -829,8 +847,12 @@ let DelegationsState = class DelegationsState {
|
|
|
829
847
|
onSuccess: () => ({}),
|
|
830
848
|
});
|
|
831
849
|
}
|
|
832
|
-
createV2(ctx, { request }) {
|
|
833
|
-
const
|
|
850
|
+
createV2(ctx, { request, asAdmin }) {
|
|
851
|
+
const path = asAdmin ? `${BASE}/Admin/v2` : `${BASE}/v2`;
|
|
852
|
+
const body = asAdmin
|
|
853
|
+
? this.buildAdminSaveRequest(request)
|
|
854
|
+
: request;
|
|
855
|
+
const req$ = this.http.post(path, body);
|
|
834
856
|
return handleApiRequest({
|
|
835
857
|
ctx,
|
|
836
858
|
key: DelegationsActionKey.Create,
|
|
@@ -847,8 +869,12 @@ let DelegationsState = class DelegationsState {
|
|
|
847
869
|
onSuccess: () => ({}),
|
|
848
870
|
});
|
|
849
871
|
}
|
|
850
|
-
updateV2(ctx, { id, request }) {
|
|
851
|
-
const
|
|
872
|
+
updateV2(ctx, { id, request, asAdmin }) {
|
|
873
|
+
const path = asAdmin ? `${BASE}/Admin/v2/${id}` : `${BASE}/v2/${id}`;
|
|
874
|
+
const body = asAdmin
|
|
875
|
+
? this.buildAdminSaveRequest(request)
|
|
876
|
+
: request;
|
|
877
|
+
const req$ = this.http.put(path, body);
|
|
852
878
|
return handleApiRequest({
|
|
853
879
|
ctx,
|
|
854
880
|
key: DelegationsActionKey.Update,
|
|
@@ -889,6 +915,21 @@ let DelegationsState = class DelegationsState {
|
|
|
889
915
|
onSuccess: () => ({}),
|
|
890
916
|
});
|
|
891
917
|
}
|
|
918
|
+
buildScopePreviewRequest(request, asAdmin) {
|
|
919
|
+
if (!asAdmin) {
|
|
920
|
+
return { scope: request.scope };
|
|
921
|
+
}
|
|
922
|
+
return {
|
|
923
|
+
delegatorUserId: request.delegatorUserId ?? null,
|
|
924
|
+
scope: request.scope,
|
|
925
|
+
};
|
|
926
|
+
}
|
|
927
|
+
buildAdminSaveRequest(request) {
|
|
928
|
+
return {
|
|
929
|
+
...request,
|
|
930
|
+
specificDays: request.specificDays ?? [],
|
|
931
|
+
};
|
|
932
|
+
}
|
|
892
933
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: DelegationsState, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
893
934
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: DelegationsState });
|
|
894
935
|
};
|
|
@@ -1049,17 +1090,21 @@ class DelegationsFacade {
|
|
|
1049
1090
|
getActive(query = {}) {
|
|
1050
1091
|
return this.store.dispatch(new GetActiveAssignedDelegations(query));
|
|
1051
1092
|
}
|
|
1052
|
-
getDetail(id) {
|
|
1053
|
-
return this.store.dispatch(new GetDelegationDetail(id));
|
|
1093
|
+
getDetail(id, asAdmin = false) {
|
|
1094
|
+
return this.store.dispatch(new GetDelegationDetail(id, asAdmin));
|
|
1054
1095
|
}
|
|
1055
1096
|
clearDetail() {
|
|
1056
1097
|
return this.store.dispatch(new ClearDelegationDetail());
|
|
1057
1098
|
}
|
|
1058
|
-
loadScopeOptions(delegatorUserId) {
|
|
1059
|
-
return this.store.dispatch(new GetScopeOptions(delegatorUserId));
|
|
1099
|
+
loadScopeOptions(delegatorUserId, asAdmin = false) {
|
|
1100
|
+
return this.store.dispatch(new GetScopeOptions(delegatorUserId, asAdmin));
|
|
1060
1101
|
}
|
|
1061
|
-
previewScope(scope) {
|
|
1062
|
-
|
|
1102
|
+
previewScope(scope, options) {
|
|
1103
|
+
const request = {
|
|
1104
|
+
delegatorUserId: options?.delegatorUserId,
|
|
1105
|
+
scope,
|
|
1106
|
+
};
|
|
1107
|
+
return this.store.dispatch(new PreviewScope(request, options?.asAdmin ?? false));
|
|
1063
1108
|
}
|
|
1064
1109
|
clearScopePreview() {
|
|
1065
1110
|
return this.store.dispatch(new ClearScopePreview());
|
|
@@ -1067,14 +1112,14 @@ class DelegationsFacade {
|
|
|
1067
1112
|
createLegacy(request) {
|
|
1068
1113
|
return this.store.dispatch(new CreateDelegationLegacy(request));
|
|
1069
1114
|
}
|
|
1070
|
-
createV2(request) {
|
|
1071
|
-
return this.store.dispatch(new CreateDelegationV2(request));
|
|
1115
|
+
createV2(request, asAdmin = false) {
|
|
1116
|
+
return this.store.dispatch(new CreateDelegationV2(request, asAdmin));
|
|
1072
1117
|
}
|
|
1073
1118
|
updateLegacy(id, request) {
|
|
1074
1119
|
return this.store.dispatch(new UpdateDelegationLegacy(id, request));
|
|
1075
1120
|
}
|
|
1076
|
-
updateV2(id, request) {
|
|
1077
|
-
return this.store.dispatch(new UpdateDelegationV2(id, request));
|
|
1121
|
+
updateV2(id, request, asAdmin = false) {
|
|
1122
|
+
return this.store.dispatch(new UpdateDelegationV2(id, request, asAdmin));
|
|
1078
1123
|
}
|
|
1079
1124
|
approve(id, request) {
|
|
1080
1125
|
return this.store.dispatch(new ApproveDelegation(id, request));
|
|
@@ -1529,6 +1574,16 @@ function normalizeGrant(g) {
|
|
|
1529
1574
|
constraints: g.constraints ?? [],
|
|
1530
1575
|
};
|
|
1531
1576
|
}
|
|
1577
|
+
function sameSelectionKeys(left, right) {
|
|
1578
|
+
const leftKeys = (left ?? [])
|
|
1579
|
+
.map((node) => node.key)
|
|
1580
|
+
.filter((key) => !!key);
|
|
1581
|
+
if (leftKeys.length !== right.length) {
|
|
1582
|
+
return false;
|
|
1583
|
+
}
|
|
1584
|
+
const rightSet = new Set(right);
|
|
1585
|
+
return leftKeys.every((key) => rightSet.has(key));
|
|
1586
|
+
}
|
|
1532
1587
|
/**
|
|
1533
1588
|
* Hierarchy-based scope picker (doc 03). Driven entirely by `scope/options`:
|
|
1534
1589
|
* - renders backend-owned Level -> Level Module -> operations hierarchy,
|
|
@@ -1542,6 +1597,7 @@ function normalizeGrant(g) {
|
|
|
1542
1597
|
class ScopePicker {
|
|
1543
1598
|
scope = model(EMPTY_SCOPE$1, ...(ngDevMode ? [{ debugName: "scope" }] : /* istanbul ignore next */ []));
|
|
1544
1599
|
readonly = input(false, ...(ngDevMode ? [{ debugName: "readonly" }] : /* istanbul ignore next */ []));
|
|
1600
|
+
adminMode = input(false, ...(ngDevMode ? [{ debugName: "adminMode" }] : /* istanbul ignore next */ []));
|
|
1545
1601
|
/**
|
|
1546
1602
|
* Delegator whose grantable scope is loaded. Undefined = current user
|
|
1547
1603
|
* (self-service). In admin mode the host rebinds this as the selected
|
|
@@ -1556,16 +1612,32 @@ class ScopePicker {
|
|
|
1556
1612
|
isPreviewing = this.facade.isPreviewingScope;
|
|
1557
1613
|
errorOptions = this.facade.errorScopeOptions;
|
|
1558
1614
|
groups = computed(() => getDelegationGrantableGroups(this.options()), ...(ngDevMode ? [{ debugName: "groups" }] : /* istanbul ignore next */ []));
|
|
1615
|
+
activeLang = toSignal(this.transloco.langChanges$, {
|
|
1616
|
+
initialValue: this.transloco.getActiveLang(),
|
|
1617
|
+
});
|
|
1559
1618
|
/** Selected grant keys for O(1) checkbox state. */
|
|
1560
1619
|
selectedKeys = computed(() => new Set(this.scope().grants.map(grantKey)), ...(ngDevMode ? [{ debugName: "selectedKeys" }] : /* istanbul ignore next */ []));
|
|
1561
|
-
|
|
1620
|
+
treeSelection = signal([], ...(ngDevMode ? [{ debugName: "treeSelection" }] : /* istanbul ignore next */ []));
|
|
1621
|
+
treeNodes = computed(() => this.buildTreeNodes(this.groups()), ...(ngDevMode ? [{ debugName: "treeNodes" }] : /* istanbul ignore next */ []));
|
|
1622
|
+
grantIndex = computed(() => {
|
|
1623
|
+
const index = new Map();
|
|
1624
|
+
for (const group of this.groups()) {
|
|
1625
|
+
for (const targetGroup of group.targets) {
|
|
1626
|
+
for (const grant of targetGroup.grants) {
|
|
1627
|
+
index.set(grantKey(grant), normalizeGrant(grant));
|
|
1628
|
+
}
|
|
1629
|
+
}
|
|
1630
|
+
}
|
|
1631
|
+
return index;
|
|
1632
|
+
}, ...(ngDevMode ? [{ debugName: "grantIndex" }] : /* istanbul ignore next */ []));
|
|
1562
1633
|
constructor() {
|
|
1563
1634
|
// Load (and reload) grantable options whenever the delegator changes.
|
|
1564
1635
|
let initialized = false;
|
|
1565
1636
|
let lastDelegatorUserId;
|
|
1566
1637
|
effect(() => {
|
|
1567
1638
|
const delegator = this.delegatorUserId();
|
|
1568
|
-
|
|
1639
|
+
const asAdmin = this.adminMode();
|
|
1640
|
+
untracked(() => this.facade.loadScopeOptions(delegator, asAdmin));
|
|
1569
1641
|
if (!initialized) {
|
|
1570
1642
|
initialized = true;
|
|
1571
1643
|
lastDelegatorUserId = delegator;
|
|
@@ -1574,13 +1646,15 @@ class ScopePicker {
|
|
|
1574
1646
|
if (lastDelegatorUserId !== delegator) {
|
|
1575
1647
|
lastDelegatorUserId = delegator;
|
|
1576
1648
|
this.scope.set(EMPTY_SCOPE$1);
|
|
1577
|
-
this.
|
|
1649
|
+
this.treeSelection.set([]);
|
|
1578
1650
|
this.facade.clearScopePreview();
|
|
1579
1651
|
}
|
|
1580
1652
|
});
|
|
1581
1653
|
let timer = null;
|
|
1582
1654
|
effect(() => {
|
|
1583
1655
|
const s = this.scope();
|
|
1656
|
+
const delegator = this.delegatorUserId();
|
|
1657
|
+
const asAdmin = this.adminMode();
|
|
1584
1658
|
untracked(() => {
|
|
1585
1659
|
if (!s.grants.length) {
|
|
1586
1660
|
this.facade.clearScopePreview();
|
|
@@ -1588,51 +1662,35 @@ class ScopePicker {
|
|
|
1588
1662
|
}
|
|
1589
1663
|
if (timer)
|
|
1590
1664
|
clearTimeout(timer);
|
|
1591
|
-
timer = setTimeout(() => this.facade.previewScope(s
|
|
1665
|
+
timer = setTimeout(() => this.facade.previewScope(s, {
|
|
1666
|
+
delegatorUserId: delegator,
|
|
1667
|
+
asAdmin,
|
|
1668
|
+
}), 300);
|
|
1592
1669
|
});
|
|
1593
1670
|
});
|
|
1594
|
-
let lastGroupKeys = '';
|
|
1595
1671
|
effect(() => {
|
|
1596
|
-
const
|
|
1597
|
-
const
|
|
1598
|
-
if (
|
|
1599
|
-
lastGroupKeys = '';
|
|
1600
|
-
this.expanded.set(new Set());
|
|
1672
|
+
const selectedKeys = Array.from(this.selectedKeys());
|
|
1673
|
+
const selection = this.treeSelection();
|
|
1674
|
+
if (sameSelectionKeys(selection, selectedKeys)) {
|
|
1601
1675
|
return;
|
|
1602
1676
|
}
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
}
|
|
1606
|
-
lastGroupKeys = nextGroupKeys;
|
|
1607
|
-
this.expanded.set(new Set(groups.map((group) => group.key)));
|
|
1677
|
+
this.treeSelection.set(selectedKeys.map((key) => ({
|
|
1678
|
+
key,
|
|
1679
|
+
})));
|
|
1608
1680
|
});
|
|
1609
1681
|
}
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
const
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
return this.selectedKeys().has(grantKey(grant));
|
|
1623
|
-
}
|
|
1624
|
-
getGroupLabel(group) {
|
|
1625
|
-
return (formatDelegationDisplayValue(group.displayName, this.currentLanguage()) ||
|
|
1626
|
-
humanizeDelegationKey(group.groupType));
|
|
1627
|
-
}
|
|
1628
|
-
getTargetLabel(target) {
|
|
1629
|
-
return formatDelegationTargetLabel(target, this.currentLanguage());
|
|
1630
|
-
}
|
|
1631
|
-
getActionLabel(grant) {
|
|
1632
|
-
return formatDelegationActionLabel(grant.action, this.currentLanguage());
|
|
1633
|
-
}
|
|
1634
|
-
getTargetActions(targetGroup) {
|
|
1635
|
-
return targetGroup.grants;
|
|
1682
|
+
onTreeAction(event) {
|
|
1683
|
+
if (event.action !== 'selectionChange' || this.readonly()) {
|
|
1684
|
+
return;
|
|
1685
|
+
}
|
|
1686
|
+
const index = this.grantIndex();
|
|
1687
|
+
const grants = (this.treeSelection() ?? [])
|
|
1688
|
+
.map((node) => (node.key ? index.get(node.key) : undefined))
|
|
1689
|
+
.filter((grant) => !!grant);
|
|
1690
|
+
this.scope.update((scope) => ({
|
|
1691
|
+
...scope,
|
|
1692
|
+
grants,
|
|
1693
|
+
}));
|
|
1636
1694
|
}
|
|
1637
1695
|
getPreviewSummary(summary) {
|
|
1638
1696
|
return formatDelegationScopeSummary(summary, this.currentLanguage());
|
|
@@ -1643,44 +1701,53 @@ class ScopePicker {
|
|
|
1643
1701
|
formatDeniedTarget(value) {
|
|
1644
1702
|
return humanizeDelegationKey(value);
|
|
1645
1703
|
}
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1704
|
+
buildTreeNodes(groups) {
|
|
1705
|
+
return groups.map((group) => ({
|
|
1706
|
+
key: `group:${group.key}`,
|
|
1707
|
+
label: this.getGroupLabel(group),
|
|
1708
|
+
selectable: false,
|
|
1709
|
+
expanded: true,
|
|
1710
|
+
children: group.targets.map((targetGroup) => this.buildTargetNode(targetGroup)),
|
|
1711
|
+
}));
|
|
1712
|
+
}
|
|
1713
|
+
buildTargetNode(targetGroup) {
|
|
1714
|
+
return {
|
|
1715
|
+
key: `target:${targetGroup.key}`,
|
|
1716
|
+
label: this.getTargetLabel(targetGroup),
|
|
1717
|
+
selectable: false,
|
|
1718
|
+
expanded: true,
|
|
1719
|
+
children: targetGroup.grants.map((grant) => ({
|
|
1720
|
+
key: grantKey(grant),
|
|
1721
|
+
label: this.getActionLabel(grant),
|
|
1722
|
+
selectable: !this.readonly(),
|
|
1723
|
+
})),
|
|
1724
|
+
};
|
|
1657
1725
|
}
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1726
|
+
getGroupLabel(group) {
|
|
1727
|
+
return group.displayName
|
|
1728
|
+
? humanizeDelegationDisplay(group.displayName, this.currentLanguage())
|
|
1729
|
+
: humanizeDelegationKey(group.groupType);
|
|
1662
1730
|
}
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1731
|
+
getTargetLabel(targetGroup) {
|
|
1732
|
+
return formatDelegationTargetLabel(targetGroup.target, this.currentLanguage());
|
|
1733
|
+
}
|
|
1734
|
+
getActionLabel(grant) {
|
|
1735
|
+
return formatDelegationActionLabel(grant.action, this.currentLanguage());
|
|
1667
1736
|
}
|
|
1668
1737
|
currentLanguage() {
|
|
1669
|
-
return this.transloco.getActiveLang();
|
|
1738
|
+
return this.activeLang() ?? this.transloco.getActiveLang();
|
|
1670
1739
|
}
|
|
1671
1740
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: ScopePicker, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
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=\"
|
|
1741
|
+
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 }, adminMode: { classPropertyName: "adminMode", publicName: "adminMode", 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=\"flex flex-col gap-4\">\n @if (isLoadingOptions()) {\n <p-skeleton height=\"2rem\"></p-skeleton>\n <p-skeleton height=\"8rem\"></p-skeleton>\n <p-skeleton height=\"8rem\"></p-skeleton>\n } @else {\n <section class=\"flex flex-col gap-3\">\n @if (errorOptions(); as errorMessage) {\n <div\n class=\"rounded-xl 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 <div\n class=\"rounded-xl border border-dashed border-surface-300 bg-surface-50 px-4 py-3 text-sm text-surface-500\"\n >\n {{ t(\"delegations.scope.noGrantableOptions\") }}\n </div>\n }\n\n @if (groups().length > 0) {\n <div class=\"rounded-2xl border border-surface-200 bg-surface-0 p-2\">\n <mt-tree\n [value]=\"treeNodes()\"\n [(selection)]=\"treeSelection\"\n selectionMode=\"checkbox\"\n [loading]=\"isLoadingOptions()\"\n [checkAllChildren]=\"!readonly()\"\n (action)=\"onTreeAction($event)\"\n [filter]=\"true\"\n filterMode=\"lenient\"\n scrollHeight=\"22rem\"\n [virtualScroll]=\"true\"\n [virtualScrollItemSize]=\"68\"\n />\n </div>\n }\n </section>\n\n <section\n class=\"flex flex-col gap-2 rounded-2xl border border-dashed border-surface-300 bg-surface-50 px-4 py-3\"\n >\n <div class=\"flex items-center justify-between gap-2\">\n <div class=\"text-sm font-medium text-surface-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-surface-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-surface-500\">\n {{ t(\"delegations.scope.noSelection\") }}\n </div>\n }\n </section>\n }\n </div>\n</ng-container>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: Tree, selector: "mt-tree", inputs: ["value", "selection", "selectionMode", "nodeIcon", "propagateSelectionUp", "propagateSelectionDown", "checkAllChildren", "loading", "emptyMessage", "checkAllLabel", "filterPlaceholder", "dataKey", "filter", "filterMode", "virtualScroll", "virtualScrollItemSize", "scrollHeight", "styleClass", "style", "pInputs", "nodeActions", "nodeContextmenuActions", "contextMenuSelection"], outputs: ["selectionChange", "contextMenuSelectionChange", "action"] }, { kind: "ngmodule", type: SkeletonModule }, { kind: "component", type: i1.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "shape", "animation", "borderRadius", "size", "width", "height"] }, { kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1673
1742
|
}
|
|
1674
1743
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: ScopePicker, decorators: [{
|
|
1675
1744
|
type: Component,
|
|
1676
|
-
args: [{ selector: 'mt-scope-picker',
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
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"] }]
|
|
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 }] }] } });
|
|
1745
|
+
args: [{ selector: 'mt-scope-picker', imports: [CommonModule, Tree, SkeletonModule, TranslocoDirective], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-container *transloco=\"let t\">\n <div class=\"flex flex-col gap-4\">\n @if (isLoadingOptions()) {\n <p-skeleton height=\"2rem\"></p-skeleton>\n <p-skeleton height=\"8rem\"></p-skeleton>\n <p-skeleton height=\"8rem\"></p-skeleton>\n } @else {\n <section class=\"flex flex-col gap-3\">\n @if (errorOptions(); as errorMessage) {\n <div\n class=\"rounded-xl 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 <div\n class=\"rounded-xl border border-dashed border-surface-300 bg-surface-50 px-4 py-3 text-sm text-surface-500\"\n >\n {{ t(\"delegations.scope.noGrantableOptions\") }}\n </div>\n }\n\n @if (groups().length > 0) {\n <div class=\"rounded-2xl border border-surface-200 bg-surface-0 p-2\">\n <mt-tree\n [value]=\"treeNodes()\"\n [(selection)]=\"treeSelection\"\n selectionMode=\"checkbox\"\n [loading]=\"isLoadingOptions()\"\n [checkAllChildren]=\"!readonly()\"\n (action)=\"onTreeAction($event)\"\n [filter]=\"true\"\n filterMode=\"lenient\"\n scrollHeight=\"22rem\"\n [virtualScroll]=\"true\"\n [virtualScrollItemSize]=\"68\"\n />\n </div>\n }\n </section>\n\n <section\n class=\"flex flex-col gap-2 rounded-2xl border border-dashed border-surface-300 bg-surface-50 px-4 py-3\"\n >\n <div class=\"flex items-center justify-between gap-2\">\n <div class=\"text-sm font-medium text-surface-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-surface-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-surface-500\">\n {{ t(\"delegations.scope.noSelection\") }}\n </div>\n }\n </section>\n }\n </div>\n</ng-container>\n" }]
|
|
1746
|
+
}], 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 }] }], adminMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "adminMode", required: false }] }], delegatorUserId: [{ type: i0.Input, args: [{ isSignal: true, alias: "delegatorUserId", required: false }] }] } });
|
|
1747
|
+
function humanizeDelegationDisplay(value, language) {
|
|
1748
|
+
return (formatDelegationScopeSummary(value, language) ||
|
|
1749
|
+
humanizeDelegationKey(typeof value === 'string' ? value : null));
|
|
1750
|
+
}
|
|
1684
1751
|
|
|
1685
1752
|
const EMPTY_SCOPE = { grants: [], metadata: {} };
|
|
1686
1753
|
function getCurrentDelegatorFromLocalStorage() {
|
|
@@ -1953,7 +2020,7 @@ class DelegationForm {
|
|
|
1953
2020
|
ngOnInit() {
|
|
1954
2021
|
const editing = this.delegationForEdit();
|
|
1955
2022
|
if (editing) {
|
|
1956
|
-
this.facade.getDetail(editing.delegationId);
|
|
2023
|
+
this.facade.getDetail(editing.delegationId, this.adminMode());
|
|
1957
2024
|
}
|
|
1958
2025
|
}
|
|
1959
2026
|
canSubmit = computed(() => !this.readonly() &&
|
|
@@ -1966,23 +2033,59 @@ class DelegationForm {
|
|
|
1966
2033
|
return;
|
|
1967
2034
|
if (!this.canSubmit())
|
|
1968
2035
|
return;
|
|
1969
|
-
const
|
|
2036
|
+
const value = this.delegationFormControl.getRawValue();
|
|
2037
|
+
const scope = this.scope();
|
|
2038
|
+
const delegationDaysType = (value?.delegationDaysType ?? 'FullRange');
|
|
2039
|
+
const specificDays = delegationDaysType === 'SpecificDays' ? (value?.specificDays ?? []) : [];
|
|
2040
|
+
const description = value?.description;
|
|
2041
|
+
const delegateFromDateTime = value?.delegateFromDateTime;
|
|
2042
|
+
const delegateToDateTime = value?.delegateToDateTime;
|
|
2043
|
+
const requiresApproval = !!value?.requiresApproval;
|
|
2044
|
+
const delegatedUserId = extractUserId(value?.delegateTo) ?? '';
|
|
2045
|
+
const selectedDelegatorId = this.selectedDelegatorId();
|
|
2046
|
+
const editing = this.delegationForEdit();
|
|
2047
|
+
if (this.adminMode()) {
|
|
2048
|
+
if (!selectedDelegatorId || !delegatedUserId) {
|
|
2049
|
+
return;
|
|
2050
|
+
}
|
|
2051
|
+
const base = {
|
|
2052
|
+
delegatorUserId: selectedDelegatorId,
|
|
2053
|
+
delegatedUserId,
|
|
2054
|
+
description,
|
|
2055
|
+
delegateFromDateTime,
|
|
2056
|
+
delegateToDateTime,
|
|
2057
|
+
delegationDaysType,
|
|
2058
|
+
specificDays,
|
|
2059
|
+
requiresApproval,
|
|
2060
|
+
scope,
|
|
2061
|
+
};
|
|
2062
|
+
if (editing) {
|
|
2063
|
+
const latestRowVersion = this.detail()?.row?.rowVersion ?? editing.rowVersion;
|
|
2064
|
+
const req = {
|
|
2065
|
+
...base,
|
|
2066
|
+
rowVersion: latestRowVersion,
|
|
2067
|
+
};
|
|
2068
|
+
this.facade.updateV2(editing.delegationId, req, true).subscribe({
|
|
2069
|
+
next: () => this.ref.close(true),
|
|
2070
|
+
});
|
|
2071
|
+
return;
|
|
2072
|
+
}
|
|
2073
|
+
this.facade.createV2(base, true).subscribe({
|
|
2074
|
+
next: () => this.ref.close(true),
|
|
2075
|
+
});
|
|
2076
|
+
return;
|
|
2077
|
+
}
|
|
1970
2078
|
const base = {
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
delegationDaysType: (v?.delegationDaysType ?? 'FullRange'),
|
|
1981
|
-
specificDays: v?.delegationDaysType === 'SpecificDays' ? (v?.specificDays ?? []) : [],
|
|
1982
|
-
requiresApproval: !!v?.requiresApproval,
|
|
1983
|
-
scope: this.scope(),
|
|
2079
|
+
delegateFrom: null,
|
|
2080
|
+
delegateTo: delegatedUserId,
|
|
2081
|
+
description,
|
|
2082
|
+
delegateFromDateTime,
|
|
2083
|
+
delegateToDateTime,
|
|
2084
|
+
delegationDaysType,
|
|
2085
|
+
specificDays,
|
|
2086
|
+
requiresApproval,
|
|
2087
|
+
scope,
|
|
1984
2088
|
};
|
|
1985
|
-
const editing = this.delegationForEdit();
|
|
1986
2089
|
if (editing) {
|
|
1987
2090
|
// Prefer the freshest rowVersion from the loaded detail (the form loads
|
|
1988
2091
|
// detail on open); fall back to the list row only if detail is absent.
|
|
@@ -2014,7 +2117,7 @@ class DelegationForm {
|
|
|
2014
2117
|
};
|
|
2015
2118
|
}
|
|
2016
2119
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: DelegationForm, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
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 '
|
|
2120
|
+
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 'flex h-full min-h-0 min-w-0 flex-col overflow-hidden ' +\n modal.contentClass\n \"\n >\n <div\n class=\"flex min-h-0 flex-1 flex-col gap-4 overflow-y-auto p-4 max-[640px]:p-3\"\n >\n <section\n class=\"flex flex-col gap-4 rounded-2xl border border-surface-200 bg-surface-0 p-4\"\n >\n <mt-dynamic-form\n [formConfig]=\"formConfig()\"\n [formControl]=\"delegationFormControl\"\n />\n </section>\n\n <section\n class=\"flex flex-col gap-4 rounded-2xl border border-surface-200 bg-surface-0 p-4\"\n >\n <div class=\"flex flex-col gap-1\">\n <h3 class=\"text-sm font-semibold text-surface-900\">\n {{ t(\"delegations.scope.permissionsTitle\") }}\n </h3>\n @if (adminMode() && !showScopePicker()) {\n <p class=\"text-xs text-surface-500\">\n {{ t(\"delegations.form.selectDelegatorFirst\") }}\n </p>\n }\n </div>\n\n @if (showScopePicker()) {\n <mt-scope-picker\n [(scope)]=\"scope\"\n [readonly]=\"readonly()\"\n [adminMode]=\"adminMode()\"\n [delegatorUserId]=\"selectedDelegatorId()\"\n ></mt-scope-picker>\n } @else {\n <div\n class=\"rounded-xl border border-dashed border-surface-300 bg-surface-50 px-4 py-3 text-sm text-surface-500\"\n >\n {{ t(\"delegations.form.selectDelegatorFirst\") }}\n </div>\n }\n </section>\n </div>\n\n <div\n [class]=\"\n 'flex shrink-0 flex-col gap-2 sm:flex-row sm:items-center sm:justify-end ' +\n modal.footerClass\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 </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: "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", "adminMode", "delegatorUserId"], outputs: ["scopeChange"] }, { kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
2018
2121
|
}
|
|
2019
2122
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: DelegationForm, decorators: [{
|
|
2020
2123
|
type: Component,
|
|
@@ -2025,7 +2128,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
|
|
|
2025
2128
|
ReactiveFormsModule,
|
|
2026
2129
|
ScopePicker,
|
|
2027
2130
|
TranslocoDirective,
|
|
2028
|
-
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-container *transloco=\"let t\">\n <div\n [class]=\"\n '
|
|
2131
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-container *transloco=\"let t\">\n <div\n [class]=\"\n 'flex h-full min-h-0 min-w-0 flex-col overflow-hidden ' +\n modal.contentClass\n \"\n >\n <div\n class=\"flex min-h-0 flex-1 flex-col gap-4 overflow-y-auto p-4 max-[640px]:p-3\"\n >\n <section\n class=\"flex flex-col gap-4 rounded-2xl border border-surface-200 bg-surface-0 p-4\"\n >\n <mt-dynamic-form\n [formConfig]=\"formConfig()\"\n [formControl]=\"delegationFormControl\"\n />\n </section>\n\n <section\n class=\"flex flex-col gap-4 rounded-2xl border border-surface-200 bg-surface-0 p-4\"\n >\n <div class=\"flex flex-col gap-1\">\n <h3 class=\"text-sm font-semibold text-surface-900\">\n {{ t(\"delegations.scope.permissionsTitle\") }}\n </h3>\n @if (adminMode() && !showScopePicker()) {\n <p class=\"text-xs text-surface-500\">\n {{ t(\"delegations.form.selectDelegatorFirst\") }}\n </p>\n }\n </div>\n\n @if (showScopePicker()) {\n <mt-scope-picker\n [(scope)]=\"scope\"\n [readonly]=\"readonly()\"\n [adminMode]=\"adminMode()\"\n [delegatorUserId]=\"selectedDelegatorId()\"\n ></mt-scope-picker>\n } @else {\n <div\n class=\"rounded-xl border border-dashed border-surface-300 bg-surface-50 px-4 py-3 text-sm text-surface-500\"\n >\n {{ t(\"delegations.form.selectDelegatorFirst\") }}\n </div>\n }\n </section>\n </div>\n\n <div\n [class]=\"\n 'flex shrink-0 flex-col gap-2 sm:flex-row sm:items-center sm:justify-end ' +\n modal.footerClass\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 </div>\n</ng-container>\n" }]
|
|
2029
2132
|
}], 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 }] }] } });
|
|
2030
2133
|
|
|
2031
2134
|
/**
|
|
@@ -2033,6 +2136,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
|
|
|
2033
2136
|
*/
|
|
2034
2137
|
class DelegationDetailDrawer {
|
|
2035
2138
|
delegationId = input.required(...(ngDevMode ? [{ debugName: "delegationId" }] : /* istanbul ignore next */ []));
|
|
2139
|
+
adminMode = input(false, ...(ngDevMode ? [{ debugName: "adminMode" }] : /* istanbul ignore next */ []));
|
|
2036
2140
|
facade = inject(DelegationsFacade);
|
|
2037
2141
|
transloco = inject(TranslocoService);
|
|
2038
2142
|
ref = inject(ModalRef);
|
|
@@ -2045,7 +2149,7 @@ class DelegationDetailDrawer {
|
|
|
2045
2149
|
detail = this.facade.detail;
|
|
2046
2150
|
row = computed(() => this.detail()?.row ?? null, ...(ngDevMode ? [{ debugName: "row" }] : /* istanbul ignore next */ []));
|
|
2047
2151
|
ngOnInit() {
|
|
2048
|
-
this.facade.getDetail(this.delegationId());
|
|
2152
|
+
this.facade.getDetail(this.delegationId(), this.adminMode());
|
|
2049
2153
|
}
|
|
2050
2154
|
setTab(tab) {
|
|
2051
2155
|
this.activeTab.set(tab);
|
|
@@ -2064,7 +2168,7 @@ class DelegationDetailDrawer {
|
|
|
2064
2168
|
return formatDelegationScopeSummary(summary, this.transloco.getActiveLang());
|
|
2065
2169
|
}
|
|
2066
2170
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: DelegationDetailDrawer, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2067
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: DelegationDetailDrawer, isStandalone: true, selector: "mt-delegation-detail-drawer", inputs: { delegationId: { classPropertyName: "delegationId", publicName: "delegationId", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<ng-container *transloco=\"let t\">\n <div class=\"flex flex-col h-full\">\n <!-- Tabs -->\n <div class=\"flex border-b border-gray-200 px-4\">\n @for (tab of tabs; track tab.key) {\n <button\n type=\"button\"\n class=\"px-3 py-2 -mb-px border-b-2 text-sm font-medium transition-colors\"\n [class.border-primary]=\"activeTab() === tab.key\"\n [class.text-primary]=\"activeTab() === tab.key\"\n [class.border-transparent]=\"activeTab() !== tab.key\"\n [class.text-gray-500]=\"activeTab() !== tab.key\"\n (click)=\"setTab(tab.key)\"\n >\n {{ t(tab.i18n) }}\n </button>\n }\n </div>\n\n <!-- Body -->\n <div class=\"flex-1 overflow-y-auto p-4\">\n @if (isLoading() && !detail()) {\n <p-skeleton height=\"2rem\" class=\"mb-3\"></p-skeleton>\n <p-skeleton height=\"6rem\" class=\"mb-3\"></p-skeleton>\n <p-skeleton height=\"6rem\"></p-skeleton>\n } @else if (detail(); as d) {\n @if (activeTab() === \"overview\") {\n <div class=\"flex flex-col gap-4\">\n <div class=\"flex items-center justify-between gap-3\">\n <div class=\"flex items-center gap-3 min-w-0\">\n <mt-avatar\n icon=\"user.user-01\"\n styleClass=\"w-12! h-12! text-lg!\"\n ></mt-avatar>\n <div class=\"flex flex-col min-w-0\">\n <div class=\"text-xs text-gray-500\">\n {{ t(\"delegations.column.delegatorName\") }}\n </div>\n <div class=\"text-base font-medium text-gray-900 truncate\">\n {{ d.row.delegator.displayName }}\n </div>\n @if (d.row.delegator.email) {\n <div class=\"text-xs text-gray-500 truncate\">\n {{ d.row.delegator.email }}\n </div>\n }\n </div>\n </div>\n <mt-delegation-status-chip\n [status]=\"d.status.effectiveStatus\"\n ></mt-delegation-status-chip>\n </div>\n\n <div class=\"grid grid-cols-2 gap-4\">\n <div class=\"flex flex-col\">\n <span class=\"text-xs text-gray-500\">\n {{ t(\"delegations.column.delegatedTo\") }}\n </span>\n <span class=\"text-sm text-gray-900\">\n {{ d.row.delegatedUser.displayName }}\n </span>\n </div>\n <div class=\"flex flex-col\">\n <span class=\"text-xs text-gray-500\">\n {{ t(\"delegations.column.approval\") }}\n </span>\n <span class=\"text-sm text-gray-900\">{{ approvalLabel() }}</span>\n </div>\n <div class=\"flex flex-col\">\n <span class=\"text-xs text-gray-500\">\n {{ t(\"delegations.column.startDate\") }}\n </span>\n <span class=\"text-sm text-gray-900\">\n {{ d.row.startsAtUtc | date: \"medium\" }}\n </span>\n </div>\n <div class=\"flex flex-col\">\n <span class=\"text-xs text-gray-500\">\n {{ t(\"delegations.column.endDate\") }}\n </span>\n <span class=\"text-sm text-gray-900\">\n {{ d.row.endsAtUtc | date: \"medium\" }}\n </span>\n </div>\n <div class=\"flex flex-col\">\n <span class=\"text-xs text-gray-500\">\n {{ t(\"delegations.form.timeZone\") }}\n </span>\n <span class=\"text-sm text-gray-900\">{{\n d.row.timeZoneId\n }}</span>\n </div>\n <div class=\"flex flex-col\">\n <span class=\"text-xs text-gray-500\">\n {{ t(\"delegations.column.days\") }}\n </span>\n <span class=\"text-sm text-gray-900\">\n @if (d.row.dayRuleMode === \"FullRange\") {\n {{ t(\"delegations.form.fullRange\") }}\n } @else {\n {{ d.row.specificDays.join(\", \") }}\n }\n </span>\n </div>\n </div>\n\n @if (d.row.cancellation?.cancellationReason) {\n <div\n class=\"rounded-md border border-gray-200 bg-surface-50 p-3 text-sm text-gray-800\"\n >\n <div class=\"font-medium\">\n {{ t(\"delegations.form.cancellationReason\") }}\n </div>\n <div>{{ d.row.cancellation.cancellationReason }}</div>\n </div>\n }\n </div>\n } @else {\n <!-- Scope tab -->\n <div class=\"flex flex-col gap-3\">\n <div class=\"text-sm text-gray-900\">\n {{\n formatScopeSummary(d.row.scopeSummary) ||\n t(\"delegations.column.scopeSummary\")\n }}\n </div>\n <div class=\"flex flex-col gap-2\">\n @for (grant of d.scope.grants; track $index) {\n <div\n class=\"border border-gray-200 rounded-md px-3 py-2 flex flex-col gap-1\"\n >\n <div class=\"flex items-center justify-between gap-2\">\n <span class=\"text-sm font-medium text-gray-900\">\n {{ getScopeTargetLabel(grant.target) }}\n </span>\n <span class=\"text-xs text-gray-500\">\n {{ grant.target.targetType }}\n </span>\n </div>\n <span\n class=\"text-xs px-2 py-0.5 rounded-md bg-primary-50 text-primary w-fit\"\n >\n {{ getScopeActionLabel(grant.action) }}\n </span>\n </div>\n }\n @if (d.scope.grants.length === 0) {\n <p class=\"text-xs text-gray-500\">\n {{ t(\"delegations.scope.noSelection\") }}\n </p>\n }\n </div>\n </div>\n }\n }\n </div>\n\n <!-- Footer -->\n <div\n class=\"border-t border-gray-200 px-4 py-3 flex items-center justify-end\"\n >\n <mt-button\n [label]=\"t('delegations.common.cancel')\"\n variant=\"outlined\"\n (click)=\"ref.close()\"\n ></mt-button>\n </div>\n </div>\n</ng-container>\n", styles: [":host{display:block;height:100%}\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: "ngmodule", type: SkeletonModule }, { kind: "component", type: i1.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "shape", "animation", "borderRadius", "size", "width", "height"] }, { 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"] }, { kind: "pipe", type: i2
|
|
2171
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: DelegationDetailDrawer, isStandalone: true, selector: "mt-delegation-detail-drawer", inputs: { delegationId: { classPropertyName: "delegationId", publicName: "delegationId", isSignal: true, isRequired: true, transformFunction: null }, adminMode: { classPropertyName: "adminMode", publicName: "adminMode", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<ng-container *transloco=\"let t\">\n <div class=\"flex flex-col h-full\">\n <!-- Tabs -->\n <div class=\"flex border-b border-gray-200 px-4\">\n @for (tab of tabs; track tab.key) {\n <button\n type=\"button\"\n class=\"px-3 py-2 -mb-px border-b-2 text-sm font-medium transition-colors\"\n [class.border-primary]=\"activeTab() === tab.key\"\n [class.text-primary]=\"activeTab() === tab.key\"\n [class.border-transparent]=\"activeTab() !== tab.key\"\n [class.text-gray-500]=\"activeTab() !== tab.key\"\n (click)=\"setTab(tab.key)\"\n >\n {{ t(tab.i18n) }}\n </button>\n }\n </div>\n\n <!-- Body -->\n <div class=\"flex-1 overflow-y-auto p-4\">\n @if (isLoading() && !detail()) {\n <p-skeleton height=\"2rem\" class=\"mb-3\"></p-skeleton>\n <p-skeleton height=\"6rem\" class=\"mb-3\"></p-skeleton>\n <p-skeleton height=\"6rem\"></p-skeleton>\n } @else if (detail(); as d) {\n @if (activeTab() === \"overview\") {\n <div class=\"flex flex-col gap-4\">\n <div class=\"flex items-center justify-between gap-3\">\n <div class=\"flex items-center gap-3 min-w-0\">\n <mt-avatar\n icon=\"user.user-01\"\n styleClass=\"w-12! h-12! text-lg!\"\n ></mt-avatar>\n <div class=\"flex flex-col min-w-0\">\n <div class=\"text-xs text-gray-500\">\n {{ t(\"delegations.column.delegatorName\") }}\n </div>\n <div class=\"text-base font-medium text-gray-900 truncate\">\n {{ d.row.delegator.displayName }}\n </div>\n @if (d.row.delegator.email) {\n <div class=\"text-xs text-gray-500 truncate\">\n {{ d.row.delegator.email }}\n </div>\n }\n </div>\n </div>\n <mt-delegation-status-chip\n [status]=\"d.status.effectiveStatus\"\n ></mt-delegation-status-chip>\n </div>\n\n <div class=\"grid grid-cols-2 gap-4\">\n <div class=\"flex flex-col\">\n <span class=\"text-xs text-gray-500\">\n {{ t(\"delegations.column.delegatedTo\") }}\n </span>\n <span class=\"text-sm text-gray-900\">\n {{ d.row.delegatedUser.displayName }}\n </span>\n </div>\n <div class=\"flex flex-col\">\n <span class=\"text-xs text-gray-500\">\n {{ t(\"delegations.column.approval\") }}\n </span>\n <span class=\"text-sm text-gray-900\">{{ approvalLabel() }}</span>\n </div>\n <div class=\"flex flex-col\">\n <span class=\"text-xs text-gray-500\">\n {{ t(\"delegations.column.startDate\") }}\n </span>\n <span class=\"text-sm text-gray-900\">\n {{ d.row.startsAtUtc | date: \"medium\" }}\n </span>\n </div>\n <div class=\"flex flex-col\">\n <span class=\"text-xs text-gray-500\">\n {{ t(\"delegations.column.endDate\") }}\n </span>\n <span class=\"text-sm text-gray-900\">\n {{ d.row.endsAtUtc | date: \"medium\" }}\n </span>\n </div>\n <div class=\"flex flex-col\">\n <span class=\"text-xs text-gray-500\">\n {{ t(\"delegations.form.timeZone\") }}\n </span>\n <span class=\"text-sm text-gray-900\">{{\n d.row.timeZoneId\n }}</span>\n </div>\n <div class=\"flex flex-col\">\n <span class=\"text-xs text-gray-500\">\n {{ t(\"delegations.column.days\") }}\n </span>\n <span class=\"text-sm text-gray-900\">\n @if (d.row.dayRuleMode === \"FullRange\") {\n {{ t(\"delegations.form.fullRange\") }}\n } @else {\n {{ d.row.specificDays.join(\", \") }}\n }\n </span>\n </div>\n </div>\n\n @if (d.row.cancellation?.cancellationReason) {\n <div\n class=\"rounded-md border border-gray-200 bg-surface-50 p-3 text-sm text-gray-800\"\n >\n <div class=\"font-medium\">\n {{ t(\"delegations.form.cancellationReason\") }}\n </div>\n <div>{{ d.row.cancellation.cancellationReason }}</div>\n </div>\n }\n </div>\n } @else {\n <!-- Scope tab -->\n <div class=\"flex flex-col gap-3\">\n <div class=\"text-sm text-gray-900\">\n {{\n formatScopeSummary(d.row.scopeSummary) ||\n t(\"delegations.column.scopeSummary\")\n }}\n </div>\n <div class=\"flex flex-col gap-2\">\n @for (grant of d.scope.grants; track $index) {\n <div\n class=\"border border-gray-200 rounded-md px-3 py-2 flex flex-col gap-1\"\n >\n <div class=\"flex items-center justify-between gap-2\">\n <span class=\"text-sm font-medium text-gray-900\">\n {{ getScopeTargetLabel(grant.target) }}\n </span>\n <span class=\"text-xs text-gray-500\">\n {{ grant.target.targetType }}\n </span>\n </div>\n <span\n class=\"text-xs px-2 py-0.5 rounded-md bg-primary-50 text-primary w-fit\"\n >\n {{ getScopeActionLabel(grant.action) }}\n </span>\n </div>\n }\n @if (d.scope.grants.length === 0) {\n <p class=\"text-xs text-gray-500\">\n {{ t(\"delegations.scope.noSelection\") }}\n </p>\n }\n </div>\n </div>\n }\n }\n </div>\n\n <!-- Footer -->\n <div\n class=\"border-t border-gray-200 px-4 py-3 flex items-center justify-end\"\n >\n <mt-button\n [label]=\"t('delegations.common.cancel')\"\n variant=\"outlined\"\n (click)=\"ref.close()\"\n ></mt-button>\n </div>\n </div>\n</ng-container>\n", styles: [":host{display:block;height:100%}\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: "ngmodule", type: SkeletonModule }, { kind: "component", type: i1.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "shape", "animation", "borderRadius", "size", "width", "height"] }, { 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"] }, { kind: "pipe", type: i2.DatePipe, name: "date" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
2068
2172
|
}
|
|
2069
2173
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: DelegationDetailDrawer, decorators: [{
|
|
2070
2174
|
type: Component,
|
|
@@ -2076,7 +2180,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
|
|
|
2076
2180
|
TranslocoDirective,
|
|
2077
2181
|
DelegationStatusChip,
|
|
2078
2182
|
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-container *transloco=\"let t\">\n <div class=\"flex flex-col h-full\">\n <!-- Tabs -->\n <div class=\"flex border-b border-gray-200 px-4\">\n @for (tab of tabs; track tab.key) {\n <button\n type=\"button\"\n class=\"px-3 py-2 -mb-px border-b-2 text-sm font-medium transition-colors\"\n [class.border-primary]=\"activeTab() === tab.key\"\n [class.text-primary]=\"activeTab() === tab.key\"\n [class.border-transparent]=\"activeTab() !== tab.key\"\n [class.text-gray-500]=\"activeTab() !== tab.key\"\n (click)=\"setTab(tab.key)\"\n >\n {{ t(tab.i18n) }}\n </button>\n }\n </div>\n\n <!-- Body -->\n <div class=\"flex-1 overflow-y-auto p-4\">\n @if (isLoading() && !detail()) {\n <p-skeleton height=\"2rem\" class=\"mb-3\"></p-skeleton>\n <p-skeleton height=\"6rem\" class=\"mb-3\"></p-skeleton>\n <p-skeleton height=\"6rem\"></p-skeleton>\n } @else if (detail(); as d) {\n @if (activeTab() === \"overview\") {\n <div class=\"flex flex-col gap-4\">\n <div class=\"flex items-center justify-between gap-3\">\n <div class=\"flex items-center gap-3 min-w-0\">\n <mt-avatar\n icon=\"user.user-01\"\n styleClass=\"w-12! h-12! text-lg!\"\n ></mt-avatar>\n <div class=\"flex flex-col min-w-0\">\n <div class=\"text-xs text-gray-500\">\n {{ t(\"delegations.column.delegatorName\") }}\n </div>\n <div class=\"text-base font-medium text-gray-900 truncate\">\n {{ d.row.delegator.displayName }}\n </div>\n @if (d.row.delegator.email) {\n <div class=\"text-xs text-gray-500 truncate\">\n {{ d.row.delegator.email }}\n </div>\n }\n </div>\n </div>\n <mt-delegation-status-chip\n [status]=\"d.status.effectiveStatus\"\n ></mt-delegation-status-chip>\n </div>\n\n <div class=\"grid grid-cols-2 gap-4\">\n <div class=\"flex flex-col\">\n <span class=\"text-xs text-gray-500\">\n {{ t(\"delegations.column.delegatedTo\") }}\n </span>\n <span class=\"text-sm text-gray-900\">\n {{ d.row.delegatedUser.displayName }}\n </span>\n </div>\n <div class=\"flex flex-col\">\n <span class=\"text-xs text-gray-500\">\n {{ t(\"delegations.column.approval\") }}\n </span>\n <span class=\"text-sm text-gray-900\">{{ approvalLabel() }}</span>\n </div>\n <div class=\"flex flex-col\">\n <span class=\"text-xs text-gray-500\">\n {{ t(\"delegations.column.startDate\") }}\n </span>\n <span class=\"text-sm text-gray-900\">\n {{ d.row.startsAtUtc | date: \"medium\" }}\n </span>\n </div>\n <div class=\"flex flex-col\">\n <span class=\"text-xs text-gray-500\">\n {{ t(\"delegations.column.endDate\") }}\n </span>\n <span class=\"text-sm text-gray-900\">\n {{ d.row.endsAtUtc | date: \"medium\" }}\n </span>\n </div>\n <div class=\"flex flex-col\">\n <span class=\"text-xs text-gray-500\">\n {{ t(\"delegations.form.timeZone\") }}\n </span>\n <span class=\"text-sm text-gray-900\">{{\n d.row.timeZoneId\n }}</span>\n </div>\n <div class=\"flex flex-col\">\n <span class=\"text-xs text-gray-500\">\n {{ t(\"delegations.column.days\") }}\n </span>\n <span class=\"text-sm text-gray-900\">\n @if (d.row.dayRuleMode === \"FullRange\") {\n {{ t(\"delegations.form.fullRange\") }}\n } @else {\n {{ d.row.specificDays.join(\", \") }}\n }\n </span>\n </div>\n </div>\n\n @if (d.row.cancellation?.cancellationReason) {\n <div\n class=\"rounded-md border border-gray-200 bg-surface-50 p-3 text-sm text-gray-800\"\n >\n <div class=\"font-medium\">\n {{ t(\"delegations.form.cancellationReason\") }}\n </div>\n <div>{{ d.row.cancellation.cancellationReason }}</div>\n </div>\n }\n </div>\n } @else {\n <!-- Scope tab -->\n <div class=\"flex flex-col gap-3\">\n <div class=\"text-sm text-gray-900\">\n {{\n formatScopeSummary(d.row.scopeSummary) ||\n t(\"delegations.column.scopeSummary\")\n }}\n </div>\n <div class=\"flex flex-col gap-2\">\n @for (grant of d.scope.grants; track $index) {\n <div\n class=\"border border-gray-200 rounded-md px-3 py-2 flex flex-col gap-1\"\n >\n <div class=\"flex items-center justify-between gap-2\">\n <span class=\"text-sm font-medium text-gray-900\">\n {{ getScopeTargetLabel(grant.target) }}\n </span>\n <span class=\"text-xs text-gray-500\">\n {{ grant.target.targetType }}\n </span>\n </div>\n <span\n class=\"text-xs px-2 py-0.5 rounded-md bg-primary-50 text-primary w-fit\"\n >\n {{ getScopeActionLabel(grant.action) }}\n </span>\n </div>\n }\n @if (d.scope.grants.length === 0) {\n <p class=\"text-xs text-gray-500\">\n {{ t(\"delegations.scope.noSelection\") }}\n </p>\n }\n </div>\n </div>\n }\n }\n </div>\n\n <!-- Footer -->\n <div\n class=\"border-t border-gray-200 px-4 py-3 flex items-center justify-end\"\n >\n <mt-button\n [label]=\"t('delegations.common.cancel')\"\n variant=\"outlined\"\n (click)=\"ref.close()\"\n ></mt-button>\n </div>\n </div>\n</ng-container>\n", styles: [":host{display:block;height:100%}\n"] }]
|
|
2079
|
-
}], propDecorators: { delegationId: [{ type: i0.Input, args: [{ isSignal: true, alias: "delegationId", required: true }] }] } });
|
|
2183
|
+
}], propDecorators: { delegationId: [{ type: i0.Input, args: [{ isSignal: true, alias: "delegationId", required: true }] }], adminMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "adminMode", required: false }] }] } });
|
|
2080
2184
|
|
|
2081
2185
|
/**
|
|
2082
2186
|
* Delegations portal page (doc 02, 09): two lists — My Delegations (current user
|
|
@@ -2104,6 +2208,7 @@ class DelegationsList {
|
|
|
2104
2208
|
assignedEmptyStateKey = input('delegations.empty.assigned', ...(ngDevMode ? [{ debugName: "assignedEmptyStateKey" }] : /* istanbul ignore next */ []));
|
|
2105
2209
|
assignedStatus = input(null, ...(ngDevMode ? [{ debugName: "assignedStatus" }] : /* istanbul ignore next */ []));
|
|
2106
2210
|
facade = inject(DelegationsFacade);
|
|
2211
|
+
location = inject(Location);
|
|
2107
2212
|
modal = inject(ModalService);
|
|
2108
2213
|
transloco = inject(TranslocoService);
|
|
2109
2214
|
breadcrumbItems = linkedSignal(() => [
|
|
@@ -2139,6 +2244,7 @@ class DelegationsList {
|
|
|
2139
2244
|
: this.activeTab() === 'my'
|
|
2140
2245
|
? this.facade.isLoadingMy()
|
|
2141
2246
|
: this.facade.isLoadingAssigned(), ...(ngDevMode ? [{ debugName: "isLoading" }] : /* istanbul ignore next */ []));
|
|
2247
|
+
isLoadingScopeOptions = this.facade.isLoadingScopeOptions;
|
|
2142
2248
|
scopeOptions = this.facade.scopeOptions;
|
|
2143
2249
|
rows = computed(() => this.adminMode()
|
|
2144
2250
|
? this.facade.adminItems()
|
|
@@ -2159,6 +2265,8 @@ class DelegationsList {
|
|
|
2159
2265
|
canCreate = computed(() => {
|
|
2160
2266
|
if (this.adminMode())
|
|
2161
2267
|
return true;
|
|
2268
|
+
if (this.isLoadingScopeOptions())
|
|
2269
|
+
return true;
|
|
2162
2270
|
return hasDelegationGrantableScope(this.scopeOptions());
|
|
2163
2271
|
}, ...(ngDevMode ? [{ debugName: "canCreate" }] : /* istanbul ignore next */ []));
|
|
2164
2272
|
tableActions = computed(() => this.canCreate()
|
|
@@ -2317,6 +2425,9 @@ class DelegationsList {
|
|
|
2317
2425
|
this.activeTab.set(tab);
|
|
2318
2426
|
this.loadCurrentTab();
|
|
2319
2427
|
}
|
|
2428
|
+
goBack() {
|
|
2429
|
+
this.location.back();
|
|
2430
|
+
}
|
|
2320
2431
|
loadCurrentTab(query = { page: 1, pageSize: 25 }) {
|
|
2321
2432
|
if (this.adminMode()) {
|
|
2322
2433
|
this.facade.getAdmin(query);
|
|
@@ -2361,7 +2472,10 @@ class DelegationsList {
|
|
|
2361
2472
|
appendTo: 'page-content',
|
|
2362
2473
|
dismissableMask: true,
|
|
2363
2474
|
dismissible: true,
|
|
2364
|
-
inputValues: {
|
|
2475
|
+
inputValues: {
|
|
2476
|
+
delegationId: row.delegationId,
|
|
2477
|
+
adminMode: this.adminMode(),
|
|
2478
|
+
},
|
|
2365
2479
|
});
|
|
2366
2480
|
}
|
|
2367
2481
|
openForm(row) {
|
|
@@ -2369,7 +2483,7 @@ class DelegationsList {
|
|
|
2369
2483
|
header: this.transloco.translate(row ? 'delegations.action.edit' : 'delegations.action.create'),
|
|
2370
2484
|
styleClass: row
|
|
2371
2485
|
? '!absolute !shadow-none !w-full !max-w-full sm:!w-[42rem] xl:!w-[50rem]'
|
|
2372
|
-
: '!w-[min(96vw,
|
|
2486
|
+
: '!w-[min(96vw,52rem)] !max-w-[96vw] !h-[min(92vh,46rem)] !max-h-[92vh] !overflow-hidden',
|
|
2373
2487
|
position: row ? 'end' : '',
|
|
2374
2488
|
appendTo: row ? 'page-content' : 'body',
|
|
2375
2489
|
dismissableMask: true,
|
|
@@ -2429,7 +2543,7 @@ class DelegationsList {
|
|
|
2429
2543
|
this.busyIds.update((ids) => on ? [...ids, key] : ids.filter((id) => id !== key));
|
|
2430
2544
|
}
|
|
2431
2545
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: DelegationsList, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
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 });
|
|
2546
|
+
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 <ng-template #tableContent>\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 </ng-template>\n\n @if (adminMode()) {\n <mt-page\n [title]=\"surfaceTitle()\"\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 <div class=\"flex min-h-full flex-col gap-4\">\n @if (showBreadcrumb()) {\n <mt-breadcrumb [items]=\"breadcrumbItems()\"></mt-breadcrumb>\n }\n <ng-container *ngTemplateOutlet=\"tableContent\"></ng-container>\n </div>\n </mt-page>\n } @else {\n <div class=\"flex h-full flex-col gap-4 p-4\">\n <mt-card [title]=\"surfaceTitle()\">\n <ng-container *ngTemplateOutlet=\"tableContent\"></ng-container>\n </mt-card>\n </div>\n }\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 .mt-actions-column{min-width:14rem}:host ::ng-deep mt-table td.mt-actions-column>div{flex-wrap:nowrap;justify-content:flex-end;white-space:nowrap}: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: "directive", type: i2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { 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: Page, selector: "mt-page", inputs: ["backButton", "backButtonIcon", "avatarIcon", "avatarStyle", "avatarShape", "title", "tabs", "activeTab", "contentClass", "contentId"], outputs: ["backButtonClick", "tabChange"] }, { 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 });
|
|
2433
2547
|
}
|
|
2434
2548
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: DelegationsList, decorators: [{
|
|
2435
2549
|
type: Component,
|
|
@@ -2438,10 +2552,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
|
|
|
2438
2552
|
Avatar,
|
|
2439
2553
|
Breadcrumb,
|
|
2440
2554
|
Card,
|
|
2555
|
+
Page,
|
|
2441
2556
|
Table,
|
|
2442
2557
|
TranslocoDirective,
|
|
2443
2558
|
DelegationStatusChip,
|
|
2444
|
-
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-container *transloco=\"let t\">\n <
|
|
2559
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-container *transloco=\"let t\">\n <ng-template #tableContent>\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 </ng-template>\n\n @if (adminMode()) {\n <mt-page\n [title]=\"surfaceTitle()\"\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 <div class=\"flex min-h-full flex-col gap-4\">\n @if (showBreadcrumb()) {\n <mt-breadcrumb [items]=\"breadcrumbItems()\"></mt-breadcrumb>\n }\n <ng-container *ngTemplateOutlet=\"tableContent\"></ng-container>\n </div>\n </mt-page>\n } @else {\n <div class=\"flex h-full flex-col gap-4 p-4\">\n <mt-card [title]=\"surfaceTitle()\">\n <ng-container *ngTemplateOutlet=\"tableContent\"></ng-container>\n </mt-card>\n </div>\n }\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 .mt-actions-column{min-width:14rem}:host ::ng-deep mt-table td.mt-actions-column>div{flex-wrap:nowrap;justify-content:flex-end;white-space:nowrap}: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
2560
|
}], 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 }] }] } });
|
|
2446
2561
|
|
|
2447
2562
|
/**
|