@L["ApplicantPortalSettings:PortalStatusHeading"]
diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/ApplicantPortalSettings/Index.js b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/ApplicantPortalSettings/Index.js
index 986ddea8d5..59d4b84e32 100644
--- a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/ApplicantPortalSettings/Index.js
+++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/ApplicantPortalSettings/Index.js
@@ -6,7 +6,7 @@
let portalStatusTable = new DataTable("#PortalStatusTable", {
paging: false,
- sort: false,
+ ordering: false,
info: false
});
@@ -14,6 +14,8 @@
e.preventDefault();
const statuses = [];
+ let hasValidationError = false;
+
$form.find('tbody tr').each(function () {
const $row = $(this);
const id = $row.find('input[type="hidden"]').val();
@@ -21,6 +23,7 @@
if (!externalStatus) {
abp.notify.warn(l('ApplicantPortalSettings:ValidationRequired'));
+ hasValidationError = true;
return false;
}
@@ -30,7 +33,7 @@
});
});
- if (statuses.length === 0) {
+ if (statuses.length === 0 || hasValidationError) {
return;
}
From 103af54ef66d25ddff479032c1f278fe819d739a Mon Sep 17 00:00:00 2001
From: Patrick <135162612+plavoie-BC@users.noreply.github.com>
Date: Mon, 13 Apr 2026 12:08:17 -0700
Subject: [PATCH 03/58] [AB#32424] Optimize external status update
---
.../GrantApplications/ApplicationStatusAppService.cs | 10 +++-------
1 file changed, 3 insertions(+), 7 deletions(-)
diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Application/GrantApplications/ApplicationStatusAppService.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.Application/GrantApplications/ApplicationStatusAppService.cs
index 0b1fd27c35..4427a21044 100644
--- a/applications/Unity.GrantManager/src/Unity.GrantManager.Application/GrantApplications/ApplicationStatusAppService.cs
+++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Application/GrantApplications/ApplicationStatusAppService.cs
@@ -31,19 +31,15 @@ public virtual async Task UpdateExternalStatusLabelsAsync(UpdateApplicationStatu
// Load all statuses in a single query by IDs
var statusIds = input.Statuses.Select(s => s.Id).ToList();
var statuses = await _applicationStatusRepository.GetListAsync(s => statusIds.Contains(s.Id));
+ var statusMap = statuses.ToDictionary(s => s.Id);
- // Build a lookup for efficient matching
- var statusMap = input.Statuses.ToDictionary(s => s.Id);
-
- // Update statuses in memory
- foreach (var status in statuses)
+ foreach (var statusDto in input.Statuses)
{
- if (statusMap.TryGetValue(status.Id, out var statusDto))
+ if (statusMap.TryGetValue(statusDto.Id, out var status))
{
status.ExternalStatus = statusDto.ExternalStatus;
await _applicationStatusRepository.UpdateAsync(status);
}
}
- // ABP's UnitOfWork batches all UpdateAsync calls into a single SaveChanges
}
}
\ No newline at end of file
From 013fecbe46b1d4f3b3d06359aa98984564adfd00 Mon Sep 17 00:00:00 2001
From: Patrick <135162612+plavoie-BC@users.noreply.github.com>
Date: Mon, 13 Apr 2026 12:14:01 -0700
Subject: [PATCH 04/58] [AB#32424] External Status - Add reset functionality
and validation messages
---
.../Localization/GrantManager/en.json | 5 +-
.../ApplicantPortalSettings/Index.cshtml | 9 +-
.../Pages/ApplicantPortalSettings/Index.js | 100 ++++++++++++++++--
3 files changed, 105 insertions(+), 9 deletions(-)
diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Domain.Shared/Localization/GrantManager/en.json b/applications/Unity.GrantManager/src/Unity.GrantManager.Domain.Shared/Localization/GrantManager/en.json
index 2a982fa99e..99744e4dcc 100644
--- a/applications/Unity.GrantManager/src/Unity.GrantManager.Domain.Shared/Localization/GrantManager/en.json
+++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Domain.Shared/Localization/GrantManager/en.json
@@ -486,8 +486,11 @@
"ApplicantPortalSettings:InternalStatus": "Internal Status",
"ApplicantPortalSettings:PortalStatusLabel": "Portal Status Label",
"ApplicantPortalSettings:SaveChanges": "Save Changes",
+ "ApplicantPortalSettings:ResetChanges": "Reset",
"ApplicantPortalSettings:SaveSuccess": "Portal status labels updated successfully.",
"ApplicantPortalSettings:SaveError": "An error occurred while saving portal status labels.",
- "ApplicantPortalSettings:ValidationRequired": "Portal status label cannot be empty."
+ "ApplicantPortalSettings:ValidationRequired": "Portal status label cannot be empty.",
+ "ApplicantPortalSettings:NoChanges": "No changes to save.",
+ "ApplicantPortalSettings:ChangesReset": "Changes have been reset to original values."
}
}
diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/ApplicantPortalSettings/Index.cshtml b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/ApplicantPortalSettings/Index.cshtml
index 31c98cb790..0c59aa2b56 100644
--- a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/ApplicantPortalSettings/Index.cshtml
+++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/ApplicantPortalSettings/Index.cshtml
@@ -67,10 +67,15 @@
-
diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/ApplicantPortalSettings/Index.js b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/ApplicantPortalSettings/Index.js
index 59d4b84e32..fede36f39c 100644
--- a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/ApplicantPortalSettings/Index.js
+++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/ApplicantPortalSettings/Index.js
@@ -3,6 +3,11 @@
const l = abp.localization.getResource('GrantManager');
const $form = $('#PortalStatusForm');
+ const $saveButton = $('#SaveButton');
+ const $resetButton = $('#ResetButton');
+
+ // Store original values on page load
+ const originalValues = new Map();
let portalStatusTable = new DataTable("#PortalStatusTable", {
paging: false,
@@ -10,13 +15,79 @@
info: false
});
+ // Capture original values after DataTable initialization
+ // Use DataTable's rows().every() to ensure all rows are captured, even if dynamically loaded
+ portalStatusTable.rows().every(function () {
+ const $row = $(this.node());
+ const id = $row.find('input[type="hidden"]').val();
+ const externalStatus = $row.find('input[type="text"]').val().trim();
+ originalValues.set(id, externalStatus);
+ });
+
+ // Check if any values have changed
+ function hasChanges() {
+ let changed = false;
+ portalStatusTable.$('tbody tr').each(function () {
+ const $row = $(this);
+ const id = $row.find('input[type="hidden"]').val();
+ const currentValue = $row.find('input[type="text"]').val().trim();
+ if (originalValues.get(id) !== currentValue) {
+ changed = true;
+ return false;
+ }
+ });
+ return changed;
+ }
+
+ // Update button states
+ function updateButtonStates() {
+ const changed = hasChanges();
+ $saveButton.prop('disabled', !changed);
+ $resetButton.prop('disabled', !changed);
+ }
+
+ // Debounce utility to limit how often updateButtonStates is called
+ function debounce(func, wait) {
+ let timeout;
+ return function () {
+ clearTimeout(timeout);
+ timeout = setTimeout(func, wait);
+ };
+ }
+
+ // Debounced version of updateButtonStates
+ const debouncedUpdateButtonStates = debounce(updateButtonStates, 150);
+
+ // Listen for input changes, using debounced handler
+ $form.on('input', '#PortalStatusTable input[type="text"]', function () {
+ debouncedUpdateButtonStates();
+ });
+
+ // Reset button handler
+ $resetButton.on('click', function (e) {
+ e.preventDefault();
+
+ portalStatusTable.$('tbody tr').each(function () {
+ const $row = $(this);
+ const id = $row.find('input[type="hidden"]').val();
+ const originalValue = originalValues.get(id);
+ $row.find('input[type="text"]').val(originalValue);
+ });
+
+ updateButtonStates();
+ abp.notify.info(l('ApplicantPortalSettings:ChangesReset'));
+ });
+
+ // Initialize button states
+ updateButtonStates();
+
$form.on('submit', function (e) {
e.preventDefault();
const statuses = [];
let hasValidationError = false;
- $form.find('tbody tr').each(function () {
+ portalStatusTable.$('tbody tr').each(function () {
const $row = $(this);
const id = $row.find('input[type="hidden"]').val();
const externalStatus = $row.find('input[type="text"]').val().trim();
@@ -27,13 +98,22 @@
return false;
}
- statuses.push({
- id: id,
- externalStatus: externalStatus
- });
+ // Only include if value has changed
+ if (originalValues.get(id) !== externalStatus) {
+ statuses.push({
+ id: id,
+ externalStatus: externalStatus
+ });
+ }
});
- if (statuses.length === 0 || hasValidationError) {
+ if (hasValidationError) {
+ return;
+ }
+
+ // Check if there are any changes
+ if (statuses.length === 0) {
+ abp.notify.info(l('ApplicantPortalSettings:NoChanges'));
return;
}
@@ -43,6 +123,14 @@
.updateExternalStatusLabels({ statuses: statuses })
.then(function () {
abp.notify.success(l('ApplicantPortalSettings:SaveSuccess'));
+
+ // Update original values after successful save
+ statuses.forEach(function(status) {
+ originalValues.set(status.id, status.externalStatus);
+ });
+
+ // Update button states after save
+ updateButtonStates();
})
.catch(function (error) {
abp.notify.error(error.message || l('ApplicantPortalSettings:SaveError'));
From 62e727ff619e876e9c1e0384d6d41b53a56e84b1 Mon Sep 17 00:00:00 2001
From: Patrick <135162612+plavoie-BC@users.noreply.github.com>
Date: Mon, 13 Apr 2026 12:16:59 -0700
Subject: [PATCH 05/58] [AB#32424] SonarQube Clean-up
---
.../Pages/ApplicantPortalSettings/Index.css | 1 -
1 file changed, 1 deletion(-)
diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/ApplicantPortalSettings/Index.css b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/ApplicantPortalSettings/Index.css
index 415f86c125..7bfa365a7a 100644
--- a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/ApplicantPortalSettings/Index.css
+++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/ApplicantPortalSettings/Index.css
@@ -27,7 +27,6 @@
}
.portal-settings-config {
- /*max-height: 80vh;*/
overflow: auto;
}
From 47a8aa650780a99ec67ca89951b533de289d6a89 Mon Sep 17 00:00:00 2001
From: Andre Goncalves