@gooddata/sdk-ui-ext 11.61.0-alpha.2 → 11.61.0-alpha.4

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.
Files changed (130) hide show
  1. package/NOTICE +20 -20
  2. package/esm/internal/components/configurationControls/colors/ColorsSection.d.ts.map +1 -1
  3. package/esm/internal/components/configurationControls/colors/ColorsSection.js +4 -9
  4. package/esm/internal/components/configurationControls/colors/coloredItemsList/ColoredItemsList.d.ts.map +1 -1
  5. package/esm/internal/components/configurationControls/colors/coloredItemsList/ColoredItemsList.js +6 -4
  6. package/esm/internal/components/configurationControls/colors/coloredItemsList/LineStyleDialog.d.ts.map +1 -1
  7. package/esm/internal/components/configurationControls/colors/coloredItemsList/LineStyleDialog.js +16 -11
  8. package/esm/internal/components/dialogs/userManagementDialogs/AddWorkspaceToSubjects.d.ts +1 -0
  9. package/esm/internal/components/dialogs/userManagementDialogs/AddWorkspaceToSubjects.d.ts.map +1 -1
  10. package/esm/internal/components/dialogs/userManagementDialogs/AddWorkspaceToSubjects.js +2 -2
  11. package/esm/internal/components/dialogs/userManagementDialogs/UserEditDialog.d.ts +1 -0
  12. package/esm/internal/components/dialogs/userManagementDialogs/UserEditDialog.d.ts.map +1 -1
  13. package/esm/internal/components/dialogs/userManagementDialogs/UserEditDialog.js +2 -2
  14. package/esm/internal/components/dialogs/userManagementDialogs/UserGroupEditDialog.d.ts +1 -0
  15. package/esm/internal/components/dialogs/userManagementDialogs/UserGroupEditDialog.d.ts.map +1 -1
  16. package/esm/internal/components/dialogs/userManagementDialogs/UserGroupEditDialog.js +2 -2
  17. package/esm/internal/components/dialogs/userManagementDialogs/Workspace/AddWorkspace.d.ts +2 -1
  18. package/esm/internal/components/dialogs/userManagementDialogs/Workspace/AddWorkspace.d.ts.map +1 -1
  19. package/esm/internal/components/dialogs/userManagementDialogs/Workspace/AddWorkspace.js +4 -4
  20. package/esm/internal/components/dialogs/userManagementDialogs/Workspace/WorkspaceItem/GranularPermissions.d.ts +2 -1
  21. package/esm/internal/components/dialogs/userManagementDialogs/Workspace/WorkspaceItem/GranularPermissions.d.ts.map +1 -1
  22. package/esm/internal/components/dialogs/userManagementDialogs/Workspace/WorkspaceItem/GranularPermissions.js +24 -24
  23. package/esm/internal/components/dialogs/userManagementDialogs/Workspace/WorkspaceItem/granularPermissionUtils.d.ts +3 -3
  24. package/esm/internal/components/dialogs/userManagementDialogs/Workspace/WorkspaceItem/granularPermissionUtils.d.ts.map +1 -1
  25. package/esm/internal/components/dialogs/userManagementDialogs/Workspace/WorkspaceItem/granularPermissionUtils.js +21 -15
  26. package/esm/internal/components/dialogs/userManagementDialogs/Workspace/WorkspaceItem/locales.d.ts +6 -0
  27. package/esm/internal/components/dialogs/userManagementDialogs/Workspace/WorkspaceItem/locales.d.ts.map +1 -1
  28. package/esm/internal/components/dialogs/userManagementDialogs/Workspace/WorkspaceItem/locales.js +4 -0
  29. package/esm/internal/components/dialogs/userManagementDialogs/Workspace/useAddWorkspace.d.ts.map +1 -1
  30. package/esm/internal/components/dialogs/userManagementDialogs/Workspace/useAddWorkspace.js +10 -3
  31. package/esm/internal/translations/de-DE.localization-bundle.d.ts +2 -1
  32. package/esm/internal/translations/de-DE.localization-bundle.d.ts.map +1 -1
  33. package/esm/internal/translations/de-DE.localization-bundle.js +2 -1
  34. package/esm/internal/translations/en-AU.localization-bundle.d.ts +2 -1
  35. package/esm/internal/translations/en-AU.localization-bundle.d.ts.map +1 -1
  36. package/esm/internal/translations/en-AU.localization-bundle.js +2 -1
  37. package/esm/internal/translations/en-GB.localization-bundle.d.ts +2 -1
  38. package/esm/internal/translations/en-GB.localization-bundle.d.ts.map +1 -1
  39. package/esm/internal/translations/en-GB.localization-bundle.js +2 -1
  40. package/esm/internal/translations/en-US.localization-bundle.d.ts +16 -0
  41. package/esm/internal/translations/en-US.localization-bundle.d.ts.map +1 -1
  42. package/esm/internal/translations/en-US.localization-bundle.js +17 -1
  43. package/esm/internal/translations/es-419.localization-bundle.d.ts +2 -1
  44. package/esm/internal/translations/es-419.localization-bundle.d.ts.map +1 -1
  45. package/esm/internal/translations/es-419.localization-bundle.js +2 -1
  46. package/esm/internal/translations/es-ES.localization-bundle.d.ts +2 -1
  47. package/esm/internal/translations/es-ES.localization-bundle.d.ts.map +1 -1
  48. package/esm/internal/translations/es-ES.localization-bundle.js +2 -1
  49. package/esm/internal/translations/fi-FI.localization-bundle.d.ts +2 -1
  50. package/esm/internal/translations/fi-FI.localization-bundle.d.ts.map +1 -1
  51. package/esm/internal/translations/fi-FI.localization-bundle.js +2 -1
  52. package/esm/internal/translations/fr-CA.localization-bundle.d.ts +2 -1
  53. package/esm/internal/translations/fr-CA.localization-bundle.d.ts.map +1 -1
  54. package/esm/internal/translations/fr-CA.localization-bundle.js +2 -1
  55. package/esm/internal/translations/fr-FR.localization-bundle.d.ts +2 -1
  56. package/esm/internal/translations/fr-FR.localization-bundle.d.ts.map +1 -1
  57. package/esm/internal/translations/fr-FR.localization-bundle.js +2 -1
  58. package/esm/internal/translations/id-ID.localization-bundle.d.ts +2 -1
  59. package/esm/internal/translations/id-ID.localization-bundle.d.ts.map +1 -1
  60. package/esm/internal/translations/id-ID.localization-bundle.js +2 -1
  61. package/esm/internal/translations/it-IT.localization-bundle.d.ts +2 -1
  62. package/esm/internal/translations/it-IT.localization-bundle.d.ts.map +1 -1
  63. package/esm/internal/translations/it-IT.localization-bundle.js +3 -2
  64. package/esm/internal/translations/ja-JP.localization-bundle.d.ts +2 -1
  65. package/esm/internal/translations/ja-JP.localization-bundle.d.ts.map +1 -1
  66. package/esm/internal/translations/ja-JP.localization-bundle.js +2 -1
  67. package/esm/internal/translations/ko-KR.localization-bundle.d.ts +2 -1
  68. package/esm/internal/translations/ko-KR.localization-bundle.d.ts.map +1 -1
  69. package/esm/internal/translations/ko-KR.localization-bundle.js +2 -1
  70. package/esm/internal/translations/nl-NL.localization-bundle.d.ts +2 -1
  71. package/esm/internal/translations/nl-NL.localization-bundle.d.ts.map +1 -1
  72. package/esm/internal/translations/nl-NL.localization-bundle.js +2 -1
  73. package/esm/internal/translations/pl-PL.localization-bundle.d.ts +2 -1
  74. package/esm/internal/translations/pl-PL.localization-bundle.d.ts.map +1 -1
  75. package/esm/internal/translations/pl-PL.localization-bundle.js +2 -1
  76. package/esm/internal/translations/pt-BR.localization-bundle.d.ts +2 -1
  77. package/esm/internal/translations/pt-BR.localization-bundle.d.ts.map +1 -1
  78. package/esm/internal/translations/pt-BR.localization-bundle.js +2 -1
  79. package/esm/internal/translations/pt-PT.localization-bundle.d.ts +2 -1
  80. package/esm/internal/translations/pt-PT.localization-bundle.d.ts.map +1 -1
  81. package/esm/internal/translations/pt-PT.localization-bundle.js +2 -1
  82. package/esm/internal/translations/ru-RU.localization-bundle.d.ts +2 -1
  83. package/esm/internal/translations/ru-RU.localization-bundle.d.ts.map +1 -1
  84. package/esm/internal/translations/ru-RU.localization-bundle.js +2 -1
  85. package/esm/internal/translations/sl-SI.localization-bundle.d.ts +2 -1
  86. package/esm/internal/translations/sl-SI.localization-bundle.d.ts.map +1 -1
  87. package/esm/internal/translations/sl-SI.localization-bundle.js +2 -1
  88. package/esm/internal/translations/th-TH.localization-bundle.d.ts +2 -1
  89. package/esm/internal/translations/th-TH.localization-bundle.d.ts.map +1 -1
  90. package/esm/internal/translations/th-TH.localization-bundle.js +2 -1
  91. package/esm/internal/translations/tr-TR.localization-bundle.d.ts +2 -1
  92. package/esm/internal/translations/tr-TR.localization-bundle.d.ts.map +1 -1
  93. package/esm/internal/translations/tr-TR.localization-bundle.js +2 -1
  94. package/esm/internal/translations/uk-UA.localization-bundle.d.ts +2 -1
  95. package/esm/internal/translations/uk-UA.localization-bundle.d.ts.map +1 -1
  96. package/esm/internal/translations/uk-UA.localization-bundle.js +2 -1
  97. package/esm/internal/translations/vi-VN.localization-bundle.d.ts +2 -1
  98. package/esm/internal/translations/vi-VN.localization-bundle.d.ts.map +1 -1
  99. package/esm/internal/translations/vi-VN.localization-bundle.js +2 -1
  100. package/esm/internal/translations/zh-HK.localization-bundle.d.ts +2 -1
  101. package/esm/internal/translations/zh-HK.localization-bundle.d.ts.map +1 -1
  102. package/esm/internal/translations/zh-HK.localization-bundle.js +2 -1
  103. package/esm/internal/translations/zh-Hans.localization-bundle.d.ts +2 -1
  104. package/esm/internal/translations/zh-Hans.localization-bundle.d.ts.map +1 -1
  105. package/esm/internal/translations/zh-Hans.localization-bundle.js +2 -1
  106. package/esm/internal/translations/zh-Hant.localization-bundle.d.ts +2 -1
  107. package/esm/internal/translations/zh-Hant.localization-bundle.d.ts.map +1 -1
  108. package/esm/internal/translations/zh-Hant.localization-bundle.js +2 -1
  109. package/esm/internal/utils/colors.d.ts +1 -0
  110. package/esm/internal/utils/colors.d.ts.map +1 -1
  111. package/esm/internal/utils/colors.js +14 -7
  112. package/esm/sdk-ui-ext.d.ts +4 -1
  113. package/esm/share/ObjectShareDialog.js +10 -7
  114. package/esm/share/messages.d.ts +21 -2
  115. package/esm/share/messages.d.ts.map +1 -1
  116. package/esm/share/messages.js +13 -2
  117. package/esm/share/objectShareController.helpers.d.ts +2 -0
  118. package/esm/share/objectShareController.helpers.d.ts.map +1 -1
  119. package/esm/share/objectShareController.helpers.js +2 -0
  120. package/esm/share/objectShareController.types.d.ts +26 -13
  121. package/esm/share/objectShareController.types.d.ts.map +1 -1
  122. package/esm/share/useAccessList.d.ts +11 -0
  123. package/esm/share/useAccessList.d.ts.map +1 -1
  124. package/esm/share/useAccessList.js +18 -1
  125. package/esm/share/useObjectShareController.d.ts.map +1 -1
  126. package/esm/share/useObjectShareController.js +52 -21
  127. package/esm/share/useObjectShareDialog.d.ts +3 -1
  128. package/esm/share/useObjectShareDialog.d.ts.map +1 -1
  129. package/esm/share/useObjectShareDialog.js +44 -8
  130. package/package.json +21 -21
@@ -6,6 +6,19 @@ import { objectShareMessages } from "./messages.js";
6
6
  import { EMPTY_IDS, NO_LABELS, changesEffectiveLevel, granularGranteeFor, levelsAbove, levelsBelow, mergeDisabledLevels, sortGrantees, toGranularGrantee, } from "./objectShareController.helpers.js";
7
7
  import { useAccessList } from "./useAccessList.js";
8
8
  import { useLabelScope } from "./useLabelScope.js";
9
+ function resolveRoleAccess({ loaded, isWorkspaceManager, canManageVisualizations, kind, callerLevel, }) {
10
+ if (!loaded) {
11
+ return undefined;
12
+ }
13
+ if (isWorkspaceManager) {
14
+ return "workspaceManager";
15
+ }
16
+ // The backend's EDIT is required too: inherited visualizations stay view-only for a holder.
17
+ if (canManageVisualizations && kind === "insight" && callerLevel === "EDIT") {
18
+ return "visualizationManager";
19
+ }
20
+ return undefined;
21
+ }
9
22
  /**
10
23
  * Manages the share-dialog state and backend I/O for ONE dialog session: the hook
11
24
  * is mounted while the dialog is open, for a single target that must not change
@@ -35,7 +48,15 @@ export function useObjectShareController(target, options) {
35
48
  const [subview, setSubview] = useState("main");
36
49
  const [pendingGrantees, setPendingGrantees] = useState([]);
37
50
  const [pendingGeneralAccess, setPendingGeneralAccess] = useState(undefined);
38
- const { hasList, grantees, isWorkspaceManager, selfIdentityResolved, generalAccess, workspaceLevel, workspaceInheritedLevel, workspaceLevelSaving, summary, status, loadError, commit, loadOptions, applyGranteeAdd, applyGranteeLevel, applyGranteeLock, applyGranteeRemove, settleGranteeEdit, failGranteeEdit, applyRuleEdit, settleRuleEdit, failRuleEdit, draft: draftValue, } = useAccessList(target, { draft, initialDraft, initialDraftGeneralAccess });
51
+ const { hasList, grantees, isWorkspaceManager, canManageVisualizations, callerLevel, selfIdentityResolved, generalAccess, workspaceLevel, workspaceInheritedLevel, workspaceLevelSaving, summary, status, loadError, commit, loadOptions, applyGranteeAdd, applyGranteeLevel, applyGranteeLock, applyGranteeRemove, settleGranteeEdit, failGranteeEdit, applyRuleEdit, settleRuleEdit, failRuleEdit, draft: draftValue, } = useAccessList(target, { draft, initialDraft, initialDraftGeneralAccess });
52
+ const roleAccess = resolveRoleAccess({
53
+ loaded: !draft && status === "success",
54
+ isWorkspaceManager,
55
+ canManageVisualizations,
56
+ kind: target?.kind,
57
+ callerLevel,
58
+ });
59
+ const roleLockedGranteeId = roleAccess === "visualizationManager" ? grantees.find((g) => g.isSelf)?.id : undefined;
39
60
  // Grantee ids the label-scope probe resolves against — a removing row is excluded
40
61
  // so its scope is dropped, not re-seeded.
41
62
  const committedGranteeIds = useMemo(() => {
@@ -218,7 +239,11 @@ export function useObjectShareController(target, options) {
218
239
  // Also refused before the label scope resolves: the re-grade below would run
219
240
  // over the assumed-all fallback and mint grants the grantee never held. The
220
241
  // UI gates on the same fact (isMutable); the controller stays authoritative.
221
- if (!grantee || grantee.pending || !labelsResolved || !changesEffectiveLevel(grantee, level)) {
242
+ if (!grantee ||
243
+ grantee.pending ||
244
+ !labelsResolved ||
245
+ granteeId === roleLockedGranteeId ||
246
+ !changesEffectiveLevel(grantee, level)) {
222
247
  return;
223
248
  }
224
249
  applyGranteeLevel(granteeId, level);
@@ -278,6 +303,7 @@ export function useObjectShareController(target, options) {
278
303
  applyGranteeLevel,
279
304
  settleGranteeEdit,
280
305
  failGranteeEdit,
306
+ roleLockedGranteeId,
281
307
  ]);
282
308
  const removeGrantee = useCallback(async (granteeId) => {
283
309
  const grantee = grantees.find((g) => g.id === granteeId);
@@ -288,7 +314,11 @@ export function useObjectShareController(target, options) {
288
314
  // this keeps the controller — not the UI — authoritative. Same for an
289
315
  // unresolved label scope: the revoke below would run over the assumed-all
290
316
  // fallback and revoke (then compensate with) grants that never existed.
291
- if (!grantee || grantee.pending || !labelsResolved || grantee.directLevel === undefined) {
317
+ if (!grantee ||
318
+ grantee.pending ||
319
+ !labelsResolved ||
320
+ grantee.directLevel === undefined ||
321
+ granteeId === roleLockedGranteeId) {
292
322
  return;
293
323
  }
294
324
  // Mark the row removed but keep it visible (muted) until the write lands.
@@ -375,6 +405,7 @@ export function useObjectShareController(target, options) {
375
405
  settleGranteeEdit,
376
406
  failGranteeEdit,
377
407
  setSelectedLabelIdsByGrantee,
408
+ roleLockedGranteeId,
378
409
  ]);
379
410
  const changeGranteeLabels = useCallback(async (granteeId, requested) => {
380
411
  const grantee = grantees.find((g) => g.id === granteeId);
@@ -607,7 +638,7 @@ export function useObjectShareController(target, options) {
607
638
  // Sorted for display only; the overlay stays in write-through order, so
608
639
  // add/rollback logic is unaffected. Memoized apart from the state assembly so
609
640
  // the array identity changes only when the rows do, not on every state change.
610
- const sortedGrantees = useMemo(() => sortGrantees(grantees), [grantees]);
641
+ const sortedGrantees = useMemo(() => sortGrantees(grantees).map((g) => g.id === roleLockedGranteeId ? { ...g, level: "EDIT", effectivePermission: undefined } : g), [grantees, roleLockedGranteeId]);
611
642
  const state = useMemo(() => {
612
643
  // Row policy is classified here, not in the dialog, so a consumer
613
644
  // injecting this controller into its own UI gets the same classification.
@@ -615,22 +646,18 @@ export function useObjectShareController(target, options) {
615
646
  // it special is that the access is THEIRS. A manager is exempt, their access
616
647
  // comes from the role, so there is no ceiling to cap and no lockout to warn of.
617
648
  const ownRow = grantees.find((g) => g.isSelf);
618
- const selfRow = isWorkspaceManager ? undefined : ownRow;
649
+ const selfRow = isWorkspaceManager || roleLockedGranteeId ? undefined : ownRow;
619
650
  const selfManagedGranteeId = selfRow?.id;
620
- // The caller cannot grant above what they hold, and the server refuses such a
621
- // write. Proven from their own row only: with no row their access may come from
622
- // a group, which the list does not report, so capping would block a real holder.
623
- // A manager has no limit, and an UNKNOWN manager status must not produce one
624
- // either: unlike the confirm, a limit that guesses wrong blocks a legitimate
625
- // grant, and a failed permission read would make that permanent.
626
- // `=== false`, not `!isWorkspaceManager`: an unread permission must not produce a
627
- // limit, or a failed read would block a caller the backend would allow.
628
- const grantableDisabledLevels = isWorkspaceManager === false && selfRow ? levelsAbove(selfRow.level) : undefined;
651
+ // The caller cannot grant above what they hold, and the server refuses such a write.
652
+ // The backend's own answer covers groups, parent workspaces and workspace permissions.
653
+ // Without it, only the caller's own row proves a level, and only for a known
654
+ // non-manager: a limit that guesses wrong would block a grant the backend allows.
655
+ const grantableDisabledLevels = callerLevel === undefined
656
+ ? isWorkspaceManager === false && selfRow
657
+ ? levelsAbove(selfRow.level)
658
+ : undefined
659
+ : levelsAbove(callerLevel);
629
660
  const granteeControlsLocked = !selfIdentityResolved && !isWorkspaceManager && grantees.some((g) => g.kind === "user");
630
- // Policy in `IObjectShareControllerState.showAdminAccessNote`: role-based access is
631
- // explained by a note, not a grantee row, so it does not depend on the caller having
632
- // (or lacking) a row of their own.
633
- const showAdminAccessNote = !draft && status === "success" && isWorkspaceManager === true;
634
661
  return {
635
662
  subview,
636
663
  status,
@@ -640,11 +667,13 @@ export function useObjectShareController(target, options) {
640
667
  selfManagedGranteeId,
641
668
  // Policy, not just classification: the caller's own row can't be raised
642
669
  // above itself, an inherited workspace level can't be undercut.
643
- selfManagedDisabledLevels: selfRow ? levelsAbove(selfRow.level) : undefined,
670
+ selfManagedDisabledLevels: selfRow ? levelsAbove(callerLevel ?? selfRow.level) : undefined,
644
671
  workspaceDisabledLevels: mergeDisabledLevels(workspaceInheritedLevel ? levelsBelow(workspaceInheritedLevel) : undefined, grantableDisabledLevels),
645
672
  grantableDisabledLevels,
673
+ callerLevel,
646
674
  granteeControlsLocked,
647
- showAdminAccessNote,
675
+ roleAccess,
676
+ roleLockedGranteeId,
648
677
  ...effectiveWorkspace,
649
678
  workspaceInheritedLevel: workspaceInheritedLevel,
650
679
  workspaceLevelLocked: generalAccess !== "WORKSPACE" || workspaceInheritedLevel === "EDIT",
@@ -664,7 +693,9 @@ export function useObjectShareController(target, options) {
664
693
  summary,
665
694
  selfIdentityResolved,
666
695
  isWorkspaceManager,
667
- draft,
696
+ roleAccess,
697
+ roleLockedGranteeId,
698
+ callerLevel,
668
699
  grantees,
669
700
  sortedGrantees,
670
701
  effectiveWorkspace,
@@ -57,7 +57,9 @@ export interface IObjectShareDialogViewModel {
57
57
  */
58
58
  isRowRemoveDisabled: (grantee: IObjectShareGrantee) => boolean;
59
59
  /** Tooltip explaining why Remove access is disabled on this row. */
60
- rowRemoveDisabledTooltip: string;
60
+ rowRemoveDisabledTooltip: (grantee: IObjectShareGrantee) => string;
61
+ /** Whether this row's direct grant is above the caller's level, so nothing on it may change. */
62
+ isRowAboveCaller: (grantee: IObjectShareGrantee) => boolean;
61
63
  /** Whether this row's controls must stay disabled (see `granteeControlsLocked`). */
62
64
  isRowControlsLocked: (grantee: IObjectShareGrantee) => boolean;
63
65
  /** Close the whole dialog, handing the draft over and discarding any staged self-restriction. */
@@ -1 +1 @@
1
- {"version":3,"file":"useObjectShareDialog.d.ts","sourceRoot":"","sources":["../../src/share/useObjectShareDialog.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useObjectShareDialog.d.ts","sourceRoot":"","sources":["../../src/share/useObjectShareDialog.ts"],"names":[],"mappings":"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"}
@@ -2,7 +2,7 @@
2
2
  import { useCallback, useEffect, useState } from "react";
3
3
  import { useIntl } from "react-intl";
4
4
  import { objectShareMessages } from "./messages.js";
5
- import { changesEffectiveLevel, levelsBelow, removalChangesEffectiveLevel, } from "./objectShareController.helpers.js";
5
+ import { ALL_LEVELS, changesEffectiveLevel, levelsAbove, levelsBelow, removalChangesEffectiveLevel, } from "./objectShareController.helpers.js";
6
6
  import { useObjectShareController } from "./useObjectShareController.js";
7
7
  /**
8
8
  * Level picks an inherited grant already covers, which the controller refuses as
@@ -54,9 +54,10 @@ export function useObjectShareDialog({ target, onClose, onSummaryChange, draft,
54
54
  onClose();
55
55
  }, [currentDraft, onDraftChange, onClose]);
56
56
  // A row belongs to the signed-in user's own sole grant when the controller has
57
- // classified it as self-managed — those changes route through the confirm.
57
+ // classified it as self-managed — lowering it routes through the confirm, raising it
58
+ // (allowed when a group grants more) commits directly.
58
59
  const onRowPermissionChange = useCallback((grantee, level) => {
59
- if (state.selfManagedGranteeId === grantee.id) {
60
+ if (state.selfManagedGranteeId === grantee.id && levelsBelow(grantee.level).includes(level)) {
60
61
  // Stage the confirm only for a pick that would really move the level the
61
62
  // user effectively holds. Comparing against the displayed level instead
62
63
  // opened "Restrict your access?" for a pick under an inherited floor, which
@@ -95,7 +96,34 @@ export function useObjectShareDialog({ target, onClose, onSummaryChange, draft,
95
96
  }, [pendingSelfChange, actions]);
96
97
  const onSelfRestrictCancel = useCallback(() => setPendingSelfChange(undefined), []);
97
98
  const inheritedCoveredTooltip = intl.formatMessage(objectShareMessages.granteeLevelInheritedCovered);
99
+ // The server refuses changing or removing a grant above the caller's own level.
100
+ const isRowAboveCaller = useCallback((grantee) => state.callerLevel !== undefined &&
101
+ grantee.directLevel !== undefined &&
102
+ levelsAbove(state.callerLevel).includes(grantee.directLevel), [state.callerLevel]);
103
+ const coveredByWorkspacePermissionTooltip = intl.formatMessage(objectShareMessages.granteeCoveredByWorkspacePermission);
104
+ const aboveCallerUserTooltip = intl.formatMessage(objectShareMessages.granteeAboveCallerUser);
105
+ const aboveCallerGroupTooltip = intl.formatMessage(objectShareMessages.granteeAboveCallerGroup);
106
+ const rowLock = useCallback((grantee) => {
107
+ if (isRowAboveCaller(grantee)) {
108
+ const tooltip = grantee.kind === "group" ? aboveCallerGroupTooltip : aboveCallerUserTooltip;
109
+ return { levels: [...ALL_LEVELS], tooltip };
110
+ }
111
+ if (state.roleLockedGranteeId === grantee.id) {
112
+ return { levels: levelsBelow("EDIT"), tooltip: coveredByWorkspacePermissionTooltip };
113
+ }
114
+ return undefined;
115
+ }, [
116
+ isRowAboveCaller,
117
+ state.roleLockedGranteeId,
118
+ aboveCallerUserTooltip,
119
+ aboveCallerGroupTooltip,
120
+ coveredByWorkspacePermissionTooltip,
121
+ ]);
98
122
  const rowDisabledLevels = useCallback((grantee) => {
123
+ const lock = rowLock(grantee);
124
+ if (lock) {
125
+ return lock.levels;
126
+ }
99
127
  const self = state.selfManagedGranteeId === grantee.id ? (state.selfManagedDisabledLevels ?? []) : [];
100
128
  // Self caps levels ABOVE the row and coverage disables those BELOW, so those
101
129
  // two are disjoint; the grant limit can overlap either.
@@ -107,8 +135,12 @@ export function useObjectShareDialog({ target, onClose, onSummaryChange, draft,
107
135
  ]),
108
136
  ];
109
137
  return merged.length > 0 ? merged : undefined;
110
- }, [state.selfManagedGranteeId, state.selfManagedDisabledLevels, state.grantableDisabledLevels]);
138
+ }, [rowLock, state.selfManagedGranteeId, state.selfManagedDisabledLevels, state.grantableDisabledLevels]);
111
139
  const rowDisabledLevelTooltips = useCallback((grantee) => {
140
+ const lock = rowLock(grantee);
141
+ if (lock) {
142
+ return Object.fromEntries(lock.levels.map((level) => [level, lock.tooltip]));
143
+ }
112
144
  const covered = inheritedCoveredLevels(grantee);
113
145
  if (covered.length === 0) {
114
146
  return undefined;
@@ -118,8 +150,11 @@ export function useObjectShareDialog({ target, onClose, onSummaryChange, draft,
118
150
  map[level] = inheritedCoveredTooltip;
119
151
  }
120
152
  return map;
121
- }, [inheritedCoveredTooltip]);
122
- const isRowRemoveDisabled = useCallback((grantee) => grantee.directLevel === undefined, []);
153
+ }, [rowLock, inheritedCoveredTooltip]);
154
+ const isRowRemoveDisabled = useCallback((grantee) => grantee.directLevel === undefined || rowLock(grantee) !== undefined, [rowLock]);
155
+ const removeInheritedTooltip = intl.formatMessage(objectShareMessages.granteeRemoveInherited);
156
+ const rowRemoveDisabledTooltip = useCallback((grantee) => rowLock(grantee)?.tooltip ?? removeInheritedTooltip, [rowLock, removeInheritedTooltip]);
157
+ const grantLimitTooltip = intl.formatMessage(objectShareMessages.toastEscalationRefused);
123
158
  // Only USER rows can be the caller's own, so a group row stays usable while the
124
159
  // profile is unresolved.
125
160
  const isRowControlsLocked = useCallback((grantee) => state.granteeControlsLocked && grantee.kind === "user", [state.granteeControlsLocked]);
@@ -136,12 +171,13 @@ export function useObjectShareDialog({ target, onClose, onSummaryChange, draft,
136
171
  isLoading,
137
172
  workspaceDisabledLevels: state.workspaceDisabledLevels,
138
173
  grantableDisabledLevels: state.grantableDisabledLevels,
139
- grantLimitTooltip: intl.formatMessage(objectShareMessages.toastEscalationRefused),
174
+ grantLimitTooltip,
140
175
  rowDisabledLevels,
141
176
  rowDisabledLevelTooltips,
142
177
  isRowRemoveDisabled,
143
178
  isRowControlsLocked,
144
- rowRemoveDisabledTooltip: intl.formatMessage(objectShareMessages.granteeRemoveInherited),
179
+ rowRemoveDisabledTooltip,
180
+ isRowAboveCaller,
145
181
  onClose: closeDialog,
146
182
  onRowPermissionChange,
147
183
  onRowRemove,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gooddata/sdk-ui-ext",
3
- "version": "11.61.0-alpha.2",
3
+ "version": "11.61.0-alpha.4",
4
4
  "description": "GoodData.UI SDK - Extensions",
5
5
  "license": "LicenseRef-LICENSE",
6
6
  "author": "GoodData Corporation",
@@ -63,20 +63,20 @@
63
63
  "ts-invariant": "0.10.3",
64
64
  "tslib": "2.8.1",
65
65
  "uuid": "11.1.1",
66
- "@gooddata/sdk-backend-base": "11.61.0-alpha.2",
67
- "@gooddata/sdk-embedding": "11.61.0-alpha.2",
68
- "@gooddata/sdk-backend-spi": "11.61.0-alpha.2",
69
- "@gooddata/sdk-ui": "11.61.0-alpha.2",
70
- "@gooddata/sdk-model": "11.61.0-alpha.2",
71
- "@gooddata/sdk-ui-filters": "11.61.0-alpha.2",
72
- "@gooddata/sdk-ui-charts": "11.61.0-alpha.2",
73
- "@gooddata/sdk-ui-geo": "11.61.0-alpha.2",
74
- "@gooddata/sdk-ui-pivot": "11.61.0-alpha.2",
75
- "@gooddata/sdk-ui-kit": "11.61.0-alpha.2",
76
- "@gooddata/sdk-ui-vis-commons": "11.61.0-alpha.2",
77
- "@gooddata/sdk-ui-semantic-search": "11.61.0-alpha.2",
78
- "@gooddata/sdk-ui-theme-provider": "11.61.0-alpha.2",
79
- "@gooddata/util": "11.61.0-alpha.2"
66
+ "@gooddata/sdk-backend-base": "11.61.0-alpha.4",
67
+ "@gooddata/sdk-backend-spi": "11.61.0-alpha.4",
68
+ "@gooddata/sdk-embedding": "11.61.0-alpha.4",
69
+ "@gooddata/sdk-model": "11.61.0-alpha.4",
70
+ "@gooddata/sdk-ui": "11.61.0-alpha.4",
71
+ "@gooddata/sdk-ui-charts": "11.61.0-alpha.4",
72
+ "@gooddata/sdk-ui-filters": "11.61.0-alpha.4",
73
+ "@gooddata/sdk-ui-geo": "11.61.0-alpha.4",
74
+ "@gooddata/sdk-ui-kit": "11.61.0-alpha.4",
75
+ "@gooddata/sdk-ui-pivot": "11.61.0-alpha.4",
76
+ "@gooddata/sdk-ui-semantic-search": "11.61.0-alpha.4",
77
+ "@gooddata/sdk-ui-theme-provider": "11.61.0-alpha.4",
78
+ "@gooddata/sdk-ui-vis-commons": "11.61.0-alpha.4",
79
+ "@gooddata/util": "11.61.0-alpha.4"
80
80
  },
81
81
  "devDependencies": {
82
82
  "@microsoft/api-documenter": "7.30.16",
@@ -120,12 +120,12 @@
120
120
  "typescript": "7.0.2",
121
121
  "vitest": "4.1.11",
122
122
  "vitest-dom": "0.1.1",
123
- "@gooddata/oxlint-config": "11.61.0-alpha.2",
124
- "@gooddata/eslint-config": "11.61.0-alpha.2",
125
- "@gooddata/i18n-toolkit": "11.61.0-alpha.2",
126
- "@gooddata/sdk-backend-mockingbird": "11.61.0-alpha.2",
127
- "@gooddata/reference-workspace": "11.61.0-alpha.2",
128
- "@gooddata/stylelint-config": "11.61.0-alpha.2"
123
+ "@gooddata/eslint-config": "11.61.0-alpha.4",
124
+ "@gooddata/oxlint-config": "11.61.0-alpha.4",
125
+ "@gooddata/i18n-toolkit": "11.61.0-alpha.4",
126
+ "@gooddata/stylelint-config": "11.61.0-alpha.4",
127
+ "@gooddata/sdk-backend-mockingbird": "11.61.0-alpha.4",
128
+ "@gooddata/reference-workspace": "11.61.0-alpha.4"
129
129
  },
130
130
  "peerDependencies": {
131
131
  "react": "^18.0.0 || ^19.0.0",