mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-06-07 06:50:56 -04:00
Show a tour when users first create a service
This commit adds a 3 screen tour, similar to those used on GOV.UK Verify and Passports. We guerilla tested this on Friday, and it really helped users to build a mental model of how Notify works, so that when they’re playing around with it they have a greater sense of what they’re aiming to do. This makes concepts like templates and placeholders click more quickly. https://www.pivotaltracker.com/story/show/116710119
This commit is contained in:
BIN
app/assets/images/tour-next.png
Normal file
BIN
app/assets/images/tour-next.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 983 B |
BIN
app/assets/images/tour/2.png
Normal file
BIN
app/assets/images/tour/2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
BIN
app/assets/images/tour/3.png
Normal file
BIN
app/assets/images/tour/3.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
BIN
app/assets/images/tour/4.png
Normal file
BIN
app/assets/images/tour/4.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 71 KiB |
@@ -127,4 +127,4 @@ a[rel="external"] {
|
||||
@include media(tablet) {
|
||||
@include external-link-19;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
a {
|
||||
&:link,
|
||||
&:visited {
|
||||
color: $text-colour;
|
||||
padding: $gutter-half;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
@@ -110,13 +110,54 @@
|
||||
background: $govuk-blue;
|
||||
color: $white;
|
||||
margin-top: $gutter;
|
||||
padding: $gutter-half;
|
||||
|
||||
.heading-medium {
|
||||
p {
|
||||
margin: 0 0 10px 0;
|
||||
}
|
||||
|
||||
&-action {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.banner-tour {
|
||||
|
||||
@extend %banner;
|
||||
background: $govuk-blue;
|
||||
color: $white;
|
||||
margin-top: $gutter;
|
||||
padding: $gutter * 2;
|
||||
|
||||
.heading-large {
|
||||
margin: 0 0 $gutter 0;
|
||||
}
|
||||
|
||||
p {
|
||||
|
||||
margin-bottom: $gutter;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0
|
||||
}
|
||||
|
||||
& + p {
|
||||
margin-top: -$gutter-half;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
a {
|
||||
|
||||
@include bold-24;
|
||||
display: inline-block;
|
||||
background-image: file-url('tour-next.png');
|
||||
background-size: auto 24px;
|
||||
padding: 0 23px 0 0;
|
||||
background-position: right 3px;
|
||||
background-repeat: no-repeat;
|
||||
|
||||
&:link,
|
||||
&:visited {
|
||||
color: $white;
|
||||
@@ -124,19 +165,25 @@
|
||||
|
||||
&:hover,
|
||||
&:active {
|
||||
color: $light-blue-25;
|
||||
background-color: $link-hover-colour;
|
||||
outline: 10px solid $link-hover-colour;
|
||||
}
|
||||
|
||||
&:active,
|
||||
&:focus {
|
||||
background-color: $yellow;
|
||||
outline: 10px solid $yellow;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.big-number {
|
||||
|
||||
margin-top: 10px;
|
||||
|
||||
&-label {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
img {
|
||||
max-width: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
&-image-flush-bottom {
|
||||
margin: 40px 0 -60px 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -23,5 +23,6 @@ from app.main.views import (
|
||||
api_keys,
|
||||
manage_users,
|
||||
invites,
|
||||
all_services
|
||||
all_services,
|
||||
tour
|
||||
)
|
||||
|
||||
@@ -44,7 +44,7 @@ def add_service():
|
||||
user_id=session['user_id'],
|
||||
email_from=email_from)
|
||||
|
||||
return redirect(url_for('main.service_dashboard', service_id=service_id))
|
||||
return redirect(url_for('main.tour', service_id=service_id, page=1))
|
||||
else:
|
||||
return render_template(
|
||||
'views/add-service.html',
|
||||
|
||||
15
app/main/views/tour.py
Normal file
15
app/main/views/tour.py
Normal file
@@ -0,0 +1,15 @@
|
||||
from flask import render_template
|
||||
from flask_login import login_required
|
||||
|
||||
from app.main import main
|
||||
|
||||
|
||||
@main.route("/services/<service_id>/tour/<int:page>")
|
||||
@login_required
|
||||
def tour(service_id, page):
|
||||
return render_template(
|
||||
'views/tour/{}.html'.format(page),
|
||||
service_id=service_id, # TODO: fix when Nick’s PR is merged
|
||||
current_page=page,
|
||||
next_page=(page + 1)
|
||||
)
|
||||
@@ -5,26 +5,26 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block maincolumn_content %}
|
||||
|
||||
{% if not templates and current_user.has_permissions(['send_texts', 'send_emails', 'send_letters'], any_=True) %}
|
||||
{% include 'views/dashboard/get-started.html' %}
|
||||
{% elif service.restricted %}
|
||||
<div class="dashboard">
|
||||
{% include 'views/dashboard/trial-mode-banner.html' %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if service.restricted %}
|
||||
{% include 'views/dashboard/trial-mode-banner.html' %}
|
||||
{% endif %}
|
||||
|
||||
{% if not templates and current_user.has_permissions(['send_texts', 'send_emails', 'send_letters'], any_=True) %}
|
||||
{% include 'views/dashboard/get-started.html' %}
|
||||
{% endif %}
|
||||
|
||||
{% if templates %}
|
||||
<div
|
||||
data-module="update-content"
|
||||
data-resource="{{url_for(".service_dashboard_updates", service_id=service_id)}}"
|
||||
data-key="today"
|
||||
data-interval-seconds="10"
|
||||
>
|
||||
{% include 'views/dashboard/today.html' %}
|
||||
</div>
|
||||
<div
|
||||
data-module="update-content"
|
||||
data-resource="{{url_for(".service_dashboard_updates", service_id=service_id)}}"
|
||||
data-key="today"
|
||||
data-interval-seconds="10"
|
||||
>
|
||||
{% include 'views/dashboard/today.html' %}
|
||||
</div>
|
||||
|
||||
{% include 'views/dashboard/jobs.html' %}
|
||||
{% endif %}
|
||||
{% include 'views/dashboard/jobs.html' %}
|
||||
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,30 +1,23 @@
|
||||
{% from "components/banner.html" import banner_wrapper %}
|
||||
|
||||
<h2 class="heading-medium">Get started</h2>
|
||||
{% if current_user.has_permissions(['manage_templates']) %}
|
||||
<p>
|
||||
You need to set up a template before you can send messages
|
||||
</p>
|
||||
<ol class="grid-row">
|
||||
<li class="column-half">
|
||||
{% call banner_wrapper(type="tip") %}
|
||||
<a href='{{ url_for(".add_service_template", service_id=service_id, template_type="sms") }}' style="white-space: nowrap;">
|
||||
Set up a text message template
|
||||
</a>
|
||||
{% endcall %}
|
||||
</li>
|
||||
<li class="column-half">
|
||||
{% call banner_wrapper(type="tip") %}
|
||||
<a href='{{ url_for(".add_service_template", service_id=service_id, template_type="email") }}'>
|
||||
Set up an email template
|
||||
</a>
|
||||
{% endcall %}
|
||||
</li>
|
||||
</ol>
|
||||
{% call banner_wrapper(type='tour') %}
|
||||
<h2 class="heading-large">You’re ready to get started</h2>
|
||||
<p>
|
||||
<a href='{{ url_for(".add_service_template", service_id=service_id, template_type="sms") }}'>
|
||||
Set up a text message template
|
||||
</a>
|
||||
</p>
|
||||
<p>
|
||||
<a href='{{ url_for(".add_service_template", service_id=service_id, template_type="email") }}'>
|
||||
Set up an email template
|
||||
</a>
|
||||
</p>
|
||||
{% endcall %}
|
||||
{% else %}
|
||||
<p>
|
||||
{% call banner_wrapper(type='mode') %}
|
||||
<p>
|
||||
You need to ask your service manager to set up some templates before you can send messages
|
||||
</p>
|
||||
</p>
|
||||
{% endcall %}
|
||||
{% endif %}
|
||||
|
||||
@@ -2,24 +2,12 @@
|
||||
{% from "components/big-number.html" import big_number %}
|
||||
|
||||
{% call banner_wrapper(type="mode") %}
|
||||
|
||||
<h2 class="heading-medium">Trial mode</h2>
|
||||
<div class="grid-row">
|
||||
<div class="column-one-half">
|
||||
<p>
|
||||
We’ll only deliver messages to you and members of your team
|
||||
<br>
|
||||
<a href="{{ url_for(".help", _anchor="trial-mode") }}">Find out more</a>
|
||||
</p>
|
||||
Your service is in trial mode
|
||||
</div>
|
||||
<div class="column-one-sixth">
|
||||
|
||||
</div>
|
||||
<div class="column-one-third">
|
||||
{{ big_number(
|
||||
service.limit - statistics.get('emails_requested', 0) - statistics.get('sms_requested', 0),
|
||||
'messages left today'
|
||||
) }}
|
||||
<div class="column-one-half">
|
||||
<a href="{{ url_for(".help", _anchor="trial-mode") }}" class="banner-mode-action">Find out more</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endcall %}
|
||||
|
||||
25
app/templates/views/tour/1.html
Normal file
25
app/templates/views/tour/1.html
Normal file
@@ -0,0 +1,25 @@
|
||||
{% extends "withoutnav_template.html" %}
|
||||
{% from "components/textbox.html" import textbox %}
|
||||
{% from "components/page-footer.html" import page_footer %}
|
||||
{% from "components/banner.html" import banner_wrapper %}
|
||||
|
||||
{% block page_title %}
|
||||
{{heading}} – GOV.UK Notify
|
||||
{% endblock %}
|
||||
|
||||
{% block maincolumn_content %}
|
||||
|
||||
{% call banner_wrapper(type='tour') %}
|
||||
<h2 class="heading-large">Trial mode</h2>
|
||||
<p>
|
||||
To start off with, you can only send messages to yourself.
|
||||
</p>
|
||||
<p>
|
||||
We can remove these restrictions when you’re ready.
|
||||
</p>
|
||||
<a href='{{ url_for('.tour', service_id=service_id, page=next_page) }}'>
|
||||
Next
|
||||
</a>
|
||||
{% endcall %}
|
||||
|
||||
{% endblock %}
|
||||
29
app/templates/views/tour/2.html
Normal file
29
app/templates/views/tour/2.html
Normal file
@@ -0,0 +1,29 @@
|
||||
{% extends "withoutnav_template.html" %}
|
||||
{% from "components/textbox.html" import textbox %}
|
||||
{% from "components/page-footer.html" import page_footer %}
|
||||
{% from "components/banner.html" import banner_wrapper %}
|
||||
|
||||
{% block page_title %}
|
||||
{{heading}} – GOV.UK Notify
|
||||
{% endblock %}
|
||||
|
||||
{% block maincolumn_content %}
|
||||
|
||||
{% call banner_wrapper(type='tour') %}
|
||||
<h2 class="heading-large">Start with templates</h2>
|
||||
<p>
|
||||
Set up a template like this:
|
||||
</p>
|
||||
<p>
|
||||
<img
|
||||
src="/static/images/tour/{{ current_page }}.png"
|
||||
width="554" height="97"
|
||||
alt="A template for a text message with placeholders for the recipients name, document and date"
|
||||
>
|
||||
</p>
|
||||
<a href='{{ url_for('.tour', service_id=service_id, page=next_page) }}'>
|
||||
Next
|
||||
</a>
|
||||
{% endcall %}
|
||||
|
||||
{% endblock %}
|
||||
32
app/templates/views/tour/3.html
Normal file
32
app/templates/views/tour/3.html
Normal file
@@ -0,0 +1,32 @@
|
||||
{% extends "withoutnav_template.html" %}
|
||||
{% from "components/textbox.html" import textbox %}
|
||||
{% from "components/page-footer.html" import page_footer %}
|
||||
{% from "components/banner.html" import banner_wrapper %}
|
||||
|
||||
{% block page_title %}
|
||||
{{heading}} – GOV.UK Notify
|
||||
{% endblock %}
|
||||
|
||||
{% block maincolumn_content %}
|
||||
|
||||
{% call banner_wrapper(type='tour') %}
|
||||
<h2 class="heading-large">Add recipients</h2>
|
||||
<p>
|
||||
Add recipients by uploading a .csv spreadsheet:
|
||||
</p>
|
||||
<p>
|
||||
<img
|
||||
src="/static/images/tour/{{ current_page }}.png"
|
||||
width="554" height="118"
|
||||
alt="A screenshot of a spreadsheet containing data about three people"
|
||||
>
|
||||
</p>
|
||||
<p>
|
||||
Developers, you can add data automatically using an API
|
||||
</p>
|
||||
<a href='{{ url_for('.tour', service_id=service_id, page=next_page) }}'>
|
||||
Next
|
||||
</a>
|
||||
{% endcall %}
|
||||
|
||||
{% endblock %}
|
||||
28
app/templates/views/tour/4.html
Normal file
28
app/templates/views/tour/4.html
Normal file
@@ -0,0 +1,28 @@
|
||||
{% extends "withoutnav_template.html" %}
|
||||
{% from "components/textbox.html" import textbox %}
|
||||
{% from "components/page-footer.html" import page_footer %}
|
||||
{% from "components/banner.html" import banner_wrapper %}
|
||||
|
||||
{% block page_title %}
|
||||
{{heading}} – GOV.UK Notify
|
||||
{% endblock %}
|
||||
|
||||
{% block maincolumn_content %}
|
||||
|
||||
{% call banner_wrapper(type='tour') %}
|
||||
<h2 class="heading-large">Send your messages</h2>
|
||||
<p>
|
||||
Notify merges your data with the template and sends the messages
|
||||
</p>
|
||||
<a href="{{ url_for('.service_dashboard', service_id=service_id) }}">
|
||||
Next
|
||||
</a>
|
||||
<img
|
||||
src="/static/images/tour/{{ current_page }}.png"
|
||||
class="banner-tour-image-flush-bottom"
|
||||
width="840" height="290"
|
||||
alt="Three mobiles phones, each showing a text message personalised with data about the recipient"
|
||||
>
|
||||
{% endcall %}
|
||||
|
||||
{% endblock %}
|
||||
@@ -26,7 +26,7 @@ def test_should_add_service_and_redirect_to_next_page(app_,
|
||||
url_for('main.add_service'),
|
||||
data={'name': 'testing the post'})
|
||||
assert response.status_code == 302
|
||||
assert response.location == url_for('main.service_dashboard', service_id=101, _external=True)
|
||||
assert response.location == url_for('main.tour', service_id=101, page=1, _external=True)
|
||||
assert mock_get_services.called
|
||||
mock_create_service.asset_called_once_with(service_name='testing the post',
|
||||
active=False,
|
||||
|
||||
19
tests/app/main/views/test_tour.py
Normal file
19
tests/app/main/views/test_tour.py
Normal file
@@ -0,0 +1,19 @@
|
||||
import pytest
|
||||
from flask import url_for
|
||||
|
||||
import app
|
||||
|
||||
|
||||
@pytest.mark.parametrize("page", range(1, 5))
|
||||
def test_should_render_tour_pages(
|
||||
app_,
|
||||
api_user_active,
|
||||
mocker,
|
||||
page
|
||||
):
|
||||
with app_.test_request_context():
|
||||
with app_.test_client() as client:
|
||||
client.login(api_user_active, mocker)
|
||||
response = client.get(url_for('main.tour', service_id=101, page=page))
|
||||
assert response.status_code == 200
|
||||
assert 'Next' in response.get_data(as_text=True)
|
||||
Reference in New Issue
Block a user