cleanup pt 2

This commit is contained in:
zandercymatics 2024-12-18 11:51:03 -07:00
parent 437981ff30
commit 08082fb0aa
No known key found for this signature in database
GPG key ID: FF4636ABEC9682B7
5 changed files with 135 additions and 153 deletions

View file

@ -7,41 +7,41 @@ import { hookupRadioTogglerListener } from './radios.js';
// This is specifically for the Member Profile (Manage Member) Page member/invitation removal // This is specifically for the Member Profile (Manage Member) Page member/invitation removal
export function initPortfolioNewMemberPageToggle() { export function initPortfolioNewMemberPageToggle() {
document.addEventListener("DOMContentLoaded", () => { document.addEventListener("DOMContentLoaded", () => {
const wrapperDeleteAction = document.getElementById("wrapper-delete-action") const wrapperDeleteAction = document.getElementById("wrapper-delete-action")
if (wrapperDeleteAction) { if (wrapperDeleteAction) {
const member_type = wrapperDeleteAction.getAttribute("data-member-type"); const member_type = wrapperDeleteAction.getAttribute("data-member-type");
const member_id = wrapperDeleteAction.getAttribute("data-member-id"); const member_id = wrapperDeleteAction.getAttribute("data-member-id");
const num_domains = wrapperDeleteAction.getAttribute("data-num-domains"); const num_domains = wrapperDeleteAction.getAttribute("data-num-domains");
const member_name = wrapperDeleteAction.getAttribute("data-member-name"); const member_name = wrapperDeleteAction.getAttribute("data-member-name");
const member_email = wrapperDeleteAction.getAttribute("data-member-email"); const member_email = wrapperDeleteAction.getAttribute("data-member-email");
const member_delete_url = `${member_type}-${member_id}/delete`; const member_delete_url = `${member_type}-${member_id}/delete`;
const unique_id = `${member_type}-${member_id}`; const unique_id = `${member_type}-${member_id}`;
let cancelInvitationButton = member_type === "invitedmember" ? "Cancel invitation" : "Remove member"; let cancelInvitationButton = member_type === "invitedmember" ? "Cancel invitation" : "Remove member";
wrapperDeleteAction.innerHTML = generateKebabHTML('remove-member', unique_id, cancelInvitationButton, `for ${member_name}`); 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 // 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 // All prod users will have emails linked to their account
MembersTable.addMemberModal(num_domains, member_email || "Samwise Gamgee", member_delete_url, unique_id, wrapperDeleteAction); MembersTable.addMemberModal(num_domains, member_email || "Samwise Gamgee", member_delete_url, unique_id, wrapperDeleteAction);
uswdsInitializeModals(); uswdsInitializeModals();
// Now the DOM and modals are ready, add listeners to the submit buttons // Now the DOM and modals are ready, add listeners to the submit buttons
const modals = document.querySelectorAll('.usa-modal__content'); const modals = document.querySelectorAll('.usa-modal__content');
modals.forEach(modal => { modals.forEach(modal => {
const submitButton = modal.querySelector('.usa-modal__submit'); const submitButton = modal.querySelector('.usa-modal__submit');
const closeButton = modal.querySelector('.usa-modal__close'); const closeButton = modal.querySelector('.usa-modal__close');
submitButton.addEventListener('click', () => { submitButton.addEventListener('click', () => {
closeButton.click(); closeButton.click();
let delete_member_form = document.getElementById("member-delete-form"); let delete_member_form = document.getElementById("member-delete-form");
if (delete_member_form) { if (delete_member_form) {
delete_member_form.submit(); delete_member_form.submit();
} }
});
}); });
} });
}); }
});
} }
@ -52,122 +52,122 @@ export function initPortfolioNewMemberPageToggle() {
export function initAddNewMemberPageListeners() { export function initAddNewMemberPageListeners() {
let add_member_form = document.getElementById("add_member_form"); let add_member_form = document.getElementById("add_member_form");
if (!add_member_form){ if (!add_member_form){
return; return;
} }
document.getElementById("confirm_new_member_submit").addEventListener("click", function() { document.getElementById("confirm_new_member_submit").addEventListener("click", function() {
// Upon confirmation, submit the form // Upon confirmation, submit the form
document.getElementById("add_member_form").submit(); document.getElementById("add_member_form").submit();
}); });
document.getElementById("add_member_form").addEventListener("submit", function(event) { document.getElementById("add_member_form").addEventListener("submit", function(event) {
event.preventDefault(); // Prevents the form from submitting event.preventDefault(); // Prevents the form from submitting
const form = document.getElementById("add_member_form") const form = document.getElementById("add_member_form")
const formData = new FormData(form); const formData = new FormData(form);
// Check if the form is valid // Check if the form is valid
// If the form is valid, open the confirmation modal // If the form is valid, open the confirmation modal
// If the form is invalid, submit it to trigger error // If the form is invalid, submit it to trigger error
fetch(form.action, { fetch(form.action, {
method: "POST", method: "POST",
body: formData, body: formData,
headers: { headers: {
"X-Requested-With": "XMLHttpRequest", "X-Requested-With": "XMLHttpRequest",
"X-CSRFToken": getCsrfToken() "X-CSRFToken": getCsrfToken()
} }
}) })
.then(response => response.json()) .then(response => response.json())
.then(data => { .then(data => {
if (data.is_valid) { if (data.is_valid) {
// If the form is valid, show the confirmation modal before submitting // If the form is valid, show the confirmation modal before submitting
openAddMemberConfirmationModal(); openAddMemberConfirmationModal();
} else { } else {
// If the form is not valid, trigger error messages by firing a submit event // If the form is not valid, trigger error messages by firing a submit event
form.submit(); 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) { function capitalizeFirstLetter(text) {
if (!text) return ''; // Return empty string if input is falsy if (!text) return ''; // Return empty string if input is falsy
return text.charAt(0).toUpperCase() + text.slice(1); 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) { function populatePermissionDetails(permission_details_div_id) {
const permissionDetailsContainer = document.getElementById("permission_details"); const permissionDetailsContainer = document.getElementById("permission_details");
permissionDetailsContainer.innerHTML = ""; // Clear previous content permissionDetailsContainer.innerHTML = ""; // Clear previous content
// Get all permission sections (divs with h3 and radio inputs) // Get all permission sections (divs with h3 and radio inputs)
const permissionSections = document.querySelectorAll(`#${permission_details_div_id} > h3`); const permissionSections = document.querySelectorAll(`#${permission_details_div_id} > h3`);
permissionSections.forEach(section => { permissionSections.forEach(section => {
// Find the <h3> element text // Find the <h3> element text
const sectionTitle = section.textContent; const sectionTitle = section.textContent;
// Find the associated radio buttons container (next fieldset) // Find the associated radio buttons container (next fieldset)
const fieldset = section.nextElementSibling; const fieldset = section.nextElementSibling;
if (fieldset && fieldset.tagName.toLowerCase() === 'fieldset') { if (fieldset && fieldset.tagName.toLowerCase() === 'fieldset') {
// Get the selected radio button within this fieldset // Get the selected radio button within this fieldset
const selectedRadio = fieldset.querySelector('input[type="radio"]:checked'); const selectedRadio = fieldset.querySelector('input[type="radio"]:checked');
// If a radio button is selected, get its label text // If a radio button is selected, get its label text
let selectedPermission = "No permission selected"; let selectedPermission = "No permission selected";
if (selectedRadio) { if (selectedRadio) {
const label = fieldset.querySelector(`label[for="${selectedRadio.id}"]`); const label = fieldset.querySelector(`label[for="${selectedRadio.id}"]`);
selectedPermission = label ? label.textContent : "No permission selected"; selectedPermission = label ? label.textContent : "No permission selected";
} }
// Create new elements for the modal content // Create new elements for the modal content
const titleElement = document.createElement("h4"); const titleElement = document.createElement("h4");
titleElement.textContent = sectionTitle; titleElement.textContent = sectionTitle;
titleElement.classList.add("text-primary"); titleElement.classList.add("text-primary");
titleElement.classList.add("margin-bottom-0"); titleElement.classList.add("margin-bottom-0");
const permissionElement = document.createElement("p"); const permissionElement = document.createElement("p");
permissionElement.textContent = selectedPermission; permissionElement.textContent = selectedPermission;
permissionElement.classList.add("margin-top-0"); permissionElement.classList.add("margin-top-0");
// Append to the modal content container // Append to the modal content container
permissionDetailsContainer.appendChild(titleElement); permissionDetailsContainer.appendChild(titleElement);
permissionDetailsContainer.appendChild(permissionElement); permissionDetailsContainer.appendChild(permissionElement);
} }
}); });
} }
/* /*
Updates and opens the "Add Member" confirmation modal. Updates and opens the "Add Member" confirmation modal.
*/ */
function openAddMemberConfirmationModal() { function openAddMemberConfirmationModal() {
//------- Populate modal details //------- Populate modal details
// Get email value // Get email value
let emailValue = document.getElementById('id_email').value; let emailValue = document.getElementById('id_email').value;
document.getElementById('modalEmail').textContent = emailValue; document.getElementById('modalEmail').textContent = emailValue;
// Get selected radio button for access level // Get selected radio button for access level
let selectedAccess = document.querySelector('input[name="member_access_level"]:checked'); 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) // 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 // This value does not have the first letter capitalized so let's capitalize it
let accessText = selectedAccess ? capitalizeFirstLetter(selectedAccess.value) : "No access level selected"; let accessText = selectedAccess ? capitalizeFirstLetter(selectedAccess.value) : "No access level selected";
document.getElementById('modalAccessLevel').textContent = accessText; document.getElementById('modalAccessLevel').textContent = accessText;
// Populate permission details based on access level // Populate permission details based on access level
if (selectedAccess && selectedAccess.value === 'admin') { if (selectedAccess && selectedAccess.value === 'admin') {
populatePermissionDetails('new-member-admin-permissions'); populatePermissionDetails('new-member-admin-permissions');
} else { } else {
populatePermissionDetails('new-member-basic-permissions'); 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();
}
} }
} }

View file

@ -190,3 +190,4 @@
{% endblock portfolio_content%} {% endblock portfolio_content%}

View file

@ -2567,20 +2567,18 @@ class TestPortfolioInviteNewMemberView(TestWithUser, WebTest):
final_response = self.client.post( final_response = self.client.post(
reverse("new-member"), reverse("new-member"),
{ {
"role": "organization_member", "member_access_level": "basic",
"domain_request_permission_member": "view_all_requests", "basic_org_domain_request_permissions": "view_only",
"email": self.new_member_email, "email": self.new_member_email,
}, },
) )
# Ensure the final submission is successful # Ensure the final submission is successful
self.assertEqual(final_response.status_code, 302) # redirects after success self.assertEqual(final_response.status_code, 302) # redirects after success
# Validate Database Changes # Validate Database Changes
portfolio_invite = PortfolioInvitation.objects.filter( portfolio_invite = PortfolioInvitation.objects.filter(
email=self.new_member_email, email=self.new_member_email, portfolio=self.portfolio
portfolio=self.portfolio,
roles__exact=[UserPortfolioRoleChoices.ORGANIZATION_MEMBER],
additional_permissions__exact=[UserPortfolioPermissionChoices.VIEW_ALL_REQUESTS],
).first() ).first()
self.assertIsNotNone(portfolio_invite) self.assertIsNotNone(portfolio_invite)
self.assertEqual(portfolio_invite.email, self.new_member_email) self.assertEqual(portfolio_invite.email, self.new_member_email)
@ -2602,14 +2600,15 @@ class TestPortfolioInviteNewMemberView(TestWithUser, WebTest):
response = self.client.post( response = self.client.post(
reverse("new-member"), reverse("new-member"),
{ {
"role": "organization_member", "member_access_level": "basic",
"domain_request_permission_member": "view_all_requests", "basic_org_domain_request_permissions": "view_only",
"email": self.invited_member_email, "email": self.invited_member_email,
}, },
) )
# Unsucessful form submissions return the same page with a 200 self.assertEqual(response.status_code, 302) # Redirects
self.assertEqual(response.status_code, 200)
self.assertEqual(response.context["form"].errors["email"][0], "An invitation already exists for this user.") # TODO: verify messages
# Validate Database has not changed # Validate Database has not changed
invite_count_after = PortfolioInvitation.objects.count() invite_count_after = PortfolioInvitation.objects.count()
self.assertEqual(invite_count_after, invite_count_before) self.assertEqual(invite_count_after, invite_count_before)
@ -2631,13 +2630,14 @@ class TestPortfolioInviteNewMemberView(TestWithUser, WebTest):
response = self.client.post( response = self.client.post(
reverse("new-member"), reverse("new-member"),
{ {
"role": "organization_member", "member_access_level": "basic",
"domain_request_permissions_member": "view_all_requests", "basic_org_domain_request_permissions": "view_only",
"email": self.user.email, "email": self.user.email,
}, },
) )
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 302) # Redirects
self.assertEqual(response.context["form"].errors["email"][0], "User is already a member of this portfolio.")
# TODO: verify messages
# Validate Database has not changed # Validate Database has not changed
invite_count_after = PortfolioInvitation.objects.count() invite_count_after = PortfolioInvitation.objects.count()
@ -2645,6 +2645,7 @@ class TestPortfolioInviteNewMemberView(TestWithUser, WebTest):
class TestEditPortfolioMemberView(WebTest): class TestEditPortfolioMemberView(WebTest):
"""Tests for the edit member page on portfolios"""
def setUp(self): def setUp(self):
self.user = create_user() self.user = create_user()

View file

@ -466,24 +466,6 @@ class PortfolioBasePermission(PermissionsLoginMixin):
return self.request.user.is_org_user(self.request) 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): class PortfolioDomainsPermission(PortfolioBasePermission):
"""Permission mixin that allows access to portfolio domain pages if user """Permission mixin that allows access to portfolio domain pages if user
has access, otherwise 403""" has access, otherwise 403"""

View file

@ -3,7 +3,6 @@
import abc # abstract base class import abc # abstract base class
from django.views.generic import DetailView, DeleteView, TemplateView, UpdateView from django.views.generic import DetailView, DeleteView, TemplateView, UpdateView
from registrar.models import Domain, DomainRequest, DomainInvitation, Portfolio 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 import User
from registrar.models.user_domain_role import UserDomainRole from registrar.models.user_domain_role import UserDomainRole
@ -15,7 +14,6 @@ from .mixins import (
DomainRequestWizardPermission, DomainRequestWizardPermission,
PortfolioDomainRequestsPermission, PortfolioDomainRequestsPermission,
PortfolioDomainsPermission, PortfolioDomainsPermission,
PortfolioInvitationCreatePermission,
PortfolioMemberDomainsPermission, PortfolioMemberDomainsPermission,
PortfolioMemberDomainsEditPermission, PortfolioMemberDomainsEditPermission,
PortfolioMemberEditPermission, PortfolioMemberEditPermission,