Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#2725: member management page - [NL] #2837

Merged
merged 37 commits into from
Oct 3, 2024
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
6954c1c
Initial Scaffolding (in progress)
CocoByte Sep 20, 2024
64b62f1
Initial scaffold for members page completed
CocoByte Sep 21, 2024
7bd0b06
Merge remote-tracking branch 'origin/main' into nl/2725-member-manage…
CocoByte Sep 21, 2024
d7fa9e8
data loads now
CocoByte Sep 23, 2024
ecf2e4d
Updated data, added manage/view icon, search & sort functionality
CocoByte Sep 23, 2024
2122745
Add member button (scaffold)
CocoByte Sep 23, 2024
64e623e
Delete permission settings around "add member" button
CocoByte Sep 23, 2024
5e2a1ce
Added admin tag, added user invites logic
CocoByte Sep 24, 2024
21b7cba
Merge remote-tracking branch 'origin/main' into nl/2725-member-manage…
CocoByte Sep 24, 2024
a8b26e3
Delete Admin read-only (comment out, just in case)
CocoByte Sep 25, 2024
b85c031
Update comments
CocoByte Sep 25, 2024
8937892
Adjust permissions so admins do not automatically have ability to vie…
CocoByte Sep 25, 2024
8673bc8
Cleanup
CocoByte Sep 25, 2024
1ad805b
linted
CocoByte Sep 25, 2024
91553ab
Removed unnecessary SCSS color override for tags
CocoByte Sep 25, 2024
140d342
Added logic for restricting editing members to only those who can edi…
CocoByte Sep 25, 2024
6ca8f69
Added some unit tests (need to fix failures still)
CocoByte Sep 25, 2024
08583ae
Cleanup logs / linted
CocoByte Sep 25, 2024
3dbb8f0
Unit test tweaks (still needs work)
CocoByte Sep 26, 2024
35fec1e
fixed unit tests
CocoByte Sep 26, 2024
ada24a6
JK on last commit -- more unit test fixes
CocoByte Sep 26, 2024
8508c99
added a test
CocoByte Sep 26, 2024
93fbfa3
linted
CocoByte Sep 26, 2024
6118ca1
fixes
CocoByte Sep 26, 2024
f624a49
last fix...
CocoByte Sep 26, 2024
92abc78
Cleanup
zandercymatics Sep 27, 2024
b84beef
Update create_federal_portfolio.py
zandercymatics Sep 30, 2024
fe31dbd
Update members_table.html
zandercymatics Sep 30, 2024
95cf224
Update src/registrar/tests/test_views_portfolio.py
zandercymatics Oct 1, 2024
c85ef25
Pr suggestions ( minus tests )
zandercymatics Oct 1, 2024
18e4647
Merge branch 'nl/2725-member-management-page' of github.com:cisagov/m…
zandercymatics Oct 1, 2024
7b09b35
fix bug
zandercymatics Oct 1, 2024
72afa03
pass in portfolio
zandercymatics Oct 1, 2024
4e9a58b
fix tests
zandercymatics Oct 2, 2024
b497931
fix sorting + unit tests
zandercymatics Oct 2, 2024
d451297
lint
zandercymatics Oct 2, 2024
76473e2
fix test
zandercymatics Oct 2, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
161 changes: 161 additions & 0 deletions src/registrar/assets/js/get-gov.js
Original file line number Diff line number Diff line change
Expand Up @@ -1853,6 +1853,150 @@ class DomainRequestsTable extends LoadTableBase {
}
}

class MembersTable extends LoadTableBase {

constructor() {
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');
}
/**
* Loads rows in the members list, as well as updates pagination around the members list
* based on the supplied attributes.
* @param {*} page - the page number of the results (starts with 1)
* @param {*} sortBy - the sort column option
* @param {*} order - the sort order {asc, desc}
* @param {*} scroll - control for the scrollToElement functionality
* @param {*} status - control for the status filter
* @param {*} searchTerm - the search term
* @param {*} portfolio - the portfolio id
*/
loadTable(page, sortBy = this.currentSortBy, order = this.currentOrder, scroll = this.scrollToTable, status = this.currentStatus, searchTerm =this.currentSearchTerm, portfolio = this.portfolioValue) {

// --------- SEARCH
let searchParams = new URLSearchParams(
{
"page": page,
"sort_by": sortBy,
"order": order,
"status": status,
"search_term": searchTerm
}
);
if (portfolio)
searchParams.append("portfolio", portfolio)


// --------- FETCH DATA
// fetch json of page of domais, given params
let baseUrl = document.getElementById("get_members_json_url");
if (!baseUrl) {
return;
}

let baseUrlValue = baseUrl.innerHTML;
if (!baseUrlValue) {
return;
}

let url = `${baseUrlValue}?${searchParams.toString()}` //TODO: uncomment for search function
fetch(url)
.then(response => response.json())
.then(data => {
if (data.error) {
console.error('Error in AJAX call: ' + data.error);
return;
}

// handle the display of proper messaging in the event that no members exist in the list or search returns no results
this.updateDisplay(data, this.tableWrapper, this.noTableWrapper, this.noSearchResultsWrapper, this.currentSearchTerm);

// identify the DOM element where the domain list will be inserted into the DOM
const memberList = document.querySelector('.members__table tbody');
memberList.innerHTML = '';

if (data.members)
zandercymatics marked this conversation as resolved.
Show resolved Hide resolved
{
data.members.forEach(member => {
// const actionUrl = domain.action_url;
const member_name = member.name;
const member_email = member.email;
const last_active = member.last_active;
const action_url = member.action_url;
const action_label = member.action_label;
const svg_icon = member.svg_icon;

const row = document.createElement('tr');

let admin_tagHTML = ``;
if (member.is_admin)
admin_tagHTML = `<span class="usa-tag margin-left-1 bg-primary">Admin</span>`

row.innerHTML = `
<th scope="row" role="rowheader" data-label="member email">
${member_email} ${admin_tagHTML}
</th>
<td data-sort-value="${last_active}" data-label="last_active">
${last_active}
</td>
<td>
<a href="${action_url}">
<svg class="usa-icon" aria-hidden="true" focusable="false" role="img" width="24">
<use xlink:href="/public/img/sprite.svg#${svg_icon}"></use>
</svg>
${action_label} <span class="usa-sr-only">${member_name}</span>
</a>
</td>
`;

memberList.appendChild(row);
});
}
else
{
//TODO: error message?
const row = document.createElement('tr');
row.innerHTML = `
<th scope="row" role="rowheader" data-label="member name">
ERROR
</th>
<td data-sort-value="test" data-label="name">
ERROR
</td>
`;

memberList.appendChild(row);
}




// initialize tool tips immediately after the associated DOM elements are added
initializeTooltips();
zandercymatics marked this conversation as resolved.
Show resolved Hide resolved

// Do not scroll on first page load
if (scroll)
ScrollToElement('class', 'members');
this.scrollToTable = true;

// update pagination
this.updatePagination(
'member',
'#members-pagination',
'#members-pagination .usa-pagination__counter',
'#members',
data.page,
data.num_pages,
data.has_previous,
data.has_next,
data.total,
);
this.currentSortBy = sortBy;
this.currentOrder = order;
this.currentSearchTerm = searchTerm;
})
.catch(error => console.error('Error fetching members:', error));
}
}


/**
* An IIFE that listens for DOM Content to be loaded, then executes. This function
Expand Down Expand Up @@ -1926,6 +2070,23 @@ const utcDateString = (dateString) => {
};



/**
* An IIFE that listens for DOM Content to be loaded, then executes. This function
* initializes the domains list and associated functionality on the home page of the app.
*
*/
document.addEventListener('DOMContentLoaded', function() {
const isMembersPage = document.querySelector("#members")
if (isMembersPage){
const membersTable = new MembersTable();
if (membersTable.tableWrapper) {
// Initial load
membersTable.loadTable(1);
}
}
});

/**
* An IIFE that displays confirmation modal on the user profile page
*/
Expand Down
1 change: 1 addition & 0 deletions src/registrar/assets/sass/_theme/_tooltips.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
@use "uswds-core" as *;


zandercymatics marked this conversation as resolved.
Show resolved Hide resolved
// Only apply this custom wrapping to desktop
@include at-media(desktop) {
.usa-tooltip--registrar .usa-tooltip__body {
Expand Down
21 changes: 17 additions & 4 deletions src/registrar/config/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,21 @@
ExportDataTypeUser,
)

from registrar.views.domain_request import Step
# --jsons
from registrar.views.domain_requests_json import get_domain_requests_json
from registrar.views.transfer_user import TransferUserView
from registrar.views.domains_json import get_domains_json
from registrar.views.portfolio_members_json import get_portfolio_members_json
from registrar.views.utility.api_views import (
get_senior_official_from_federal_agency_json,
get_federal_and_portfolio_types_from_federal_agency_json,
get_action_needed_email_for_user_json,
)
from registrar.views.domains_json import get_domains_json

from registrar.views.domain_request import Step
from registrar.views.transfer_user import TransferUserView
from registrar.views.utility import always_404
from api.views import available, get_current_federal, get_current_full


DOMAIN_REQUEST_NAMESPACE = views.DomainRequestWizard.URL_NAMESPACE
domain_request_urls = [
path("", views.DomainRequestWizard.as_view(), name=""),
Expand Down Expand Up @@ -74,6 +76,16 @@
views.PortfolioNoDomainsView.as_view(),
name="no-portfolio-domains",
),
path(
"members/",
views.PortfolioMembersView.as_view(),
name="members",
),
# path(
# "no-organization-members/",
# views.PortfolioNoMembersView.as_view(),
# name="no-portfolio-members",
# ),
path(
"requests/",
views.PortfolioDomainRequestsView.as_view(),
Expand Down Expand Up @@ -275,6 +287,7 @@
),
path("get-domains-json/", get_domains_json, name="get_domains_json"),
path("get-domain-requests-json/", get_domain_requests_json, name="get_domain_requests_json"),
path("get-portfolio-members-json/", get_portfolio_members_json, name="get_portfolio_members_json"),
]

# Djangooidc strips out context data from that context, so we define a custom error
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Generated by Django 4.2.10 on 2024-09-25 00:49

import django.contrib.postgres.fields
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("registrar", "0128_alter_domaininformation_state_territory_and_more"),
]

operations = [
migrations.AlterField(
model_name="portfolioinvitation",
name="portfolio_roles",
field=django.contrib.postgres.fields.ArrayField(
base_field=models.CharField(
choices=[("organization_admin", "Admin"), ("organization_member", "Member")], max_length=50
),
blank=True,
help_text="Select one or more roles.",
null=True,
size=None,
),
),
migrations.AlterField(
model_name="userportfoliopermission",
name="roles",
field=django.contrib.postgres.fields.ArrayField(
base_field=models.CharField(
choices=[("organization_admin", "Admin"), ("organization_member", "Member")], max_length=50
),
blank=True,
help_text="Select one or more roles.",
null=True,
size=None,
),
),
]
10 changes: 0 additions & 10 deletions src/registrar/models/user_portfolio_permission.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ class Meta:
PORTFOLIO_ROLE_PERMISSIONS = {
UserPortfolioRoleChoices.ORGANIZATION_ADMIN: [
UserPortfolioPermissionChoices.VIEW_ALL_DOMAINS,
UserPortfolioPermissionChoices.VIEW_MEMBERS,
UserPortfolioPermissionChoices.EDIT_MEMBERS,
Comment on lines -18 to -19
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tagging for myself: figure out why these were deleted

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I discussed this with Alysia. It is regarding the AC where admins cannot, by default, view or edit members. The AC says "this needs to be layered on", and it was clarified to me that this meant deleting these permissions from admin so that they have to be explicitly added in "additional permissions"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah thank you @CocoByte!

UserPortfolioPermissionChoices.VIEW_ALL_REQUESTS,
UserPortfolioPermissionChoices.EDIT_REQUESTS,
UserPortfolioPermissionChoices.VIEW_PORTFOLIO,
Expand All @@ -25,14 +23,6 @@ class Meta:
UserPortfolioPermissionChoices.VIEW_SUBORGANIZATION,
UserPortfolioPermissionChoices.EDIT_SUBORGANIZATION,
],
UserPortfolioRoleChoices.ORGANIZATION_ADMIN_READ_ONLY: [
UserPortfolioPermissionChoices.VIEW_ALL_DOMAINS,
UserPortfolioPermissionChoices.VIEW_MEMBERS,
UserPortfolioPermissionChoices.VIEW_ALL_REQUESTS,
UserPortfolioPermissionChoices.VIEW_PORTFOLIO,
# Domain: field specific permissions
UserPortfolioPermissionChoices.VIEW_SUBORGANIZATION,
],
UserPortfolioRoleChoices.ORGANIZATION_MEMBER: [
UserPortfolioPermissionChoices.VIEW_PORTFOLIO,
],
Expand Down
1 change: 0 additions & 1 deletion src/registrar/models/utility/portfolio_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ class UserPortfolioRoleChoices(models.TextChoices):
"""

ORGANIZATION_ADMIN = "organization_admin", "Admin"
ORGANIZATION_ADMIN_READ_ONLY = "organization_admin_read_only", "Admin read only"
ORGANIZATION_MEMBER = "organization_member", "Member"


Expand Down
4 changes: 2 additions & 2 deletions src/registrar/templates/includes/header_extended.html
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,9 @@
</li>
{% endif %}

{% if has_organization_members_flag %}
{% if has_organization_members_flag and has_view_members_portfolio_permission %}
<li class="usa-nav__primary-item">
<a href="#" class="usa-nav-link">
<a href="/members/" class="usa-nav-link">
Members
</a>
</li>
Expand Down
Loading
Loading