mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-05-17 18:09:25 +02:00
Merge remote-tracking branch 'origin/main' into nl/2136-CISA-rep-additional-details
This commit is contained in:
commit
ed4c9b930b
14 changed files with 393 additions and 166 deletions
|
@ -879,6 +879,146 @@ function unloadModals() {
|
||||||
window.modal.off();
|
window.modal.off();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function that scrolls to an element
|
||||||
|
* @param {string} attributeName - The string "class" or "id"
|
||||||
|
* @param {string} attributeValue - The class or id name
|
||||||
|
*/
|
||||||
|
function ScrollToElement(attributeName, attributeValue) {
|
||||||
|
let targetEl = null;
|
||||||
|
|
||||||
|
if (attributeName === 'class') {
|
||||||
|
targetEl = document.getElementsByClassName(attributeValue)[0];
|
||||||
|
} else if (attributeName === 'id') {
|
||||||
|
targetEl = document.getElementById(attributeValue);
|
||||||
|
} else {
|
||||||
|
console.log('Error: unknown attribute name provided.');
|
||||||
|
return; // Exit the function if an invalid attributeName is provided
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetEl) {
|
||||||
|
const rect = targetEl.getBoundingClientRect();
|
||||||
|
const scrollTop = window.scrollY || document.documentElement.scrollTop;
|
||||||
|
window.scrollTo({
|
||||||
|
top: rect.top + scrollTop,
|
||||||
|
behavior: 'smooth' // Optional: for smooth scrolling
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generalized function to update pagination for a list.
|
||||||
|
* @param {string} itemName - The name displayed in the counter
|
||||||
|
* @param {string} paginationSelector - CSS selector for the pagination container.
|
||||||
|
* @param {string} counterSelector - CSS selector for the pagination counter.
|
||||||
|
* @param {string} headerAnchor - CSS selector for the header element to anchor the links to.
|
||||||
|
* @param {Function} loadPageFunction - Function to call when a page link is clicked.
|
||||||
|
* @param {number} currentPage - The current page number (starting with 1).
|
||||||
|
* @param {number} numPages - The total number of pages.
|
||||||
|
* @param {boolean} hasPrevious - Whether there is a page before the current page.
|
||||||
|
* @param {boolean} hasNext - Whether there is a page after the current page.
|
||||||
|
* @param {number} totalItems - The total number of items.
|
||||||
|
*/
|
||||||
|
function updatePagination(itemName, paginationSelector, counterSelector, headerAnchor, loadPageFunction, currentPage, numPages, hasPrevious, hasNext, totalItems) {
|
||||||
|
const paginationContainer = document.querySelector(paginationSelector);
|
||||||
|
const paginationCounter = document.querySelector(counterSelector);
|
||||||
|
const paginationButtons = document.querySelector(`${paginationSelector} .usa-pagination__list`);
|
||||||
|
paginationCounter.innerHTML = '';
|
||||||
|
paginationButtons.innerHTML = '';
|
||||||
|
|
||||||
|
// Buttons should only be displayed if there are more than one pages of results
|
||||||
|
paginationButtons.classList.toggle('display-none', numPages <= 1);
|
||||||
|
|
||||||
|
// Counter should only be displayed if there is more than 1 item
|
||||||
|
paginationContainer.classList.toggle('display-none', totalItems < 1);
|
||||||
|
|
||||||
|
paginationCounter.innerHTML = `${totalItems} ${itemName}${totalItems > 1 ? 's' : ''}`;
|
||||||
|
|
||||||
|
if (hasPrevious) {
|
||||||
|
const prevPageItem = document.createElement('li');
|
||||||
|
prevPageItem.className = 'usa-pagination__item usa-pagination__arrow';
|
||||||
|
prevPageItem.innerHTML = `
|
||||||
|
<a href="${headerAnchor}" class="usa-pagination__link usa-pagination__previous-page" aria-label="Previous page">
|
||||||
|
<svg class="usa-icon" aria-hidden="true" role="img">
|
||||||
|
<use xlink:href="/public/img/sprite.svg#navigate_before"></use>
|
||||||
|
</svg>
|
||||||
|
<span class="usa-pagination__link-text">Previous</span>
|
||||||
|
</a>
|
||||||
|
`;
|
||||||
|
prevPageItem.querySelector('a').addEventListener('click', (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
loadPageFunction(currentPage - 1);
|
||||||
|
});
|
||||||
|
paginationButtons.appendChild(prevPageItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to create a page item
|
||||||
|
function createPageItem(page) {
|
||||||
|
const pageItem = document.createElement('li');
|
||||||
|
pageItem.className = 'usa-pagination__item usa-pagination__page-no';
|
||||||
|
pageItem.innerHTML = `
|
||||||
|
<a href="${headerAnchor}" class="usa-pagination__button" aria-label="Page ${page}">${page}</a>
|
||||||
|
`;
|
||||||
|
if (page === currentPage) {
|
||||||
|
pageItem.querySelector('a').classList.add('usa-current');
|
||||||
|
pageItem.querySelector('a').setAttribute('aria-current', 'page');
|
||||||
|
}
|
||||||
|
pageItem.querySelector('a').addEventListener('click', (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
loadPageFunction(page);
|
||||||
|
});
|
||||||
|
return pageItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add first page and ellipsis if necessary
|
||||||
|
if (currentPage > 2) {
|
||||||
|
paginationButtons.appendChild(createPageItem(1));
|
||||||
|
if (currentPage > 3) {
|
||||||
|
const ellipsis = document.createElement('li');
|
||||||
|
ellipsis.className = 'usa-pagination__item usa-pagination__overflow';
|
||||||
|
ellipsis.setAttribute('aria-label', 'ellipsis indicating non-visible pages');
|
||||||
|
ellipsis.innerHTML = '<span>…</span>';
|
||||||
|
paginationButtons.appendChild(ellipsis);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add pages around the current page
|
||||||
|
for (let i = Math.max(1, currentPage - 1); i <= Math.min(numPages, currentPage + 1); i++) {
|
||||||
|
paginationButtons.appendChild(createPageItem(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add last page and ellipsis if necessary
|
||||||
|
if (currentPage < numPages - 1) {
|
||||||
|
if (currentPage < numPages - 2) {
|
||||||
|
const ellipsis = document.createElement('li');
|
||||||
|
ellipsis.className = 'usa-pagination__item usa-pagination__overflow';
|
||||||
|
ellipsis.setAttribute('aria-label', 'ellipsis indicating non-visible pages');
|
||||||
|
ellipsis.innerHTML = '<span>…</span>';
|
||||||
|
paginationButtons.appendChild(ellipsis);
|
||||||
|
}
|
||||||
|
paginationButtons.appendChild(createPageItem(numPages));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasNext) {
|
||||||
|
const nextPageItem = document.createElement('li');
|
||||||
|
nextPageItem.className = 'usa-pagination__item usa-pagination__arrow';
|
||||||
|
nextPageItem.innerHTML = `
|
||||||
|
<a href="${headerAnchor}" 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">
|
||||||
|
<use xlink:href="/public/img/sprite.svg#navigate_next"></use>
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
`;
|
||||||
|
nextPageItem.querySelector('a').addEventListener('click', (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
loadPageFunction(currentPage + 1);
|
||||||
|
});
|
||||||
|
paginationButtons.appendChild(nextPageItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An IIFE that listens for DOM Content to be loaded, then executes. This function
|
* 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.
|
* initializes the domains list and associated functionality on the home page of the app.
|
||||||
|
@ -891,6 +1031,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||||
let currentSortBy = 'id';
|
let currentSortBy = 'id';
|
||||||
let currentOrder = 'asc';
|
let currentOrder = 'asc';
|
||||||
let noDomainsWrapper = document.querySelector('.no-domains-wrapper');
|
let noDomainsWrapper = document.querySelector('.no-domains-wrapper');
|
||||||
|
let hasLoaded = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads rows in the domains list, as well as updates pagination around the domains list
|
* Loads rows in the domains list, as well as updates pagination around the domains list
|
||||||
|
@ -898,8 +1039,9 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||||
* @param {*} page - the page number of the results (starts with 1)
|
* @param {*} page - the page number of the results (starts with 1)
|
||||||
* @param {*} sortBy - the sort column option
|
* @param {*} sortBy - the sort column option
|
||||||
* @param {*} order - the sort order {asc, desc}
|
* @param {*} order - the sort order {asc, desc}
|
||||||
|
* @param {*} loaded - control for the scrollToElement functionality
|
||||||
*/
|
*/
|
||||||
function loadDomains(page, sortBy = currentSortBy, order = currentOrder) {
|
function loadDomains(page, sortBy = currentSortBy, order = currentOrder, loaded = hasLoaded) {
|
||||||
//fetch json of page of domains, given page # and sort
|
//fetch json of page of domains, given page # and sort
|
||||||
fetch(`/get-domains-json/?page=${page}&sort_by=${sortBy}&order=${order}`)
|
fetch(`/get-domains-json/?page=${page}&sort_by=${sortBy}&order=${order}`)
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
|
@ -923,9 +1065,12 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||||
domainList.innerHTML = '';
|
domainList.innerHTML = '';
|
||||||
|
|
||||||
data.domains.forEach(domain => {
|
data.domains.forEach(domain => {
|
||||||
|
const options = { year: 'numeric', month: 'short', day: 'numeric' };
|
||||||
const expirationDate = domain.expiration_date ? new Date(domain.expiration_date) : null;
|
const expirationDate = domain.expiration_date ? new Date(domain.expiration_date) : null;
|
||||||
|
const expirationDateFormatted = expirationDate ? expirationDate.toLocaleDateString('en-US', options) : null;
|
||||||
const expirationDateSortValue = expirationDate ? expirationDate.getTime() : '';
|
const expirationDateSortValue = expirationDate ? expirationDate.getTime() : '';
|
||||||
const actionUrl = domain.action_url;
|
const actionUrl = domain.action_url;
|
||||||
|
|
||||||
|
|
||||||
const row = document.createElement('tr');
|
const row = document.createElement('tr');
|
||||||
row.innerHTML = `
|
row.innerHTML = `
|
||||||
|
@ -933,7 +1078,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||||
${domain.name}
|
${domain.name}
|
||||||
</th>
|
</th>
|
||||||
<td data-sort-value="${expirationDateSortValue}" data-label="Expires">
|
<td data-sort-value="${expirationDateSortValue}" data-label="Expires">
|
||||||
${expirationDate ? expirationDate.toLocaleDateString() : ''}
|
${expirationDateFormatted}
|
||||||
</td>
|
</td>
|
||||||
<td data-label="Status">
|
<td data-label="Status">
|
||||||
${domain.state_display}
|
${domain.state_display}
|
||||||
|
@ -961,84 +1106,31 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||||
});
|
});
|
||||||
// initialize tool tips immediately after the associated DOM elements are added
|
// initialize tool tips immediately after the associated DOM elements are added
|
||||||
initializeTooltips();
|
initializeTooltips();
|
||||||
|
if (loaded)
|
||||||
|
ScrollToElement('id', 'domains-header');
|
||||||
|
|
||||||
hasLoaded = true;
|
hasLoaded = true;
|
||||||
|
|
||||||
// update pagination
|
// update pagination
|
||||||
updateDomainsPagination(data.page, data.num_pages, data.has_previous, data.has_next, data.total);
|
updatePagination(
|
||||||
|
'domain',
|
||||||
|
'#domains-pagination',
|
||||||
|
'#domains-pagination .usa-pagination__counter',
|
||||||
|
'#domains-header',
|
||||||
|
loadDomains,
|
||||||
|
data.page,
|
||||||
|
data.num_pages,
|
||||||
|
data.has_previous,
|
||||||
|
data.has_next,
|
||||||
|
data.total
|
||||||
|
);
|
||||||
currentSortBy = sortBy;
|
currentSortBy = sortBy;
|
||||||
currentOrder = order;
|
currentOrder = order;
|
||||||
})
|
})
|
||||||
.catch(error => console.error('Error fetching domains:', error));
|
.catch(error => console.error('Error fetching domains:', error));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the pagination below the domains list.
|
|
||||||
* @param {*} currentPage - the current page number (starting with 1)
|
|
||||||
* @param {*} numPages - the number of pages indicated by the domains list response
|
|
||||||
* @param {*} hasPrevious - if there is a page of results prior to the current page
|
|
||||||
* @param {*} hasNext - if there is a page of results after the current page
|
|
||||||
*/
|
|
||||||
function updateDomainsPagination(currentPage, numPages, hasPrevious, hasNext, totalItems) {
|
|
||||||
// identify the DOM element where the pagination will be inserted
|
|
||||||
const paginationContainer = document.querySelector('#domains-pagination');
|
|
||||||
const paginationCounter = document.querySelector('#domains-pagination .usa-pagination__counter');
|
|
||||||
const paginationButtons = document.querySelector('#domains-pagination .usa-pagination__list');
|
|
||||||
paginationCounter.innerHTML = '';
|
|
||||||
paginationButtons.innerHTML = '';
|
|
||||||
|
|
||||||
// Buttons should only be displayed if there are more than one pages of results
|
|
||||||
paginationButtons.classList.toggle('display-none', numPages <= 1);
|
|
||||||
|
|
||||||
// Counter should only be displayed if there is more than 1 item
|
|
||||||
paginationContainer.classList.toggle('display-none', totalItems < 1);
|
|
||||||
|
|
||||||
paginationCounter.innerHTML = `${totalItems} domain${totalItems > 1 ? 's' : ''}`;
|
|
||||||
|
|
||||||
if (hasPrevious) {
|
|
||||||
const prevPageItem = document.createElement('li');
|
|
||||||
prevPageItem.className = 'usa-pagination__item usa-pagination__arrow';
|
|
||||||
prevPageItem.innerHTML = `
|
|
||||||
<a href="#domains-header" class="usa-pagination__link usa-pagination__previous-page" aria-label="Domains previous page">
|
|
||||||
<svg class="usa-icon" aria-hidden="true" role="img">
|
|
||||||
<use xlink:href="/public/img/sprite.svg#navigate_before"></use>
|
|
||||||
</svg>
|
|
||||||
<span class="usa-pagination__link-text">Previous</span>
|
|
||||||
</a>
|
|
||||||
`;
|
|
||||||
prevPageItem.querySelector('a').addEventListener('click', () => loadDomains(currentPage - 1));
|
|
||||||
paginationButtons.appendChild(prevPageItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 1; i <= numPages; i++) {
|
|
||||||
const pageItem = document.createElement('li');
|
|
||||||
pageItem.className = 'usa-pagination__item usa-pagination__page-no';
|
|
||||||
pageItem.innerHTML = `
|
|
||||||
<a href="#domains-header" class="usa-pagination__button" aria-label="Domains page ${i}">${i}</a>
|
|
||||||
`;
|
|
||||||
if (i === currentPage) {
|
|
||||||
pageItem.querySelector('a').classList.add('usa-current');
|
|
||||||
pageItem.querySelector('a').setAttribute('aria-current', 'page');
|
|
||||||
}
|
|
||||||
pageItem.querySelector('a').addEventListener('click', () => loadDomains(i));
|
|
||||||
paginationButtons.appendChild(pageItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasNext) {
|
|
||||||
const nextPageItem = document.createElement('li');
|
|
||||||
nextPageItem.className = 'usa-pagination__item usa-pagination__arrow';
|
|
||||||
nextPageItem.innerHTML = `
|
|
||||||
<a href="#domains-header" class="usa-pagination__link usa-pagination__next-page" aria-label="Domains next page">
|
|
||||||
<span class="usa-pagination__link-text">Next</span>
|
|
||||||
<svg class="usa-icon" aria-hidden="true" role="img">
|
|
||||||
<use xlink:href="/public/img/sprite.svg#navigate_next"></use>
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
`;
|
|
||||||
nextPageItem.querySelector('a').addEventListener('click', () => loadDomains(currentPage + 1));
|
|
||||||
paginationButtons.appendChild(nextPageItem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add event listeners to table headers for sorting
|
// Add event listeners to table headers for sorting
|
||||||
document.querySelectorAll('.dotgov-table__registered-domains th[data-sortable]').forEach(header => {
|
document.querySelectorAll('.dotgov-table__registered-domains th[data-sortable]').forEach(header => {
|
||||||
|
@ -1060,6 +1152,17 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const utcDateString = (dateString) => {
|
||||||
|
const date = new Date(dateString);
|
||||||
|
const utcYear = date.getUTCFullYear();
|
||||||
|
const utcMonth = date.toLocaleString('en-US', { month: 'short', timeZone: 'UTC' });
|
||||||
|
const utcDay = date.getUTCDate().toString().padStart(2, '0');
|
||||||
|
const utcHours = date.getUTCHours().toString().padStart(2, '0');
|
||||||
|
const utcMinutes = date.getUTCMinutes().toString().padStart(2, '0');
|
||||||
|
|
||||||
|
return `${utcMonth} ${utcDay}, ${utcYear}, ${utcHours}:${utcMinutes} UTC`;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An IIFE that listens for DOM Content to be loaded, then executes. This function
|
* An IIFE that listens for DOM Content to be loaded, then executes. This function
|
||||||
* initializes the domain requests list and associated functionality on the home page of the app.
|
* initializes the domain requests list and associated functionality on the home page of the app.
|
||||||
|
@ -1072,6 +1175,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||||
let currentSortBy = 'id';
|
let currentSortBy = 'id';
|
||||||
let currentOrder = 'asc';
|
let currentOrder = 'asc';
|
||||||
let noDomainRequestsWrapper = document.querySelector('.no-domain-requests-wrapper');
|
let noDomainRequestsWrapper = document.querySelector('.no-domain-requests-wrapper');
|
||||||
|
let hasLoaded = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads rows in the domain requests list, as well as updates pagination around the domain requests list
|
* Loads rows in the domain requests list, as well as updates pagination around the domain requests list
|
||||||
|
@ -1079,8 +1183,9 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||||
* @param {*} page - the page number of the results (starts with 1)
|
* @param {*} page - the page number of the results (starts with 1)
|
||||||
* @param {*} sortBy - the sort column option
|
* @param {*} sortBy - the sort column option
|
||||||
* @param {*} order - the sort order {asc, desc}
|
* @param {*} order - the sort order {asc, desc}
|
||||||
|
* @param {*} loaded - control for the scrollToElement functionality
|
||||||
*/
|
*/
|
||||||
function loadDomainRequests(page, sortBy = currentSortBy, order = currentOrder) {
|
function loadDomainRequests(page, sortBy = currentSortBy, order = currentOrder, loaded = hasLoaded) {
|
||||||
//fetch json of page of domain requests, given page # and sort
|
//fetch json of page of domain requests, given page # and sort
|
||||||
fetch(`/get-domain-requests-json/?page=${page}&sort_by=${sortBy}&order=${order}`)
|
fetch(`/get-domain-requests-json/?page=${page}&sort_by=${sortBy}&order=${order}`)
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
|
@ -1107,10 +1212,10 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||||
// after the DOM content changes and there are new delete modal buttons added
|
// after the DOM content changes and there are new delete modal buttons added
|
||||||
unloadModals();
|
unloadModals();
|
||||||
data.domain_requests.forEach(request => {
|
data.domain_requests.forEach(request => {
|
||||||
const domainName = request.requested_domain ? request.requested_domain : `New domain request <span class="text-base font-body-xs">(${new Date(request.created_at).toLocaleString()} UTC)</span>`;
|
const options = { year: 'numeric', month: 'short', day: 'numeric' };
|
||||||
|
const domainName = request.requested_domain ? request.requested_domain : `New domain request <br><span class="text-base font-body-xs">(${utcDateString(request.created_at)})</span>`;
|
||||||
const actionUrl = request.action_url;
|
const actionUrl = request.action_url;
|
||||||
const actionLabel = request.action_label;
|
const actionLabel = request.action_label;
|
||||||
const options = { year: 'numeric', month: 'short', day: 'numeric' };
|
|
||||||
const submissionDate = request.submission_date ? new Date(request.submission_date).toLocaleDateString('en-US', options) : `<span class="text-base">Not submitted</span>`;
|
const submissionDate = request.submission_date ? new Date(request.submission_date).toLocaleDateString('en-US', options) : `<span class="text-base">Not submitted</span>`;
|
||||||
const deleteButton = request.is_deletable ? `
|
const deleteButton = request.is_deletable ? `
|
||||||
<a
|
<a
|
||||||
|
@ -1151,85 +1256,30 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||||
});
|
});
|
||||||
// initialize modals immediately after the DOM content is updated
|
// initialize modals immediately after the DOM content is updated
|
||||||
initializeModals();
|
initializeModals();
|
||||||
|
if (loaded)
|
||||||
|
ScrollToElement('id', 'domain-requests-header');
|
||||||
|
|
||||||
hasLoaded = true;
|
hasLoaded = true;
|
||||||
|
|
||||||
// update the pagination after the domain requests list is updated
|
// update the pagination after the domain requests list is updated
|
||||||
updateDomainRequestsPagination(data.page, data.num_pages, data.has_previous, data.has_next, data.total);
|
updatePagination(
|
||||||
|
'domain request',
|
||||||
|
'#domain-requests-pagination',
|
||||||
|
'#domain-requests-pagination .usa-pagination__counter',
|
||||||
|
'#domain-requests-header',
|
||||||
|
loadDomainRequests,
|
||||||
|
data.page,
|
||||||
|
data.num_pages,
|
||||||
|
data.has_previous,
|
||||||
|
data.has_next,
|
||||||
|
data.total
|
||||||
|
);
|
||||||
currentSortBy = sortBy;
|
currentSortBy = sortBy;
|
||||||
currentOrder = order;
|
currentOrder = order;
|
||||||
})
|
})
|
||||||
.catch(error => console.error('Error fetching domain requests:', error));
|
.catch(error => console.error('Error fetching domain requests:', error));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the pagination below the domain requests list.
|
|
||||||
* @param {*} currentPage - the current page number (starting with 1)
|
|
||||||
* @param {*} numPages - the number of pages indicated by the domain request list response
|
|
||||||
* @param {*} hasPrevious - if there is a page of results prior to the current page
|
|
||||||
* @param {*} hasNext - if there is a page of results after the current page
|
|
||||||
*/
|
|
||||||
function updateDomainRequestsPagination(currentPage, numPages, hasPrevious, hasNext, totalItems) {
|
|
||||||
// identify the DOM element where pagination is contained
|
|
||||||
const paginationContainer = document.querySelector('#domain-requests-pagination');
|
|
||||||
const paginationCounter = document.querySelector('#domain-requests-pagination .usa-pagination__counter');
|
|
||||||
const paginationButtons = document.querySelector('#domain-requests-pagination .usa-pagination__list');
|
|
||||||
paginationCounter.innerHTML = '';
|
|
||||||
paginationButtons.innerHTML = '';
|
|
||||||
|
|
||||||
// Buttons should only be displayed if there are more than one pages of results
|
|
||||||
paginationButtons.classList.toggle('display-none', numPages <= 1);
|
|
||||||
|
|
||||||
// Counter should only be displayed if there is more than 1 item
|
|
||||||
paginationContainer.classList.toggle('display-none', totalItems < 1);
|
|
||||||
|
|
||||||
paginationCounter.innerHTML = `${totalItems} domain request${totalItems > 1 ? 's' : ''}`;
|
|
||||||
|
|
||||||
if (hasPrevious) {
|
|
||||||
const prevPageItem = document.createElement('li');
|
|
||||||
prevPageItem.className = 'usa-pagination__item usa-pagination__arrow';
|
|
||||||
prevPageItem.innerHTML = `
|
|
||||||
<a href="#domain-requests-header" class="usa-pagination__link usa-pagination__previous-page" aria-label="Domain requests previous page">
|
|
||||||
<svg class="usa-icon" aria-hidden="true" role="img">
|
|
||||||
<use xlink:href="/public/img/sprite.svg#navigate_before"></use>
|
|
||||||
</svg>
|
|
||||||
<span class="usa-pagination__link-text">Previous</span>
|
|
||||||
</a>
|
|
||||||
`;
|
|
||||||
prevPageItem.querySelector('a').addEventListener('click', () => loadDomainRequests(currentPage - 1));
|
|
||||||
paginationButtons.appendChild(prevPageItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 1; i <= numPages; i++) {
|
|
||||||
const pageItem = document.createElement('li');
|
|
||||||
pageItem.className = 'usa-pagination__item usa-pagination__page-no';
|
|
||||||
pageItem.innerHTML = `
|
|
||||||
<a href="#domain-requests-header" class="usa-pagination__button" aria-label="Domain requests page ${i}">${i}</a>
|
|
||||||
`;
|
|
||||||
if (i === currentPage) {
|
|
||||||
pageItem.querySelector('a').classList.add('usa-current');
|
|
||||||
pageItem.querySelector('a').setAttribute('aria-current', 'page');
|
|
||||||
}
|
|
||||||
pageItem.querySelector('a').addEventListener('click', () => loadDomainRequests(i));
|
|
||||||
paginationButtons.appendChild(pageItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasNext) {
|
|
||||||
const nextPageItem = document.createElement('li');
|
|
||||||
nextPageItem.className = 'usa-pagination__item usa-pagination__arrow';
|
|
||||||
nextPageItem.innerHTML = `
|
|
||||||
<a href="#domain-requests-header" class="usa-pagination__link usa-pagination__next-page" aria-label="Domain requests next page">
|
|
||||||
<span class="usa-pagination__link-text">Next</span>
|
|
||||||
<svg class="usa-icon" aria-hidden="true" role="img">
|
|
||||||
<use xlink:href="/public/img/sprite.svg#navigate_next"></use>
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
`;
|
|
||||||
nextPageItem.querySelector('a').addEventListener('click', () => loadDomainRequests(currentPage + 1));
|
|
||||||
paginationButtons.appendChild(nextPageItem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add event listeners to table headers for sorting
|
// Add event listeners to table headers for sorting
|
||||||
document.querySelectorAll('.dotgov-table__domain-requests th[data-sortable]').forEach(header => {
|
document.querySelectorAll('.dotgov-table__domain-requests th[data-sortable]').forEach(header => {
|
||||||
header.addEventListener('click', function() {
|
header.addEventListener('click', function() {
|
||||||
|
|
15
src/registrar/assets/sass/_theme/_pagination.scss
Normal file
15
src/registrar/assets/sass/_theme/_pagination.scss
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
@use "uswds-core" as *;
|
||||||
|
|
||||||
|
.usa-pagination {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
background-color: transparent;
|
||||||
|
.usa-current {
|
||||||
|
background-color: color('base-dark');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@include at-media(desktop) {
|
||||||
|
.usa-pagination {
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,6 +13,7 @@
|
||||||
@forward "links";
|
@forward "links";
|
||||||
@forward "lists";
|
@forward "lists";
|
||||||
@forward "buttons";
|
@forward "buttons";
|
||||||
|
@forward "pagination";
|
||||||
@forward "forms";
|
@forward "forms";
|
||||||
@forward "tooltips";
|
@forward "tooltips";
|
||||||
@forward "fieldsets";
|
@forward "fieldsets";
|
||||||
|
|
|
@ -0,0 +1,131 @@
|
||||||
|
# Generated by Django 4.2.10 on 2024-05-28 14:40
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import phonenumber_field.modelfields
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("registrar", "0095_user_middle_name_user_title"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="contact",
|
||||||
|
name="email",
|
||||||
|
field=models.EmailField(blank=True, max_length=320, null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="contact",
|
||||||
|
name="first_name",
|
||||||
|
field=models.CharField(blank=True, null=True, verbose_name="first name"),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="contact",
|
||||||
|
name="last_name",
|
||||||
|
field=models.CharField(blank=True, null=True, verbose_name="last name"),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="contact",
|
||||||
|
name="phone",
|
||||||
|
field=phonenumber_field.modelfields.PhoneNumberField(blank=True, max_length=128, null=True, region=None),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="domaininformation",
|
||||||
|
name="organization_name",
|
||||||
|
field=models.CharField(blank=True, null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="domaininformation",
|
||||||
|
name="zipcode",
|
||||||
|
field=models.CharField(blank=True, max_length=10, null=True, verbose_name="zip code"),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="domainrequest",
|
||||||
|
name="organization_name",
|
||||||
|
field=models.CharField(blank=True, null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="domainrequest",
|
||||||
|
name="zipcode",
|
||||||
|
field=models.CharField(blank=True, max_length=10, null=True, verbose_name="zip code"),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="transitiondomain",
|
||||||
|
name="first_name",
|
||||||
|
field=models.CharField(
|
||||||
|
blank=True, help_text="First name / given name", null=True, verbose_name="first name"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="transitiondomain",
|
||||||
|
name="organization_name",
|
||||||
|
field=models.CharField(blank=True, help_text="Organization name", null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="transitiondomain",
|
||||||
|
name="zipcode",
|
||||||
|
field=models.CharField(blank=True, help_text="Zip code", max_length=10, null=True, verbose_name="zip code"),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="user",
|
||||||
|
name="phone",
|
||||||
|
field=phonenumber_field.modelfields.PhoneNumberField(
|
||||||
|
blank=True, help_text="Phone", max_length=128, null=True, region=None
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="verifiedbystaff",
|
||||||
|
name="email",
|
||||||
|
field=models.EmailField(max_length=254),
|
||||||
|
),
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name="contact",
|
||||||
|
index=models.Index(fields=["user"], name="registrar_c_user_id_4059c4_idx"),
|
||||||
|
),
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name="contact",
|
||||||
|
index=models.Index(fields=["email"], name="registrar_c_email_bde2de_idx"),
|
||||||
|
),
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name="domain",
|
||||||
|
index=models.Index(fields=["name"], name="registrar_d_name_5b1956_idx"),
|
||||||
|
),
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name="domain",
|
||||||
|
index=models.Index(fields=["state"], name="registrar_d_state_84c134_idx"),
|
||||||
|
),
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name="domaininformation",
|
||||||
|
index=models.Index(fields=["domain"], name="registrar_d_domain__88838a_idx"),
|
||||||
|
),
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name="domaininformation",
|
||||||
|
index=models.Index(fields=["domain_request"], name="registrar_d_domain__d1fba8_idx"),
|
||||||
|
),
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name="domaininvitation",
|
||||||
|
index=models.Index(fields=["status"], name="registrar_d_status_e84571_idx"),
|
||||||
|
),
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name="domainrequest",
|
||||||
|
index=models.Index(fields=["requested_domain"], name="registrar_d_request_6894eb_idx"),
|
||||||
|
),
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name="domainrequest",
|
||||||
|
index=models.Index(fields=["approved_domain"], name="registrar_d_approve_ac4c46_idx"),
|
||||||
|
),
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name="domainrequest",
|
||||||
|
index=models.Index(fields=["status"], name="registrar_d_status_a32b59_idx"),
|
||||||
|
),
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name="user",
|
||||||
|
index=models.Index(fields=["username"], name="registrar_u_usernam_964b1b_idx"),
|
||||||
|
),
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name="user",
|
||||||
|
index=models.Index(fields=["email"], name="registrar_u_email_c8f2c4_idx"),
|
||||||
|
),
|
||||||
|
]
|
|
@ -17,6 +17,14 @@ class Contact(TimeStampedModel):
|
||||||
will be updated if any updates are made to it through Login.gov.
|
will be updated if any updates are made to it through Login.gov.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Contains meta information about this class"""
|
||||||
|
|
||||||
|
indexes = [
|
||||||
|
models.Index(fields=["user"]),
|
||||||
|
models.Index(fields=["email"]),
|
||||||
|
]
|
||||||
|
|
||||||
user = models.OneToOneField(
|
user = models.OneToOneField(
|
||||||
"registrar.User",
|
"registrar.User",
|
||||||
null=True,
|
null=True,
|
||||||
|
@ -28,7 +36,6 @@ class Contact(TimeStampedModel):
|
||||||
null=True,
|
null=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
verbose_name="first name",
|
verbose_name="first name",
|
||||||
db_index=True,
|
|
||||||
)
|
)
|
||||||
middle_name = models.CharField(
|
middle_name = models.CharField(
|
||||||
null=True,
|
null=True,
|
||||||
|
@ -38,7 +45,6 @@ class Contact(TimeStampedModel):
|
||||||
null=True,
|
null=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
verbose_name="last name",
|
verbose_name="last name",
|
||||||
db_index=True,
|
|
||||||
)
|
)
|
||||||
title = models.CharField(
|
title = models.CharField(
|
||||||
null=True,
|
null=True,
|
||||||
|
@ -48,13 +54,11 @@ class Contact(TimeStampedModel):
|
||||||
email = models.EmailField(
|
email = models.EmailField(
|
||||||
null=True,
|
null=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
db_index=True,
|
|
||||||
max_length=320,
|
max_length=320,
|
||||||
)
|
)
|
||||||
phone = PhoneNumberField(
|
phone = PhoneNumberField(
|
||||||
null=True,
|
null=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
db_index=True,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def _get_all_relations(self):
|
def _get_all_relations(self):
|
||||||
|
|
|
@ -65,6 +65,14 @@ class Domain(TimeStampedModel, DomainHelper):
|
||||||
domain meets the required checks.
|
domain meets the required checks.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Contains meta information about this class"""
|
||||||
|
|
||||||
|
indexes = [
|
||||||
|
models.Index(fields=["name"]),
|
||||||
|
models.Index(fields=["state"]),
|
||||||
|
]
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self._cache = {}
|
self._cache = {}
|
||||||
super(Domain, self).__init__(*args, **kwargs)
|
super(Domain, self).__init__(*args, **kwargs)
|
||||||
|
|
|
@ -22,6 +22,16 @@ class DomainInformation(TimeStampedModel):
|
||||||
the domain request once approved, so copying them that way we can make changes
|
the domain request once approved, so copying them that way we can make changes
|
||||||
after its approved. Most fields here are copied from DomainRequest."""
|
after its approved. Most fields here are copied from DomainRequest."""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Contains meta information about this class"""
|
||||||
|
|
||||||
|
indexes = [
|
||||||
|
models.Index(fields=["domain"]),
|
||||||
|
models.Index(fields=["domain_request"]),
|
||||||
|
]
|
||||||
|
|
||||||
|
verbose_name_plural = "Domain information"
|
||||||
|
|
||||||
StateTerritoryChoices = DomainRequest.StateTerritoryChoices
|
StateTerritoryChoices = DomainRequest.StateTerritoryChoices
|
||||||
|
|
||||||
# use the short names in Django admin
|
# use the short names in Django admin
|
||||||
|
@ -111,7 +121,6 @@ class DomainInformation(TimeStampedModel):
|
||||||
organization_name = models.CharField(
|
organization_name = models.CharField(
|
||||||
null=True,
|
null=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
db_index=True,
|
|
||||||
)
|
)
|
||||||
address_line1 = models.CharField(
|
address_line1 = models.CharField(
|
||||||
null=True,
|
null=True,
|
||||||
|
@ -138,7 +147,6 @@ class DomainInformation(TimeStampedModel):
|
||||||
max_length=10,
|
max_length=10,
|
||||||
null=True,
|
null=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
db_index=True,
|
|
||||||
verbose_name="zip code",
|
verbose_name="zip code",
|
||||||
)
|
)
|
||||||
urbanization = models.CharField(
|
urbanization = models.CharField(
|
||||||
|
@ -350,6 +358,3 @@ class DomainInformation(TimeStampedModel):
|
||||||
def _get_many_to_many_fields():
|
def _get_many_to_many_fields():
|
||||||
"""Returns a set of each field.name that has the many to many relation"""
|
"""Returns a set of each field.name that has the many to many relation"""
|
||||||
return {field.name for field in DomainInformation._meta.many_to_many} # type: ignore
|
return {field.name for field in DomainInformation._meta.many_to_many} # type: ignore
|
||||||
|
|
||||||
class Meta:
|
|
||||||
verbose_name_plural = "Domain information"
|
|
||||||
|
|
|
@ -15,6 +15,13 @@ logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class DomainInvitation(TimeStampedModel):
|
class DomainInvitation(TimeStampedModel):
|
||||||
|
class Meta:
|
||||||
|
"""Contains meta information about this class"""
|
||||||
|
|
||||||
|
indexes = [
|
||||||
|
models.Index(fields=["status"]),
|
||||||
|
]
|
||||||
|
|
||||||
# Constants for status field
|
# Constants for status field
|
||||||
class DomainInvitationStatus(models.TextChoices):
|
class DomainInvitationStatus(models.TextChoices):
|
||||||
INVITED = "invited", "Invited"
|
INVITED = "invited", "Invited"
|
||||||
|
|
|
@ -25,6 +25,15 @@ logger = logging.getLogger(__name__)
|
||||||
class DomainRequest(TimeStampedModel):
|
class DomainRequest(TimeStampedModel):
|
||||||
"""A registrant's domain request for a new domain."""
|
"""A registrant's domain request for a new domain."""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Contains meta information about this class"""
|
||||||
|
|
||||||
|
indexes = [
|
||||||
|
models.Index(fields=["requested_domain"]),
|
||||||
|
models.Index(fields=["approved_domain"]),
|
||||||
|
models.Index(fields=["status"]),
|
||||||
|
]
|
||||||
|
|
||||||
# https://django-auditlog.readthedocs.io/en/latest/usage.html#object-history
|
# https://django-auditlog.readthedocs.io/en/latest/usage.html#object-history
|
||||||
# If we note any performace degradation due to this addition,
|
# If we note any performace degradation due to this addition,
|
||||||
# we can query the auditlogs table in admin.py and add the results to
|
# we can query the auditlogs table in admin.py and add the results to
|
||||||
|
@ -331,7 +340,6 @@ class DomainRequest(TimeStampedModel):
|
||||||
organization_name = models.CharField(
|
organization_name = models.CharField(
|
||||||
null=True,
|
null=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
db_index=True,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
address_line1 = models.CharField(
|
address_line1 = models.CharField(
|
||||||
|
@ -360,7 +368,6 @@ class DomainRequest(TimeStampedModel):
|
||||||
null=True,
|
null=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
verbose_name="zip code",
|
verbose_name="zip code",
|
||||||
db_index=True,
|
|
||||||
)
|
)
|
||||||
urbanization = models.CharField(
|
urbanization = models.CharField(
|
||||||
null=True,
|
null=True,
|
||||||
|
|
|
@ -59,7 +59,6 @@ class TransitionDomain(TimeStampedModel):
|
||||||
null=True,
|
null=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
help_text="Organization name",
|
help_text="Organization name",
|
||||||
db_index=True,
|
|
||||||
)
|
)
|
||||||
federal_type = models.CharField(
|
federal_type = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
|
@ -85,7 +84,6 @@ class TransitionDomain(TimeStampedModel):
|
||||||
blank=True,
|
blank=True,
|
||||||
help_text="First name / given name",
|
help_text="First name / given name",
|
||||||
verbose_name="first name",
|
verbose_name="first name",
|
||||||
db_index=True,
|
|
||||||
)
|
)
|
||||||
middle_name = models.CharField(
|
middle_name = models.CharField(
|
||||||
null=True,
|
null=True,
|
||||||
|
@ -136,7 +134,6 @@ class TransitionDomain(TimeStampedModel):
|
||||||
blank=True,
|
blank=True,
|
||||||
verbose_name="zip code",
|
verbose_name="zip code",
|
||||||
help_text="Zip code",
|
help_text="Zip code",
|
||||||
db_index=True,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
|
|
@ -31,6 +31,17 @@ class User(AbstractUser):
|
||||||
will be updated if any updates are made to it through Login.gov.
|
will be updated if any updates are made to it through Login.gov.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
indexes = [
|
||||||
|
models.Index(fields=["username"]),
|
||||||
|
models.Index(fields=["email"]),
|
||||||
|
]
|
||||||
|
|
||||||
|
permissions = [
|
||||||
|
("analyst_access_permission", "Analyst Access Permission"),
|
||||||
|
("full_access_permission", "Full Access Permission"),
|
||||||
|
]
|
||||||
|
|
||||||
class VerificationTypeChoices(models.TextChoices):
|
class VerificationTypeChoices(models.TextChoices):
|
||||||
"""
|
"""
|
||||||
Users achieve access to our system in a few different ways.
|
Users achieve access to our system in a few different ways.
|
||||||
|
@ -77,7 +88,6 @@ class User(AbstractUser):
|
||||||
null=True,
|
null=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
help_text="Phone",
|
help_text="Phone",
|
||||||
db_index=True,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
middle_name = models.CharField(
|
middle_name = models.CharField(
|
||||||
|
@ -281,9 +291,3 @@ class User(AbstractUser):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.check_domain_invitations_on_login()
|
self.check_domain_invitations_on_login()
|
||||||
|
|
||||||
class Meta:
|
|
||||||
permissions = [
|
|
||||||
("analyst_access_permission", "Analyst Access Permission"),
|
|
||||||
("full_access_permission", "Full Access Permission"),
|
|
||||||
]
|
|
||||||
|
|
|
@ -9,7 +9,6 @@ class VerifiedByStaff(TimeStampedModel):
|
||||||
email = models.EmailField(
|
email = models.EmailField(
|
||||||
null=False,
|
null=False,
|
||||||
blank=False,
|
blank=False,
|
||||||
db_index=True,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
requestor = models.ForeignKey(
|
requestor = models.ForeignKey(
|
||||||
|
|
|
@ -63,7 +63,7 @@
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<nav aria-label="Pagination" class="usa-pagination flex-justify" id="domains-pagination">
|
<nav aria-label="Pagination" class="usa-pagination flex-justify" id="domains-pagination">
|
||||||
<span class="usa-pagination__counter text-base-dark padding-left-2">
|
<span class="usa-pagination__counter text-base-dark padding-left-2 margin-bottom-1">
|
||||||
<!-- Count will be dynamically populated by JS -->
|
<!-- Count will be dynamically populated by JS -->
|
||||||
</span>
|
</span>
|
||||||
<ul class="usa-pagination__list">
|
<ul class="usa-pagination__list">
|
||||||
|
@ -134,7 +134,7 @@
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<nav aria-label="Pagination" class="usa-pagination flex-justify" id="domain-requests-pagination">
|
<nav aria-label="Pagination" class="usa-pagination flex-justify" id="domain-requests-pagination">
|
||||||
<span class="usa-pagination__counter text-base-dark padding-left-2">
|
<span class="usa-pagination__counter text-base-dark padding-left-2 margin-bottom-1">
|
||||||
<!-- Count will be dynamically populated by JS -->
|
<!-- Count will be dynamically populated by JS -->
|
||||||
</span>
|
</span>
|
||||||
<ul class="usa-pagination__list">
|
<ul class="usa-pagination__list">
|
||||||
|
|
|
@ -455,7 +455,6 @@ def export_data_full_to_csv(csv_file):
|
||||||
|
|
||||||
def export_data_federal_to_csv(csv_file):
|
def export_data_federal_to_csv(csv_file):
|
||||||
"""Federal domains report"""
|
"""Federal domains report"""
|
||||||
|
|
||||||
writer = csv.writer(csv_file)
|
writer = csv.writer(csv_file)
|
||||||
# define columns to include in export
|
# define columns to include in export
|
||||||
columns = [
|
columns = [
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue