This commit is contained in:
zandercymatics 2024-09-30 14:35:22 -06:00
parent cfa1879909
commit 7cc5231cc0
No known key found for this signature in database
GPG key ID: FF4636ABEC9682B7
2 changed files with 128 additions and 115 deletions

View file

@ -437,23 +437,46 @@ function initializeWidgetOnList(list, parentId) {
} }
})(); })();
class CustomizableEmailBase { class CustomizableEmailBase {
constructor(dropdown, textarea, textareaPlaceholder, directEditButton, modalTrigger, modalConfirm, formLabel, lastSentEmailContent, apiUrl, textAreaFormGroup, dropdownFormGroup) {
this.dropdown = dropdown; /**
this.textarea = textarea; * @param {Object} config - must contain the following:
this.textareaPlaceholder = textareaPlaceholder; * @property {HTMLElement} dropdown - The dropdown element.
this.directEditButton = directEditButton; * @property {HTMLElement} textarea - The textarea element.
this.modalTrigger = modalTrigger; * @property {HTMLElement} textareaPlaceholder - The placeholder for the textarea.
this.modalConfirm = modalConfirm; * @property {HTMLElement} directEditButton - The button to directly edit the email.
this.formLabel = formLabel; * @property {HTMLElement} modalTrigger - The trigger for the modal.
this.lastSentEmailContent = lastSentEmailContent; * @property {HTMLElement} modalConfirm - The confirm button in the modal.
this.apiUrl = apiUrl; * @property {HTMLElement} formLabel - The label for the form.
* @property {HTMLElement} lastSentEmailContent - The last sent email content element.
* @property {HTMLElement} textAreaFormGroup - The form group for the textarea.
* @property {HTMLElement} dropdownFormGroup - The form group for the dropdown.
* @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} sessionVariableName - The session variable name. Used for show/hide on textAreaFormGroup/dropdownFormGroup.
* @property {string} apiErrorMessage - The error message that the ajax call returns.
*/
constructor(config) {
this.dropdown = config.dropdown;
this.textarea = config.textarea;
this.textareaPlaceholder = config.textareaPlaceholder;
this.directEditButton = config.directEditButton;
this.modalTrigger = config.modalTrigger;
this.modalConfirm = config.modalConfirm;
this.formLabel = config.formLabel;
this.lastSentEmailContent = config.lastSentEmailContent;
this.apiUrl = config.apiUrl;
this.apiErrorMessage = config.apiErrorMessage;
// These fields are hidden/shown on pageload depending on the current status // These fields are hidden/shown on pageload depending on the current status
this.textAreaFormGroup = textAreaFormGroup; this.textAreaFormGroup = config.textAreaFormGroup;
this.dropdownFormGroup = dropdownFormGroup; this.dropdownFormGroup = config.dropdownFormGroup;
this.statusSelect = document.getElementById("id_status"); this.statusToCheck = config.statusToCheck;
this.sessionVariableName = config.sessionVariableName;
// Non-configurable variables
this.statusSelect = document.getElementById("id_status");
this.domainRequestId = this.dropdown ? document.getElementById("domain_request_id").value : null this.domainRequestId = this.dropdown ? document.getElementById("domain_request_id").value : null
this.initialDropdownValue = this.dropdown ? this.dropdown.value : null; this.initialDropdownValue = this.dropdown ? this.dropdown.value : null;
this.initialEmailValue = this.textarea ? this.textarea.value : null; this.initialEmailValue = this.textarea ? this.textarea.value : null;
@ -466,19 +489,19 @@ class CustomizableEmailBase {
} }
// Handle showing/hiding the related fields on page load. // Handle showing/hiding the related fields on page load.
initializeFormGroups(statusToCheck, sessionVariableName) { initializeFormGroups() {
let isStatus = this.statusSelect.value == statusToCheck; let isStatus = this.statusSelect.value == this.statusToCheck;
// Initial handling of these groups. // Initial handling of these groups.
this.updateFormGroupVisibility(isStatus, isStatus); this.updateFormGroupVisibility(isStatus);
// 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.
// Then track if its shown or hidden in our session cache. // Then track if its shown or hidden in our session cache.
isStatus = this.statusSelect.value == statusToCheck; isStatus = this.statusSelect.value == this.statusToCheck;
this.updateFormGroupVisibility(isStatus, isStatus); this.updateFormGroupVisibility(isStatus);
addOrRemoveSessionBoolean(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
@ -488,20 +511,25 @@ class CustomizableEmailBase {
const observer = new PerformanceObserver((list) => { const observer = new PerformanceObserver((list) => {
list.getEntries().forEach((entry) => { list.getEntries().forEach((entry) => {
if (entry.type === "back_forward") { if (entry.type === "back_forward") {
let showTextAreaFormGroup = sessionStorage.getItem(sessionVariableName) !== null; let showTextAreaFormGroup = sessionStorage.getItem(this.sessionVariableName) !== null;
this.updateFormGroupVisibility(showTextAreaFormGroup, showTextAreaFormGroup); this.updateFormGroupVisibility(showTextAreaFormGroup);
} }
}); });
}); });
observer.observe({ type: "navigation" }); observer.observe({ type: "navigation" });
} }
updateFormGroupVisibility(showTextAreaFormGroup, showDropDownFormGroup) { updateFormGroupVisibility(showFormGroups) {
showTextAreaFormGroup ? showElement(this.textAreaFormGroup) : hideElement(this.textAreaFormGroup); if (showFormGroups) {
showDropDownFormGroup ? showElement(this.dropdownFormGroup) : hideElement(this.dropdownFormGroup); showElement(this.textAreaFormGroup);
showElement(this.dropdownFormGroup);
}else {
hideElement(this.textAreaFormGroup);
hideElement(this.dropdownFormGroup);
}
} }
initializeDropdown(errorMessage) { initializeDropdown() {
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) {
@ -525,7 +553,7 @@ class CustomizableEmailBase {
this.updateUserInterface(reason); this.updateUserInterface(reason);
}) })
.catch(error => { .catch(error => {
console.error(errorMessage, error) console.error(this.apiErrorMessage, error)
}); });
} }
}); });
@ -553,7 +581,7 @@ class CustomizableEmailBase {
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) { updateUserInterface(reason=this.dropdown.value) {
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();
@ -611,46 +639,33 @@ class CustomizableEmailBase {
class customActionNeededEmail extends CustomizableEmailBase { class customActionNeededEmail extends CustomizableEmailBase {
constructor() { constructor() {
const dropdown = document.getElementById("id_action_needed_reason"); const emailConfig = {
const textarea = document.getElementById("id_action_needed_reason_email") dropdown: document.getElementById("id_action_needed_reason"),
const textareaPlaceholder = document.querySelector(".field-action_needed_reason_email__placeholder"); textarea: document.getElementById("id_action_needed_reason_email"),
const directEditButton = document.querySelector('.field-action_needed_reason_email__edit'); textareaPlaceholder: document.querySelector(".field-action_needed_reason_email__placeholder"),
const modalTrigger = document.querySelector('.field-action_needed_reason_email__modal-trigger'); directEditButton: document.querySelector('.field-action_needed_reason_email__edit'),
const modalConfirm = document.getElementById('confirm-edit-email'); modalTrigger: document.querySelector('.field-action_needed_reason_email__modal-trigger'),
const formLabel = document.querySelector('label[for="id_action_needed_reason_email"]'); modalConfirm: document.getElementById('confirm-edit-email'),
const lastSentEmailContent = document.getElementById("last-sent-action-needed-email-content"); formLabel: document.querySelector('label[for="id_action_needed_reason_email"]'),
lastSentEmailContent: document.getElementById("last-sent-action-needed-email-content"),
let apiContainer = document.getElementById("get-action-needed-email-for-user-json") apiUrl: document.getElementById("get-action-needed-email-for-user-json")?.value || null,
const apiUrl = apiContainer ? apiContainer.value : null; textAreaFormGroup: document.querySelector('.field-action_needed_reason'),
dropdownFormGroup: document.querySelector('.field-action_needed_reason_email'),
// These fields are hidden on pageload statusToCheck: "rejected",
const textAreaFormGroup = document.querySelector('.field-action_needed_reason'); sessionVariableName: "showRejectionReason",
const dropdownFormGroup = document.querySelector('.field-action_needed_reason_email'); apiErrorMessage: "Error when attempting to grab rejected email: "
}
super( super(emailConfig);
dropdown,
textarea,
textareaPlaceholder,
directEditButton,
modalTrigger,
modalConfirm,
formLabel,
lastSentEmailContent,
apiUrl,
textAreaFormGroup,
dropdownFormGroup,
);
} }
loadActionNeededEmail() { loadActionNeededEmail() {
// Hide/show the email fields depending on the current status // Hide/show the email fields depending on the current status
this.initializeFormGroups("action needed", "showActionNeededReason"); this.initializeFormGroups();
// Setup the textarea, edit button, helper text // Setup the textarea, edit button, helper text
this.updateUserInterface(this.dropdown.value); this.updateUserInterface();
this.initializeDropdown("Error when attempting to grab action needed email: ") this.initializeDropdown();
this.initializeModalConfirm() this.initializeModalConfirm();
this.initializeDirectEditButton() this.initializeDirectEditButton();
} }
// Overrides the placeholder text when no reason is selected // Overrides the placeholder text when no reason is selected
@ -681,40 +696,28 @@ document.addEventListener('DOMContentLoaded', function() {
class customRejectedEmail extends CustomizableEmailBase { class customRejectedEmail extends CustomizableEmailBase {
constructor() { constructor() {
const dropdown = document.getElementById("id_rejection_reason"); const emailConfig = {
const textarea = document.getElementById("id_rejection_reason_email") dropdown: document.getElementById("id_rejection_reason"),
const textareaPlaceholder = document.querySelector(".field-rejection_reason_email__placeholder"); textarea: document.getElementById("id_rejection_reason_email"),
const directEditButton = document.querySelector('.field-rejection_reason_email__edit'); textareaPlaceholder: document.querySelector(".field-rejection_reason_email__placeholder"),
const modalTrigger = document.querySelector('.field-rejection_reason_email__modal-trigger'); directEditButton: document.querySelector('.field-rejection_reason_email__edit'),
const modalConfirm = document.getElementById('confirm-edit-email'); modalTrigger: document.querySelector('.field-rejection_reason_email__modal-trigger'),
const formLabel = document.querySelector('label[for="id_rejection_reason_email"]'); modalConfirm: document.getElementById('confirm-edit-email'),
const lastSentEmailContent = document.getElementById("last-sent-rejection-email-content"); formLabel: document.querySelector('label[for="id_rejection_reason_email"]'),
lastSentEmailContent: document.getElementById("last-sent-rejection-email-content"),
let apiContainer = document.getElementById("get-rejection-email-for-user-json"); apiUrl: document.getElementById("get-rejection-email-for-user-json")?.value || null,
const apiUrl = apiContainer ? apiContainer.value : null; textAreaFormGroup: document.querySelector('.field-rejection_reason'),
dropdownFormGroup: document.querySelector('.field-rejection_reason_email'),
// These fields are hidden on pageload statusToCheck: "rejected",
const textAreaFormGroup = document.querySelector('.field-rejection_reason'); sessionVariableName: "showRejectionReason",
const dropdownFormGroup = document.querySelector('.field-rejection_reason_email'); errorMessage: "Error when attempting to grab rejected email: "
};
super( super(emailConfig);
dropdown,
textarea,
textareaPlaceholder,
directEditButton,
modalTrigger,
modalConfirm,
formLabel,
lastSentEmailContent,
apiUrl,
textAreaFormGroup,
dropdownFormGroup,
);
} }
loadRejectedEmail() { loadRejectedEmail() {
this.initializeFormGroups("rejected", "showRejectionReason"); this.initializeFormGroups("rejected", "showRejectionReason");
this.updateUserInterface(this.dropdown.value); this.updateUserInterface();
this.initializeDropdown("Error when attempting to grab rejected email: ") this.initializeDropdown("Error when attempting to grab rejected email: ")
this.initializeModalConfirm() this.initializeModalConfirm()
this.initializeDirectEditButton() this.initializeDirectEditButton()

View file

@ -661,39 +661,49 @@ class DomainRequest(TimeStampedModel):
super().save(*args, **kwargs) super().save(*args, **kwargs)
# Handle the action needed email. # Handle custom status emails.
# An email is sent out when action_needed_reason is changed or added. # An email is sent out when a, for example, action_needed_reason is changed or added.
if self.status == self.DomainRequestStatus.ACTION_NEEDED: statuses_that_send_custom_emails = [self.DomainRequestStatus.ACTION_NEEDED, self.DomainRequestStatus.REJECTED]
self.send_another_status_reason_email( if self.status in statuses_that_send_custom_emails:
checked_status=self.DomainRequestStatus.ACTION_NEEDED, self.send_another_status_reason_email(self.status)
old_reason=self._cached_action_needed_reason,
new_reason=self.action_needed_reason,
excluded_reasons=[DomainRequest.ActionNeededReasons.OTHER],
email_to_send=self.action_needed_reason_email
)
elif self.status == self.DomainRequestStatus.REJECTED:
self.send_another_status_reason_email(
checked_status=self.DomainRequestStatus.REJECTED,
old_reason=self._cached_rejection_reason,
new_reason=self.rejection_reason,
excluded_reasons=[DomainRequest.RejectionReasons.OTHER],
email_to_send=self.rejection_reason_email,
)
# Update the cached values after saving # Update the cached values after saving
self._cache_status_and_status_reasons() self._cache_status_and_status_reasons()
def send_another_status_reason_email(self, checked_status, old_reason, new_reason, excluded_reasons, email_to_send): def send_another_status_reason_email(self, status):
"""Helper function to send out a second status email when the status remains the same, """Helper function to send out a second status email when the status remains the same,
but the reason has changed.""" but the reason has changed."""
# Currently, we store all this information in three variables.
# When adding new reasons, this can be a lot to manage so we store it here
# in a centralized location. However, this may need to change if this scales.
status_information = {
self.DomainRequestStatus.ACTION_NEEDED: {
"cached_reason": self._cached_action_needed_reason,
"reason": self.action_needed_reason,
"email": self.action_needed_reason_email,
"excluded_reasons": [DomainRequest.ActionNeededReasons.OTHER],
},
self.DomainRequestStatus.REJECTED: {
"cached_reason": self._cached_rejection_reason,
"reason": self.rejection_reason,
"email": self.rejection_reason_email,
"excluded_reasons": [DomainRequest.RejectionReasons.OTHER],
}
}
current_status = status_information.get(status)
old_reason = status_information.get("cached_reason")
new_reason = status_information.get("reason")
email_to_send = status_information.get("email")
# If the status itself changed, then we already sent out an email # If the status itself changed, then we already sent out an email
if self._cached_status != checked_status or old_reason is None: if self._cached_status != status or old_reason is None:
return return
# We should never send an email if no reason was specified # We should never send an email if no reason was specified
# Additionally, Don't send out emails for reasons that shouldn't send them # Additionally, Don't send out emails for reasons that shouldn't send them
if new_reason is None or new_reason in excluded_reasons: if new_reason is None or new_reason in current_status.get("excluded_reasons"):
return return
# Only send out an email if the underlying email itself changed # Only send out an email if the underlying email itself changed