Skip to content

Commit

Permalink
Merge pull request #3270 from cisagov/rh/3142-expiring-soon
Browse files Browse the repository at this point in the history
#3142: Domain Expiring Filter + CTA Banner - [RH]
  • Loading branch information
therealslimhsiehdy authored Dec 26, 2024
2 parents ccd4833 + 736f6ed commit 5909a7c
Show file tree
Hide file tree
Showing 13 changed files with 442 additions and 35 deletions.
36 changes: 33 additions & 3 deletions src/registrar/assets/src/js/getgov/table-domains.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ export class DomainsTable extends BaseTable {
</td>
`
}
const isExpiring = domain.state_display === "Expiring soon"
const iconType = isExpiring ? "error_outline" : "info_outline";
const iconColor = isExpiring ? "text-secondary-vivid" : "text-accent-cool"
row.innerHTML = `
<th scope="row" role="rowheader" data-label="Domain name">
${domain.name}
Expand All @@ -41,14 +44,14 @@ export class DomainsTable extends BaseTable {
<td data-label="Status">
${domain.state_display}
<svg
class="usa-icon usa-tooltip usa-tooltip--registrar text-middle margin-bottom-05 text-accent-cool no-click-outline-and-cursor-help"
data-position="top"
class="usa-icon usa-tooltip usa-tooltip--registrar text-middle margin-bottom-05 ${iconColor} no-click-outline-and-cursor-help"
data-position="top"
title="${domain.get_state_help_text}"
focusable="true"
aria-label="${domain.get_state_help_text}"
role="tooltip"
>
<use aria-hidden="true" xlink:href="/public/img/sprite.svg#info_outline"></use>
<use aria-hidden="true" xlink:href="/public/img/sprite.svg#${iconType}"></use>
</svg>
</td>
${markupForSuborganizationRow}
Expand Down Expand Up @@ -77,3 +80,30 @@ export function initDomainsTable() {
}
});
}

// For clicking the "Expiring" checkbox
document.addEventListener('DOMContentLoaded', () => {
const expiringLink = document.getElementById('link-expiring-domains');

if (expiringLink) {
// Grab the selection for the status filter by
const statusCheckboxes = document.querySelectorAll('input[name="filter-status"]');

expiringLink.addEventListener('click', (event) => {
event.preventDefault();
// Loop through all statuses
statusCheckboxes.forEach(checkbox => {
// To find the for checkbox for "Expiring soon"
if (checkbox.value === "expiring") {
// If the checkbox is not already checked, check it
if (!checkbox.checked) {
checkbox.checked = true;
// Do the checkbox action
let event = new Event('change');
checkbox.dispatchEvent(event)
}
}
});
});
}
});
11 changes: 11 additions & 0 deletions src/registrar/context_processors.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,19 @@ def portfolio_permissions(request):
"has_organization_requests_flag": False,
"has_organization_members_flag": False,
"is_portfolio_admin": False,
"has_domain_renewal_flag": False,
}
try:
portfolio = request.session.get("portfolio")

# These feature flags will display and doesn't depend on portfolio
portfolio_context.update(
{
"has_organization_feature_flag": True,
"has_domain_renewal_flag": request.user.has_domain_renewal_flag(),
}
)

# Linting: line too long
view_suborg = request.user.has_view_suborganization_portfolio_permission(portfolio)
edit_suborg = request.user.has_edit_suborganization_portfolio_permission(portfolio)
Expand All @@ -90,6 +100,7 @@ def portfolio_permissions(request):
"has_organization_requests_flag": request.user.has_organization_requests_flag(),
"has_organization_members_flag": request.user.has_organization_members_flag(),
"is_portfolio_admin": request.user.is_portfolio_admin(portfolio),
"has_domain_renewal_flag": request.user.has_domain_renewal_flag(),
}
return portfolio_context

Expand Down
26 changes: 21 additions & 5 deletions src/registrar/models/domain.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import logging
import ipaddress
import re
from datetime import date
from datetime import date, timedelta
from typing import Optional
from django_fsm import FSMField, transition, TransitionNotAllowed # type: ignore

Expand Down Expand Up @@ -40,6 +40,7 @@
from .public_contact import PublicContact

from .user_domain_role import UserDomainRole
from waffle.decorators import flag_is_active

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -1152,14 +1153,29 @@ def is_expired(self):
now = timezone.now().date()
return self.expiration_date < now

def state_display(self):
def is_expiring(self):
"""
Check if the domain's expiration date is within 60 days.
Return True if domain expiration date exists and within 60 days
and otherwise False bc there's no expiration date meaning so not expiring
"""
if self.expiration_date is None:
return False

now = timezone.now().date()

threshold_date = now + timedelta(days=60)
return now < self.expiration_date <= threshold_date

def state_display(self, request=None):
"""Return the display status of the domain."""
if self.is_expired() and self.state != self.State.UNKNOWN:
if self.is_expired() and (self.state != self.State.UNKNOWN):
return "Expired"
elif flag_is_active(request, "domain_renewal") and self.is_expiring():
return "Expiring soon"
elif self.state == self.State.UNKNOWN or self.state == self.State.DNS_NEEDED:
return "DNS needed"
else:
return self.state.capitalize()
return self.state.capitalize()

def map_epp_contact_to_public_contact(self, contact: eppInfo.InfoContactResultData, contact_id, contact_type):
"""Maps the Epp contact representation to a PublicContact object.
Expand Down
19 changes: 19 additions & 0 deletions src/registrar/models/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
from .domain_request import DomainRequest
from registrar.utility.waffle import flag_is_active_for_user
from waffle.decorators import flag_is_active
from django.utils import timezone
from datetime import timedelta

from phonenumber_field.modelfields import PhoneNumberField # type: ignore

Expand Down Expand Up @@ -163,6 +165,20 @@ def get_active_requests_count(self):
active_requests_count = self.domain_requests_created.filter(status__in=allowed_states).count()
return active_requests_count

def get_num_expiring_domains(self, request):
"""Return number of expiring domains"""
domain_ids = self.get_user_domain_ids(request)
now = timezone.now().date()
expiration_window = 60
threshold_date = now + timedelta(days=expiration_window)
num_of_expiring_domains = Domain.objects.filter(
id__in=domain_ids,
expiration_date__isnull=False,
expiration_date__lte=threshold_date,
expiration_date__gt=now,
).count()
return num_of_expiring_domains

def get_rejected_requests_count(self):
"""Return count of rejected requests"""
return self.domain_requests_created.filter(status=DomainRequest.DomainRequestStatus.REJECTED).count()
Expand Down Expand Up @@ -259,6 +275,9 @@ def has_edit_suborganization_portfolio_permission(self, portfolio):
def is_portfolio_admin(self, portfolio):
return "Admin" in self.portfolio_role_summary(portfolio)

def has_domain_renewal_flag(self):
return flag_is_active_for_user(self, "domain_renewal")

def get_first_portfolio(self):
permission = self.portfolio_permissions.first()
if permission:
Expand Down
15 changes: 12 additions & 3 deletions src/registrar/templates/domain_detail.html
Original file line number Diff line number Diff line change
Expand Up @@ -35,18 +35,27 @@ <h2 class="text-bold text-primary-dark domain-name-wrap">{{ domain.name }}</h2>
Status:
</span>
<span class="text-primary-darker">

{# UNKNOWN domains would not have an expiration date and thus would show 'Expired' #}
{% if domain.is_expired and domain.state != domain.State.UNKNOWN %}
Expired
{% elif has_domain_renewal_flag and domain.is_expiring %}
Expiring soon
{% elif domain.state == domain.State.UNKNOWN or domain.state == domain.State.DNS_NEEDED %}
DNS needed
{% else %}
{{ domain.state|title }}
{{ domain.state|title }}
{% endif %}
</span>
{% if domain.get_state_help_text %}
<div class="padding-top-1 text-primary-darker">
{{ domain.get_state_help_text }}
{% if has_domain_renewal_flag and domain.is_expiring and is_domain_manager %}
This domain will expire soon. <a href="/not-available-yet">Renew to maintain access.</a>
{% elif has_domain_renewal_flag and domain.is_expiring and is_portfolio_user %}
This domain will expire soon. Contact one of the listed domain managers to renew the domain.
{% else %}
{{ domain.get_state_help_text }}
{% endif %}
</div>
{% endif %}
</p>
Expand Down Expand Up @@ -119,4 +128,4 @@ <h3 class="margin-top-3"> DNS name servers </h3>
{% endif %}

</div>
{% endblock %} {# domain_content #}
{% endblock %} {# domain_content #}
57 changes: 53 additions & 4 deletions src/registrar/templates/includes/domains_table.html
Original file line number Diff line number Diff line change
@@ -1,10 +1,30 @@
{% load static %}



{% comment %} Stores the json endpoint in a url for easier access {% endcomment %}
{% url 'get_domains_json' as url %}



<span id="get_domains_json_url" class="display-none">{{url}}</span>

<!-- Org model banner (org manager can view, domain manager can edit) -->
{% if has_domain_renewal_flag and num_expiring_domains > 0 and has_any_domains_portfolio_permission %}
<section class="usa-site-alert usa-site-alert--info margin-bottom-2 {% if add_class %}{{ add_class }}{% endif %}" aria-label="Site alert">
<div class="usa-alert">
<div class="usa-alert__body {% if is_widescreen_mode %}usa-alert__body--widescreen{% endif %}">
<p class="usa-alert__text maxw-none">
{% if num_expiring_domains == 1%}
One domain will expire soon. Go to "Manage" to renew the domain. <a href="#" id="link-expiring-domains" class="usa-link">Show expiring domain.</a>
{% else%}
Multiple domains will expire soon. Go to "Manage" to renew the domains. <a href="#" id="link-expiring-domains" class="usa-link">Show expiring domains.</a>
{% endif %}
</p>
</div>
</div>
</section>
{% endif %}

<section class="section-outlined domains margin-top-0{% if portfolio %} section-outlined--border-base-light{% endif %}" id="domains">
<div class="section-outlined__header margin-bottom-3 {% if not portfolio %} section-outlined__header--no-portfolio justify-content-space-between{% else %} grid-row{% endif %}">
{% if not portfolio %}
Expand Down Expand Up @@ -53,7 +73,24 @@ <h2 id="domains-header" class="display-inline-block">Domains</h2>
</div>
{% endif %}
</div>
{% if portfolio %}

<!-- Non org model banner -->
{% if has_domain_renewal_flag and num_expiring_domains > 0 and not portfolio %}
<section class="usa-site-alert usa-site-alert--info margin-bottom-2 {% if add_class %}{{ add_class }}{% endif %}" aria-label="Site alert">
<div class="usa-alert">
<div class="usa-alert__body {% if is_widescreen_mode %}usa-alert__body--widescreen{% endif %}">
<p class="usa-alert__text maxw-none">
{% if num_expiring_domains == 1%}
One domain will expire soon. Go to "Manage" to renew the domain. <a href="#" id="link-expiring-domains" class="usa-link">Show expiring domain.</a>
{% else%}
Multiple domains will expire soon. Go to "Manage" to renew the domains. <a href="#" id="link-expiring-domains" class="usa-link">Show expiring domains.</a>
{% endif %}
</p>
</div>
</div>
</section>
{% endif %}

<div class="display-flex flex-align-center">
<span class="margin-right-2 margin-top-neg-1 usa-prose text-base-darker">Filter by</span>
<div class="usa-accordion usa-accordion--select margin-right-2">
Expand Down Expand Up @@ -135,6 +172,19 @@ <h2>Status</h2>
>Deleted</label
>
</div>
{% if has_domain_renewal_flag and num_expiring_domains > 0 %}
<div class="usa-checkbox">
<input
class="usa-checkbox__input"
id="filter-status-expiring"
type="checkbox"
name="filter-status"
value="expiring"
/>
<label class="usa-checkbox__label" for="filter-status-expiring"
>Expiring soon</label>
</div>
{% endif %}
</fieldset>
</div>
</div>
Expand All @@ -149,7 +199,6 @@ <h2>Status</h2>
</svg>
</button>
</div>
{% endif %}
<div class="display-none usa-table-container--scrollable margin-top-0" tabindex="0" id="domains__table-wrapper">
<table class="usa-table usa-table--borderless usa-table--stacked dotgov-table dotgov-table--stacked">
<caption class="sr-only">Your registered domains</caption>
Expand Down Expand Up @@ -200,4 +249,4 @@ <h2>Status</h2>
<ul class="usa-pagination__list">
<!-- Pagination links will be dynamically populated by JS -->
</ul>
</nav>
</nav>
4 changes: 2 additions & 2 deletions src/registrar/templates/portfolio_domains.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@

<div id="main-content">
<h1 id="domains-header">Domains</h1>
{% include "includes/domains_table.html" with portfolio=portfolio user_domain_count=user_domain_count %}
{% include "includes/domains_table.html" with portfolio=portfolio user_domain_count=user_domain_count num_expiring_domains=num_expiring_domains%}
</div>
{% endblock %}
{% endblock %}
Loading

0 comments on commit 5909a7c

Please sign in to comment.