@masterteam/delegations 0.0.23 → 0.0.25

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.
@@ -557,9 +557,11 @@ class GetActiveAssignedDelegations {
557
557
  }
558
558
  class GetDelegationDetail {
559
559
  id;
560
+ asAdmin;
560
561
  static type = '[Delegations] Get Delegation Detail';
561
- constructor(id) {
562
+ constructor(id, asAdmin = false) {
562
563
  this.id = id;
564
+ this.asAdmin = asAdmin;
563
565
  }
564
566
  }
565
567
  class ClearDelegationDetail {
@@ -570,16 +572,20 @@ class ClearDelegationDetail {
570
572
  // ---------------------------------------------------------------------------
571
573
  class GetScopeOptions {
572
574
  delegatorUserId;
575
+ asAdmin;
573
576
  static type = '[Delegations] Get Scope Options';
574
- constructor(delegatorUserId) {
577
+ constructor(delegatorUserId, asAdmin = false) {
575
578
  this.delegatorUserId = delegatorUserId;
579
+ this.asAdmin = asAdmin;
576
580
  }
577
581
  }
578
582
  class PreviewScope {
579
- scope;
583
+ request;
584
+ asAdmin;
580
585
  static type = '[Delegations] Preview Scope';
581
- constructor(scope) {
582
- this.scope = scope;
586
+ constructor(request, asAdmin = false) {
587
+ this.request = request;
588
+ this.asAdmin = asAdmin;
583
589
  }
584
590
  }
585
591
  class ClearScopePreview {
@@ -597,9 +603,11 @@ class CreateDelegationLegacy {
597
603
  }
598
604
  class CreateDelegationV2 {
599
605
  request;
606
+ asAdmin;
600
607
  static type = '[Delegations] Create Delegation (v2)';
601
- constructor(request) {
608
+ constructor(request, asAdmin = false) {
602
609
  this.request = request;
610
+ this.asAdmin = asAdmin;
603
611
  }
604
612
  }
605
613
  class UpdateDelegationLegacy {
@@ -614,10 +622,12 @@ class UpdateDelegationLegacy {
614
622
  class UpdateDelegationV2 {
615
623
  id;
616
624
  request;
625
+ asAdmin;
617
626
  static type = '[Delegations] Update Delegation (v2)';
618
- constructor(id, request) {
627
+ constructor(id, request, asAdmin = false) {
619
628
  this.id = id;
620
629
  this.request = request;
630
+ this.asAdmin = asAdmin;
621
631
  }
622
632
  }
623
633
  // ---------------------------------------------------------------------------
@@ -776,8 +786,9 @@ let DelegationsState = class DelegationsState {
776
786
  onSuccess: (response) => ({ active: response.data }),
777
787
  });
778
788
  }
779
- getDetail(ctx, { id }) {
780
- const req$ = this.http.get(`${BASE}/${id}`);
789
+ getDetail(ctx, { id, asAdmin }) {
790
+ const path = asAdmin ? `${BASE}/Admin/${id}` : `${BASE}/${id}`;
791
+ const req$ = this.http.get(path);
781
792
  return handleApiRequest({
782
793
  ctx,
783
794
  key: DelegationsActionKey.GetDetail,
@@ -791,12 +802,17 @@ let DelegationsState = class DelegationsState {
791
802
  // ============================================================================
792
803
  // Scope
793
804
  // ============================================================================
794
- getScopeOptions(ctx, { delegatorUserId }) {
805
+ getScopeOptions(ctx, { delegatorUserId, asAdmin }) {
795
806
  ctx.patchState({ scopeOptions: null, scopePreview: null });
796
807
  let params = new HttpParams();
797
808
  if (delegatorUserId)
798
809
  params = params.set('delegatorUserId', delegatorUserId);
799
- const req$ = this.http.get(`${BASE}/scope/options`, { params });
810
+ const path = asAdmin
811
+ ? `${BASE}/Admin/scope/options`
812
+ : `${BASE}/scope/options`;
813
+ const req$ = this.http.get(path, {
814
+ params,
815
+ });
800
816
  return handleApiRequest({
801
817
  ctx,
802
818
  key: DelegationsActionKey.GetScopeOptions,
@@ -804,8 +820,11 @@ let DelegationsState = class DelegationsState {
804
820
  onSuccess: (response) => ({ scopeOptions: response.data ?? null }),
805
821
  });
806
822
  }
807
- previewScope(ctx, { scope }) {
808
- const req$ = this.http.post(`${BASE}/scope/preview`, { scope });
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));
809
828
  return handleApiRequest({
810
829
  ctx,
811
830
  key: DelegationsActionKey.PreviewScope,
@@ -828,8 +847,12 @@ let DelegationsState = class DelegationsState {
828
847
  onSuccess: () => ({}),
829
848
  });
830
849
  }
831
- createV2(ctx, { request }) {
832
- const req$ = this.http.post(`${BASE}/v2`, request);
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);
833
856
  return handleApiRequest({
834
857
  ctx,
835
858
  key: DelegationsActionKey.Create,
@@ -846,8 +869,12 @@ let DelegationsState = class DelegationsState {
846
869
  onSuccess: () => ({}),
847
870
  });
848
871
  }
849
- updateV2(ctx, { id, request }) {
850
- const req$ = this.http.put(`${BASE}/v2/${id}`, request);
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);
851
878
  return handleApiRequest({
852
879
  ctx,
853
880
  key: DelegationsActionKey.Update,
@@ -888,6 +915,21 @@ let DelegationsState = class DelegationsState {
888
915
  onSuccess: () => ({}),
889
916
  });
890
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
+ }
891
933
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: DelegationsState, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
892
934
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: DelegationsState });
893
935
  };
@@ -1048,17 +1090,21 @@ class DelegationsFacade {
1048
1090
  getActive(query = {}) {
1049
1091
  return this.store.dispatch(new GetActiveAssignedDelegations(query));
1050
1092
  }
1051
- getDetail(id) {
1052
- return this.store.dispatch(new GetDelegationDetail(id));
1093
+ getDetail(id, asAdmin = false) {
1094
+ return this.store.dispatch(new GetDelegationDetail(id, asAdmin));
1053
1095
  }
1054
1096
  clearDetail() {
1055
1097
  return this.store.dispatch(new ClearDelegationDetail());
1056
1098
  }
1057
- loadScopeOptions(delegatorUserId) {
1058
- return this.store.dispatch(new GetScopeOptions(delegatorUserId));
1099
+ loadScopeOptions(delegatorUserId, asAdmin = false) {
1100
+ return this.store.dispatch(new GetScopeOptions(delegatorUserId, asAdmin));
1059
1101
  }
1060
- previewScope(scope) {
1061
- return this.store.dispatch(new PreviewScope(scope));
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));
1062
1108
  }
1063
1109
  clearScopePreview() {
1064
1110
  return this.store.dispatch(new ClearScopePreview());
@@ -1066,14 +1112,14 @@ class DelegationsFacade {
1066
1112
  createLegacy(request) {
1067
1113
  return this.store.dispatch(new CreateDelegationLegacy(request));
1068
1114
  }
1069
- createV2(request) {
1070
- return this.store.dispatch(new CreateDelegationV2(request));
1115
+ createV2(request, asAdmin = false) {
1116
+ return this.store.dispatch(new CreateDelegationV2(request, asAdmin));
1071
1117
  }
1072
1118
  updateLegacy(id, request) {
1073
1119
  return this.store.dispatch(new UpdateDelegationLegacy(id, request));
1074
1120
  }
1075
- updateV2(id, request) {
1076
- return this.store.dispatch(new UpdateDelegationV2(id, request));
1121
+ updateV2(id, request, asAdmin = false) {
1122
+ return this.store.dispatch(new UpdateDelegationV2(id, request, asAdmin));
1077
1123
  }
1078
1124
  approve(id, request) {
1079
1125
  return this.store.dispatch(new ApproveDelegation(id, request));
@@ -1551,6 +1597,7 @@ function sameSelectionKeys(left, right) {
1551
1597
  class ScopePicker {
1552
1598
  scope = model(EMPTY_SCOPE$1, ...(ngDevMode ? [{ debugName: "scope" }] : /* istanbul ignore next */ []));
1553
1599
  readonly = input(false, ...(ngDevMode ? [{ debugName: "readonly" }] : /* istanbul ignore next */ []));
1600
+ adminMode = input(false, ...(ngDevMode ? [{ debugName: "adminMode" }] : /* istanbul ignore next */ []));
1554
1601
  /**
1555
1602
  * Delegator whose grantable scope is loaded. Undefined = current user
1556
1603
  * (self-service). In admin mode the host rebinds this as the selected
@@ -1589,7 +1636,8 @@ class ScopePicker {
1589
1636
  let lastDelegatorUserId;
1590
1637
  effect(() => {
1591
1638
  const delegator = this.delegatorUserId();
1592
- untracked(() => this.facade.loadScopeOptions(delegator));
1639
+ const asAdmin = this.adminMode();
1640
+ untracked(() => this.facade.loadScopeOptions(delegator, asAdmin));
1593
1641
  if (!initialized) {
1594
1642
  initialized = true;
1595
1643
  lastDelegatorUserId = delegator;
@@ -1605,6 +1653,8 @@ class ScopePicker {
1605
1653
  let timer = null;
1606
1654
  effect(() => {
1607
1655
  const s = this.scope();
1656
+ const delegator = this.delegatorUserId();
1657
+ const asAdmin = this.adminMode();
1608
1658
  untracked(() => {
1609
1659
  if (!s.grants.length) {
1610
1660
  this.facade.clearScopePreview();
@@ -1612,7 +1662,10 @@ class ScopePicker {
1612
1662
  }
1613
1663
  if (timer)
1614
1664
  clearTimeout(timer);
1615
- timer = setTimeout(() => this.facade.previewScope(s), 300);
1665
+ timer = setTimeout(() => this.facade.previewScope(s, {
1666
+ delegatorUserId: delegator,
1667
+ asAdmin,
1668
+ }), 300);
1616
1669
  });
1617
1670
  });
1618
1671
  effect(() => {
@@ -1648,11 +1701,17 @@ class ScopePicker {
1648
1701
  formatDeniedTarget(value) {
1649
1702
  return humanizeDelegationKey(value);
1650
1703
  }
1704
+ groupTargetCount(group) {
1705
+ return group.targets.length;
1706
+ }
1707
+ targetGrantCount(targetGroup) {
1708
+ return targetGroup.grants.length;
1709
+ }
1651
1710
  buildTreeNodes(groups) {
1652
1711
  return groups.map((group) => ({
1653
1712
  key: `group:${group.key}`,
1654
- label: this.getGroupLabel(group),
1655
- selectable: false,
1713
+ label: `${this.getGroupLabel(group)} (${this.groupTargetCount(group)})`,
1714
+ selectable: !this.readonly(),
1656
1715
  expanded: true,
1657
1716
  children: group.targets.map((targetGroup) => this.buildTargetNode(targetGroup)),
1658
1717
  }));
@@ -1660,8 +1719,8 @@ class ScopePicker {
1660
1719
  buildTargetNode(targetGroup) {
1661
1720
  return {
1662
1721
  key: `target:${targetGroup.key}`,
1663
- label: this.getTargetLabel(targetGroup),
1664
- selectable: false,
1722
+ label: `${this.getTargetLabel(targetGroup)} (${this.targetGrantCount(targetGroup)})`,
1723
+ selectable: !this.readonly(),
1665
1724
  expanded: true,
1666
1725
  children: targetGroup.grants.map((grant) => ({
1667
1726
  key: grantKey(grant),
@@ -1685,12 +1744,12 @@ class ScopePicker {
1685
1744
  return this.activeLang() ?? this.transloco.getActiveLang();
1686
1745
  }
1687
1746
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: ScopePicker, deps: [], target: i0.ɵɵFactoryTarget.Component });
1688
- 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=\"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 });
1747
+ 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 <div class=\"grid gap-4 2xl:grid-cols-[minmax(0,1fr)_20rem]\">\n <section\n class=\"flex flex-col gap-4 rounded-3xl border border-surface-200 bg-surface-0 p-4 shadow-xs\"\n >\n <div class=\"flex flex-col gap-2\">\n <p-skeleton width=\"9rem\" height=\"1rem\"></p-skeleton>\n <p-skeleton width=\"20rem\" height=\"0.9rem\"></p-skeleton>\n </div>\n\n <p-skeleton height=\"3rem\"></p-skeleton>\n\n <div class=\"grid gap-3\">\n @for (item of [0, 1, 2, 3]; track item) {\n <div class=\"rounded-2xl border border-surface-200 p-4\">\n <div class=\"flex items-center justify-between gap-3\">\n <p-skeleton width=\"12rem\" height=\"1rem\"></p-skeleton>\n <p-skeleton width=\"1.5rem\" height=\"1.5rem\"></p-skeleton>\n </div>\n </div>\n }\n </div>\n </section>\n\n <section\n class=\"flex flex-col gap-4 rounded-3xl border border-dashed border-surface-300 bg-surface-50 p-4\"\n >\n <div class=\"flex flex-col gap-2\">\n <p-skeleton width=\"8rem\" height=\"1rem\"></p-skeleton>\n <p-skeleton width=\"100%\" height=\"0.85rem\"></p-skeleton>\n </div>\n\n <div class=\"flex flex-col gap-3\">\n @for (item of [0, 1, 2]; track item) {\n <p-skeleton height=\"1rem\"></p-skeleton>\n }\n </div>\n </section>\n </div>\n } @else {\n <section class=\"flex flex-col gap-3\">\n @if (errorOptions(); as errorMessage) {\n <div\n class=\"rounded-2xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700\"\n >\n {{ errorMessage }}\n </div>\n }\n\n @if (groups().length === 0 && !errorOptions()) {\n <div\n class=\"flex min-h-72 flex-col items-center justify-center gap-4 rounded-3xl border border-dashed border-surface-300 bg-surface-50 px-6 py-8 text-center\"\n >\n <div\n class=\"flex size-18 items-center justify-center rounded-3xl bg-surface-0 text-primary shadow-sm\"\n >\n <svg\n viewBox=\"0 0 64 64\"\n class=\"size-10\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden=\"true\"\n >\n <rect\n x=\"10\"\n y=\"12\"\n width=\"44\"\n height=\"38\"\n rx=\"12\"\n class=\"fill-primary/10 stroke-primary/35\"\n stroke-width=\"2\"\n />\n <path\n d=\"M21 27H43\"\n class=\"stroke-primary\"\n stroke-width=\"3\"\n stroke-linecap=\"round\"\n />\n <path\n d=\"M21 35H37\"\n class=\"stroke-primary/70\"\n stroke-width=\"3\"\n stroke-linecap=\"round\"\n />\n <circle\n cx=\"46\"\n cy=\"19\"\n r=\"6\"\n class=\"fill-surface-0 stroke-primary\"\n stroke-width=\"2\"\n />\n <path\n d=\"M43 16L49 22\"\n class=\"stroke-primary\"\n stroke-width=\"2.5\"\n stroke-linecap=\"round\"\n />\n <path\n d=\"M49 16L43 22\"\n class=\"stroke-primary\"\n stroke-width=\"2.5\"\n stroke-linecap=\"round\"\n />\n </svg>\n </div>\n\n <div class=\"space-y-1\">\n <p class=\"text-base font-medium text-surface-900\">\n {{ t(\"delegations.scope.permissionsTitle\") }}\n </p>\n <p class=\"text-sm text-surface-500\">\n {{ t(\"delegations.scope.noGrantableOptions\") }}\n </p>\n </div>\n </div>\n }\n\n @if (groups().length > 0) {\n <div\n class=\"rounded-3xl border border-surface-200 bg-surface-0 p-3 shadow-xs\"\n >\n <mt-tree\n [value]=\"treeNodes()\"\n [(selection)]=\"treeSelection\"\n selectionMode=\"checkbox\"\n [loading]=\"isLoadingOptions()\"\n [propagateSelectionDown]=\"true\"\n [propagateSelectionUp]=\"true\"\n (action)=\"onTreeAction($event)\"\n [filter]=\"true\"\n filterMode=\"lenient\"\n scrollHeight=\"28rem\"\n [virtualScroll]=\"true\"\n [virtualScrollItemSize]=\"72\"\n />\n </div>\n }\n </section>\n\n <section\n class=\"flex flex-col gap-3 rounded-3xl border border-dashed border-surface-300 bg-surface-50 px-4 py-4\"\n >\n <div class=\"flex items-center justify-between gap-2\">\n <div class=\"text-base font-medium text-surface-800\">\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 font-medium text-surface-900\">\n {{\n getPreviewSummary(p.summary) ||\n t(\"delegations.column.scopeSummary\")\n }}\n </div>\n } @else {\n <div class=\"text-sm font-medium text-red-700\">\n {{ t(\"delegations.scope.previewInvalid\") }}\n </div>\n }\n @if (p.warnings.length > 0) {\n <ul class=\"list-inside list-disc space-y-1 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 space-y-1 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 });
1689
1748
  }
1690
1749
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: ScopePicker, decorators: [{
1691
1750
  type: Component,
1692
- 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" }]
1693
- }], 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 }] }] } });
1751
+ 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 <div class=\"grid gap-4 2xl:grid-cols-[minmax(0,1fr)_20rem]\">\n <section\n class=\"flex flex-col gap-4 rounded-3xl border border-surface-200 bg-surface-0 p-4 shadow-xs\"\n >\n <div class=\"flex flex-col gap-2\">\n <p-skeleton width=\"9rem\" height=\"1rem\"></p-skeleton>\n <p-skeleton width=\"20rem\" height=\"0.9rem\"></p-skeleton>\n </div>\n\n <p-skeleton height=\"3rem\"></p-skeleton>\n\n <div class=\"grid gap-3\">\n @for (item of [0, 1, 2, 3]; track item) {\n <div class=\"rounded-2xl border border-surface-200 p-4\">\n <div class=\"flex items-center justify-between gap-3\">\n <p-skeleton width=\"12rem\" height=\"1rem\"></p-skeleton>\n <p-skeleton width=\"1.5rem\" height=\"1.5rem\"></p-skeleton>\n </div>\n </div>\n }\n </div>\n </section>\n\n <section\n class=\"flex flex-col gap-4 rounded-3xl border border-dashed border-surface-300 bg-surface-50 p-4\"\n >\n <div class=\"flex flex-col gap-2\">\n <p-skeleton width=\"8rem\" height=\"1rem\"></p-skeleton>\n <p-skeleton width=\"100%\" height=\"0.85rem\"></p-skeleton>\n </div>\n\n <div class=\"flex flex-col gap-3\">\n @for (item of [0, 1, 2]; track item) {\n <p-skeleton height=\"1rem\"></p-skeleton>\n }\n </div>\n </section>\n </div>\n } @else {\n <section class=\"flex flex-col gap-3\">\n @if (errorOptions(); as errorMessage) {\n <div\n class=\"rounded-2xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700\"\n >\n {{ errorMessage }}\n </div>\n }\n\n @if (groups().length === 0 && !errorOptions()) {\n <div\n class=\"flex min-h-72 flex-col items-center justify-center gap-4 rounded-3xl border border-dashed border-surface-300 bg-surface-50 px-6 py-8 text-center\"\n >\n <div\n class=\"flex size-18 items-center justify-center rounded-3xl bg-surface-0 text-primary shadow-sm\"\n >\n <svg\n viewBox=\"0 0 64 64\"\n class=\"size-10\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden=\"true\"\n >\n <rect\n x=\"10\"\n y=\"12\"\n width=\"44\"\n height=\"38\"\n rx=\"12\"\n class=\"fill-primary/10 stroke-primary/35\"\n stroke-width=\"2\"\n />\n <path\n d=\"M21 27H43\"\n class=\"stroke-primary\"\n stroke-width=\"3\"\n stroke-linecap=\"round\"\n />\n <path\n d=\"M21 35H37\"\n class=\"stroke-primary/70\"\n stroke-width=\"3\"\n stroke-linecap=\"round\"\n />\n <circle\n cx=\"46\"\n cy=\"19\"\n r=\"6\"\n class=\"fill-surface-0 stroke-primary\"\n stroke-width=\"2\"\n />\n <path\n d=\"M43 16L49 22\"\n class=\"stroke-primary\"\n stroke-width=\"2.5\"\n stroke-linecap=\"round\"\n />\n <path\n d=\"M49 16L43 22\"\n class=\"stroke-primary\"\n stroke-width=\"2.5\"\n stroke-linecap=\"round\"\n />\n </svg>\n </div>\n\n <div class=\"space-y-1\">\n <p class=\"text-base font-medium text-surface-900\">\n {{ t(\"delegations.scope.permissionsTitle\") }}\n </p>\n <p class=\"text-sm text-surface-500\">\n {{ t(\"delegations.scope.noGrantableOptions\") }}\n </p>\n </div>\n </div>\n }\n\n @if (groups().length > 0) {\n <div\n class=\"rounded-3xl border border-surface-200 bg-surface-0 p-3 shadow-xs\"\n >\n <mt-tree\n [value]=\"treeNodes()\"\n [(selection)]=\"treeSelection\"\n selectionMode=\"checkbox\"\n [loading]=\"isLoadingOptions()\"\n [propagateSelectionDown]=\"true\"\n [propagateSelectionUp]=\"true\"\n (action)=\"onTreeAction($event)\"\n [filter]=\"true\"\n filterMode=\"lenient\"\n scrollHeight=\"28rem\"\n [virtualScroll]=\"true\"\n [virtualScrollItemSize]=\"72\"\n />\n </div>\n }\n </section>\n\n <section\n class=\"flex flex-col gap-3 rounded-3xl border border-dashed border-surface-300 bg-surface-50 px-4 py-4\"\n >\n <div class=\"flex items-center justify-between gap-2\">\n <div class=\"text-base font-medium text-surface-800\">\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 font-medium text-surface-900\">\n {{\n getPreviewSummary(p.summary) ||\n t(\"delegations.column.scopeSummary\")\n }}\n </div>\n } @else {\n <div class=\"text-sm font-medium text-red-700\">\n {{ t(\"delegations.scope.previewInvalid\") }}\n </div>\n }\n @if (p.warnings.length > 0) {\n <ul class=\"list-inside list-disc space-y-1 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 space-y-1 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" }]
1752
+ }], 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 }] }] } });
1694
1753
  function humanizeDelegationDisplay(value, language) {
1695
1754
  return (formatDelegationScopeSummary(value, language) ||
1696
1755
  humanizeDelegationKey(typeof value === 'string' ? value : null));
@@ -1967,7 +2026,7 @@ class DelegationForm {
1967
2026
  ngOnInit() {
1968
2027
  const editing = this.delegationForEdit();
1969
2028
  if (editing) {
1970
- this.facade.getDetail(editing.delegationId);
2029
+ this.facade.getDetail(editing.delegationId, this.adminMode());
1971
2030
  }
1972
2031
  }
1973
2032
  canSubmit = computed(() => !this.readonly() &&
@@ -1980,23 +2039,59 @@ class DelegationForm {
1980
2039
  return;
1981
2040
  if (!this.canSubmit())
1982
2041
  return;
1983
- const v = this.delegationFormControl.value;
2042
+ const value = this.delegationFormControl.getRawValue();
2043
+ const scope = this.scope();
2044
+ const delegationDaysType = (value?.delegationDaysType ?? 'FullRange');
2045
+ const specificDays = delegationDaysType === 'SpecificDays' ? (value?.specificDays ?? []) : [];
2046
+ const description = value?.description;
2047
+ const delegateFromDateTime = value?.delegateFromDateTime;
2048
+ const delegateToDateTime = value?.delegateToDateTime;
2049
+ const requiresApproval = !!value?.requiresApproval;
2050
+ const delegatedUserId = extractUserId(value?.delegateTo) ?? '';
2051
+ const selectedDelegatorId = this.selectedDelegatorId();
2052
+ const editing = this.delegationForEdit();
2053
+ if (this.adminMode()) {
2054
+ if (!selectedDelegatorId || !delegatedUserId) {
2055
+ return;
2056
+ }
2057
+ const base = {
2058
+ delegatorUserId: selectedDelegatorId,
2059
+ delegatedUserId,
2060
+ description,
2061
+ delegateFromDateTime,
2062
+ delegateToDateTime,
2063
+ delegationDaysType,
2064
+ specificDays,
2065
+ requiresApproval,
2066
+ scope,
2067
+ };
2068
+ if (editing) {
2069
+ const latestRowVersion = this.detail()?.row?.rowVersion ?? editing.rowVersion;
2070
+ const req = {
2071
+ ...base,
2072
+ rowVersion: latestRowVersion,
2073
+ };
2074
+ this.facade.updateV2(editing.delegationId, req, true).subscribe({
2075
+ next: () => this.ref.close(true),
2076
+ });
2077
+ return;
2078
+ }
2079
+ this.facade.createV2(base, true).subscribe({
2080
+ next: () => this.ref.close(true),
2081
+ });
2082
+ return;
2083
+ }
1984
2084
  const base = {
1985
- // Admin mode sends the chosen delegator; self-service omits it so the
1986
- // backend defaults delegateFrom to the current user (doc 04).
1987
- delegateFrom: this.adminMode()
1988
- ? (this.selectedDelegatorId() ?? null)
1989
- : null,
1990
- delegateTo: extractUserId(v?.delegateTo) ?? '',
1991
- description: v?.description,
1992
- delegateFromDateTime: v?.delegateFromDateTime,
1993
- delegateToDateTime: v?.delegateToDateTime,
1994
- delegationDaysType: (v?.delegationDaysType ?? 'FullRange'),
1995
- specificDays: v?.delegationDaysType === 'SpecificDays' ? (v?.specificDays ?? []) : [],
1996
- requiresApproval: !!v?.requiresApproval,
1997
- scope: this.scope(),
2085
+ delegateFrom: null,
2086
+ delegateTo: delegatedUserId,
2087
+ description,
2088
+ delegateFromDateTime,
2089
+ delegateToDateTime,
2090
+ delegationDaysType,
2091
+ specificDays,
2092
+ requiresApproval,
2093
+ scope,
1998
2094
  };
1999
- const editing = this.delegationForEdit();
2000
2095
  if (editing) {
2001
2096
  // Prefer the freshest rowVersion from the loaded detail (the form loads
2002
2097
  // detail on open); fall back to the list row only if detail is absent.
@@ -2028,7 +2123,7 @@ class DelegationForm {
2028
2123
  };
2029
2124
  }
2030
2125
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: DelegationForm, deps: [], target: i0.ɵɵFactoryTarget.Component });
2031
- 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 [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", "delegatorUserId"], outputs: ["scopeChange"] }, { kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2126
+ 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=\"grid min-h-0 flex-1 gap-4 overflow-y-auto p-4 max-[640px]:p-3 lg:grid-cols-[minmax(0,28rem)_minmax(0,1fr)] lg:items-start\"\n >\n <section\n class=\"flex flex-col gap-4 rounded-3xl border border-surface-200 bg-surface-0 p-4 shadow-xs lg:sticky lg:top-0\"\n >\n <mt-dynamic-form\n [formConfig]=\"formConfig()\"\n [formControl]=\"delegationFormControl\"\n />\n </section>\n\n <section\n class=\"flex min-h-0 flex-col gap-4 rounded-3xl border border-surface-200 bg-surface-0 p-4 shadow-xs\"\n >\n <div class=\"flex flex-col gap-2\">\n <div\n class=\"inline-flex w-fit items-center gap-2 rounded-full bg-primary-50 px-3 py-1 text-xs font-medium text-primary-700\"\n >\n <span class=\"inline-flex size-2 rounded-full bg-primary-500\"></span>\n {{ t(\"delegations.scope.previewTitle\") }}\n </div>\n <h3 class=\"text-xl font-semibold text-surface-900\">\n {{ t(\"delegations.scope.permissionsTitle\") }}\n </h3>\n @if (adminMode() && !showScopePicker()) {\n <p class=\"text-sm text-surface-500\">\n {{ t(\"delegations.form.selectDelegatorFirst\") }}\n </p>\n } @else {\n <p class=\"text-sm text-surface-500\">\n {{ t(\"delegations.scope.noSelection\") }}\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=\"flex min-h-72 flex-col items-center justify-center gap-4 rounded-3xl border border-dashed border-surface-300 bg-surface-50 px-6 py-8 text-center\"\n >\n <div\n class=\"flex size-18 items-center justify-center rounded-3xl bg-surface-0 text-primary shadow-sm\"\n >\n <svg\n viewBox=\"0 0 64 64\"\n class=\"size-10\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden=\"true\"\n >\n <rect\n x=\"10\"\n y=\"12\"\n width=\"44\"\n height=\"40\"\n rx=\"12\"\n class=\"fill-primary/10 stroke-primary/35\"\n stroke-width=\"2\"\n />\n <path\n d=\"M22 28H42\"\n class=\"stroke-primary\"\n stroke-width=\"3\"\n stroke-linecap=\"round\"\n />\n <path\n d=\"M22 36H34\"\n class=\"stroke-primary/70\"\n stroke-width=\"3\"\n stroke-linecap=\"round\"\n />\n <circle\n cx=\"45\"\n cy=\"20\"\n r=\"7\"\n class=\"fill-surface-0 stroke-primary\"\n stroke-width=\"2\"\n />\n <path\n d=\"M45 17V23\"\n class=\"stroke-primary\"\n stroke-width=\"2.5\"\n stroke-linecap=\"round\"\n />\n <path\n d=\"M42 20H48\"\n class=\"stroke-primary\"\n stroke-width=\"2.5\"\n stroke-linecap=\"round\"\n />\n </svg>\n </div>\n <div class=\"space-y-1\">\n <p class=\"text-base font-medium text-surface-900\">\n {{ t(\"delegations.scope.permissionsTitle\") }}\n </p>\n <p class=\"text-sm text-surface-500\">\n {{ t(\"delegations.form.selectDelegatorFirst\") }}\n </p>\n </div>\n </div>\n }\n </section>\n </div>\n\n <div [class]=\"modal.footerClass\">\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 });
2032
2127
  }
2033
2128
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: DelegationForm, decorators: [{
2034
2129
  type: Component,
@@ -2039,7 +2134,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
2039
2134
  ReactiveFormsModule,
2040
2135
  ScopePicker,
2041
2136
  TranslocoDirective,
2042
- ], 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 [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" }]
2137
+ ], 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=\"grid min-h-0 flex-1 gap-4 overflow-y-auto p-4 max-[640px]:p-3 lg:grid-cols-[minmax(0,28rem)_minmax(0,1fr)] lg:items-start\"\n >\n <section\n class=\"flex flex-col gap-4 rounded-3xl border border-surface-200 bg-surface-0 p-4 shadow-xs lg:sticky lg:top-0\"\n >\n <mt-dynamic-form\n [formConfig]=\"formConfig()\"\n [formControl]=\"delegationFormControl\"\n />\n </section>\n\n <section\n class=\"flex min-h-0 flex-col gap-4 rounded-3xl border border-surface-200 bg-surface-0 p-4 shadow-xs\"\n >\n <div class=\"flex flex-col gap-2\">\n <div\n class=\"inline-flex w-fit items-center gap-2 rounded-full bg-primary-50 px-3 py-1 text-xs font-medium text-primary-700\"\n >\n <span class=\"inline-flex size-2 rounded-full bg-primary-500\"></span>\n {{ t(\"delegations.scope.previewTitle\") }}\n </div>\n <h3 class=\"text-xl font-semibold text-surface-900\">\n {{ t(\"delegations.scope.permissionsTitle\") }}\n </h3>\n @if (adminMode() && !showScopePicker()) {\n <p class=\"text-sm text-surface-500\">\n {{ t(\"delegations.form.selectDelegatorFirst\") }}\n </p>\n } @else {\n <p class=\"text-sm text-surface-500\">\n {{ t(\"delegations.scope.noSelection\") }}\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=\"flex min-h-72 flex-col items-center justify-center gap-4 rounded-3xl border border-dashed border-surface-300 bg-surface-50 px-6 py-8 text-center\"\n >\n <div\n class=\"flex size-18 items-center justify-center rounded-3xl bg-surface-0 text-primary shadow-sm\"\n >\n <svg\n viewBox=\"0 0 64 64\"\n class=\"size-10\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden=\"true\"\n >\n <rect\n x=\"10\"\n y=\"12\"\n width=\"44\"\n height=\"40\"\n rx=\"12\"\n class=\"fill-primary/10 stroke-primary/35\"\n stroke-width=\"2\"\n />\n <path\n d=\"M22 28H42\"\n class=\"stroke-primary\"\n stroke-width=\"3\"\n stroke-linecap=\"round\"\n />\n <path\n d=\"M22 36H34\"\n class=\"stroke-primary/70\"\n stroke-width=\"3\"\n stroke-linecap=\"round\"\n />\n <circle\n cx=\"45\"\n cy=\"20\"\n r=\"7\"\n class=\"fill-surface-0 stroke-primary\"\n stroke-width=\"2\"\n />\n <path\n d=\"M45 17V23\"\n class=\"stroke-primary\"\n stroke-width=\"2.5\"\n stroke-linecap=\"round\"\n />\n <path\n d=\"M42 20H48\"\n class=\"stroke-primary\"\n stroke-width=\"2.5\"\n stroke-linecap=\"round\"\n />\n </svg>\n </div>\n <div class=\"space-y-1\">\n <p class=\"text-base font-medium text-surface-900\">\n {{ t(\"delegations.scope.permissionsTitle\") }}\n </p>\n <p class=\"text-sm text-surface-500\">\n {{ t(\"delegations.form.selectDelegatorFirst\") }}\n </p>\n </div>\n </div>\n }\n </section>\n </div>\n\n <div [class]=\"modal.footerClass\">\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" }]
2043
2138
  }], 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 }] }] } });
2044
2139
 
2045
2140
  /**
@@ -2047,6 +2142,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
2047
2142
  */
2048
2143
  class DelegationDetailDrawer {
2049
2144
  delegationId = input.required(...(ngDevMode ? [{ debugName: "delegationId" }] : /* istanbul ignore next */ []));
2145
+ adminMode = input(false, ...(ngDevMode ? [{ debugName: "adminMode" }] : /* istanbul ignore next */ []));
2050
2146
  facade = inject(DelegationsFacade);
2051
2147
  transloco = inject(TranslocoService);
2052
2148
  ref = inject(ModalRef);
@@ -2059,7 +2155,7 @@ class DelegationDetailDrawer {
2059
2155
  detail = this.facade.detail;
2060
2156
  row = computed(() => this.detail()?.row ?? null, ...(ngDevMode ? [{ debugName: "row" }] : /* istanbul ignore next */ []));
2061
2157
  ngOnInit() {
2062
- this.facade.getDetail(this.delegationId());
2158
+ this.facade.getDetail(this.delegationId(), this.adminMode());
2063
2159
  }
2064
2160
  setTab(tab) {
2065
2161
  this.activeTab.set(tab);
@@ -2078,7 +2174,7 @@ class DelegationDetailDrawer {
2078
2174
  return formatDelegationScopeSummary(summary, this.transloco.getActiveLang());
2079
2175
  }
2080
2176
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: DelegationDetailDrawer, deps: [], target: i0.ɵɵFactoryTarget.Component });
2081
- 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.DatePipe, name: "date" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2177
+ 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 });
2082
2178
  }
2083
2179
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: DelegationDetailDrawer, decorators: [{
2084
2180
  type: Component,
@@ -2090,7 +2186,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
2090
2186
  TranslocoDirective,
2091
2187
  DelegationStatusChip,
2092
2188
  ], 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"] }]
2093
- }], propDecorators: { delegationId: [{ type: i0.Input, args: [{ isSignal: true, alias: "delegationId", required: true }] }] } });
2189
+ }], propDecorators: { delegationId: [{ type: i0.Input, args: [{ isSignal: true, alias: "delegationId", required: true }] }], adminMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "adminMode", required: false }] }] } });
2094
2190
 
2095
2191
  /**
2096
2192
  * Delegations portal page (doc 02, 09): two lists — My Delegations (current user
@@ -2176,7 +2272,7 @@ class DelegationsList {
2176
2272
  if (this.adminMode())
2177
2273
  return true;
2178
2274
  if (this.isLoadingScopeOptions())
2179
- return true;
2275
+ return false;
2180
2276
  return hasDelegationGrantableScope(this.scopeOptions());
2181
2277
  }, ...(ngDevMode ? [{ debugName: "canCreate" }] : /* istanbul ignore next */ []));
2182
2278
  tableActions = computed(() => this.canCreate()
@@ -2382,15 +2478,18 @@ class DelegationsList {
2382
2478
  appendTo: 'page-content',
2383
2479
  dismissableMask: true,
2384
2480
  dismissible: true,
2385
- inputValues: { delegationId: row.delegationId },
2481
+ inputValues: {
2482
+ delegationId: row.delegationId,
2483
+ adminMode: this.adminMode(),
2484
+ },
2386
2485
  });
2387
2486
  }
2388
2487
  openForm(row) {
2389
2488
  const ref = this.modal.openModal(DelegationForm, row ? 'drawer' : 'dialog', {
2390
2489
  header: this.transloco.translate(row ? 'delegations.action.edit' : 'delegations.action.create'),
2391
2490
  styleClass: row
2392
- ? '!absolute !shadow-none !w-full !max-w-full sm:!w-[42rem] xl:!w-[50rem]'
2393
- : '!w-[min(96vw,52rem)] !max-w-[96vw] !h-[min(92vh,46rem)] !max-h-[92vh] !overflow-hidden',
2491
+ ? '!absolute !shadow-none !w-full !max-w-full sm:!w-[56rem] xl:!w-[68rem]'
2492
+ : '!w-[min(98vw,88rem)] !max-w-[98vw] !h-[min(94vh,56rem)] !max-h-[94vh] !overflow-hidden',
2394
2493
  position: row ? 'end' : '',
2395
2494
  appendTo: row ? 'page-content' : 'body',
2396
2495
  dismissableMask: true,