fixed javascript and other issues on domain request admin

This commit is contained in:
David Kennedy 2025-03-06 22:00:37 -05:00
parent fe1e64828b
commit 343d8dc962
No known key found for this signature in database
GPG key ID: 6528A5386E66B96B
3 changed files with 149 additions and 92 deletions

View file

@ -2976,6 +2976,10 @@ class DomainRequestAdmin(ListHeaderAdmin, ImportExportRegistrarModelAdmin):
"federally_recognized_tribe", "federally_recognized_tribe",
"state_recognized_tribe", "state_recognized_tribe",
"about_your_organization", "about_your_organization",
"rejection_reason",
"rejection_reason_email",
"action_needed_reason",
"action_needed_reason_email",
] ]
autocomplete_fields = [ autocomplete_fields = [

View file

@ -106,7 +106,9 @@ export function initApprovedDomain() {
} }
const statusToCheck = "approved"; const statusToCheck = "approved";
const readonlyStatusToCheck = "Approved";
const statusSelect = document.getElementById("id_status"); const statusSelect = document.getElementById("id_status");
const statusField = document.querySelector("field-status");
const sessionVariableName = "showApprovedDomain"; const sessionVariableName = "showApprovedDomain";
let approvedDomainFormGroup = document.querySelector(".field-approved_domain"); let approvedDomainFormGroup = document.querySelector(".field-approved_domain");
@ -120,11 +122,22 @@ export function initApprovedDomain() {
// Handle showing/hiding the related fields on page load. // Handle showing/hiding the related fields on page load.
function initializeFormGroups() { function initializeFormGroups() {
let isStatus = statusSelect.value == statusToCheck; let isStatus = false;
if (statusSelect) {
isStatus = statusSelect.value == statusToCheck;
} else {
// statusSelect does not exist, indicating readonly
if (statusField) {
let readonlyDiv = statusField.querySelector("div.readonly");
let readonlyStatusText = readonlyDiv.textContent.trim();
isStatus = readonlyStatusText == readonlyStatusToCheck;
}
}
// Initial handling of these groups. // Initial handling of these groups.
updateFormGroupVisibility(isStatus); updateFormGroupVisibility(isStatus);
if (statusSelect) {
// Listen to change events and handle rejectionReasonFormGroup display, then save status to session storage // Listen to change events and handle rejectionReasonFormGroup display, then save status to session storage
statusSelect.addEventListener('change', () => { statusSelect.addEventListener('change', () => {
// Show the approved if the status is what we expect. // Show the approved if the status is what we expect.
@ -132,6 +145,7 @@ export function initApprovedDomain() {
updateFormGroupVisibility(isStatus); updateFormGroupVisibility(isStatus);
addOrRemoveSessionBoolean(sessionVariableName, isStatus); addOrRemoveSessionBoolean(sessionVariableName, isStatus);
}); });
}
// Listen to Back/Forward button navigation and handle approvedDomainFormGroup display based on session storage // Listen to Back/Forward button navigation and handle approvedDomainFormGroup display based on session storage
// When you navigate using forward/back after changing status but not saving, when you land back on the DA page the // When you navigate using forward/back after changing status but not saving, when you land back on the DA page the
@ -322,6 +336,7 @@ class CustomizableEmailBase {
* @property {HTMLElement} modalConfirm - The confirm button in the modal. * @property {HTMLElement} modalConfirm - The confirm button in the modal.
* @property {string} apiUrl - The API URL for fetching email content. * @property {string} apiUrl - The API URL for fetching email content.
* @property {string} statusToCheck - The status to check against. Used for show/hide on textAreaFormGroup/dropdownFormGroup. * @property {string} statusToCheck - The status to check against. Used for show/hide on textAreaFormGroup/dropdownFormGroup.
* @property {string} readonlyStatusToCheck - The status to check against when readonly. Used for show/hide on textAreaFormGroup/dropdownFormGroup.
* @property {string} sessionVariableName - The session variable name. Used for show/hide on textAreaFormGroup/dropdownFormGroup. * @property {string} sessionVariableName - The session variable name. Used for show/hide on textAreaFormGroup/dropdownFormGroup.
* @property {string} apiErrorMessage - The error message that the ajax call returns. * @property {string} apiErrorMessage - The error message that the ajax call returns.
*/ */
@ -338,6 +353,7 @@ class CustomizableEmailBase {
this.textAreaFormGroup = config.textAreaFormGroup; this.textAreaFormGroup = config.textAreaFormGroup;
this.dropdownFormGroup = config.dropdownFormGroup; this.dropdownFormGroup = config.dropdownFormGroup;
this.statusToCheck = config.statusToCheck; this.statusToCheck = config.statusToCheck;
this.readonlyStatusToCheck = config.readonlyStatusToCheck;
this.sessionVariableName = config.sessionVariableName; this.sessionVariableName = config.sessionVariableName;
// Non-configurable variables // Non-configurable variables
@ -363,11 +379,22 @@ class CustomizableEmailBase {
// Handle showing/hiding the related fields on page load. // Handle showing/hiding the related fields on page load.
initializeFormGroups() { initializeFormGroups() {
let isStatus = this.statusSelect.value == this.statusToCheck; let isStatus = false;
if (this.statusSelect) {
this.statusSelect.value == this.statusToCheck;
} else {
// statusSelect does not exist, indicating readonly
if (this.dropdownFormGroup) {
let readonlyDiv = this.dropdownFormGroup.querySelector("div.readonly");
let readonlyStatusText = readonlyDiv.textContent.trim();
isStatus = readonlyStatusText == this.readonlyStatusToCheck;
}
}
// Initial handling of these groups. // Initial handling of these groups.
this.updateFormGroupVisibility(isStatus); this.updateFormGroupVisibility(isStatus);
if (this.statusSelect) {
// Listen to change events and handle rejectionReasonFormGroup display, then save status to session storage // Listen to change events and handle rejectionReasonFormGroup display, then save status to session storage
this.statusSelect.addEventListener('change', () => { this.statusSelect.addEventListener('change', () => {
// Show the action needed field if the status is what we expect. // Show the action needed field if the status is what we expect.
@ -376,6 +403,7 @@ class CustomizableEmailBase {
this.updateFormGroupVisibility(isStatus); this.updateFormGroupVisibility(isStatus);
addOrRemoveSessionBoolean(this.sessionVariableName, isStatus); addOrRemoveSessionBoolean(this.sessionVariableName, isStatus);
}); });
}
// Listen to Back/Forward button navigation and handle rejectionReasonFormGroup display based on session storage // Listen to Back/Forward button navigation and handle rejectionReasonFormGroup display based on session storage
// When you navigate using forward/back after changing status but not saving, when you land back on the DA page the // When you navigate using forward/back after changing status but not saving, when you land back on the DA page the
@ -403,6 +431,7 @@ class CustomizableEmailBase {
} }
initializeDropdown() { initializeDropdown() {
if (this.dropdown) {
this.dropdown.addEventListener("change", () => { this.dropdown.addEventListener("change", () => {
let reason = this.dropdown.value; let reason = this.dropdown.value;
if (this.initialDropdownValue !== this.dropdown.value || this.initialEmailValue !== this.textarea.value) { if (this.initialDropdownValue !== this.dropdown.value || this.initialEmailValue !== this.textarea.value) {
@ -431,8 +460,10 @@ class CustomizableEmailBase {
} }
}); });
} }
}
initializeModalConfirm() { initializeModalConfirm() {
if (this.modalConfirm) {
this.modalConfirm.addEventListener("click", () => { this.modalConfirm.addEventListener("click", () => {
this.textarea.removeAttribute('readonly'); this.textarea.removeAttribute('readonly');
this.textarea.focus(); this.textarea.focus();
@ -440,8 +471,10 @@ class CustomizableEmailBase {
hideElement(this.modalTrigger); hideElement(this.modalTrigger);
}); });
} }
}
initializeDirectEditButton() { initializeDirectEditButton() {
if (this.directEditButton) {
this.directEditButton.addEventListener("click", () => { this.directEditButton.addEventListener("click", () => {
this.textarea.removeAttribute('readonly'); this.textarea.removeAttribute('readonly');
this.textarea.focus(); this.textarea.focus();
@ -449,12 +482,13 @@ class CustomizableEmailBase {
hideElement(this.modalTrigger); hideElement(this.modalTrigger);
}); });
} }
}
isEmailAlreadySent() { isEmailAlreadySent() {
return this.lastSentEmailContent.value.replace(/\s+/g, '') === this.textarea.value.replace(/\s+/g, ''); return this.lastSentEmailContent.value.replace(/\s+/g, '') === this.textarea.value.replace(/\s+/g, '');
} }
updateUserInterface(reason=this.dropdown.value, excluded_reasons=["other"]) { updateUserInterface(reason, excluded_reasons=["other"]) {
if (!reason) { if (!reason) {
// No reason selected, we will set the label to "Email", show the "Make a selection" placeholder, hide the trigger, textarea, hide the help text // No reason selected, we will set the label to "Email", show the "Make a selection" placeholder, hide the trigger, textarea, hide the help text
this.showPlaceholderNoReason(); this.showPlaceholderNoReason();
@ -468,6 +502,7 @@ class CustomizableEmailBase {
// Helper function that makes overriding the readonly textarea easy // Helper function that makes overriding the readonly textarea easy
showReadonlyTextarea() { showReadonlyTextarea() {
if (this.textarea && this.textareaPlaceholder) {
// A triggering selection is selected, all hands on board: // A triggering selection is selected, all hands on board:
this.textarea.setAttribute('readonly', true); this.textarea.setAttribute('readonly', true);
showElement(this.textarea); showElement(this.textarea);
@ -487,6 +522,7 @@ class CustomizableEmailBase {
this.formLabel.innerHTML = "Email:"; this.formLabel.innerHTML = "Email:";
} }
} }
}
// Helper function that makes overriding the placeholder reason easy // Helper function that makes overriding the placeholder reason easy
showPlaceholderNoReason() { showPlaceholderNoReason() {
@ -516,9 +552,10 @@ class customActionNeededEmail extends CustomizableEmailBase {
lastSentEmailContent: document.getElementById("last-sent-action-needed-email-content"), lastSentEmailContent: document.getElementById("last-sent-action-needed-email-content"),
modalConfirm: document.getElementById("action-needed-reason__confirm-edit-email"), modalConfirm: document.getElementById("action-needed-reason__confirm-edit-email"),
apiUrl: document.getElementById("get-action-needed-email-for-user-json")?.value || null, apiUrl: document.getElementById("get-action-needed-email-for-user-json")?.value || null,
textAreaFormGroup: document.querySelector('.field-action_needed_reason'), textAreaFormGroup: document.querySelector('.field-action_needed_reason_email'),
dropdownFormGroup: document.querySelector('.field-action_needed_reason_email'), dropdownFormGroup: document.querySelector('.field-action_needed_reason'),
statusToCheck: "action needed", statusToCheck: "action needed",
readonlyStatusToCheck: "Action needed",
sessionVariableName: "showActionNeededReason", sessionVariableName: "showActionNeededReason",
apiErrorMessage: "Error when attempting to grab action needed email: " apiErrorMessage: "Error when attempting to grab action needed email: "
} }
@ -529,7 +566,15 @@ class customActionNeededEmail extends CustomizableEmailBase {
// Hide/show the email fields depending on the current status // Hide/show the email fields depending on the current status
this.initializeFormGroups(); this.initializeFormGroups();
// Setup the textarea, edit button, helper text // Setup the textarea, edit button, helper text
this.updateUserInterface(); let reason = null;
if (this.dropdown) {
reason = this.dropdown.value;
} else if (this.dropdownFormGroup && this.dropdownFormGroup.querySelector("div.readonly")) {
if (this.dropdownFormGroup.querySelector("div.readonly").textContent) {
reason = this.dropdownFormGroup.querySelector("div.readonly").textContent.trim()
}
}
this.updateUserInterface(reason);
this.initializeDropdown(); this.initializeDropdown();
this.initializeModalConfirm(); this.initializeModalConfirm();
this.initializeDirectEditButton(); this.initializeDirectEditButton();
@ -560,12 +605,12 @@ export function initActionNeededEmail() {
// Initialize UI // Initialize UI
const customEmail = new customActionNeededEmail(); const customEmail = new customActionNeededEmail();
// Check that every variable was setup correctly // // Check that every variable was setup correctly
const nullItems = Object.entries(customEmail.config).filter(([key, value]) => value === null).map(([key]) => key); // const nullItems = Object.entries(customEmail.config).filter(([key, value]) => value === null).map(([key]) => key);
if (nullItems.length > 0) { // if (nullItems.length > 0) {
console.error(`Failed to load customActionNeededEmail(). Some variables were null: ${nullItems.join(", ")}`) // console.error(`Failed to load customActionNeededEmail(). Some variables were null: ${nullItems.join(", ")}`)
return; // return;
} // }
customEmail.loadActionNeededEmail() customEmail.loadActionNeededEmail()
}); });
} }
@ -581,6 +626,7 @@ class customRejectedEmail extends CustomizableEmailBase {
textAreaFormGroup: document.querySelector('.field-rejection_reason'), textAreaFormGroup: document.querySelector('.field-rejection_reason'),
dropdownFormGroup: document.querySelector('.field-rejection_reason_email'), dropdownFormGroup: document.querySelector('.field-rejection_reason_email'),
statusToCheck: "rejected", statusToCheck: "rejected",
readonlyStatusToCheck: "Rejected",
sessionVariableName: "showRejectionReason", sessionVariableName: "showRejectionReason",
errorMessage: "Error when attempting to grab rejected email: " errorMessage: "Error when attempting to grab rejected email: "
}; };
@ -589,7 +635,15 @@ class customRejectedEmail extends CustomizableEmailBase {
loadRejectedEmail() { loadRejectedEmail() {
this.initializeFormGroups(); this.initializeFormGroups();
this.updateUserInterface(); let reason = null;
if (this.dropdown) {
reason = this.dropdown.value;
} else if (this.dropdownFormGroup && this.dropdownFormGroup.querySelector("div.readonly")) {
if (this.dropdownFormGroup.querySelector("div.readonly").textContent) {
reason = this.dropdownFormGroup.querySelector("div.readonly").textContent.trim()
}
}
this.updateUserInterface(reason);
this.initializeDropdown(); this.initializeDropdown();
this.initializeModalConfirm(); this.initializeModalConfirm();
this.initializeDirectEditButton(); this.initializeDirectEditButton();
@ -600,7 +654,7 @@ class customRejectedEmail extends CustomizableEmailBase {
this.showPlaceholder("Email:", "Select a rejection reason to see email"); this.showPlaceholder("Email:", "Select a rejection reason to see email");
} }
updateUserInterface(reason=this.dropdown.value, excluded_reasons=[]) { updateUserInterface(reason, excluded_reasons=[]) {
super.updateUserInterface(reason, excluded_reasons); super.updateUserInterface(reason, excluded_reasons);
} }
} }
@ -619,12 +673,12 @@ export function initRejectedEmail() {
// Initialize UI // Initialize UI
const customEmail = new customRejectedEmail(); const customEmail = new customRejectedEmail();
// Check that every variable was setup correctly // // Check that every variable was setup correctly
const nullItems = Object.entries(customEmail.config).filter(([key, value]) => value === null).map(([key]) => key); // const nullItems = Object.entries(customEmail.config).filter(([key, value]) => value === null).map(([key]) => key);
if (nullItems.length > 0) { // if (nullItems.length > 0) {
console.error(`Failed to load customRejectedEmail(). Some variables were null: ${nullItems.join(", ")}`) // console.error(`Failed to load customRejectedEmail(). Some variables were null: ${nullItems.join(", ")}`)
return; // return;
} // }
customEmail.loadRejectedEmail() customEmail.loadRejectedEmail()
}); });
} }
@ -648,7 +702,6 @@ function handleSuborgFieldsAndButtons() {
// Ensure that every variable is present before proceeding // Ensure that every variable is present before proceeding
if (!requestedSuborganizationField || !suborganizationCity || !suborganizationStateTerritory || !rejectButton) { if (!requestedSuborganizationField || !suborganizationCity || !suborganizationStateTerritory || !rejectButton) {
console.warn("handleSuborganizationSelection() => Could not find required fields.")
return; return;
} }

View file

@ -404,7 +404,7 @@ export function handlePortfolioSelection(
updateSubOrganizationDropdown(portfolio_id); updateSubOrganizationDropdown(portfolio_id);
// Show fields relevant to a selected portfolio // Show fields relevant to a selected portfolio
showElement(suborganizationField); if (suborganizationField) showElement(suborganizationField);
hideElement(seniorOfficialField); hideElement(seniorOfficialField);
showElement(portfolioSeniorOfficialField); showElement(portfolioSeniorOfficialField);
@ -427,7 +427,7 @@ export function handlePortfolioSelection(
// No portfolio is selected - reverse visibility of fields // No portfolio is selected - reverse visibility of fields
// Hide suborganization field as no portfolio is selected // Hide suborganization field as no portfolio is selected
hideElement(suborganizationField); if (suborganizationField) hideElement(suborganizationField);
// Show fields that are relevant when no portfolio is selected // Show fields that are relevant when no portfolio is selected
showElement(seniorOfficialField); showElement(seniorOfficialField);