-
Notifications
You must be signed in to change notification settings - Fork 39
feat: Onboarding Prototype #5518
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
joybytes
wants to merge
24
commits into
main
Choose a base branch
from
feat/onboarding-prototype-v2
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
Show all changes
24 commits
Select commit
Hold shift + click to select a range
e97ce16
feat: O3 -> 04 Add Banner on first login attempt
joybytes 2290d56
feat: O4 -> O5 Add Choose Organisation
joybytes def971c
feat: O5 -> O6 Enter Service Name
joybytes b130ad9
feat: O6 -> O7 Test Mode
joybytes f31cfec
feat: O9 Replace Trial Mode with Test Mode
joybytes 994bf80
feat: O13 -> O14 Choose From Name
joybytes 554cdab
feat: O14 -> O15a Let recipients reply to your emails
joybytes aa2d140
wip
joybytes 00e3d86
fix: trial mode string send.py
joybytes d1d0372
Add new / updated pages to do with estimated volumes
klssmith fbffebf
chore: uncomment verify code for platform admin
joybytes 13a61ea
Stop logging out platform admin users after 30 mins
klssmith e274411
Add test mode inset text to more pages
klssmith 86102b7
Preselect the users org when adding a service and choosing org type
klssmith b3d23c0
O16: preselect "enter a single address" radio button
klssmith 0429ebb
O19: Add "add a from name" to the task list
klssmith 2e2c077
Remove O16: preselect enter a single address radio button changes
joybytes 060b219
Add sender email journey backlinks
joybytes 034843d
Bypass email verification wait screen
joybytes 6b773d7
Remove test mode inset from tour
joybytes 5f5b46d
Add template type check so text messages are not affected
joybytes b16359b
06 and 020 - Remove web application or back office system message
joybytes 2714949
Refacor template button to: Save and Preview
joybytes 0a5c97a
Merge remote-tracking branch 'origin/main' into feat/onboarding-proto…
joybytes File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| (function(window) { | ||
| "use strict"; | ||
|
|
||
| // Based on GOVUK.ErrorBanner | ||
| window.GOVUK.NotificationBanner = { | ||
| hideBanner: () => $('.govuk-notification-banner').addClass('govuk-!-display-none'), | ||
| showBanner: () => | ||
| $('.govuk-notification-banner') | ||
| .removeClass('govuk-!-display-none') | ||
| .trigger('focus') | ||
| }; | ||
| })(window); | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1298,7 +1298,7 @@ def populate(self, domains_list): | |
|
|
||
| class CreateServiceForm(StripWhitespaceForm): | ||
| name = GovukTextInputField( | ||
| "Service name", | ||
| "", | ||
| validators=[ | ||
| DataRequired(message="Enter a service name"), | ||
| MustContainAlphanumericCharacters(), | ||
|
|
@@ -1308,6 +1308,24 @@ class CreateServiceForm(StripWhitespaceForm): | |
| organisation_type = OrganisationTypeField("Who runs this service?") | ||
|
|
||
|
|
||
| class ChooseOrganisationForm(StripWhitespaceForm): | ||
| choices = [] | ||
|
|
||
| def __init__(self, *args, **kwargs): | ||
| super().__init__(*args, **kwargs) | ||
| self.organisation.choices = [ | ||
| ("default", kwargs["default_organisation"]), | ||
| ("other", "Other public sector body"), | ||
| ] | ||
|
|
||
| organisation = GovukRadiosField( | ||
| "", | ||
| validators=[DataRequired()], | ||
| choices=choices, | ||
| thing="an organisation", | ||
| ) | ||
|
|
||
|
|
||
| class CreateNhsServiceForm(CreateServiceForm): | ||
| organisation_type = OrganisationTypeField( | ||
| "Who runs this service?", | ||
|
|
@@ -1744,6 +1762,29 @@ def validate(self, *args, **kwargs): | |
| return super().validate(*args, **kwargs) | ||
|
|
||
|
|
||
| class ExpectToSendForm(StripWhitespaceForm): | ||
| message_types = GovukCheckboxesField( | ||
| "", | ||
| validators=[DataRequired(message="Select at least 1 option")], | ||
| choices=[ | ||
| ("emails", "Emails"), | ||
| ("texts", "Text messages"), | ||
| ("letters", "Letters"), | ||
| ], | ||
| ) | ||
|
|
||
|
|
||
| class EmailUsageForm(StripWhitespaceForm): | ||
| high_volume_emails = GovukRadiosField( | ||
| "", | ||
| choices=[ | ||
| ("yes", "Yes"), | ||
| ("no", "No"), | ||
| ("maybe", "Maybe"), | ||
| ], | ||
| ) | ||
|
|
||
|
|
||
| class AdminProviderRatioForm(OrderableFieldsForm): | ||
| def __init__(self, providers): | ||
| self._providers = providers | ||
|
|
@@ -1935,19 +1976,23 @@ class ServiceEmailSenderForm(StripWhitespaceForm): | |
| "alert", | ||
| } | ||
|
|
||
| use_custom_email_sender_name = OnOffField( | ||
| "Choose a sender name", | ||
| choices_for_error_message="same or custom", | ||
| CHOICE_CUSTOM = "custom" | ||
| CHOICE_ORGANISATION = "organisation" | ||
| CHOICE_SERVICE = "service" | ||
|
|
||
| use_custom_email_sender_name = GovukRadiosField( | ||
| "", | ||
| choices=[ | ||
| (False, "Use the name of your service"), | ||
| (True, "Enter a custom sender name"), | ||
| (CHOICE_CUSTOM, "Enter a ‘from’ name"), | ||
| (CHOICE_ORGANISATION, "Use the name of your organisation"), | ||
| (CHOICE_SERVICE, "Use the name of your service"), | ||
| ], | ||
| ) | ||
|
|
||
| custom_email_sender_name = GovukTextInputField("Sender name", validators=[]) | ||
|
|
||
| def validate(self, *args, **kwargs): | ||
| if self.use_custom_email_sender_name.data is True: | ||
| if self.use_custom_email_sender_name.data == self.CHOICE_CUSTOM: | ||
| self.custom_email_sender_name.validators = [ | ||
| NotifyDataRequired(thing="a sender name"), | ||
| MustContainAlphanumericCharacters(thing="sender name"), | ||
|
|
@@ -1961,7 +2006,7 @@ def validate_custom_email_sender_name(self, field): | |
| Validate that the email from name ("Sender Name" <[email protected]) | ||
| is under 320 characters (if it's over, SES will reject the email and we'll end up with technical errors) | ||
| """ | ||
| if self.use_custom_email_sender_name.data is not True: | ||
| if self.use_custom_email_sender_name.data != self.CHOICE_CUSTOM: | ||
| return | ||
|
|
||
| normalised_sender_name = make_string_safe_for_email_local_part(field.data) | ||
|
|
@@ -2359,6 +2404,43 @@ def get_placeholder_form_instance( | |
| return PlaceholderForm(placeholder_value=dict_to_populate_from.get(placeholder_name, "")) | ||
|
|
||
|
|
||
| class AddRecipientForm(StripWhitespaceForm): | ||
| CHOICE_UPLOAD_CSV = "upload_csv" | ||
| CHOICE_ENTER_SINGLE_EMAIL = "enter_single" | ||
| CHOICE_USE_OWN_EMAIL = "use_my_email" | ||
|
|
||
| ADD_RECIPIENT_CHOICES = [ | ||
| (CHOICE_UPLOAD_CSV, "Upload a list of email addresses"), | ||
| (CHOICE_ENTER_SINGLE_EMAIL, "Enter a single email address"), | ||
| (CHOICE_USE_OWN_EMAIL, "Use my email address"), | ||
| ] | ||
|
|
||
| add_recipient_method = GovukRadiosField( | ||
| "", | ||
| choices=ADD_RECIPIENT_CHOICES, | ||
| thing="how to add recipients", | ||
| validators=[DataRequired()], | ||
| ) | ||
|
|
||
| enter_single_address = make_email_address_field( | ||
| gov_user=False, | ||
| thing="an email address", | ||
| label="Email address", | ||
| ) | ||
|
|
||
| def validate(self, *args, **kwargs): | ||
| self.enter_single_address.validators = [] | ||
|
|
||
| if self.add_recipient_method.data == self.CHOICE_ENTER_SINGLE_EMAIL: | ||
| self.enter_single_address.validators = [ | ||
| NotifyDataRequired(thing="an email address"), | ||
| ValidEmail(), | ||
| ValidGovEmail(), | ||
| ] | ||
|
|
||
| return super().validate(*args, **kwargs) | ||
|
|
||
|
|
||
| class SetSenderForm(StripWhitespaceForm): | ||
| def __init__(self, *args, **kwargs): | ||
| super().__init__(*args, **kwargs) | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| from urllib.parse import urlparse | ||
|
|
||
| from flask import request, session, url_for | ||
|
|
||
| from app import Service | ||
|
|
||
|
|
||
| def get_backlink_email_sender(service: Service, template_id: str): | ||
| has_from_name = service.email_sender_name is not None | ||
| has_reply_to = bool(service.email_reply_to_addresses) | ||
| has_multiple_reply_to = len(service.email_reply_to_addresses) > 1 | ||
|
|
||
| args = {"service_id": service.id, "template_id": template_id} | ||
|
|
||
| # Needs to choose from name | ||
| if not has_from_name: | ||
| base = ["view_template", "service_email_sender_change"] | ||
|
|
||
| # 1a. clicks do this later -> add recipients | ||
| # 1b. clicks add reply to email -> add recipients (endpoint will be added on the go) | ||
| if not has_reply_to: | ||
| expected_flow = base + ["service_email_reply_to"] | ||
| # 2. choose reply to email -> add recipients | ||
| elif has_multiple_reply_to: | ||
| expected_flow = base + ["set_sender"] | ||
| # 3. add recipients | ||
| else: | ||
| expected_flow = base | ||
| # Needs to choose reply to email | ||
| elif not has_reply_to: | ||
| expected_flow = ["view_template", "service_email_reply_to"] | ||
|
|
||
| # Has multiple reply to email | ||
| elif has_multiple_reply_to: | ||
| expected_flow = ["view_template", "set_sender"] | ||
| # Contains all necessary fields and single reply to email (DONE) | ||
| else: | ||
| expected_flow = ["view_template"] | ||
|
|
||
| return create_backlinks(expected_flow, **args) | ||
|
|
||
|
|
||
| def create_backlinks(routes, **kwargs): | ||
| return [url_for(f"main.{route}", **kwargs) for route in routes] | ||
|
|
||
|
|
||
| def get_previous_backlink(service_id, template_id): | ||
| if service_id is None or template_id is None: | ||
| return None | ||
|
|
||
| backlinks = session.get("email_sender_backlinks", []) | ||
|
|
||
| current_path = request.path # Just the path, no query string | ||
|
|
||
| parsed_paths = [urlparse(url).path for url in backlinks] | ||
|
|
||
| # Special journey check: user completed reply-to add and verify, now at step-0 | ||
| # In this case we are redirecting it back to view_template as too many values | ||
| # have been set and we want to keep things consistent | ||
| has_add_reply_to = any("email-reply-to/add" in path for path in parsed_paths) | ||
| has_verify_reply_to = any("email-reply-to/" in path and "/verify" in path for path in parsed_paths) | ||
| is_on_step_0 = "/one-off/step-0" in current_path | ||
|
|
||
| if is_on_step_0 and has_add_reply_to and has_verify_reply_to: | ||
| return url_for("main.view_template", service_id=service_id, template_id=template_id) | ||
|
|
||
| try: | ||
| paths = [urlparse(url).path for url in backlinks] | ||
| current_index = paths.index(current_path) | ||
| if current_index > 0: | ||
| return backlinks[current_index - 1] | ||
| except ValueError: | ||
| pass | ||
|
|
||
| return url_for("main.view_template", service_id=service_id, template_id=template_id) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.