From e04337f94c2ccd11000b83dfe1b33606218927fa Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Thu, 17 Oct 2024 15:39:55 -0600 Subject: [PATCH 01/73] initial form --- src/registrar/forms/domain_request_wizard.py | 74 ++++++++++++++++++- src/registrar/models/domain_request.py | 10 +++ .../domain_request_requesting_entity.html | 15 +++- src/registrar/views/domain_request.py | 2 +- 4 files changed, 94 insertions(+), 7 deletions(-) diff --git a/src/registrar/forms/domain_request_wizard.py b/src/registrar/forms/domain_request_wizard.py index 6b160b14d..80d162762 100644 --- a/src/registrar/forms/domain_request_wizard.py +++ b/src/registrar/forms/domain_request_wizard.py @@ -13,7 +13,7 @@ BaseYesNoForm, BaseDeletableRegistrarForm, ) -from registrar.models import Contact, DomainRequest, DraftDomain, Domain, FederalAgency +from registrar.models import Contact, DomainRequest, DraftDomain, Domain, FederalAgency, Suborganization from registrar.templatetags.url_helpers import public_site_url from registrar.utility.enums import ValidationReturnType from registrar.utility.constants import BranchChoices @@ -22,11 +22,81 @@ class RequestingEntityForm(RegistrarForm): + sub_organization = forms.ModelChoiceField( + label="Suborganization name", + # not required because this field won't be filled out unless + # it is a federal agency. Use clean to check programatically + # if it has been filled in when required. + required=False, + queryset=Suborganization.objects.all(), + empty_label="--Select--", + ) organization_name = forms.CharField( - label="Organization name", + label="Requested suborganization", + required=False, error_messages={"required": "Enter the name of your organization."}, ) + city = forms.CharField( + label="City", + required=False, + error_messages={"required": "Enter the city where your organization is located."}, + ) + state_territory = forms.ChoiceField( + label="State, territory, or military post", + required=False, + choices=[("", "--Select--")] + DomainRequest.StateTerritoryChoices.choices, + error_messages={ + "required": ("Select the state, territory, or military post where your organization is located.") + }, + ) + is_suborganization = forms.NullBooleanField( + widget=forms.RadioSelect( + choices=[ + (True, "Yes"), + (False, "No"), + ], + ) + ) + def clean_sub_organization(self): + """Require something to be selected when this is a federal agency.""" + sub_organization = self.cleaned_data.get("sub_organization", None) + if self.cleaned_data.get("is_suborganization", None): + # TODO - logic for if other is selected, display other stuff + if not sub_organization: + # no answer was selected + raise forms.ValidationError( + "Select a suborganization.", + code="required", + ) + # Maybe we just represent this with none? + elif sub_organization == "other": + org_name = self.cleaned_data.get("organization_name", None) + city = self.cleaned_data.get("city", None) + state = self.cleaned_data.get("state_territory", None) + if not org_name or not city or not state: + raise forms.ValidationError( + "Enter details for your suborganization.", + code="required", + ) + return sub_organization + +class RequestingEntityYesNoForm(BaseYesNoForm): + """The yes/no field for the RequestingEntity form.""" + + form_choices = ((False, "Dynamic portfolio field"), (True, "A suborganization. (choose from list)")) + field_name = "is_suborganization" + + @property + def form_is_checked(self): + """ + Determines the initial checked state of the form based on the domain_request's attributes. + """ + if self.domain_request.portfolio and (self.domain_request.sub_organization or self.domain_request.organization_name): + return self.domain_request.organization_name != self.domain_request.portfolio.organization_name + else: + # No pre-selection for new domain requests + return None class OrganizationTypeForm(RegistrarForm): generic_org_type = forms.ChoiceField( diff --git a/src/registrar/models/domain_request.py b/src/registrar/models/domain_request.py index b9e3315d5..fb61e93e5 100644 --- a/src/registrar/models/domain_request.py +++ b/src/registrar/models/domain_request.py @@ -1173,6 +1173,16 @@ def is_federal(self) -> Union[bool, None]: return True return False + def is_suborganization(self) -> bool: + if self.portfolio: + if self.sub_organization: + return True + + if self.organization_name != self.portfolio.organization_name: + return True + + return False + def to_dict(self): """This is to process to_dict for Domain Information, making it friendly to "copy" it diff --git a/src/registrar/templates/domain_request_requesting_entity.html b/src/registrar/templates/domain_request_requesting_entity.html index ed8dd771c..e59ad501c 100644 --- a/src/registrar/templates/domain_request_requesting_entity.html +++ b/src/registrar/templates/domain_request_requesting_entity.html @@ -2,15 +2,22 @@ {% load field_helpers url_helpers %} {% block form_instructions %} -
🛸🛸🛸🛸 Placeholder content 🛸🛸🛸🛸
+To help with our review, we need to understand whether the domain you're requesting will be used by the Department of Energy or by one of its suborganizations.
+We define a suborganization as any entity (agency, bureau, office) that falls under the overarching organization.
{% endblock %} {% block form_fields %} {% endblock %} diff --git a/src/registrar/views/domain_request.py b/src/registrar/views/domain_request.py index 1dbe9f082..e098ce935 100644 --- a/src/registrar/views/domain_request.py +++ b/src/registrar/views/domain_request.py @@ -588,7 +588,7 @@ class PortfolioDomainRequestWizard(DomainRequestWizard): # Portfolio pages class RequestingEntity(DomainRequestWizard): template_name = "domain_request_requesting_entity.html" - forms = [forms.RequestingEntityForm] + forms = [forms.RequestingEntityYesNoForm, forms.RequestingEntityForm] class PortfolioAdditionalDetails(DomainRequestWizard): From b3e90969108744733d330aa5147c41bfaad05dbb Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Thu, 17 Oct 2024 15:46:26 -0600 Subject: [PATCH 02/73] dynamic options --- src/registrar/forms/domain_request_wizard.py | 10 +++++++++- src/registrar/forms/utility/wizard_form_helper.py | 1 + 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/registrar/forms/domain_request_wizard.py b/src/registrar/forms/domain_request_wizard.py index 80d162762..74f5a32bc 100644 --- a/src/registrar/forms/domain_request_wizard.py +++ b/src/registrar/forms/domain_request_wizard.py @@ -83,9 +83,17 @@ def clean_sub_organization(self): class RequestingEntityYesNoForm(BaseYesNoForm): """The yes/no field for the RequestingEntity form.""" - form_choices = ((False, "Dynamic portfolio field"), (True, "A suborganization. (choose from list)")) + # This first option will change dynamically + form_choices = ((False, "Current Organization"), (True, "A suborganization. (choose from list)")) field_name = "is_suborganization" + def __init__(self, *args, **kwargs): + """Extend the initialization of the form from RegistrarForm __init__""" + super().__init__(*args, **kwargs) + if self.domain_request.portfolio: + self.form_choices = ((False, self.domain_request.portfolio), (True, "A suborganization. (choose from list)")) + self.fields[self.field_name] = self.get_typed_choice_field() + @property def form_is_checked(self): """ diff --git a/src/registrar/forms/utility/wizard_form_helper.py b/src/registrar/forms/utility/wizard_form_helper.py index eedf5839b..0a1019aae 100644 --- a/src/registrar/forms/utility/wizard_form_helper.py +++ b/src/registrar/forms/utility/wizard_form_helper.py @@ -261,6 +261,7 @@ def get_typed_choice_field(self): "required": self.required_error_message, }, ) + print(f"here are the form choices: {self.form_choices}, here is the initial: {self.get_initial_value()}") return choice_field From 65f1e628a754a3c60154ecdcde3be5488b4b69fd Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 18 Oct 2024 15:34:08 -0600 Subject: [PATCH 03/73] add javascript logic (needs some minor refinement) --- src/registrar/assets/js/get-gov.js | 84 +++++++++++++++++++ src/registrar/forms/domain_request_wizard.py | 9 +- .../domain_request_requesting_entity.html | 2 +- 3 files changed, 93 insertions(+), 2 deletions(-) diff --git a/src/registrar/assets/js/get-gov.js b/src/registrar/assets/js/get-gov.js index 8a07b3f27..c34493015 100644 --- a/src/registrar/assets/js/get-gov.js +++ b/src/registrar/assets/js/get-gov.js @@ -2379,3 +2379,87 @@ document.addEventListener('DOMContentLoaded', function() { } } })(); + +(function handleRequestingEntityFieldset() { + // Check if the requesting-entity-fieldset exists. + // This determines if we are on the requesting entity page or not. + const fieldset = document.getElementById("requesting-entity-fieldset"); + if (!fieldset) return; + console.log("past here") + // Get the is_suborganization radio buttons + // Sadly, these ugly ids are the auto generated + const formPrefix = "portfolio_requesting_entity" + const isSuborgRadios = document.querySelectorAll(`input[name="${formPrefix}-is_suborganization"]`); + var selectedRequestingEntityValue = document.querySelector(`input[name="${formPrefix}-is_suborganization"]:checked`)?.value; + const subOrgSelect = document.querySelector(`#id_${formPrefix}-sub_organization`); + const orgName = document.querySelector(`#id_${formPrefix}-organization_name`); + const city = document.querySelector(`#id_${formPrefix}-city`); + const stateTerritory = document.querySelector(`#id_${formPrefix}-state_territory`); + + console.log(isSuborgRadios) + console.log(subOrgSelect) + console.log(orgName) + console.log(city) + console.log(stateTerritory) + console.log(selectedRequestingEntityValue) + // Don't do anything if we are missing crucial page elements + if (!isSuborgRadios || !subOrgSelect || !orgName || !city || !stateTerritory) return; + console.log("past here x2") + + // Add fake "other" option to sub_organization select + if (subOrgSelect && !Array.from(subOrgSelect.options).some(option => option.value === "other")) { + const fakeOption = document.createElement("option"); + fakeOption.value = "other"; + fakeOption.text = "Other (enter your organization manually)"; + subOrgSelect.add(fakeOption); + } + + // Hide organization_name, city, state_territory by default + hideElement(orgName.parentElement); + hideElement(city.parentElement); + hideElement(stateTerritory.parentElement); + + // Function to toggle forms based on is_suborganization selection + function toggleSubOrganizationFields () { + selectedRequestingEntityValue = document.querySelector(`input[name="${formPrefix}-is_suborganization"]:checked`)?.value; + if (selectedRequestingEntityValue === "True") { + showElement(subOrgSelect.parentElement); + toggleOrganizationDetails(); + } else { + hideElement(subOrgSelect.parentElement); + hideElement(orgName.parentElement); + hideElement(city.parentElement); + hideElement(stateTerritory.parentElement); + } + }; + + // Function to toggle organization details based on sub_organization selection + function toggleOrganizationDetails () { + // We should hide the org name fields when we select the special other value + if (subOrgSelect.value === "other") { + showElement(orgName.parentElement); + showElement(city.parentElement); + showElement(stateTerritory.parentElement); + } else { + hideElement(orgName.parentElement); + hideElement(city.parentElement); + hideElement(stateTerritory.parentElement); + } + }; + + // Initialize visibility + toggleSubOrganizationFields(); + + // Add event listeners to is_suborganization radio buttons + isSuborgRadios.forEach(radio => { + radio.addEventListener("change", () => { + toggleSubOrganizationFields(); + }); + }); + + subOrgSelect.addEventListener("change", () => { + if (selectedRequestingEntityValue === "True") { + toggleOrganizationDetails(); + } + }); +})(); \ No newline at end of file diff --git a/src/registrar/forms/domain_request_wizard.py b/src/registrar/forms/domain_request_wizard.py index 74f5a32bc..a7a971912 100644 --- a/src/registrar/forms/domain_request_wizard.py +++ b/src/registrar/forms/domain_request_wizard.py @@ -28,7 +28,7 @@ class RequestingEntityForm(RegistrarForm): # it is a federal agency. Use clean to check programatically # if it has been filled in when required. required=False, - queryset=Suborganization.objects.all(), + queryset=Suborganization.objects.none(), empty_label="--Select--", ) organization_name = forms.CharField( @@ -57,6 +57,13 @@ class RequestingEntityForm(RegistrarForm): ], ) ) + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + if self.domain_request.portfolio: + self.fields["sub_organization"].queryset = Suborganization.objects.filter(portfolio=self.domain_request.portfolio) + def clean_sub_organization(self): """Require something to be selected when this is a federal agency.""" sub_organization = self.cleaned_data.get("sub_organization", None) diff --git a/src/registrar/templates/domain_request_requesting_entity.html b/src/registrar/templates/domain_request_requesting_entity.html index e59ad501c..ab7b2337b 100644 --- a/src/registrar/templates/domain_request_requesting_entity.html +++ b/src/registrar/templates/domain_request_requesting_entity.html @@ -7,7 +7,7 @@ {% endblock %} {% block form_fields %} -