mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-08-04 17:01:56 +02:00
functionality done
This commit is contained in:
parent
c11a282f27
commit
b036b70112
4 changed files with 258 additions and 28 deletions
|
@ -1166,7 +1166,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||
const resetSearchButton = document.querySelector('.domains__reset-search');
|
||||
const resetFiltersButton = document.querySelector('.domains__reset-filters');
|
||||
const statusCheckboxes = document.querySelectorAll('input[name="filter-status"]');
|
||||
const statusIndicator = document.querySelector('.domain__filter-indicator');
|
||||
const statusIndicator = document.querySelector('.filter-indicator');
|
||||
const statusToggle = document.querySelector('.usa-button--filter');
|
||||
const portfolioElement = document.getElementById('portfolio-js-value');
|
||||
const portfolioValue = portfolioElement ? portfolioElement.getAttribute('data-portfolio') : null;
|
||||
|
@ -1419,10 +1419,10 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||
function updateStatusIndicator() {
|
||||
statusIndicator.innerHTML = '';
|
||||
// Even if the element is empty, it'll mess up the flex layout unless we set display none
|
||||
statusIndicator.hideElement();
|
||||
hideElement(statusIndicator);
|
||||
if (currentStatus.length)
|
||||
statusIndicator.innerHTML = '(' + currentStatus.length + ')';
|
||||
statusIndicator.showElement();
|
||||
showElement(statusIndicator);
|
||||
}
|
||||
|
||||
function closeFilters() {
|
||||
|
@ -1436,9 +1436,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 openFilterAccordion = document.querySelector('.usa-button--filter[aria-expanded="true"]');
|
||||
|
||||
if (accordionIsOpen && !accordion.contains(event.target)) {
|
||||
if (openFilterAccordion && !accordion.contains(event.target)) {
|
||||
closeFilters();
|
||||
}
|
||||
});
|
||||
|
@ -1447,9 +1447,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 openFilterAccordion = document.querySelector('.usa-button--filter[aria-expanded="true"]');
|
||||
|
||||
if (accordionIsOpen && !accordion.contains(event.target)) {
|
||||
if (openFilterAccordion && !accordion.contains(event.target)) {
|
||||
closeFilters();
|
||||
}
|
||||
});
|
||||
|
@ -1488,14 +1488,17 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||
const noDomainRequestsWrapper = document.querySelector('.domain-requests__no-data');
|
||||
const noSearchResultsWrapper = document.querySelector('.domain-requests__no-search-results');
|
||||
let scrollToTable = false;
|
||||
let currentStatus = [];
|
||||
let currentSearchTerm = '';
|
||||
const domainRequestsSearchInput = document.getElementById('domain-requests__search-field');
|
||||
const domainRequestsSearchSubmit = document.getElementById('domain-requests__search-field-submit');
|
||||
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;
|
||||
const resetFiltersButton = document.querySelector('.domains__reset-filters');
|
||||
const statusCheckboxes = document.querySelectorAll('input[name="filter-status"]');
|
||||
const statusIndicator = document.querySelector('.filter-indicator');
|
||||
const statusToggle = document.querySelector('.usa-button--filter');
|
||||
const portfolioElement = document.getElementById('portfolio-js-value');
|
||||
const portfolioValue = portfolioElement ? portfolioElement.getAttribute('data-portfolio') : null;
|
||||
|
||||
|
@ -1537,7 +1540,6 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||
return document.querySelector('input[name="csrfmiddlewaretoken"]').value;
|
||||
}
|
||||
|
||||
let currentStatus = []
|
||||
/**
|
||||
* Loads rows in the domain requests list, as well as updates pagination around the domain requests list
|
||||
* based on the supplied attributes.
|
||||
|
@ -1547,7 +1549,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, status = currentStatus, portfolio = portfolioValue, portfolio = portfolioValue) {
|
||||
function loadDomainRequests(page, sortBy = currentSortBy, order = currentOrder, scroll = scrollToTable, searchTerm = currentSearchTerm, status = currentStatus, portfolio = portfolioValue) {
|
||||
// fetch json of page of domain requests, given params
|
||||
let baseUrl = document.getElementById("get_domain_requests_json_url");
|
||||
if (!baseUrl) {
|
||||
|
@ -1882,6 +1884,44 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||
resetHeaders();
|
||||
});
|
||||
|
||||
if (statusToggle) {
|
||||
statusToggle.addEventListener('click', function() {
|
||||
toggleCaret(statusToggle);
|
||||
});
|
||||
}
|
||||
|
||||
// Add event listeners to status filter checkboxes
|
||||
statusCheckboxes.forEach(checkbox => {
|
||||
checkbox.addEventListener('change', function() {
|
||||
const checkboxValue = this.value;
|
||||
|
||||
// Update currentStatus array based on checkbox state
|
||||
if (this.checked) {
|
||||
currentStatus.push(checkboxValue);
|
||||
} else {
|
||||
const index = currentStatus.indexOf(checkboxValue);
|
||||
if (index > -1) {
|
||||
currentStatus.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Manage visibility of reset filters button
|
||||
if (currentStatus.length == 0) {
|
||||
hideElement(resetFiltersButton);
|
||||
} else {
|
||||
showElement(resetFiltersButton);
|
||||
}
|
||||
|
||||
// Disable the auto scroll
|
||||
scrollToTable = false;
|
||||
|
||||
// Call loadDomains with updated status
|
||||
loadDomainRequests(1, 'id', 'asc');
|
||||
resetHeaders();
|
||||
updateStatusIndicator();
|
||||
});
|
||||
});
|
||||
|
||||
// Reset UI and accessibility
|
||||
function resetHeaders() {
|
||||
tableHeaders.forEach(header => {
|
||||
|
@ -1906,34 +1946,85 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||
});
|
||||
}
|
||||
|
||||
function closeMoreActionMenu(accordionIsOpen) {
|
||||
if (accordionIsOpen.getAttribute("aria-expanded") === "true") {
|
||||
accordionIsOpen.click();
|
||||
function closeMoreActionMenu(openFilterAccordion) {
|
||||
if (openFilterAccordion.getAttribute("aria-expanded") === "true") {
|
||||
openFilterAccordion.click();
|
||||
}
|
||||
}
|
||||
|
||||
function resetFilters() {
|
||||
currentStatus = [];
|
||||
statusCheckboxes.forEach(checkbox => {
|
||||
checkbox.checked = false;
|
||||
});
|
||||
hideElement(resetFiltersButton);
|
||||
|
||||
// Disable the auto scroll
|
||||
scrollToTable = false;
|
||||
|
||||
loadDomainRequests(1, 'id', 'asc');
|
||||
resetHeaders();
|
||||
updateStatusIndicator();
|
||||
// No need to toggle close the filters. The focus shift will trigger that for us.
|
||||
}
|
||||
|
||||
if (resetFiltersButton) {
|
||||
resetFiltersButton.addEventListener('click', function() {
|
||||
resetFilters();
|
||||
});
|
||||
}
|
||||
|
||||
function updateStatusIndicator() {
|
||||
statusIndicator.innerHTML = '';
|
||||
// Even if the element is empty, it'll mess up the flex layout unless we set display none
|
||||
hideElement(statusIndicator);
|
||||
if (currentStatus.length)
|
||||
statusIndicator.innerHTML = '(' + currentStatus.length + ')';
|
||||
showElement(statusIndicator);
|
||||
}
|
||||
|
||||
function closeFilters() {
|
||||
if (statusToggle.getAttribute("aria-expanded") === "true") {
|
||||
statusToggle.click();
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('focusin', function(event) {
|
||||
const accordions = document.querySelectorAll('.usa-accordion--more-actions');
|
||||
const openAccordions = document.querySelectorAll('.usa-button--more-actions[aria-expanded="true"]');
|
||||
const openMoreActionsAccordions = document.querySelectorAll('.usa-button--more-actions[aria-expanded="true"]');
|
||||
|
||||
openAccordions.forEach((openAccordionButton) => {
|
||||
const accordion = openAccordionButton.closest('.usa-accordion--more-actions'); // Find the corresponding accordion
|
||||
if (accordion && !accordion.contains(event.target)) {
|
||||
closeMoreActionMenu(openAccordionButton); // Close the accordion if the focus is outside
|
||||
openMoreActionsAccordions.forEach((openMoreActionsAccordionButton) => {
|
||||
const moreActionsAccordion = openMoreActionsAccordionButton.closest('.usa-accordion--more-actions'); // Find the corresponding accordion
|
||||
if (moreActionsAccordion && !moreActionsAccordion.contains(event.target)) {
|
||||
closeMoreActionMenu(openMoreActionsAccordionButton); // Close the accordion if the focus is outside
|
||||
}
|
||||
});
|
||||
|
||||
const openFilterAccordion = document.querySelector('.usa-button--filter[aria-expanded="true"]');
|
||||
const moreFilterAccordion = openFilterAccordion ? openFilterAccordion.closest('.usa-accordion--select') : undefined;
|
||||
|
||||
if (openFilterAccordion) {
|
||||
if (!moreFilterAccordion.contains(event.target)) {
|
||||
closeFilters();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('click', function(event) {
|
||||
const accordions = document.querySelectorAll('.usa-accordion--more-actions');
|
||||
const openAccordions = document.querySelectorAll('.usa-button--more-actions[aria-expanded="true"]');
|
||||
const openMoreActionsAccordions = document.querySelectorAll('.usa-button--more-actions[aria-expanded="true"]');
|
||||
|
||||
openAccordions.forEach((openAccordionButton) => {
|
||||
const accordion = openAccordionButton.closest('.usa-accordion--more-actions'); // Find the corresponding accordion
|
||||
openMoreActionsAccordions.forEach((openMoreActionsAccordionButton) => {
|
||||
const accordion = openMoreActionsAccordionButton.closest('.usa-accordion--more-actions'); // Find the corresponding accordion
|
||||
if (accordion && !accordion.contains(event.target)) {
|
||||
closeMoreActionMenu(openAccordionButton); // Close the accordion if the click is outside
|
||||
closeMoreActionMenu(openMoreActionsAccordionButton); // Close the accordion if the click is outside
|
||||
}
|
||||
});
|
||||
|
||||
const openFilterAccordion = document.querySelector('.usa-button--filter[aria-expanded="true"]');
|
||||
const moreFilterAccordion = openFilterAccordion ? openFilterAccordion.closest('.usa-accordion--select') : undefined;
|
||||
|
||||
if (openFilterAccordion && moreFilterAccordion && !moreFilterAccordion.contains(event.target)) {
|
||||
closeFilters();
|
||||
}
|
||||
});
|
||||
|
||||
// Initial load
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
</svg>
|
||||
Reset
|
||||
</button>
|
||||
<label class="usa-sr-only" for="domain-requests__search-field">Search by domain name</label>
|
||||
<label class="usa-sr-only" for="domain-requests__search-field">Search by domain name or creator</label>
|
||||
<input
|
||||
class="usa-input"
|
||||
id="domain-requests__search-field"
|
||||
|
@ -42,6 +42,125 @@
|
|||
</section>
|
||||
</div>
|
||||
</div>
|
||||
{% if portfolio %}
|
||||
<div class="display-flex flex-align-center">
|
||||
<span class="margin-right-2 margin-top-neg-1 usa-prose text-base-darker">Filter by</span>
|
||||
<div class="usa-accordion usa-accordion--select margin-right-2">
|
||||
<div class="usa-accordion__heading">
|
||||
<button
|
||||
type="button"
|
||||
class="usa-button usa-button--small padding--8-8-9 usa-button--outline usa-button--filter usa-accordion__button"
|
||||
aria-expanded="false"
|
||||
aria-controls="filter-status"
|
||||
>
|
||||
<span class="filter-indicator text-bold display-none"></span> Status
|
||||
<svg class="usa-icon top-2px" aria-hidden="true" focusable="false" role="img" width="24">
|
||||
<use xlink:href="/public/img/sprite.svg#expand_more"></use>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div id="filter-status" class="usa-accordion__content usa-prose shadow-1">
|
||||
<h2>Status</h2>
|
||||
<fieldset class="usa-fieldset margin-top-0">
|
||||
<legend class="usa-legend">Select to apply <span class="sr-only">status</span> filter</legend>
|
||||
<div class="usa-checkbox">
|
||||
<input
|
||||
class="usa-checkbox__input"
|
||||
id="filter-status-started"
|
||||
type="checkbox"
|
||||
name="filter-status"
|
||||
value="started"
|
||||
/>
|
||||
<label class="usa-checkbox__label" for="filter-status-started"
|
||||
>Started</label
|
||||
>
|
||||
</div>
|
||||
<div class="usa-checkbox">
|
||||
<input
|
||||
class="usa-checkbox__input"
|
||||
id="filter-status-submitted"
|
||||
type="checkbox"
|
||||
name="filter-status"
|
||||
value="submitted"
|
||||
/>
|
||||
<label class="usa-checkbox__label" for="filter-status-submitted"
|
||||
>Submitted</label
|
||||
>
|
||||
</div>
|
||||
<div class="usa-checkbox">
|
||||
<input
|
||||
class="usa-checkbox__input"
|
||||
id="filter-status-in-review"
|
||||
type="checkbox"
|
||||
name="filter-status"
|
||||
value="in review"
|
||||
/>
|
||||
<label class="usa-checkbox__label" for="filter-status-in-review"
|
||||
>In review</label
|
||||
>
|
||||
</div>
|
||||
<div class="usa-checkbox">
|
||||
<input
|
||||
class="usa-checkbox__input"
|
||||
id="filter-status-action-needed"
|
||||
type="checkbox"
|
||||
name="filter-status"
|
||||
value="action needed"
|
||||
/>
|
||||
<label class="usa-checkbox__label" for="filter-status-action-needed"
|
||||
>Action needed</label
|
||||
>
|
||||
</div>
|
||||
<div class="usa-checkbox">
|
||||
<input
|
||||
class="usa-checkbox__input"
|
||||
id="filter-status-rejected"
|
||||
type="checkbox"
|
||||
name="filter-status"
|
||||
value="rejected"
|
||||
/>
|
||||
<label class="usa-checkbox__label" for="filter-status-rejected"
|
||||
>Rejected</label
|
||||
>
|
||||
</div>
|
||||
<div class="usa-checkbox">
|
||||
<input
|
||||
class="usa-checkbox__input"
|
||||
id="filter-status-withdrawn"
|
||||
type="checkbox"
|
||||
name="filter-status"
|
||||
value="withdrawn"
|
||||
/>
|
||||
<label class="usa-checkbox__label" for="filter-status-withdrawn"
|
||||
>Withdrawn</label
|
||||
>
|
||||
</div>
|
||||
<div class="usa-checkbox">
|
||||
<input
|
||||
class="usa-checkbox__input"
|
||||
id="filter-status-ineligible"
|
||||
type="checkbox"
|
||||
name="filter-status"
|
||||
value="ineligible"
|
||||
/>
|
||||
<label class="usa-checkbox__label" for="filter-status-ineligible"
|
||||
>Ineligible</label
|
||||
>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
class="usa-button usa-button--small padding--8-12-9-12 usa-button--outline usa-button--filter domains__reset-filters display-none"
|
||||
>
|
||||
Clear filters
|
||||
<svg class="usa-icon top-1px" aria-hidden="true" focusable="false" role="img" width="24">
|
||||
<use xlink:href="/public/img/sprite.svg#close"></use>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="domain-requests__table-wrapper display-none usa-table-container--scrollable margin-top-0" tabindex="0">
|
||||
<table class="usa-table usa-table--borderless usa-table--stacked dotgov-table dotgov-table--stacked domain-requests__table">
|
||||
<caption class="sr-only">Your domain requests</caption>
|
||||
|
|
|
@ -64,7 +64,7 @@
|
|||
aria-expanded="false"
|
||||
aria-controls="filter-status"
|
||||
>
|
||||
<span class="domain__filter-indicator text-bold display-none"></span> Status
|
||||
<span class="filter-indicator text-bold display-none"></span> Status
|
||||
<svg class="usa-icon top-2px" aria-hidden="true" focusable="false" role="img" width="24">
|
||||
<use xlink:href="/public/img/sprite.svg#expand_more"></use>
|
||||
</svg>
|
||||
|
|
|
@ -18,6 +18,7 @@ def get_domain_requests_json(request):
|
|||
unfiltered_total = objects.count()
|
||||
|
||||
objects = apply_search(objects, request)
|
||||
objects = apply_status_filter(objects, request)
|
||||
objects = apply_sorting(objects, request)
|
||||
|
||||
paginator = Paginator(objects, 10)
|
||||
|
@ -62,6 +63,7 @@ def get_domain_request_ids_from_request(request):
|
|||
|
||||
def apply_search(queryset, request):
|
||||
search_term = request.GET.get("search_term")
|
||||
is_portfolio = request.GET.get("portfolio")
|
||||
|
||||
if search_term:
|
||||
search_term_lower = search_term.lower()
|
||||
|
@ -73,12 +75,30 @@ def apply_search(queryset, request):
|
|||
if search_term_lower in new_domain_request_text:
|
||||
queryset = queryset.filter(
|
||||
Q(requested_domain__name__icontains=search_term) | Q(requested_domain__isnull=True)
|
||||
)
|
||||
)
|
||||
elif is_portfolio:
|
||||
queryset = queryset.filter(Q(requested_domain__name__icontains=search_term) | Q(creator__first_name__icontains=search_term) | Q(creator__last_name__icontains=search_term) | Q(creator__email__icontains=search_term))
|
||||
# For non org users
|
||||
else:
|
||||
queryset = queryset.filter(Q(requested_domain__name__icontains=search_term))
|
||||
return queryset
|
||||
|
||||
|
||||
def apply_status_filter(queryset, request):
|
||||
status_param = request.GET.get("status")
|
||||
if status_param:
|
||||
status_list = status_param.split(",")
|
||||
statuses = [status for status in status_list if status in DomainRequest.DomainRequestStatus.values]
|
||||
# Construct Q objects for statuses that can be queried through ORM
|
||||
status_query = Q()
|
||||
if statuses:
|
||||
status_query |= Q(status__in=statuses)
|
||||
# Apply the combined query
|
||||
queryset = queryset.filter(status_query)
|
||||
|
||||
return queryset
|
||||
|
||||
|
||||
def apply_sorting(queryset, request):
|
||||
sort_by = request.GET.get("sort_by", "id") # Default to 'id'
|
||||
order = request.GET.get("order", "asc") # Default to 'asc'
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue