diff --git a/app/assets/stylesheets/components/banner.scss b/app/assets/stylesheets/components/banner.scss new file mode 100644 index 000000000..a801b2ec7 --- /dev/null +++ b/app/assets/stylesheets/components/banner.scss @@ -0,0 +1,21 @@ +.banner { + + @include core-19; + background: $turquoise; + color: $white; + display: block; + padding: $gutter-half $gutter; + margin: 0 0 $gutter 0; + text-align: center; + position: relative; + + &:before { + @include core-24; + content: '✔'; + position: absolute; + top: $gutter-half; + left: $gutter-half; + margin-top: -2px; + } + +} diff --git a/app/assets/stylesheets/components/big-number.scss b/app/assets/stylesheets/components/big-number.scss new file mode 100644 index 000000000..4beea24df --- /dev/null +++ b/app/assets/stylesheets/components/big-number.scss @@ -0,0 +1,10 @@ +.big-number { + + @include bold-48; + + &-label { + @include core-19; + display: block; + } + +} diff --git a/app/assets/stylesheets/components/sms-message.scss b/app/assets/stylesheets/components/sms-message.scss index 9263b8ec5..4fa727388 100644 --- a/app/assets/stylesheets/components/sms-message.scss +++ b/app/assets/stylesheets/components/sms-message.scss @@ -28,6 +28,7 @@ } &-wrapper { + width: 100%; display: inline-block; box-sizing: border-box; position: relative; @@ -41,7 +42,30 @@ &-recipient { @include copy-19; color: $secondary-text-colour; - margin: 0; + margin: -$gutter-half 0 $gutter 0; + } + + &-history { + + background: $turquoise; + color: $white; + padding: $gutter-half; + @include bold-19; + margin: 0 0 $gutter 0; + + &-heading { + + @include bold-19; + margin: 0; + + &-time { + @include inline-block; + margin-left: 10px; + font-weight: normal; + } + + } + } } diff --git a/app/assets/stylesheets/components/table.scss b/app/assets/stylesheets/components/table.scss index 33ff865ef..37478ad6d 100644 --- a/app/assets/stylesheets/components/table.scss +++ b/app/assets/stylesheets/components/table.scss @@ -1,7 +1,43 @@ -.table { +.table-heading { + text-align: left; +} + +%table-field, +.table-field { + + &:last-child { + padding-right: 0; + } + + &-status { + + &-default { + color: $secondary-text-colour; + } + + &-error { + color: $error-colour; + font-weight: bold; + } - &-heading { - text-align: left; } } + +.table-field-heading { + + &:last-child { + padding-right: 0; + } + + &-right-aligned { + display: block; + text-align: right; + } + +} + +.table-field-right-aligned { + @extend .table-field; + text-align: right; +} diff --git a/app/assets/stylesheets/main.scss b/app/assets/stylesheets/main.scss index d6f59934b..13ac54a99 100644 --- a/app/assets/stylesheets/main.scss +++ b/app/assets/stylesheets/main.scss @@ -27,6 +27,7 @@ @import '../govuk_elements/public/sass/elements/panels'; @import '../govuk_elements/public/sass/elements/tables'; + // Specific to this application @import 'grids'; @import 'components/template-picker'; @@ -35,6 +36,10 @@ @import 'components/submit-form'; @import 'components/table'; @import 'components/navigation'; +@import 'components/big-number'; +@import 'components/banner'; + +@import 'views/job'; // TODO: break this up @import 'app'; diff --git a/app/assets/stylesheets/views/job.scss b/app/assets/stylesheets/views/job.scss new file mode 100644 index 000000000..35d54e57d --- /dev/null +++ b/app/assets/stylesheets/views/job.scss @@ -0,0 +1,3 @@ +.job-totals { + margin-bottom: $gutter; +} diff --git a/app/main/__init__.py b/app/main/__init__.py index 021b5c086..a7134aef1 100644 --- a/app/main/__init__.py +++ b/app/main/__init__.py @@ -3,4 +3,6 @@ from flask import Blueprint main = Blueprint('main', __name__) -from app.main.views import index, sign_in, register, two_factor, verify, sms, add_service, code_not_received +from app.main.views import ( + index, sign_in, register, two_factor, verify, sms, add_service, code_not_received, jobs, dashboard +) diff --git a/app/main/views/_jobs.py b/app/main/views/_jobs.py new file mode 100644 index 000000000..52337d2ff --- /dev/null +++ b/app/main/views/_jobs.py @@ -0,0 +1,62 @@ +jobs = [ + { + 'job': 'Test message 1', + 'file': 'dispatch_20151114.csv', + 'time': 'Just now', + 'status': 'Queued' + }, + { + 'job': 'Mickey Mouse', + 'file': 'dispatch_20151117.csv', + 'time': '5 minutes ago', + 'status': 'Delivered' + }, + { + 'job': 'Asdfgg', + 'file': 'remdinder_monday.csv', + 'time': '30 minutes ago', + 'status': 'Failed' + }, + { + 'job': 'Test message 2', + 'file': 'appointments_wed.csv', + 'time': '2 hours ago', + 'status': 'Delivered' + }, + { + 'job': 'Reminder file', + 'file': 'appointments_mon.csv', + 'time': '11:44 Yesterday', + 'status': 'Delivered' + }, + { + 'job': 'Test message 1', + 'file': 'dispatch_20151114.csv', + 'time': '20 Jun 2015', + 'status': 'Queued' + }, + { + 'job': 'Mickey Mouse', + 'file': 'dispatch_20151117.csv', + 'time': '18 Jun 2015', + 'status': 'Delivered' + }, + { + 'job': 'Asdfgg', + 'file': 'remdinder_monday.csv', + 'time': '11 Jun 2015', + 'status': 'Failed' + }, + { + 'job': 'Test message 2', + 'file': 'appointments_wed.csv', + 'time': '11 Jun 2015', + 'status': 'Delivered' + }, + { + 'job': 'Final reminder', + 'file': 'appointments_mon.csv', + 'time': '11 Jun 2015', + 'status': 'Delivered' + } +] diff --git a/app/main/views/dashboard.py b/app/main/views/dashboard.py new file mode 100644 index 000000000..0e0275f45 --- /dev/null +++ b/app/main/views/dashboard.py @@ -0,0 +1,15 @@ +from flask import render_template + +from app.main import main + +from ._jobs import jobs + + +@main.route("/dashboard") +def dashboard(): + return render_template( + 'views/dashboard.html', + jobs=jobs, + free_text_messages_remaining=560, + spent_this_month='0.00' + ) diff --git a/app/main/views/index.py b/app/main/views/index.py index c9cdc41dd..d3f2f7c16 100644 --- a/app/main/views/index.py +++ b/app/main/views/index.py @@ -24,17 +24,6 @@ def verifymobile(): return render_template('views/verify-mobile.html') -@main.route("/text-not-received-2") -def textnotreceived2(): - return render_template('views/text-not-received-2.html') - - -@main.route("/dashboard") -@login_required -def dashboard(): - return render_template('views/dashboard.html') - - @main.route("/send-email") def sendemail(): return render_template('views/send-email.html') @@ -45,21 +34,6 @@ def checkemail(): return render_template('views/check-email.html') -@main.route("/jobs") -def showjobs(): - return render_template('views/jobs.html') - - -@main.route("/jobs/job") -def showjob(): - return render_template('views/job.html') - - -@main.route("/jobs/job/notification") -def shownotification(): - return render_template('views/notification.html') - - @main.route("/forgot-password") def forgotpassword(): return render_template('views/forgot-password.html') @@ -90,11 +64,6 @@ def apikeys(): return render_template('views/api-keys.html') -@main.route("/verification-not-received") -def verificationnotreceived(): - return render_template('views/verification-not-received.html') - - @main.route("/manage-templates") def managetemplates(): return render_template('views/manage-templates.html') diff --git a/app/main/views/jobs.py b/app/main/views/jobs.py new file mode 100644 index 000000000..867bb0269 --- /dev/null +++ b/app/main/views/jobs.py @@ -0,0 +1,80 @@ +import time +from flask import render_template +from app.main import main + +from ._jobs import jobs + +now = time.strftime('%H:%M') + +messages = [ + { + 'phone': '+44 7700 900 579', + 'message': 'Vehicle tax: Your vehicle tax for LV75 TDG expires on 18 January 2016. Renew at www.gov.uk/vehicletax', # noqa + 'status': 'Delivered', + 'time': now, + 'id': '0' + }, + { + 'phone': '+44 7700 900 306', + 'message': 'Vehicle tax: Your vehicle tax for PL53 GBD expires on 18 January 2016. Renew at www.gov.uk/vehicletax', # noqa + 'status': 'Delivered', + 'time': now, + 'id': '1' + }, + { + 'phone': '+44 7700 900 454', + 'message': 'Vehicle tax: Your vehicle tax for LV75 TDG expires on 18 January 2016. Renew at www.gov.uk/vehicletax', # noqa + 'status': 'Delivered', + 'time': now, + 'id': '2' + }, + { + 'phone': '+44 7700 900 522', + 'message': 'Vehicle tax: Your vehicle tax for RE67 PLM expires on 18 January 2016. Renew at www.gov.uk/vehicletax', # noqa + 'status': 'Failed', + 'time': now, + 'id': '3' + } +] + + +@main.route("/jobs") +def showjobs(): + return render_template( + 'views/jobs.html', + jobs=jobs + ) + + +@main.route("/jobs/job") +def showjob(): + return render_template( + 'views/job.html', + messages=messages, + counts={ + 'total': len(messages), + 'delivered': len([ + message for message in messages if message['status'] == 'Delivered' + ]), + 'failed': len([ + message for message in messages if message['status'] == 'Failed' + ]) + }, + cost='£0.00', + uploaded_file_name='dispatch_20151114.csv', + uploaded_file_time=now, + template_used='Test message 1', + flash_message='We’ve started sending your messages' + ) + + +@main.route("/jobs/job/notification/") +def shownotification(notification_id): + return render_template( + 'views/notification.html', + message=[ + message for message in messages if message['id'] == notification_id + ][0], + delivered_at=now, + uploaded_at=now + ) diff --git a/app/templates/components/banner.html b/app/templates/components/banner.html new file mode 100644 index 000000000..2dab12c5d --- /dev/null +++ b/app/templates/components/banner.html @@ -0,0 +1,3 @@ +{% macro banner(body) %} + +{% endmacro %} diff --git a/app/templates/components/big-number.html b/app/templates/components/big-number.html new file mode 100644 index 000000000..089466c25 --- /dev/null +++ b/app/templates/components/big-number.html @@ -0,0 +1,6 @@ +{% macro big_number(number, label) %} +
+ {{ number }} + {{ label }} +
+{% endmacro %} diff --git a/app/templates/components/sms-message.html b/app/templates/components/sms-message.html index 5d5f11d12..38d30b877 100644 --- a/app/templates/components/sms-message.html +++ b/app/templates/components/sms-message.html @@ -1,12 +1,20 @@ {% macro sms_message(body, recipient) %} - {% if recipient %} -

- {{ recipient }} -

- {% endif %}
{{ body|placeholders }}
+ {% if recipient %} +

+ {{ recipient }} +

+ {% endif %} +{% endmacro %} + +{% macro message_status(status, time) %} +
+

+ {{ status }} {{ time }} +

+
{% endmacro %} diff --git a/app/templates/components/table.html b/app/templates/components/table.html index c104c926a..ee3c2838f 100644 --- a/app/templates/components/table.html +++ b/app/templates/components/table.html @@ -1,6 +1,6 @@ -{% macro table(items, caption='', field_headings='', field_headings_visible=True) -%} +{% macro table(items, caption='', field_headings='', field_headings_visible=True, caption_visible=True) -%} - @@ -32,8 +32,12 @@
+ {{ caption }}
{%- endmacro %} -{% macro field(first=False, wide=False, action=False) -%} - - {{ caller() }} +{% macro field(align='left', status='') -%} + + {{ caller() }} {%- endmacro %} + +{% macro right_aligned_field_heading(text) %} + {{ text }} +{%- endmacro %} diff --git a/app/templates/views/dashboard.html b/app/templates/views/dashboard.html index d504eb152..3e6339605 100644 --- a/app/templates/views/dashboard.html +++ b/app/templates/views/dashboard.html @@ -1,55 +1,51 @@ {% extends "withnav_template.html" %} - +{% from "components/table.html" import table, field %} +{% from "components/big-number.html" import big_number %} {% block page_title %} -GOV.UK Notify | Dashboard + GOV.UK Notify | Dashboard {% endblock %} {% block maincolumn_content %} -

Dashboard

+

Service name

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Recent activity
Test message 1dispatch_20151114.csvJust nowQueued
Mickey Mousedispatch_20151117.csv5 minutes agoDelivered
Asdfggremdinder_monday.csv30 minutes agoFailed
Test message 2appointments_wed.csv2 hours agoDelivered
Reminder file+44788988763411:44 - 21 Nov 2015Delivered
-
-

See all notifications activity

+ + + {% call(item) table( + jobs[:3], + caption="Recent text messages", + field_headings=['Job', 'File', 'Time', 'Status'] + ) %} + {% call field() %} + {{ item.file }} + {% endcall %} + {% call field() %} + {{ item.job }} + {% endcall %} + {% call field() %} + {{ item.time }} + {% endcall %} + {% call field() %} + {{ item.status }} + {% endcall %} + {% endcall %} +

+ See all notifications activity +

{% endblock %} diff --git a/app/templates/views/job.html b/app/templates/views/job.html index 7457debbc..2cfd7a74e 100644 --- a/app/templates/views/job.html +++ b/app/templates/views/job.html @@ -1,4 +1,7 @@ {% extends "withnav_template.html" %} +{% from "components/table.html" import table, field, right_aligned_field_heading %} +{% from "components/big-number.html" import big_number %} +{% from "components/banner.html" import banner %} {% block page_title %} GOV.UK Notify | Notifications activity @@ -6,14 +9,58 @@ GOV.UK Notify | Notifications activity {% block maincolumn_content %} -

Notifications for a specific job

+

+ {{ uploaded_file_name }} +

-

This page will be where we list the notifications for a specific job.

+ {{ banner(flash_message) }} - - + + +

+ Sent with template {{ template_used }} at {{ uploaded_file_time }} +

+ + {% call(item) table( + messages, + caption='Messages', + caption_visible=False, + field_headings=[ + 'To', + 'Message', + right_aligned_field_heading('Delivery status') + ] + ) %} + {% call field() %} + {{item.phone}} + {% endcall %} + {% call field() %} + {{item.message[:50]}}… + {% endcall %} + {% call field( + align='right', + status='error' if item.status == 'Failed' else 'default' + ) %} + {{ item.status }} {{ item.time }} + {% endcall %} + {% endcall %} {% endblock %} diff --git a/app/templates/views/jobs.html b/app/templates/views/jobs.html index 79a9604d8..b9bda799d 100644 --- a/app/templates/views/jobs.html +++ b/app/templates/views/jobs.html @@ -1,4 +1,5 @@ {% extends "withnav_template.html" %} +{% from "components/table.html" import table, field %} {% block page_title %} GOV.UK Notify | Notifications activity @@ -8,11 +9,25 @@ GOV.UK Notify | Notifications activity

Notifications activity

-

This page will be where we show the list of jobs that this service has processed

- -

- view a particular notification job -

+ {% call(item) table( + jobs, + caption="Recent activity", + caption_visible=False, + field_headings=['Job', 'File', 'Time', 'Status'] + ) %} + {% call field() %} + {{ item.file }} + {% endcall %} + {% call field() %} + {{ item.job }} + {% endcall %} + {% call field() %} + {{ item.time }} + {% endcall %} + {% call field() %} + {{ item.status }} + {% endcall %} + {% endcall %} {% endblock %} diff --git a/app/templates/views/notification.html b/app/templates/views/notification.html index 99a6902db..1dcb034a2 100644 --- a/app/templates/views/notification.html +++ b/app/templates/views/notification.html @@ -1,4 +1,5 @@ {% extends "withnav_template.html" %} +{% from "components/sms-message.html" import sms_message, message_status %} {% block page_title %} GOV.UK Notify | Notifications activity @@ -7,12 +8,21 @@ GOV.UK Notify | Notifications activity {% block maincolumn_content %} -

A specific notification

+

+ Text message +

-

This page will be where we show what happened for a specific notification.

+
+
+ {{ sms_message(message.message, message.phone) }} +
+
+ {{ message_status(message.status, delivered_at) }} +
+

- View other notifications in this job + View other notifications in this job

diff --git a/tests/app/main/views/test_dashboard.py b/tests/app/main/views/test_dashboard.py new file mode 100644 index 000000000..2b134b39d --- /dev/null +++ b/tests/app/main/views/test_dashboard.py @@ -0,0 +1,6 @@ +def test_should_show_recent_jobs_on_dashboard(notifications_admin): + response = notifications_admin.test_client().get('/dashboard') + + assert response.status_code == 200 + assert 'Test message 1' in response.get_data(as_text=True) + assert 'Asdfgg' in response.get_data(as_text=True) diff --git a/tests/app/main/views/test_jobs.py b/tests/app/main/views/test_jobs.py new file mode 100644 index 000000000..be7354873 --- /dev/null +++ b/tests/app/main/views/test_jobs.py @@ -0,0 +1,22 @@ +def test_should_return_list_of_all_jobs(notifications_admin): + response = notifications_admin.test_client().get('/jobs') + + assert response.status_code == 200 + assert 'Test message 1' in response.get_data(as_text=True) + assert 'Final reminder' in response.get_data(as_text=True) + + +def test_should_show_page_for_one_job(notifications_admin): + response = notifications_admin.test_client().get('/jobs/job') + + assert response.status_code == 200 + assert 'dispatch_20151114.csv' in response.get_data(as_text=True) + assert 'Test message 1' in response.get_data(as_text=True) + + +def test_should_show_page_for_one_notification(notifications_admin): + response = notifications_admin.test_client().get('/jobs/job/notification/3') + + assert response.status_code == 200 + assert 'Text message' in response.get_data(as_text=True) + assert '+44 7700 900 522' in response.get_data(as_text=True) diff --git a/tests/app/main/views/test_sms.py b/tests/app/main/views/test_sms.py new file mode 100644 index 000000000..092d1c03d --- /dev/null +++ b/tests/app/main/views/test_sms.py @@ -0,0 +1,27 @@ +def test_should_return_sms_template_picker(notifications_admin): + response = notifications_admin.test_client().get('/sms/send') + + assert response.status_code == 200 + assert 'Choose text message template' in response.get_data(as_text=True) + + +def test_should_redirect_to_sms_check_page(notifications_admin): + response = notifications_admin.test_client().post('/sms/send') + + assert response.status_code == 302 + assert response.location == 'http://localhost/sms/check' + + +def test_should_return_check_sms_page(notifications_admin): + response = notifications_admin.test_client().get('/sms/check') + + assert response.status_code == 200 + assert 'Check and confirm' in response.get_data(as_text=True) + assert 'Send 10 text messages' in response.get_data(as_text=True) + + +def test_should_redirect_to_job(notifications_admin): + response = notifications_admin.test_client().post('/sms/check') + + assert response.status_code == 302 + assert response.location == 'http://localhost/jobs/job'