From a8fec4fccac7c5c8fc11575a9ee881181dbc2e4e Mon Sep 17 00:00:00 2001 From: Rachid Mrad Date: Fri, 18 Oct 2024 18:22:07 -0400 Subject: [PATCH] JS --- src/registrar/assets/js/get-gov.js | 136 +++++++++++++++++- .../includes/member_domains_table.html | 37 ++++- .../templates/portfolio_member_domains.html | 12 +- src/registrar/views/member_domains_json.py | 49 +------ 4 files changed, 169 insertions(+), 65 deletions(-) diff --git a/src/registrar/assets/js/get-gov.js b/src/registrar/assets/js/get-gov.js index 8281aa50a..efb8c6f11 100644 --- a/src/registrar/assets/js/get-gov.js +++ b/src/registrar/assets/js/get-gov.js @@ -1899,7 +1899,7 @@ class MembersTable extends LoadTableBase { // --------- FETCH DATA - // fetch json of page of domais, given params + // fetch json of page of domains, given params let baseUrl = document.getElementById("get_members_json_url"); if (!baseUrl) { return; @@ -2014,14 +2014,120 @@ class MembersTable extends LoadTableBase { } } +class MemberDomainsTable extends LoadTableBase { + + constructor() { + super('.member-domains__table', '.member-domains__table-wrapper', '#member-domains__search-field', '#member-domains__search-field-submit', '.member-domains__reset-search', '.member-domains__reset-filters', '.member-domains__no-data', '.member-domains__no-search-results'); + } + /** + * Loads rows in the members list, as well as updates pagination around the members 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 {*} searchTerm - the search term + * @param {*} portfolio - the portfolio id + */ + loadTable(page, sortBy = 'name', order = this.currentOrder, scroll = this.scrollToTable, searchTerm =this.currentSearchTerm, portfolio = this.portfolioValue) { + + // --------- SEARCH + let searchParams = new URLSearchParams( + { + "page": page, + "sort_by": sortBy, + "order": order, + "search_term": searchTerm, + } + ); + + let emailValue = this.portfolioElement ? this.portfolioElement.getAttribute('data-email') : null; + let memberIdValue = this.portfolioElement ? this.portfolioElement.getAttribute('data-member-id') : null; + let memberOnly = this.portfolioElement ? this.portfolioElement.getAttribute('data-member-only') : null; + + if (portfolio) + searchParams.append("portfolio", portfolio) + if (emailValue) + searchParams.append("email", emailValue) + if (memberIdValue) + searchParams.append("member_id", memberIdValue) + if (memberOnly) + searchParams.append("member_only", memberOnly) + + + // --------- FETCH DATA + // fetch json of page of domais, given params + let baseUrl = document.getElementById("get_member_domains_json_url"); + if (!baseUrl) { + return; + } + + let baseUrlValue = baseUrl.innerHTML; + if (!baseUrlValue) { + return; + } + + let url = `${baseUrlValue}?${searchParams.toString()}` //TODO: uncomment for search function + fetch(url) + .then(response => response.json()) + .then(data => { + if (data.error) { + console.error('Error in AJAX call: ' + data.error); + return; + } + + // handle the display of proper messaging in the event that no members exist in the list or search returns no results + 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 memberDomainsList = document.querySelector('.member-domains__table tbody'); + memberDomainsList.innerHTML = ''; + + + data.domains.forEach(domain => { + const row = document.createElement('tr'); + + row.innerHTML = ` + + ${domain.name} + + `; + memberDomainsList.appendChild(row); + }); + + // Do not scroll on first page load + if (scroll) + ScrollToElement('class', 'member-domains'); + this.scrollToTable = true; + + // update pagination + this.updatePagination( + 'member domain', + '#member-domains-pagination', + '#member-domains-pagination .usa-pagination__counter', + '#member-domains', + data.page, + data.num_pages, + data.has_previous, + data.has_next, + data.total, + ); + this.currentSortBy = sortBy; + this.currentOrder = order; + this.currentSearchTerm = searchTerm; + }) + .catch(error => console.error('Error fetching domains:', error)); + } +} + /** * 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. * */ document.addEventListener('DOMContentLoaded', function() { - const isDomainsPage = document.querySelector("#domains") + const isDomainsPage = document.getElementById("domains") if (isDomainsPage){ const domainsTable = new DomainsTable(); if (domainsTable.tableWrapper) { @@ -2033,11 +2139,11 @@ document.addEventListener('DOMContentLoaded', 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. * */ document.addEventListener('DOMContentLoaded', function() { - const domainRequestsSectionWrapper = document.querySelector('.domain-requests'); + const domainRequestsSectionWrapper = document.getElementById('domain-requests'); if (domainRequestsSectionWrapper) { const domainRequestsTable = new DomainRequestsTable(); if (domainRequestsTable.tableWrapper) { @@ -2090,11 +2196,11 @@ const utcDateString = (dateString) => { /** * 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 members list and associated functionality. * */ document.addEventListener('DOMContentLoaded', function() { - const isMembersPage = document.querySelector("#members") + const isMembersPage = document.getElementById("members") if (isMembersPage){ const membersTable = new MembersTable(); if (membersTable.tableWrapper) { @@ -2104,6 +2210,22 @@ document.addEventListener('DOMContentLoaded', function() { } }); +/** + * An IIFE that listens for DOM Content to be loaded, then executes. This function + * initializes the member domains list and associated functionality. + * + */ +document.addEventListener('DOMContentLoaded', function() { + const isMemberDomainsPage = document.getElementById("member-domains") + if (isMemberDomainsPage){ + const memberDomainsTable = new MemberDomainsTable(); + if (memberDomainsTable.tableWrapper) { + // Initial load + memberDomainsTable.loadTable(1); + } + } +}); + /** * An IIFE that displays confirmation modal on the user profile page */ diff --git a/src/registrar/templates/includes/member_domains_table.html b/src/registrar/templates/includes/member_domains_table.html index 7d9a37308..29ce042d6 100644 --- a/src/registrar/templates/includes/member_domains_table.html +++ b/src/registrar/templates/includes/member_domains_table.html @@ -1,8 +1,37 @@ {% load static %} -
+{% if member %} + +{% else %} + +{% endif %} + +{% comment %} Stores the json endpoint in a url for easier access {% endcomment %} +{% url 'get_member_domains_json' as url %} + +
+

- Domains assigned to {{ email }} + Domains assigned to + {% if member %} + {{ member.email }} + {% else %} + {{ portfolio_invitation.email }} + {% endif %}

@@ -25,7 +54,7 @@ @@ -49,7 +78,7 @@ member domains - Domains + Domains diff --git a/src/registrar/templates/portfolio_member_domains.html b/src/registrar/templates/portfolio_member_domains.html index c51c3464e..1f811c707 100644 --- a/src/registrar/templates/portfolio_member_domains.html +++ b/src/registrar/templates/portfolio_member_domains.html @@ -48,17 +48,7 @@ A domain manager can be assigned to any domain across the organization. Domain managers can change domain information, adjust DNS settings, and invite or assign other domain managers to their assigned domains.

- {% if member %} - {% with member.email as email %} - {% include "includes/member_domains_table.html" with email=email %} - {% endwith %} - {% else %} - {% with portfolio_invitation.email as email %} - {% include "includes/member_domains_table.html" with email=email %} - {% endwith %} - {% endif %} + {% include "includes/member_domains_table.html" %} - -
{% endblock %} diff --git a/src/registrar/views/member_domains_json.py b/src/registrar/views/member_domains_json.py index 4125fe345..7dcec6bef 100644 --- a/src/registrar/views/member_domains_json.py +++ b/src/registrar/views/member_domains_json.py @@ -27,7 +27,6 @@ class PortfolioMemberDomainsJson(PortfolioMemberDomainsPermission, View): unfiltered_total = objects.count() objects = self.apply_search(objects, request) - objects = self.apply_state_filter(objects, request) objects = self.apply_sorting(objects, request) paginator = Paginator(objects, 10) @@ -53,14 +52,14 @@ class PortfolioMemberDomainsJson(PortfolioMemberDomainsPermission, View): """Get domain ids from request. request.get.email - email address of invited member - request.get.member - member id of member + request.get.member_id - member id of member request.get.portfolio - portfolio id of portfolio request.get.member_only - whether to return only domains associated with member or to return all domains in the portfolio """ portfolio = request.GET.get("portfolio") email = request.GET.get("email") - member_id = request.GET.get("member") + member_id = request.GET.get("member_id") member_only = request.GET.get("member_only", "false").lower() in ["true", "1"] if member_only: if member_id: @@ -86,48 +85,12 @@ class PortfolioMemberDomainsJson(PortfolioMemberDomainsPermission, View): return queryset - def apply_state_filter(self, queryset, request): - status_param = request.GET.get("status") - if status_param: - status_list = status_param.split(",") - # if unknown is in status_list, append 'dns needed' since both - # unknown and dns needed display as DNS Needed, and both are - # searchable via state parameter of 'unknown' - if "unknown" in status_list: - status_list.append("dns needed") - # Split the status list into normal states and custom states - normal_states = [state for state in status_list if state in Domain.State.values] - custom_states = [state for state in status_list if state == "expired"] - # Construct Q objects for normal states that can be queried through ORM - state_query = Q() - if normal_states: - state_query |= Q(state__in=normal_states) - # Handle custom states in Python, as expired can not be queried through ORM - if "expired" in custom_states: - expired_domain_ids = [domain.id for domain in queryset if domain.state_display() == "Expired"] - state_query |= Q(id__in=expired_domain_ids) - # Apply the combined query - queryset = queryset.filter(state_query) - # If there are filtered states, and expired is not one of them, domains with - # state_display of 'Expired' must be removed - if "expired" not in custom_states: - expired_domain_ids = [domain.id for domain in queryset if domain.state_display() == "Expired"] - queryset = queryset.exclude(id__in=expired_domain_ids) - - return queryset - - def apply_sorting(self, queryset, request): - sort_by = request.GET.get("sort_by", "id") + sort_by = request.GET.get("sort_by", "name") order = request.GET.get("order", "asc") - if sort_by == "state_display": - objects = list(queryset) - objects.sort(key=lambda domain: domain.state_display(), reverse=(order == "desc")) - return objects - else: - if order == "desc": - sort_by = f"-{sort_by}" - return queryset.order_by(sort_by) + if order == "desc": + sort_by = f"-{sort_by}" + return queryset.order_by(sort_by) def serialize_domain(self, domain, user):