Skip to content

Commit

Permalink
Merge pull request #2720 from cisagov/rjm/2351-org-requests-page
Browse files Browse the repository at this point in the history
#2351: Org requests page - [RJM]
  • Loading branch information
zandercymatics authored Sep 12, 2024
2 parents b25a441 + 4124ad1 commit ccd1d0f
Show file tree
Hide file tree
Showing 35 changed files with 1,104 additions and 230 deletions.
115 changes: 104 additions & 11 deletions src/registrar/assets/js/get-gov.js
Original file line number Diff line number Diff line change
Expand Up @@ -1168,7 +1168,6 @@ document.addEventListener('DOMContentLoaded', function() {
const statusCheckboxes = document.querySelectorAll('input[name="filter-status"]');
const statusIndicator = document.querySelector('.domain__filter-indicator');
const statusToggle = document.querySelector('.usa-button--filter');
const noPortfolioFlag = document.getElementById('no-portfolio-js-flag');
const portfolioElement = document.getElementById('portfolio-js-value');
const portfolioValue = portfolioElement ? portfolioElement.getAttribute('data-portfolio') : null;

Expand Down Expand Up @@ -1226,7 +1225,7 @@ document.addEventListener('DOMContentLoaded', function() {

let markupForSuborganizationRow = '';

if (!noPortfolioFlag) {
if (portfolioValue) {
markupForSuborganizationRow = `
<td>
<span class="text-wrap" aria-label="${domain.suborganization ? suborganization : 'No suborganization'}">${suborganization}</span>
Expand Down Expand Up @@ -1427,9 +1426,9 @@ document.addEventListener('DOMContentLoaded', function() {
// NOTE: We may need to evolve this as we add more filters.
document.addEventListener('focusin', function(event) {
const accordion = document.querySelector('.usa-accordion--select');
const accordionIsOpen = document.querySelector('.usa-button--filter[aria-expanded="true"]');
const accordionThatIsOpen = document.querySelector('.usa-button--filter[aria-expanded="true"]');

if (accordionIsOpen && !accordion.contains(event.target)) {
if (accordionThatIsOpen && !accordion.contains(event.target)) {
closeFilters();
}
});
Expand All @@ -1438,9 +1437,9 @@ document.addEventListener('DOMContentLoaded', function() {
// NOTE: We may need to evolve this as we add more filters.
document.addEventListener('click', function(event) {
const accordion = document.querySelector('.usa-accordion--select');
const accordionIsOpen = document.querySelector('.usa-button--filter[aria-expanded="true"]');
const accordionThatIsOpen = document.querySelector('.usa-button--filter[aria-expanded="true"]');

if (accordionIsOpen && !accordion.contains(event.target)) {
if (accordionThatIsOpen && !accordion.contains(event.target)) {
closeFilters();
}
});
Expand Down Expand Up @@ -1485,6 +1484,8 @@ document.addEventListener('DOMContentLoaded', function() {
const tableHeaders = document.querySelectorAll('.domain-requests__table th[data-sortable]');
const tableAnnouncementRegion = document.querySelector('.domain-requests__table-wrapper .usa-table__announcement-region');
const resetSearchButton = document.querySelector('.domain-requests__reset-search');
const portfolioElement = document.getElementById('portfolio-js-value');
const portfolioValue = portfolioElement ? portfolioElement.getAttribute('data-portfolio') : null;

/**
* Delete is actually a POST API that requires a csrf token. The token will be waiting for us in the template as a hidden input.
Expand Down Expand Up @@ -1533,7 +1534,7 @@ document.addEventListener('DOMContentLoaded', function() {
* @param {*} scroll - control for the scrollToElement functionality
* @param {*} searchTerm - the search term
*/
function loadDomainRequests(page, sortBy = currentSortBy, order = currentOrder, scroll = scrollToTable, searchTerm = currentSearchTerm) {
function loadDomainRequests(page, sortBy = currentSortBy, order = currentOrder, scroll = scrollToTable, searchTerm = currentSearchTerm, portfolio = portfolioValue) {
// fetch json of page of domain requests, given params
let baseUrl = document.getElementById("get_domain_requests_json_url");
if (!baseUrl) {
Expand All @@ -1545,7 +1546,12 @@ document.addEventListener('DOMContentLoaded', function() {
return;
}

fetch(`${baseUrlValue}?page=${page}&sort_by=${sortBy}&order=${order}&search_term=${searchTerm}`)
// fetch json of page of requests, given params
let url = `${baseUrlValue}?page=${page}&sort_by=${sortBy}&order=${order}&search_term=${searchTerm}`
if (portfolio)
url += `&portfolio=${portfolio}`

fetch(url)
.then(response => response.json())
.then(data => {
if (data.error) {
Expand Down Expand Up @@ -1601,10 +1607,21 @@ document.addEventListener('DOMContentLoaded', function() {
const actionLabel = request.action_label;
const submissionDate = request.last_submitted_date ? new Date(request.last_submitted_date).toLocaleDateString('en-US', options) : `<span class="text-base">Not submitted</span>`;

// Even if the request is not deletable, we may need this empty string for the td if the deletable column is displayed
// The markup for the delete function either be a simple trigger or a 3 dots menu with a hidden trigger (in the case of portfolio requests page)
// Even if the request is not deletable, we may need these empty strings for the td if the deletable column is displayed
let modalTrigger = '';

// If the request is deletable, create modal body and insert it
let markupCreatorRow = '';

if (portfolioValue) {
markupCreatorRow = `
<td>
<span class="text-wrap break-word">${request.creator ? request.creator : ''}</span>
</td>
`
}

// If the request is deletable, create modal body and insert it. This is true for both requests and portfolio requests pages
if (request.is_deletable) {
let modalHeading = '';
let modalDescription = '';
Expand All @@ -1627,7 +1644,7 @@ document.addEventListener('DOMContentLoaded', function() {
role="button"
id="button-toggle-delete-domain-alert-${request.id}"
href="#toggle-delete-domain-alert-${request.id}"
class="usa-button--unstyled text-no-underline late-loading-modal-trigger"
class="usa-button text-secondary usa-button--unstyled text-no-underline late-loading-modal-trigger line-height-sans-5"
aria-controls="toggle-delete-domain-alert-${request.id}"
data-open-modal
>
Expand Down Expand Up @@ -1692,8 +1709,57 @@ document.addEventListener('DOMContentLoaded', function() {
`

domainRequestsSectionWrapper.appendChild(modal);

// Request is deletable, modal and modalTrigger are built. Now check if we are on the portfolio requests page (by seeing if there is a portfolio value) and enhance the modalTrigger accordingly
if (portfolioValue) {
modalTrigger = `
<a
role="button"
id="button-toggle-delete-domain-alert-${request.id}"
href="#toggle-delete-domain-alert-${request.id}"
class="usa-button text-secondary usa-button--unstyled text-no-underline late-loading-modal-trigger margin-top-2 visible-mobile-flex line-height-sans-5"
aria-controls="toggle-delete-domain-alert-${request.id}"
data-open-modal
>
<svg class="usa-icon" aria-hidden="true" focusable="false" role="img" width="24">
<use xlink:href="/public/img/sprite.svg#delete"></use>
</svg> Delete <span class="usa-sr-only">${domainName}</span>
</a>
<div class="usa-accordion usa-accordion--more-actions margin-right-2 hidden-mobile-flex">
<div class="usa-accordion__heading">
<button
type="button"
class="usa-button usa-button--unstyled usa-button--with-icon usa-accordion__button usa-button--more-actions"
aria-expanded="false"
aria-controls="more-actions-${request.id}"
>
<svg class="usa-icon top-2px" aria-hidden="true" focusable="false" role="img" width="24">
<use xlink:href="/public/img/sprite.svg#more_vert"></use>
</svg>
</button>
</div>
<div id="more-actions-${request.id}" class="usa-accordion__content usa-prose shadow-1 left-auto right-0" hidden>
<h2>More options</h2>
<a
role="button"
id="button-toggle-delete-domain-alert-${request.id}"
href="#toggle-delete-domain-alert-${request.id}"
class="usa-button text-secondary usa-button--unstyled text-no-underline late-loading-modal-trigger margin-top-2 line-height-sans-5"
aria-controls="toggle-delete-domain-alert-${request.id}"
data-open-modal
>
<svg class="usa-icon" aria-hidden="true" focusable="false" role="img" width="24">
<use xlink:href="/public/img/sprite.svg#delete"></use>
</svg> Delete <span class="usa-sr-only">${domainName}</span>
</a>
</div>
</div>
`
}
}


const row = document.createElement('tr');
row.innerHTML = `
<th scope="row" role="rowheader" data-label="Domain name">
Expand All @@ -1702,6 +1768,7 @@ document.addEventListener('DOMContentLoaded', function() {
<td data-sort-value="${new Date(request.last_submitted_date).getTime()}" data-label="Date submitted">
${submissionDate}
</td>
${markupCreatorRow}
<td data-label="Status">
${request.status}
</td>
Expand Down Expand Up @@ -1817,6 +1884,32 @@ document.addEventListener('DOMContentLoaded', function() {
});
}

function closeMoreActionMenu(accordionThatIsOpen) {
if (accordionThatIsOpen.getAttribute("aria-expanded") === "true") {
accordionThatIsOpen.click();
}
}

document.addEventListener('focusin', function(event) {
closeOpenAccordions(event);
});

document.addEventListener('click', function(event) {
closeOpenAccordions(event);
});

function closeOpenAccordions(event) {
const openAccordions = document.querySelectorAll('.usa-button--more-actions[aria-expanded="true"]');
openAccordions.forEach((openAccordionButton) => {
// Find the corresponding accordion
const accordion = openAccordionButton.closest('.usa-accordion--more-actions');
if (accordion && !accordion.contains(event.target)) {
// Close the accordion if the click is outside
closeMoreActionMenu(openAccordionButton);
}
});
}

// Initial load
loadDomainRequests(1);
}
Expand Down
18 changes: 16 additions & 2 deletions src/registrar/assets/sass/_theme/_accordions.scss
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
@use "uswds-core" as *;

.usa-accordion--select {
.usa-accordion--select,
.usa-accordion--more-actions {
display: inline-block;
width: auto;
position: relative;
Expand All @@ -14,7 +15,6 @@
// Note, width is determined by a custom width class on one of the children
position: absolute;
z-index: 1;
top: 33.88px;
left: 0;
border-radius: 4px;
border: solid 1px color('base-lighter');
Expand All @@ -31,3 +31,17 @@
margin-top: 0 !important;
}
}

.usa-accordion--select .usa-accordion__content {
top: 33.88px;
}

.usa-accordion--more-actions .usa-accordion__content {
top: 30px;
}

tr:last-child .usa-accordion--more-actions .usa-accordion__content {
top: auto;
bottom: -10px;
right: 30px;
}
26 changes: 24 additions & 2 deletions src/registrar/assets/sass/_theme/_base.scss
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,23 @@ abbr[title] {
}
}

.hidden-mobile-flex {
display: none!important;
}
.visible-mobile-flex {
display: flex!important;
}

@include at-media(tablet) {
.hidden-mobile-flex {
display: flex!important;
}
.visible-mobile-flex {
display: none!important;
}
}


.flex-end {
align-items: flex-end;
}
Expand Down Expand Up @@ -200,6 +217,11 @@ abbr[title] {
}
}

.margin-right-neg-4px {
margin-right: -4px;
// Boost this USWDS utility class for the accordions in the portfolio requests table
.left-auto {
left: auto!important;
}

.break-word {
word-break: break-word;
}
15 changes: 7 additions & 8 deletions src/registrar/assets/sass/_theme/_buttons.scss
Original file line number Diff line number Diff line change
Expand Up @@ -211,14 +211,7 @@ a.usa-button--unstyled:visited {
align-items: center;
}


.dotgov-table a,
.usa-link--icon {
&:visited {
color: color('primary');
}
}

.dotgov-table a
a .usa-icon,
.usa-button--with-icon .usa-icon {
height: 1.3em;
Expand All @@ -230,3 +223,9 @@ a .usa-icon,
height: 1.5em;
width: 1.5em;
}

button.text-secondary,
button.text-secondary:hover,
.dotgov-table a.text-secondary {
color: $theme-color-error;
}
12 changes: 10 additions & 2 deletions src/registrar/assets/sass/_theme/_header.scss
Original file line number Diff line number Diff line change
Expand Up @@ -89,16 +89,24 @@
.usa-nav__primary {
.usa-nav-link,
.usa-nav-link:hover,
.usa-nav-link:active {
.usa-nav-link:active,
button {
color: color('primary');
font-weight: font-weight('normal');
font-size: 16px;
}
.usa-current,
.usa-current:hover,
.usa-current:active {
.usa-current:active,
button.usa-current {
font-weight: font-weight('bold');
}
button[aria-expanded="true"] {
color: color('white');
}
button:not(.usa-current):hover::after {
display: none!important;
}
}
.usa-nav__secondary {
// I don't know why USWDS has this at 2 rem, which puts it out of alignment
Expand Down
5 changes: 5 additions & 0 deletions src/registrar/config/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,11 @@
views.PortfolioDomainRequestsView.as_view(),
name="domain-requests",
),
path(
"no-organization-requests/",
views.PortfolioNoDomainRequestsView.as_view(),
name="no-portfolio-requests",
),
path(
"organization/",
views.PortfolioOrganizationView.as_view(),
Expand Down
33 changes: 20 additions & 13 deletions src/registrar/context_processors.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,35 +60,42 @@ def add_has_profile_feature_flag_to_context(request):

def portfolio_permissions(request):
"""Make portfolio permissions for the request user available in global context"""
context = {
portfolio_context = {
"has_base_portfolio_permission": False,
"has_domains_portfolio_permission": False,
"has_domain_requests_portfolio_permission": False,
"has_any_domains_portfolio_permission": False,
"has_any_requests_portfolio_permission": False,
"has_edit_request_portfolio_permission": False,
"has_view_suborganization_portfolio_permission": False,
"has_edit_suborganization_portfolio_permission": False,
"has_view_members_portfolio_permission": False,
"has_edit_members_portfolio_permission": False,
"has_view_suborganization": False,
"has_edit_suborganization": False,
"portfolio": None,
"has_organization_feature_flag": False,
"has_organization_requests_flag": False,
"has_organization_members_flag": False,
}
try:
portfolio = request.session.get("portfolio")
# Linting: line too long
view_suborg = request.user.has_view_suborganization_portfolio_permission(portfolio)
edit_suborg = request.user.has_edit_suborganization_portfolio_permission(portfolio)
if portfolio:
return {
"has_base_portfolio_permission": request.user.has_base_portfolio_permission(portfolio),
"has_domains_portfolio_permission": request.user.has_domains_portfolio_permission(portfolio),
"has_domain_requests_portfolio_permission": request.user.has_domain_requests_portfolio_permission(
portfolio
),
"has_edit_request_portfolio_permission": request.user.has_edit_request_portfolio_permission(portfolio),
"has_view_suborganization_portfolio_permission": view_suborg,
"has_edit_suborganization_portfolio_permission": edit_suborg,
"has_any_domains_portfolio_permission": request.user.has_any_domains_portfolio_permission(portfolio),
"has_any_requests_portfolio_permission": request.user.has_any_requests_portfolio_permission(portfolio),
"has_view_members_portfolio_permission": request.user.has_view_members_portfolio_permission(portfolio),
"has_edit_members_portfolio_permission": request.user.has_edit_members_portfolio_permission(portfolio),
"has_view_suborganization": request.user.has_view_suborganization(portfolio),
"has_edit_suborganization": request.user.has_edit_suborganization(portfolio),
"portfolio": portfolio,
"has_organization_feature_flag": True,
"has_organization_requests_flag": request.user.has_organization_requests_flag(),
"has_organization_members_flag": request.user.has_organization_members_flag(),
}
return context
return portfolio_context

except AttributeError:
# Handles cases where request.user might not exist
return context
return portfolio_context
Loading

0 comments on commit ccd1d0f

Please sign in to comment.