diff --git a/app/assets/stylesheets/main.scss b/app/assets/stylesheets/main.scss index 29c376245..89b283ddd 100644 --- a/app/assets/stylesheets/main.scss +++ b/app/assets/stylesheets/main.scss @@ -66,6 +66,7 @@ $path: '/static/images/'; @import 'views/api'; @import 'views/product-page'; @import 'views/template'; +@import 'views/notification'; // TODO: break this up @import 'app'; diff --git a/app/assets/stylesheets/views/dashboard.scss b/app/assets/stylesheets/views/dashboard.scss index a1fed973a..c3bc7b9b7 100644 --- a/app/assets/stylesheets/views/dashboard.scss +++ b/app/assets/stylesheets/views/dashboard.scss @@ -46,17 +46,6 @@ transition: width 0.6s ease-in-out; } - &-label { - @include bold-19; - display: block; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - color: $govuk-blue; - max-width: 100%; - background: $white; - } - } .file-list { @@ -67,12 +56,17 @@ white-space: nowrap; overflow: hidden; text-overflow: ellipsis; + padding-bottom: 30px; + padding-top: 10px; + margin-bottom: -30px; + margin-top: -10px; } &-hint { @include core-16; display: block; color: $secondary-text-colour; + pointer-events: none; } } @@ -96,5 +90,6 @@ } .align-with-message-body { + display: block; margin-top: $gutter * 5 / 6; } diff --git a/app/assets/stylesheets/views/notification.scss b/app/assets/stylesheets/views/notification.scss new file mode 100644 index 000000000..08def6684 --- /dev/null +++ b/app/assets/stylesheets/views/notification.scss @@ -0,0 +1,18 @@ +.notification-status { + + @include core-16; + color: $secondary-text-colour; + margin-top: -$gutter-half; + + &.error { + + color: $error-colour; + font-weight: bold; + + a { + color: $error-colour; + } + + } + +} diff --git a/app/main/views/jobs.py b/app/main/views/jobs.py index 266811651..1f459c886 100644 --- a/app/main/views/jobs.py +++ b/app/main/views/jobs.py @@ -14,6 +14,10 @@ from flask import ( stream_with_context ) from flask_login import login_required +from notifications_utils.template import ( + Template, + WithSubjectTemplate, +) from werkzeug.datastructures import MultiDict from app import ( @@ -110,21 +114,7 @@ def view_job(service_id, job_id): 'views/jobs/job.html', finished=(total_notifications == processed_notifications), uploaded_file_name=job['original_file_name'], - template=get_template( - service_api_client.get_service_template( - service_id=service_id, - template_id=job['template'], - version=job['template_version'] - )['data'], - current_service, - letter_preview_url=url_for( - '.view_template_version_preview', - service_id=service_id, - template_id=job['template'], - version=job['template_version'], - filetype='png', - ), - ), + template_id=job['template'], status=request.args.get('status', ''), updates_url=url_for( ".view_job_updates", @@ -271,7 +261,7 @@ def get_notifications(service_id, message_type, status_override=None): ), 'notifications': render_template( 'views/activity/notifications.html', - notifications=notifications['notifications'], + notifications=add_preview_of_content_to_notifications(notifications['notifications']), page=page, prev_page=prev_page, next_page=next_page, @@ -360,6 +350,11 @@ def get_job_partials(job): notifications = notification_api_client.get_notifications_for_service( job['service'], job['id'], status=filter_args['status'] ) + template = service_api_client.get_service_template( + service_id=current_service['id'], + template_id=job['template'], + version=job['template_version'] + )['data'] return { 'counts': render_template( 'partials/count.html', @@ -368,7 +363,9 @@ def get_job_partials(job): ), 'notifications': render_template( 'partials/jobs/notifications.html', - notifications=notifications['notifications'], + notifications=list( + add_preview_of_content_to_notifications(notifications['notifications']) + ), more_than_one_page=bool(notifications.get('links', {}).get('next')), percentage_complete=(job['notifications_requested'] / job['notification_count'] * 100), download_link=url_for( @@ -379,10 +376,26 @@ def get_job_partials(job): ), help=get_help_argument(), time_left=get_time_left(job['created_at']), - job=job + job=job, + template=template, + template_version=job['template_version'], ), 'status': render_template( 'partials/jobs/status.html', job=job ), } + + +def add_preview_of_content_to_notifications(notifications): + return [ + dict( + preview_of_content=( + str(Template(notification['template'], notification['personalisation'])) + if notification['template']['template_type'] == 'sms' else + WithSubjectTemplate(notification['template'], notification['personalisation']).subject + ), + **notification + ) + for notification in notifications + ] diff --git a/app/main/views/notifications.py b/app/main/views/notifications.py index ec51789bd..e29f06ce0 100644 --- a/app/main/views/notifications.py +++ b/app/main/views/notifications.py @@ -10,6 +10,7 @@ from flask_login import login_required from app import ( notification_api_client, + job_api_client, current_service ) from app.main import main @@ -39,27 +40,34 @@ def get_status_arg(filter_args): return REQUESTED_STATUSES -@main.route("/services//one-off-notification/") +@main.route("/services//notification/") @login_required @user_has_permissions('view_activity', admin_override=True) def view_notification(service_id, notification_id): notification = notification_api_client.get_notification(service_id, notification_id) + template = get_template( + notification['template'], + current_service, + letter_preview_url=url_for( + '.view_template_version_preview', + service_id=service_id, + template_id=notification['template']['id'], + version=notification['template_version'], + filetype='png', + ), + show_recipient=True, + ) + template.values = get_all_personalisation_from_notification(notification) + if notification['job']: + job = job_api_client.get_job(service_id, notification['job']['id'])['data'] + else: + job = None return render_template( 'views/notifications/notification.html', finished=(notification['status'] in (DELIVERED_STATUSES + FAILURE_STATUSES)), uploaded_file_name='Report', - template=get_template( - notification['template'], - current_service, - letter_preview_url=url_for( - '.view_template_version_preview', - service_id=service_id, - template_id=notification['template']['id'], - version=notification['template_version'], - filetype='png', - ), - ), - status=request.args.get('status'), + template=template, + job=job, updates_url=url_for( ".view_notification_updates", service_id=service_id, @@ -68,11 +76,13 @@ def view_notification(service_id, notification_id): help=get_help_argument() ), partials=get_single_notification_partials(notification), + created_by=notification.get('created_by'), + created_at=notification['created_at'], help=get_help_argument() ) -@main.route("/services//one-off-notification/.json") +@main.route("/services//notification/.json") @user_has_permissions('view_activity', admin_override=True) def view_notification_updates(service_id, notification_id): return jsonify(**get_single_notification_partials( @@ -80,49 +90,10 @@ def view_notification_updates(service_id, notification_id): )) -def _get_single_notification_counts(notification, help_argument): - return [ - ( - label, - query_param, - url_for( - ".view_notification", - service_id=notification['service'], - notification_id=notification['id'], - status=query_param, - help=help_argument - ), - count - ) for label, query_param, count in [ - [ - 'total', '', - 1 - ], - [ - 'sending', 'sending', - int(notification['status'] in SENDING_STATUSES) - ], - [ - 'delivered', 'delivered', - int(notification['status'] in DELIVERED_STATUSES) - ], - [ - 'failed', 'failed', - int(notification['status'] in FAILURE_STATUSES) - ] - ] - ] - - def get_single_notification_partials(notification): status_args = get_status_arg(request.args) return { - 'counts': render_template( - 'partials/count.html', - counts=_get_single_notification_counts(notification, request.args.get('help', 0)), - status=status_args - ), 'notifications': render_template( 'partials/notifications/notifications.html', notification=notification, @@ -135,3 +106,18 @@ def get_single_notification_partials(notification): notification=notification ), } + + +def get_all_personalisation_from_notification(notification): + if notification['template']['template_type'] == 'email': + return dict( + email_address=notification['to'], + **notification['personalisation'] + ) + if notification['template']['template_type'] == 'sms': + return dict( + phone_number=notification['to'], + **notification['personalisation'] + ) + if notification['template']['template_type'] == 'letter': + return notification['personalisation'] diff --git a/app/templates/components/table.html b/app/templates/components/table.html index 8708465b2..5ad942fe6 100644 --- a/app/templates/components/table.html +++ b/app/templates/components/table.html @@ -120,6 +120,7 @@ {% macro notification_status_field(notification) %} {% call field(status=notification.status|format_notification_status_as_field_status, align='right') %} + {% if notification.status in ['created', 'sending', 'delivered'] %}{% endif %} {% if notification.status|format_notification_status_as_url %} {% endif %} @@ -135,6 +136,7 @@ (notification.updated_at or notification.created_at)|format_datetime_short ) }} + {% if notification.status in ['created', 'sending', 'delivered'] %}{% endif %} {% endcall %} {% endmacro %} diff --git a/app/templates/main_nav.html b/app/templates/main_nav.html index 2dc588d0e..3c0ca9ef2 100644 --- a/app/templates/main_nav.html +++ b/app/templates/main_nav.html @@ -32,7 +32,7 @@ Notify delivers the message

{% if help == '3' %} -
+ Now go to your dashboard {% endif %} diff --git a/app/templates/partials/jobs/notifications.html b/app/templates/partials/jobs/notifications.html index 8c2964bdd..4bec657b3 100644 --- a/app/templates/partials/jobs/notifications.html +++ b/app/templates/partials/jobs/notifications.html @@ -5,7 +5,9 @@ {% if job.job_status == 'scheduled' %}

- Sending will start {{ job.scheduled_for|format_datetime_relative }} + Sending + {{ template.name }} + {{ job.scheduled_for|format_datetime_relative }}