From 3989d1b5761e28d3b503d97491878c2ac9b5d9e6 Mon Sep 17 00:00:00 2001 From: Chris Hill-Scott Date: Thu, 7 Jan 2016 12:29:56 +0000 Subject: [PATCH 1/5] =?UTF-8?q?Add=20pages=20for=20=E2=80=98service=20sett?= =?UTF-8?q?ings=E2=80=99=20flow?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds the pages and wires them together, so that it’s possible to click through them. The wording is not quite English, but attempts to be an rough description of what the consequences are for each of the four actions. --- .../stylesheets/components/browse-list.scss | 27 ++++++++ .../stylesheets/components/submit-form.scss | 6 ++ app/assets/stylesheets/main.scss | 1 + app/main/__init__.py | 2 +- app/main/views/index.py | 5 -- app/main/views/service_settings.py | 62 +++++++++++++++++++ app/templates/components/browse-list.html | 20 ++++++ app/templates/components/list.html | 11 ++++ app/templates/components/submit-form.html | 6 +- app/templates/components/table.html | 37 +++++++---- app/templates/main_nav.html | 2 +- app/templates/views/dashboard.html | 5 +- app/templates/views/job.html | 4 +- app/templates/views/jobs.html | 4 +- app/templates/views/service-settings.html | 34 ++++++++-- .../views/service-settings/delete.html | 22 +++++++ .../views/service-settings/name.html | 22 +++++++ .../service-settings/request-to-go-live.html | 34 ++++++++++ .../views/service-settings/status.html | 33 ++++++++++ tests/app/main/views/test_service_settings.py | 61 ++++++++++++++++++ 20 files changed, 364 insertions(+), 34 deletions(-) create mode 100644 app/assets/stylesheets/components/browse-list.scss create mode 100644 app/main/views/service_settings.py create mode 100644 app/templates/components/browse-list.html create mode 100644 app/templates/components/list.html create mode 100644 app/templates/views/service-settings/delete.html create mode 100644 app/templates/views/service-settings/name.html create mode 100644 app/templates/views/service-settings/request-to-go-live.html create mode 100644 app/templates/views/service-settings/status.html create mode 100644 tests/app/main/views/test_service_settings.py diff --git a/app/assets/stylesheets/components/browse-list.scss b/app/assets/stylesheets/components/browse-list.scss new file mode 100644 index 000000000..1393aa352 --- /dev/null +++ b/app/assets/stylesheets/components/browse-list.scss @@ -0,0 +1,27 @@ +.browse-list { + + margin-bottom: $gutter; + + &-item { + list-style: none; + margin-bottom: $gutter-two-thirds; + } + + a.browse-list-link { + + @include bold-24; + + &-destructive, + &-destructive:visited { + @include bold-24; + color: $error-colour; + } + + } + + &-hint { + @include core-16; + margin-top: 5px; + } + +} diff --git a/app/assets/stylesheets/components/submit-form.scss b/app/assets/stylesheets/components/submit-form.scss index 041c71773..7a6681e46 100644 --- a/app/assets/stylesheets/components/submit-form.scss +++ b/app/assets/stylesheets/components/submit-form.scss @@ -9,4 +9,10 @@ margin-left: 5px; } + .button {} + + .button-destructive { + @include button($error-colour) + } + } diff --git a/app/assets/stylesheets/main.scss b/app/assets/stylesheets/main.scss index ece58780a..ce2093c01 100644 --- a/app/assets/stylesheets/main.scss +++ b/app/assets/stylesheets/main.scss @@ -39,6 +39,7 @@ @import 'components/big-number'; @import 'components/banner'; @import 'components/textbox'; +@import 'components/browse-list'; @import 'views/job'; diff --git a/app/main/__init__.py b/app/main/__init__.py index 93354853b..9354e06db 100644 --- a/app/main/__init__.py +++ b/app/main/__init__.py @@ -5,5 +5,5 @@ main = Blueprint('main', __name__) from app.main.views import ( index, sign_in, sign_out, register, two_factor, verify, sms, add_service, - code_not_received, jobs, dashboard, templates + code_not_received, jobs, dashboard, templates, service_settings ) diff --git a/app/main/views/index.py b/app/main/views/index.py index 9c9ef1f6c..dec87cf89 100644 --- a/app/main/views/index.py +++ b/app/main/views/index.py @@ -54,11 +54,6 @@ def manageusers(): return render_template('views/manage-users.html') -@main.route("/service-settings") -def servicesettings(): - return render_template('views/service-settings.html') - - @main.route("/api-keys") def apikeys(): return render_template('views/api-keys.html') diff --git a/app/main/views/service_settings.py b/app/main/views/service_settings.py new file mode 100644 index 000000000..0bdd9ba71 --- /dev/null +++ b/app/main/views/service_settings.py @@ -0,0 +1,62 @@ +from flask import render_template, redirect, request, url_for +from flask_login import login_required + +from app.main import main + +service = { + 'name': 'Service name', + 'live': False, + 'active': True +} + + +@main.route("/service-settings") +def service_settings(): + return render_template( + 'views/service-settings.html', + service=service + ) + + +@main.route("/service-settings/name", methods=['GET', 'POST']) +def name(): + if request.method == 'GET': + return render_template( + 'views/service-settings/name.html', + service=service + ) + elif request.method == 'POST': + return redirect(url_for('.service_settings')) + + +@main.route("/service-settings/request-to-go-live", methods=['GET', 'POST']) +def request_to_go_live(): + if request.method == 'GET': + return render_template( + 'views/service-settings/request-to-go-live.html', + service=service + ) + elif request.method == 'POST': + return redirect(url_for('.service_settings')) + + +@main.route("/service-settings/status", methods=['GET', 'POST']) +def status(): + if request.method == 'GET': + return render_template( + 'views/service-settings/status.html', + service=service + ) + elif request.method == 'POST': + return redirect(url_for('.service_settings')) + + +@main.route("/service-settings/delete", methods=['GET', 'POST']) +def delete(): + if request.method == 'GET': + return render_template( + 'views/service-settings/delete.html', + service=service + ) + elif request.method == 'POST': + return redirect(url_for('.index')) diff --git a/app/templates/components/browse-list.html b/app/templates/components/browse-list.html new file mode 100644 index 000000000..d59e9ae37 --- /dev/null +++ b/app/templates/components/browse-list.html @@ -0,0 +1,20 @@ +{% macro browse_list(items) %} + {% if items %} + + {% endif %} +{% endmacro %} diff --git a/app/templates/components/list.html b/app/templates/components/list.html new file mode 100644 index 000000000..71025a8ed --- /dev/null +++ b/app/templates/components/list.html @@ -0,0 +1,11 @@ +{% macro list(items) %} + {% if items %} + + {% endif %} +{% endmacro %} diff --git a/app/templates/components/submit-form.html b/app/templates/components/submit-form.html index 012d0a580..4627b6204 100644 --- a/app/templates/components/submit-form.html +++ b/app/templates/components/submit-form.html @@ -1,9 +1,9 @@ -{% macro submit_form(button_text, back_link=False) %} +{% macro submit_form(button_text, back_link=False, back_link_text="Back", destructive=False) %}
- + {% if back_link %} - Back + {{ back_link_text }} {% endif %}
{% endmacro %} diff --git a/app/templates/components/table.html b/app/templates/components/table.html index ee3c2838f..bfa53232d 100644 --- a/app/templates/components/table.html +++ b/app/templates/components/table.html @@ -1,4 +1,4 @@ -{% macro table(items, caption='', field_headings='', field_headings_visible=True, caption_visible=True) -%} +{% macro mapping_table(caption='', field_headings=[], field_headings_visible=True, caption_visible=True) -%} - {% if items %} - {% for item in items %} - - {{ caller(item) }} - - {% endfor %} - {% else %} -

- {{ empty_message }} -

- {% endif %} + {{ caller() }}
{{ caption }} @@ -17,21 +17,34 @@
{%- endmacro %} +{% macro list_table(items, caption='', empty_message='', field_headings=[], field_headings_visible=True, caption_visible=True) -%} + {% if items %} + {% set parent_caller = caller %} + {% call mapping_table(caption, field_headings, field_headings_visible, caption_visible) %} + {% for item in items %} + {% call row() %} + {{ parent_caller(item) }} + {% endcall %} + {% endfor %} + {%- endcall %} + {% else %} +

+ {{ empty_message }} +

+ {% endif %} +{%- endmacro %} + +{% macro row() -%} + + {{ caller() }} + +{%- endmacro %} + {% macro field(align='left', status='') -%} {{ caller() }} diff --git a/app/templates/main_nav.html b/app/templates/main_nav.html index f252d98f3..3fab88acb 100644 --- a/app/templates/main_nav.html +++ b/app/templates/main_nav.html @@ -10,7 +10,7 @@ - {% call(item) table( + {% call(item) list_table( jobs[:3], caption="Recent text messages", + empty_message="No recent text messages", field_headings=['Job', 'File', 'Time', 'Status'] ) %} {% call field() %} diff --git a/app/templates/views/job.html b/app/templates/views/job.html index 963ff1810..29ae57ba7 100644 --- a/app/templates/views/job.html +++ b/app/templates/views/job.html @@ -1,5 +1,5 @@ {% extends "withnav_template.html" %} -{% from "components/table.html" import table, field, right_aligned_field_heading %} +{% from "components/table.html" import list_table, field, right_aligned_field_heading %} {% from "components/big-number.html" import big_number %} {% from "components/banner.html" import banner %} @@ -41,7 +41,7 @@ GOV.UK Notify | Notifications activity Sent with template {{ template_used }} at {{ uploaded_file_time }}

- {% call(item) table( + {% call(item) list_table( messages, caption='Messages', caption_visible=False, diff --git a/app/templates/views/jobs.html b/app/templates/views/jobs.html index b9bda799d..bbcfa88fb 100644 --- a/app/templates/views/jobs.html +++ b/app/templates/views/jobs.html @@ -1,5 +1,5 @@ {% extends "withnav_template.html" %} -{% from "components/table.html" import table, field %} +{% from "components/table.html" import list_table, field %} {% block page_title %} GOV.UK Notify | Notifications activity @@ -9,7 +9,7 @@ GOV.UK Notify | Notifications activity

Notifications activity

- {% call(item) table( + {% call(item) list_table( jobs, caption="Recent activity", caption_visible=False, diff --git a/app/templates/views/service-settings.html b/app/templates/views/service-settings.html index e53fb9efe..a9e0bce19 100644 --- a/app/templates/views/service-settings.html +++ b/app/templates/views/service-settings.html @@ -1,4 +1,5 @@ {% extends "withnav_template.html" %} +{% from "components/browse-list.html" import browse_list %} {% block page_title %} GOV.UK Notify | Service settings @@ -6,12 +7,33 @@ GOV.UK Notify | Service settings {% block maincolumn_content %} -

Service settings

- -

Here's where users can update their service profile.

- -

Back to dashboard

- +

Service settings

+ {{ browse_list([ + { + 'title': 'Rename service', + 'link': url_for('.name'), + 'hint': 'Your service is named “{}”'.format(service.name) + }, + { + 'title': 'Make service live', + 'link': url_for('.request_to_go_live'), + 'hint': 'A live service can send messages to anyone', + } if not service.live else { + }, + { + 'title': 'Stop sending messages', + 'link': url_for('.status') + } if service.active else { + 'title': 'Unsuspend service', + 'link': url_for('.status') + }, + { + 'title': 'Delete everything', + 'link': url_for('.delete'), + 'hint': '', + 'destructive': True + }, + ]) }} {% endblock %} diff --git a/app/templates/views/service-settings/delete.html b/app/templates/views/service-settings/delete.html new file mode 100644 index 000000000..2449f95bd --- /dev/null +++ b/app/templates/views/service-settings/delete.html @@ -0,0 +1,22 @@ +{% extends "withnav_template.html" %} +{% from "components/submit-form.html" import submit_form %} + +{% block page_title %} + GOV.UK Notify | Service settings +{% endblock %} + +{% block maincolumn_content %} + +

Delete this service

+ +

+ All will be lost. +

+ +
+ {{ submit_form('Delete', destructive=True) }} +
+ +

Back to service settings

+ +{% endblock %} diff --git a/app/templates/views/service-settings/name.html b/app/templates/views/service-settings/name.html new file mode 100644 index 000000000..c12fb377b --- /dev/null +++ b/app/templates/views/service-settings/name.html @@ -0,0 +1,22 @@ +{% extends "withnav_template.html" %} +{% from "components/textbox.html" import textbox %} +{% from "components/submit-form.html" import submit_form %} + +{% block page_title %} +GOV.UK Notify | Service settings +{% endblock %} + +{% block maincolumn_content %} + +

Rename your service

+ +
+ {{ textbox('new_name', 'New name', value=service.name) }} + {{ submit_form('Save') }} +
+ +

Back to service settings

+ + + +{% endblock %} diff --git a/app/templates/views/service-settings/request-to-go-live.html b/app/templates/views/service-settings/request-to-go-live.html new file mode 100644 index 000000000..110f15fb6 --- /dev/null +++ b/app/templates/views/service-settings/request-to-go-live.html @@ -0,0 +1,34 @@ +{% extends "withnav_template.html" %} +{% from "components/submit-form.html" import submit_form %} +{% from "components/list.html" import list %} + +{% block page_title %} +GOV.UK Notify | Service settings +{% endblock %} + +{% block maincolumn_content %} + +

Make your service live

+ +
+
+ +

+ Before you can send messages to anyone, you need to: +

+ + {{ list([ + "Have spoken to someone", + "Have permission from someone else", + "etc." + ]) }} + +
+ {{ submit_form("Request to go live") }} +
+ +

Back to service settings

+
+
+ +{% endblock %} diff --git a/app/templates/views/service-settings/status.html b/app/templates/views/service-settings/status.html new file mode 100644 index 000000000..b7c210a98 --- /dev/null +++ b/app/templates/views/service-settings/status.html @@ -0,0 +1,33 @@ +{% extends "withnav_template.html" %} +{% from "components/submit-form.html" import submit_form %} + +{% block page_title %} +GOV.UK Notify | Service settings +{% endblock %} + +{% block maincolumn_content %} + +
+
+

Suspend your service

+ +

+ Suspending a service means it won’t send messages. Any messages that are + already queued will still be sent. +

+ +

+ You can undo this at any time. +

+ +
+ {{ submit_form('Suspend') }} +
+ +

Back to service settings

+
+
+ + + +{% endblock %} diff --git a/tests/app/main/views/test_service_settings.py b/tests/app/main/views/test_service_settings.py new file mode 100644 index 000000000..115e7121d --- /dev/null +++ b/tests/app/main/views/test_service_settings.py @@ -0,0 +1,61 @@ +def test_should_show_overview(notifications_admin): + response = notifications_admin.test_client().get('/service-settings') + + assert response.status_code == 200 + assert 'Service settings' in response.get_data(as_text=True) + + +def test_should_show_service_name(notifications_admin): + response = notifications_admin.test_client().get('/service-settings/name') + + assert response.status_code == 200 + assert 'Service name' in response.get_data(as_text=True) + + +def test_should_redirect_after_change_service_name(notifications_admin): + response = notifications_admin.test_client().post('/service-settings/request-to-go-live') + + assert response.status_code == 302 + assert 'http://localhost/service-settings' == response.location + + +def test_should_show_request_to_go_live(notifications_admin): + response = notifications_admin.test_client().get('/service-settings/request-to-go-live') + + assert response.status_code == 200 + assert 'Request to go live' in response.get_data(as_text=True) + + +def test_should_redirect_after_request_to_go_live(notifications_admin): + response = notifications_admin.test_client().post('/service-settings/request-to-go-live') + + assert response.status_code == 302 + assert 'http://localhost/service-settings' == response.location + + +def test_should_show_status_page(notifications_admin): + response = notifications_admin.test_client().get('/service-settings/status') + + assert response.status_code == 200 + assert 'Suspend your service' in response.get_data(as_text=True) + + +def test_should_show_redirect_after_status_change(notifications_admin): + response = notifications_admin.test_client().post('/service-settings/status') + + assert response.status_code == 302 + assert 'http://localhost/service-settings' == response.location + + +def test_should_show_delete_page(notifications_admin): + response = notifications_admin.test_client().get('/service-settings/delete') + + assert response.status_code == 200 + assert 'Delete service' in response.get_data(as_text=True) + + +def test_should_show_redirect_after_deleting_service(notifications_admin): + response = notifications_admin.test_client().post('/service-settings/delete') + + assert response.status_code == 302 + assert 'http://localhost/' == response.location From 0b62d1e75510e60475d60c0f9805f03cc42f122f Mon Sep 17 00:00:00 2001 From: Chris Hill-Scott Date: Thu, 7 Jan 2016 16:24:43 +0000 Subject: [PATCH 2/5] Add confirm loop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For pages where - we want you to be sure that you want to do what you’re about to do - we want to be sure it’s you trying to do the thing This adds a page that asks the user to confirm their password. --- .../stylesheets/components/browse-list.scss | 2 +- .../stylesheets/components/submit-form.scss | 3 +- app/main/views/service_settings.py | 42 +++++++++++++- app/templates/components/list.html | 11 ---- app/templates/components/textbox.html | 4 +- app/templates/views/service-settings.html | 56 +++++++++---------- .../views/service-settings/confirm.html | 27 +++++++++ .../views/service-settings/delete.html | 37 +++++++++--- .../views/service-settings/name.html | 22 +++++--- .../service-settings/request-to-go-live.html | 37 ++++++++---- .../views/service-settings/status.html | 18 +++--- tests/app/main/views/test_service_settings.py | 50 +++++++++++++++-- 12 files changed, 227 insertions(+), 82 deletions(-) delete mode 100644 app/templates/components/list.html create mode 100644 app/templates/views/service-settings/confirm.html diff --git a/app/assets/stylesheets/components/browse-list.scss b/app/assets/stylesheets/components/browse-list.scss index 1393aa352..abb95ecc0 100644 --- a/app/assets/stylesheets/components/browse-list.scss +++ b/app/assets/stylesheets/components/browse-list.scss @@ -20,7 +20,7 @@ } &-hint { - @include core-16; + @include core-19; margin-top: 5px; } diff --git a/app/assets/stylesheets/components/submit-form.scss b/app/assets/stylesheets/components/submit-form.scss index 7a6681e46..8b98d2905 100644 --- a/app/assets/stylesheets/components/submit-form.scss +++ b/app/assets/stylesheets/components/submit-form.scss @@ -12,7 +12,8 @@ .button {} .button-destructive { - @include button($error-colour) + @include button($error-colour); + padding: 0.52632em 0.78947em 0.26316em 0.78947em; } } diff --git a/app/main/views/service_settings.py b/app/main/views/service_settings.py index 0bdd9ba71..768a08a42 100644 --- a/app/main/views/service_settings.py +++ b/app/main/views/service_settings.py @@ -1,4 +1,4 @@ -from flask import render_template, redirect, request, url_for +from flask import render_template, redirect, request, url_for, abort from flask_login import login_required from app.main import main @@ -25,6 +25,18 @@ def name(): 'views/service-settings/name.html', service=service ) + elif request.method == 'POST': + return redirect(url_for('.confirm_name_change')) + + +@main.route("/service-settings/name/confirm", methods=['GET', 'POST']) +def confirm_name_change(): + if request.method == 'GET': + return render_template( + 'views/service-settings/confirm.html', + heading='Rename service', + submit_button_text='Confirm' + ) elif request.method == 'POST': return redirect(url_for('.service_settings')) @@ -47,6 +59,19 @@ def status(): 'views/service-settings/status.html', service=service ) + elif request.method == 'POST': + return redirect(url_for('.confirm_status_change')) + + +@main.route("/service-settings/status/confirm", methods=['GET', 'POST']) +def confirm_status_change(): + if request.method == 'GET': + return render_template( + 'views/service-settings/confirm.html', + heading='Turn off outgoing messages', + submit_button_text='Confirm', + destructive=True + ) elif request.method == 'POST': return redirect(url_for('.service_settings')) @@ -59,4 +84,17 @@ def delete(): service=service ) elif request.method == 'POST': - return redirect(url_for('.index')) + return redirect(url_for('.confirm_delete')) + + +@main.route("/service-settings/delete/confirm", methods=['GET', 'POST']) +def confirm_delete(): + if request.method == 'GET': + return render_template( + 'views/service-settings/confirm.html', + heading='Delete service', + submit_button_text='Confirm', + destructive=True + ) + elif request.method == 'POST': + return redirect(url_for('.dashboard')) diff --git a/app/templates/components/list.html b/app/templates/components/list.html deleted file mode 100644 index 71025a8ed..000000000 --- a/app/templates/components/list.html +++ /dev/null @@ -1,11 +0,0 @@ -{% macro list(items) %} - {% if items %} - - {% endif %} -{% endmacro %} diff --git a/app/templates/components/textbox.html b/app/templates/components/textbox.html index a32a76c4b..7a74f0985 100644 --- a/app/templates/components/textbox.html +++ b/app/templates/components/textbox.html @@ -1,8 +1,8 @@ -{% macro textbox(name, label, value='', small=True, highlight_tags=False) %} +{% macro textbox(name, label, value='', small=True, highlight_tags=False, password=False) %}
{% if small %} - + {% else %}