Skip to content

Commit

Permalink
Merge branch 'main' into dk/3166-node-permissions-fix
Browse files Browse the repository at this point in the history
  • Loading branch information
dave-kennedy-ecs committed Dec 5, 2024
2 parents c3a3fd2 + ee4a584 commit 85b4caf
Show file tree
Hide file tree
Showing 6 changed files with 1,472 additions and 288 deletions.
1,130 changes: 972 additions & 158 deletions src/package-lock.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions src/registrar/assets/src/js/getgov/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { initDomainRequestsTable } from './table-domain-requests.js';
import { initMembersTable } from './table-members.js';
import { initMemberDomainsTable } from './table-member-domains.js';
import { initPortfolioMemberPageToggle } from './portfolio-member-page.js';
import { initAddNewMemberPageListeners } from './portfolio-member-page.js';

initDomainValidators();

Expand Down Expand Up @@ -42,3 +43,4 @@ initMembersTable();
initMemberDomainsTable();

initPortfolioMemberPageToggle();
initAddNewMemberPageListeners();
129 changes: 129 additions & 0 deletions src/registrar/assets/src/js/getgov/portfolio-member-page.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { uswdsInitializeModals } from './helpers-uswds.js';
import { getCsrfToken } from './helpers.js';
import { generateKebabHTML } from './table-base.js';
import { MembersTable } from './table-members.js';

Expand Down Expand Up @@ -41,3 +42,131 @@ export function initPortfolioMemberPageToggle() {
}
});
}


/**
* Hooks up specialized listeners for handling form validation and modals
* on the Add New Member page.
*/
export function initAddNewMemberPageListeners() {
add_member_form = document.getElementById("add_member_form")
if (!add_member_form){
return;
}
document.getElementById("confirm_new_member_submit").addEventListener("click", function() {
// 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);

// 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)
*/
function capitalizeFirstLetter(text) {
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
*/
function populatePermissionDetails(permission_details_div_id) {
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`);

permissionSections.forEach(section => {
// Find the <h3> element text
const sectionTitle = section.textContent;

// 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 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");

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

/*
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;

// 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')
}

//------- Show the modal
let modalTrigger = document.querySelector("#invite_member_trigger");
if (modalTrigger) {
modalTrigger.click()
}
}

}
88 changes: 82 additions & 6 deletions src/registrar/templates/portfolio_members_add_new.html
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ <h1>Add a new member</h1>

{% include "includes/required_fields.html" %}

<form class="usa-form usa-form--large" method="post" novalidate>
<form class="usa-form usa-form--large" method="post" id="add_member_form" novalidate>

<fieldset class="usa-fieldset margin-top-2">
<legend>
<h2>Email</h2>
Expand Down Expand Up @@ -80,12 +81,17 @@ <h2>Member Access</h2>
<h2>Admin access permissions</h2>
<p>Member permissions available for admin-level acccess.</p>

<h3 class="margin-bottom-0">Organization domain requests</h3>
<h3 class="summary-item__title
text-primary-dark
margin-bottom-0">Organization domain requests</h3>
{% with group_classes="usa-form-editable usa-form-editable--no-border padding-top-0" %}
{% input_with_errors form.admin_org_domain_request_permissions %}
{% endwith %}

<h3 class="margin-bottom-0 margin-top-3">Organization members</h3>
<h3 class="summary-item__title
text-primary-dark
margin-bottom-0
margin-top-3">Organization members</h3>
{% with group_classes="usa-form-editable usa-form-editable--no-border padding-top-0" %}
{% input_with_errors form.admin_org_members_permissions %}
{% endwith %}
Expand All @@ -94,8 +100,12 @@ <h3 class="margin-bottom-0 margin-top-3">Organization members</h3>
<!-- Basic access form -->
<div id="new-member-basic-permissions" class="margin-top-2">
<h2>Basic member permissions</h2>
<p>Member permissions available for basic-level access</p>
{% input_with_errors form.basic_org_domain_request_permissions %}
<p>Member permissions available for basic-level acccess.</p>

<h3 class="margin-bottom-0">Organization domain requests</h3>
{% with group_classes="usa-form-editable usa-form-editable--no-border padding-top-0" %}
{% input_with_errors form.basic_org_domain_request_permissions %}
{% endwith %}
</div>

<!-- Submit/cancel buttons -->
Expand All @@ -108,10 +118,76 @@ <h2>Basic member permissions</h2>
aria-label="Cancel adding new member"
>Cancel
</a>
<button type="submit" class="usa-button">Invite Member</button>
<a
id="invite_member_trigger"
href="#invite-member-modal"
class="usa-button usa-button--outline margin-top-1 display-none"
aria-controls="invite-member-modal"
data-open-modal
>Trigger invite member modal</a>
<button id="invite_new_member_submit" type="submit" class="usa-button">Invite Member</button>
</div>
</form>

<div
class="usa-modal"
id="invite-member-modal"
aria-labelledby="invite-member-heading"
aria-describedby="confirm-invite-description"
style="display: none;"
>
<div class="usa-modal__content">
<div class="usa-modal__main">
<h2 class="usa-modal__heading" id="invite-member-heading">
Invite this member to the organization?
</h2>
<h3 class="summary-item__title
text-primary-dark">Member information and permissions</h3>
<div class="usa-prose">
<!-- Display email as a header and access level -->
<h4 class="text-primary">Email</h4>
<p class="margin-top-0" id="modalEmail"></p>

<h4 class="text-primary">Member Access</h4>
<p class="margin-top-0" id="modalAccessLevel"></p>

<!-- Dynamic Permissions Details -->
<div id="permission_details"></div>
</div>

<div class="usa-modal__footer">
<ul class="usa-button-group">
<li class="usa-button-group__item">
<button id="confirm_new_member_submit" type="submit" class="usa-button">Yes, invite member</button>
</li>
<li class="usa-button-group__item">
<button
type="button"
class="usa-button usa-button--unstyled"
data-close-modal
onclick="closeModal()"
>
Cancel
</button>
</li>
</ul>
</div>
</div>
<button
type="button"
class="usa-button usa-modal__close"
aria-label="Close this window"
data-close-modal
onclick="closeModal()"
>
<svg class="usa-icon" aria-hidden="true" focusable="false" role="img">
<use xlink:href="{% static 'img/sprite.svg' %}#close"></use>
</svg>
</button>
</div>
</div>


{% endblock portfolio_content%}


Loading

0 comments on commit 85b4caf

Please sign in to comment.