Feature/ab#28192 Tenant Configuration Management Menu #2333
Conversation
New Configuration Menu that merges multiple configurations into a single location. Primarily uses invoking components from their original location where possible.
|
There was a problem hiding this comment.
Pull request overview
Adds a consolidated Configuration Management page to centralize multiple tenant configuration areas (Notifications, Payments, Flex configs, Tags, AI) and updates existing components/scripts so they can be invoked from this new location.
Changes:
- Introduces
/ConfigurationManagementRazor Page with side-menu navigation, shared layout styling, and section visibility controlled by feature/permission flags. - Moves script/style loading responsibilities from embedded setting component views into bundling/page-level includes to support reuse.
- Adapts Flex preview components to support new preview container IDs and adds local JS/CSS copies for configuration subpages.
Reviewed changes
Copilot reviewed 18 out of 18 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| applications/Unity.GrantManager/src/Unity.GrantManager.Web/Views/Settings/TagManagement/TagManagementViewComponent.cshtml | Removes inline script include from Tag Management widget view. |
| applications/Unity.GrantManager/src/Unity.GrantManager.Web/Views/Settings/TagManagement/TagManagement.js | Defers permission checks/modal initialization to document-ready. |
| applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/ConfigurationManagement/ScoresheetConfiguration.js | Adds page-local Scoresheet configuration script for the consolidated page. |
| applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/ConfigurationManagement/PaymentConfigurations.js | Adds page-local Payments configuration script (DataTables + modals + prefix update). |
| applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/ConfigurationManagement/PaymentConfigurations.css | Adds page-local Payments configuration styling. |
| applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/ConfigurationManagement/NotificationSettings.css | Adds page-local notification styling (currently unreferenced). |
| applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/ConfigurationManagement/Index.js | Implements side-menu navigation, section switching, DataTables adjustment, resizable splits. |
| applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/ConfigurationManagement/Index.css | Adds consolidated page layout + resizable split + preview styling. |
| applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/ConfigurationManagement/Index.cshtml.cs | Adds Configuration Management page model with feature/permission-driven visibility flags and payment config loading. |
| applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/ConfigurationManagement/Index.cshtml | Adds consolidated configuration page UI and bundles/scripts/styles for embedded components. |
| applications/Unity.GrantManager/src/Unity.GrantManager.Web/Menus/GrantManagerMenus.cs | Adds ConfigurationManagement menu constant. |
| applications/Unity.GrantManager/src/Unity.GrantManager.Domain.Shared/Localization/GrantManager/en.json | Adds localization for the new menu entry and normalizes formatting in the touched section. |
| applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/Themes/UX2/Components/Topbar/Default.cshtml | Replaces multiple config links with a single Configuration Management link and removes Settings link. |
| applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.Web/Views/Settings/NotificationsSettingGroup/NotificationsSettingViewComponent.cs | Ensures InternalEmailGroups JS is included via the widget bundle contributor. |
| applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.Web/Views/Settings/NotificationsSettingGroup/Default.cshtml | Removes inline script/style includes from the Notifications settings view. |
| applications/Unity.GrantManager/modules/Unity.Flex/src/Unity.Flex.Web/Views/Shared/Components/WorksheetListWidget/WorksheetList.js | Supports alternate preview container id (#worksheet-preview) and scopes readonly input selection. |
| applications/Unity.GrantManager/modules/Unity.Flex/src/Unity.Flex.Web/Views/Shared/Components/Scoresheet/Scoresheet.js | Supports alternate preview container id (#scoresheet-preview). |
| applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Web/Views/Settings/AISettingGroup/Default.cshtml | Removes inline script include from AI settings view. |
| .v-scroll { | ||
| max-height: 600px; | ||
| overflow-y: auto; | ||
| padding-right: 10px; | ||
| scroll-behavior: smooth; | ||
| } | ||
|
|
||
| .tui-editor-body { | ||
| min-height: 250px; | ||
| } | ||
|
|
||
| .note-text { | ||
| font-size: 12px; | ||
| } | ||
|
|
||
| /* Group Users Table - Push remove button to far right */ | ||
| #groupUsersTable td:last-child, | ||
| #createGroupUsersTable td:last-child { | ||
| text-align: right !important; | ||
| } | ||
|
|
||
| #groupUsersTable th:last-child, | ||
| #createGroupUsersTable th:last-child { | ||
| width: 30px !important; | ||
| min-width: 30px !important; | ||
| max-width: 30px !important; | ||
| } | ||
|
|
||
| /* Fix DataTable empty message positioning */ | ||
| #groupUsersTable td.dataTables_empty, | ||
| #createGroupUsersTable td.dataTables_empty, | ||
| #groupUsersTable td.dt-empty, | ||
| #createGroupUsersTable td.dt-empty { | ||
| text-align: center !important; | ||
| } | ||
|
|
||
| /* Ensure table maintains proper width */ | ||
| #createGroupUsersTable { | ||
| width: 100% !important; | ||
| } | ||
|
|
||
| /* Add User Button Styling */ | ||
| .btn-add-user { | ||
| background-color: white !important; | ||
| border: 2px solid var(--bs-primary) !important; | ||
| color: var(--bs-primary) !important; | ||
| font-weight: 700; | ||
| padding: 0.25rem 0.5rem; | ||
| white-space: nowrap; | ||
| transition: all 0.15s ease-in-out; | ||
| border-radius: 4px; | ||
| font-size: 0.875rem; | ||
| } | ||
|
|
||
| .btn-add-user:hover:not(:disabled) { | ||
| background-color: var(--bs-primary) !important; | ||
| color: white !important; | ||
| } | ||
|
|
||
| .btn-add-user:disabled { | ||
| opacity: 0.5; | ||
| cursor: not-allowed; | ||
| border-color: var(--bs-secondary) !important; | ||
| color: var(--bs-secondary) !important; | ||
| } | ||
|
|
||
| .btn-add-user i { | ||
| font-size: 0.8rem; | ||
| } | ||
|
|
||
| .modal-footer { | ||
| margin-top: 10px; | ||
| } |
There was a problem hiding this comment.
This stylesheet appears to be unused: there are no references to /Pages/ConfigurationManagement/NotificationSettings.css in the repository. If these styles are needed for the Configuration Management notifications section, add it to the page’s style bundle; otherwise remove the file to avoid dead/duplicated CSS that will be hard to maintain.
| .v-scroll { | |
| max-height: 600px; | |
| overflow-y: auto; | |
| padding-right: 10px; | |
| scroll-behavior: smooth; | |
| } | |
| .tui-editor-body { | |
| min-height: 250px; | |
| } | |
| .note-text { | |
| font-size: 12px; | |
| } | |
| /* Group Users Table - Push remove button to far right */ | |
| #groupUsersTable td:last-child, | |
| #createGroupUsersTable td:last-child { | |
| text-align: right !important; | |
| } | |
| #groupUsersTable th:last-child, | |
| #createGroupUsersTable th:last-child { | |
| width: 30px !important; | |
| min-width: 30px !important; | |
| max-width: 30px !important; | |
| } | |
| /* Fix DataTable empty message positioning */ | |
| #groupUsersTable td.dataTables_empty, | |
| #createGroupUsersTable td.dataTables_empty, | |
| #groupUsersTable td.dt-empty, | |
| #createGroupUsersTable td.dt-empty { | |
| text-align: center !important; | |
| } | |
| /* Ensure table maintains proper width */ | |
| #createGroupUsersTable { | |
| width: 100% !important; | |
| } | |
| /* Add User Button Styling */ | |
| .btn-add-user { | |
| background-color: white !important; | |
| border: 2px solid var(--bs-primary) !important; | |
| color: var(--bs-primary) !important; | |
| font-weight: 700; | |
| padding: 0.25rem 0.5rem; | |
| white-space: nowrap; | |
| transition: all 0.15s ease-in-out; | |
| border-radius: 4px; | |
| font-size: 0.875rem; | |
| } | |
| .btn-add-user:hover:not(:disabled) { | |
| background-color: var(--bs-primary) !important; | |
| color: white !important; | |
| } | |
| .btn-add-user:disabled { | |
| opacity: 0.5; | |
| cursor: not-allowed; | |
| border-color: var(--bs-secondary) !important; | |
| color: var(--bs-secondary) !important; | |
| } | |
| .btn-add-user i { | |
| font-size: 0.8rem; | |
| } | |
| .modal-footer { | |
| margin-top: 10px; | |
| } | |
| /* Unused stylesheet content removed. If Notification Settings still requires page-specific | |
| styles, add this file to the page's style bundle and reintroduce only the selectors that | |
| are actively used by the page. */ |
| @if (CurrentUser.IsInRole("system_admin") && await FeatureChecker.IsEnabledAsync("SettingManagement.Enable")) | ||
| { | ||
| <abp-dropdown-item href="/ScoresheetConfiguration">Scoresheets Configuration</abp-dropdown-item> | ||
| <abp-dropdown-item href="/WorksheetConfiguration">Custom Fields Configuration</abp-dropdown-item> | ||
| <abp-dropdown-item href="/ConfigurationManagement">Configuration Management</abp-dropdown-item> | ||
| } | ||
| @if (CurrentUser.IsInRole("ITOperations")) | ||
| { | ||
| <abp-dropdown-item href="/UnityAdmin">Unity Admin</abp-dropdown-item> | ||
| } | ||
| @if (CurrentUser.IsInRole("system_admin") && await FeatureChecker.IsEnabledAsync("SettingManagement.Enable")) | ||
| { | ||
| <abp-dropdown-item href="/SettingManagement">Settings</abp-dropdown-item> | ||
| } | ||
|
|
There was a problem hiding this comment.
The topbar no longer links to /SettingManagement. The new Configuration Management page does not include all existing Setting Management groups (e.g., the Background Jobs settings group), so removing this link makes those settings effectively unreachable through the UI. Either keep the Settings entry in the dropdown, or add the missing Setting Management groups into Configuration Management so functionality isn’t lost.
| { | ||
| <abp-script src="/Pages/ConfigurationManagement/PaymentConfigurations.js" /> | ||
| } | ||
| <abp-script src="/Pages/ConfigurationManagement/ScoresheetConfiguration.js" /> |
There was a problem hiding this comment.
ScoresheetConfiguration.js is included unconditionally, even when the Scoresheets section is not rendered. This script wires up PubSub subscriptions and DOM updates (e.g., scoresheet-info-widget) and can throw runtime errors if the element isn’t present. Make this script include conditional on Model.ShowScoresheets (similar to Payments/Notifications) to avoid executing scoresheet-only logic on tenants where Scoresheets is hidden/disabled.
| <abp-script src="/Pages/ConfigurationManagement/ScoresheetConfiguration.js" /> | |
| @if (Model.ShowScoresheets) | |
| { | |
| <abp-script src="/Pages/ConfigurationManagement/ScoresheetConfiguration.js" /> | |
| } |
| <li id="notifications-menu-item" class="nav-item btn btn-nav" data-target="notifications-div"> | ||
| Notifications | ||
| </li> | ||
| } | ||
| @if (Model.ShowPayments) | ||
| { | ||
| <li id="payments-menu-item" class="nav-item btn btn-nav" data-target="payments-div"> | ||
| Payments | ||
| </li> | ||
| } | ||
| @if (Model.ShowCustomFields) | ||
| { | ||
| <li id="custom-fields-menu-item" class="nav-item btn btn-nav" data-target="custom-fields-div"> | ||
| Custom Fields | ||
| </li> | ||
| } | ||
| @if (Model.ShowScoresheets) | ||
| { | ||
| <li id="scoresheets-menu-item" class="nav-item btn btn-nav" data-target="scoresheets-div"> | ||
| Scoresheets | ||
| </li> | ||
| } | ||
| @if (Model.ShowTags) | ||
| { | ||
| <li id="tags-menu-item" class="nav-item btn btn-nav" data-target="tags-div"> | ||
| Tags | ||
| </li> | ||
| } | ||
| @if (Model.ShowAI) | ||
| { | ||
| <li id="ai-menu-item" class="nav-item btn btn-nav" data-target="ai-div"> |
There was a problem hiding this comment.
The side menu items are rendered as clickable <li> elements with only a click handler. <li> isn’t keyboard-focusable by default, so this breaks keyboard navigation/screen reader interaction. Use <button>/<a> elements for menu actions, or add role="button", tabindex="0", and key handlers (Enter/Space) to meet accessibility requirements.
| <li id="notifications-menu-item" class="nav-item btn btn-nav" data-target="notifications-div"> | |
| Notifications | |
| </li> | |
| } | |
| @if (Model.ShowPayments) | |
| { | |
| <li id="payments-menu-item" class="nav-item btn btn-nav" data-target="payments-div"> | |
| Payments | |
| </li> | |
| } | |
| @if (Model.ShowCustomFields) | |
| { | |
| <li id="custom-fields-menu-item" class="nav-item btn btn-nav" data-target="custom-fields-div"> | |
| Custom Fields | |
| </li> | |
| } | |
| @if (Model.ShowScoresheets) | |
| { | |
| <li id="scoresheets-menu-item" class="nav-item btn btn-nav" data-target="scoresheets-div"> | |
| Scoresheets | |
| </li> | |
| } | |
| @if (Model.ShowTags) | |
| { | |
| <li id="tags-menu-item" class="nav-item btn btn-nav" data-target="tags-div"> | |
| Tags | |
| </li> | |
| } | |
| @if (Model.ShowAI) | |
| { | |
| <li id="ai-menu-item" class="nav-item btn btn-nav" data-target="ai-div"> | |
| <li id="notifications-menu-item" class="nav-item btn btn-nav" data-target="notifications-div" role="button" tabindex="0" onkeydown="if (event.key === 'Enter' || event.key === ' ') { event.preventDefault(); this.click(); }"> | |
| Notifications | |
| </li> | |
| } | |
| @if (Model.ShowPayments) | |
| { | |
| <li id="payments-menu-item" class="nav-item btn btn-nav" data-target="payments-div" role="button" tabindex="0" onkeydown="if (event.key === 'Enter' || event.key === ' ') { event.preventDefault(); this.click(); }"> | |
| Payments | |
| </li> | |
| } | |
| @if (Model.ShowCustomFields) | |
| { | |
| <li id="custom-fields-menu-item" class="nav-item btn btn-nav" data-target="custom-fields-div" role="button" tabindex="0" onkeydown="if (event.key === 'Enter' || event.key === ' ') { event.preventDefault(); this.click(); }"> | |
| Custom Fields | |
| </li> | |
| } | |
| @if (Model.ShowScoresheets) | |
| { | |
| <li id="scoresheets-menu-item" class="nav-item btn btn-nav" data-target="scoresheets-div" role="button" tabindex="0" onkeydown="if (event.key === 'Enter' || event.key === ' ') { event.preventDefault(); this.click(); }"> | |
| Scoresheets | |
| </li> | |
| } | |
| @if (Model.ShowTags) | |
| { | |
| <li id="tags-menu-item" class="nav-item btn btn-nav" data-target="tags-div" role="button" tabindex="0" onkeydown="if (event.key === 'Enter' || event.key === ' ') { event.preventDefault(); this.click(); }"> | |
| Tags | |
| </li> | |
| } | |
| @if (Model.ShowAI) | |
| { | |
| <li id="ai-menu-item" class="nav-item btn btn-nav" data-target="ai-div" role="button" tabindex="0" onkeydown="if (event.key === 'Enter' || event.key === ' ') { event.preventDefault(); this.click(); }"> |
| const splitRestoreMap = { | ||
| 'custom-fields-div': initResizableSplit('worksheet-split-container', 'worksheet-left', 'worksheet-divider', 'worksheet-right', 'worksheetSplitWidth'), | ||
| 'scoresheets-div': initResizableSplit('scoresheet-split-container', 'scoresheet-left', 'scoresheet-divider', 'scoresheet-right', 'scoresheetSplitWidth') | ||
| }; |
There was a problem hiding this comment.
splitRestoreMap is initialized after init() has already auto-selected and shown the first section. As a result, saved split widths won’t be restored for the initially displayed section until the user clicks away/back. Consider constructing splitRestoreMap before calling init(), or after auto-selecting the first section explicitly invoke the appropriate restore function when applicable.
| disableColumnSelect: true, | ||
| externalSearchId: 'search-data-table' |
There was a problem hiding this comment.
PaymentSettingsDataTable is configured to use the external search input #search-data-table, but that search box only exists in the Account Coding tab. This means Payment Settings can end up filtered by a hidden input, and the user can’t see or clear the active filter while on the Payment Settings tab. Consider disabling external search for this table (omit externalSearchId) or adding a Payment Settings search input and using a different ID.
| disableColumnSelect: true, | |
| externalSearchId: 'search-data-table' | |
| disableColumnSelect: true |
| </label> | ||
| </abp-column> | ||
| </abp-row> | ||
| <div class="btn-group" id="app_custom_buttons"> |
There was a problem hiding this comment.
This page renders multiple elements with the same id="app_custom_buttons" (one in the Account Coding tab and another in the Payment Settings tab). Duplicate IDs are invalid HTML and can cause selectors (and any scripts/plugins relying on #app_custom_buttons) to behave unpredictably. Use unique IDs per tab or remove the id if it’s not required.
| <div class="btn-group" id="app_custom_buttons"> | |
| <div class="btn-group" id="payment_settings_custom_buttons"> |



New Configuration Menu that merges multiple configurations into a single location. Primarily uses invoking components from their original location where possible.