Merge pull request #3547 from alphagov/broadcast-tour

Add a tour for users new to broadcast services
This commit is contained in:
Chris Hill-Scott
2020-08-03 14:21:03 +01:00
committed by GitHub
16 changed files with 3400 additions and 0 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

File diff suppressed because one or more lines are too long

View File

@@ -78,6 +78,11 @@
box-shadow: inset 0 -1em 1.6em 0 rgba(0, 0, 0, 0.05); box-shadow: inset 0 -1em 1.6em 0 rgba(0, 0, 0, 0.05);
border: 0; border: 0;
&-no-fixed-height {
height: auto;
min-height: 425px;
}
.heading-medium { .heading-medium {
@include core-24; @include core-24;
} }
@@ -98,6 +103,18 @@
} }
ul {
@include core-24;
color: $white;
margin-bottom: govuk-spacing(5);
}
li {
margin-bottom: govuk-spacing(3);
list-style-type: disc;
margin-left: govuk-spacing(6);
}
a { a {
font-weight: bold; font-weight: bold;
@@ -123,6 +140,10 @@
} }
} }
img {
width: 100%;
height: auto;
}
.greyed-out-step { .greyed-out-step {
opacity: 0.6; opacity: 0.6;

View File

@@ -19,6 +19,17 @@ from app.models.broadcast_message import BroadcastMessage, BroadcastMessages
from app.utils import service_has_permission, user_has_permissions from app.utils import service_has_permission, user_has_permissions
@main.route('/services/<uuid:service_id>/broadcast-tour/<int:step_index>')
@user_has_permissions()
@service_has_permission('broadcast')
def broadcast_tour(service_id, step_index):
if step_index not in (1, 2, 3):
abort(404)
return render_template(
f'views/broadcast/tour/{step_index}.html'
)
@main.route('/services/<uuid:service_id>/broadcast-dashboard') @main.route('/services/<uuid:service_id>/broadcast-dashboard')
@user_has_permissions() @user_has_permissions()
@service_has_permission('broadcast') @service_has_permission('broadcast')

View File

@@ -40,6 +40,9 @@ def accept_invite(token):
if invited_user.status == 'accepted': if invited_user.status == 'accepted':
session.pop('invited_user', None) session.pop('invited_user', None)
service = Service.from_id(invited_user.service)
if service.has_permission('broadcast'):
return redirect(url_for('main.broadcast_tour', service_id=service.id, step_index=1))
return redirect(url_for('main.service_dashboard', service_id=invited_user.service)) return redirect(url_for('main.service_dashboard', service_id=invited_user.service))
session['invited_user'] = invited_user.serialize() session['invited_user'] = invited_user.serialize()
@@ -66,6 +69,8 @@ def accept_invite(token):
permissions=invited_user.permissions, permissions=invited_user.permissions,
folder_permissions=invited_user.folder_permissions, folder_permissions=invited_user.folder_permissions,
) )
if service.has_permission('broadcast'):
return redirect(url_for('main.broadcast_tour', service_id=service.id, step_index=1))
return redirect(url_for('main.service_dashboard', service_id=service.id)) return redirect(url_for('main.service_dashboard', service_id=service.id))
else: else:
return redirect(url_for('main.register_from_invite')) return redirect(url_for('main.register_from_invite'))

View File

@@ -15,6 +15,7 @@ from notifications_utils.url_safe_token import check_token
from app import user_api_client from app import user_api_client
from app.main import main from app.main import main
from app.main.forms import TwoFactorForm from app.main.forms import TwoFactorForm
from app.models.service import Service
from app.models.user import InvitedUser, User from app.models.user import InvitedUser, User
from app.utils import redirect_to_sign_in from app.utils import redirect_to_sign_in
@@ -77,6 +78,9 @@ def activate_user(user_id):
invited_user = session.get('invited_user') invited_user = session.get('invited_user')
if invited_user: if invited_user:
service_id = _add_invited_user_to_service(invited_user) service_id = _add_invited_user_to_service(invited_user)
service = Service.from_id(service_id)
if service.has_permission('broadcast'):
return redirect(url_for('main.broadcast_tour', service_id=service.id, step_index=1))
return redirect(url_for('main.service_dashboard', service_id=service_id)) return redirect(url_for('main.service_dashboard', service_id=service_id))
invited_org_user = session.get('invited_org_user') invited_org_user = session.get('invited_org_user')

View File

@@ -352,6 +352,7 @@ class HeaderNavigation(Navigation):
'old_guest_list', 'old_guest_list',
'who_can_use_notify', 'who_can_use_notify',
'who_its_for', 'who_its_for',
'broadcast_tour',
'broadcast_dashboard', 'broadcast_dashboard',
'broadcast_dashboard_updates', 'broadcast_dashboard_updates',
'broadcast', 'broadcast',
@@ -375,6 +376,7 @@ class MainNavigation(Navigation):
mapping = { mapping = {
'dashboard': { 'dashboard': {
'broadcast_tour',
'broadcast_dashboard', 'broadcast_dashboard',
'broadcast_dashboard_updates', 'broadcast_dashboard_updates',
'conversation', 'conversation',
@@ -702,6 +704,7 @@ class CaseworkNavigation(Navigation):
mapping = { mapping = {
'dashboard': { 'dashboard': {
'broadcast_tour',
'broadcast_dashboard', 'broadcast_dashboard',
'broadcast_dashboard_updates', 'broadcast_dashboard_updates',
}, },
@@ -1330,6 +1333,7 @@ class OrgNavigation(Navigation):
'old_guest_list', 'old_guest_list',
'who_can_use_notify', 'who_can_use_notify',
'who_its_for', 'who_its_for',
'broadcast_tour',
'broadcast_dashboard', 'broadcast_dashboard',
'broadcast_dashboard_updates', 'broadcast_dashboard_updates',
'broadcast', 'broadcast',

View File

@@ -10,6 +10,14 @@
<h1 class="govuk-visually-hidden">Dashboard</h1> <h1 class="govuk-visually-hidden">Dashboard</h1>
{% if current_user.has_permissions('manage_templates') and not current_service.all_templates %}
<nav class="govuk-!-margin-top-2 govuk-!-margin-bottom-6">
<a class="govuk-link govuk-link--no-visited-state pill-separate-item govuk-!-padding-top-4 govuk-!-padding-bottom-4 govuk-!-font-weight-bold" href="{{ url_for('.choose_template', service_id=current_service.id) }}">
Start by creating a template
</a>
</nav>
{% endif %}
<h2 class="heading-medium govuk-!-margin-bottom-2">Live broadcasts</h2> <h2 class="heading-medium govuk-!-margin-bottom-2">Live broadcasts</h2>
{{ ajax_block( {{ ajax_block(

View File

@@ -0,0 +1,41 @@
{% from "components/banner.html" import banner_wrapper %}
{% extends "admin_template.html" %}
{% block per_page_title %}
Step 1
{% endblock %}
{% set mainClasses = "govuk-!-padding-top-0 govuk-!-padding-bottom-0" %}
{% block content %}
<div class="banner-tour banner-tour-no-fixed-height govuk-!-margin-top-0">
<div class="govuk-grid-row">
<div class="govuk-grid-column-one-half">
<h1 class="heading-medium">
Youve been invited to use GOV.UK Notify to broadcast alerts
</h1>
<ul>
<li>
Broadcasting is a new technology for alerting members of the
public
</li>
<li>
It should be used when theres an imminent risk to life
</li>
</ul>
<p class="govuk-body heading-medium">
<a class="govuk-link govuk-link--no-visited-state" href='{{ url_for(".broadcast_tour", service_id=current_service.id, step_index=2) }}'>
Find out how it works
</a>
</p>
</div>
<div class="govuk-grid-column-one-half">
<img src="{{ asset_url('images/broadcast-tour/1.png') }}" alt="">
<link rel="preload" href="{{ asset_url('images/broadcast-tour/2.png') }}" as="image">
</div>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,44 @@
{% from "components/banner.html" import banner_wrapper %}
{% extends "admin_template.html" %}
{% block per_page_title %}
Step 2
{% endblock %}
{% set mainClasses = "govuk-!-padding-top-0 govuk-!-padding-bottom-0" %}
{% block content %}
<div class="banner-tour banner-tour-no-fixed-height govuk-!-margin-top-0">
<div class="govuk-grid-row">
<div class="govuk-grid-column-one-half">
<h1 class="heading-medium">
A broadcast will alert every phone in an area
</h1>
<ul>
<li>
The alert will appear even if the phones screen is locked
</li>
<li>
The phone will play an alarm sound even if its set to silent
</li>
<li>
Any phone that enters the area while the broadcast is live will
get the alert
</li>
</ul>
<p class="govuk-body heading-medium govuk-!-margin-bottom-6">
<a class="govuk-link govuk-link--no-visited-state" href='{{ url_for(".broadcast_tour", service_id=current_service.id, step_index=3) }}'>
How to try it out
</a>
</p>
</div>
<div class="govuk-grid-column-one-half">
<img src="{{ asset_url('images/broadcast-tour/2.png') }}" alt="">
<link rel="preload" href="{{ asset_url('images/broadcast-tour/3.png') }}" as="image">
</div>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,43 @@
{% from "components/banner.html" import banner_wrapper %}
{% extends "admin_template.html" %}
{% block per_page_title %}
Step 3
{% endblock %}
{% set mainClasses = "govuk-!-padding-top-0 govuk-!-padding-bottom-0" %}
{% block content %}
<div class="banner-tour banner-tour-no-fixed-height govuk-!-margin-top-0">
<div class="govuk-grid-row">
<div class="govuk-grid-column-one-half">
<h1 class="heading-medium">
You have a trial account
</h1>
<ul>
<li>
Theres no risk of triggering a real, live broadcast
</li>
<li>
Get familiar with the system now, so youre ready to use it if
theres an emergency
</li>
<li>
When youre ready, apply for a live account
</li>
</ul>
<p class="govuk-body heading-medium">
<a class="govuk-link govuk-link--no-visited-state" href='{{ url_for(".service_dashboard", service_id=current_service.id) }}'>
Continue to dashboard
</a>
</p>
</div>
<div class="govuk-grid-column-one-half">
<img src="{{ asset_url('images/broadcast-tour/3.png') }}" alt="">
</div>
</div>
</div>
{% endblock %}

View File

@@ -94,6 +94,7 @@ def test_empty_broadcast_dashboard(
client_request, client_request,
service_one, service_one,
mock_get_no_broadcast_messages, mock_get_no_broadcast_messages,
mock_get_service_templates_when_no_templates_exist,
): ):
service_one['permissions'] += ['broadcast'] service_one['permissions'] += ['broadcast']
page = client_request.get( page = client_request.get(
@@ -114,6 +115,7 @@ def test_broadcast_dashboard(
client_request, client_request,
service_one, service_one,
mock_get_broadcast_messages, mock_get_broadcast_messages,
mock_get_service_templates,
): ):
service_one['permissions'] += ['broadcast'] service_one['permissions'] += ['broadcast']
page = client_request.get( page = client_request.get(

View File

@@ -319,6 +319,7 @@ def test_register_from_email_auth_invite(
mock_accept_invite, mock_accept_invite,
mock_create_event, mock_create_event,
mock_add_user_to_service, mock_add_user_to_service,
mock_get_service,
invite_email_address, invite_email_address,
): ):
sample_invite['auth_type'] = 'email_auth' sample_invite['auth_type'] = 'email_auth'
@@ -372,6 +373,7 @@ def test_can_register_email_auth_without_phone_number(
mock_accept_invite, mock_accept_invite,
mock_create_event, mock_create_event,
mock_add_user_to_service, mock_add_user_to_service,
mock_get_service,
): ):
sample_invite['auth_type'] = 'email_auth' sample_invite['auth_type'] = 'email_auth'
with client.session_transaction() as session: with client.session_transaction() as session:

View File

@@ -178,6 +178,7 @@ def test_activate_user_redirects_to_service_dashboard_if_user_already_belongs_to
sample_invite, sample_invite,
api_user_active, api_user_active,
mock_login, mock_login,
mock_get_service,
): ):
mocker.patch('app.user_api_client.add_user_to_service', side_effect=HTTPError( mocker.patch('app.user_api_client.add_user_to_service', side_effect=HTTPError(
response=Mock( response=Mock(