From 24d55f41bf69a93f39940069fd2d611af58483e5 Mon Sep 17 00:00:00 2001 From: Rebecca Hsieh Date: Tue, 29 Oct 2024 14:13:42 -0700 Subject: [PATCH] Add success message for invited member and easter egg --- src/registrar/assets/js/get-gov.js | 190 ++++++++++++++--------------- src/registrar/views/portfolios.py | 10 +- 2 files changed, 100 insertions(+), 100 deletions(-) diff --git a/src/registrar/assets/js/get-gov.js b/src/registrar/assets/js/get-gov.js index 61daeaa5c..7e3fad391 100644 --- a/src/registrar/assets/js/get-gov.js +++ b/src/registrar/assets/js/get-gov.js @@ -88,7 +88,7 @@ function makeVisible(el) { // TODO: Write caption here function addModal(member_email, member_id, num_domains, submit_delete_url, wrapper_element) { - console.log("We are in addModal") + let modalHeading = ''; let modalDescription = ''; @@ -122,8 +122,6 @@ function addModal(member_email, member_id, num_domains, submit_delete_url, wrapp modal.setAttribute('aria-describedby', 'Member will be removed'); modal.setAttribute('data-force-action', ''); - console.log("modal is", modal) - modal.innerHTML = `
@@ -169,8 +167,6 @@ function addModal(member_email, member_id, num_domains, submit_delete_url, wrapp } else { document.body.appendChild(modal); } - - // wrapper_element.appendChild(modal); } // TODO: Write caption here @@ -224,83 +220,6 @@ function generateKebabHTML(unique_id, member_name, member_type) { return kebab } -function deleteMember(member_delete_url, pageToDisplay) { - // Debugging - console.log(member_delete_url); - - // Get csrf token - const csrfToken = getCsrfToken(); - // Create FormData object and append the CSRF token - const formData = `csrfmiddlewaretoken=${encodeURIComponent(csrfToken)}`; - - fetch(`${member_delete_url}`, { - method: 'POST', - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - 'X-Requested-With': 'XMLHttpRequest', - 'X-CSRFToken': csrfToken, - }, - body: formData - }) - .then(response => { - if (response.status === 200) { - response.json().then(data => { - if (data.success) { - addAlert("success", data.success); - } - memberTableInstance.loadTable(pageToDisplay, this.currentSortBy, this.currentOrder, this.scrollToTable, this.currentSearchTerm); - }); - } else { - // If the response isn't 204, handle the error response - response.json().then(data => { - console.log("Member response not 200"); - if (data.error) { - // This should display the error given from backend for - // either only admin OR in progress requests - addAlert("error", data.error); - } else { - throw new Error(`Unexpected status: ${response.status}`); - } - }); - } - }) - .catch(error => { - console.error('Error deleting member:', error); - }); -} - - -/** - * Adds an alert message to the page with an alert class. - * - * @param {string} alertClass - {error, warning, info, success} - * @param {string} alertMessage - The text that will be displayed - * - */ -function addAlert(alertClass, alertMessage) { - let toggleableAlertDiv = document.getElementById("toggleable-alert"); - this.resetAlerts(); - toggleableAlertDiv.classList.add(`usa-alert--${alertClass}`); - let alertParagraph = toggleableAlertDiv.querySelector(".usa-alert__text"); - alertParagraph.innerHTML = alertMessage - showElement(toggleableAlertDiv); -} - -/** - * Resets the reusable alert message - * - */ -function resetAlerts() { - // Create a list of any alert that's leftover and remove - document.querySelectorAll(".usa-alert:not(#toggleable-alert)").forEach(alert => { - alert.remove(); - }); - let toggleableAlertDiv = document.getElementById("toggleable-alert"); - toggleableAlertDiv.classList.remove('usa-alert--error'); - toggleableAlertDiv.classList.remove('usa-alert--success'); - hideElement(toggleableAlertDiv); -} - /** * Toggles expand_more / expand_more svgs in buttons or anchors * @param {Element} element - DOM element @@ -1203,7 +1122,10 @@ function initializeTooltips() { * */ function initializeModals() { + console.log("We are going to initializeModals") window.modal.on(); + console.log("Finish initializeModals") + } /** @@ -1214,7 +1136,9 @@ function initializeModals() { * */ function unloadModals() { + console.log("We are going to unloadModals") window.modal.off(); + console.log("Finish unloadModals") } class LoadTableBase { @@ -2216,6 +2140,78 @@ class MembersTable extends LoadTableBase { return domainsHTML; } + deleteMember(member_delete_url, pageToDisplay) { + // Get csrf token + const csrfToken = getCsrfToken(); + // Create FormData object and append the CSRF token + const formData = `csrfmiddlewaretoken=${encodeURIComponent(csrfToken)}`; + + fetch(`${member_delete_url}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'X-Requested-With': 'XMLHttpRequest', + 'X-CSRFToken': csrfToken, + }, + body: formData + }) + .then(response => { + if (response.status === 200) { + response.json().then(data => { + if (data.success) { + this.addAlert("success", data.success); + } + this.loadTable(pageToDisplay, this.currentSortBy, this.currentOrder, this.scrollToTable, this.currentSearchTerm); + }); + } else { + // If the response isn't 204, handle the error response + response.json().then(data => { + if (data.error) { + // This should display the error given from backend for + // either only admin OR in progress requests + this.addAlert("error", data.error); + } else { + throw new Error(`Unexpected status: ${response.status}`); + } + }); + } + }) + .catch(error => { + console.error('Error deleting member:', error); + }); + } + + + /** + * Adds an alert message to the page with an alert class. + * + * @param {string} alertClass - {error, warning, info, success} + * @param {string} alertMessage - The text that will be displayed + * + */ + addAlert(alertClass, alertMessage) { + let toggleableAlertDiv = document.getElementById("toggleable-alert"); + this.resetAlerts(); + toggleableAlertDiv.classList.add(`usa-alert--${alertClass}`); + let alertParagraph = toggleableAlertDiv.querySelector(".usa-alert__text"); + alertParagraph.innerHTML = alertMessage + showElement(toggleableAlertDiv); + } + + /** + * Resets the reusable alert message + */ + resetAlerts() { + // Create a list of any alert that's leftover and remove + document.querySelectorAll(".usa-alert:not(#toggleable-alert)").forEach(alert => { + alert.remove(); + }); + let toggleableAlertDiv = document.getElementById("toggleable-alert"); + toggleableAlertDiv.classList.remove('usa-alert--error'); + toggleableAlertDiv.classList.remove('usa-alert--success'); + hideElement(toggleableAlertDiv); + } + /** * Generates an HTML string summarizing a user's additional permissions within a portfolio, * based on the user's permissions and predefined permission choices. @@ -2290,8 +2286,6 @@ class MembersTable extends LoadTableBase { * @param {*} portfolio - the portfolio id */ loadTable(page, sortBy = this.currentSortBy, order = this.currentOrder, scroll = this.scrollToTable, searchTerm =this.currentSearchTerm, portfolio = this.portfolioValue) { - - console.log("in loadTable"); // --------- SEARCH let searchParams = new URLSearchParams( { @@ -2318,9 +2312,6 @@ class MembersTable extends LoadTableBase { // Get whether the logged in user has edit members permission const hasEditPermission = this.portfolioElement ? this.portfolioElement.getAttribute('data-has-edit-permission')==='True' : null; - - console.log(this.portfolioElement.getAttribute('data-has-edit-permission')) - console.log(hasEditPermission) let url = `${baseUrlValue}?${searchParams.toString()}` //TODO: uncomment for search function fetch(url) @@ -2334,6 +2325,10 @@ class MembersTable extends LoadTableBase { // handle the display of proper messaging in the event that no members exist in the list or search returns no results this.updateDisplay(data, this.tableWrapper, this.noTableWrapper, this.noSearchResultsWrapper, this.currentSearchTerm); + // remove any existing modal elements from the DOM so they can be properly re-initialized + // after the DOM content changes and there are new delete modal buttons added + unloadModals(); + // identify the DOM element where the domain list will be inserted into the DOM const memberList = document.querySelector('#members tbody'); memberList.innerHTML = ''; @@ -2364,9 +2359,7 @@ class MembersTable extends LoadTableBase { const num_domains = member.domain_urls.length; const last_active = this.handleLastActive(member.last_active); const kebabHTML = hasEditPermission ? generateKebabHTML(unique_id, member.name, member.type): ''; - - if (hasEditPermission) addModal(member.email, unique_id, num_domains, member_delete_url, this.tableWrapper); - + const row = document.createElement('tr'); let admin_tagHTML = ``; @@ -2422,8 +2415,11 @@ class MembersTable extends LoadTableBase { if (domainsHTML || permissionsHTML) { memberList.appendChild(showMoreRow); } + // This easter egg is only for fixtures that dont have names as we are displaying their emails + // All prod users will have emails linked to their account + if (hasEditPermission) addModal(member.email || "Samwise Gamgee", unique_id, num_domains, member_delete_url, row); }); - + this.initShowMoreButtons(); // initialize modals immediately after the DOM content is updated @@ -2445,7 +2441,7 @@ class MembersTable extends LoadTableBase { pageToDisplay--; } - deleteMember(pk, pageToDisplay); + this.deleteMember(pk, pageToDisplay); }); }); @@ -2992,9 +2988,7 @@ document.addEventListener('DOMContentLoaded', function() { document.addEventListener("DOMContentLoaded", () => { (function portfolioMemberToggle() { - console.log("IN PORTFOLIOMEMBERTOGGLE") const wrapperDeleteAction = document.getElementById("wrapper-delete-action") - console.log("!!!", wrapperDeleteAction) if (wrapperDeleteAction) { const member_type = wrapperDeleteAction.getAttribute("data-member-type"); const member_id = wrapperDeleteAction.getAttribute("data-member-id"); @@ -3005,19 +2999,15 @@ document.addEventListener("DOMContentLoaded", () => { const unique_id = `${member_type}-${member_id}`; wrapperDeleteAction.innerHTML = generateKebabHTML(unique_id, member_name, member_type); - console.log("WE GENERATED THE KEBAB HERE") // Select the button and the menu we just inserted const kebabButton = wrapperDeleteAction.querySelector(`#button-toggle-more-actions-${unique_id}`); const kebabMenu = wrapperDeleteAction.querySelector(`#more-actions-${unique_id}`); - console.log("BEFORE LISTENER") kebabButton.addEventListener('click', () => { const isExpanded = kebabButton.getAttribute('aria-expanded') === 'true'; kebabButton.setAttribute('aria-expanded', !isExpanded); - console.log("IN LISTENER") kebabMenu.style.display = isExpanded ? 'none' : 'block'; - console.log("Menu is now", isExpanded ? "hidden" : "visible"); }); // Handles clicks outside the kebab menu @@ -3033,7 +3023,9 @@ document.addEventListener("DOMContentLoaded", () => { }); console.log("AFTER LISTENER") - addModal(member_email, unique_id, num_domains, member_delete_url, wrapperDeleteAction); + // This easter egg is only for fixtures that dont have names as we are displaying their emails + // All prod users will have emails linked to their account + addModal(member_email || "Samwise Gamgee", unique_id, num_domains, member_delete_url, wrapperDeleteAction); initializeModals(); diff --git a/src/registrar/views/portfolios.py b/src/registrar/views/portfolios.py index 762b17a9c..5093ff565 100644 --- a/src/registrar/views/portfolios.py +++ b/src/registrar/views/portfolios.py @@ -130,6 +130,7 @@ class PortfolioMemberDeleteView(PortfolioMemberPermission, View): "permissions, make sure they log into the registrar, and then remove this member." ) + # From the Members Table page Else the Member Page if error_message: if request.headers.get("X-Requested-With") == "XMLHttpRequest": return JsonResponse( @@ -143,6 +144,7 @@ class PortfolioMemberDeleteView(PortfolioMemberPermission, View): # passed all error conditions portfolio_member_permission.delete() + # From the Members Table page Else the Member Page success_message = f"You've removed {member.email} from the organization." if request.headers.get("X-Requested-With") == "XMLHttpRequest": return JsonResponse({"success": success_message}, status=200) @@ -258,7 +260,13 @@ class PortfolioInvitedMemberDeleteView(PortfolioMemberPermission, View): portfolio_invitation.delete() - return HttpResponse(status=204) + success_message = f"You've removed {portfolio_invitation.email} from the organization." + # From the Members Table page Else the Member Page + if request.headers.get("X-Requested-With") == "XMLHttpRequest": + return JsonResponse({"success": success_message}, status=200) + else: + messages.success(request, success_message) + return redirect(reverse("members")) class PortfolioInvitedMemberEditView(PortfolioMemberEditPermissionView, View):