mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-09-09 03:33:13 -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 media(tablet) {
|
||||||
@include external-link-19;
|
@include external-link-19;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,7 +71,7 @@
|
|||||||
a {
|
a {
|
||||||
&:link,
|
&:link,
|
||||||
&:visited {
|
&:visited {
|
||||||
color: $text-colour;
|
padding: $gutter-half;
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -110,13 +110,54 @@
|
|||||||
background: $govuk-blue;
|
background: $govuk-blue;
|
||||||
color: $white;
|
color: $white;
|
||||||
margin-top: $gutter;
|
margin-top: $gutter;
|
||||||
|
padding: $gutter-half;
|
||||||
|
|
||||||
.heading-medium {
|
p {
|
||||||
margin: 0 0 10px 0;
|
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 {
|
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,
|
&:link,
|
||||||
&:visited {
|
&:visited {
|
||||||
color: $white;
|
color: $white;
|
||||||
@@ -124,19 +165,25 @@
|
|||||||
|
|
||||||
&:hover,
|
&:hover,
|
||||||
&:active {
|
&: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 {
|
img {
|
||||||
|
max-width: 100%;
|
||||||
margin-top: 10px;
|
display: block;
|
||||||
|
}
|
||||||
&-label {
|
|
||||||
padding-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
&-image-flush-bottom {
|
||||||
|
margin: 40px 0 -60px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,5 +23,6 @@ from app.main.views import (
|
|||||||
api_keys,
|
api_keys,
|
||||||
manage_users,
|
manage_users,
|
||||||
invites,
|
invites,
|
||||||
all_services
|
all_services,
|
||||||
|
tour
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ def add_service():
|
|||||||
user_id=session['user_id'],
|
user_id=session['user_id'],
|
||||||
email_from=email_from)
|
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:
|
else:
|
||||||
return render_template(
|
return render_template(
|
||||||
'views/add-service.html',
|
'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 %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block maincolumn_content %}
|
{% 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) %}
|
<div
|
||||||
{% include 'views/dashboard/get-started.html' %}
|
data-module="update-content"
|
||||||
{% endif %}
|
data-resource="{{url_for(".service_dashboard_updates", service_id=service_id)}}"
|
||||||
|
data-key="today"
|
||||||
{% if templates %}
|
data-interval-seconds="10"
|
||||||
<div
|
>
|
||||||
data-module="update-content"
|
{% include 'views/dashboard/today.html' %}
|
||||||
data-resource="{{url_for(".service_dashboard_updates", service_id=service_id)}}"
|
</div>
|
||||||
data-key="today"
|
|
||||||
data-interval-seconds="10"
|
|
||||||
>
|
|
||||||
{% include 'views/dashboard/today.html' %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% include 'views/dashboard/jobs.html' %}
|
{% include 'views/dashboard/jobs.html' %}
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -1,30 +1,23 @@
|
|||||||
{% from "components/banner.html" import banner_wrapper %}
|
{% from "components/banner.html" import banner_wrapper %}
|
||||||
|
|
||||||
<h2 class="heading-medium">Get started</h2>
|
|
||||||
{% if current_user.has_permissions(['manage_templates']) %}
|
{% if current_user.has_permissions(['manage_templates']) %}
|
||||||
<p>
|
{% call banner_wrapper(type='tour') %}
|
||||||
You need to set up a template before you can send messages
|
<h2 class="heading-large">You’re ready to get started</h2>
|
||||||
</p>
|
<p>
|
||||||
<ol class="grid-row">
|
<a href='{{ url_for(".add_service_template", service_id=service_id, template_type="sms") }}'>
|
||||||
<li class="column-half">
|
Set up a text message template
|
||||||
{% call banner_wrapper(type="tip") %}
|
</a>
|
||||||
<a href='{{ url_for(".add_service_template", service_id=service_id, template_type="sms") }}' style="white-space: nowrap;">
|
</p>
|
||||||
Set up a text message template
|
<p>
|
||||||
</a>
|
<a href='{{ url_for(".add_service_template", service_id=service_id, template_type="email") }}'>
|
||||||
{% endcall %}
|
Set up an email template
|
||||||
</li>
|
</a>
|
||||||
<li class="column-half">
|
</p>
|
||||||
{% call banner_wrapper(type="tip") %}
|
{% endcall %}
|
||||||
<a href='{{ url_for(".add_service_template", service_id=service_id, template_type="email") }}'>
|
|
||||||
Set up an email template
|
|
||||||
</a>
|
|
||||||
{% endcall %}
|
|
||||||
</li>
|
|
||||||
</ol>
|
|
||||||
{% else %}
|
{% else %}
|
||||||
<p>
|
{% call banner_wrapper(type='mode') %}
|
||||||
<p>
|
<p>
|
||||||
You need to ask your service manager to set up some templates before you can send messages
|
You need to ask your service manager to set up some templates before you can send messages
|
||||||
</p>
|
</p>
|
||||||
</p>
|
{% endcall %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -2,24 +2,12 @@
|
|||||||
{% from "components/big-number.html" import big_number %}
|
{% from "components/big-number.html" import big_number %}
|
||||||
|
|
||||||
{% call banner_wrapper(type="mode") %}
|
{% call banner_wrapper(type="mode") %}
|
||||||
|
|
||||||
<h2 class="heading-medium">Trial mode</h2>
|
|
||||||
<div class="grid-row">
|
<div class="grid-row">
|
||||||
<div class="column-one-half">
|
<div class="column-one-half">
|
||||||
<p>
|
Your service is in trial mode
|
||||||
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>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="column-one-sixth">
|
<div class="column-one-half">
|
||||||
|
<a href="{{ url_for(".help", _anchor="trial-mode") }}" class="banner-mode-action">Find out more</a>
|
||||||
</div>
|
|
||||||
<div class="column-one-third">
|
|
||||||
{{ big_number(
|
|
||||||
service.limit - statistics.get('emails_requested', 0) - statistics.get('sms_requested', 0),
|
|
||||||
'messages left today'
|
|
||||||
) }}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endcall %}
|
{% 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'),
|
url_for('main.add_service'),
|
||||||
data={'name': 'testing the post'})
|
data={'name': 'testing the post'})
|
||||||
assert response.status_code == 302
|
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
|
assert mock_get_services.called
|
||||||
mock_create_service.asset_called_once_with(service_name='testing the post',
|
mock_create_service.asset_called_once_with(service_name='testing the post',
|
||||||
active=False,
|
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