mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-08-04 17:01:56 +02:00
code refactors for readability and efficiency, comments for clarity
This commit is contained in:
parent
b06ffec6b6
commit
07e4960cc6
2 changed files with 153 additions and 80 deletions
|
@ -1874,7 +1874,24 @@ class MembersTable extends LoadTableBase {
|
|||
super('.members__table', '.members__table-wrapper', '#members__search-field', '#members__search-field-submit', '.members__reset-search', '.members__reset-filters', '.members__no-data', '.members__no-search-results');
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes "Show More" buttons on the page, enabling toggle functionality to show or hide content.
|
||||
*
|
||||
* The function finds elements with "Show More" buttons and sets up a click event listener to toggle the visibility
|
||||
* of a corresponding content div. When clicked, the button updates its visual state (e.g., text/icon change),
|
||||
* and the associated content is shown or hidden based on its current visibility status.
|
||||
*
|
||||
* @function initShowMoreButtons
|
||||
*/
|
||||
initShowMoreButtons() {
|
||||
/**
|
||||
* Toggles the visibility of a content section when the "Show More" button is clicked.
|
||||
* Updates the button text/icon based on whether the content is shown or hidden.
|
||||
*
|
||||
* @param {HTMLElement} toggleButton - The button that toggles the content visibility.
|
||||
* @param {HTMLElement} contentDiv - The content div whose visibility is toggled.
|
||||
* @param {HTMLElement} buttonParentRow - The parent row element containing the button.
|
||||
*/
|
||||
function toggleShowMoreButton(toggleButton, contentDiv, buttonParentRow) {
|
||||
const spanElement = toggleButton.querySelector('span');
|
||||
const useElement = toggleButton.querySelector('use');
|
||||
|
@ -1896,6 +1913,7 @@ class MembersTable extends LoadTableBase {
|
|||
let toggleButtons = document.querySelectorAll('.usa-button--show-more-button');
|
||||
toggleButtons.forEach((toggleButton) => {
|
||||
|
||||
// get contentDiv for element specified in data-for attribute of toggleButton
|
||||
let dataFor = toggleButton.dataset.for;
|
||||
let contentDiv = document.getElementById(dataFor);
|
||||
let buttonParentRow = toggleButton.parentElement.parentElement;
|
||||
|
@ -1911,50 +1929,141 @@ class MembersTable extends LoadTableBase {
|
|||
}
|
||||
|
||||
/**
|
||||
* Helper function which takes last_active and returns display value and sort value
|
||||
* @param {*} last_active - UTC date, or strings "Invited" or "Invalid date"
|
||||
* @returns
|
||||
* Converts a given `last_active` value into a display value and a numeric sort value.
|
||||
* The input can be a UTC date, the strings "Invited", "Invalid date", or null/undefined.
|
||||
*
|
||||
* @param {string} last_active - UTC date string or special status like "Invited" or "Invalid date".
|
||||
* @returns {Object} - An object containing `display_value` (formatted date or status string)
|
||||
* and `sort_value` (numeric value for sorting).
|
||||
*/
|
||||
handleLastActive(last_active) {
|
||||
let last_active_display = '';
|
||||
let last_active_sort_value = -1; // sort by default as numeric value to sort with dates
|
||||
|
||||
const invited = 'Invited';
|
||||
const invalid_date = 'Invalid date';
|
||||
const options = { year: 'numeric', month: 'long', day: 'numeric' }; // Format options for date
|
||||
|
||||
// Check if last_active is not null
|
||||
if (last_active) {
|
||||
if (last_active === invited) {
|
||||
last_active_display = invited;
|
||||
last_active_sort_value = 0; // sort as numeric value
|
||||
} else if (last_active === invalid_date) {
|
||||
last_active_display = invalid_date;
|
||||
const options = { year: 'numeric', month: 'long', day: 'numeric' }; // Date display format
|
||||
|
||||
let display_value = invalid_date; // Default display value for invalid or null dates
|
||||
let sort_value = -1; // Default sort value for invalid or null dates
|
||||
|
||||
if (last_active === invited) {
|
||||
// Handle "Invited" status: special case with 0 sort value
|
||||
display_value = invited;
|
||||
sort_value = 0;
|
||||
} else if (last_active && last_active !== invalid_date) {
|
||||
// Parse and format valid UTC date strings
|
||||
const parsedDate = new Date(last_active);
|
||||
|
||||
if (!isNaN(parsedDate.getTime())) {
|
||||
// Valid date
|
||||
display_value = parsedDate.toLocaleDateString('en-US', options);
|
||||
sort_value = parsedDate.getTime(); // Use timestamp for sorting
|
||||
} else {
|
||||
const parsedDate = new Date(last_active);
|
||||
|
||||
try {
|
||||
if (!isNaN(parsedDate.getTime())) { // Check if the date is valid
|
||||
last_active_display = parsedDate.toLocaleDateString('en-US', options);
|
||||
last_active_sort_value = parsedDate.getTime(); // sort as numeric value, seconds since 1970
|
||||
} else {
|
||||
throw new Error(invalid_date); // Throw an error to catch in catch block
|
||||
}
|
||||
} catch (e) { // catch invalid values and treat as 'Invalid date'
|
||||
console.error(`Error parsing date: ${last_active}. Error: ${e}`);
|
||||
last_active_display = invalid_date;
|
||||
}
|
||||
console.error(`Error: Invalid date string provided: ${last_active}`);
|
||||
}
|
||||
} else { // last_active is null or undefined
|
||||
last_active_display = invalid_date;
|
||||
}
|
||||
|
||||
return {
|
||||
display_value: last_active_display,
|
||||
sort_value: last_active_sort_value
|
||||
};
|
||||
|
||||
return { display_value, sort_value };
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates HTML for the list of domains assigned to a member.
|
||||
*
|
||||
* @param {number} num_domains - The number of domains the member is assigned to.
|
||||
* @param {Array} domain_names - An array of domain names.
|
||||
* @param {Array} domain_urls - An array of corresponding domain URLs.
|
||||
* @returns {string} - A string of HTML displaying the domains assigned to the member.
|
||||
*/
|
||||
generateDomainsHTML(num_domains, domain_names, domain_urls) {
|
||||
// Initialize an empty string for the HTML
|
||||
let domainsHTML = '';
|
||||
|
||||
// Only generate HTML if the member has one or more assigned domains
|
||||
if (num_domains > 0) {
|
||||
domainsHTML += "<div class='desktop:grid-col-5 margin-bottom-2 desktop:margin-bottom-0'>";
|
||||
domainsHTML += "<h4 class='margin-y-0 text-primary'>Domains assigned</h4>";
|
||||
domainsHTML += `<p class='margin-y-0'>This member is assigned to ${num_domains} domains:</p>`;
|
||||
domainsHTML += "<ul class='usa-list usa-list--unstyled margin-y-0'>";
|
||||
|
||||
// Display up to 6 domains with their URLs
|
||||
for (let i = 0; i < num_domains && i < 6; i++) {
|
||||
domainsHTML += `<li><a href="${domain_urls[i]}">${domain_names[i]}</a></li>`;
|
||||
}
|
||||
|
||||
domainsHTML += "</ul>";
|
||||
|
||||
// If there are more than 6 domains, display a "View assigned domains" link
|
||||
if (num_domains >= 6) {
|
||||
domainsHTML += "<p><a href='#'>View assigned domains</a></p>";
|
||||
}
|
||||
|
||||
domainsHTML += "</div>";
|
||||
}
|
||||
|
||||
return domainsHTML;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an HTML string summarizing a user's additional permissions within a portfolio,
|
||||
* based on the user's permissions and predefined permission choices.
|
||||
*
|
||||
* @param {Array} member_permissions - An array of permission strings that the member has.
|
||||
* @param {Object} UserPortfolioPermissionChoices - An object containing predefined permission choice constants.
|
||||
* Expected keys include:
|
||||
* - VIEW_ALL_DOMAINS
|
||||
* - VIEW_MANAGED_DOMAINS
|
||||
* - EDIT_REQUESTS
|
||||
* - VIEW_ALL_REQUESTS
|
||||
* - EDIT_MEMBERS
|
||||
* - VIEW_MEMBERS
|
||||
*
|
||||
* @returns {string} - A string of HTML representing the user's additional permissions.
|
||||
* If the user has no specific permissions, it returns a default message
|
||||
* indicating no additional permissions.
|
||||
*
|
||||
* Behavior:
|
||||
* - The function checks the user's permissions (`member_permissions`) and generates
|
||||
* corresponding HTML sections based on the permission choices defined in `UserPortfolioPermissionChoices`.
|
||||
* - Permissions are categorized into domains, requests, and members:
|
||||
* - Domains: Determines whether the user can view or manage all or assigned domains.
|
||||
* - Requests: Differentiates between users who can edit requests, view all requests, or have no request privileges.
|
||||
* - Members: Distinguishes between members who can manage or only view other members.
|
||||
* - If no relevant permissions are found, the function returns a message stating that the user has no additional permissions.
|
||||
* - The resulting HTML always includes a header "Additional permissions for this member" and appends the relevant permission descriptions.
|
||||
*/
|
||||
generatePermissionsHTML(member_permissions, UserPortfolioPermissionChoices) {
|
||||
let permissionsHTML = '';
|
||||
|
||||
// Check domain-related permissions
|
||||
if (member_permissions.includes(UserPortfolioPermissionChoices.VIEW_ALL_DOMAINS)) {
|
||||
permissionsHTML += "<p class='margin-top-1 p--blockquote'><strong class='text-base-dark'>Domains:</strong> Can view all organization domains. Can manage domains they are assigned to and edit information about the domain (including DNS settings).</p>";
|
||||
} else if (member_permissions.includes(UserPortfolioPermissionChoices.VIEW_MANAGED_DOMAINS)) {
|
||||
permissionsHTML += "<p class='margin-top-1 p--blockquote'><strong class='text-base-dark'>Domains:</strong> Can manage domains they are assigned to and edit information about the domain (including DNS settings).</p>";
|
||||
}
|
||||
|
||||
// Check request-related permissions
|
||||
if (member_permissions.includes(UserPortfolioPermissionChoices.EDIT_REQUESTS)) {
|
||||
permissionsHTML += "<p class='margin-top-1 p--blockquote'><strong class='text-base-dark'>Domain requests:</strong> Can view all organization domain requests. Can create domain requests and modify their own requests.</p>";
|
||||
} else if (member_permissions.includes(UserPortfolioPermissionChoices.VIEW_ALL_REQUESTS)) {
|
||||
permissionsHTML += "<p class='margin-top-1 p--blockquote'><strong class='text-base-dark'>Domain requests (view-only):</strong> Can view all organization domain requests. Can't create or modify any domain requests.</p>";
|
||||
}
|
||||
|
||||
// Check member-related permissions
|
||||
if (member_permissions.includes(UserPortfolioPermissionChoices.EDIT_MEMBERS)) {
|
||||
permissionsHTML += "<p class='margin-top-1 p--blockquote'><strong class='text-base-dark'>Members:</strong> Can manage members including inviting new members, removing current members, and assigning domains to members.</p>";
|
||||
} else if (member_permissions.includes(UserPortfolioPermissionChoices.VIEW_MEMBERS)) {
|
||||
permissionsHTML += "<p class='margin-top-1 p--blockquote'><strong class='text-base-dark'>Members (view-only):</strong> Can view all organizational members. Can't manage any members.</p>";
|
||||
}
|
||||
|
||||
// If no specific permissions are assigned, display a message indicating no additional permissions
|
||||
if (!permissionsHTML) {
|
||||
permissionsHTML += "<p class='margin-top-1 p--blockquote'><b>No additional permissions:</b> There are no additional permissions for this member.</p>";
|
||||
}
|
||||
|
||||
// Add a permissions header and wrap the entire output in a container
|
||||
permissionsHTML = "<div class='desktop:grid-col-7'><h4 class='margin-y-0 text-primary'>Additional permissions for this member</h4>" + permissionsHTML + "</div>";
|
||||
|
||||
return permissionsHTML;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads rows in the members list, as well as updates pagination around the members list
|
||||
* based on the supplied attributes.
|
||||
|
@ -2033,47 +2142,11 @@ class MembersTable extends LoadTableBase {
|
|||
if (member.is_admin)
|
||||
admin_tagHTML = `<span class="usa-tag margin-left-1 bg-primary">Admin</span>`
|
||||
|
||||
// generate html blocks for domains and permissions for the member
|
||||
let domainsHTML = this.generateDomainsHTML(num_domains, domain_names, domain_urls);
|
||||
let permissionsHTML = this.generatePermissionsHTML(member_permissions, UserPortfolioPermissionChoices);
|
||||
|
||||
// domainsHTML block and permissionsHTML block need to be wrapped with hide/show toggle, Expand
|
||||
|
||||
let domainsHTML = '';
|
||||
if (num_domains > 0) {
|
||||
domainsHTML += "<div class='desktop:grid-col-5 margin-bottom-2 desktop:margin-bottom-0'>";
|
||||
domainsHTML += "<h4 class='margin-y-0 text-primary'>Domains assigned</h4>";
|
||||
domainsHTML += "<p class='margin-y-0'>This member is assigned to " + num_domains + " domains:";
|
||||
domainsHTML += "<ul class='usa-list usa-list--unstyled margin-y-0'>";
|
||||
for (let i = 0; i < num_domains && i < 6; i++) {
|
||||
domainsHTML += `<li><a href="${domain_urls[i]}">${domain_names[i]}</a></li>`;
|
||||
}
|
||||
domainsHTML += "</ul>";
|
||||
if (num_domains >= 6) {
|
||||
domainsHTML += "<p><a href='#'>View assigned domains</a></p>";
|
||||
}
|
||||
domainsHTML += "</div>";
|
||||
}
|
||||
|
||||
let permissionsHTML = '';
|
||||
if (member_permissions.includes(UserPortfolioPermissionChoices.VIEW_ALL_DOMAINS)) {
|
||||
permissionsHTML += "<p class='margin-top-1 p--blockquote'><strong class='text-base-dark'>Domains:</strong> Can view all organization domains. Can manage domains they are assigned to and edit information about the domain (including DNS settings).</p>";
|
||||
} else if (member_permissions.includes(UserPortfolioPermissionChoices.VIEW_MANAGED_DOMAINS)) {
|
||||
permissionsHTML += "<p class='margin-top-1 p--blockquote'><strong class='text-base-dark'>Domains:</strong> Can manage domains they are assigned to and edit information about the domain (including DNS settings).</p>";
|
||||
}
|
||||
if (member_permissions.includes(UserPortfolioPermissionChoices.EDIT_REQUESTS)) {
|
||||
permissionsHTML += "<p class='margin-top-1 p--blockquote'><strong class='text-base-dark'>Domain requests:</strong> Can view all organization domain requests. Can create domain requests and modify their own requests.</p>";
|
||||
} else if (member_permissions.includes(UserPortfolioPermissionChoices.VIEW_ALL_REQUESTS)) {
|
||||
permissionsHTML += "<p class='margin-top-1 p--blockquote'><strong class='text-base-dark'>Domain requests (view-only):</strong> Can view all organization domain requests. Can't create or modify any domain requests.</p>";
|
||||
}
|
||||
if (member_permissions.includes(UserPortfolioPermissionChoices.EDIT_MEMBERS)) {
|
||||
permissionsHTML += "<p class='margin-top-1 p--blockquote'><strong class='text-base-dark'>Members:</strong> Can manage members including inviting new members, removing current members, and assigning domains to members.";
|
||||
} else if (member_permissions.includes(UserPortfolioPermissionChoices.VIEW_MEMBERS)) {
|
||||
permissionsHTML += "<p class='margin-top-1 p--blockquote'><strong class='text-base-dark'>Members (view-only):</strong> Can view all organizational members. Can't manage any members.";
|
||||
}
|
||||
// if there are no additional permissions, display a no additional permissions message
|
||||
if (!permissionsHTML) {
|
||||
permissionsHTML += "<p class='margin-top-1 p--blockquote'><b>No additional permissions:</b> There are no additional permissions for this member.</p>";
|
||||
}
|
||||
// add permissions header in all cases
|
||||
permissionsHTML = "<div class='desktop:grid-col-7'><h4 class='margin-y-0 text-primary'>Additional permissions for this member</h4>" + permissionsHTML + "</div>";
|
||||
|
||||
let showMoreButton = '';
|
||||
const showMoreRow = document.createElement('tr');
|
||||
if (domainsHTML || permissionsHTML) {
|
||||
|
|
|
@ -138,14 +138,14 @@ class GetPortfolioMembersJsonTest(MockEppLib, WebTest):
|
|||
actual_roles = {role for member in data["members"] for role in member["roles"]}
|
||||
self.assertEqual(expected_roles, actual_roles)
|
||||
|
||||
# Assert that the expected additional permissions are in the actual entire permissions list
|
||||
expected_additional_permissions = {
|
||||
UserPortfolioPermissionChoices.VIEW_MEMBERS,
|
||||
UserPortfolioPermissionChoices.EDIT_MEMBERS,
|
||||
}
|
||||
actual_additional_permissions = {
|
||||
permission for member in data["members"] for permission in member["permissions"]
|
||||
}
|
||||
self.assertTrue(expected_additional_permissions.issubset(actual_additional_permissions))
|
||||
# actual_permissions includes additional permissions as well as permissions from roles
|
||||
actual_permissions = {permission for member in data["members"] for permission in member["permissions"]}
|
||||
self.assertTrue(expected_additional_permissions.issubset(actual_permissions))
|
||||
|
||||
def test_get_portfolio_invited_json_authenticated(self):
|
||||
"""Test that portfolio invitees are returned properly for an authenticated user."""
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue