@robertraaijmakers/pptb-securityplugin 0.1.5 → 0.1.6

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.
package/dist/app.js CHANGED
@@ -14758,13 +14758,39 @@ ${logTarget.textContent}`;
14758
14758
  "delete",
14759
14759
  "append",
14760
14760
  "appendto",
14761
- "assign"
14761
+ "assign",
14762
+ "share"
14762
14763
  ];
14764
+ var privilegeLabels = {
14765
+ create: "Create",
14766
+ read: "Read",
14767
+ write: "Write",
14768
+ delete: "Delete",
14769
+ append: "Append",
14770
+ appendto: "Append To",
14771
+ assign: "Assign",
14772
+ share: "Share"
14773
+ };
14763
14774
  var levelOptions = [
14764
14775
  { level: "user", icon: "\u{1F464}", label: "User", className: "level-user" },
14765
- { level: "businessUnit", icon: "\u{1F3E2}", label: "Business Unit", className: "level-businessUnit" },
14766
- { level: "parentChild", icon: "\u{1F9E9}", label: "Parent: Child BU", className: "level-parentChild" },
14767
- { level: "organization", icon: "\u{1F310}", label: "Organization", className: "level-organization" },
14776
+ {
14777
+ level: "businessUnit",
14778
+ icon: "\u{1F3E2}",
14779
+ label: "Business Unit",
14780
+ className: "level-businessUnit"
14781
+ },
14782
+ {
14783
+ level: "parentChild",
14784
+ icon: "\u{1F9E9}",
14785
+ label: "Parent: Child BU",
14786
+ className: "level-parentChild"
14787
+ },
14788
+ {
14789
+ level: "organization",
14790
+ icon: "\u{1F310}",
14791
+ label: "Organization",
14792
+ className: "level-organization"
14793
+ },
14768
14794
  { level: "none", icon: "\u26D4", label: "None", className: "level-none" }
14769
14795
  ];
14770
14796
  var levelColorMap = {
@@ -14789,6 +14815,10 @@ ${logTarget.textContent}`;
14789
14815
  loadingDashboardRoleTeamCounts: "Loading teams per role",
14790
14816
  loadingDashboardFilters: "Preparing dashboard filters",
14791
14817
  tableTitlePrivileges: "Privileges",
14818
+ bulkUpdateSelect: "Set visible...",
14819
+ bulkUpdateApply: "Set",
14820
+ bulkUpdateNoRights: "No visible rights in this column",
14821
+ bulkUpdateNoSelection: "Select a level first",
14792
14822
  roleFilterAll: "All roles",
14793
14823
  statusAssigned: "Assigned",
14794
14824
  statusNotAssigned: "Not assigned",
@@ -14889,6 +14919,15 @@ ${logTarget.textContent}`;
14889
14919
  function formatCachedPrivileges(privilegeCount, rolePrivilegeCount, roleCount) {
14890
14920
  return `Cached ${privilegeCount} privileges and ${rolePrivilegeCount} role privilege rows for ${roleCount} roles.`;
14891
14921
  }
14922
+ function formatBulkUpdatedVisible(privilegeLabel, levelLabel, updatedCount, skippedCount) {
14923
+ if (skippedCount > 0) {
14924
+ return `Bulk set ${privilegeLabel} to ${levelLabel} for ${updatedCount} visible rows (${skippedCount} skipped).`;
14925
+ }
14926
+ return `Bulk set ${privilegeLabel} to ${levelLabel} for ${updatedCount} visible rows.`;
14927
+ }
14928
+ function formatBulkUpdateNoRows(privilegeLabel) {
14929
+ return `No visible rows with editable ${privilegeLabel} rights.`;
14930
+ }
14892
14931
  function formatRoleAssignmentLogUsers(action, roleName, userNames) {
14893
14932
  const verb = action === "add" ? "Added" : "Removed";
14894
14933
  const prep = action === "add" ? "to" : "from";
@@ -14979,6 +15018,7 @@ ${logTarget.textContent}`;
14979
15018
  },
14980
15019
  assignmentFilter: "",
14981
15020
  assignmentSearch: "",
15021
+ privilegeSearch: "",
14982
15022
  sort: {
14983
15023
  column: "label",
14984
15024
  direction: "asc"
@@ -14990,7 +15030,8 @@ ${logTarget.textContent}`;
14990
15030
  delete: "",
14991
15031
  append: "",
14992
15032
  appendto: "",
14993
- assign: ""
15033
+ assign: "",
15034
+ share: ""
14994
15035
  },
14995
15036
  eventsHooked: false,
14996
15037
  readyToastShown: false,
@@ -15002,56 +15043,133 @@ ${logTarget.textContent}`;
15002
15043
  }
15003
15044
  };
15004
15045
  var elements2 = {
15005
- connectionBadge: document.getElementById("connection-badge"),
15046
+ connectionBadge: document.getElementById(
15047
+ "connection-badge"
15048
+ ),
15006
15049
  filterMode: document.getElementById("filter-mode"),
15007
15050
  roleSelect: document.getElementById("role-select"),
15008
15051
  entitySelect: document.getElementById("entity-select"),
15009
- roleSelectControl: document.getElementById("role-select-control"),
15010
- entitySelectControl: document.getElementById("entity-select-control"),
15052
+ roleSelectControl: document.getElementById(
15053
+ "role-select-control"
15054
+ ),
15055
+ entitySelectControl: document.getElementById(
15056
+ "entity-select-control"
15057
+ ),
15011
15058
  rightsFilter: document.getElementById("rights-filter"),
15012
- roleFilterControl: document.getElementById("role-filter-control"),
15013
- roleFilterButton: document.getElementById("role-filter-button"),
15059
+ roleFilterControl: document.getElementById(
15060
+ "role-filter-control"
15061
+ ),
15062
+ roleFilterButton: document.getElementById(
15063
+ "role-filter-button"
15064
+ ),
15014
15065
  roleFilterMenu: document.getElementById("role-filter-menu"),
15015
15066
  roleFilterList: document.getElementById("role-filter-list"),
15016
- roleFilterAll: document.getElementById("role-filter-all"),
15017
- roleFilterNone: document.getElementById("role-filter-none"),
15018
- rolesCustomOnlyGlobal: document.getElementById("roles-custom-only-global"),
15019
- tabButtons: Array.from(document.querySelectorAll(".tab-button")),
15067
+ roleFilterAll: document.getElementById(
15068
+ "role-filter-all"
15069
+ ),
15070
+ roleFilterNone: document.getElementById(
15071
+ "role-filter-none"
15072
+ ),
15073
+ rolesCustomOnlyGlobal: document.getElementById(
15074
+ "roles-custom-only-global"
15075
+ ),
15076
+ tabButtons: Array.from(
15077
+ document.querySelectorAll(".tab-button")
15078
+ ),
15020
15079
  tabPrivileges: document.getElementById("tab-privileges"),
15021
15080
  tabAssignments: document.getElementById("tab-assignments"),
15022
15081
  tabDashboard: document.getElementById("tab-dashboard"),
15023
- assignmentMode: document.getElementById("assignment-mode"),
15024
- assignmentRoleSelect: document.getElementById("assignment-role-select"),
15025
- assignmentUserSelect: document.getElementById("assignment-user-select"),
15026
- assignmentTeamSelect: document.getElementById("assignment-team-select"),
15027
- assignmentRoleControl: document.getElementById("assignment-role-control"),
15028
- assignmentUserControl: document.getElementById("assignment-user-control"),
15029
- assignmentTeamControl: document.getElementById("assignment-team-control"),
15030
- assignmentTitle: document.getElementById("assignment-title"),
15082
+ assignmentMode: document.getElementById(
15083
+ "assignment-mode"
15084
+ ),
15085
+ assignmentRoleSelect: document.getElementById(
15086
+ "assignment-role-select"
15087
+ ),
15088
+ assignmentUserSelect: document.getElementById(
15089
+ "assignment-user-select"
15090
+ ),
15091
+ assignmentTeamSelect: document.getElementById(
15092
+ "assignment-team-select"
15093
+ ),
15094
+ assignmentRoleControl: document.getElementById(
15095
+ "assignment-role-control"
15096
+ ),
15097
+ assignmentUserControl: document.getElementById(
15098
+ "assignment-user-control"
15099
+ ),
15100
+ assignmentTeamControl: document.getElementById(
15101
+ "assignment-team-control"
15102
+ ),
15103
+ assignmentTitle: document.getElementById(
15104
+ "assignment-title"
15105
+ ),
15031
15106
  assignmentAdd: document.getElementById("assignment-add"),
15032
- assignmentRemove: document.getElementById("assignment-remove"),
15033
- assignmentCount: document.getElementById("assignment-count"),
15034
- assignmentSelectAll: document.getElementById("assignment-select-all"),
15035
- assignmentClear: document.getElementById("assignment-clear"),
15036
- assignmentStatus: document.getElementById("assignment-status"),
15037
- assignmentTableBody: document.getElementById("assignment-table-body"),
15107
+ assignmentRemove: document.getElementById(
15108
+ "assignment-remove"
15109
+ ),
15110
+ assignmentCount: document.getElementById(
15111
+ "assignment-count"
15112
+ ),
15113
+ assignmentSelectAll: document.getElementById(
15114
+ "assignment-select-all"
15115
+ ),
15116
+ assignmentClear: document.getElementById(
15117
+ "assignment-clear"
15118
+ ),
15119
+ assignmentStatus: document.getElementById(
15120
+ "assignment-status"
15121
+ ),
15122
+ assignmentTableBody: document.getElementById(
15123
+ "assignment-table-body"
15124
+ ),
15038
15125
  assignmentSortButtons: Array.from(
15039
15126
  document.querySelectorAll("[data-assign-sort]")
15040
15127
  ),
15041
- assignmentFilterAssigned: document.getElementById("assignment-filter-assigned"),
15042
- assignmentSearch: document.getElementById("assignment-search"),
15043
- controlsPrivileges: document.getElementById("controls-privileges"),
15044
- controlsAssignments: document.getElementById("controls-assignments"),
15045
- controlsDashboard: document.getElementById("controls-dashboard"),
15046
- dashboardUserStatus: document.getElementById("dashboard-user-status"),
15047
- dashboardUserType: document.getElementById("dashboard-user-type"),
15048
- dashboardBusinessUnitSelect: document.getElementById("dashboard-bu-select"),
15049
- dashboardRoleSelect: document.getElementById("dashboard-role-select"),
15050
- dashboardTeamSelect: document.getElementById("dashboard-team-select"),
15051
- dashboardExport: document.getElementById("dashboard-export"),
15052
- dashboardLoading: document.getElementById("dashboard-loading"),
15053
- dashboardLoadingBar: document.getElementById("dashboard-loading-bar"),
15054
- dashboardLoadingText: document.getElementById("dashboard-loading-text"),
15128
+ assignmentFilterAssigned: document.getElementById(
15129
+ "assignment-filter-assigned"
15130
+ ),
15131
+ assignmentSearch: document.getElementById(
15132
+ "assignment-search"
15133
+ ),
15134
+ privilegeSearch: document.getElementById(
15135
+ "privilege-search"
15136
+ ),
15137
+ controlsPrivileges: document.getElementById(
15138
+ "controls-privileges"
15139
+ ),
15140
+ controlsAssignments: document.getElementById(
15141
+ "controls-assignments"
15142
+ ),
15143
+ controlsDashboard: document.getElementById(
15144
+ "controls-dashboard"
15145
+ ),
15146
+ dashboardUserStatus: document.getElementById(
15147
+ "dashboard-user-status"
15148
+ ),
15149
+ dashboardUserType: document.getElementById(
15150
+ "dashboard-user-type"
15151
+ ),
15152
+ dashboardBusinessUnitSelect: document.getElementById(
15153
+ "dashboard-bu-select"
15154
+ ),
15155
+ dashboardRoleSelect: document.getElementById(
15156
+ "dashboard-role-select"
15157
+ ),
15158
+ dashboardTeamSelect: document.getElementById(
15159
+ "dashboard-team-select"
15160
+ ),
15161
+ dashboardExport: document.getElementById(
15162
+ "dashboard-export"
15163
+ ),
15164
+ dashboardLoading: document.getElementById(
15165
+ "dashboard-loading"
15166
+ ),
15167
+ dashboardLoadingBar: document.getElementById(
15168
+ "dashboard-loading-bar"
15169
+ ),
15170
+ dashboardLoadingText: document.getElementById(
15171
+ "dashboard-loading-text"
15172
+ ),
15055
15173
  dashboardChartCards: Array.from(
15056
15174
  document.querySelectorAll("[data-dashboard-chart]")
15057
15175
  ),
@@ -15059,38 +15177,84 @@ ${logTarget.textContent}`;
15059
15177
  document.querySelectorAll(".chart-expand")
15060
15178
  ),
15061
15179
  chartModal: document.getElementById("chart-modal"),
15062
- chartModalTitle: document.getElementById("chart-modal-title"),
15063
- chartModalCanvas: document.getElementById("chart-modal-canvas"),
15064
- chartModalClose: document.getElementById("chart-modal-close"),
15065
- chartModalBackdrop: document.querySelector("[data-modal-close='true']"),
15180
+ chartModalTitle: document.getElementById(
15181
+ "chart-modal-title"
15182
+ ),
15183
+ chartModalCanvas: document.getElementById(
15184
+ "chart-modal-canvas"
15185
+ ),
15186
+ chartModalClose: document.getElementById(
15187
+ "chart-modal-close"
15188
+ ),
15189
+ chartModalBackdrop: document.querySelector(
15190
+ "[data-modal-close='true']"
15191
+ ),
15066
15192
  chartModalBody: document.querySelector(".modal-body"),
15067
- metricHumanActive: document.getElementById("metric-human-active"),
15068
- metricHumanInactive: document.getElementById("metric-human-inactive"),
15069
- metricAppActive: document.getElementById("metric-app-active"),
15070
- metricAppInactive: document.getElementById("metric-app-inactive"),
15071
- metricCustomRoles: document.getElementById("metric-custom-roles"),
15072
- metricManagedRoles: document.getElementById("metric-managed-roles"),
15073
- metricRolesWithoutUsers: document.getElementById("metric-roles-without-users"),
15074
- metricTotalTeams: document.getElementById("metric-total-teams"),
15075
- chartUsersByRole: document.getElementById("chart-users-by-role"),
15076
- chartTeamsByRole: document.getElementById("chart-teams-by-role"),
15077
- chartUsersByBusinessUnit: document.getElementById("chart-users-by-bu"),
15078
- chartUsersByTeam: document.getElementById("chart-users-by-team"),
15193
+ metricHumanActive: document.getElementById(
15194
+ "metric-human-active"
15195
+ ),
15196
+ metricHumanInactive: document.getElementById(
15197
+ "metric-human-inactive"
15198
+ ),
15199
+ metricAppActive: document.getElementById(
15200
+ "metric-app-active"
15201
+ ),
15202
+ metricAppInactive: document.getElementById(
15203
+ "metric-app-inactive"
15204
+ ),
15205
+ metricCustomRoles: document.getElementById(
15206
+ "metric-custom-roles"
15207
+ ),
15208
+ metricManagedRoles: document.getElementById(
15209
+ "metric-managed-roles"
15210
+ ),
15211
+ metricRolesWithoutUsers: document.getElementById(
15212
+ "metric-roles-without-users"
15213
+ ),
15214
+ metricTotalTeams: document.getElementById(
15215
+ "metric-total-teams"
15216
+ ),
15217
+ chartUsersByRole: document.getElementById(
15218
+ "chart-users-by-role"
15219
+ ),
15220
+ chartTeamsByRole: document.getElementById(
15221
+ "chart-teams-by-role"
15222
+ ),
15223
+ chartUsersByBusinessUnit: document.getElementById(
15224
+ "chart-users-by-bu"
15225
+ ),
15226
+ chartUsersByTeam: document.getElementById(
15227
+ "chart-users-by-team"
15228
+ ),
15079
15229
  applyBtn: document.getElementById("apply-btn"),
15080
15230
  undoBtn: document.getElementById("undo-btn"),
15081
15231
  refreshBtn: document.getElementById("refresh-btn"),
15082
15232
  pendingCount: document.getElementById("pending-count"),
15083
15233
  tableTitle: document.getElementById("table-title"),
15084
- privilegesTable: document.querySelector("#privileges-table tbody"),
15085
- privilegesTableRoot: document.getElementById("privileges-table"),
15234
+ privilegesTable: document.querySelector(
15235
+ "#privileges-table tbody"
15236
+ ),
15237
+ privilegesTableRoot: document.getElementById(
15238
+ "privileges-table"
15239
+ ),
15086
15240
  tableLoading: document.getElementById("table-loading"),
15087
15241
  tableEmpty: document.getElementById("table-empty"),
15088
15242
  loadingBar: document.getElementById("loading-bar"),
15089
15243
  loadingText: document.getElementById("loading-text"),
15090
15244
  log: document.getElementById("log"),
15091
15245
  themeToggle: document.getElementById("theme-toggle"),
15092
- sortButtons: Array.from(document.querySelectorAll(".sort-button")),
15093
- filterSelects: Array.from(document.querySelectorAll(".filter-select"))
15246
+ sortButtons: Array.from(
15247
+ document.querySelectorAll(".sort-button")
15248
+ ),
15249
+ bulkSelects: Array.from(
15250
+ document.querySelectorAll("[data-bulk-select]")
15251
+ ),
15252
+ bulkApplyButtons: Array.from(
15253
+ document.querySelectorAll("[data-bulk-apply]")
15254
+ ),
15255
+ filterSelects: Array.from(
15256
+ document.querySelectorAll(".filter-select")
15257
+ )
15094
15258
  };
15095
15259
  initLogger(elements2.log);
15096
15260
  function setTableTitle(text) {
@@ -15327,14 +15491,18 @@ ${logTarget.textContent}`;
15327
15491
  }
15328
15492
  function setLoading(active, message) {
15329
15493
  state.loading.active = active;
15494
+ const hasLoadedData = state.cacheLoaded;
15330
15495
  if (elements2.tableLoading) {
15331
15496
  elements2.tableLoading.classList.toggle("hidden", !active);
15332
15497
  }
15333
15498
  if (elements2.privilegesTableRoot) {
15334
- elements2.privilegesTableRoot.classList.toggle("hidden", active);
15499
+ elements2.privilegesTableRoot.classList.toggle(
15500
+ "hidden",
15501
+ active || !hasLoadedData
15502
+ );
15335
15503
  }
15336
15504
  if (elements2.tableEmpty) {
15337
- elements2.tableEmpty.classList.toggle("hidden", active || state.cacheLoaded);
15505
+ elements2.tableEmpty.classList.toggle("hidden", active || hasLoadedData);
15338
15506
  }
15339
15507
  if (message && elements2.loadingText) {
15340
15508
  elements2.loadingText.textContent = message;
@@ -15400,10 +15568,16 @@ ${logTarget.textContent}`;
15400
15568
  elements2.tabDashboard.classList.toggle("hidden", tab !== "dashboard");
15401
15569
  }
15402
15570
  if (elements2.controlsPrivileges) {
15403
- elements2.controlsPrivileges.classList.toggle("hidden", tab !== "privileges");
15571
+ elements2.controlsPrivileges.classList.toggle(
15572
+ "hidden",
15573
+ tab !== "privileges"
15574
+ );
15404
15575
  }
15405
15576
  if (elements2.controlsAssignments) {
15406
- elements2.controlsAssignments.classList.toggle("hidden", tab !== "assignments");
15577
+ elements2.controlsAssignments.classList.toggle(
15578
+ "hidden",
15579
+ tab !== "assignments"
15580
+ );
15407
15581
  }
15408
15582
  if (elements2.controlsDashboard) {
15409
15583
  elements2.controlsDashboard.classList.toggle("hidden", tab !== "dashboard");
@@ -15475,7 +15649,9 @@ ${logTarget.textContent}`;
15475
15649
  });
15476
15650
  return [...filtered].sort((a, b) => {
15477
15651
  if (state.assignmentSort.column === "label") {
15478
- const cmp = a.label.localeCompare(b.label, void 0, { sensitivity: "base" });
15652
+ const cmp = a.label.localeCompare(b.label, void 0, {
15653
+ sensitivity: "base"
15654
+ });
15479
15655
  return state.assignmentSort.direction === "asc" ? cmp : -cmp;
15480
15656
  }
15481
15657
  const aRank = a.assigned ? 1 : 2;
@@ -15620,12 +15796,146 @@ ${logTarget.textContent}`;
15620
15796
  for (const button of elements2.sortButtons) {
15621
15797
  button.classList.remove("sort-asc", "sort-desc");
15622
15798
  if (button.dataset.sort === state.sort.column) {
15623
- button.classList.add(state.sort.direction === "asc" ? "sort-asc" : "sort-desc");
15799
+ button.classList.add(
15800
+ state.sort.direction === "asc" ? "sort-asc" : "sort-desc"
15801
+ );
15624
15802
  }
15625
15803
  }
15626
15804
  }
15627
15805
  function isPrivilegeAvailable(row, privilege) {
15628
- return Boolean(state.privilegeIdByKey.get(`${row.entityLogicalName}:${privilege}`));
15806
+ return Boolean(
15807
+ state.privilegeIdByKey.get(`${row.entityLogicalName}:${privilege}`)
15808
+ );
15809
+ }
15810
+ function getAllowedLevelsForPrivilege(row, privilege) {
15811
+ const privilegeId = state.privilegeIdByKey.get(
15812
+ `${row.entityLogicalName}:${privilege}`
15813
+ );
15814
+ if (!privilegeId) {
15815
+ return [];
15816
+ }
15817
+ const info = state.privilegeInfoById.get(privilegeId);
15818
+ const allowedLevels = ["none"];
15819
+ if (info?.canBeBasic) {
15820
+ allowedLevels.push("user");
15821
+ }
15822
+ if (info?.canBeLocal) {
15823
+ allowedLevels.push("businessUnit");
15824
+ }
15825
+ if (info?.canBeDeep) {
15826
+ allowedLevels.push("parentChild");
15827
+ }
15828
+ if (info?.canBeGlobal) {
15829
+ allowedLevels.push("organization");
15830
+ }
15831
+ return allowedLevels;
15832
+ }
15833
+ function getRoleIdForVisibleRow(row, isRoleMode) {
15834
+ if (isRoleMode) {
15835
+ return elements2.roleSelect.value || void 0;
15836
+ }
15837
+ return row.roleId;
15838
+ }
15839
+ function renderBulkControls(rows, isRoleMode) {
15840
+ const selectByPrivilege = /* @__PURE__ */ new Map();
15841
+ for (const select of elements2.bulkSelects) {
15842
+ const privilege = select.dataset.bulkSelect;
15843
+ if (!privilege) {
15844
+ continue;
15845
+ }
15846
+ selectByPrivilege.set(privilege, select);
15847
+ }
15848
+ const buttonByPrivilege = /* @__PURE__ */ new Map();
15849
+ for (const button of elements2.bulkApplyButtons) {
15850
+ const privilege = button.dataset.bulkApply;
15851
+ if (!privilege) {
15852
+ continue;
15853
+ }
15854
+ buttonByPrivilege.set(privilege, button);
15855
+ }
15856
+ for (const privilege of accessRights) {
15857
+ const select = selectByPrivilege.get(privilege);
15858
+ const button = buttonByPrivilege.get(privilege);
15859
+ if (!select || !button) {
15860
+ continue;
15861
+ }
15862
+ const allowedLevels = /* @__PURE__ */ new Set();
15863
+ let hasEditableVisibleRows = false;
15864
+ for (const row of rows) {
15865
+ const roleId = getRoleIdForVisibleRow(row, isRoleMode);
15866
+ if (!roleId) {
15867
+ continue;
15868
+ }
15869
+ const rowAllowedLevels = getAllowedLevelsForPrivilege(row, privilege);
15870
+ if (rowAllowedLevels.length === 0) {
15871
+ continue;
15872
+ }
15873
+ hasEditableVisibleRows = true;
15874
+ for (const level of rowAllowedLevels) {
15875
+ allowedLevels.add(level);
15876
+ }
15877
+ }
15878
+ select.innerHTML = "";
15879
+ const placeholder = document.createElement("option");
15880
+ placeholder.value = "";
15881
+ placeholder.textContent = UI_TEXT.bulkUpdateSelect;
15882
+ select.appendChild(placeholder);
15883
+ for (const optionMeta of levelOptions) {
15884
+ if (!allowedLevels.has(optionMeta.level)) {
15885
+ continue;
15886
+ }
15887
+ const option = document.createElement("option");
15888
+ option.value = optionMeta.level;
15889
+ option.textContent = `${optionMeta.icon} ${optionMeta.label}`;
15890
+ select.appendChild(option);
15891
+ }
15892
+ select.disabled = !hasEditableVisibleRows;
15893
+ button.disabled = !hasEditableVisibleRows;
15894
+ button.textContent = UI_TEXT.bulkUpdateApply;
15895
+ button.title = hasEditableVisibleRows ? UI_TEXT.bulkUpdateApply : UI_TEXT.bulkUpdateNoRights;
15896
+ }
15897
+ }
15898
+ function applyBulkUpdate(privilege, level) {
15899
+ const isRoleMode = state.tableMode === "role";
15900
+ const visibleRows = applySortAndFilters(state.privilegeRows);
15901
+ let updatedCount = 0;
15902
+ let skippedCount = 0;
15903
+ for (const row of visibleRows) {
15904
+ const roleId = getRoleIdForVisibleRow(row, isRoleMode);
15905
+ if (!roleId) {
15906
+ skippedCount++;
15907
+ continue;
15908
+ }
15909
+ const allowedLevels = getAllowedLevelsForPrivilege(row, privilege);
15910
+ if (allowedLevels.length === 0 || !allowedLevels.includes(level)) {
15911
+ skippedCount++;
15912
+ continue;
15913
+ }
15914
+ const changed = updatePendingChange(
15915
+ roleId,
15916
+ row.entityLogicalName,
15917
+ privilege,
15918
+ level
15919
+ );
15920
+ if (changed) {
15921
+ updatedCount++;
15922
+ }
15923
+ }
15924
+ const privilegeLabel = privilegeLabels[privilege] ?? privilege;
15925
+ const levelLabel = levelOptions.find((option) => option.level === level)?.label ?? level;
15926
+ if (updatedCount > 0) {
15927
+ logMessage(
15928
+ formatBulkUpdatedVisible(
15929
+ privilegeLabel,
15930
+ levelLabel,
15931
+ updatedCount,
15932
+ skippedCount
15933
+ )
15934
+ );
15935
+ } else {
15936
+ logMessage(formatBulkUpdateNoRows(privilegeLabel));
15937
+ }
15938
+ renderPrivilegeTable();
15629
15939
  }
15630
15940
  function hasAnyRights(row) {
15631
15941
  return accessRights.some((privilege) => {
@@ -15646,11 +15956,17 @@ ${logTarget.textContent}`;
15646
15956
  );
15647
15957
  }
15648
15958
  function updatePendingChange(roleId, entityLogicalName, privilege, level) {
15649
- const currentLevel = getCurrentPrivilegeLevel(roleId, entityLogicalName, privilege);
15959
+ const currentLevel = getCurrentPrivilegeLevel(
15960
+ roleId,
15961
+ entityLogicalName,
15962
+ privilege
15963
+ );
15650
15964
  const existing = findPendingChange(roleId, entityLogicalName, privilege);
15651
15965
  if (level === currentLevel) {
15652
15966
  if (existing) {
15653
- state.pendingChanges = state.pendingChanges.filter((change) => change !== existing);
15967
+ state.pendingChanges = state.pendingChanges.filter(
15968
+ (change) => change !== existing
15969
+ );
15654
15970
  }
15655
15971
  updatePendingUi();
15656
15972
  return false;
@@ -15693,6 +16009,14 @@ ${logTarget.textContent}`;
15693
16009
  }
15694
16010
  function applySortAndFilters(rows) {
15695
16011
  const filtered = rows.filter((row) => {
16012
+ if (state.privilegeSearch) {
16013
+ const term = state.privilegeSearch.toLowerCase();
16014
+ const labelMatch = row.entityLabel.toLowerCase().includes(term);
16015
+ const logicalNameMatch = row.entityLogicalName.toLowerCase().includes(term);
16016
+ if (!labelMatch && !logicalNameMatch) {
16017
+ return false;
16018
+ }
16019
+ }
15696
16020
  if (state.rightsFilter === "with" && !hasAnyRights(row)) {
15697
16021
  return false;
15698
16022
  }
@@ -15722,18 +16046,30 @@ ${logTarget.textContent}`;
15722
16046
  });
15723
16047
  const sorted = [...filtered].sort((a, b) => {
15724
16048
  if (state.sort.column === "label") {
15725
- const cmp = a.entityLabel.localeCompare(b.entityLabel, void 0, { sensitivity: "base" });
16049
+ const cmp = a.entityLabel.localeCompare(b.entityLabel, void 0, {
16050
+ sensitivity: "base"
16051
+ });
15726
16052
  return state.sort.direction === "asc" ? cmp : -cmp;
15727
16053
  }
15728
16054
  const privilege = state.sort.column;
15729
16055
  const aAvailable = isPrivilegeAvailable(a, privilege);
15730
16056
  const bAvailable = isPrivilegeAvailable(b, privilege);
15731
- const aRank = getPrivilegeSortRank(a[privilege], aAvailable, state.sort.direction);
15732
- const bRank = getPrivilegeSortRank(b[privilege], bAvailable, state.sort.direction);
16057
+ const aRank = getPrivilegeSortRank(
16058
+ a[privilege],
16059
+ aAvailable,
16060
+ state.sort.direction
16061
+ );
16062
+ const bRank = getPrivilegeSortRank(
16063
+ b[privilege],
16064
+ bAvailable,
16065
+ state.sort.direction
16066
+ );
15733
16067
  if (aRank !== bRank) {
15734
16068
  return aRank - bRank;
15735
16069
  }
15736
- return a.entityLabel.localeCompare(b.entityLabel, void 0, { sensitivity: "base" });
16070
+ return a.entityLabel.localeCompare(b.entityLabel, void 0, {
16071
+ sensitivity: "base"
16072
+ });
15737
16073
  });
15738
16074
  return sorted;
15739
16075
  }
@@ -15741,6 +16077,7 @@ ${logTarget.textContent}`;
15741
16077
  elements2.privilegesTable.innerHTML = "";
15742
16078
  const isRoleMode = state.tableMode === "role";
15743
16079
  const rows = applySortAndFilters(state.privilegeRows);
16080
+ renderBulkControls(rows, isRoleMode);
15744
16081
  if (elements2.tableEmpty) {
15745
16082
  elements2.tableEmpty.classList.toggle("hidden", state.cacheLoaded);
15746
16083
  }
@@ -15764,26 +16101,15 @@ ${logTarget.textContent}`;
15764
16101
  tr.appendChild(ownershipCell);
15765
16102
  for (const privilege of accessRights) {
15766
16103
  const td = document.createElement("td");
15767
- const privilegeId = state.privilegeIdByKey.get(`${row.entityLogicalName}:${privilege}`);
16104
+ const privilegeId = state.privilegeIdByKey.get(
16105
+ `${row.entityLogicalName}:${privilege}`
16106
+ );
15768
16107
  if (!privilegeId) {
15769
16108
  td.textContent = UI_TEXT.tableCellEmpty;
15770
16109
  tr.appendChild(td);
15771
16110
  continue;
15772
16111
  }
15773
- const info = state.privilegeInfoById.get(privilegeId);
15774
- const allowedLevels = ["none"];
15775
- if (info?.canBeBasic) {
15776
- allowedLevels.push("user");
15777
- }
15778
- if (info?.canBeLocal) {
15779
- allowedLevels.push("businessUnit");
15780
- }
15781
- if (info?.canBeDeep) {
15782
- allowedLevels.push("parentChild");
15783
- }
15784
- if (info?.canBeGlobal) {
15785
- allowedLevels.push("organization");
15786
- }
16112
+ const allowedLevels = getAllowedLevelsForPrivilege(row, privilege);
15787
16113
  const select = document.createElement("select");
15788
16114
  select.className = "level-select";
15789
16115
  select.dataset.entityLogicalName = row.entityLogicalName;
@@ -15793,6 +16119,12 @@ ${logTarget.textContent}`;
15793
16119
  } else if (row.roleId) {
15794
16120
  select.dataset.roleId = row.roleId;
15795
16121
  }
16122
+ const pending = select.dataset.roleId ? findPendingChange(
16123
+ select.dataset.roleId,
16124
+ row.entityLogicalName,
16125
+ privilege
16126
+ ) : void 0;
16127
+ const effectiveLevel = pending?.level ?? row[privilege];
15796
16128
  for (const optionMeta of levelOptions) {
15797
16129
  if (!allowedLevels.includes(optionMeta.level)) {
15798
16130
  continue;
@@ -15802,7 +16134,7 @@ ${logTarget.textContent}`;
15802
16134
  option.textContent = `${optionMeta.icon} ${optionMeta.label}`;
15803
16135
  option.className = optionMeta.className;
15804
16136
  option.style.color = getLevelColor(optionMeta.level);
15805
- if (row[privilege] === optionMeta.level) {
16137
+ if (effectiveLevel === optionMeta.level) {
15806
16138
  option.selected = true;
15807
16139
  }
15808
16140
  select.appendChild(option);
@@ -15813,7 +16145,12 @@ ${logTarget.textContent}`;
15813
16145
  return;
15814
16146
  }
15815
16147
  const level = select.value;
15816
- const isPending = updatePendingChange(roleId, row.entityLogicalName, privilege, level);
16148
+ const isPending = updatePendingChange(
16149
+ roleId,
16150
+ row.entityLogicalName,
16151
+ privilege,
16152
+ level
16153
+ );
15817
16154
  setPendingClass(select, isPending);
15818
16155
  });
15819
16156
  select.disabled = !isRoleMode && !row.roleId;
@@ -15821,12 +16158,7 @@ ${logTarget.textContent}`;
15821
16158
  select.addEventListener("change", () => {
15822
16159
  applyLevelClass(select, select.value);
15823
16160
  });
15824
- if (select.dataset.roleId) {
15825
- const pending = Boolean(
15826
- findPendingChange(select.dataset.roleId, row.entityLogicalName, privilege)
15827
- );
15828
- setPendingClass(select, pending);
15829
- }
16161
+ setPendingClass(select, Boolean(pending));
15830
16162
  td.appendChild(select);
15831
16163
  tr.appendChild(td);
15832
16164
  }
@@ -16041,7 +16373,8 @@ ${logTarget.textContent}`;
16041
16373
  { prefix: "prvDelete", access: "delete" },
16042
16374
  { prefix: "prvAppendTo", access: "appendto" },
16043
16375
  { prefix: "prvAppend", access: "append" },
16044
- { prefix: "prvAssign", access: "assign" }
16376
+ { prefix: "prvAssign", access: "assign" },
16377
+ { prefix: "prvShare", access: "share" }
16045
16378
  ];
16046
16379
  for (const { prefix, access } of prefixes) {
16047
16380
  if (name.startsWith(prefix)) {
@@ -16066,7 +16399,8 @@ ${logTarget.textContent}`;
16066
16399
  delete: "none",
16067
16400
  append: "none",
16068
16401
  appendto: "none",
16069
- assign: "none"
16402
+ assign: "none",
16403
+ share: "none"
16070
16404
  });
16071
16405
  }
16072
16406
  return roleMap.get(entityLogicalName);
@@ -16125,7 +16459,11 @@ ${logTarget.textContent}`;
16125
16459
  elements2.assignmentRoleSelect,
16126
16460
  state.roles,
16127
16461
  (item) => item.id,
16128
- (item) => formatAssignmentSelectLabel(item.name, counts.get(item.id) ?? 0, unitLabel)
16462
+ (item) => formatAssignmentSelectLabel(
16463
+ item.name,
16464
+ counts.get(item.id) ?? 0,
16465
+ unitLabel
16466
+ )
16129
16467
  );
16130
16468
  }
16131
16469
  function renderAssignmentUserOptionsWithCounts(unitLabel, counts) {
@@ -16133,7 +16471,11 @@ ${logTarget.textContent}`;
16133
16471
  elements2.assignmentUserSelect,
16134
16472
  state.users,
16135
16473
  (item) => item.id,
16136
- (item) => formatAssignmentSelectLabel(item.name, counts.get(item.id) ?? 0, unitLabel)
16474
+ (item) => formatAssignmentSelectLabel(
16475
+ item.name,
16476
+ counts.get(item.id) ?? 0,
16477
+ unitLabel
16478
+ )
16137
16479
  );
16138
16480
  }
16139
16481
  function renderAssignmentTeamOptionsWithCounts(unitLabel, counts) {
@@ -16141,7 +16483,11 @@ ${logTarget.textContent}`;
16141
16483
  elements2.assignmentTeamSelect,
16142
16484
  state.teams,
16143
16485
  (item) => item.id,
16144
- (item) => formatAssignmentSelectLabel(item.name, counts.get(item.id) ?? 0, unitLabel)
16486
+ (item) => formatAssignmentSelectLabel(
16487
+ item.name,
16488
+ counts.get(item.id) ?? 0,
16489
+ unitLabel
16490
+ )
16145
16491
  );
16146
16492
  }
16147
16493
  async function getAssignedUsersForRoleCached(roleId) {
@@ -16182,7 +16528,11 @@ ${logTarget.textContent}`;
16182
16528
  state.roles.length ? Promise.resolve() : loadRoles2(),
16183
16529
  state.teamsLoaded ? Promise.resolve() : loadTeams2()
16184
16530
  ]);
16185
- setDashboardLoadingProgress(++currentStep, totalSteps, UI_TEXT.loadingDashboardPeople);
16531
+ setDashboardLoadingProgress(
16532
+ ++currentStep,
16533
+ totalSteps,
16534
+ UI_TEXT.loadingDashboardPeople
16535
+ );
16186
16536
  const [users, businessUnits, teamMemberships] = await Promise.all([
16187
16537
  loadUsersForDashboard(),
16188
16538
  loadBusinessUnits(),
@@ -16407,9 +16757,15 @@ ${logTarget.textContent}`;
16407
16757
  continue;
16408
16758
  }
16409
16759
  if (isApplicationUser(user)) {
16410
- appCounts.set(user.businessUnitId, (appCounts.get(user.businessUnitId) ?? 0) + 1);
16760
+ appCounts.set(
16761
+ user.businessUnitId,
16762
+ (appCounts.get(user.businessUnitId) ?? 0) + 1
16763
+ );
16411
16764
  } else {
16412
- humanCounts.set(user.businessUnitId, (humanCounts.get(user.businessUnitId) ?? 0) + 1);
16765
+ humanCounts.set(
16766
+ user.businessUnitId,
16767
+ (humanCounts.get(user.businessUnitId) ?? 0) + 1
16768
+ );
16413
16769
  }
16414
16770
  }
16415
16771
  const units = state.dashboardBusinessUnits.map((unit) => ({
@@ -16437,11 +16793,17 @@ ${logTarget.textContent}`;
16437
16793
  const appCounts = /* @__PURE__ */ new Map();
16438
16794
  for (const membership of state.dashboardTeamMemberships) {
16439
16795
  if (humanIds.has(membership.userId)) {
16440
- humanCounts.set(membership.teamId, (humanCounts.get(membership.teamId) ?? 0) + 1);
16796
+ humanCounts.set(
16797
+ membership.teamId,
16798
+ (humanCounts.get(membership.teamId) ?? 0) + 1
16799
+ );
16441
16800
  continue;
16442
16801
  }
16443
16802
  if (appIds.has(membership.userId)) {
16444
- appCounts.set(membership.teamId, (appCounts.get(membership.teamId) ?? 0) + 1);
16803
+ appCounts.set(
16804
+ membership.teamId,
16805
+ (appCounts.get(membership.teamId) ?? 0) + 1
16806
+ );
16445
16807
  }
16446
16808
  }
16447
16809
  const teamFilterId = elements2.dashboardTeamSelect?.value ?? "";
@@ -16564,7 +16926,9 @@ ${logTarget.textContent}`;
16564
16926
  if (!elements2.chartModalBody || !elements2.chartModalCanvas) {
16565
16927
  return;
16566
16928
  }
16567
- let empty = elements2.chartModalBody.querySelector(".chart-empty");
16929
+ let empty = elements2.chartModalBody.querySelector(
16930
+ ".chart-empty"
16931
+ );
16568
16932
  if (!empty) {
16569
16933
  empty = document.createElement("div");
16570
16934
  empty.className = "chart-empty";
@@ -16592,13 +16956,27 @@ ${logTarget.textContent}`;
16592
16956
  elements2.chartUsersByRole,
16593
16957
  usersByRoleData.labels,
16594
16958
  [
16595
- { label: "Human users", data: usersByRoleData.human, backgroundColor: "#d35400" },
16596
- { label: "Application users", data: usersByRoleData.app, backgroundColor: "#f39c4a" }
16959
+ {
16960
+ label: "Human users",
16961
+ data: usersByRoleData.human,
16962
+ backgroundColor: "#d35400"
16963
+ },
16964
+ {
16965
+ label: "Application users",
16966
+ data: usersByRoleData.app,
16967
+ backgroundColor: "#f39c4a"
16968
+ }
16597
16969
  ],
16598
16970
  true
16599
16971
  );
16600
- setChartEmptyState(elements2.chartUsersByRole, usersByRoleData.labels.length === 0);
16601
- const teamsByRoleCounts = buildTeamsByRoleCounts(businessUnitId, teamFilterId);
16972
+ setChartEmptyState(
16973
+ elements2.chartUsersByRole,
16974
+ usersByRoleData.labels.length === 0
16975
+ );
16976
+ const teamsByRoleCounts = buildTeamsByRoleCounts(
16977
+ businessUnitId,
16978
+ teamFilterId
16979
+ );
16602
16980
  const teamsByRoleData = buildRoleTotalChartData(
16603
16981
  teamsByRoleCounts,
16604
16982
  roleFilterId || null,
@@ -16610,34 +16988,63 @@ ${logTarget.textContent}`;
16610
16988
  elements2.chartTeamsByRole,
16611
16989
  teamsByRoleData.labels,
16612
16990
  [
16613
- { label: "Teams", data: teamsByRoleData.values, backgroundColor: "#1f7a8c" }
16991
+ {
16992
+ label: "Teams",
16993
+ data: teamsByRoleData.values,
16994
+ backgroundColor: "#1f7a8c"
16995
+ }
16614
16996
  ]
16615
16997
  );
16616
- setChartEmptyState(elements2.chartTeamsByRole, teamsByRoleData.labels.length === 0);
16998
+ setChartEmptyState(
16999
+ elements2.chartTeamsByRole,
17000
+ teamsByRoleData.labels.length === 0
17001
+ );
16617
17002
  const usersByBuData = buildUsersByBusinessUnitData(filteredUsers, maxRoles);
16618
17003
  state.dashboardCharts.usersByBusinessUnit = renderOrUpdateBarChart(
16619
17004
  state.dashboardCharts.usersByBusinessUnit,
16620
17005
  elements2.chartUsersByBusinessUnit,
16621
17006
  usersByBuData.labels,
16622
17007
  [
16623
- { label: "Human users", data: usersByBuData.human, backgroundColor: "#d35400" },
16624
- { label: "Application users", data: usersByBuData.app, backgroundColor: "#f39c4a" }
17008
+ {
17009
+ label: "Human users",
17010
+ data: usersByBuData.human,
17011
+ backgroundColor: "#d35400"
17012
+ },
17013
+ {
17014
+ label: "Application users",
17015
+ data: usersByBuData.app,
17016
+ backgroundColor: "#f39c4a"
17017
+ }
16625
17018
  ],
16626
17019
  true
16627
17020
  );
16628
- setChartEmptyState(elements2.chartUsersByBusinessUnit, usersByBuData.labels.length === 0);
17021
+ setChartEmptyState(
17022
+ elements2.chartUsersByBusinessUnit,
17023
+ usersByBuData.labels.length === 0
17024
+ );
16629
17025
  const usersByTeamData = buildUsersByTeamData(filteredUsers, maxRoles);
16630
17026
  state.dashboardCharts.usersByTeam = renderOrUpdateBarChart(
16631
17027
  state.dashboardCharts.usersByTeam,
16632
17028
  elements2.chartUsersByTeam,
16633
17029
  usersByTeamData.labels,
16634
17030
  [
16635
- { label: "Human users", data: usersByTeamData.human, backgroundColor: "#d35400" },
16636
- { label: "Application users", data: usersByTeamData.app, backgroundColor: "#f39c4a" }
17031
+ {
17032
+ label: "Human users",
17033
+ data: usersByTeamData.human,
17034
+ backgroundColor: "#d35400"
17035
+ },
17036
+ {
17037
+ label: "Application users",
17038
+ data: usersByTeamData.app,
17039
+ backgroundColor: "#f39c4a"
17040
+ }
16637
17041
  ],
16638
17042
  true
16639
17043
  );
16640
- setChartEmptyState(elements2.chartUsersByTeam, usersByTeamData.labels.length === 0);
17044
+ setChartEmptyState(
17045
+ elements2.chartUsersByTeam,
17046
+ usersByTeamData.labels.length === 0
17047
+ );
16641
17048
  }
16642
17049
  function getDashboardChartData(kind, maxRoles) {
16643
17050
  const roleFilterId = elements2.dashboardRoleSelect?.value ?? "";
@@ -16658,13 +17065,24 @@ ${logTarget.textContent}`;
16658
17065
  labels: data.labels,
16659
17066
  stacked: true,
16660
17067
  datasets: [
16661
- { label: "Human users", data: data.human, backgroundColor: "#d35400" },
16662
- { label: "Application users", data: data.app, backgroundColor: "#f39c4a" }
17068
+ {
17069
+ label: "Human users",
17070
+ data: data.human,
17071
+ backgroundColor: "#d35400"
17072
+ },
17073
+ {
17074
+ label: "Application users",
17075
+ data: data.app,
17076
+ backgroundColor: "#f39c4a"
17077
+ }
16663
17078
  ]
16664
17079
  };
16665
17080
  }
16666
17081
  case "teamsByRole": {
16667
- const teamsByRoleCounts = buildTeamsByRoleCounts(businessUnitId, teamFilterId);
17082
+ const teamsByRoleCounts = buildTeamsByRoleCounts(
17083
+ businessUnitId,
17084
+ teamFilterId
17085
+ );
16668
17086
  const data = buildRoleTotalChartData(
16669
17087
  teamsByRoleCounts,
16670
17088
  roleFilterId || null,
@@ -16675,7 +17093,9 @@ ${logTarget.textContent}`;
16675
17093
  title: "Teams per role",
16676
17094
  labels: data.labels,
16677
17095
  stacked: false,
16678
- datasets: [{ label: "Teams", data: data.values, backgroundColor: "#1f7a8c" }]
17096
+ datasets: [
17097
+ { label: "Teams", data: data.values, backgroundColor: "#1f7a8c" }
17098
+ ]
16679
17099
  };
16680
17100
  }
16681
17101
  case "usersByBusinessUnit": {
@@ -16685,8 +17105,16 @@ ${logTarget.textContent}`;
16685
17105
  labels: data.labels,
16686
17106
  stacked: true,
16687
17107
  datasets: [
16688
- { label: "Human users", data: data.human, backgroundColor: "#d35400" },
16689
- { label: "Application users", data: data.app, backgroundColor: "#f39c4a" }
17108
+ {
17109
+ label: "Human users",
17110
+ data: data.human,
17111
+ backgroundColor: "#d35400"
17112
+ },
17113
+ {
17114
+ label: "Application users",
17115
+ data: data.app,
17116
+ backgroundColor: "#f39c4a"
17117
+ }
16690
17118
  ]
16691
17119
  };
16692
17120
  }
@@ -16697,8 +17125,16 @@ ${logTarget.textContent}`;
16697
17125
  labels: data.labels,
16698
17126
  stacked: true,
16699
17127
  datasets: [
16700
- { label: "Human users", data: data.human, backgroundColor: "#d35400" },
16701
- { label: "Application users", data: data.app, backgroundColor: "#f39c4a" }
17128
+ {
17129
+ label: "Human users",
17130
+ data: data.human,
17131
+ backgroundColor: "#d35400"
17132
+ },
17133
+ {
17134
+ label: "Application users",
17135
+ data: data.app,
17136
+ backgroundColor: "#f39c4a"
17137
+ }
16702
17138
  ]
16703
17139
  };
16704
17140
  }
@@ -16764,11 +17200,15 @@ ${logTarget.textContent}`;
16764
17200
  return isActiveUser(user) ? "Active" : "Inactive";
16765
17201
  }
16766
17202
  function buildUserRoleExportRows() {
16767
- const roleNameById = new Map(state.allRoles.map((role) => [role.id, role.name]));
17203
+ const roleNameById = new Map(
17204
+ state.allRoles.map((role) => [role.id, role.name])
17205
+ );
16768
17206
  const businessUnitById = new Map(
16769
17207
  state.dashboardBusinessUnits.map((unit) => [unit.id, unit.name])
16770
17208
  );
16771
- const usersById = new Map(state.dashboardUsers.map((user) => [user.id, user]));
17209
+ const usersById = new Map(
17210
+ state.dashboardUsers.map((user) => [user.id, user])
17211
+ );
16772
17212
  const rows = [
16773
17213
  ["User", "Email", "Status", "Business Unit", "Security Role"]
16774
17214
  ];
@@ -16791,7 +17231,9 @@ ${logTarget.textContent}`;
16791
17231
  }
16792
17232
  function buildUserTeamExportRows() {
16793
17233
  const teamNameById = new Map(state.teams.map((team) => [team.id, team.name]));
16794
- const usersById = new Map(state.dashboardUsers.map((user) => [user.id, user]));
17234
+ const usersById = new Map(
17235
+ state.dashboardUsers.map((user) => [user.id, user])
17236
+ );
16795
17237
  const rows = [["User", "Email", "Status", "Team"]];
16796
17238
  for (const membership of state.dashboardTeamMemberships) {
16797
17239
  const user = usersById.get(membership.userId);
@@ -16828,8 +17270,14 @@ ${logTarget.textContent}`;
16828
17270
  toolboxAPI2.fileSystem,
16829
17271
  toolboxAPI2.utils,
16830
17272
  [
16831
- { filename: `security-roles-users-${timestamp}.csv`, content: userRolesCsv },
16832
- { filename: `security-roles-teams-${timestamp}.csv`, content: userTeamsCsv }
17273
+ {
17274
+ filename: `security-roles-users-${timestamp}.csv`,
17275
+ content: userRolesCsv
17276
+ },
17277
+ {
17278
+ filename: `security-roles-teams-${timestamp}.csv`,
17279
+ content: userTeamsCsv
17280
+ }
16833
17281
  ],
16834
17282
  `security-roles-users-${timestamp}.csv`
16835
17283
  );
@@ -16872,7 +17320,10 @@ ${logTarget.textContent}`;
16872
17320
  canBeGlobal: Boolean(privilege.canbeglobal)
16873
17321
  });
16874
17322
  privilegeMeta.set(privilege.privilegeid, parsed);
16875
- privilegeIdByKey.set(`${parsed.entityLogicalName}:${parsed.access}`, privilege.privilegeid);
17323
+ privilegeIdByKey.set(
17324
+ `${parsed.entityLogicalName}:${parsed.access}`,
17325
+ privilege.privilegeid
17326
+ );
16876
17327
  }
16877
17328
  const rolePrivilegesCache = /* @__PURE__ */ new Map();
16878
17329
  const rolesToCache = state.allRoles.length > 0 ? state.allRoles : state.roles;
@@ -16896,7 +17347,11 @@ ${logTarget.textContent}`;
16896
17347
  rolePrivilegeCount += roleEntries.length;
16897
17348
  loadedRoles += 1;
16898
17349
  if (state.loading.active) {
16899
- updateLoadingProgress(loadedRoles, rolesToCache.length, UI_TEXT.loadingRolePrivileges);
17350
+ updateLoadingProgress(
17351
+ loadedRoles,
17352
+ rolesToCache.length,
17353
+ UI_TEXT.loadingRolePrivileges
17354
+ );
16900
17355
  }
16901
17356
  for (const entry of roleEntries) {
16902
17357
  const privilegeId = entry.PrivilegeId ?? entry.privilegeid ?? entry._privilegeid_value;
@@ -16909,7 +17364,10 @@ ${logTarget.textContent}`;
16909
17364
  continue;
16910
17365
  }
16911
17366
  const roleMap = rolePrivilegesCache.get(role.id);
16912
- const record = ensureRolePrivilegeRecord(roleMap, meta.entityLogicalName);
17367
+ const record = ensureRolePrivilegeRecord(
17368
+ roleMap,
17369
+ meta.entityLogicalName
17370
+ );
16913
17371
  record[meta.access] = mapPrivilegeLevelFromDepth(depth);
16914
17372
  }
16915
17373
  }
@@ -16919,7 +17377,11 @@ ${logTarget.textContent}`;
16919
17377
  state.rolePrivileges = rolePrivilegesCache;
16920
17378
  state.cacheLoaded = true;
16921
17379
  logMessage(
16922
- formatCachedPrivileges(privileges.length, rolePrivilegeCount, rolesToCache.length)
17380
+ formatCachedPrivileges(
17381
+ privileges.length,
17382
+ rolePrivilegeCount,
17383
+ rolesToCache.length
17384
+ )
16923
17385
  );
16924
17386
  return true;
16925
17387
  }
@@ -16969,7 +17431,8 @@ ${logTarget.textContent}`;
16969
17431
  delete: "none",
16970
17432
  append: "none",
16971
17433
  appendto: "none",
16972
- assign: "none"
17434
+ assign: "none",
17435
+ share: "none"
16973
17436
  };
16974
17437
  return {
16975
17438
  entityLabel: entity.displayName,
@@ -16981,7 +17444,8 @@ ${logTarget.textContent}`;
16981
17444
  delete: record.delete,
16982
17445
  append: record.append,
16983
17446
  appendto: record.appendto,
16984
- assign: record.assign
17447
+ assign: record.assign,
17448
+ share: record.share
16985
17449
  };
16986
17450
  });
16987
17451
  renderPrivilegeTable();
@@ -16994,8 +17458,12 @@ ${logTarget.textContent}`;
16994
17458
  return;
16995
17459
  }
16996
17460
  state.tableMode = "entity";
16997
- const entityLabel = state.entities.find((entity) => entity.logicalName === entityLogicalName)?.displayName;
16998
- const visibleRoles = state.roles.filter((role) => state.selectedRoleIds.has(role.id));
17461
+ const entityLabel = state.entities.find(
17462
+ (entity) => entity.logicalName === entityLogicalName
17463
+ )?.displayName;
17464
+ const visibleRoles = state.roles.filter(
17465
+ (role) => state.selectedRoleIds.has(role.id)
17466
+ );
16999
17467
  state.privilegeRows = visibleRoles.map((role) => {
17000
17468
  const record = state.rolePrivileges.get(role.id)?.get(entityLogicalName) ?? {
17001
17469
  create: "none",
@@ -17004,7 +17472,8 @@ ${logTarget.textContent}`;
17004
17472
  delete: "none",
17005
17473
  append: "none",
17006
17474
  appendto: "none",
17007
- assign: "none"
17475
+ assign: "none",
17476
+ share: "none"
17008
17477
  };
17009
17478
  return {
17010
17479
  roleId: role.id,
@@ -17017,11 +17486,14 @@ ${logTarget.textContent}`;
17017
17486
  delete: record.delete,
17018
17487
  append: record.append,
17019
17488
  appendto: record.appendto,
17020
- assign: record.assign
17489
+ assign: record.assign,
17490
+ share: record.share
17021
17491
  };
17022
17492
  });
17023
17493
  renderPrivilegeTable();
17024
- setTableTitle(formatPrivilegesForTableTitle(entityLabel ?? entityLogicalName));
17494
+ setTableTitle(
17495
+ formatPrivilegesForTableTitle(entityLabel ?? entityLogicalName)
17496
+ );
17025
17497
  }
17026
17498
  async function loadAssignmentView() {
17027
17499
  if (!state.allRoles.length) {
@@ -17039,7 +17511,10 @@ ${logTarget.textContent}`;
17039
17511
  activeUserIds
17040
17512
  );
17041
17513
  }
17042
- renderAssignmentRoleOptionsWithCounts(UI_TEXT.unitUsers, state.assignmentRoleUserCounts);
17514
+ renderAssignmentRoleOptionsWithCounts(
17515
+ UI_TEXT.unitUsers,
17516
+ state.assignmentRoleUserCounts
17517
+ );
17043
17518
  const roleId = elements2.assignmentRoleSelect.value;
17044
17519
  if (!roleId) {
17045
17520
  setAssignmentStatus(UI_TEXT.assignmentSelectRoleUsers);
@@ -17071,7 +17546,10 @@ ${logTarget.textContent}`;
17071
17546
  if (state.assignmentUserRoleCounts.size !== state.users.length) {
17072
17547
  state.assignmentUserRoleCounts = await loadUserAssignmentCounts();
17073
17548
  }
17074
- renderAssignmentUserOptionsWithCounts(UI_TEXT.unitRoles, state.assignmentUserRoleCounts);
17549
+ renderAssignmentUserOptionsWithCounts(
17550
+ UI_TEXT.unitRoles,
17551
+ state.assignmentUserRoleCounts
17552
+ );
17075
17553
  const userId = elements2.assignmentUserSelect.value;
17076
17554
  if (!userId) {
17077
17555
  setAssignmentStatus(UI_TEXT.assignmentSelectUserRoles);
@@ -17106,7 +17584,10 @@ ${logTarget.textContent}`;
17106
17584
  state.allRoles
17107
17585
  );
17108
17586
  }
17109
- renderAssignmentRoleOptionsWithCounts(UI_TEXT.unitTeams, state.assignmentRoleTeamCounts);
17587
+ renderAssignmentRoleOptionsWithCounts(
17588
+ UI_TEXT.unitTeams,
17589
+ state.assignmentRoleTeamCounts
17590
+ );
17110
17591
  const roleId = elements2.assignmentRoleSelect.value;
17111
17592
  if (!roleId) {
17112
17593
  setAssignmentStatus(UI_TEXT.assignmentSelectRoleTeams);
@@ -17138,7 +17619,10 @@ ${logTarget.textContent}`;
17138
17619
  if (state.assignmentTeamRoleCounts.size !== state.teams.length) {
17139
17620
  state.assignmentTeamRoleCounts = await loadTeamAssignmentCounts();
17140
17621
  }
17141
- renderAssignmentTeamOptionsWithCounts(UI_TEXT.unitRoles, state.assignmentTeamRoleCounts);
17622
+ renderAssignmentTeamOptionsWithCounts(
17623
+ UI_TEXT.unitRoles,
17624
+ state.assignmentTeamRoleCounts
17625
+ );
17142
17626
  const teamId = elements2.assignmentTeamSelect.value;
17143
17627
  if (!teamId) {
17144
17628
  setAssignmentStatus(UI_TEXT.assignmentSelectTeamRoles);
@@ -17191,7 +17675,9 @@ ${logTarget.textContent}`;
17191
17675
  processedUsers.push(user.name);
17192
17676
  }
17193
17677
  if (processedUsers.length > 0) {
17194
- logMessage(formatRoleAssignmentLogUsers(action, roleName, processedUsers));
17678
+ logMessage(
17679
+ formatRoleAssignmentLogUsers(action, roleName, processedUsers)
17680
+ );
17195
17681
  }
17196
17682
  } else if (state.assignmentMode === "user") {
17197
17683
  const userId = elements2.assignmentUserSelect.value;
@@ -17218,7 +17704,9 @@ ${logTarget.textContent}`;
17218
17704
  processedRoles.push(roleName);
17219
17705
  }
17220
17706
  if (processedRoles.length > 0) {
17221
- logMessage(formatRoleAssignmentLogRolesForUser(action, user.name, processedRoles));
17707
+ logMessage(
17708
+ formatRoleAssignmentLogRolesForUser(action, user.name, processedRoles)
17709
+ );
17222
17710
  }
17223
17711
  } else if (state.assignmentMode === "role-team") {
17224
17712
  const roleRootId = elements2.assignmentRoleSelect.value;
@@ -17245,7 +17733,9 @@ ${logTarget.textContent}`;
17245
17733
  processedTeams.push(team.name);
17246
17734
  }
17247
17735
  if (processedTeams.length > 0) {
17248
- logMessage(formatRoleAssignmentLogTeams(action, roleName, processedTeams));
17736
+ logMessage(
17737
+ formatRoleAssignmentLogTeams(action, roleName, processedTeams)
17738
+ );
17249
17739
  }
17250
17740
  } else {
17251
17741
  const teamId = elements2.assignmentTeamSelect.value;
@@ -17272,7 +17762,9 @@ ${logTarget.textContent}`;
17272
17762
  processedRoles.push(roleName);
17273
17763
  }
17274
17764
  if (processedRoles.length > 0) {
17275
- logMessage(formatRoleAssignmentLogRolesForTeam(action, team.name, processedRoles));
17765
+ logMessage(
17766
+ formatRoleAssignmentLogRolesForTeam(action, team.name, processedRoles)
17767
+ );
17276
17768
  }
17277
17769
  }
17278
17770
  resetAssignmentCaches();
@@ -17296,17 +17788,25 @@ ${logTarget.textContent}`;
17296
17788
  const removesByRole = /* @__PURE__ */ new Map();
17297
17789
  const addsByRole = /* @__PURE__ */ new Map();
17298
17790
  for (const change of state.pendingChanges) {
17299
- const privilegeId = state.privilegeIdByKey.get(`${change.entityLogicalName}:${change.privilege}`);
17791
+ const privilegeId = state.privilegeIdByKey.get(
17792
+ `${change.entityLogicalName}:${change.privilege}`
17793
+ );
17300
17794
  if (!privilegeId) {
17301
- logMessage(formatMissingPrivilegeId(change.entityLogicalName, change.privilege));
17795
+ logMessage(
17796
+ formatMissingPrivilegeId(change.entityLogicalName, change.privilege)
17797
+ );
17302
17798
  continue;
17303
17799
  }
17304
17800
  const roleMap = state.rolePrivileges.get(change.roleId) ?? /* @__PURE__ */ new Map();
17305
17801
  const record = roleMap.get(change.entityLogicalName) ?? {
17802
+ create: "none",
17306
17803
  read: "none",
17307
17804
  write: "none",
17805
+ delete: "none",
17308
17806
  append: "none",
17309
- appendto: "none"
17807
+ appendto: "none",
17808
+ assign: "none",
17809
+ share: "none"
17310
17810
  };
17311
17811
  const currentLevel = record[change.privilege];
17312
17812
  if (currentLevel === change.level) {
@@ -17343,20 +17843,31 @@ ${logTarget.textContent}`;
17343
17843
  for (const privilegeId of privilegeIds) {
17344
17844
  await removePrivilegesFromRole(roleId, privilegeId);
17345
17845
  completedCalls += 1;
17346
- updateLoadingProgress(completedCalls, totalCalls, UI_TEXT.loadingApplyingChanges);
17846
+ updateLoadingProgress(
17847
+ completedCalls,
17848
+ totalCalls,
17849
+ UI_TEXT.loadingApplyingChanges
17850
+ );
17347
17851
  }
17348
17852
  }
17349
17853
  for (const [roleId, privileges] of addsByRole) {
17350
17854
  await addPrivilegesToRole(roleId, privileges);
17351
17855
  completedCalls += 1;
17352
- updateLoadingProgress(completedCalls, totalCalls, UI_TEXT.loadingApplyingChanges);
17856
+ updateLoadingProgress(
17857
+ completedCalls,
17858
+ totalCalls,
17859
+ UI_TEXT.loadingApplyingChanges
17860
+ );
17353
17861
  }
17354
17862
  for (const change of state.pendingChanges) {
17355
17863
  if (!state.rolePrivileges.has(change.roleId)) {
17356
17864
  state.rolePrivileges.set(change.roleId, /* @__PURE__ */ new Map());
17357
17865
  }
17358
17866
  const roleMap = state.rolePrivileges.get(change.roleId);
17359
- const record = ensureRolePrivilegeRecord(roleMap, change.entityLogicalName);
17867
+ const record = ensureRolePrivilegeRecord(
17868
+ roleMap,
17869
+ change.entityLogicalName
17870
+ );
17360
17871
  record[change.privilege] = change.level;
17361
17872
  }
17362
17873
  } catch (error) {
@@ -17631,10 +18142,18 @@ ${logTarget.textContent}`;
17631
18142
  loadAssignmentView();
17632
18143
  }
17633
18144
  });
17634
- elements2.assignmentAdd.addEventListener("click", () => applyAssignmentChange("add"));
17635
- elements2.assignmentRemove.addEventListener("click", () => applyAssignmentChange("remove"));
18145
+ elements2.assignmentAdd.addEventListener(
18146
+ "click",
18147
+ () => applyAssignmentChange("add")
18148
+ );
18149
+ elements2.assignmentRemove.addEventListener(
18150
+ "click",
18151
+ () => applyAssignmentChange("remove")
18152
+ );
17636
18153
  elements2.assignmentSelectAll.addEventListener("click", () => {
17637
- state.selectedAssignmentIds = new Set(state.assignmentItems.map((item) => item.id));
18154
+ state.selectedAssignmentIds = new Set(
18155
+ state.assignmentItems.map((item) => item.id)
18156
+ );
17638
18157
  renderAssignmentTable(state.assignmentItems);
17639
18158
  updateAssignmentSelectionUi();
17640
18159
  });
@@ -17668,6 +18187,19 @@ ${logTarget.textContent}`;
17668
18187
  renderAssignmentTable(state.assignmentItems);
17669
18188
  });
17670
18189
  }
18190
+ if (elements2.privilegeSearch) {
18191
+ elements2.privilegeSearch.addEventListener("input", async () => {
18192
+ state.privilegeSearch = elements2.privilegeSearch.value.trim().toLowerCase();
18193
+ if (!state.cacheLoaded) {
18194
+ const loaded = await ensureSecurityCacheLoaded();
18195
+ if (loaded) {
18196
+ await refreshPrivilegeView();
18197
+ }
18198
+ return;
18199
+ }
18200
+ renderPrivilegeTable();
18201
+ });
18202
+ }
17671
18203
  for (const button of elements2.sortButtons) {
17672
18204
  button.addEventListener("click", () => {
17673
18205
  const column = button.dataset.sort || "label";
@@ -17681,6 +18213,22 @@ ${logTarget.textContent}`;
17681
18213
  renderPrivilegeTable();
17682
18214
  });
17683
18215
  }
18216
+ for (const button of elements2.bulkApplyButtons) {
18217
+ button.addEventListener("click", () => {
18218
+ const privilege = button.dataset.bulkApply;
18219
+ if (!privilege) {
18220
+ return;
18221
+ }
18222
+ const select = elements2.bulkSelects.find(
18223
+ (item) => item.dataset.bulkSelect === privilege
18224
+ );
18225
+ if (!select || !select.value) {
18226
+ logMessage(UI_TEXT.bulkUpdateNoSelection);
18227
+ return;
18228
+ }
18229
+ applyBulkUpdate(privilege, select.value);
18230
+ });
18231
+ }
17684
18232
  for (const select of elements2.filterSelects) {
17685
18233
  select.addEventListener("change", async () => {
17686
18234
  const privilege = select.dataset.filter;