Merge remote-tracking branch 'origin/main' into nl/2136-CISA-rep-additional-details

This commit is contained in:
CocoByte 2024-06-05 16:55:12 -06:00
commit ed4c9b930b
No known key found for this signature in database
GPG key ID: BBFAA2526384C97F
14 changed files with 393 additions and 166 deletions

View file

@ -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() {

View 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;
}
}

View file

@ -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";

View file

@ -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"),
),
]

View file

@ -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):

View file

@ -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)

View file

@ -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"

View file

@ -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"

View file

@ -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,

View file

@ -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):

View file

@ -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"),
]

View file

@ -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(

View file

@ -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">

View file

@ -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 = [