mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-08-16 06:24:12 +02:00
refactor domain requests
This commit is contained in:
parent
bfb942a43d
commit
0126d6e81f
2 changed files with 424 additions and 568 deletions
|
@ -33,6 +33,14 @@ const showElement = (element) => {
|
|||
element.classList.remove('display-none');
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper function to get the CSRF token from the cookie
|
||||
*
|
||||
*/
|
||||
function getCsrfToken() {
|
||||
return document.querySelector('input[name="csrfmiddlewaretoken"]').value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function that scrolls to an element
|
||||
* @param {string} attributeName - The string "class" or "id"
|
||||
|
@ -995,28 +1003,28 @@ function unloadModals() {
|
|||
}
|
||||
|
||||
class LoadTableBase {
|
||||
constructor(tableSelector, tableWrapper, searchFieldId, searchSubmitId, resetSearchBtn, resetFiltersBtn, noDataDisplay, noSearchresultsDisplay) {
|
||||
this.domainsWrapper = document.querySelector(tableWrapper);
|
||||
constructor(tableSelector, tableWrapperSelector, searchFieldId, searchSubmitId, resetSearchBtn, resetFiltersBtn, noDataDisplay, noSearchresultsDisplay) {
|
||||
this.tableWrapper = document.querySelector(tableWrapperSelector);
|
||||
this.tableHeaders = document.querySelectorAll(`${tableSelector} th[data-sortable]`);
|
||||
this.currentSortBy = 'id';
|
||||
this.currentOrder = 'asc';
|
||||
this.currentStatus = [];
|
||||
this.currentSearchTerm = '';
|
||||
this.scrollToTable = false;
|
||||
this.domainsSearchInput = document.querySelector(searchFieldId);
|
||||
this.domainsSearchSubmit = document.querySelector(searchSubmitId);
|
||||
this.tableAnnouncementRegion = document.querySelector(`${tableWrapper} .usa-table__announcement-region`);
|
||||
this.searchInput = document.querySelector(searchFieldId);
|
||||
this.searchSubmit = document.querySelector(searchSubmitId);
|
||||
this.tableAnnouncementRegion = document.querySelector(`${tableWrapperSelector} .usa-table__announcement-region`);
|
||||
this.resetSearchButton = document.querySelector(resetSearchBtn);
|
||||
this.resetFiltersButton = document.querySelector(resetFiltersBtn);
|
||||
// NOTE: these 3 can't be used if filters are active on a page with more than 1 table
|
||||
this.statusCheckboxes = document.querySelectorAll('input[name="filter-status"]');
|
||||
this.statusIndicator = document.querySelector('.filter-indicator');
|
||||
this.statusToggle = document.querySelector('.usa-button--filter');
|
||||
this.noDomainsWrapper = document.querySelector(noDataDisplay);
|
||||
this.noTableWrapper = document.querySelector(noDataDisplay);
|
||||
this.noSearchResultsWrapper = document.querySelector(noSearchresultsDisplay);
|
||||
this.portfolioElement = document.getElementById('portfolio-js-value');
|
||||
this.portfolioValue = this.portfolioElement ? this.portfolioElement.getAttribute('data-portfolio') : null;
|
||||
this.initializetableHeaders();
|
||||
this.initializeTableHeaders();
|
||||
this.initializeSearchHandler();
|
||||
this.initializeStatusToggleHandler();
|
||||
this.initializeFilterCheckboxes();
|
||||
|
@ -1188,7 +1196,7 @@ class LoadTableBase {
|
|||
}
|
||||
|
||||
// Add event listeners to table headers for sorting
|
||||
initializetableHeaders() {
|
||||
initializeTableHeaders() {
|
||||
this.tableHeaders.forEach(header => {
|
||||
header.addEventListener('click', () => {
|
||||
const sortBy = header.getAttribute('data-sortable');
|
||||
|
@ -1205,9 +1213,9 @@ class LoadTableBase {
|
|||
}
|
||||
|
||||
initializeSearchHandler() {
|
||||
this.domainsSearchSubmit.addEventListener('click', (e) => {
|
||||
this.searchSubmit.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
this.currentSearchTerm = this.domainsSearchInput.value;
|
||||
this.currentSearchTerm = this.searchInput.value;
|
||||
// If the search is blank, we match the resetSearch functionality
|
||||
if (this.currentSearchTerm) {
|
||||
showElement(this.resetSearchButton);
|
||||
|
@ -1272,7 +1280,7 @@ class LoadTableBase {
|
|||
}
|
||||
|
||||
resetSearch() {
|
||||
this.domainsSearchInput.value = '';
|
||||
this.searchInput.value = '';
|
||||
this.currentSearchTerm = '';
|
||||
hideElement(this.resetSearchButton);
|
||||
this.loadTable(1, 'id', 'asc');
|
||||
|
@ -1404,7 +1412,7 @@ class DomainsTable extends LoadTableBase {
|
|||
}
|
||||
|
||||
// handle the display of proper messaging in the event that no domains exist in the list or search returns no results
|
||||
this.updateDisplay(data, this.domainsWrapper, this.noDomainsWrapper, this.noSearchResultsWrapper, this.currentSearchTerm);
|
||||
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 domainList = document.querySelector('.domains__table tbody');
|
||||
|
@ -1491,114 +1499,25 @@ class DomainsTable extends LoadTableBase {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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 domainsTable = new DomainsTable();
|
||||
class DomainRequestsTable extends LoadTableBase {
|
||||
|
||||
constructor() {
|
||||
super('.domain-requests__table', '.domain-requests__table-wrapper', '#domain-requests__search-field', '#domain-requests__search-field-submit', '.domain-requests__reset-search', '.domain-requests__reset-filters', '.domain-requests__no-data', '.domain-requests__no-search-results');
|
||||
this.domainRequestPk = 1
|
||||
|
||||
if (domainsTable.domainsWrapper) {
|
||||
|
||||
// Initial load
|
||||
domainsTable.loadTable(1);
|
||||
}
|
||||
});
|
||||
|
||||
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');
|
||||
let utcHours = date.getUTCHours();
|
||||
const utcMinutes = date.getUTCMinutes().toString().padStart(2, '0');
|
||||
|
||||
const ampm = utcHours >= 12 ? 'PM' : 'AM';
|
||||
utcHours = utcHours % 12 || 12; // Convert to 12-hour format, '0' hours should be '12'
|
||||
|
||||
return `${utcMonth} ${utcDay}, ${utcYear}, ${utcHours}:${utcMinutes} ${ampm} UTC`;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const domainRequestsSectionWrapper = document.querySelector('.domain-requests');
|
||||
const domainRequestsWrapper = document.querySelector('.domain-requests__table-wrapper');
|
||||
|
||||
if (domainRequestsWrapper) {
|
||||
let currentSortBy = 'id';
|
||||
let currentOrder = 'asc';
|
||||
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 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;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @param {*} domainRequestPk - the identifier for the request that we're deleting
|
||||
* @param {*} pageToDisplay - If we're deleting the last item on a page that is not page 1, we'll need to display the previous page
|
||||
*/
|
||||
function deleteDomainRequest(domainRequestPk,pageToDisplay) {
|
||||
|
||||
// Use to debug uswds modal issues
|
||||
//console.log('deleteDomainRequest')
|
||||
|
||||
// Get csrf token
|
||||
const csrfToken = getCsrfToken();
|
||||
// Create FormData object and append the CSRF token
|
||||
const formData = `csrfmiddlewaretoken=${encodeURIComponent(csrfToken)}&delete-domain-request=`;
|
||||
|
||||
fetch(`/domain-request/${domainRequestPk}/delete`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'X-CSRFToken': csrfToken,
|
||||
},
|
||||
body: formData
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
// Update data and UI
|
||||
loadDomainRequests(pageToDisplay, currentSortBy, currentOrder, scrollToTable, currentSearchTerm);
|
||||
})
|
||||
.catch(error => console.error('Error fetching domain requests:', error));
|
||||
}
|
||||
|
||||
// Helper function to get the CSRF token from the cookie
|
||||
function getCsrfToken() {
|
||||
return document.querySelector('input[name="csrfmiddlewaretoken"]').value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads rows in the domain requests list, as well as updates pagination around the domain requests list
|
||||
* Loads rows in the domains list, as well as updates pagination around the domains 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
|
||||
*/
|
||||
function loadDomainRequests(page, sortBy = currentSortBy, order = currentOrder, scroll = scrollToTable, searchTerm = currentSearchTerm, status = currentStatus, portfolio = portfolioValue) {
|
||||
// fetch json of page of domain requests, given params
|
||||
loadTable(page, sortBy = this.currentSortBy, order = this.currentOrder, scroll = this.scrollToTable, status = this.currentStatus, searchTerm = this.currentSearchTerm, portfolio = this.portfolioValue) {
|
||||
let baseUrl = document.getElementById("get_domain_requests_json_url");
|
||||
if (!baseUrl) {
|
||||
return;
|
||||
|
@ -1632,7 +1551,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||
}
|
||||
|
||||
// handle the display of proper messaging in the event that no requests exist in the list or search returns no results
|
||||
updateDisplay(data, domainRequestsWrapper, noDomainRequestsWrapper, noSearchResultsWrapper, currentSearchTerm);
|
||||
this.updateDisplay(data, this.tableWrapper, this.noTableWrapper, this.noSearchResultsWrapper, this.currentSearchTerm);
|
||||
|
||||
// identify the DOM element where the domain request list will be inserted into the DOM
|
||||
const tbody = document.querySelector('.domain-requests__table tbody');
|
||||
|
@ -1685,7 +1604,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||
|
||||
let markupCreatorRow = '';
|
||||
|
||||
if (portfolioValue) {
|
||||
if (this.portfolioValue) {
|
||||
markupCreatorRow = `
|
||||
<td>
|
||||
<span class="text-wrap break-word">${request.creator ? request.creator : ''}</span>
|
||||
|
@ -1780,10 +1699,10 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||
</div>
|
||||
`
|
||||
|
||||
domainRequestsSectionWrapper.appendChild(modal);
|
||||
this.tableWrapper.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) {
|
||||
if (this.portfolioValue) {
|
||||
modalTrigger = `
|
||||
<a
|
||||
role="button"
|
||||
|
@ -1866,8 +1785,8 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||
modals.forEach(modal => {
|
||||
const submitButton = modal.querySelector('.usa-modal__submit');
|
||||
const closeButton = modal.querySelector('.usa-modal__close');
|
||||
submitButton.addEventListener('click', function() {
|
||||
pk = submitButton.getAttribute('data-pk');
|
||||
submitButton.addEventListener('click', () => {
|
||||
let pk = submitButton.getAttribute('data-pk');
|
||||
// Close the modal to remove the USWDS UI local classes
|
||||
closeButton.click();
|
||||
// If we're deleting the last item on a page that is not page 1, we'll need to refresh the display to the previous page
|
||||
|
@ -1875,165 +1794,95 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||
if (data.total == 1 && data.unfiltered_total > 1) {
|
||||
pageToDisplay--;
|
||||
}
|
||||
deleteDomainRequest(pk, pageToDisplay);
|
||||
this.deleteDomainRequest(pk, pageToDisplay);
|
||||
});
|
||||
});
|
||||
|
||||
// Do not scroll on first page load
|
||||
if (scroll)
|
||||
ScrollToElement('class', 'domain-requests');
|
||||
scrollToTable = true;
|
||||
this.scrollToTable = true;
|
||||
|
||||
// update the pagination after the domain requests list is updated
|
||||
updatePagination(
|
||||
this.updatePagination(
|
||||
'domain request',
|
||||
'#domain-requests-pagination',
|
||||
'#domain-requests-pagination .usa-pagination__counter',
|
||||
'#domain-requests',
|
||||
loadDomainRequests,
|
||||
data.page,
|
||||
data.num_pages,
|
||||
data.has_previous,
|
||||
data.has_next,
|
||||
data.total,
|
||||
currentSearchTerm
|
||||
);
|
||||
currentSortBy = sortBy;
|
||||
currentOrder = order;
|
||||
currentSearchTerm = searchTerm;
|
||||
this.currentSortBy = sortBy;
|
||||
this.currentOrder = order;
|
||||
this.currentSearchTerm = searchTerm;
|
||||
})
|
||||
.catch(error => console.error('Error fetching domain requests:', error));
|
||||
}
|
||||
|
||||
// Add event listeners to table headers for sorting
|
||||
tableHeaders.forEach(header => {
|
||||
header.addEventListener('click', function() {
|
||||
const sortBy = this.getAttribute('data-sortable');
|
||||
let order = 'asc';
|
||||
// sort order will be ascending, unless the currently sorted column is ascending, and the user
|
||||
// is selecting the same column to sort in descending order
|
||||
if (sortBy === currentSortBy) {
|
||||
order = currentOrder === 'asc' ? 'desc' : 'asc';
|
||||
/**
|
||||
* 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.
|
||||
* @param {*} domainRequestPk - the identifier for the request that we're deleting
|
||||
* @param {*} pageToDisplay - If we're deleting the last item on a page that is not page 1, we'll need to display the previous page
|
||||
*/
|
||||
deleteDomainRequest(domainRequestPk, pageToDisplay) {
|
||||
// Use to debug uswds modal issues
|
||||
//console.log('deleteDomainRequest')
|
||||
|
||||
// Get csrf token
|
||||
const csrfToken = getCsrfToken();
|
||||
// Create FormData object and append the CSRF token
|
||||
const formData = `csrfmiddlewaretoken=${encodeURIComponent(csrfToken)}&delete-domain-request=`;
|
||||
|
||||
fetch(`/domain-request/${domainRequestPk}/delete`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'X-CSRFToken': csrfToken,
|
||||
},
|
||||
body: formData
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
loadDomainRequests(1, sortBy, order);
|
||||
});
|
||||
});
|
||||
|
||||
domainRequestsSearchSubmit.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
currentSearchTerm = domainRequestsSearchInput.value;
|
||||
// If the search is blank, we match the resetSearch functionality
|
||||
if (currentSearchTerm) {
|
||||
showElement(resetSearchButton);
|
||||
} else {
|
||||
hideElement(resetSearchButton);
|
||||
// Update data and UI
|
||||
this.loadTable(pageToDisplay, this.currentSortBy, this.currentOrder, this.scrollToTable, this.currentSearchTerm);
|
||||
})
|
||||
.catch(error => console.error('Error fetching domain requests:', error));
|
||||
}
|
||||
loadDomainRequests(1, 'id', 'asc');
|
||||
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);
|
||||
/**
|
||||
* 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 isDomainsPage = document.querySelector("#domains")
|
||||
if (isDomainsPage){
|
||||
const domainsTable = new DomainsTable();
|
||||
if (domainsTable.tableWrapper) {
|
||||
// Initial load
|
||||
domainsTable.loadTable(1);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Manage visibility of reset filters button
|
||||
if (currentStatus.length == 0) {
|
||||
hideElement(resetFiltersButton);
|
||||
} else {
|
||||
showElement(resetFiltersButton);
|
||||
}
|
||||
|
||||
// Disable the auto scroll
|
||||
scrollToTable = false;
|
||||
|
||||
// Call loadTable with updated status
|
||||
loadDomainRequests(1, 'id', 'asc');
|
||||
resetHeaders();
|
||||
updateStatusIndicator();
|
||||
});
|
||||
});
|
||||
|
||||
// Reset UI and accessibility
|
||||
function resetHeaders() {
|
||||
tableHeaders.forEach(header => {
|
||||
// unset sort UI in headers
|
||||
unsetHeader(header);
|
||||
});
|
||||
// Reset the announcement region
|
||||
tableAnnouncementRegion.innerHTML = '';
|
||||
}
|
||||
|
||||
function resetSearch() {
|
||||
domainRequestsSearchInput.value = '';
|
||||
currentSearchTerm = '';
|
||||
hideElement(resetSearchButton);
|
||||
loadDomainRequests(1, 'id', 'asc');
|
||||
resetHeaders();
|
||||
}
|
||||
|
||||
if (resetSearchButton) {
|
||||
resetSearchButton.addEventListener('click', function() {
|
||||
resetSearch();
|
||||
});
|
||||
}
|
||||
|
||||
function closeMoreActionMenu(accordionThatIsOpen) {
|
||||
if (accordionThatIsOpen.getAttribute("aria-expanded") === "true") {
|
||||
accordionThatIsOpen.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();
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const domainRequestsSectionWrapper = document.querySelector('.domain-requests');
|
||||
if (domainRequestsSectionWrapper) {
|
||||
const domainRequestsTable = new DomainRequestsTable();
|
||||
if (domainRequestsTable.tableWrapper) {
|
||||
domainRequestsTable.loadTable(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2045,6 +1894,12 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||
closeOpenAccordions(event);
|
||||
});
|
||||
|
||||
function closeMoreActionMenu(accordionThatIsOpen) {
|
||||
if (accordionThatIsOpen.getAttribute("aria-expanded") === "true") {
|
||||
accordionThatIsOpen.click();
|
||||
}
|
||||
}
|
||||
|
||||
function closeOpenAccordions(event) {
|
||||
const openAccordions = document.querySelectorAll('.usa-button--more-actions[aria-expanded="true"]');
|
||||
openAccordions.forEach((openAccordionButton) => {
|
||||
|
@ -2055,22 +1910,23 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||
closeMoreActionMenu(openAccordionButton);
|
||||
}
|
||||
});
|
||||
|
||||
// Close the filter accordion
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initial load
|
||||
loadDomainRequests(1);
|
||||
}
|
||||
});
|
||||
|
||||
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');
|
||||
let utcHours = date.getUTCHours();
|
||||
const utcMinutes = date.getUTCMinutes().toString().padStart(2, '0');
|
||||
|
||||
const ampm = utcHours >= 12 ? 'PM' : 'AM';
|
||||
utcHours = utcHours % 12 || 12; // Convert to 12-hour format, '0' hours should be '12'
|
||||
|
||||
return `${utcMonth} ${utcDay}, ${utcYear}, ${utcHours}:${utcMinutes} ${ampm} UTC`;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* An IIFE that displays confirmation modal on the user profile page
|
||||
|
|
|
@ -160,7 +160,7 @@
|
|||
</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"
|
||||
class="usa-button usa-button--small padding--8-12-9-12 usa-button--outline usa-button--filter domain-requests__reset-filters display-none"
|
||||
>
|
||||
Clear filters
|
||||
<svg class="usa-icon top-1px" aria-hidden="true" focusable="false" role="img" width="24">
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue