diff --git a/src/registrar/assets/js/get-gov-admin.js b/src/registrar/assets/js/get-gov-admin.js index 04f5417b0..93b8359bf 100644 --- a/src/registrar/assets/js/get-gov-admin.js +++ b/src/registrar/assets/js/get-gov-admin.js @@ -513,9 +513,18 @@ function initializeWidgetOnList(list, parentId) { var readonlyView = document.querySelector("#action-needed-reason-email-readonly"); let emailWasSent = document.getElementById("action-needed-email-sent"); - let actionNeededEmailData = document.getElementById('action-needed-emails-data').textContent; - let actionNeededEmailsJson = JSON.parse(actionNeededEmailData); + let emailData = document.getElementById('action-needed-emails-data'); + if (!emailData) { + return; + } + + let actionNeededEmailData = emailData.textContent; + if(!actionNeededEmailData) { + return; + } + + let actionNeededEmailsJson = JSON.parse(actionNeededEmailData); const domainRequestId = actionNeededReasonDropdown ? document.querySelector("#domain_request_id").value : null const emailSentSessionVariableName = `actionNeededEmailSent-${domainRequestId}`; const oldDropdownValue = actionNeededReasonDropdown ? actionNeededReasonDropdown.value : null; @@ -750,3 +759,118 @@ function initializeWidgetOnList(list, parentId) { }); } })(); + + +/** An IIFE for dynamically changing some fields on the portfolio admin model +*/ +(function dynamicPortfolioFields(){ + + document.addEventListener('DOMContentLoaded', function() { + + let isPortfolioPage = document.getElementById("portfolio_form"); + if (!isPortfolioPage) { + return; + } + + // $ symbolically denotes that this is using jQuery + let $federalAgency = django.jQuery("#id_federal_agency"); + let organizationType = document.getElementById("id_organization_type"); + if ($federalAgency && organizationType) { + // Execute this function once on load + handleFederalAgencyChange($federalAgency, organizationType); + + // Attach the change event listener + $federalAgency.on("change", function() { + handleFederalAgencyChange($federalAgency, organizationType); + }); + } + + // Handle dynamically hiding the urbanization field + let urbanizationField = document.querySelector(".field-urbanization"); + let stateTerritory = document.getElementById("id_state_territory"); + if (urbanizationField && stateTerritory) { + // Execute this function once on load + handleStateTerritoryChange(stateTerritory, urbanizationField); + + // Attach the change event listener for state/territory + stateTerritory.addEventListener("change", function() { + handleStateTerritoryChange(stateTerritory, urbanizationField); + }); + } + }); + + function handleFederalAgencyChange(federalAgency, organizationType) { + // Set the org type to federal if an agency is selected + let selectedText = federalAgency.find("option:selected").text(); + + // There isn't a federal senior official associated with null records + if (!selectedText) { + return; + } + + if (selectedText !== "Non-Federal Agency") { + if (organizationType.value !== "federal") { + organizationType.value = "federal"; + } + }else { + if (organizationType.value === "federal") { + organizationType.value = ""; + } + } + + // Get the associated senior official with this federal agency + let $seniorOfficial = django.jQuery("#id_senior_official"); + if (!$seniorOfficial) { + console.log("Could not find the senior official field"); + return; + } + + let seniorOfficialApi = document.getElementById("senior_official_from_agency_json_url").value; + fetch(`${seniorOfficialApi}?agency_name=${selectedText}`) + .then(response => { + const statusCode = response.status; + return response.json().then(data => ({ statusCode, data })); + }) + .then(({ statusCode, data }) => { + if (data.error) { + // Clear the field if the SO doesn't exist. + if (statusCode === 404) { + $seniorOfficial.val("").trigger("change"); + console.warn("Record not found: " + data.error); + }else { + console.error("Error in AJAX call: " + data.error); + } + return; + } + + let seniorOfficialId = data.id; + let seniorOfficialName = [data.first_name, data.last_name].join(" "); + if (!seniorOfficialId || !seniorOfficialName || !seniorOfficialName.trim()){ + // Clear the field if the SO doesn't exist + $seniorOfficial.val("").trigger("change"); + return; + } + + // Add the senior official to the dropdown. + // This format supports select2 - if we decide to convert this field in the future. + if ($seniorOfficial.find(`option[value='${seniorOfficialId}']`).length) { + // Select the value that is associated with the current Senior Official. + $seniorOfficial.val(seniorOfficialId).trigger("change"); + } else { + // Create a DOM Option that matches the desired Senior Official. Then append it and select it. + let userOption = new Option(seniorOfficialName, seniorOfficialId, true, true); + $seniorOfficial.append(userOption).trigger("change"); + } + }) + .catch(error => console.error("Error fetching senior official: ", error)); + } + + function handleStateTerritoryChange(stateTerritory, urbanizationField) { + let selectedValue = stateTerritory.value; + if (selectedValue === "PR") { + showElement(urbanizationField) + } else { + hideElement(urbanizationField) + } + } +})(); diff --git a/src/registrar/assets/js/get-gov.js b/src/registrar/assets/js/get-gov.js index d238823b3..f3b41eb51 100644 --- a/src/registrar/assets/js/get-gov.js +++ b/src/registrar/assets/js/get-gov.js @@ -1183,8 +1183,19 @@ document.addEventListener('DOMContentLoaded', function() { * @param {*} portfolio - the portfolio id */ function loadDomains(page, sortBy = currentSortBy, order = currentOrder, scroll = scrollToTable, status = currentStatus, searchTerm = currentSearchTerm, portfolio = portfolioValue) { + // fetch json of page of domais, given params + let baseUrl = document.getElementById("get_domains_json_url"); + if (!baseUrl) { + return; + } + + let baseUrlValue = baseUrl.innerHTML; + if (!baseUrlValue) { + return; + } + // fetch json of page of domains, given params - let url = `/get-domains-json/?page=${page}&sort_by=${sortBy}&order=${order}&status=${status}&search_term=${searchTerm}` + let url = `${baseUrlValue}?page=${page}&sort_by=${sortBy}&order=${order}&status=${status}&search_term=${searchTerm}` if (portfolio) url += `&portfolio=${portfolio}` @@ -1524,7 +1535,17 @@ document.addEventListener('DOMContentLoaded', function() { */ function loadDomainRequests(page, sortBy = currentSortBy, order = currentOrder, scroll = scrollToTable, searchTerm = currentSearchTerm) { // fetch json of page of domain requests, given params - fetch(`/get-domain-requests-json/?page=${page}&sort_by=${sortBy}&order=${order}&search_term=${searchTerm}`) + let baseUrl = document.getElementById("get_domain_requests_json_url"); + if (!baseUrl) { + return; + } + + let baseUrlValue = baseUrl.innerHTML; + if (!baseUrlValue) { + return; + } + + fetch(`${baseUrlValue}?page=${page}&sort_by=${sortBy}&order=${order}&search_term=${searchTerm}`) .then(response => response.json()) .then(data => { if (data.error) { diff --git a/src/registrar/config/urls.py b/src/registrar/config/urls.py index b70c0b9ff..413449896 100644 --- a/src/registrar/config/urls.py +++ b/src/registrar/config/urls.py @@ -24,6 +24,7 @@ from registrar.views.report_views import ( from registrar.views.domain_request import Step from registrar.views.domain_requests_json import get_domain_requests_json +from registrar.views.utility.api_views import get_senior_official_from_federal_agency_json from registrar.views.domains_json import get_domains_json from registrar.views.utility import always_404 from api.views import available, get_current_federal, get_current_full @@ -133,6 +134,11 @@ urlpatterns = [ AnalyticsView.as_view(), name="analytics", ), + path( + "admin/api/get-senior-official-from-federal-agency-json/", + get_senior_official_from_federal_agency_json, + name="get-senior-official-from-federal-agency-json", + ), path("admin/", admin.site.urls), path( "reports/export_data_type_user/", diff --git a/src/registrar/forms/domain.py b/src/registrar/forms/domain.py index 02299395f..a7a006788 100644 --- a/src/registrar/forms/domain.py +++ b/src/registrar/forms/domain.py @@ -539,8 +539,7 @@ class DomainOrgNameAddressForm(forms.ModelForm): elif self.is_tribal and not self._field_unchanged("organization_name"): raise ValueError("organization_name cannot be modified when the generic_org_type is tribal") - else: - super().save() + super().save() def _field_unchanged(self, field_name) -> bool: """ @@ -552,6 +551,21 @@ class DomainOrgNameAddressForm(forms.ModelForm): """ old_value = self.initial.get(field_name, None) new_value = self.cleaned_data.get(field_name, None) + + field = self.fields[field_name] + + # Check if the field has a queryset attribute before accessing it + if hasattr(field, "queryset") and isinstance(new_value, str): + try: + # Convert the string to the corresponding ID + new_value = field.queryset.get(name=new_value).id + except field.queryset.model.DoesNotExist: + pass # Handle the case where the object does not exist + + elif hasattr(new_value, "id"): + # If new_value is a model instance, compare by ID. + new_value = new_value.id + return old_value == new_value diff --git a/src/registrar/management/commands/email_current_metadata_report.py b/src/registrar/management/commands/email_current_metadata_report.py index 905e4a57a..b28051f0c 100644 --- a/src/registrar/management/commands/email_current_metadata_report.py +++ b/src/registrar/management/commands/email_current_metadata_report.py @@ -58,17 +58,17 @@ class Command(BaseCommand): reports = { "Domain report": { "report_filename": f"domain-metadata-{self.current_date}.csv", - "report_function": csv_export.export_data_type_to_csv, + "report_function": csv_export.DomainDataType.export_data_to_csv, }, "Domain request report": { "report_filename": f"domain-request-metadata-{self.current_date}.csv", - "report_function": csv_export.DomainRequestExport.export_full_domain_request_report, + "report_function": csv_export.DomainRequestDataFull.export_data_to_csv, }, } # Set the password equal to our content in SECRET_ENCRYPT_METADATA. # For local development, this will be "devpwd" unless otherwise set. - # Uncomment these lines if you want to use this: + # Uncomment these lines (and comment out the line after) if you want to use this: # override = settings.SECRET_ENCRYPT_METADATA is None and not settings.IS_PRODUCTION # password = "devpwd" if override else settings.SECRET_ENCRYPT_METADATA password = settings.SECRET_ENCRYPT_METADATA diff --git a/src/registrar/models/portfolio.py b/src/registrar/models/portfolio.py index 06b01e672..484c45e8c 100644 --- a/src/registrar/models/portfolio.py +++ b/src/registrar/models/portfolio.py @@ -110,3 +110,12 @@ class Portfolio(TimeStampedModel): def __str__(self) -> str: return f"{self.organization_name}" + + def save(self, *args, **kwargs): + """Save override for custom properties""" + + # The urbanization field is only intended for the state_territory puerto rico + if self.state_territory != self.StateTerritoryChoices.PUERTO_RICO and self.urbanization: + self.urbanization = None + + super().save(*args, **kwargs) diff --git a/src/registrar/templates/django/admin/portfolio_change_form.html b/src/registrar/templates/django/admin/portfolio_change_form.html index 3a6f13ccf..f6382758b 100644 --- a/src/registrar/templates/django/admin/portfolio_change_form.html +++ b/src/registrar/templates/django/admin/portfolio_change_form.html @@ -1,6 +1,13 @@ {% extends 'django/admin/email_clipboard_change_form.html' %} {% load i18n static %} +{% block content %} + {% comment %} Stores the json endpoint in a url for easier access {% endcomment %} + {% url 'get-senior-official-from-federal-agency-json' as url %} + + {{ block.super }} +{% endblock content %} + {% block after_related_objects %}