From 2730047588e8f75a02ac825b470f9a3130474a0c Mon Sep 17 00:00:00 2001 From: David Kennedy Date: Tue, 10 Dec 2024 13:41:48 -0500 Subject: [PATCH 01/12] domain information changes done --- src/registrar/admin.py | 149 +++++++++++++++++- .../getgov-admin/domain-information-form.js | 12 +- .../helpers-portfolio-dynamic-fields.js | 24 +-- 3 files changed, 160 insertions(+), 25 deletions(-) diff --git a/src/registrar/admin.py b/src/registrar/admin.py index 0e8e4847a..7adb7e3ed 100644 --- a/src/registrar/admin.py +++ b/src/registrar/admin.py @@ -220,6 +220,14 @@ class Meta: fields = "__all__" widgets = { "other_contacts": NoAutocompleteFilteredSelectMultiple("other_contacts", False), + "portfolio": AutocompleteSelectWithPlaceholder( + DomainRequest._meta.get_field("portfolio"), admin.site, attrs={"data-placeholder": "---------"} + ), + "sub_organization": AutocompleteSelectWithPlaceholder( + DomainRequest._meta.get_field("sub_organization"), + admin.site, + attrs={"data-placeholder": "---------", "ajax-url": "get-suborganization-list-json"}, + ), } @@ -1523,6 +1531,71 @@ def converted_generic_org_type(self, obj): orderable_fk_fields = [("domain", "name")] + # Define methods to display fields from the related portfolio + def portfolio_senior_official(self, obj) -> Optional[SeniorOfficial]: + return obj.portfolio.senior_official if obj.portfolio and obj.portfolio.senior_official else None + + portfolio_senior_official.short_description = "Senior official" # type: ignore + + def portfolio_organization_type(self, obj): + return ( + DomainRequest.OrganizationChoices.get_org_label(obj.portfolio.organization_type) + if obj.portfolio and obj.portfolio.organization_type + else "-" + ) + + portfolio_organization_type.short_description = "Organization type" # type: ignore + + def portfolio_federal_type(self, obj): + return ( + BranchChoices.get_branch_label(obj.portfolio.federal_type) + if obj.portfolio and obj.portfolio.federal_type + else "-" + ) + + portfolio_federal_type.short_description = "Federal type" # type: ignore + + def portfolio_organization_name(self, obj): + return obj.portfolio.organization_name if obj.portfolio else "" + + portfolio_organization_name.short_description = "Organization name" # type: ignore + + def portfolio_federal_agency(self, obj): + return obj.portfolio.federal_agency if obj.portfolio else "" + + portfolio_federal_agency.short_description = "Federal agency" # type: ignore + + def portfolio_state_territory(self, obj): + return obj.portfolio.state_territory if obj.portfolio else "" + + portfolio_state_territory.short_description = "State, territory, or military post" # type: ignore + + def portfolio_address_line1(self, obj): + return obj.portfolio.address_line1 if obj.portfolio else "" + + portfolio_address_line1.short_description = "Address line 1" # type: ignore + + def portfolio_address_line2(self, obj): + return obj.portfolio.address_line2 if obj.portfolio else "" + + portfolio_address_line2.short_description = "Address line 2" # type: ignore + + def portfolio_city(self, obj): + return obj.portfolio.city if obj.portfolio else "" + + portfolio_city.short_description = "City" # type: ignore + + def portfolio_zipcode(self, obj): + return obj.portfolio.zipcode if obj.portfolio else "" + + portfolio_zipcode.short_description = "Zip code" # type: ignore + + def portfolio_urbanization(self, obj): + return obj.portfolio.urbanization if obj.portfolio else "" + + portfolio_urbanization.short_description = "Urbanization" # type: ignore + + # Filters list_filter = [GenericOrgFilter] @@ -1535,18 +1608,38 @@ def converted_generic_org_type(self, obj): fieldsets = [ ( None, + { + "fields": [ + "domain_request", + "notes", + ] + }, + ), + ( + "Requested by", { "fields": [ "portfolio", "sub_organization", "creator", - "domain_request", - "notes", ] }, ), (".gov domain", {"fields": ["domain"]}), - ("Contacts", {"fields": ["senior_official", "other_contacts", "no_other_contacts_rationale"]}), + ( + "Contacts", + { + "fields": [ + "senior_official", + "portfolio_senior_official", + "other_contacts", + "no_other_contacts_rationale", + "cisa_representative_first_name", + "cisa_representative_last_name", + "cisa_representative_email", + ] + }, + ), ("Background info", {"fields": ["anything_else"]}), ( "Type of organization", @@ -1595,10 +1688,58 @@ def converted_generic_org_type(self, obj): ], }, ), + # the below three sections are for portfolio fields + ( + "Type of organization", + { + "fields": [ + "portfolio_organization_type", + "portfolio_federal_type", + ] + }, + ), + ( + "Organization name and mailing address", + { + "fields": [ + "portfolio_organization_name", + "portfolio_federal_agency", + ] + }, + ), + ( + "Show details", + { + "classes": ["collapse--dgfieldset"], + "description": "Extends organization name and mailing address", + "fields": [ + "portfolio_state_territory", + "portfolio_address_line1", + "portfolio_address_line2", + "portfolio_city", + "portfolio_zipcode", + "portfolio_urbanization", + ], + }, + ), ] # Readonly fields for analysts and superusers - readonly_fields = ("other_contacts", "is_election_board") + readonly_fields = ( + "portfolio_senior_official", + "portfolio_organization_type", + "portfolio_federal_type", + "portfolio_organization_name", + "portfolio_federal_agency", + "portfolio_state_territory", + "portfolio_address_line1", + "portfolio_address_line2", + "portfolio_city", + "portfolio_zipcode", + "portfolio_urbanization", + "other_contacts", + "is_election_board" + ) # Read only that we'll leverage for CISA Analysts analyst_readonly_fields = [ diff --git a/src/registrar/assets/src/js/getgov-admin/domain-information-form.js b/src/registrar/assets/src/js/getgov-admin/domain-information-form.js index 8139c752f..072b73720 100644 --- a/src/registrar/assets/src/js/getgov-admin/domain-information-form.js +++ b/src/registrar/assets/src/js/getgov-admin/domain-information-form.js @@ -1,17 +1,11 @@ -import { handleSuborganizationFields } from './helpers-portfolio-dynamic-fields.js'; +import { handlePortfolioSelection } from './helpers-portfolio-dynamic-fields.js'; /** - * A function for dynamic DomainInformation fields + * A function for dynamic DomainRequest fields */ export function initDynamicDomainInformationFields(){ const domainInformationPage = document.getElementById("domaininformation_form"); if (domainInformationPage) { - handleSuborganizationFields(); - } - - // DomainInformation is embedded inside domain so this should fire there too - const domainPage = document.getElementById("domain_form"); - if (domainPage) { - handleSuborganizationFields(portfolioDropdownSelector="#id_domain_info-0-portfolio", suborgDropdownSelector="#id_domain_info-0-sub_organization"); + handlePortfolioSelection(); } } diff --git a/src/registrar/assets/src/js/getgov-admin/helpers-portfolio-dynamic-fields.js b/src/registrar/assets/src/js/getgov-admin/helpers-portfolio-dynamic-fields.js index 39f30b87f..ca4c4b44c 100644 --- a/src/registrar/assets/src/js/getgov-admin/helpers-portfolio-dynamic-fields.js +++ b/src/registrar/assets/src/js/getgov-admin/helpers-portfolio-dynamic-fields.js @@ -24,13 +24,13 @@ export function handleSuborganizationFields( function toggleSuborganizationFields() { if (portfolioDropdown.val() && !suborganizationDropdown.val()) { - showElement(requestedSuborgField); - showElement(suborgCity); - showElement(suborgStateTerritory); + if (requestedSuborgField) showElement(requestedSuborgField); + if (suborgCity) showElement(suborgCity); + if (suborgStateTerritory) showElement(suborgStateTerritory); }else { - hideElement(requestedSuborgField); - hideElement(suborgCity); - hideElement(suborgStateTerritory); + if (requestedSuborgField) hideElement(requestedSuborgField); + if (suborgCity) hideElement(suborgCity); + if (suborgStateTerritory) hideElement(suborgStateTerritory); } } @@ -504,14 +504,14 @@ export function handlePortfolioSelection() { if (portfolio_id && !suborganization_id) { // Show suborganization request fields - showElement(requestedSuborganizationField); - showElement(suborganizationCity); - showElement(suborganizationStateTerritory); + if (requestedSuborganizationField) showElement(requestedSuborganizationField); + if (suborganizationCity) showElement(suborganizationCity); + if (suborganizationStateTerritory) showElement(suborganizationStateTerritory); } else { // Hide suborganization request fields if suborganization is selected - hideElement(requestedSuborganizationField); - hideElement(suborganizationCity); - hideElement(suborganizationStateTerritory); + if (requestedSuborganizationField) hideElement(requestedSuborganizationField); + if (suborganizationCity) hideElement(suborganizationCity); + if (suborganizationStateTerritory) hideElement(suborganizationStateTerritory); } } From 787506989742024344ebbb526e8c61ccbae8b223 Mon Sep 17 00:00:00 2001 From: David Kennedy Date: Tue, 10 Dec 2024 15:37:18 -0500 Subject: [PATCH 02/12] fixed rest of domain information --- src/registrar/admin.py | 4 ++-- .../django/admin/domain_information_change_form.html | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/registrar/admin.py b/src/registrar/admin.py index 7adb7e3ed..30b49f17e 100644 --- a/src/registrar/admin.py +++ b/src/registrar/admin.py @@ -221,10 +221,10 @@ class Meta: widgets = { "other_contacts": NoAutocompleteFilteredSelectMultiple("other_contacts", False), "portfolio": AutocompleteSelectWithPlaceholder( - DomainRequest._meta.get_field("portfolio"), admin.site, attrs={"data-placeholder": "---------"} + DomainInformation._meta.get_field("portfolio"), admin.site, attrs={"data-placeholder": "---------"} ), "sub_organization": AutocompleteSelectWithPlaceholder( - DomainRequest._meta.get_field("sub_organization"), + DomainInformation._meta.get_field("sub_organization"), admin.site, attrs={"data-placeholder": "---------", "ajax-url": "get-suborganization-list-json"}, ), diff --git a/src/registrar/templates/django/admin/domain_information_change_form.html b/src/registrar/templates/django/admin/domain_information_change_form.html index c5b0d54b8..487fd97e1 100644 --- a/src/registrar/templates/django/admin/domain_information_change_form.html +++ b/src/registrar/templates/django/admin/domain_information_change_form.html @@ -1,6 +1,13 @@ {% extends 'admin/change_form.html' %} {% load i18n static %} +{% block content %} + {% comment %} Stores the json endpoint in a url for easier access {% endcomment %} + {% url 'get-portfolio-json' as url %} + + {{ block.super }} +{% endblock content %} + {% block field_sets %} {% for fieldset in adminform %} {% comment %} From 42c62c2ba0830fd0a79c6f777a01006f915022dc Mon Sep 17 00:00:00 2001 From: David Kennedy Date: Tue, 10 Dec 2024 16:29:21 -0500 Subject: [PATCH 03/12] changes to domain form --- src/registrar/admin.py | 73 +++++++++++++++++++ .../assets/src/js/getgov-admin/domain-form.js | 14 ++++ .../getgov-admin/domain-information-form.js | 3 +- .../helpers-portfolio-dynamic-fields.js | 17 +++-- .../assets/src/js/getgov-admin/main.js | 2 + .../django/admin/domain_change_form.html | 7 ++ 6 files changed, 108 insertions(+), 8 deletions(-) diff --git a/src/registrar/admin.py b/src/registrar/admin.py index 30b49f17e..76c935f96 100644 --- a/src/registrar/admin.py +++ b/src/registrar/admin.py @@ -239,6 +239,14 @@ class Meta: fields = "__all__" widgets = { "other_contacts": NoAutocompleteFilteredSelectMultiple("other_contacts", False), + "portfolio": AutocompleteSelectWithPlaceholder( + DomainInformation._meta.get_field("portfolio"), admin.site, attrs={"data-placeholder": "---------"} + ), + "sub_organization": AutocompleteSelectWithPlaceholder( + DomainInformation._meta.get_field("sub_organization"), + admin.site, + attrs={"data-placeholder": "---------", "ajax-url": "get-suborganization-list-json"}, + ), } @@ -2716,7 +2724,72 @@ class DomainInformationInline(admin.StackedInline): template = "django/admin/includes/domain_info_inline_stacked.html" model = models.DomainInformation + # Define methods to display fields from the related portfolio + def portfolio_senior_official(self, obj) -> Optional[SeniorOfficial]: + return obj.portfolio.senior_official if obj.portfolio and obj.portfolio.senior_official else None + + portfolio_senior_official.short_description = "Senior official" # type: ignore + + def portfolio_organization_type(self, obj): + return ( + DomainRequest.OrganizationChoices.get_org_label(obj.portfolio.organization_type) + if obj.portfolio and obj.portfolio.organization_type + else "-" + ) + + portfolio_organization_type.short_description = "Organization type" # type: ignore + + def portfolio_federal_type(self, obj): + return ( + BranchChoices.get_branch_label(obj.portfolio.federal_type) + if obj.portfolio and obj.portfolio.federal_type + else "-" + ) + + portfolio_federal_type.short_description = "Federal type" # type: ignore + + def portfolio_organization_name(self, obj): + return obj.portfolio.organization_name if obj.portfolio else "" + + portfolio_organization_name.short_description = "Organization name" # type: ignore + + def portfolio_federal_agency(self, obj): + return obj.portfolio.federal_agency if obj.portfolio else "" + + portfolio_federal_agency.short_description = "Federal agency" # type: ignore + + def portfolio_state_territory(self, obj): + return obj.portfolio.state_territory if obj.portfolio else "" + + portfolio_state_territory.short_description = "State, territory, or military post" # type: ignore + + def portfolio_address_line1(self, obj): + return obj.portfolio.address_line1 if obj.portfolio else "" + + portfolio_address_line1.short_description = "Address line 1" # type: ignore + + def portfolio_address_line2(self, obj): + return obj.portfolio.address_line2 if obj.portfolio else "" + + portfolio_address_line2.short_description = "Address line 2" # type: ignore + + def portfolio_city(self, obj): + return obj.portfolio.city if obj.portfolio else "" + + portfolio_city.short_description = "City" # type: ignore + + def portfolio_zipcode(self, obj): + return obj.portfolio.zipcode if obj.portfolio else "" + + portfolio_zipcode.short_description = "Zip code" # type: ignore + + def portfolio_urbanization(self, obj): + return obj.portfolio.urbanization if obj.portfolio else "" + + portfolio_urbanization.short_description = "Urbanization" # type: ignore + fieldsets = copy.deepcopy(list(DomainInformationAdmin.fieldsets)) + readonly_fields = copy.deepcopy(DomainInformationAdmin.readonly_fields) analyst_readonly_fields = copy.deepcopy(DomainInformationAdmin.analyst_readonly_fields) autocomplete_fields = copy.deepcopy(DomainInformationAdmin.autocomplete_fields) diff --git a/src/registrar/assets/src/js/getgov-admin/domain-form.js b/src/registrar/assets/src/js/getgov-admin/domain-form.js index 474e2822c..8c5ab5b1c 100644 --- a/src/registrar/assets/src/js/getgov-admin/domain-form.js +++ b/src/registrar/assets/src/js/getgov-admin/domain-form.js @@ -1,3 +1,5 @@ +import { handlePortfolioSelection } from './helpers-portfolio-dynamic-fields.js'; + /** * A function that appends target="_blank" to the domain_form buttons */ @@ -28,3 +30,15 @@ export function initDomainFormTargetBlankButtons() { domainSubmitButton.addEventListener("mouseout", () => openInNewTab(domainFormElement, false)); } } + +/** + * A function for dynamic Domain fields +*/ +export function initDynamicDomainFields(){ + const domainPage = document.getElementById("domain_form"); + if (domainPage) { + console.log("handling domain page"); + handlePortfolioSelection("#id_domain_info-0-portfolio", + "#id_domain_info-0-sub_organization"); + } +} diff --git a/src/registrar/assets/src/js/getgov-admin/domain-information-form.js b/src/registrar/assets/src/js/getgov-admin/domain-information-form.js index 072b73720..6c79bd32a 100644 --- a/src/registrar/assets/src/js/getgov-admin/domain-information-form.js +++ b/src/registrar/assets/src/js/getgov-admin/domain-information-form.js @@ -1,11 +1,12 @@ import { handlePortfolioSelection } from './helpers-portfolio-dynamic-fields.js'; /** - * A function for dynamic DomainRequest fields + * A function for dynamic DomainInformation fields */ export function initDynamicDomainInformationFields(){ const domainInformationPage = document.getElementById("domaininformation_form"); if (domainInformationPage) { + console.log("handling domain information page"); handlePortfolioSelection(); } } diff --git a/src/registrar/assets/src/js/getgov-admin/helpers-portfolio-dynamic-fields.js b/src/registrar/assets/src/js/getgov-admin/helpers-portfolio-dynamic-fields.js index ca4c4b44c..64813df86 100644 --- a/src/registrar/assets/src/js/getgov-admin/helpers-portfolio-dynamic-fields.js +++ b/src/registrar/assets/src/js/getgov-admin/helpers-portfolio-dynamic-fields.js @@ -48,10 +48,13 @@ export function handleSuborganizationFields( * * IMPORTANT NOTE: The logic in this method is paired dynamicPortfolioFields */ -export function handlePortfolioSelection() { +export function handlePortfolioSelection( + portfolioDropdownSelector="#id_portfolio", + suborgDropdownSelector="#id_sub_organization" +) { // These dropdown are select2 fields so they must be interacted with via jquery - const portfolioDropdown = django.jQuery("#id_portfolio"); - const suborganizationDropdown = django.jQuery("#id_sub_organization"); + const portfolioDropdown = django.jQuery(portfolioDropdownSelector); + const suborganizationDropdown = django.jQuery(suborgDropdownSelector); const suborganizationField = document.querySelector(".field-sub_organization"); const requestedSuborganizationField = document.querySelector(".field-requested_suborganization"); const suborganizationCity = document.querySelector(".field-suborganization_city"); @@ -440,8 +443,8 @@ export function handlePortfolioSelection() { showElement(portfolioSeniorOfficialField); // Hide fields not applicable when a portfolio is selected - hideElement(otherEmployeesField); - hideElement(noOtherContactsRationaleField); + if (otherEmployeesField) hideElement(otherEmployeesField); + if (noOtherContactsRationaleField) hideElement(noOtherContactsRationaleField); hideElement(cisaRepresentativeFirstNameField); hideElement(cisaRepresentativeLastNameField); hideElement(cisaRepresentativeEmailField); @@ -463,8 +466,8 @@ export function handlePortfolioSelection() { // Show fields that are relevant when no portfolio is selected showElement(seniorOfficialField); hideElement(portfolioSeniorOfficialField); - showElement(otherEmployeesField); - showElement(noOtherContactsRationaleField); + if (otherEmployeesField) showElement(otherEmployeesField); + if (noOtherContactsRationaleField) showElement(noOtherContactsRationaleField); showElement(cisaRepresentativeFirstNameField); showElement(cisaRepresentativeLastNameField); showElement(cisaRepresentativeEmailField); diff --git a/src/registrar/assets/src/js/getgov-admin/main.js b/src/registrar/assets/src/js/getgov-admin/main.js index ec9aeeedf..64be572b2 100644 --- a/src/registrar/assets/src/js/getgov-admin/main.js +++ b/src/registrar/assets/src/js/getgov-admin/main.js @@ -14,6 +14,7 @@ import { import { initDomainFormTargetBlankButtons } from './domain-form.js'; import { initDynamicPortfolioFields } from './portfolio-form.js'; import { initDynamicDomainInformationFields } from './domain-information-form.js'; +import { initDynamicDomainFields } from './domain-form.js'; // General initModals(); @@ -33,6 +34,7 @@ initDynamicDomainRequestFields(); // Domain initDomainFormTargetBlankButtons(); +initDynamicDomainFields(); // Portfolio initDynamicPortfolioFields(); diff --git a/src/registrar/templates/django/admin/domain_change_form.html b/src/registrar/templates/django/admin/domain_change_form.html index 662328660..7aa0034b9 100644 --- a/src/registrar/templates/django/admin/domain_change_form.html +++ b/src/registrar/templates/django/admin/domain_change_form.html @@ -1,6 +1,13 @@ {% extends 'admin/change_form.html' %} {% load i18n static %} +{% block content %} + {% comment %} Stores the json endpoint in a url for easier access {% endcomment %} + {% url 'get-portfolio-json' as url %} + + {{ block.super }} +{% endblock content %} + {% block field_sets %}
From 41c148b15e2518e29e197a9581b06a88c4234aa0 Mon Sep 17 00:00:00 2001 From: Rachid Mrad Date: Tue, 10 Dec 2024 17:59:38 -0500 Subject: [PATCH 04/12] init refactor of portfolio form js --- .../helpers-portfolio-dynamic-fields.js | 70 ++--- .../src/js/getgov-admin/portfolio-form.js | 295 +++++++++--------- 2 files changed, 187 insertions(+), 178 deletions(-) diff --git a/src/registrar/assets/src/js/getgov-admin/helpers-portfolio-dynamic-fields.js b/src/registrar/assets/src/js/getgov-admin/helpers-portfolio-dynamic-fields.js index 64813df86..96c4290e8 100644 --- a/src/registrar/assets/src/js/getgov-admin/helpers-portfolio-dynamic-fields.js +++ b/src/registrar/assets/src/js/getgov-admin/helpers-portfolio-dynamic-fields.js @@ -4,41 +4,41 @@ import { hideElement, showElement } from './helpers-admin.js'; * Helper function that handles business logic for the suborganization field. * Can be used anywhere the suborganization dropdown exists */ -export function handleSuborganizationFields( - portfolioDropdownSelector="#id_portfolio", - suborgDropdownSelector="#id_sub_organization", - requestedSuborgFieldSelector=".field-requested_suborganization", - suborgCitySelector=".field-suborganization_city", - suborgStateTerritorySelector=".field-suborganization_state_territory" -) { - // These dropdown are select2 fields so they must be interacted with via jquery - const portfolioDropdown = django.jQuery(portfolioDropdownSelector) - const suborganizationDropdown = django.jQuery(suborgDropdownSelector) - const requestedSuborgField = document.querySelector(requestedSuborgFieldSelector); - const suborgCity = document.querySelector(suborgCitySelector); - const suborgStateTerritory = document.querySelector(suborgStateTerritorySelector); - if (!suborganizationDropdown || !requestedSuborgField || !suborgCity || !suborgStateTerritory) { - console.error("Requested suborg fields not found."); - return; - } - - function toggleSuborganizationFields() { - if (portfolioDropdown.val() && !suborganizationDropdown.val()) { - if (requestedSuborgField) showElement(requestedSuborgField); - if (suborgCity) showElement(suborgCity); - if (suborgStateTerritory) showElement(suborgStateTerritory); - }else { - if (requestedSuborgField) hideElement(requestedSuborgField); - if (suborgCity) hideElement(suborgCity); - if (suborgStateTerritory) hideElement(suborgStateTerritory); - } - } - - // Run the function once on page startup, then attach an event listener - toggleSuborganizationFields(); - suborganizationDropdown.on("change", toggleSuborganizationFields); - portfolioDropdown.on("change", toggleSuborganizationFields); -} +// export function handleSuborganizationFields( +// portfolioDropdownSelector="#id_portfolio", +// suborgDropdownSelector="#id_sub_organization", +// requestedSuborgFieldSelector=".field-requested_suborganization", +// suborgCitySelector=".field-suborganization_city", +// suborgStateTerritorySelector=".field-suborganization_state_territory" +// ) { +// // These dropdown are select2 fields so they must be interacted with via jquery +// const portfolioDropdown = django.jQuery(portfolioDropdownSelector) +// const suborganizationDropdown = django.jQuery(suborgDropdownSelector) +// const requestedSuborgField = document.querySelector(requestedSuborgFieldSelector); +// const suborgCity = document.querySelector(suborgCitySelector); +// const suborgStateTerritory = document.querySelector(suborgStateTerritorySelector); +// if (!suborganizationDropdown || !requestedSuborgField || !suborgCity || !suborgStateTerritory) { +// console.error("Requested suborg fields not found."); +// return; +// } + +// function toggleSuborganizationFields() { +// if (portfolioDropdown.val() && !suborganizationDropdown.val()) { +// if (requestedSuborgField) showElement(requestedSuborgField); +// if (suborgCity) showElement(suborgCity); +// if (suborgStateTerritory) showElement(suborgStateTerritory); +// }else { +// if (requestedSuborgField) hideElement(requestedSuborgField); +// if (suborgCity) hideElement(suborgCity); +// if (suborgStateTerritory) hideElement(suborgStateTerritory); +// } +// } + +// // Run the function once on page startup, then attach an event listener +// toggleSuborganizationFields(); +// suborganizationDropdown.on("change", toggleSuborganizationFields); +// portfolioDropdown.on("change", toggleSuborganizationFields); +// } /** diff --git a/src/registrar/assets/src/js/getgov-admin/portfolio-form.js b/src/registrar/assets/src/js/getgov-admin/portfolio-form.js index f001bf39b..ada162681 100644 --- a/src/registrar/assets/src/js/getgov-admin/portfolio-form.js +++ b/src/registrar/assets/src/js/getgov-admin/portfolio-form.js @@ -4,69 +4,87 @@ import { hideElement, showElement } from './helpers-admin.js'; * A function for dynamically changing some fields on the portfolio admin model * IMPORTANT NOTE: The logic in this function is paired handlePortfolioSelection and should be refactored once we solidify our requirements. */ -export function initDynamicPortfolioFields(){ +function handlePortfolioFields(){ - // the federal agency change listener fires on page load, which we don't want. - var isInitialPageLoad = true + let isPageLoading = true + let seniorOfficialContactList = document.querySelector(".field-senior_official .dja-address-contact-list"); + const federalAgency = document.querySelector(".field-federal_agency"); + // $ symbolically denotes that this is using jQuery + let $federalAgency = django.jQuery("#id_federal_agency"); + let organizationType = document.getElementById("id_organization_type"); + let readonlyOrganizationType = document.querySelector(".field-organization_type .readonly"); + let organizationName = document.querySelector(".field-organization_name"); + let federalType = document.querySelector(".field-federal_type"); + let urbanization = document.querySelector(".field-urbanization"); + let stateTerritory = document.getElementById("id_state_territory"); + let $seniorOfficial = django.jQuery("#id_senior_official"); + let readonlySeniorOfficial = document.querySelector(".field-senior_official .readonly"); - // This is the additional information that exists beneath the SO element. - var contactList = document.querySelector(".field-senior_official .dja-address-contact-list"); - const federalAgencyContainer = document.querySelector(".field-federal_agency"); - 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"); - let readonlyOrganizationType = document.querySelector(".field-organization_type .readonly"); - - let organizationNameContainer = document.querySelector(".field-organization_name"); - let federalType = document.querySelector(".field-federal_type"); - - if ($federalAgency && (organizationType || readonlyOrganizationType)) { - // Attach the change event listener - $federalAgency.on("change", function() { - handleFederalAgencyChange($federalAgency, organizationType, readonlyOrganizationType, organizationNameContainer, federalType); + function getFederalTypeFromAgency(agency) { + let federalPortfolioApi = document.getElementById("federal_and_portfolio_types_from_agency_json_url").value; + return fetch(`${federalPortfolioApi}?&agency_name=${agency}`) + .then(response => { + const statusCode = response.status; + return response.json().then(data => ({ statusCode, data })); + }) + .then(({ statusCode, data }) => { + if (data.error) { + console.error("Error in AJAX call: " + data.error); + return; + } + return data.federal_type + }) + .catch(error => { + console.error("Error fetching federal and portfolio types: ", error); + return null }); - } - - // 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 getSeniorOfficialFromAgency(agency, seniorOfficialAddUrl) { + let seniorOfficialApi = document.getElementById("senior_official_from_agency_json_url").value; + return fetch(`${seniorOfficialApi}?agency_name=${agency}`) + .then(response => { + const statusCode = response.status; + return response.json().then(data => ({ statusCode, data })); + }) + .then(({ statusCode, data }) => { + if (data.error) { + if (statusCode === 404) { - // Handle hiding the organization name field when the organization_type is federal. - // Run this first one page load, then secondly on a change event. - handleOrganizationTypeChange(organizationType, organizationNameContainer, federalType); - organizationType.addEventListener("change", function() { - handleOrganizationTypeChange(organizationType, organizationNameContainer, federalType); - }); - }); + if ($seniorOfficial && $seniorOfficial.length > 0) { + $seniorOfficial.val("").trigger("change"); + } else { + // Show the "create one now" text if this field is none in readonly mode. + readonlySeniorOfficial.innerHTML = `No senior official found. Create one now.`; + } + console.warn("Record not found: " + data.error); + } else { + console.error("Error in AJAX call: " + data.error); + } + return null; + } else { + return data; + } + }) + .catch(error => { + console.error("Error fetching senior official: ", error) + return null; + }); + } + function handleOrganizationTypeChange(organizationType, organizationNameContainer, federalType) { if (organizationType && organizationNameContainer) { let selectedValue = organizationType.value; if (selectedValue === "federal") { hideElement(organizationNameContainer); - showElement(federalAgencyContainer); + showElement(federalAgency); if (federalType) { showElement(federalType); } } else { showElement(organizationNameContainer); - hideElement(federalAgencyContainer); + hideElement(federalAgency); if (federalType) { hideElement(federalType); } @@ -75,106 +93,62 @@ export function initDynamicPortfolioFields(){ } function handleFederalAgencyChange(federalAgency, organizationType, readonlyOrganizationType, organizationNameContainer, federalType) { - // Don't do anything on page load - if (isInitialPageLoad) { - isInitialPageLoad = false; - return; - } - - // 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; - } - - let organizationTypeValue = organizationType ? organizationType.value : readonlyOrganizationType.innerText.toLowerCase(); - if (selectedText !== "Non-Federal Agency") { - if (organizationTypeValue !== "federal") { - if (organizationType){ - organizationType.value = "federal"; - }else { - readonlyOrganizationType.innerText = "Federal" - } - } - }else { - if (organizationTypeValue === "federal") { - if (organizationType){ - organizationType.value = ""; - }else { - readonlyOrganizationType.innerText = "-" - } - } - } + if (!isPageLoading) { - handleOrganizationTypeChange(organizationType, organizationNameContainer, federalType); - - // Determine if any changes are necessary to the display of portfolio type or federal type - // based on changes to the Federal Agency - let federalPortfolioApi = document.getElementById("federal_and_portfolio_types_from_agency_json_url").value; - fetch(`${federalPortfolioApi}?&agency_name=${selectedText}`) - .then(response => { - const statusCode = response.status; - return response.json().then(data => ({ statusCode, data })); - }) - .then(({ statusCode, data }) => { - if (data.error) { - console.error("Error in AJAX call: " + data.error); + let selectedFederalAgency = federalAgency.find("option:selected").text(); + // There isn't a federal senior official associated with null records + if (!selectedFederalAgency) { return; } - updateReadOnly(data.federal_type, '.field-federal_type'); - }) - .catch(error => console.error("Error fetching federal and portfolio types: ", error)); - // Hide the contactList initially. - // If we can update the contact information, it'll be shown again. - hideElement(contactList.parentElement); - - let seniorOfficialAddUrl = document.getElementById("senior-official-add-url").value; - let $seniorOfficial = django.jQuery("#id_senior_official"); - let readonlySeniorOfficial = document.querySelector(".field-senior_official .readonly"); - 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) { - if ($seniorOfficial && $seniorOfficial.length > 0) { - $seniorOfficial.val("").trigger("change"); + let organizationTypeValue = organizationType ? organizationType.value : readonlyOrganizationType.innerText.toLowerCase(); + if (selectedFederalAgency !== "Non-Federal Agency") { + if (organizationTypeValue !== "federal") { + if (organizationType){ + organizationType.value = "federal"; }else { - // Show the "create one now" text if this field is none in readonly mode. - readonlySeniorOfficial.innerHTML = `No senior official found. Create one now.`; + readonlyOrganizationType.innerText = "Federal" + } + } + } else { + if (organizationTypeValue === "federal") { + if (organizationType){ + organizationType.value = ""; + }else { + readonlyOrganizationType.innerText = "-" } - console.warn("Record not found: " + data.error); - }else { - console.error("Error in AJAX call: " + data.error); } - return; } - // Update the "contact details" blurb beneath senior official - updateContactInfo(data); - showElement(contactList.parentElement); + handleOrganizationTypeChange(organizationType, organizationNameContainer, federalType); + + // Determine if any changes are necessary to the display of portfolio type or federal type + // based on changes to the Federal Agency + getFederalTypeFromAgency(selectedFederalAgency).then((federalType) => updateReadOnly(federalType, '.field-federal_type')); - // Get the associated senior official with this federal agency - let seniorOfficialId = data.id; - let seniorOfficialName = [data.first_name, data.last_name].join(" "); - if ($seniorOfficial && $seniorOfficial.length > 0) { - // If the senior official is a dropdown field, edit that - updateSeniorOfficialDropdown($seniorOfficial, seniorOfficialId, seniorOfficialName); - }else { - if (readonlySeniorOfficial) { - let seniorOfficialLink = `${seniorOfficialName}` - readonlySeniorOfficial.innerHTML = seniorOfficialName ? seniorOfficialLink : "-"; + hideElement(seniorOfficialContactList.parentElement); + let seniorOfficialAddUrl = document.getElementById("senior-official-add-url").value; + getSeniorOfficialFromAgency(selectedFederalAgency, seniorOfficialAddUrl).then((data) => { + // Update the "contact details" blurb beneath senior official + updateContactInfo(data); + showElement(seniorOfficialContactList.parentElement); + // Get the associated senior official with this federal agency + let seniorOfficialId = data.id; + let seniorOfficialName = [data.first_name, data.last_name].join(" "); + if ($seniorOfficial && $seniorOfficial.length > 0) { + // If the senior official is a dropdown field, edit that + updateSeniorOfficialDropdown($seniorOfficial, seniorOfficialId, seniorOfficialName); + }else { + if (readonlySeniorOfficial) { + let seniorOfficialLink = `${seniorOfficialName}` + readonlySeniorOfficial.innerHTML = seniorOfficialName ? seniorOfficialLink : "-"; + } } - } - }) - .catch(error => console.error("Error fetching senior official: ", error)); + }); + + } else { + isPageLoading = false; + } } @@ -184,7 +158,6 @@ export function initDynamicPortfolioFields(){ dropdown.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 (dropdown.find(`option[value='${seniorOfficialId}']`).length) { @@ -227,11 +200,11 @@ export function initDynamicPortfolioFields(){ } function updateContactInfo(data) { - if (!contactList) return; + if (!seniorOfficialContactList) return; - const titleSpan = contactList.querySelector(".contact_info_title"); - const emailSpan = contactList.querySelector(".contact_info_email"); - const phoneSpan = contactList.querySelector(".contact_info_phone"); + const titleSpan = seniorOfficialContactList.querySelector(".contact_info_title"); + const emailSpan = seniorOfficialContactList.querySelector(".contact_info_email"); + const phoneSpan = seniorOfficialContactList.querySelector(".contact_info_phone"); if (titleSpan) { titleSpan.textContent = data.title || "None"; @@ -239,10 +212,10 @@ export function initDynamicPortfolioFields(){ // Update the email field and the content for the clipboard if (emailSpan) { - let copyButton = contactList.querySelector(".admin-icon-group"); + let copyButton = seniorOfficialContactList.querySelector(".admin-icon-group"); emailSpan.textContent = data.email || "None"; if (data.email) { - const clipboardInput = contactList.querySelector(".admin-icon-group input"); + const clipboardInput = seniorOfficialContactList.querySelector(".admin-icon-group input"); if (clipboardInput) { clipboardInput.value = data.email; }; @@ -256,4 +229,40 @@ export function initDynamicPortfolioFields(){ phoneSpan.textContent = data.phone || "None"; }; } + + function initializePortfolioSettings() { + if (urbanization && stateTerritory) { + handleStateTerritoryChange(stateTerritory, urbanization); + } + handleOrganizationTypeChange(organizationType, organizationName, federalType); + } + + function setEventListeners() { + if ($federalAgency && (organizationType || readonlyOrganizationType)) { + $federalAgency.on("change", function() { + handleFederalAgencyChange($federalAgency, organizationType, readonlyOrganizationType, organizationName, federalType); + }); + } + if (urbanization && stateTerritory) { + stateTerritory.addEventListener("change", function() { + handleStateTerritoryChange(stateTerritory, urbanization); + }); + } + organizationType.addEventListener("change", function() { + handleOrganizationTypeChange(organizationType, organizationName, federalType); + }); + } + + // Run initial setup functions + initializePortfolioSettings(); + setEventListeners(); +} + +export function initPortfolioFields() { + document.addEventListener('DOMContentLoaded', function() { + let isPortfolioPage = document.getElementById("portfolio_form"); + if (isPortfolioPage) { + handlePortfolioFields(); + } + }); } From 7f0dc4bfca0bbc98f157d8904c80e930bcefac7f Mon Sep 17 00:00:00 2001 From: David Kennedy Date: Wed, 11 Dec 2024 11:29:20 -0500 Subject: [PATCH 05/12] updated handling of error from lookup api, plus some variable naming --- .../src/js/getgov-admin/portfolio-form.js | 81 ++++++++++--------- 1 file changed, 43 insertions(+), 38 deletions(-) diff --git a/src/registrar/assets/src/js/getgov-admin/portfolio-form.js b/src/registrar/assets/src/js/getgov-admin/portfolio-form.js index ada162681..5c34c8fce 100644 --- a/src/registrar/assets/src/js/getgov-admin/portfolio-form.js +++ b/src/registrar/assets/src/js/getgov-admin/portfolio-form.js @@ -7,18 +7,21 @@ import { hideElement, showElement } from './helpers-admin.js'; function handlePortfolioFields(){ let isPageLoading = true - let seniorOfficialContactList = document.querySelector(".field-senior_official .dja-address-contact-list"); - const federalAgency = document.querySelector(".field-federal_agency"); // $ symbolically denotes that this is using jQuery - let $federalAgency = django.jQuery("#id_federal_agency"); + const $seniorOfficial = django.jQuery("#id_senior_official"); + const seniorOfficialField = document.querySelector(".field-senior_official"); + const seniorOfficialAddress = seniorOfficialField.querySelector(".dja-address-contact-list"); + const seniorOfficialReadonly = seniorOfficialField.querySelector(".readonly"); + const $federalAgency = django.jQuery("#id_federal_agency"); + const federalAgencyField = document.querySelector(".field-federal_agency"); let organizationType = document.getElementById("id_organization_type"); let readonlyOrganizationType = document.querySelector(".field-organization_type .readonly"); let organizationName = document.querySelector(".field-organization_name"); let federalType = document.querySelector(".field-federal_type"); let urbanization = document.querySelector(".field-urbanization"); let stateTerritory = document.getElementById("id_state_territory"); - let $seniorOfficial = django.jQuery("#id_senior_official"); - let readonlySeniorOfficial = document.querySelector(".field-senior_official .readonly"); + + const seniorOfficialAddUrl = document.getElementById("senior-official-add-url").value; function getFederalTypeFromAgency(agency) { let federalPortfolioApi = document.getElementById("federal_and_portfolio_types_from_agency_json_url").value; @@ -40,8 +43,9 @@ function handlePortfolioFields(){ }); } - function getSeniorOfficialFromAgency(agency, seniorOfficialAddUrl) { - let seniorOfficialApi = document.getElementById("senior_official_from_agency_json_url").value; + function getSeniorOfficialFromAgency(agency) { + const seniorOfficialApi = document.getElementById("senior_official_from_agency_json_url").value; + return fetch(`${seniorOfficialApi}?agency_name=${agency}`) .then(response => { const statusCode = response.status; @@ -49,27 +53,15 @@ function handlePortfolioFields(){ }) .then(({ statusCode, data }) => { if (data.error) { - if (statusCode === 404) { - - if ($seniorOfficial && $seniorOfficial.length > 0) { - $seniorOfficial.val("").trigger("change"); - } else { - // Show the "create one now" text if this field is none in readonly mode. - readonlySeniorOfficial.innerHTML = `No senior official found. Create one now.`; - } - - console.warn("Record not found: " + data.error); - } else { - console.error("Error in AJAX call: " + data.error); - } - return null; + // Throw an error with status code and message + throw { statusCode, message: data.error }; } else { return data; } }) .catch(error => { - console.error("Error fetching senior official: ", error) - return null; + console.error("Error fetching senior official: ", error); + throw error; // Re-throw for external handling }); } @@ -78,13 +70,13 @@ function handlePortfolioFields(){ let selectedValue = organizationType.value; if (selectedValue === "federal") { hideElement(organizationNameContainer); - showElement(federalAgency); + showElement(federalAgencyField); if (federalType) { showElement(federalType); } } else { showElement(organizationNameContainer); - hideElement(federalAgency); + hideElement(federalAgencyField); if (federalType) { hideElement(federalType); } @@ -126,12 +118,12 @@ function handlePortfolioFields(){ // based on changes to the Federal Agency getFederalTypeFromAgency(selectedFederalAgency).then((federalType) => updateReadOnly(federalType, '.field-federal_type')); - hideElement(seniorOfficialContactList.parentElement); - let seniorOfficialAddUrl = document.getElementById("senior-official-add-url").value; - getSeniorOfficialFromAgency(selectedFederalAgency, seniorOfficialAddUrl).then((data) => { + hideElement(seniorOfficialAddress.parentElement); + + getSeniorOfficialFromAgency(selectedFederalAgency).then((data) => { // Update the "contact details" blurb beneath senior official updateContactInfo(data); - showElement(seniorOfficialContactList.parentElement); + showElement(seniorOfficialAddress.parentElement); // Get the associated senior official with this federal agency let seniorOfficialId = data.id; let seniorOfficialName = [data.first_name, data.last_name].join(" "); @@ -139,11 +131,24 @@ function handlePortfolioFields(){ // If the senior official is a dropdown field, edit that updateSeniorOfficialDropdown($seniorOfficial, seniorOfficialId, seniorOfficialName); }else { - if (readonlySeniorOfficial) { + if (seniorOfficialReadonly) { let seniorOfficialLink = `${seniorOfficialName}` - readonlySeniorOfficial.innerHTML = seniorOfficialName ? seniorOfficialLink : "-"; + seniorOfficialReadonly.innerHTML = seniorOfficialName ? seniorOfficialLink : "-"; } } + }) + .catch(error => { + if (error.statusCode === 404) { + // Handle "not found" senior official + if ($seniorOfficial && $seniorOfficial.length > 0) { + $seniorOfficial.val("").trigger("change"); + } else { + seniorOfficialReadonly.innerHTML = `No senior official found. Create one now.`; + } + } else { + // Handle other errors + console.error("An error occurred:", error.message); + } }); } else { @@ -200,11 +205,11 @@ function handlePortfolioFields(){ } function updateContactInfo(data) { - if (!seniorOfficialContactList) return; + if (!seniorOfficialAddress) return; - const titleSpan = seniorOfficialContactList.querySelector(".contact_info_title"); - const emailSpan = seniorOfficialContactList.querySelector(".contact_info_email"); - const phoneSpan = seniorOfficialContactList.querySelector(".contact_info_phone"); + const titleSpan = seniorOfficialAddress.querySelector(".contact_info_title"); + const emailSpan = seniorOfficialAddress.querySelector(".contact_info_email"); + const phoneSpan = seniorOfficialAddress.querySelector(".contact_info_phone"); if (titleSpan) { titleSpan.textContent = data.title || "None"; @@ -212,10 +217,10 @@ function handlePortfolioFields(){ // Update the email field and the content for the clipboard if (emailSpan) { - let copyButton = seniorOfficialContactList.querySelector(".admin-icon-group"); + let copyButton = seniorOfficialAddress.querySelector(".admin-icon-group"); emailSpan.textContent = data.email || "None"; if (data.email) { - const clipboardInput = seniorOfficialContactList.querySelector(".admin-icon-group input"); + const clipboardInput = seniorOfficialAddress.querySelector(".admin-icon-group input"); if (clipboardInput) { clipboardInput.value = data.email; }; @@ -258,7 +263,7 @@ function handlePortfolioFields(){ setEventListeners(); } -export function initPortfolioFields() { +export function initDynamicPortfolioFields() { document.addEventListener('DOMContentLoaded', function() { let isPortfolioPage = document.getElementById("portfolio_form"); if (isPortfolioPage) { From b85bfd7244f9ac791dc8557ba421e404027d0305 Mon Sep 17 00:00:00 2001 From: David Kennedy Date: Wed, 11 Dec 2024 12:02:44 -0500 Subject: [PATCH 06/12] wip variable name changes, and removing params from methods, and inheriting from global consts --- .../src/js/getgov-admin/portfolio-form.js | 61 ++++++++++--------- 1 file changed, 31 insertions(+), 30 deletions(-) diff --git a/src/registrar/assets/src/js/getgov-admin/portfolio-form.js b/src/registrar/assets/src/js/getgov-admin/portfolio-form.js index 5c34c8fce..b1c85a527 100644 --- a/src/registrar/assets/src/js/getgov-admin/portfolio-form.js +++ b/src/registrar/assets/src/js/getgov-admin/portfolio-form.js @@ -14,13 +14,14 @@ function handlePortfolioFields(){ const seniorOfficialReadonly = seniorOfficialField.querySelector(".readonly"); const $federalAgency = django.jQuery("#id_federal_agency"); const federalAgencyField = document.querySelector(".field-federal_agency"); - let organizationType = document.getElementById("id_organization_type"); - let readonlyOrganizationType = document.querySelector(".field-organization_type .readonly"); - let organizationName = document.querySelector(".field-organization_name"); - let federalType = document.querySelector(".field-federal_type"); - let urbanization = document.querySelector(".field-urbanization"); - let stateTerritory = document.getElementById("id_state_territory"); - + const organizationTypeField = document.querySelector(".field-organization_type"); + const organizationTypeReadonly = organizationTypeField.querySelector(".readonly"); + const organizationTypeDropdown = document.getElementById("id_organization_type"); + const organizationNameField = document.querySelector(".field-organization_name"); + const federalTypeField = document.querySelector(".field-federal_type"); + const urbanizationField = document.querySelector(".field-urbanization"); + const stateTerritoryDropdown = document.getElementById("id_state_territory"); + // consts for different urls const seniorOfficialAddUrl = document.getElementById("senior-official-add-url").value; function getFederalTypeFromAgency(agency) { @@ -65,26 +66,26 @@ function handlePortfolioFields(){ }); } - function handleOrganizationTypeChange(organizationType, organizationNameContainer, federalType) { - if (organizationType && organizationNameContainer) { - let selectedValue = organizationType.value; + function handleOrganizationTypeChange() { + if (organizationTypeDropdown && organizationNameField) { + let selectedValue = organizationTypeDropdown.value; if (selectedValue === "federal") { - hideElement(organizationNameContainer); + hideElement(organizationNameField); showElement(federalAgencyField); - if (federalType) { - showElement(federalType); + if (federalTypeField) { + showElement(federalTypeField); } } else { - showElement(organizationNameContainer); + showElement(organizationNameField); hideElement(federalAgencyField); - if (federalType) { - hideElement(federalType); + if (federalTypeField) { + hideElement(federalTypeField); } } } } - function handleFederalAgencyChange(federalAgency, organizationType, readonlyOrganizationType, organizationNameContainer, federalType) { + function handleFederalAgencyChange(federalAgency, organizationType, readonlyOrganizationType) { if (!isPageLoading) { let selectedFederalAgency = federalAgency.find("option:selected").text(); @@ -112,7 +113,7 @@ function handlePortfolioFields(){ } } - handleOrganizationTypeChange(organizationType, organizationNameContainer, federalType); + handleOrganizationTypeChange(); // Determine if any changes are necessary to the display of portfolio type or federal type // based on changes to the Federal Agency @@ -175,8 +176,8 @@ function handlePortfolioFields(){ } } - function handleStateTerritoryChange(stateTerritory, urbanizationField) { - let selectedValue = stateTerritory.value; + function handleStateTerritoryChange() { + let selectedValue = stateTerritoryDropdown.value; if (selectedValue === "PR") { showElement(urbanizationField) } else { @@ -236,25 +237,25 @@ function handlePortfolioFields(){ } function initializePortfolioSettings() { - if (urbanization && stateTerritory) { - handleStateTerritoryChange(stateTerritory, urbanization); + if (urbanizationField && stateTerritoryDropdown) { + handleStateTerritoryChange(); } - handleOrganizationTypeChange(organizationType, organizationName, federalType); + handleOrganizationTypeChange(); } function setEventListeners() { - if ($federalAgency && (organizationType || readonlyOrganizationType)) { + if ($federalAgency && (organizationTypeDropdown || organizationTypeReadonly)) { $federalAgency.on("change", function() { - handleFederalAgencyChange($federalAgency, organizationType, readonlyOrganizationType, organizationName, federalType); + handleFederalAgencyChange($federalAgency, organizationTypeDropdown, organizationTypeReadonly); }); } - if (urbanization && stateTerritory) { - stateTerritory.addEventListener("change", function() { - handleStateTerritoryChange(stateTerritory, urbanization); + if (urbanizationField && stateTerritoryDropdown) { + stateTerritoryDropdown.addEventListener("change", function() { + handleStateTerritoryChange(); }); } - organizationType.addEventListener("change", function() { - handleOrganizationTypeChange(organizationType, organizationName, federalType); + organizationTypeDropdown.addEventListener("change", function() { + handleOrganizationTypeChange(); }); } From ee142a40964df37f460dce0503c93efc4c6d6f1a Mon Sep 17 00:00:00 2001 From: Rachid Mrad Date: Wed, 11 Dec 2024 12:36:15 -0500 Subject: [PATCH 07/12] removed some more params --- .../assets/src/js/getgov-admin/portfolio-form.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/registrar/assets/src/js/getgov-admin/portfolio-form.js b/src/registrar/assets/src/js/getgov-admin/portfolio-form.js index b1c85a527..92f57712b 100644 --- a/src/registrar/assets/src/js/getgov-admin/portfolio-form.js +++ b/src/registrar/assets/src/js/getgov-admin/portfolio-form.js @@ -85,7 +85,7 @@ function handlePortfolioFields(){ } } - function handleFederalAgencyChange(federalAgency, organizationType, readonlyOrganizationType) { + function handleFederalAgencyChange() { if (!isPageLoading) { let selectedFederalAgency = federalAgency.find("option:selected").text(); @@ -99,7 +99,7 @@ function handlePortfolioFields(){ if (organizationTypeValue !== "federal") { if (organizationType){ organizationType.value = "federal"; - }else { + } else { readonlyOrganizationType.innerText = "Federal" } } @@ -107,7 +107,7 @@ function handlePortfolioFields(){ if (organizationTypeValue === "federal") { if (organizationType){ organizationType.value = ""; - }else { + } else { readonlyOrganizationType.innerText = "-" } } @@ -131,7 +131,7 @@ function handlePortfolioFields(){ if ($seniorOfficial && $seniorOfficial.length > 0) { // If the senior official is a dropdown field, edit that updateSeniorOfficialDropdown($seniorOfficial, seniorOfficialId, seniorOfficialName); - }else { + } else { if (seniorOfficialReadonly) { let seniorOfficialLink = `${seniorOfficialName}` seniorOfficialReadonly.innerHTML = seniorOfficialName ? seniorOfficialLink : "-"; @@ -246,7 +246,7 @@ function handlePortfolioFields(){ function setEventListeners() { if ($federalAgency && (organizationTypeDropdown || organizationTypeReadonly)) { $federalAgency.on("change", function() { - handleFederalAgencyChange($federalAgency, organizationTypeDropdown, organizationTypeReadonly); + handleFederalAgencyChange(); }); } if (urbanizationField && stateTerritoryDropdown) { From 058d42f0140975b8915d5b6f9efe59d6c99cc97c Mon Sep 17 00:00:00 2001 From: Rachid Mrad Date: Wed, 11 Dec 2024 12:51:02 -0500 Subject: [PATCH 08/12] fix misnamed references --- .../src/js/getgov-admin/portfolio-form.js | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/registrar/assets/src/js/getgov-admin/portfolio-form.js b/src/registrar/assets/src/js/getgov-admin/portfolio-form.js index 92f57712b..9422c95f6 100644 --- a/src/registrar/assets/src/js/getgov-admin/portfolio-form.js +++ b/src/registrar/assets/src/js/getgov-admin/portfolio-form.js @@ -12,7 +12,7 @@ function handlePortfolioFields(){ const seniorOfficialField = document.querySelector(".field-senior_official"); const seniorOfficialAddress = seniorOfficialField.querySelector(".dja-address-contact-list"); const seniorOfficialReadonly = seniorOfficialField.querySelector(".readonly"); - const $federalAgency = django.jQuery("#id_federal_agency"); + const $federalAgencyDropdown = django.jQuery("#id_federal_agency"); const federalAgencyField = document.querySelector(".field-federal_agency"); const organizationTypeField = document.querySelector(".field-organization_type"); const organizationTypeReadonly = organizationTypeField.querySelector(".readonly"); @@ -88,39 +88,39 @@ function handlePortfolioFields(){ function handleFederalAgencyChange() { if (!isPageLoading) { - let selectedFederalAgency = federalAgency.find("option:selected").text(); - // There isn't a federal senior official associated with null records + let selectedFederalAgency = $federalAgencyDropdown.find("option:selected").text(); if (!selectedFederalAgency) { return; } - let organizationTypeValue = organizationType ? organizationType.value : readonlyOrganizationType.innerText.toLowerCase(); + // 1. Handle organization type + let organizationTypeValue = organizationTypeDropdown ? organizationTypeDropdown.value : organizationTypeReadonly.innerText.toLowerCase(); if (selectedFederalAgency !== "Non-Federal Agency") { if (organizationTypeValue !== "federal") { - if (organizationType){ - organizationType.value = "federal"; + if (organizationTypeDropdown){ + organizationTypeDropdown.value = "federal"; } else { - readonlyOrganizationType.innerText = "Federal" + organizationTypeReadonly.innerText = "Federal" } } } else { if (organizationTypeValue === "federal") { - if (organizationType){ - organizationType.value = ""; + if (organizationTypeDropdown){ + organizationTypeDropdown.value = ""; } else { - readonlyOrganizationType.innerText = "-" + organizationTypeReadonly.innerText = "-" } } } + // 2. Handle organization type change side effects handleOrganizationTypeChange(); - // Determine if any changes are necessary to the display of portfolio type or federal type - // based on changes to the Federal Agency + // 3. Handle federal type getFederalTypeFromAgency(selectedFederalAgency).then((federalType) => updateReadOnly(federalType, '.field-federal_type')); + // 4. Handle senior official hideElement(seniorOfficialAddress.parentElement); - getSeniorOfficialFromAgency(selectedFederalAgency).then((data) => { // Update the "contact details" blurb beneath senior official updateContactInfo(data); @@ -244,8 +244,8 @@ function handlePortfolioFields(){ } function setEventListeners() { - if ($federalAgency && (organizationTypeDropdown || organizationTypeReadonly)) { - $federalAgency.on("change", function() { + if ($federalAgencyDropdown && (organizationTypeDropdown || organizationTypeReadonly)) { + $federalAgencyDropdown.on("change", function() { handleFederalAgencyChange(); }); } From 591cc0ee7546eddcb6ee3ac76b12f6ad506bdd09 Mon Sep 17 00:00:00 2001 From: Rachid Mrad Date: Wed, 11 Dec 2024 13:04:18 -0500 Subject: [PATCH 09/12] clean up JS and add comments --- .../helpers-portfolio-dynamic-fields.js | 43 +------------- .../src/js/getgov-admin/portfolio-form.js | 56 ++++++++++++++++--- 2 files changed, 48 insertions(+), 51 deletions(-) diff --git a/src/registrar/assets/src/js/getgov-admin/helpers-portfolio-dynamic-fields.js b/src/registrar/assets/src/js/getgov-admin/helpers-portfolio-dynamic-fields.js index 96c4290e8..0e5946c23 100644 --- a/src/registrar/assets/src/js/getgov-admin/helpers-portfolio-dynamic-fields.js +++ b/src/registrar/assets/src/js/getgov-admin/helpers-portfolio-dynamic-fields.js @@ -1,52 +1,11 @@ import { hideElement, showElement } from './helpers-admin.js'; -/** - * Helper function that handles business logic for the suborganization field. - * Can be used anywhere the suborganization dropdown exists -*/ -// export function handleSuborganizationFields( -// portfolioDropdownSelector="#id_portfolio", -// suborgDropdownSelector="#id_sub_organization", -// requestedSuborgFieldSelector=".field-requested_suborganization", -// suborgCitySelector=".field-suborganization_city", -// suborgStateTerritorySelector=".field-suborganization_state_territory" -// ) { -// // These dropdown are select2 fields so they must be interacted with via jquery -// const portfolioDropdown = django.jQuery(portfolioDropdownSelector) -// const suborganizationDropdown = django.jQuery(suborgDropdownSelector) -// const requestedSuborgField = document.querySelector(requestedSuborgFieldSelector); -// const suborgCity = document.querySelector(suborgCitySelector); -// const suborgStateTerritory = document.querySelector(suborgStateTerritorySelector); -// if (!suborganizationDropdown || !requestedSuborgField || !suborgCity || !suborgStateTerritory) { -// console.error("Requested suborg fields not found."); -// return; -// } - -// function toggleSuborganizationFields() { -// if (portfolioDropdown.val() && !suborganizationDropdown.val()) { -// if (requestedSuborgField) showElement(requestedSuborgField); -// if (suborgCity) showElement(suborgCity); -// if (suborgStateTerritory) showElement(suborgStateTerritory); -// }else { -// if (requestedSuborgField) hideElement(requestedSuborgField); -// if (suborgCity) hideElement(suborgCity); -// if (suborgStateTerritory) hideElement(suborgStateTerritory); -// } -// } - -// // Run the function once on page startup, then attach an event listener -// toggleSuborganizationFields(); -// suborganizationDropdown.on("change", toggleSuborganizationFields); -// portfolioDropdown.on("change", toggleSuborganizationFields); -// } - - /** * * This function handles the portfolio selection as well as display of * portfolio-related fields in the DomainRequest Form. * - * IMPORTANT NOTE: The logic in this method is paired dynamicPortfolioFields + * IMPORTANT NOTE: The business logic in this method is based on dynamicPortfolioFields */ export function handlePortfolioSelection( portfolioDropdownSelector="#id_portfolio", diff --git a/src/registrar/assets/src/js/getgov-admin/portfolio-form.js b/src/registrar/assets/src/js/getgov-admin/portfolio-form.js index 9422c95f6..1d73e2bd5 100644 --- a/src/registrar/assets/src/js/getgov-admin/portfolio-form.js +++ b/src/registrar/assets/src/js/getgov-admin/portfolio-form.js @@ -2,7 +2,7 @@ import { hideElement, showElement } from './helpers-admin.js'; /** * A function for dynamically changing some fields on the portfolio admin model - * IMPORTANT NOTE: The logic in this function is paired handlePortfolioSelection and should be refactored once we solidify our requirements. + * IMPORTANT NOTE: The business logic in this function is related to handlePortfolioSelection */ function handlePortfolioFields(){ @@ -21,9 +21,15 @@ function handlePortfolioFields(){ const federalTypeField = document.querySelector(".field-federal_type"); const urbanizationField = document.querySelector(".field-urbanization"); const stateTerritoryDropdown = document.getElementById("id_state_territory"); - // consts for different urls const seniorOfficialAddUrl = document.getElementById("senior-official-add-url").value; + /** + * Fetches federal type data based on a selected agency using an AJAX call. + * + * @param {string} agency + * @returns {Promise} - A promise that resolves to the portfolio data object if successful, + * or null if there was an error. + */ function getFederalTypeFromAgency(agency) { let federalPortfolioApi = document.getElementById("federal_and_portfolio_types_from_agency_json_url").value; return fetch(`${federalPortfolioApi}?&agency_name=${agency}`) @@ -44,6 +50,13 @@ function handlePortfolioFields(){ }); } + /** + * Fetches senior official contact data based on a selected agency using an AJAX call. + * + * @param {string} agency + * @returns {Promise} - A promise that resolves to the portfolio data object if successful, + * or null if there was an error. + */ function getSeniorOfficialFromAgency(agency) { const seniorOfficialApi = document.getElementById("senior_official_from_agency_json_url").value; @@ -66,6 +79,12 @@ function handlePortfolioFields(){ }); } + /** + * Handles the side effects of change on the organization type field + * + * 1. If selection is federal, hide org name, show federal agency, show federal type if applicable + * 2. else show org name, hide federal agency, hide federal type if applicable + */ function handleOrganizationTypeChange() { if (organizationTypeDropdown && organizationNameField) { let selectedValue = organizationTypeDropdown.value; @@ -85,6 +104,14 @@ function handlePortfolioFields(){ } } + /** + * Handles the side effects of change on the federal agency field + * + * 1. handle org type dropdown or readonly + * 2. call handleOrganizationTypeChange + * 3. call getFederalTypeFromAgency and update federal type + * 4. call getSeniorOfficialFromAgency and update the SO fieldset + */ function handleFederalAgencyChange() { if (!isPageLoading) { @@ -123,7 +150,7 @@ function handlePortfolioFields(){ hideElement(seniorOfficialAddress.parentElement); getSeniorOfficialFromAgency(selectedFederalAgency).then((data) => { // Update the "contact details" blurb beneath senior official - updateContactInfo(data); + updateSeniorOfficialContactInfo(data); showElement(seniorOfficialAddress.parentElement); // Get the associated senior official with this federal agency let seniorOfficialId = data.id; @@ -158,6 +185,9 @@ function handlePortfolioFields(){ } + /** + * Helper for updating federal type field + */ function updateSeniorOfficialDropdown(dropdown, seniorOfficialId, seniorOfficialName) { if (!seniorOfficialId || !seniorOfficialName || !seniorOfficialName.trim()){ // Clear the field if the SO doesn't exist @@ -176,6 +206,9 @@ function handlePortfolioFields(){ } } + /** + * Handle urbanization + */ function handleStateTerritoryChange() { let selectedValue = stateTerritoryDropdown.value; if (selectedValue === "PR") { @@ -186,11 +219,7 @@ function handlePortfolioFields(){ } /** - * Utility that selects a div from the DOM using selectorString, - * and updates a div within that div which has class of 'readonly' - * so that the text of the div is updated to updateText - * @param {*} updateText - * @param {*} selectorString + * Helper for updating senior official dropdown */ function updateReadOnly(updateText, selectorString) { // find the div by selectorString @@ -205,7 +234,10 @@ function handlePortfolioFields(){ } } - function updateContactInfo(data) { + /** + * Helper for updating senior official contact info + */ + function updateSeniorOfficialContactInfo(data) { if (!seniorOfficialAddress) return; const titleSpan = seniorOfficialAddress.querySelector(".contact_info_title"); @@ -236,6 +268,9 @@ function handlePortfolioFields(){ }; } + /** + * Initializes necessary data and display configurations for the portfolio fields. + */ function initializePortfolioSettings() { if (urbanizationField && stateTerritoryDropdown) { handleStateTerritoryChange(); @@ -243,6 +278,9 @@ function handlePortfolioFields(){ handleOrganizationTypeChange(); } + /** + * Sets event listeners for key UI elements. + */ function setEventListeners() { if ($federalAgencyDropdown && (organizationTypeDropdown || organizationTypeReadonly)) { $federalAgencyDropdown.on("change", function() { From f599d4fb67d8edf70bf410d1139d8b83df412eb5 Mon Sep 17 00:00:00 2001 From: Rachid Mrad Date: Wed, 11 Dec 2024 13:09:17 -0500 Subject: [PATCH 10/12] lint --- src/registrar/admin.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/registrar/admin.py b/src/registrar/admin.py index 76c935f96..f7fd4991c 100644 --- a/src/registrar/admin.py +++ b/src/registrar/admin.py @@ -1603,7 +1603,6 @@ def portfolio_urbanization(self, obj): portfolio_urbanization.short_description = "Urbanization" # type: ignore - # Filters list_filter = [GenericOrgFilter] @@ -1635,7 +1634,7 @@ def portfolio_urbanization(self, obj): ), (".gov domain", {"fields": ["domain"]}), ( - "Contacts", + "Contacts", { "fields": [ "senior_official", @@ -1746,7 +1745,7 @@ def portfolio_urbanization(self, obj): "portfolio_zipcode", "portfolio_urbanization", "other_contacts", - "is_election_board" + "is_election_board", ) # Read only that we'll leverage for CISA Analysts From bc6c756aab85f6efcf0de2afaa08091d093fccd1 Mon Sep 17 00:00:00 2001 From: Rachid Mrad Date: Wed, 11 Dec 2024 13:36:54 -0500 Subject: [PATCH 11/12] Fix unit tests --- src/registrar/tests/test_admin.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index 8307163c6..a259e5bef 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -853,9 +853,9 @@ def test_contact_fields_have_detail_table(self): self.test_helper.assert_response_contains_distinct_values(response, expected_other_employees_fields) # Test for the copy link - # We expect 3 in the form + 2 from the js module copy-to-clipboard.js + # We expect 4 in the form + 2 from the js module copy-to-clipboard.js # that gets pulled in the test in django.contrib.staticfiles.finders.FileSystemFinder - self.assertContains(response, "copy-to-clipboard", count=5) + self.assertContains(response, "copy-to-clipboard", count=6) # cleanup this test domain_info.delete() @@ -871,6 +871,17 @@ def test_readonly_fields_for_analyst(self): readonly_fields = self.admin.get_readonly_fields(request) expected_fields = [ + "portfolio_senior_official", + "portfolio_organization_type", + "portfolio_federal_type", + "portfolio_organization_name", + "portfolio_federal_agency", + "portfolio_state_territory", + "portfolio_address_line1", + "portfolio_address_line2", + "portfolio_city", + "portfolio_zipcode", + "portfolio_urbanization", "other_contacts", "is_election_board", "federal_agency", From 30c15fa817394d98fb911fd1805f2926827a4bf2 Mon Sep 17 00:00:00 2001 From: David Kennedy Date: Wed, 11 Dec 2024 14:38:14 -0500 Subject: [PATCH 12/12] js cleanup --- .../assets/src/js/getgov-admin/domain-form.js | 1 - .../src/js/getgov-admin/portfolio-form.js | 53 ++++++++----------- 2 files changed, 23 insertions(+), 31 deletions(-) diff --git a/src/registrar/assets/src/js/getgov-admin/domain-form.js b/src/registrar/assets/src/js/getgov-admin/domain-form.js index 8c5ab5b1c..5335bd0d3 100644 --- a/src/registrar/assets/src/js/getgov-admin/domain-form.js +++ b/src/registrar/assets/src/js/getgov-admin/domain-form.js @@ -37,7 +37,6 @@ export function initDomainFormTargetBlankButtons() { export function initDynamicDomainFields(){ const domainPage = document.getElementById("domain_form"); if (domainPage) { - console.log("handling domain page"); handlePortfolioSelection("#id_domain_info-0-portfolio", "#id_domain_info-0-sub_organization"); } diff --git a/src/registrar/assets/src/js/getgov-admin/portfolio-form.js b/src/registrar/assets/src/js/getgov-admin/portfolio-form.js index 1d73e2bd5..74729c2b2 100644 --- a/src/registrar/assets/src/js/getgov-admin/portfolio-form.js +++ b/src/registrar/assets/src/js/getgov-admin/portfolio-form.js @@ -8,7 +8,7 @@ function handlePortfolioFields(){ let isPageLoading = true // $ symbolically denotes that this is using jQuery - const $seniorOfficial = django.jQuery("#id_senior_official"); + const $seniorOfficialDropdown = django.jQuery("#id_senior_official"); const seniorOfficialField = document.querySelector(".field-senior_official"); const seniorOfficialAddress = seniorOfficialField.querySelector(".dja-address-contact-list"); const seniorOfficialReadonly = seniorOfficialField.querySelector(".readonly"); @@ -22,6 +22,8 @@ function handlePortfolioFields(){ const urbanizationField = document.querySelector(".field-urbanization"); const stateTerritoryDropdown = document.getElementById("id_state_territory"); const seniorOfficialAddUrl = document.getElementById("senior-official-add-url").value; + const seniorOfficialApi = document.getElementById("senior_official_from_agency_json_url").value; + const federalPortfolioApi = document.getElementById("federal_and_portfolio_types_from_agency_json_url").value; /** * Fetches federal type data based on a selected agency using an AJAX call. @@ -31,7 +33,6 @@ function handlePortfolioFields(){ * or null if there was an error. */ function getFederalTypeFromAgency(agency) { - let federalPortfolioApi = document.getElementById("federal_and_portfolio_types_from_agency_json_url").value; return fetch(`${federalPortfolioApi}?&agency_name=${agency}`) .then(response => { const statusCode = response.status; @@ -58,8 +59,6 @@ function handlePortfolioFields(){ * or null if there was an error. */ function getSeniorOfficialFromAgency(agency) { - const seniorOfficialApi = document.getElementById("senior_official_from_agency_json_url").value; - return fetch(`${seniorOfficialApi}?agency_name=${agency}`) .then(response => { const statusCode = response.status; @@ -148,16 +147,16 @@ function handlePortfolioFields(){ // 4. Handle senior official hideElement(seniorOfficialAddress.parentElement); - getSeniorOfficialFromAgency(selectedFederalAgency).then((data) => { + getSeniorOfficialFromAgency(selectedFederalAgency).then((senior_official) => { // Update the "contact details" blurb beneath senior official - updateSeniorOfficialContactInfo(data); + updateSeniorOfficialContactInfo(senior_official); showElement(seniorOfficialAddress.parentElement); // Get the associated senior official with this federal agency - let seniorOfficialId = data.id; - let seniorOfficialName = [data.first_name, data.last_name].join(" "); - if ($seniorOfficial && $seniorOfficial.length > 0) { + let seniorOfficialId = senior_official.id; + let seniorOfficialName = [senior_official.first_name, senior_official.last_name].join(" "); + if ($seniorOfficialDropdown && $seniorOfficialDropdown.length > 0) { // If the senior official is a dropdown field, edit that - updateSeniorOfficialDropdown($seniorOfficial, seniorOfficialId, seniorOfficialName); + updateSeniorOfficialDropdown(seniorOfficialId, seniorOfficialName); } else { if (seniorOfficialReadonly) { let seniorOfficialLink = `${seniorOfficialName}` @@ -168,8 +167,8 @@ function handlePortfolioFields(){ .catch(error => { if (error.statusCode === 404) { // Handle "not found" senior official - if ($seniorOfficial && $seniorOfficial.length > 0) { - $seniorOfficial.val("").trigger("change"); + if ($seniorOfficialDropdown && $seniorOfficialDropdown.length > 0) { + $seniorOfficialDropdown.val("").trigger("change"); } else { seniorOfficialReadonly.innerHTML = `No senior official found. Create one now.`; } @@ -177,32 +176,30 @@ function handlePortfolioFields(){ // Handle other errors console.error("An error occurred:", error.message); } - }); - + }); } else { isPageLoading = false; } - } /** * Helper for updating federal type field */ - function updateSeniorOfficialDropdown(dropdown, seniorOfficialId, seniorOfficialName) { + function updateSeniorOfficialDropdown(seniorOfficialId, seniorOfficialName) { if (!seniorOfficialId || !seniorOfficialName || !seniorOfficialName.trim()){ // Clear the field if the SO doesn't exist - dropdown.val("").trigger("change"); + $seniorOfficialDropdown.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 (dropdown.find(`option[value='${seniorOfficialId}']`).length) { + if ($seniorOfficialDropdown.find(`option[value='${seniorOfficialId}']`).length) { // Select the value that is associated with the current Senior Official. - dropdown.val(seniorOfficialId).trigger("change"); + $seniorOfficialDropdown.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); - dropdown.append(userOption).trigger("change"); + $seniorOfficialDropdown.append(userOption).trigger("change"); } } @@ -237,34 +234,30 @@ function handlePortfolioFields(){ /** * Helper for updating senior official contact info */ - function updateSeniorOfficialContactInfo(data) { + function updateSeniorOfficialContactInfo(senior_official) { if (!seniorOfficialAddress) return; - const titleSpan = seniorOfficialAddress.querySelector(".contact_info_title"); const emailSpan = seniorOfficialAddress.querySelector(".contact_info_email"); const phoneSpan = seniorOfficialAddress.querySelector(".contact_info_phone"); - if (titleSpan) { - titleSpan.textContent = data.title || "None"; + titleSpan.textContent = senior_official.title || "None"; }; - // Update the email field and the content for the clipboard if (emailSpan) { let copyButton = seniorOfficialAddress.querySelector(".admin-icon-group"); - emailSpan.textContent = data.email || "None"; - if (data.email) { + emailSpan.textContent = senior_official.email || "None"; + if (senior_official.email) { const clipboardInput = seniorOfficialAddress.querySelector(".admin-icon-group input"); if (clipboardInput) { - clipboardInput.value = data.email; + clipboardInput.value = senior_official.email; }; showElement(copyButton); }else { hideElement(copyButton); } } - if (phoneSpan) { - phoneSpan.textContent = data.phone || "None"; + phoneSpan.textContent = senior_official.phone || "None"; }; }