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
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();
}
}
}

View file

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

View file

@ -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()

View file

@ -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"""

View file

@ -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,