Skip to content

Commit

Permalink
Search - Pagination (#2683)
Browse files Browse the repository at this point in the history
* Form - Limit and page fields

* View/HTML/JS - Pagination object implementation

* Search - order by FAC accepted date by default

* Remove a print
  • Loading branch information
jperson1 authored Nov 2, 2023
1 parent 9a55124 commit 724e048
Show file tree
Hide file tree
Showing 5 changed files with 151 additions and 79 deletions.
5 changes: 5 additions & 0 deletions backend/dissemination/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ class SearchForm(forms.Form):
(x, str(x)) for x in range(2016, 2024)
) # ((2016, "2016"), (2017, "2017"), ..., (2023, "2023"))

# Query params
entity_name = forms.CharField(required=False)
uei_or_ein = forms.CharField(required=False)
aln = forms.CharField(required=False)
Expand All @@ -14,3 +15,7 @@ class SearchForm(forms.Form):
cog_or_oversight = forms.CharField(required=False)
agency_name = forms.CharField(required=False)
audit_year = forms.MultipleChoiceField(choices=AY_choices, required=False)

# Display params
limit = forms.CharField(required=False)
page = forms.CharField(required=False)
2 changes: 1 addition & 1 deletion backend/dissemination/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,6 @@ def search_general(
fiscal_year_match = Q(audit_year__in=audit_years)
query.add(fiscal_year_match, Q.AND)

results = General.objects.filter(query)
results = General.objects.filter(query).order_by("-fac_accepted_date")

return results
155 changes: 79 additions & 76 deletions backend/dissemination/templates/search.html
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,14 @@ <h3>Filters</h3>
aria-controls="entity-name">Name (Entity, Auditee, or Auditor)</button>
</label>
<textarea class="usa-textarea" id="entity-name" name="entity_name">{{form.cleaned_data.entity_name}}</textarea>
{% comment %} Fiscal Year {% endcomment %}
{% comment %} Audit Year {% endcomment %}
<button type="button"
class="usa-accordion__button"
aria-expanded="true"
aria-controls="audit-year">Audit Year</button>
<div id="audit-year">
{% for value, text in form.audit_year.field.choices %}
<div class="usa-checkbox bg-base-lightest">
<div class="usa-checkbox bg-base-lighter">
<input class="usa-checkbox__input"
id="audit-year-{{ text }}"
name="audit_year"
Expand Down Expand Up @@ -121,14 +121,30 @@ <h3>Filters</h3>
<input class="usa-button" type="submit" value="Search" />
<button class="usa-button usa-button--unstyled">Reset Search</button>
</div>
{% comment %} Hidden page input for use when clicking pagination buttons {% endcomment %}
<input class="usa-input"
id="page"
name="page"
type="number"
value="{{ page }}"
hidden />
</form>
</div>
<div class="tablet:grid-col-8 audit-search-results">
<h2>Search single audit reports</h2>
<h2 class="font-sans-2xl">Search single audit reports</h2>
{% if results %}
<div class="usa-alert usa-alert--info">
<div class="usa-alert__body">
<h4 class="usa-alert__heading">Sorting</h4>
<p class="usa-alert__text">
Use the arrows at the top of each column to sort results. Sorting only applies to the results shown per page.
</p>
</div>
</div>
<table class="usa-table usa-table--striped">
<caption>
Results {{ results|length }}
Results: {{ results_count }}
<span class="margin-left-2 text-normal text-italic">showing {{ limit }} per page</span>
</caption>
<thead>
<tr>
Expand All @@ -137,16 +153,18 @@ <h2>Search single audit reports</h2>
<th data-sortable scope="col" role="columnheader">Acc Date</th>
<th data-sortable scope="col" role="columnheader">AY</th>
<th data-sortable scope="col" role="columnheader">Cog or Over</th>
<th data-sortable scope="col" role="columnheader">View</th>
<th data-sortable scope="col" role="columnheader">PDF</th>
<th scope="col" role="columnheader">View</th>
<th scope="col" role="columnheader">PDF</th>
</tr>
</thead>
<tbody>
{% for result in results %}
<tr>
<th scope="row" role="rowheader">{{ result.auditee_name }}</th>
<td>{{ result.auditee_uei }}</td>
<td data-sort-value="1696528569380">{{ result.fac_accepted_date }}</td>
{% comment %} Sorts ascending/descending by the numeric date string (i.e. 20231231) {% endcomment %}
<td data-sort-value={{ result.fac_accepted_date|date:"Ymd" }}>{{ result.fac_accepted_date }}
</td>
<td>{{ result.audit_year }}</td>
<td>
{% if result.oversight_agency %}
Expand All @@ -168,11 +186,13 @@ <h2>Search single audit reports</h2>
</a>
</td>
<td>
<a href="{% url 'dissemination:PdfDownload' report_id=result.report_id %}" target="_blank">
<a class="usa-link display-flex flex-column flex-align-center"
href="{% url 'dissemination:PdfDownload' report_id=result.report_id %}"
target="_blank">
<svg class="usa-icon usa-icon--size-4"
aria-hidden="true"
focusable="false"
role="img">
aria-hidden="true"
focusable="false"
role="img">
{% uswds_sprite "file_download" %}
</svg>
</a>
Expand All @@ -183,70 +203,53 @@ <h2>Search single audit reports</h2>
</table>
<nav aria-label="Pagination" class="usa-pagination">
<ul class="usa-pagination__list">
<li class="usa-pagination__item usa-pagination__arrow">
<a href="javascript:void(0);"
class="usa-pagination__link usa-pagination__previous-page"
aria-label="Previous page">
<svg class="usa-icon" aria-hidden="true" role="img">
{% uswds_sprite "navigate_before" %}
</svg>
<span class="usa-pagination__link-text">Previous</span></a>
</li>
<li class="usa-pagination__item usa-pagination__page-no">
<a href="javascript:void(0);"
class="usa-pagination__button"
aria-label="Page 1">1</a>
</li>
<li class="usa-pagination__item usa-pagination__overflow"
aria-label="ellipsis indicating non-visible pages">
<span></span>
</li>
<li class="usa-pagination__item usa-pagination__page-no">
<a href="javascript:void(0);"
class="usa-pagination__button"
aria-label="Page 9">9</a>
</li>
<li class="usa-pagination__item usa-pagination__page-no">
<a href="javascript:void(0);"
class="usa-pagination__button usa-current"
aria-label="Page 10"
aria-current="page">10</a>
</li>
<li class="usa-pagination__item usa-pagination__page-no">
<a href="javascript:void(0);"
class="usa-pagination__button"
aria-label="Page 11">11</a>
</li>
<li class="usa-pagination__item usa-pagination__overflow"
aria-label="ellipsis indicating non-visible pages">
<span></span>
</li>
<li class="usa-pagination__item usa-pagination__page-no">
<a href="javascript:void(0);"
class="usa-pagination__button"
aria-label="Last page, page 24">24</a>
</li>
<li class="usa-pagination__item usa-pagination__arrow">
<a href="javascript:void(0);"
class="usa-pagination__link usa-pagination__next-page"
aria-label="Next page"><span class="usa-pagination__link-text">Next</span>
<svg class="usa-icon" aria-hidden="true" role="img">
{% uswds_sprite "navigate_next" %}
</svg>
</a>
</li>
</ul>
</nav>
{% else %}
<div class="search-instructions">
<img src="{% static 'img/circle-arrow.svg' %}"
alt="an arrow points left, toward the search form" />
<p>
Enter your filters and select <em>Search</em> to begin
</p>
{% if results.has_previous %}
<li class="usa-pagination__item usa-pagination__arrow">
<a class="usa-pagination__link usa-pagination__previous-page"
aria-label="Previous page">
<svg class="usa-icon" aria-hidden="true" role="img">
{% uswds_sprite "navigate_before" %}
</svg>
<span class="usa-pagination__link-text">Previous</span></a>
</li>
{% endif %}
{% for page_number in results.adjusted_elided_pages %}
{% if page_number == results.paginator.ELLIPSIS %}
<li class="usa-pagination__item usa-pagination__overflow"
aria-label="ellipsis indicating non-visible pages">
<span></span>
</li>
{% else %}
<li class="usa-pagination__item usa-pagination__page-no">
<a class="usa-pagination__button {% if results.number == page_number %}usa-current{% endif %}"
aria-label="Page {{ page_number }}">{{ page_number }}</a>
</li>
{% endif %}
{% endfor %}
{% if results.has_next %}
<li class="usa-pagination__item usa-pagination__arrow">
<a class="usa-pagination__link usa-pagination__next-page"
aria-label="Next page">
<span class="usa-pagination__link-text">Next</span>
<svg class="usa-icon" aria-hidden="true" role="img">
{% uswds_sprite "navigate_next" %}
</svg>
</a>
</li>
{% endif %}
</ul>
</nav>
{% else %}
<div class="search-instructions">
<img src="{% static 'img/circle-arrow.svg' %}"
alt="an arrow points left, toward the search form" />
<p>
Enter your filters and select <em>Search</em> to begin
</p>
</div>
{% endif %}
</div>
{% endif %}
</div>
</div>
</div>
</div>
{% endblock %}
<script src="{% static 'compiled/js/search-results-pagination.js' %}"></script>
{% endblock content %}
30 changes: 28 additions & 2 deletions backend/dissemination/views.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from django.core.exceptions import PermissionDenied
from django.core.exceptions import BadRequest, PermissionDenied
from django.core.paginator import Paginator
from django.http import Http404
from django.shortcuts import get_object_or_404, redirect, render
from django.views.generic import View
Expand Down Expand Up @@ -30,6 +31,7 @@ def get(self, request, *args, **kwargs):
def post(self, request, *args, **kwargs):
form = SearchForm(request.POST)
results = []
context = {}

if form.is_valid():
names = form.cleaned_data["entity_name"].splitlines()
Expand All @@ -42,6 +44,11 @@ def post(self, request, *args, **kwargs):
int(year) for year in form.cleaned_data["audit_year"]
] # Cast strings from HTML to int

# TODO: Add a limit choice field to the form
limit = form.cleaned_data["limit"] or 30
# Changed in the form via pagination links
page = form.cleaned_data["page"] or 1

results = search_general(
names,
uei_or_eins,
Expand All @@ -51,13 +58,32 @@ def post(self, request, *args, **kwargs):
agency_name,
audit_years,
)
results_count = results.count() # Total result count
paginator = Paginator(
results, per_page=limit
) # Paginator object handles results splicing, page count, and pagination buttons
results = paginator.get_page(page) # Results for a given page
results.adjusted_elided_pages = paginator.get_elided_page_range(
page, on_each_side=1
) # Pagination buttons, adjust ellipses around the current page

# Reformat these so the date-picker elements in HTML prepopulate
if form.cleaned_data["start_date"]:
form.cleaned_data["start_date"] = start_date.strftime("%Y-%m-%d")
if form.cleaned_data["end_date"]:
form.cleaned_data["end_date"] = end_date.strftime("%Y-%m-%d")
else:
raise BadRequest("Form data validation error.", form.errors)

context = context | {
"form": form,
"limit": limit,
"results": results,
"results_count": results_count,
"page": page,
}

return render(request, "search.html", {"form": form, "results": results})
return render(request, "search.html", context)


class AuditSummaryView(View):
Expand Down
38 changes: 38 additions & 0 deletions backend/static/js/search-results-pagination.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
var FORM = document.forms[1];
const pagination_links = document.querySelectorAll('[aria-label^="Page"]');
const next_page_link = document.querySelectorAll('[aria-label="Next page"]');
const previous_page_link = document.querySelectorAll(
'[aria-label="Previous page"]'
);

function attachEventHandlers() {
// If any pagination links are clicked, set the form element and submit it for a reload
pagination_links.forEach((link) => {
link.addEventListener('click', (e) => {
e.preventDefault();
FORM.elements['page'].value = link.textContent;
FORM.submit();
});
});
// If the next or previous page buttons are clicked, set the form element to be +/- 1 and submit for a reload
if (next_page_link[0]) {
next_page_link[0].addEventListener('click', (e) => {
e.preventDefault();
FORM.elements['page'].value = parseInt(FORM.elements['page'].value) + 1;
FORM.submit();
});
}
if (previous_page_link[0]) {
previous_page_link[0].addEventListener('click', (e) => {
e.preventDefault();
FORM.elements['page'].value = parseInt(FORM.elements['page'].value) - 1;
FORM.submit();
});
}
}

function init() {
attachEventHandlers();
}

window.onload = init;

0 comments on commit 724e048

Please sign in to comment.