From 1cfa08a83aa97a179bb752a47e99839366d0e92a Mon Sep 17 00:00:00 2001 From: Alysia Broddrick Date: Thu, 6 Jun 2024 15:48:00 -0700 Subject: [PATCH 01/61] started adr --- .../decisions/0026-django-waffle-library.md | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 docs/architecture/decisions/0026-django-waffle-library.md diff --git a/docs/architecture/decisions/0026-django-waffle-library.md b/docs/architecture/decisions/0026-django-waffle-library.md new file mode 100644 index 000000000..878cb81ed --- /dev/null +++ b/docs/architecture/decisions/0026-django-waffle-library.md @@ -0,0 +1,28 @@ +# 24. Production Release Cadence + +Date: 2024-07-06 + +## Status + +Approved + +## Context + + + +## Considered Options + +**Option 1:** Releasing to stable/staging once a sprint + +**Option 2:** Releasing to stable/staging once a week + + +In both of the above scenarios, the release date would fall on the same day of the week that the sprint starts which is currently a Wednesday. Additionally, in both scenarios the release commits would eventually be tagged with both a staging and stable tag. Furthermore, critical bugs or features would be exempt from these restrictions based on the product owner's discretion. + +## Decision + + + +## Consequences + + From bfc3b74283843ae48cfec6f8c319e2b163236a2e Mon Sep 17 00:00:00 2001 From: Alysia Broddrick Date: Thu, 13 Jun 2024 14:10:15 -0700 Subject: [PATCH 02/61] updated adr --- .../decisions/0026-django-waffle-library.md | 31 +++++++++++++++++-- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/docs/architecture/decisions/0026-django-waffle-library.md b/docs/architecture/decisions/0026-django-waffle-library.md index 878cb81ed..a8dbfccac 100644 --- a/docs/architecture/decisions/0026-django-waffle-library.md +++ b/docs/architecture/decisions/0026-django-waffle-library.md @@ -8,16 +8,41 @@ Approved ## Context +We release finished code twice a week allowing features to reach users fast. However, several upcoming features required a series of changes that would need to be done over a couple sprints and should not display to users until we are all done. Thus, if we followed our normal process, users would see half-finished features. + +At the same time, some of these features should only be turned on for users upon request (and likely during user research). We would want a way for our CISA users to be able to turn this feature on and off for people without requiring a lengthy process or code changes. + +This brought us to finding solutions that could fix one or both of these problems. ## Considered Options -**Option 1:** Releasing to stable/staging once a sprint +**Option 1:** Environment variables +Environment variables would allow implementation over multiple sprints, by allowing us to set a true or false value to the given variable. When the varaible is on (true) the feature shows, otherwise it remains hidden. ANother benefit is that it is free and we already use environment variables for other things in our code. -**Option 2:** Releasing to stable/staging once a week +The down side, is that to see what the current setting is on a sandbox you would need to go to cloud.gov or use the cf cli to see the current settings. This is very technical, meaning only developers would really be able to see what features were set and we would be the only ones to adjust them. It would also be really easy to accidentally have the feature on or off without noticing. This also would not solve the problem of being able to turn features on and off easily for a given user group. +**Option 2:** Feature branches +Like environrment variables, using feature branches would be free and allow us to iterate on development of big features over multiple sprints. We would make a feature branch that developers working on that feature would push and pull from to iterate on. This quickly brings us to the downsides of this approach. + +WIth feature branches we do not solve the problem of being able to turn features on and off easily for a user group. More importantly, by working in a seperated branch for more than a sprint we easily run the risk of having out of sync migrations and merge conflicts that would slow down development time and cause frustration. Out of sync migrations can also cause technical issues on sandboxes as well, which further adds to development frustration. + +**Option 3:** Feature flags +Feature flags are free, allow us to implement thigns over multiple sprints, and some libar + +Waffle feature flag libary + + +**Option 3a:** Feature flags with waffle +The Waffle feature flag library is a highly reccomended django libary for handling large features. It has clear documentation on how to turn on feature flags for user groups as this is one of the main problems it attempts to solve. It also provides Samples which can turn on flags for a certain percent of users and Switches which can be used to hollistically turn features on and off. The reviews from those who used it were highly favorable, some even mentioning how it beat out competitors like gargoyl. It's also comaptible with django admin, providing a quick way to add the view of the flags in django admin so any user with admin access can modify flags for their sandbox. + + +**Option 3b:** Feature flags with gargoyl +Another feature flag libary with django, but with more forks of the libary, less consitent maintanence and reviews saying it wasn't as easy to work with as waffle. The reviews and less robust documentation were immediatly huge cons to this as an option + +**Option 3c:** Paid feature flag system with github integration- LaunchDarkly +LaunchDarkly is a Fedramp'd solution with excellent reviews for controlling feature flags straight from github to promote any team member to be in easy control of setting feature flags. However, the big con to this was that it would be a paid solution and would take time to procure thus slowing down our ability to start on these large features. -In both of the above scenarios, the release date would fall on the same day of the week that the sprint starts which is currently a Wednesday. Additionally, in both scenarios the release commits would eventually be tagged with both a staging and stable tag. Furthermore, critical bugs or features would be exempt from these restrictions based on the product owner's discretion. ## Decision From 8e50ac82f2e3cadb6cf2b7f1e2c6a344c2573f1d Mon Sep 17 00:00:00 2001 From: David Kennedy Date: Thu, 20 Jun 2024 13:42:14 -0400 Subject: [PATCH 03/61] added status filter to domain json search --- src/registrar/views/domains_json.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/registrar/views/domains_json.py b/src/registrar/views/domains_json.py index 6b286ab6e..a14e607f9 100644 --- a/src/registrar/views/domains_json.py +++ b/src/registrar/views/domains_json.py @@ -20,11 +20,18 @@ def get_domains_json(request): # Handle sorting sort_by = request.GET.get("sort_by", "id") # Default to 'id' order = request.GET.get("order", "asc") # Default to 'asc' - search_term = request.GET.get("search_term") + # Handle search term + search_term = request.GET.get("search_term") if search_term: objects = objects.filter(Q(name__icontains=search_term)) + # Handle state + status_param = request.GET.get("status") + if status_param: + status_list = status_param.split(',') + objects = objects.filter(state__in=status_list) + if sort_by == "state_display": # Fetch the objects and sort them in Python objects = list(objects) # Evaluate queryset to a list From b263c7e144dd9af07b017b660299dd1e6f2fdb73 Mon Sep 17 00:00:00 2001 From: David Kennedy Date: Thu, 20 Jun 2024 15:01:27 -0400 Subject: [PATCH 04/61] updated domain json query to handle expired logic --- src/registrar/views/domains_json.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/registrar/views/domains_json.py b/src/registrar/views/domains_json.py index a14e607f9..e626a88f9 100644 --- a/src/registrar/views/domains_json.py +++ b/src/registrar/views/domains_json.py @@ -1,3 +1,4 @@ +from datetime import timezone from django.http import JsonResponse from django.core.paginator import Paginator from registrar.models import UserDomainRole, Domain @@ -30,7 +31,27 @@ def get_domains_json(request): status_param = request.GET.get("status") if status_param: status_list = status_param.split(',') - objects = objects.filter(state__in=status_list) + + # Split the status list into normal states and custom states + normal_states = [state for state in status_list if state in Domain.State.values] + custom_states = [state for state in status_list if state == 'expired'] + + # Construct Q objects for normal states that can be queried through ORM + state_query = Q() + if normal_states: + state_query |= Q(state__in=normal_states) + + # Handle custom states in Python, as expired can not be queried through ORM + if 'expired' in custom_states: + expired_domains = [domain.id for domain in objects if domain.state_display() == 'Expired'] + state_query |= Q(id__in=expired_domains) + + # Apply the combined query + objects = objects.filter(state_query) + + # NOTE: when a domain has a state of 'ready' and is_expired(), a search for + # status=ready will include the domain, even though the domain's state_display + # is Expired. if sort_by == "state_display": # Fetch the objects and sort them in Python From 38610d204415fd4ce821bb9faa479210b556e636 Mon Sep 17 00:00:00 2001 From: Rachid Mrad Date: Thu, 20 Jun 2024 15:16:23 -0400 Subject: [PATCH 05/61] base dropdown UI --- .../assets/sass/_theme/_accordions.scss | 36 ++++++ src/registrar/assets/sass/_theme/_base.scss | 4 + src/registrar/assets/sass/_theme/styles.scss | 1 + .../templates/includes/domains_table.html | 103 ++++++++++++++++++ 4 files changed, 144 insertions(+) create mode 100644 src/registrar/assets/sass/_theme/_accordions.scss diff --git a/src/registrar/assets/sass/_theme/_accordions.scss b/src/registrar/assets/sass/_theme/_accordions.scss new file mode 100644 index 000000000..065bfb6d8 --- /dev/null +++ b/src/registrar/assets/sass/_theme/_accordions.scss @@ -0,0 +1,36 @@ +@use "uswds-core" as *; + +.usa-accordion--select { + display: inline-block; + width: auto; + position: relative; + .usa-accordion__button { + background: color('white'); + border: solid 1px color('base-lighter'); + border-radius: 4px; + padding: units(1) units(2); + color: color('primary-darker'); + font-weight: font-weight('normal'); + font-size: size('ui', 'xs'); + } + .usa-accordion__button[aria-expanded=false], + .usa-accordion__button[aria-expanded=false]:hover { + background-image: none; + } + .usa-accordion__content { + // Note, width is determined by a custom width class on one of the children + position: absolute; + z-index: 1; + top: 36.48px; + left: 0; + } + h2 { + font-size: size('body', 'sm'); + } + .usa-button { + width: 100%; + } + .margin-top-0 { + margin-top: 0 !important; + } +} \ No newline at end of file diff --git a/src/registrar/assets/sass/_theme/_base.scss b/src/registrar/assets/sass/_theme/_base.scss index e5e8b89ee..82884ce15 100644 --- a/src/registrar/assets/sass/_theme/_base.scss +++ b/src/registrar/assets/sass/_theme/_base.scss @@ -211,3 +211,7 @@ abbr[title] { .usa-logo button.usa-button--unstyled.disabled-button:hover{ color: #{$dhs-dark-gray-85}; } + +.width-145 { + width: 145px; +} diff --git a/src/registrar/assets/sass/_theme/styles.scss b/src/registrar/assets/sass/_theme/styles.scss index 921976b44..622448d1c 100644 --- a/src/registrar/assets/sass/_theme/styles.scss +++ b/src/registrar/assets/sass/_theme/styles.scss @@ -12,6 +12,7 @@ @forward "typography"; @forward "links"; @forward "lists"; +@forward "accordions"; @forward "buttons"; @forward "pagination"; @forward "forms"; diff --git a/src/registrar/templates/includes/domains_table.html b/src/registrar/templates/includes/domains_table.html index 4347520cc..a818af905 100644 --- a/src/registrar/templates/includes/domains_table.html +++ b/src/registrar/templates/includes/domains_table.html @@ -33,6 +33,109 @@ + {% if portfolio %} +
+
+ +
+
+

Status

+
+ Select to apply status filter +
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + + + +
+
+ {% endif %}