mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-08-03 08:22:18 +02:00
cleanup pt 2
This commit is contained in:
parent
437981ff30
commit
08082fb0aa
5 changed files with 135 additions and 153 deletions
|
@ -7,41 +7,41 @@ import { hookupRadioTogglerListener } from './radios.js';
|
|||
// This is specifically for the Member Profile (Manage Member) Page member/invitation removal
|
||||
export function initPortfolioNewMemberPageToggle() {
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
const wrapperDeleteAction = document.getElementById("wrapper-delete-action")
|
||||
if (wrapperDeleteAction) {
|
||||
const member_type = wrapperDeleteAction.getAttribute("data-member-type");
|
||||
const member_id = wrapperDeleteAction.getAttribute("data-member-id");
|
||||
const num_domains = wrapperDeleteAction.getAttribute("data-num-domains");
|
||||
const member_name = wrapperDeleteAction.getAttribute("data-member-name");
|
||||
const member_email = wrapperDeleteAction.getAttribute("data-member-email");
|
||||
const member_delete_url = `${member_type}-${member_id}/delete`;
|
||||
const unique_id = `${member_type}-${member_id}`;
|
||||
|
||||
let cancelInvitationButton = member_type === "invitedmember" ? "Cancel invitation" : "Remove member";
|
||||
wrapperDeleteAction.innerHTML = generateKebabHTML('remove-member', unique_id, cancelInvitationButton, `for ${member_name}`);
|
||||
|
||||
// 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
|
||||
MembersTable.addMemberModal(num_domains, member_email || "Samwise Gamgee", member_delete_url, unique_id, wrapperDeleteAction);
|
||||
|
||||
uswdsInitializeModals();
|
||||
|
||||
// Now the DOM and modals are ready, add listeners to the submit buttons
|
||||
const modals = document.querySelectorAll('.usa-modal__content');
|
||||
|
||||
modals.forEach(modal => {
|
||||
const submitButton = modal.querySelector('.usa-modal__submit');
|
||||
const closeButton = modal.querySelector('.usa-modal__close');
|
||||
submitButton.addEventListener('click', () => {
|
||||
closeButton.click();
|
||||
let delete_member_form = document.getElementById("member-delete-form");
|
||||
if (delete_member_form) {
|
||||
delete_member_form.submit();
|
||||
}
|
||||
});
|
||||
const wrapperDeleteAction = document.getElementById("wrapper-delete-action")
|
||||
if (wrapperDeleteAction) {
|
||||
const member_type = wrapperDeleteAction.getAttribute("data-member-type");
|
||||
const member_id = wrapperDeleteAction.getAttribute("data-member-id");
|
||||
const num_domains = wrapperDeleteAction.getAttribute("data-num-domains");
|
||||
const member_name = wrapperDeleteAction.getAttribute("data-member-name");
|
||||
const member_email = wrapperDeleteAction.getAttribute("data-member-email");
|
||||
const member_delete_url = `${member_type}-${member_id}/delete`;
|
||||
const unique_id = `${member_type}-${member_id}`;
|
||||
|
||||
let cancelInvitationButton = member_type === "invitedmember" ? "Cancel invitation" : "Remove member";
|
||||
wrapperDeleteAction.innerHTML = generateKebabHTML('remove-member', unique_id, cancelInvitationButton, `for ${member_name}`);
|
||||
|
||||
// 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
|
||||
MembersTable.addMemberModal(num_domains, member_email || "Samwise Gamgee", member_delete_url, unique_id, wrapperDeleteAction);
|
||||
|
||||
uswdsInitializeModals();
|
||||
|
||||
// Now the DOM and modals are ready, add listeners to the submit buttons
|
||||
const modals = document.querySelectorAll('.usa-modal__content');
|
||||
|
||||
modals.forEach(modal => {
|
||||
const submitButton = modal.querySelector('.usa-modal__submit');
|
||||
const closeButton = modal.querySelector('.usa-modal__close');
|
||||
submitButton.addEventListener('click', () => {
|
||||
closeButton.click();
|
||||
let delete_member_form = document.getElementById("member-delete-form");
|
||||
if (delete_member_form) {
|
||||
delete_member_form.submit();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
@ -52,122 +52,122 @@ export function initPortfolioNewMemberPageToggle() {
|
|||
export function initAddNewMemberPageListeners() {
|
||||
let add_member_form = document.getElementById("add_member_form");
|
||||
if (!add_member_form){
|
||||
return;
|
||||
return;
|
||||
}
|
||||
document.getElementById("confirm_new_member_submit").addEventListener("click", function() {
|
||||
// Upon confirmation, submit the form
|
||||
document.getElementById("add_member_form").submit();
|
||||
// Upon confirmation, submit the form
|
||||
document.getElementById("add_member_form").submit();
|
||||
});
|
||||
|
||||
document.getElementById("add_member_form").addEventListener("submit", function(event) {
|
||||
event.preventDefault(); // Prevents the form from submitting
|
||||
const form = document.getElementById("add_member_form")
|
||||
const formData = new FormData(form);
|
||||
event.preventDefault(); // Prevents the form from submitting
|
||||
const form = document.getElementById("add_member_form")
|
||||
const formData = new FormData(form);
|
||||
|
||||
// Check if the form is valid
|
||||
// If the form is valid, open the confirmation modal
|
||||
// If the form is invalid, submit it to trigger error
|
||||
fetch(form.action, {
|
||||
method: "POST",
|
||||
body: formData,
|
||||
headers: {
|
||||
"X-Requested-With": "XMLHttpRequest",
|
||||
"X-CSRFToken": getCsrfToken()
|
||||
}
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.is_valid) {
|
||||
// If the form is valid, show the confirmation modal before submitting
|
||||
openAddMemberConfirmationModal();
|
||||
} else {
|
||||
// If the form is not valid, trigger error messages by firing a submit event
|
||||
form.submit();
|
||||
}
|
||||
});
|
||||
// Check if the form is valid
|
||||
// If the form is valid, open the confirmation modal
|
||||
// If the form is invalid, submit it to trigger error
|
||||
fetch(form.action, {
|
||||
method: "POST",
|
||||
body: formData,
|
||||
headers: {
|
||||
"X-Requested-With": "XMLHttpRequest",
|
||||
"X-CSRFToken": getCsrfToken()
|
||||
}
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.is_valid) {
|
||||
// If the form is valid, show the confirmation modal before submitting
|
||||
openAddMemberConfirmationModal();
|
||||
} else {
|
||||
// If the form is not valid, trigger error messages by firing a submit event
|
||||
form.submit();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/*
|
||||
Helper function to capitalize the first letter in a string (for display purposes)
|
||||
Helper function to capitalize the first letter in a string (for display purposes)
|
||||
*/
|
||||
function capitalizeFirstLetter(text) {
|
||||
if (!text) return ''; // Return empty string if input is falsy
|
||||
return text.charAt(0).toUpperCase() + text.slice(1);
|
||||
if (!text) return ''; // Return empty string if input is falsy
|
||||
return text.charAt(0).toUpperCase() + text.slice(1);
|
||||
}
|
||||
|
||||
/*
|
||||
Populates contents of the "Add Member" confirmation modal
|
||||
Populates contents of the "Add Member" confirmation modal
|
||||
*/
|
||||
function populatePermissionDetails(permission_details_div_id) {
|
||||
const permissionDetailsContainer = document.getElementById("permission_details");
|
||||
permissionDetailsContainer.innerHTML = ""; // Clear previous content
|
||||
const permissionDetailsContainer = document.getElementById("permission_details");
|
||||
permissionDetailsContainer.innerHTML = ""; // Clear previous content
|
||||
|
||||
// Get all permission sections (divs with h3 and radio inputs)
|
||||
const permissionSections = document.querySelectorAll(`#${permission_details_div_id} > h3`);
|
||||
// Get all permission sections (divs with h3 and radio inputs)
|
||||
const permissionSections = document.querySelectorAll(`#${permission_details_div_id} > h3`);
|
||||
|
||||
permissionSections.forEach(section => {
|
||||
// Find the <h3> element text
|
||||
const sectionTitle = section.textContent;
|
||||
permissionSections.forEach(section => {
|
||||
// Find the <h3> element text
|
||||
const sectionTitle = section.textContent;
|
||||
|
||||
// Find the associated radio buttons container (next fieldset)
|
||||
const fieldset = section.nextElementSibling;
|
||||
// Find the associated radio buttons container (next fieldset)
|
||||
const fieldset = section.nextElementSibling;
|
||||
|
||||
if (fieldset && fieldset.tagName.toLowerCase() === 'fieldset') {
|
||||
// Get the selected radio button within this fieldset
|
||||
const selectedRadio = fieldset.querySelector('input[type="radio"]:checked');
|
||||
if (fieldset && fieldset.tagName.toLowerCase() === 'fieldset') {
|
||||
// Get the selected radio button within this fieldset
|
||||
const selectedRadio = fieldset.querySelector('input[type="radio"]:checked');
|
||||
|
||||
// If a radio button is selected, get its label text
|
||||
let selectedPermission = "No permission selected";
|
||||
if (selectedRadio) {
|
||||
const label = fieldset.querySelector(`label[for="${selectedRadio.id}"]`);
|
||||
selectedPermission = label ? label.textContent : "No permission selected";
|
||||
}
|
||||
// If a radio button is selected, get its label text
|
||||
let selectedPermission = "No permission selected";
|
||||
if (selectedRadio) {
|
||||
const label = fieldset.querySelector(`label[for="${selectedRadio.id}"]`);
|
||||
selectedPermission = label ? label.textContent : "No permission selected";
|
||||
}
|
||||
|
||||
// Create new elements for the modal content
|
||||
const titleElement = document.createElement("h4");
|
||||
titleElement.textContent = sectionTitle;
|
||||
titleElement.classList.add("text-primary");
|
||||
titleElement.classList.add("margin-bottom-0");
|
||||
// Create new elements for the modal content
|
||||
const titleElement = document.createElement("h4");
|
||||
titleElement.textContent = sectionTitle;
|
||||
titleElement.classList.add("text-primary");
|
||||
titleElement.classList.add("margin-bottom-0");
|
||||
|
||||
const permissionElement = document.createElement("p");
|
||||
permissionElement.textContent = selectedPermission;
|
||||
permissionElement.classList.add("margin-top-0");
|
||||
const permissionElement = document.createElement("p");
|
||||
permissionElement.textContent = selectedPermission;
|
||||
permissionElement.classList.add("margin-top-0");
|
||||
|
||||
// Append to the modal content container
|
||||
permissionDetailsContainer.appendChild(titleElement);
|
||||
permissionDetailsContainer.appendChild(permissionElement);
|
||||
}
|
||||
});
|
||||
// Append to the modal content container
|
||||
permissionDetailsContainer.appendChild(titleElement);
|
||||
permissionDetailsContainer.appendChild(permissionElement);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
Updates and opens the "Add Member" confirmation modal.
|
||||
Updates and opens the "Add Member" confirmation modal.
|
||||
*/
|
||||
function openAddMemberConfirmationModal() {
|
||||
//------- Populate modal details
|
||||
// Get email value
|
||||
let emailValue = document.getElementById('id_email').value;
|
||||
document.getElementById('modalEmail').textContent = emailValue;
|
||||
//------- Populate modal details
|
||||
// Get email value
|
||||
let emailValue = document.getElementById('id_email').value;
|
||||
document.getElementById('modalEmail').textContent = emailValue;
|
||||
|
||||
// Get selected radio button for access level
|
||||
let selectedAccess = document.querySelector('input[name="member_access_level"]:checked');
|
||||
// Set the selected permission text to 'Basic' or 'Admin' (the value of the selected radio button)
|
||||
// This value does not have the first letter capitalized so let's capitalize it
|
||||
let accessText = selectedAccess ? capitalizeFirstLetter(selectedAccess.value) : "No access level selected";
|
||||
document.getElementById('modalAccessLevel').textContent = accessText;
|
||||
// Get selected radio button for access level
|
||||
let selectedAccess = document.querySelector('input[name="member_access_level"]:checked');
|
||||
// Set the selected permission text to 'Basic' or 'Admin' (the value of the selected radio button)
|
||||
// This value does not have the first letter capitalized so let's capitalize it
|
||||
let accessText = selectedAccess ? capitalizeFirstLetter(selectedAccess.value) : "No access level selected";
|
||||
document.getElementById('modalAccessLevel').textContent = accessText;
|
||||
|
||||
// Populate permission details based on access level
|
||||
if (selectedAccess && selectedAccess.value === 'admin') {
|
||||
populatePermissionDetails('new-member-admin-permissions');
|
||||
} else {
|
||||
populatePermissionDetails('new-member-basic-permissions');
|
||||
// Populate permission details based on access level
|
||||
if (selectedAccess && selectedAccess.value === 'admin') {
|
||||
populatePermissionDetails('new-member-admin-permissions');
|
||||
} else {
|
||||
populatePermissionDetails('new-member-basic-permissions');
|
||||
}
|
||||
|
||||
//------- Show the modal
|
||||
let modalTrigger = document.querySelector("#invite_member_trigger");
|
||||
if (modalTrigger) {
|
||||
modalTrigger.click();
|
||||
}
|
||||
|
||||
//------- Show the modal
|
||||
let modalTrigger = document.querySelector("#invite_member_trigger");
|
||||
if (modalTrigger) {
|
||||
modalTrigger.click();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -190,3 +190,4 @@
|
|||
|
||||
{% endblock portfolio_content%}
|
||||
|
||||
|
||||
|
|
|
@ -2567,20 +2567,18 @@ class TestPortfolioInviteNewMemberView(TestWithUser, WebTest):
|
|||
final_response = self.client.post(
|
||||
reverse("new-member"),
|
||||
{
|
||||
"role": "organization_member",
|
||||
"domain_request_permission_member": "view_all_requests",
|
||||
"member_access_level": "basic",
|
||||
"basic_org_domain_request_permissions": "view_only",
|
||||
"email": self.new_member_email,
|
||||
},
|
||||
)
|
||||
|
||||
# Ensure the final submission is successful
|
||||
self.assertEqual(final_response.status_code, 302) # redirects after success
|
||||
|
||||
# Validate Database Changes
|
||||
portfolio_invite = PortfolioInvitation.objects.filter(
|
||||
email=self.new_member_email,
|
||||
portfolio=self.portfolio,
|
||||
roles__exact=[UserPortfolioRoleChoices.ORGANIZATION_MEMBER],
|
||||
additional_permissions__exact=[UserPortfolioPermissionChoices.VIEW_ALL_REQUESTS],
|
||||
email=self.new_member_email, portfolio=self.portfolio
|
||||
).first()
|
||||
self.assertIsNotNone(portfolio_invite)
|
||||
self.assertEqual(portfolio_invite.email, self.new_member_email)
|
||||
|
@ -2602,14 +2600,15 @@ class TestPortfolioInviteNewMemberView(TestWithUser, WebTest):
|
|||
response = self.client.post(
|
||||
reverse("new-member"),
|
||||
{
|
||||
"role": "organization_member",
|
||||
"domain_request_permission_member": "view_all_requests",
|
||||
"member_access_level": "basic",
|
||||
"basic_org_domain_request_permissions": "view_only",
|
||||
"email": self.invited_member_email,
|
||||
},
|
||||
)
|
||||
# Unsucessful form submissions return the same page with a 200
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.context["form"].errors["email"][0], "An invitation already exists for this user.")
|
||||
self.assertEqual(response.status_code, 302) # Redirects
|
||||
|
||||
# TODO: verify messages
|
||||
|
||||
# Validate Database has not changed
|
||||
invite_count_after = PortfolioInvitation.objects.count()
|
||||
self.assertEqual(invite_count_after, invite_count_before)
|
||||
|
@ -2631,13 +2630,14 @@ class TestPortfolioInviteNewMemberView(TestWithUser, WebTest):
|
|||
response = self.client.post(
|
||||
reverse("new-member"),
|
||||
{
|
||||
"role": "organization_member",
|
||||
"domain_request_permissions_member": "view_all_requests",
|
||||
"member_access_level": "basic",
|
||||
"basic_org_domain_request_permissions": "view_only",
|
||||
"email": self.user.email,
|
||||
},
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.context["form"].errors["email"][0], "User is already a member of this portfolio.")
|
||||
self.assertEqual(response.status_code, 302) # Redirects
|
||||
|
||||
# TODO: verify messages
|
||||
|
||||
# Validate Database has not changed
|
||||
invite_count_after = PortfolioInvitation.objects.count()
|
||||
|
@ -2645,6 +2645,7 @@ class TestPortfolioInviteNewMemberView(TestWithUser, WebTest):
|
|||
|
||||
|
||||
class TestEditPortfolioMemberView(WebTest):
|
||||
"""Tests for the edit member page on portfolios"""
|
||||
|
||||
def setUp(self):
|
||||
self.user = create_user()
|
||||
|
|
|
@ -466,24 +466,6 @@ class PortfolioBasePermission(PermissionsLoginMixin):
|
|||
return self.request.user.is_org_user(self.request)
|
||||
|
||||
|
||||
class PortfolioInvitationCreatePermission(PortfolioBasePermission):
|
||||
"""Permission mixin that redirects to portfolio pages if user
|
||||
has access, otherwise 403"""
|
||||
|
||||
def has_permission(self):
|
||||
"""Check if this user has access to this portfolio.
|
||||
|
||||
The user is in self.request.user and the portfolio can be looked
|
||||
up from the portfolio's primary key in self.kwargs["pk"]
|
||||
"""
|
||||
has_perm = super().has_permission()
|
||||
if not has_perm:
|
||||
return False
|
||||
|
||||
portfolio = self.request.session.get("portfolio")
|
||||
return self.request.user.has_edit_members_portfolio_permission(portfolio)
|
||||
|
||||
|
||||
class PortfolioDomainsPermission(PortfolioBasePermission):
|
||||
"""Permission mixin that allows access to portfolio domain pages if user
|
||||
has access, otherwise 403"""
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
import abc # abstract base class
|
||||
from django.views.generic import DetailView, DeleteView, TemplateView, UpdateView
|
||||
from registrar.models import Domain, DomainRequest, DomainInvitation, Portfolio
|
||||
from registrar.models.portfolio_invitation import PortfolioInvitation
|
||||
from registrar.models.user import User
|
||||
from registrar.models.user_domain_role import UserDomainRole
|
||||
|
||||
|
@ -15,7 +14,6 @@ from .mixins import (
|
|||
DomainRequestWizardPermission,
|
||||
PortfolioDomainRequestsPermission,
|
||||
PortfolioDomainsPermission,
|
||||
PortfolioInvitationCreatePermission,
|
||||
PortfolioMemberDomainsPermission,
|
||||
PortfolioMemberDomainsEditPermission,
|
||||
PortfolioMemberEditPermission,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue