diff --git a/experimenter/experimenter/experiments/models.py b/experimenter/experimenter/experiments/models.py index 6c64ab644e..ed6b112395 100644 --- a/experimenter/experimenter/experiments/models.py +++ b/experimenter/experimenter/experiments/models.py @@ -681,10 +681,18 @@ def is_started(self): def can_draft_to_preview(self): return self.is_draft and not self.is_review + @property + def can_draft_to_review(self): + return self.can_draft_to_preview + @property def can_preview_to_draft(self): return self.is_preview + @property + def can_preview_to_review(self): + return self.is_preview + @property def draft_date(self): if change := self.changes.all().order_by("changed_on").first(): diff --git a/experimenter/experimenter/nimbus_ui_new/static/js/control.js b/experimenter/experimenter/nimbus_ui_new/static/js/control.js index 7fb9939dfd..88faa90720 100644 --- a/experimenter/experimenter/nimbus_ui_new/static/js/control.js +++ b/experimenter/experimenter/nimbus_ui_new/static/js/control.js @@ -1,13 +1,14 @@ -export default function toggleSubmitButton() { - const checkboxes = document.querySelectorAll(".form-check-input"); - const submitButton = document.getElementById("request-launch-button"); - const allChecked = Array.from(checkboxes).every( - (checkbox) => checkbox.checked, +window.showRecommendation = function () { + const defaultControls = document.getElementById("default-controls"); + const recommendationMessage = document.getElementById( + "recommendation-message", ); - submitButton.disabled = !allChecked; -} -document.body.addEventListener("htmx:afterSwap", () => { - document.querySelectorAll(".form-check-input").forEach((checkbox) => { - checkbox.addEventListener("change", toggleSubmitButton); - }); -}); + defaultControls.classList.add("d-none"); + recommendationMessage.classList.remove("d-none"); +}; +window.toggleSubmitButton = function () { + const checkbox1 = document.getElementById("checkbox-1"); + const checkbox2 = document.getElementById("checkbox-2"); + const submitButton = document.getElementById("request-launch-button"); + submitButton.disabled = !(checkbox1.checked && checkbox2.checked); +}; diff --git a/experimenter/experimenter/nimbus_ui_new/templates/nimbus_experiments/detail.html b/experimenter/experimenter/nimbus_ui_new/templates/nimbus_experiments/detail.html index 29abf9ce74..f851011df7 100644 --- a/experimenter/experimenter/nimbus_ui_new/templates/nimbus_experiments/detail.html +++ b/experimenter/experimenter/nimbus_ui_new/templates/nimbus_experiments/detail.html @@ -22,47 +22,8 @@ {% endfor %} {% endif %} - - - - -
-
- {% csrf_token %} - - {% if experiment.is_draft %} - {% if experiment.can_draft_to_preview %} - - {% endif %} - {% if experiment.can_draft_to_review %} - - {% endif %} - {% elif experiment.is_preview %} - {% if experiment.can_preview_to_draft %} - {% endif %} - {% if eperiment.can_preview_to_review %} - {% endif %} - {% elif experiment.is_review %} - {% endif %} -
-
- - - + + {% include "nimbus_experiments/launch_controls.html" %} {% include "nimbus_experiments/takeaways_card.html" %} diff --git a/experimenter/experimenter/nimbus_ui_new/templates/nimbus_experiments/launch_controls.html b/experimenter/experimenter/nimbus_ui_new/templates/nimbus_experiments/launch_controls.html index ab5c14f9b8..ab676d4b18 100644 --- a/experimenter/experimenter/nimbus_ui_new/templates/nimbus_experiments/launch_controls.html +++ b/experimenter/experimenter/nimbus_ui_new/templates/nimbus_experiments/launch_controls.html @@ -1,31 +1,130 @@ -{# templates/nimbus_experiments/launch_controls.html #} -
-

- Do you want to test this experiment before launching to production? - Learn more -

-
-
- {% csrf_token %} - -
-
- {% csrf_token %} - -
-
+
+
+ {% csrf_token %} + + {% if experiment.is_draft %} +
+

+ Do you want to test this experiment before launching to production? + Learn more +

+ {% if experiment.can_draft_to_preview %} + + {% endif %} + {% if experiment.can_draft_to_review %} + + {% endif %} +
+ +
+
+

+ We recommend previewing before launch + +

+
+ + +
+
+ + +
+ + +
+
+ + {% elif experiment.is_preview %} +
+

All set! Your experiment is in Preview mode and you can test it now.

+
+
+

+ This experiment is currently live for testing, but you will need to let QA know in your + PI request. When you have received a sign-off, click “Request Launch” to launch the experiment. + Note: It can take up to an hour before clients receive a preview experiment. +

+
+ + +
+
+ + +
+ {% if experiment.can_preview_to_review %} + + {% endif %} + {% if experiment.can_preview_to_draft %} + + {% endif %} +
+ + {% elif experiment.is_review %} +
+

The experiment is currently under review. If you wish to cancel the review, click the button below:

+ +
+ {% endif %} +
diff --git a/experimenter/experimenter/nimbus_ui_new/templates/nimbus_experiments/launch_with_preview_controls.html b/experimenter/experimenter/nimbus_ui_new/templates/nimbus_experiments/launch_with_preview_controls.html deleted file mode 100644 index 1d4d248c87..0000000000 --- a/experimenter/experimenter/nimbus_ui_new/templates/nimbus_experiments/launch_with_preview_controls.html +++ /dev/null @@ -1,55 +0,0 @@ -{# templates/nimbus_experiments/launch_with_preview_controls.html #} -
-

- - - - All set! Your experiment is in Preview mode and you can test it now. -

-
-
-
- {% csrf_token %} -

- This experiment is currently live for testing, but you will need to let QA know in your - PI request. When you have received a sign-off, click “Request Launch” to launch the experiment. - Note: It can take up to an hour before clients receive a preview experiment. -

-
- - -
-
- - -
-
- - -
-
-
diff --git a/experimenter/experimenter/nimbus_ui_new/templates/nimbus_experiments/launch_without_preview_controls.html b/experimenter/experimenter/nimbus_ui_new/templates/nimbus_experiments/launch_without_preview_controls.html deleted file mode 100644 index ef5ceb61b6..0000000000 --- a/experimenter/experimenter/nimbus_ui_new/templates/nimbus_experiments/launch_without_preview_controls.html +++ /dev/null @@ -1,46 +0,0 @@ -{# templates/nimbus_experiments/launch_without_preview_controls.html #} -
-
- {% csrf_token %} - We recommend previewing before launch - -
-
-
- {% csrf_token %} -
- - -
-
- - -
- - -
diff --git a/experimenter/experimenter/nimbus_ui_new/tests/test_forms.py b/experimenter/experimenter/nimbus_ui_new/tests/test_forms.py index b320d65035..f79f41cc7f 100644 --- a/experimenter/experimenter/nimbus_ui_new/tests/test_forms.py +++ b/experimenter/experimenter/nimbus_ui_new/tests/test_forms.py @@ -8,17 +8,17 @@ ) from experimenter.nimbus_ui_new.constants import NimbusUIConstants from experimenter.nimbus_ui_new.forms import ( - ReviewToDraftForm, DocumentationLinkCreateForm, DocumentationLinkDeleteForm, - PreviewToDraftForm, - PreviewToReviewForm, DraftToPreviewForm, DraftToReviewForm, MetricsForm, NimbusExperimentCreateForm, OverviewForm, + PreviewToDraftForm, + PreviewToReviewForm, QAStatusForm, + ReviewToDraftForm, SignoffForm, SubscribeForm, TakeawaysForm, @@ -327,62 +327,77 @@ def setUp(self): super().setUp() self.experiment = NimbusExperimentFactory.create() - def test_launch_to_preview_form(self): + def test_draft_to_preview_form(self): form = DraftToPreviewForm(data={}, instance=self.experiment, request=self.request) self.assertTrue(form.is_valid(), form.errors) experiment = form.save() self.assertEqual(experiment.status, NimbusExperiment.Status.PREVIEW) + self.assertEqual(experiment.status_next, NimbusExperiment.Status.PREVIEW) + self.assertEqual(experiment.publish_status, NimbusExperiment.PublishStatus.IDLE) changelog = experiment.changes.latest("changed_on") self.assertEqual(changelog.changed_by, self.user) self.assertIn("launched experiment to Preview", changelog.message) - def test_launch_without_preview_form(self): + def test_draft_to_review_form(self): form = DraftToReviewForm(data={}, instance=self.experiment, request=self.request) self.assertTrue(form.is_valid(), form.errors) experiment = form.save() - self.assertEqual(experiment.status, self.experiment.status) + self.assertEqual(experiment.status, NimbusExperiment.Status.DRAFT) + self.assertEqual(experiment.status_next, NimbusExperiment.Status.LIVE) + self.assertEqual(experiment.publish_status, NimbusExperiment.PublishStatus.REVIEW) changelog = experiment.changes.latest("changed_on") self.assertEqual(changelog.changed_by, self.user) self.assertIn("requested launch without Preview", changelog.message) - def test_launch_preview_to_review_form(self): + def test_preview_to_review_form(self): + self.experiment.status = NimbusExperiment.Status.PREVIEW + self.experiment.save() + form = PreviewToReviewForm( data={}, instance=self.experiment, request=self.request ) self.assertTrue(form.is_valid(), form.errors) experiment = form.save() - self.assertEqual(experiment.publish_status, NimbusExperiment.PublishStatus.REVIEW) self.assertEqual(experiment.status, NimbusExperiment.Status.DRAFT) self.assertEqual(experiment.status_next, NimbusExperiment.Status.LIVE) + self.assertEqual(experiment.publish_status, NimbusExperiment.PublishStatus.REVIEW) changelog = experiment.changes.latest("changed_on") self.assertEqual(changelog.changed_by, self.user) self.assertIn("requested launch from Preview", changelog.message) - def test_launch_preview_to_draft_form(self): + def test_preview_to_draft_form(self): + self.experiment.status = NimbusExperiment.Status.PREVIEW + self.experiment.save() + form = PreviewToDraftForm(data={}, instance=self.experiment, request=self.request) self.assertTrue(form.is_valid(), form.errors) experiment = form.save() self.assertEqual(experiment.status, NimbusExperiment.Status.DRAFT) - self.assertIsNone(experiment.status_next) + self.assertEqual(experiment.status_next, NimbusExperiment.Status.DRAFT) + self.assertEqual(experiment.publish_status, NimbusExperiment.PublishStatus.IDLE) changelog = experiment.changes.latest("changed_on") self.assertEqual(changelog.changed_by, self.user) self.assertIn("moved the experiment back to Draft", changelog.message) - def test_cancel_review_form(self): + def test_review_to_draft_form(self): + self.experiment.publish_status = NimbusExperiment.PublishStatus.REVIEW + self.experiment.save() + form = ReviewToDraftForm(data={}, instance=self.experiment, request=self.request) self.assertTrue(form.is_valid(), form.errors) experiment = form.save() + self.assertEqual(experiment.status, NimbusExperiment.Status.DRAFT) + self.assertEqual(experiment.status_next, NimbusExperiment.Status.DRAFT) self.assertEqual(experiment.publish_status, NimbusExperiment.PublishStatus.IDLE) - self.assertIsNone(experiment.status_next) changelog = experiment.changes.latest("changed_on") self.assertEqual(changelog.changed_by, self.user) diff --git a/experimenter/experimenter/nimbus_ui_new/tests/test_views.py b/experimenter/experimenter/nimbus_ui_new/tests/test_views.py index b3b119fce2..fddd14d5ff 100644 --- a/experimenter/experimenter/nimbus_ui_new/tests/test_views.py +++ b/experimenter/experimenter/nimbus_ui_new/tests/test_views.py @@ -1289,61 +1289,70 @@ def setUp(self): super().setUp() self.experiment = NimbusExperimentFactory.create() - def test_launch_to_preview_view(self): + def test_draft_to_preview(self): response = self.client.post( reverse("nimbus-new-draft-to-preview", kwargs={"slug": self.experiment.slug}), ) self.assertEqual(response.status_code, 200) self.experiment.refresh_from_db() self.assertEqual(self.experiment.status, NimbusExperiment.Status.PREVIEW) + self.assertEqual(self.experiment.status_next, NimbusExperiment.Status.PREVIEW) + self.assertEqual( + self.experiment.publish_status, NimbusExperiment.PublishStatus.IDLE + ) - def test_launch_without_preview_view(self): + def test_draft_to_review(self): response = self.client.post( - reverse( - "nimbus-new-draft-to-review", - kwargs={"slug": self.experiment.slug}, - ), + reverse("nimbus-new-draft-to-review", kwargs={"slug": self.experiment.slug}), ) self.assertEqual(response.status_code, 200) - self.assertEqual(self.experiment.status, self.experiment.status) + self.experiment.refresh_from_db() + self.assertEqual(self.experiment.status, NimbusExperiment.Status.DRAFT) + self.assertEqual(self.experiment.status_next, NimbusExperiment.Status.LIVE) + self.assertEqual( + self.experiment.publish_status, NimbusExperiment.PublishStatus.REVIEW + ) - def test_launch_preview_to_review_view(self): + def test_preview_to_review(self): + self.experiment.status = NimbusExperiment.Status.PREVIEW + self.experiment.save() response = self.client.post( reverse( - "nimbus-new-preview-to-review", - kwargs={"slug": self.experiment.slug}, + "nimbus-new-preview-to-review", kwargs={"slug": self.experiment.slug} ), ) self.assertEqual(response.status_code, 200) self.experiment.refresh_from_db() + self.assertEqual(self.experiment.status, NimbusExperiment.Status.DRAFT) + self.assertEqual(self.experiment.status_next, NimbusExperiment.Status.LIVE) self.assertEqual( self.experiment.publish_status, NimbusExperiment.PublishStatus.REVIEW ) - self.assertEqual(self.experiment.status, NimbusExperiment.Status.DRAFT) - self.assertEqual(self.experiment.status_next, NimbusExperiment.Status.LIVE) - def test_launch_preview_to_draft_view(self): + def test_preview_to_draft(self): + self.experiment.status = NimbusExperiment.Status.PREVIEW + self.experiment.save() response = self.client.post( - reverse( - "nimbus-new-preview-to-draft", - kwargs={"slug": self.experiment.slug}, - ), + reverse("nimbus-new-preview-to-draft", kwargs={"slug": self.experiment.slug}), ) self.assertEqual(response.status_code, 200) self.experiment.refresh_from_db() self.assertEqual(self.experiment.status, NimbusExperiment.Status.DRAFT) - self.assertIsNone(self.experiment.status_next) + self.assertEqual(self.experiment.status_next, NimbusExperiment.Status.DRAFT) + self.assertEqual( + self.experiment.publish_status, NimbusExperiment.PublishStatus.IDLE + ) - def test_cancel_review_view(self): + def test_cancel_review(self): self.experiment.publish_status = NimbusExperiment.PublishStatus.REVIEW self.experiment.save() - response = self.client.post( reverse("nimbus-new-review-to-draft", kwargs={"slug": self.experiment.slug}), ) self.assertEqual(response.status_code, 200) self.experiment.refresh_from_db() + self.assertEqual(self.experiment.status, NimbusExperiment.Status.DRAFT) + self.assertEqual(self.experiment.status_next, NimbusExperiment.Status.DRAFT) self.assertEqual( self.experiment.publish_status, NimbusExperiment.PublishStatus.IDLE ) - self.assertIsNone(self.experiment.status_next) diff --git a/experimenter/experimenter/nimbus_ui_new/urls.py b/experimenter/experimenter/nimbus_ui_new/urls.py index 3f6345597b..1ae7f3fd8b 100644 --- a/experimenter/experimenter/nimbus_ui_new/urls.py +++ b/experimenter/experimenter/nimbus_ui_new/urls.py @@ -1,11 +1,8 @@ from django.urls import re_path from experimenter.nimbus_ui_new.views import ( - ReviewToDraftView, DocumentationLinkCreateView, DocumentationLinkDeleteView, - PreviewToDraftView, - PreviewToReviewView, DraftToPreviewView, DraftToReviewView, MetricsUpdateView, @@ -14,7 +11,10 @@ NimbusExperimentsCreateView, NimbusExperimentsListTableView, OverviewUpdateView, + PreviewToDraftView, + PreviewToReviewView, QAStatusUpdateView, + ReviewToDraftView, SignoffUpdateView, SubscribeView, TakeawaysUpdateView, diff --git a/experimenter/experimenter/nimbus_ui_new/views.py b/experimenter/experimenter/nimbus_ui_new/views.py index eeb8ca9322..17c1e4f33e 100644 --- a/experimenter/experimenter/nimbus_ui_new/views.py +++ b/experimenter/experimenter/nimbus_ui_new/views.py @@ -1,6 +1,5 @@ from django.conf import settings from django.http import HttpResponse -from django.template.loader import render_to_string from django.urls import reverse from django.views.generic import CreateView, DetailView from django.views.generic.edit import UpdateView @@ -18,17 +17,17 @@ StatusChoices, ) from experimenter.nimbus_ui_new.forms import ( - ReviewToDraftForm, DocumentationLinkCreateForm, DocumentationLinkDeleteForm, - PreviewToDraftForm, - PreviewToReviewForm, DraftToPreviewForm, DraftToReviewForm, MetricsForm, NimbusExperimentCreateForm, OverviewForm, + PreviewToDraftForm, + PreviewToReviewForm, QAStatusForm, + ReviewToDraftForm, SignoffForm, SubscribeForm, TakeawaysForm,