mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-08-23 07:46:23 -04:00
merge from main and code review feedback
This commit is contained in:
@@ -48,13 +48,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
.sms-message-sender {
|
||||
margin: units(1) 0 0;
|
||||
.sms-message-sender, .sms-message-file-name, .sms-message-scheduler, .sms-message-template, .sms-message-sender {
|
||||
margin:0.25rem 0 0;
|
||||
}
|
||||
|
||||
.sms-message-recipient {
|
||||
color: color('gray-cool-90');
|
||||
margin: 0 0 units(1);
|
||||
margin: units(1) 0 units(1);
|
||||
}
|
||||
|
||||
.sms-message-status {
|
||||
@@ -131,7 +131,7 @@
|
||||
&-label,
|
||||
&-button-label {
|
||||
font-weight: bold;
|
||||
font-size: 19px;
|
||||
font-size: 19px;
|
||||
display: block;
|
||||
margin: 0 0 10px 0;
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ i.e.
|
||||
.usa-logo {
|
||||
font-family: family("sans");
|
||||
margin: units(4) 0;
|
||||
@include at-media-max('mobile-lg') {
|
||||
@include at-media-max('desktop') {
|
||||
margin: units(4) 0 units(4) units(2);
|
||||
}
|
||||
img {
|
||||
@@ -43,6 +43,16 @@ i.e.
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
@include at-media-max('desktop') {
|
||||
padding: 0 units(2);
|
||||
ul li {
|
||||
padding-bottom: units(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
.usa-nav-container {
|
||||
max-width: 100%;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -345,6 +355,9 @@ td.table-empty-message {
|
||||
background-image: url(../img/material-icons/description.svg);
|
||||
}
|
||||
}
|
||||
.table-wrapper {
|
||||
overflow-x: scroll;
|
||||
}
|
||||
}
|
||||
|
||||
.dashboard-table {
|
||||
@@ -403,8 +416,11 @@ td.table-empty-message {
|
||||
width: 5%;
|
||||
}
|
||||
th {
|
||||
padding: 0.5rem 0.5rem;
|
||||
padding: 0.5rem 1rem
|
||||
}
|
||||
td {
|
||||
padding: 0.5rem 1rem
|
||||
}
|
||||
}
|
||||
|
||||
#template-list {
|
||||
|
||||
@@ -36,7 +36,7 @@ class Config(object):
|
||||
NR_BROWSER_KEY = getenv("NR_BROWSER_KEY")
|
||||
settings = newrelic.agent.global_settings()
|
||||
NR_MONITOR_ON = settings and settings.monitor_mode
|
||||
COMMIT_HASH = getenv("COMMIT_HASH", "Unknown")
|
||||
COMMIT_HASH = getenv("COMMIT_HASH", "-----")
|
||||
|
||||
TEMPLATE_PREVIEW_API_HOST = getenv(
|
||||
"TEMPLATE_PREVIEW_API_HOST", "http://localhost:9999"
|
||||
|
||||
@@ -1226,7 +1226,11 @@ class CsvUploadForm(StripWhitespaceForm):
|
||||
validators=[
|
||||
DataRequired(message="Please pick a file"),
|
||||
CsvFileValidator(),
|
||||
FileSize(max_size=10e6, message="File must be smaller than 10Mb"), # 10Mb
|
||||
FileSize(
|
||||
max_size=10e6,
|
||||
message="File must be smaller than 10Mb. If you are trying to upload an Excel file, \
|
||||
please export the contents in the CSV format and then try again.",
|
||||
), # 10Mb
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -76,6 +76,7 @@ def service_dashboard(service_id):
|
||||
"notifications": aggregate_notifications_by_job.get(job["id"], []),
|
||||
}
|
||||
for job in job_response
|
||||
if aggregate_notifications_by_job.get(job["id"], [])
|
||||
]
|
||||
return render_template(
|
||||
"views/dashboard/dashboard.html",
|
||||
|
||||
@@ -102,15 +102,6 @@ def security():
|
||||
return render_template("views/security.html", navigation_links=features_nav())
|
||||
|
||||
|
||||
@main.route("/features/terms", endpoint="terms")
|
||||
@user_is_logged_in
|
||||
def terms():
|
||||
return render_template(
|
||||
"views/terms-of-use.html",
|
||||
navigation_links=features_nav(),
|
||||
)
|
||||
|
||||
|
||||
@main.route("/features/using_notify")
|
||||
@user_is_logged_in
|
||||
def using_notify():
|
||||
@@ -214,7 +205,6 @@ def send_files_by_email():
|
||||
|
||||
|
||||
@main.route("/roadmap", endpoint="old_roadmap")
|
||||
@main.route("/terms", endpoint="old_terms")
|
||||
@main.route("/information-security", endpoint="information_security")
|
||||
@main.route("/using_notify", endpoint="old_using_notify")
|
||||
@main.route("/information-risk-management", endpoint="information_risk_management")
|
||||
@@ -222,7 +212,6 @@ def send_files_by_email():
|
||||
def old_page_redirects():
|
||||
redirects = {
|
||||
"main.old_roadmap": "main.roadmap",
|
||||
"main.old_terms": "main.terms",
|
||||
"main.information_security": "main.using_notify",
|
||||
"main.old_using_notify": "main.using_notify",
|
||||
"main.information_risk_management": "main.security",
|
||||
|
||||
@@ -276,12 +276,16 @@ def get_status_filters(service, message_type, statistics):
|
||||
}
|
||||
else:
|
||||
stats = statistics[message_type]
|
||||
stats["sending"] = stats["requested"] - stats["delivered"] - stats["failed"]
|
||||
|
||||
if stats.get("failure") is not None:
|
||||
stats["failed"] = stats["failure"]
|
||||
|
||||
stats["pending"] = stats["requested"] - stats["delivered"] - stats["failed"]
|
||||
|
||||
filters = [
|
||||
# key, label, option
|
||||
("requested", "total", "sending,delivered,failed"),
|
||||
("sending", "pending", "pending"),
|
||||
("pending", "pending", "pending"),
|
||||
("delivered", "delivered", "delivered"),
|
||||
("failed", "failed", "failed"),
|
||||
]
|
||||
@@ -296,7 +300,7 @@ def get_status_filters(service, message_type, statistics):
|
||||
message_type=message_type,
|
||||
status=option,
|
||||
),
|
||||
stats[key],
|
||||
stats.get(key),
|
||||
)
|
||||
for key, label, option in filters
|
||||
]
|
||||
|
||||
@@ -6,6 +6,7 @@ from zipfile import BadZipFile
|
||||
|
||||
from flask import abort, flash, redirect, render_template, request, session, url_for
|
||||
from flask_login import current_user
|
||||
from markupsafe import Markup
|
||||
from notifications_python_client.errors import HTTPError
|
||||
from notifications_utils import SMS_CHAR_COUNT_LIMIT
|
||||
from notifications_utils.insensitive_dict import InsensitiveDict
|
||||
@@ -151,8 +152,11 @@ def send_messages(service_id, template_id):
|
||||
# just show the first error, as we don't expect the form to have more
|
||||
# than one, since it only has one field
|
||||
first_field_errors = list(form.errors.values())[0]
|
||||
flash(first_field_errors[0])
|
||||
|
||||
error_message = '<span class="usa-error-message">'
|
||||
error_message = f"{error_message}{first_field_errors[0]}"
|
||||
error_message = f"{error_message}</span>"
|
||||
error_message = Markup(error_message)
|
||||
flash(error_message)
|
||||
column_headings = get_spreadsheet_column_headings_from_template(template)
|
||||
|
||||
return render_template(
|
||||
@@ -504,13 +508,18 @@ def _check_messages(service_id, template_id, upload_id, preview_row):
|
||||
template = get_template(
|
||||
db_template,
|
||||
current_service,
|
||||
show_recipient=True,
|
||||
show_recipient=False,
|
||||
email_reply_to=email_reply_to,
|
||||
sms_sender=sms_sender,
|
||||
)
|
||||
simplifed_template = get_template(
|
||||
db_template,
|
||||
current_service,
|
||||
show_recipient=False,
|
||||
)
|
||||
recipients = RecipientCSV(
|
||||
contents,
|
||||
template=template,
|
||||
template=template or simplifed_template,
|
||||
max_initial_rows_shown=50,
|
||||
max_errors_shown=50,
|
||||
guestlist=(
|
||||
@@ -530,11 +539,20 @@ def _check_messages(service_id, template_id, upload_id, preview_row):
|
||||
back_link = url_for(
|
||||
"main.send_one_off", service_id=service_id, template_id=template.id
|
||||
)
|
||||
back_link_from_preview = url_for(
|
||||
"main.send_one_off", service_id=service_id, template_id=template.id
|
||||
)
|
||||
choose_time_form = None
|
||||
else:
|
||||
back_link = url_for(
|
||||
"main.send_messages", service_id=service_id, template_id=template.id
|
||||
)
|
||||
back_link_from_preview = url_for(
|
||||
"main.check_messages",
|
||||
service_id=service_id,
|
||||
template_id=template.id,
|
||||
upload_id=upload_id,
|
||||
)
|
||||
choose_time_form = ChooseTimeForm()
|
||||
|
||||
if preview_row < 2:
|
||||
@@ -542,6 +560,7 @@ def _check_messages(service_id, template_id, upload_id, preview_row):
|
||||
|
||||
if preview_row < len(recipients) + 2:
|
||||
template.values = recipients[preview_row - 2].recipient_and_personalisation
|
||||
simplifed_template.values = recipients[preview_row - 2].recipient_and_personalisation
|
||||
elif preview_row > 2:
|
||||
abort(404)
|
||||
|
||||
@@ -562,11 +581,14 @@ def _check_messages(service_id, template_id, upload_id, preview_row):
|
||||
remaining_messages=remaining_messages,
|
||||
choose_time_form=choose_time_form,
|
||||
back_link=back_link,
|
||||
back_link_from_preview=back_link_from_preview,
|
||||
first_recipient_column=recipients.recipient_column_headers[0],
|
||||
preview_row=preview_row,
|
||||
sent_previously=job_api_client.has_sent_previously(
|
||||
service_id, template.id, db_template["version"], original_file_name
|
||||
),
|
||||
template_id=template_id,
|
||||
simplifed_template=simplifed_template,
|
||||
)
|
||||
|
||||
|
||||
@@ -614,13 +636,34 @@ def check_messages(service_id, template_id, upload_id, row_index=2):
|
||||
return render_template("views/check/ok.html", **data)
|
||||
|
||||
|
||||
@main.route(
|
||||
"/services/<uuid:service_id>/<uuid:template_id>/check/<uuid:upload_id>/preview",
|
||||
methods=["POST"],
|
||||
)
|
||||
@main.route(
|
||||
"/services/<uuid:service_id>/<uuid:template_id>/check/<uuid:upload_id>/preview/row-<int:row_index>",
|
||||
methods=["POST"],
|
||||
)
|
||||
@user_has_permissions("send_messages", restrict_admin_usage=True)
|
||||
def preview_job(service_id, template_id, upload_id, row_index=2):
|
||||
session["scheduled_for"] = request.form.get("scheduled_for", "")
|
||||
data = _check_messages(service_id, template_id, upload_id, row_index)
|
||||
|
||||
return render_template(
|
||||
"views/check/preview.html",
|
||||
scheduled_for=session["scheduled_for"],
|
||||
**data,
|
||||
)
|
||||
|
||||
|
||||
@main.route("/services/<uuid:service_id>/start-job/<uuid:upload_id>", methods=["POST"])
|
||||
@user_has_permissions("send_messages", restrict_admin_usage=True)
|
||||
def start_job(service_id, upload_id):
|
||||
scheduled_for = session.pop("scheduled_for", None)
|
||||
job_api_client.create_job(
|
||||
upload_id,
|
||||
service_id,
|
||||
scheduled_for=request.form.get("scheduled_for", ""),
|
||||
scheduled_for=scheduled_for,
|
||||
)
|
||||
|
||||
session.pop("sender_id", None)
|
||||
@@ -679,7 +722,20 @@ def get_send_test_page_title(template_type, entering_recipient, name=None):
|
||||
return "Personalize this message"
|
||||
|
||||
|
||||
def get_back_link(service_id, template, step_index, placeholders=None):
|
||||
def get_back_link(
|
||||
service_id,
|
||||
template,
|
||||
step_index,
|
||||
placeholders=None,
|
||||
preview=False,
|
||||
):
|
||||
if preview:
|
||||
return url_for(
|
||||
"main.check_notification",
|
||||
service_id=service_id,
|
||||
template_id=template.id,
|
||||
)
|
||||
|
||||
if step_index == 0:
|
||||
if should_skip_template_page(template._template):
|
||||
return url_for(
|
||||
@@ -779,11 +835,18 @@ def _check_notification(service_id, template_id, exception=None):
|
||||
email_reply_to=email_reply_to,
|
||||
sms_sender=sms_sender,
|
||||
)
|
||||
|
||||
simplifed_template = get_template(
|
||||
db_template,
|
||||
current_service,
|
||||
)
|
||||
placeholders = fields_to_fill_in(template)
|
||||
|
||||
back_link = get_back_link(service_id, template, len(placeholders), placeholders)
|
||||
|
||||
back_link_from_preview = get_back_link(
|
||||
service_id, template, len(placeholders), placeholders, preview=True
|
||||
)
|
||||
|
||||
choose_time_form = ChooseTimeForm()
|
||||
|
||||
if (not session.get("recipient")) or not all_placeholders_in_session(
|
||||
@@ -797,8 +860,10 @@ def _check_notification(service_id, template_id, exception=None):
|
||||
return dict(
|
||||
template=template,
|
||||
back_link=back_link,
|
||||
back_link_from_preview=back_link_from_preview,
|
||||
choose_time_form=choose_time_form,
|
||||
**(get_template_error_dict(exception) if exception else {}),
|
||||
simplifed_template=simplifed_template
|
||||
)
|
||||
|
||||
|
||||
@@ -828,12 +893,39 @@ def get_template_error_dict(exception):
|
||||
}
|
||||
|
||||
|
||||
@main.route(
|
||||
"/services/<uuid:service_id>/template/<uuid:template_id>/notification/check/preview",
|
||||
methods=["POST"],
|
||||
)
|
||||
@user_has_permissions("send_messages", restrict_admin_usage=True)
|
||||
def preview_notification(service_id, template_id):
|
||||
recipient = get_recipient()
|
||||
if not recipient:
|
||||
return redirect(
|
||||
url_for(
|
||||
".send_one_off",
|
||||
service_id=service_id,
|
||||
template_id=template_id,
|
||||
)
|
||||
)
|
||||
|
||||
session["scheduled_for"] = request.form.get("scheduled_for", "")
|
||||
|
||||
return render_template(
|
||||
"views/notifications/preview.html",
|
||||
**_check_notification(service_id, template_id),
|
||||
scheduled_for=session["scheduled_for"],
|
||||
recipient=recipient,
|
||||
)
|
||||
|
||||
|
||||
@main.route(
|
||||
"/services/<uuid:service_id>/template/<uuid:template_id>/notification/check",
|
||||
methods=["POST"],
|
||||
)
|
||||
@user_has_permissions("send_messages", restrict_admin_usage=True)
|
||||
def send_notification(service_id, template_id):
|
||||
scheduled_for = session.pop("scheduled_for", "")
|
||||
recipient = get_recipient()
|
||||
if not recipient:
|
||||
return redirect(
|
||||
@@ -868,7 +960,7 @@ def send_notification(service_id, template_id):
|
||||
job_api_client.create_job(
|
||||
upload_id,
|
||||
service_id,
|
||||
scheduled_for=request.form.get("scheduled_for", ""),
|
||||
scheduled_for=scheduled_for,
|
||||
template_id=template_id,
|
||||
original_file_name=filename,
|
||||
notification_count=1,
|
||||
|
||||
@@ -53,22 +53,19 @@ def _get_access_token(code, state):
|
||||
# JWT expiration time (10 minute maximum)
|
||||
"exp": int(time.time()) + (10 * 60),
|
||||
}
|
||||
current_app.logger.warning(f"Here is the raw payload {payload}")
|
||||
token = jwt.encode(payload, keystring, algorithm="RS256")
|
||||
base_url = f"{access_token_url}?"
|
||||
cli_assert = f"client_assertion={token}"
|
||||
cli_assert_type = "client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer"
|
||||
code_param = f"code={code}"
|
||||
url = f"{base_url}{cli_assert}&{cli_assert_type}&{code_param}&grant_type=authorization_code"
|
||||
current_app.logger.info(f"This is the url we use to get the access token: {url}")
|
||||
headers = {"Authorization": "Bearer %s" % token}
|
||||
response = requests.post(url, headers=headers)
|
||||
current_app.logger.info(f"GOT A RESPONSE {response.json()}")
|
||||
access_token = response.json()["access_token"]
|
||||
return access_token
|
||||
|
||||
|
||||
def _get_user_email(access_token):
|
||||
def _get_user_email_and_uuid(access_token):
|
||||
headers = {"Authorization": "Bearer %s" % access_token}
|
||||
user_info_url = os.getenv("LOGIN_DOT_GOV_USER_INFO_URL")
|
||||
user_attributes = requests.get(
|
||||
@@ -76,7 +73,8 @@ def _get_user_email(access_token):
|
||||
headers=headers,
|
||||
)
|
||||
user_email = user_attributes.json()["email"]
|
||||
return user_email
|
||||
user_uuid = user_attributes.json()["sub"]
|
||||
return user_email, user_uuid
|
||||
|
||||
|
||||
@main.route("/sign-in", methods=(["GET", "POST"]))
|
||||
@@ -88,11 +86,11 @@ def sign_in():
|
||||
login_gov_error = request.args.get("error")
|
||||
if code and state:
|
||||
access_token = _get_access_token(code, state)
|
||||
user_email = _get_user_email(access_token)
|
||||
user_email, user_uuid = _get_user_email_and_uuid(access_token)
|
||||
redirect_url = request.args.get("next")
|
||||
|
||||
# activate the user
|
||||
user = user_api_client.get_user_by_email(user_email)
|
||||
user = user_api_client.get_user_by_uuid_or_email(user_uuid, user_email)
|
||||
activate_user(user["id"])
|
||||
return redirect(url_for("main.show_accounts_or_dashboard", next=redirect_url))
|
||||
|
||||
@@ -198,7 +196,7 @@ def sign_in():
|
||||
form=form,
|
||||
again=bool(redirect_url),
|
||||
other_device=other_device,
|
||||
login_gov_enabled=bool(notify_env in ["development", "staging"]),
|
||||
login_gov_enabled=bool(notify_env in ["development", "staging", "demo"]),
|
||||
password_reset_url=password_reset_url,
|
||||
initial_signin_url=initial_signin_url,
|
||||
)
|
||||
|
||||
@@ -18,10 +18,6 @@ def features_nav():
|
||||
"name": "Security",
|
||||
"link": "main.security",
|
||||
},
|
||||
{
|
||||
"name": "Terms of use",
|
||||
"link": "main.terms",
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -45,7 +45,6 @@ class HeaderNavigation(Navigation):
|
||||
"features_sms",
|
||||
"roadmap",
|
||||
"security",
|
||||
"terms",
|
||||
},
|
||||
"using_notify": {
|
||||
"get_started",
|
||||
|
||||
@@ -44,6 +44,16 @@ class UserApiClient(NotifyAdminAPIClient):
|
||||
user_data = self.post("/user/email", data={"email": email_address})
|
||||
return user_data["data"]
|
||||
|
||||
def get_user_by_uuid_or_email(self, user_uuid, email_address):
|
||||
|
||||
user_data = self.post(
|
||||
"/user/get-login-gov-user",
|
||||
data={"login_uuid": user_uuid, "email": email_address},
|
||||
)
|
||||
if user_data is None:
|
||||
raise Exception("User not found")
|
||||
return user_data["data"]
|
||||
|
||||
def get_user_by_email_or_none(self, email_address):
|
||||
try:
|
||||
return self.get_user_by_email(email_address)
|
||||
|
||||
@@ -203,10 +203,6 @@
|
||||
"href": url_for("main.security"),
|
||||
"text": "Security"
|
||||
},
|
||||
{
|
||||
"href": url_for("main.terms"),
|
||||
"text": "Terms of use"
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -44,61 +44,63 @@
|
||||
|
||||
<!-- usa header -->
|
||||
<header class="usa-header usa-header--extended">
|
||||
<div class="usa-navbar">
|
||||
<div class="usa-logo display-flex flex-align-center" id="-logo">
|
||||
{# <div class="logo-img display-flex">
|
||||
<span class="usa-sr-only">US Notify Logo</span>
|
||||
<image src="{{ params.assetsPath | default('/static/images') }}/us-notify-color.png" alt="US Notify logo" xlink:href=""
|
||||
class="usa-flag-logo margin-right-1" width="40" height="35"></image>
|
||||
</div> #}
|
||||
<em class="logo-text usa-logo__text">
|
||||
<a href="/" title="notify.gov">notify.gov</a>
|
||||
</em>
|
||||
</div>
|
||||
{% if current_user.is_authenticated %}
|
||||
<button type="button" class="usa-menu-btn">Menu</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
<nav aria-label="Primary navigation" class="usa-nav">
|
||||
<div class="usa-nav__inner">
|
||||
<button type="button" class="usa-nav__close">
|
||||
<img src="/static/images/usa-icons/close.svg" role="img" alt="Close" />
|
||||
</button>
|
||||
<ul class="usa-nav__primary usa-accordion margin-right-1">
|
||||
{% for item in params.navigation %}
|
||||
{% if item.href and item.text %}
|
||||
<li class="usa-nav__primary-item{{ ' is-current' if item.active }}">
|
||||
<a class="usa-nav__link {{ ' usa-current' if item.active }}" href="{{ item.href }}" {% for attribute, value in
|
||||
item.attributes %} {{attribute}}="{{value}}" {% endfor %}>
|
||||
<span>{{ item.text }}</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<div class="usa-nav__secondary margin-bottom-2">
|
||||
<ul class="usa-nav__secondary-links">
|
||||
{% for item in params.secondaryNavigation %}
|
||||
{% if item.href and item.text %}
|
||||
<li class="usa-nav__secondary-item{{ ' is-current' if item.active }}">
|
||||
<a class="usa-nav__link {{ ' usa-current' if item.active }}" href="{{ item.href }}" {% for attribute, value in
|
||||
item.attributes %} {{attribute}}="{{value}}" {% endfor %}>
|
||||
<span>{{ item.text }}</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<!-- <section aria-label="Search component">
|
||||
<form class="usa-search usa-search--small margin-bottom-2" role="search">
|
||||
<label class="usa-sr-only" for="search-field">Search</label>
|
||||
<input class="usa-input" id="search-field" type="search" name="search" />
|
||||
<button class="usa-button" type="submit">
|
||||
<img src="/static/images/usa-icons-bg/search--white.svg" class="usa-search__submit-icon" alt="Search" />
|
||||
</button>
|
||||
</form>
|
||||
</section> -->
|
||||
<div class="usa-nav-container">
|
||||
<div class="usa-navbar">
|
||||
<div class="usa-logo display-flex flex-align-center flex-justify" id="-logo">
|
||||
{# <div class="logo-img display-flex">
|
||||
<span class="usa-sr-only">US Notify Logo</span>
|
||||
<image src="{{ params.assetsPath | default('/static/images') }}/us-notify-color.png" alt="US Notify logo" xlink:href=""
|
||||
class="usa-flag-logo margin-right-1" width="40" height="35"></image>
|
||||
</div> #}
|
||||
<em class="logo-text usa-logo__text">
|
||||
<a href="/" title="Notify.gov">Notify.gov</a>
|
||||
</em>
|
||||
{% if params.navigation %}
|
||||
<button type="button" class="usa-menu-btn">Menu</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<nav aria-label="Primary navigation" class="usa-nav">
|
||||
<div class="usa-nav__inner">
|
||||
<button type="button" class="usa-nav__close">
|
||||
<img src="/static/images/usa-icons/close.svg" role="img" alt="Close" />
|
||||
</button>
|
||||
<ul class="usa-nav__primary usa-accordion margin-right-1">
|
||||
{% for item in params.navigation %}
|
||||
{% if item.href and item.text %}
|
||||
<li class="usa-nav__primary-item{{ ' is-current' if item.active }}">
|
||||
<a class="usa-nav__link {{ ' usa-current' if item.active }}" href="{{ item.href }}" {% for attribute, value in
|
||||
item.attributes %} {{attribute}}="{{value}}" {% endfor %}>
|
||||
<span>{{ item.text }}</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<div class="usa-nav__secondary margin-bottom-2">
|
||||
<ul class="usa-nav__secondary-links">
|
||||
{% for item in params.secondaryNavigation %}
|
||||
{% if item.href and item.text %}
|
||||
<li class="usa-nav__secondary-item{{ ' is-current' if item.active }}">
|
||||
<a class="usa-nav__link {{ ' usa-current' if item.active }}" href="{{ item.href }}" {% for attribute, value in
|
||||
item.attributes %} {{attribute}}="{{value}}" {% endfor %}>
|
||||
<span>{{ item.text }}</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<!-- <section aria-label="Search component">
|
||||
<form class="usa-search usa-search--small margin-bottom-2" role="search">
|
||||
<label class="usa-sr-only" for="search-field">Search</label>
|
||||
<input class="usa-input" id="search-field" type="search" name="search" />
|
||||
<button class="usa-button" type="submit">
|
||||
<img src="/static/images/usa-icons-bg/search--white.svg" class="usa-search__submit-icon" alt="Search" />
|
||||
</button>
|
||||
</form>
|
||||
</section> -->
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
@@ -171,8 +171,8 @@
|
||||
{% endif %}
|
||||
<p class="status-hint margin-0 width-card ">
|
||||
{{ notification.status|format_notification_status_as_time(
|
||||
notification.created_at|format_datetime_short,
|
||||
(notification.sent_at or notification.created_at)|format_datetime_short
|
||||
notification.created_at|format_datetime_short_america,
|
||||
(notification.sent_at or notification.created_at)|format_datetime_short_america
|
||||
) }}
|
||||
</p>
|
||||
{% if displayed_on_single_line %}</span>{% endif %}
|
||||
|
||||
@@ -11,10 +11,10 @@
|
||||
<div class="grid-row">
|
||||
|
||||
{% if navigation_links %}
|
||||
<div class="tablet:grid-col-2">
|
||||
<div class="tablet:grid-col-2 margin-bottom-4">
|
||||
{{ sub_navigation(navigation_links) }}
|
||||
</div>
|
||||
<div class="tablet:grid-col-10 padding-left-4 usa-prose site-prose">
|
||||
<div class="tablet:grid-col-10 tablet:padding-left-4 usa-prose site-prose">
|
||||
{% else %}
|
||||
<div class="tablet:grid-col-10">
|
||||
{% endif %}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{% if help %}
|
||||
{% include 'partials/tour.html' %}
|
||||
{% else %}
|
||||
<nav class="nav">
|
||||
<nav class="nav margin-bottom-4">
|
||||
<a class="usa-button margin-top-1 margin-bottom-5 width-full"
|
||||
href="{{ url_for('.choose_template', service_id=current_service.id) }}">Send messages</a>
|
||||
<ul class="usa-sidenav">
|
||||
|
||||
372
app/templates/new/base.html
Normal file
372
app/templates/new/base.html
Normal file
@@ -0,0 +1,372 @@
|
||||
{% from "../components/banner.html" import banner %}
|
||||
{% from "../components/components/skip-link/macro.njk" import usaSkipLink -%}
|
||||
{% from "../components/components/header/macro.njk" import usaHeader -%}
|
||||
{% from "../components/components/footer/macro.njk" import usaFooter -%}
|
||||
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ htmlLang | default('en') }}" class="{{ htmlClasses }}">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>
|
||||
{% block pageTitle %}
|
||||
{% block per_page_title %} {% endblock %}Notify.gov
|
||||
<!-- on templates that were using content_template.html, we might need to use the {{ content_page_title }} variable for the per_page_title -->
|
||||
{% endblock %}
|
||||
</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
||||
<meta name="theme-color" media="(prefers-color-scheme: light)" content="f0f0f0" />
|
||||
<meta name="theme-color" media="(prefers-color-scheme: dark)" content="1b1b1b" />
|
||||
{% if config['NR_MONITOR_ON'] %}
|
||||
{% include "partials/newrelic.html" -%}
|
||||
{% endif %}
|
||||
{# Ensure that older IE versions always render with the correct rendering engine #}
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
{% block headIcons %}
|
||||
<link rel="shortcut icon" sizes="16x16 32x32 48x48" href="{{ asset_url('images/favicon.ico') }}" type="image/x-icon" />
|
||||
<link rel="mask-icon" href="{{ asset_url('images/usa-mask-icon.svg') }}" color="{{ themeColor | default('#F0F0F0') }}">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="{{ asset_url('images/apple-touch-icon.png') }}">
|
||||
<link rel="apple-touch-icon" href="{{ asset_url('images/apple-touch-icon.png') }}">
|
||||
{% endblock %}
|
||||
|
||||
{% block head %}
|
||||
<link rel="stylesheet" media="screen" href="{{ asset_url('css/styles.css') }}" />
|
||||
{% block extra_stylesheets %}
|
||||
{% endblock %}
|
||||
{% if g.hide_from_search_engines %}
|
||||
<meta name="robots" content="noindex" />
|
||||
{% endif %}
|
||||
<meta name="google-site-verification" content="niWnSqImOWz6mVQTYqNb5tFK8HaKSB4b3ED4Z9gtUQ0" />
|
||||
{# The default og:image is added below head so that scrapers see any custom metatags first, and this is just a fallback #}
|
||||
{% block meta_format_detection %}
|
||||
<meta name="format-detection" content="telephone=no">
|
||||
{% endblock %}
|
||||
{% block meta %}
|
||||
<meta property="og:site_name" content="Notify.gov">
|
||||
<meta property="og:image" content="{{ asset_url('images/usa-opengraph-image.png') }}">
|
||||
{% endblock %}
|
||||
<script type="text/javascript" src="{{ asset_url('js/gtm_head.js') }}"></script>
|
||||
{% endblock %}
|
||||
</head>
|
||||
|
||||
<body class="usa-template__body {{ bodyClasses }}">
|
||||
<script nonce="{{ csp_nonce() }}">document.body.className = ((document.body.className) ? document.body.className + ' js-enabled' : 'js-enabled');</script>
|
||||
{% block bodyStart %}
|
||||
{% block extra_javascripts_before_body %}
|
||||
<!-- Google Tag Manager (noscript) -->
|
||||
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-WX5NGWF"
|
||||
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
|
||||
<!-- End Google Tag Manager (noscript) -->
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
{% block skipLink %}
|
||||
{{ usaSkipLink({
|
||||
"href": '#main-content',
|
||||
"text": 'Skip to main content'
|
||||
}) }}
|
||||
{% endblock %}
|
||||
|
||||
<!-- \\#region header -->
|
||||
{% block header %}
|
||||
{% if current_user.is_authenticated %}
|
||||
{% if current_user.platform_admin %}
|
||||
{% set navigation = [
|
||||
{
|
||||
"href": url_for("main.show_accounts_or_dashboard"),
|
||||
"text": "Current service",
|
||||
"active": header_navigation.is_selected('accounts-or-dashboard')
|
||||
},
|
||||
{
|
||||
"href": url_for('main.get_started'),
|
||||
"text": "Using Notify",
|
||||
"active": header_navigation.is_selected('using_notify')
|
||||
},
|
||||
{
|
||||
"href": url_for('main.features'),
|
||||
"text": "Features",
|
||||
"active": header_navigation.is_selected('features')
|
||||
},
|
||||
{
|
||||
"href": url_for('main.platform_admin_splash_page'),
|
||||
"text": "Platform admin",
|
||||
"active": header_navigation.is_selected('platform-admin')
|
||||
},
|
||||
{
|
||||
"href": url_for('main.support'),
|
||||
"text": "Contact us",
|
||||
"active": header_navigation.is_selected('support')
|
||||
}
|
||||
] %}
|
||||
{% if current_service %}
|
||||
{% set secondaryNavigation = [
|
||||
{
|
||||
"href": url_for('main.service_settings', service_id=current_service.id),
|
||||
"text": "Settings",
|
||||
"active": secondary_navigation.is_selected('settings')
|
||||
},
|
||||
{
|
||||
"href": url_for('main.sign_out'),
|
||||
"text": "Sign out"
|
||||
}
|
||||
] %}
|
||||
{% else %}
|
||||
{% set secondaryNavigation = [
|
||||
{
|
||||
"href": url_for('main.sign_out'),
|
||||
"text": "Sign out"
|
||||
}
|
||||
] %}
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{% set navigation = [
|
||||
{
|
||||
"href": url_for("main.show_accounts_or_dashboard"),
|
||||
"text": "Current service",
|
||||
"active": header_navigation.is_selected('accounts-or-dashboard')
|
||||
},
|
||||
{
|
||||
"href": url_for('main.get_started'),
|
||||
"text": "Using Notify",
|
||||
"active": header_navigation.is_selected('using_notify')
|
||||
},
|
||||
{
|
||||
"href": url_for('main.features'),
|
||||
"text": "Features",
|
||||
"active": header_navigation.is_selected('features')
|
||||
},
|
||||
{
|
||||
"href": url_for('main.support'),
|
||||
"text": "Contact us",
|
||||
"active": header_navigation.is_selected('support')
|
||||
},
|
||||
{
|
||||
"href": url_for('main.user_profile'),
|
||||
"text": "User profile",
|
||||
"active": header_navigation.is_selected('user-profile')
|
||||
}
|
||||
] %}
|
||||
{% if current_service %}
|
||||
{% set secondaryNavigation = [
|
||||
{
|
||||
"href": url_for('main.service_settings', service_id=current_service.id),
|
||||
"text": "Settings",
|
||||
"active": secondary_navigation.is_selected('settings')
|
||||
},
|
||||
{
|
||||
"href": url_for('main.sign_out'),
|
||||
"text": "Sign out"
|
||||
}
|
||||
] %}
|
||||
{% else %}
|
||||
{% set secondaryNavigation = [
|
||||
{
|
||||
"href": url_for('main.sign_out'),
|
||||
"text": "Sign out"
|
||||
}
|
||||
] %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<!-- Add navigation back after pilot -->
|
||||
{# {% set navigation = [
|
||||
{
|
||||
"href": url_for('main.get_started'),
|
||||
"text": "Using Notify",
|
||||
"active": header_navigation.is_selected('using_notify')
|
||||
},
|
||||
{
|
||||
"href": url_for('main.features'),
|
||||
"text": "Features",
|
||||
"active": header_navigation.is_selected('features')
|
||||
},
|
||||
{
|
||||
"href": url_for('main.support'),
|
||||
"text": "Contact us",
|
||||
"active": header_navigation.is_selected('support')
|
||||
},
|
||||
{
|
||||
"href": url_for('main.sign_in'),
|
||||
"text": "Sign in",
|
||||
"active": header_navigation.is_selected('sign-in')
|
||||
}
|
||||
] %} #}
|
||||
{% endif %}
|
||||
|
||||
{{ usaHeader({
|
||||
"homepageUrl": url_for('main.show_accounts_or_dashboard'),
|
||||
"productName": "Notify",
|
||||
"navigation": navigation,
|
||||
"navigationClasses": "govuk-header__navigation--end",
|
||||
"secondaryNavigation": secondaryNavigation,
|
||||
"assetsPath": asset_path + "images"
|
||||
}) }}
|
||||
{% endblock %}
|
||||
<!-- \\#endregion -->
|
||||
|
||||
<!-- \\#region block main -->
|
||||
{% block main %}
|
||||
<div class="grid-container">
|
||||
{% block beforeContent %}
|
||||
{% block backLink %}{% endblock %}
|
||||
{% endblock %}
|
||||
{% block mainClasses %}
|
||||
<!-- notes set mainClasses = "margin-top-5 padding-bottom-5" where withoutnav_template was used and maybe templates that are using content_template -->
|
||||
<main class="{{ mainClasses }}" id="main-content" role="main">
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
{% block flash_messages %}
|
||||
<!-- flash_message.html was from the withoutnav_template and is only included on child templates that was using withoutnav_template. Now, we can add in flash_message blocks and include 'flash_messages.html' to child templates that was using withoutnav_template. This will help to eliminate the use of a whole other parent template. -->
|
||||
{% endblock %}
|
||||
{% block maincolumn_content %}
|
||||
{% block fromContentTemplatetwoColumnGrid %}
|
||||
<div class="grid-row">
|
||||
{% if navigation_links %}
|
||||
<div class="tablet:grid-col-2">
|
||||
{{ sub_navigation(navigation_links) }}
|
||||
</div>
|
||||
<div class="tablet:grid-col-10 padding-left-4 usa-prose site-prose">
|
||||
{% else %}
|
||||
<div class="tablet:grid-col-10">
|
||||
{% endif %}
|
||||
{% block content_column_content %}{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
<!-- content_column_content block is from the content_template.html. We do not need this template. We can consolidate and move it to the base.html template. We can call on this block where child templates were using the content_template.html -->
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
</main>
|
||||
</div>
|
||||
{% endblock %}
|
||||
<!-- \\#endregion -->
|
||||
|
||||
<!-- \\#region block footer -->
|
||||
{% block footer %}
|
||||
|
||||
{% if current_service and current_service.research_mode %}
|
||||
{% set meta_suffix = 'Built by the <a href="https://www.gsa.gov/about-us/organization/federal-acquisition-service/technology-transformation-services/tts-solutions" class="usa-link">Technology Transformation Services</a><span id="research-mode" class="research-mode">research mode</span>' %}
|
||||
{% else %}
|
||||
{% set meta_suffix = 'Built by the <a href="https://www.gsa.gov/about-us/organization/federal-acquisition-service/technology-transformation-services/tts-solutions" class="usa-link">Technology Transformation Services</a>' %}
|
||||
{% endif %}
|
||||
|
||||
{{ usaFooter({
|
||||
"classes": "js-footer",
|
||||
"navigation": [
|
||||
{
|
||||
"title": "About Notify",
|
||||
"columns": 1,
|
||||
"items": [
|
||||
{
|
||||
"href": url_for("main.features"),
|
||||
"text": "Features"
|
||||
},
|
||||
{
|
||||
"href": url_for("main.roadmap"),
|
||||
"text": "Roadmap"
|
||||
},
|
||||
{
|
||||
"href": url_for("main.security"),
|
||||
"text": "Security"
|
||||
},
|
||||
{
|
||||
"href": url_for("main.terms"),
|
||||
"text": "Terms of use"
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Using Notify",
|
||||
"columns": 1,
|
||||
"items": [
|
||||
{
|
||||
"href": url_for("main.get_started"),
|
||||
"text": "Get started"
|
||||
},
|
||||
{
|
||||
"href": url_for("main.pricing"),
|
||||
"text": "Pricing"
|
||||
},
|
||||
{
|
||||
"href": url_for("main.trial_mode_new"),
|
||||
"text": "Trial mode"
|
||||
},
|
||||
{
|
||||
"href": url_for("main.message_status"),
|
||||
"text": "Delivery status"
|
||||
},
|
||||
{
|
||||
"href": url_for("main.guidance_index"),
|
||||
"text": "Guidance"
|
||||
},
|
||||
{
|
||||
"href": url_for("main.documentation"),
|
||||
"text": "API documentation"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Support",
|
||||
"columns": 1,
|
||||
"items": [
|
||||
{
|
||||
"href": url_for('main.support'),
|
||||
"text": "Contact us"
|
||||
},
|
||||
]
|
||||
},
|
||||
],
|
||||
"meta": {
|
||||
"items": meta_items,
|
||||
"html": meta_suffix
|
||||
}
|
||||
}) }}
|
||||
|
||||
{% if current_user.is_authenticated %}
|
||||
{% block sessionUserWarning %}
|
||||
<dialog class="usa-modal" id="sessionTimer" aria-labelledby="sessionTimerHeading" aria-describedby="timerWarning">
|
||||
<div class="usa-modal__content">
|
||||
<div class="usa-modal__main">
|
||||
<h2 class="usa-modal__heading" id="sessionTimerHeading">
|
||||
Your session will end soon.
|
||||
<span class="usa-sr-only">Please choose to extend your session or sign out. Your session will expire in 5 minutes or less.</span>
|
||||
</h2>
|
||||
<div class="usa-prose">
|
||||
<p>You have been inactive for too long.
|
||||
Your session will expire in <span id="timeLeft" role="timer"></span>.
|
||||
</p>
|
||||
</div>
|
||||
<div class="usa-modal__footer">
|
||||
<ul class="usa-button-group">
|
||||
<li class="usa-button-group__item">
|
||||
<button type="button" class="usa-button" id="extendSessionTimer" data-close-modal>
|
||||
Extend Session
|
||||
</button>
|
||||
</li>
|
||||
<li class="usa-button-group__item">
|
||||
<button type="button" class="usa-button usa-button--unstyled padding-105 text-center" id="logOutTimer"
|
||||
data-close-modal>
|
||||
Sign out
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</dialog>
|
||||
{% endblock %}
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
<!-- \\#endregion -->
|
||||
|
||||
{% block bodyEnd %}
|
||||
{% block extra_javascripts %}
|
||||
{% endblock %}
|
||||
<!--[if gt IE 8]><!-->
|
||||
<script type="text/javascript" src="{{ asset_url('javascripts/all.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ asset_url('js/uswds.min.js') }}"></script>
|
||||
<!--<![endif]-->
|
||||
{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
35
app/templates/new/layouts/org_template.html
Normal file
35
app/templates/new/layouts/org_template.html
Normal file
@@ -0,0 +1,35 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block per_page_title %}
|
||||
{% block org_page_title %}{% endblock %} – {{ current_org.name }}
|
||||
{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
<div class="grid-container">
|
||||
<div class="navigation-service usa-breadcrumb">
|
||||
{% if current_user.platform_admin %}
|
||||
<a href="{{ url_for('.organizations') }}" class="usa-link navigation-organization-link">Organizations</a>
|
||||
{% endif %}
|
||||
<div class="navigation-service">
|
||||
{{ current_org.name }}
|
||||
</div>
|
||||
<a href="{{ url_for('main.choose_account') }}" class="usa-link navigation-service">Switch service</a>
|
||||
</div>
|
||||
<div class="grid-row">
|
||||
<div class="grid-col-3">
|
||||
{% include "org_nav.html" %}
|
||||
</div>
|
||||
<div class="grid-col-9">
|
||||
{% block beforeContent %}
|
||||
{% block backLink %}{% endblock %}
|
||||
{% endblock %}
|
||||
<main class="main" id="main-content" role="main" >
|
||||
{% block content %}
|
||||
{% include 'flash_messages.html' %}
|
||||
{% block maincolumn_content %}{% endblock %}
|
||||
{% endblock %}
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
41
app/templates/new/layouts/withnav_template.html
Normal file
41
app/templates/new/layouts/withnav_template.html
Normal file
@@ -0,0 +1,41 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block per_page_title %}
|
||||
{% block service_page_title %}{% endblock %} – {{ current_service.name }}
|
||||
{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
<div class="grid-container">
|
||||
{% block serviceNavigation %}
|
||||
{% include "service_navigation.html" %}
|
||||
{% endblock %}
|
||||
<!-- The withnav_template can be used to replace the settings_template. when it comes to setting_template and withnav_template, this service_navigation.html is only used in withnav_template and in settings_template, it was not used. That is one out of the two differences between settings template and withnav template. Child templates that extends settings_template, include the block serviceNavigation but leave it empty. Within the app, the only pages settings_template.html is used: manage-users.html, service-settings.html, and user-profile.html -->
|
||||
<div class="grid-row margin-top-5">
|
||||
{% if help %}
|
||||
<div class="grid-col-3">
|
||||
{% else %}
|
||||
<div class="grid-col-3">
|
||||
{% endif %}
|
||||
{% block sideNavigation %}
|
||||
{% include "main_nav.html" %}
|
||||
<!-- include settings_nav.html for child templates that used settings_template -->
|
||||
{% endblock %}
|
||||
</div>
|
||||
{% if help %}
|
||||
<div class="grid-col-8">
|
||||
{% else %}
|
||||
<div class="grid-col-9 padding-left-4">
|
||||
{% endif %}
|
||||
{% block beforeContent %}
|
||||
{% block backLink %}{% endblock %}
|
||||
{% endblock %}
|
||||
<main id="main-content" role="main" class="usa-prose site-prose margin-bottom-10">
|
||||
{% block content %}
|
||||
{% include 'flash_messages.html' %}
|
||||
{% block maincolumn_content %}{% endblock %}
|
||||
{% endblock %}
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
37
app/templates/new/templates_glossary.md
Normal file
37
app/templates/new/templates_glossary.md
Normal file
@@ -0,0 +1,37 @@
|
||||
|
||||
# New Templates Glossary
|
||||
|
||||
This document serves as a glossary for the templates directory structure of the project.
|
||||
|
||||
## Directory Structure
|
||||
|
||||
- `/templates`
|
||||
- `base.html`: The main base template from which all other templates inherit. This template is a combination of `main_template`, `admin_template`, `withoutnav_template` and `content_template`.
|
||||
- **/layouts**: Contains base templates and shared layouts used across the site. Simply put, it defines the overall structure or skeleton of the application (less frequently revised).
|
||||
- `withnav_template.html`: A variation of the base layout that includes a sidebar.
|
||||
- `org_template.html`: A variaton of the withnav_template
|
||||
- **/components**: Houses reusable UI components that can be included in multiple templates and can be tailored with different content or links depending on the context.(more frequently revised or customized)
|
||||
- `header.html`: Template for the site's header, included in `base.html`.
|
||||
- `footer.html`: Template for the site's footer, included in `base.html`.
|
||||
- **/views** (or **/pages**): Individual page templates that use the base layouts, components, and partials to present content.
|
||||
|
||||
### Best Practices
|
||||
|
||||
- Use **inheritance** (`{% extends %}`) to build on base layouts.
|
||||
- Employ **components** (`{% include %}`) for reusable UI elements to keep the code DRY and facilitate easier updates.
|
||||
|
||||
### Observation Notes
|
||||
- The macro-options.json files in the header and footer component act as structural guides. They aren't directly used as data passed to the usaFooter function/macro. Instead, these files outline the expected properties and provide a description of their purpose. The `usaFooter` macro component is currently only invoked in the `admin_template`, which will eventually serve as the `base.html` template. This will simplify the approach when we change the footer macros to componenets by eliminating the need to dynamically pass this data from the base.html template.
|
||||
|
||||
|
||||
|
||||
### Old Layout Templates We Don't Need
|
||||
- withoutnav_template.html Delete
|
||||
- main_template.html Delete
|
||||
- settings_templates.html `withnav_template` can be used to replace `settings_template`.
|
||||
- settings_nav.html (move to /new/navigation directory)
|
||||
- main_nav.html (move to /new/navigation directory)
|
||||
- service_navigation.html (move to /new/navigation directory)
|
||||
- org_template, could be under it's own directory called /layout/organization
|
||||
- org_nav.html (move to /new/navigation directory)
|
||||
- content_template.html Delete
|
||||
@@ -2,12 +2,12 @@
|
||||
<p class='bottom-gutter'>
|
||||
{% if job.scheduled_for %}
|
||||
{% if job.processing_started %}
|
||||
Sent by {{ job.created_by.name }} on {{ job.processing_started|format_datetime_short }}
|
||||
Sent by {{ job.created_by.name }} on {{ job.processing_started|format_datetime_short_america }}
|
||||
{% else %}
|
||||
Uploaded by {{ job.created_by.name }} on {{ job.created_at|format_datetime_short }}
|
||||
Uploaded by {{ job.created_by.name }} on {{ job.created_at|format_datetime_short_america }}
|
||||
{% endif %}
|
||||
{% else %}
|
||||
Sent by {{ job.created_by.name }} on {{ job.created_at|format_datetime_short }}
|
||||
Sent by {{ job.created_by.name }} on {{ job.created_at|format_datetime_short_america }}
|
||||
{% endif %}
|
||||
</p>
|
||||
{% if job.status == 'sending limits exceeded'%}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{% from "components/table.html" import list_table, field, right_aligned_field_heading, row_heading, notification_status_field %}
|
||||
{% from "components/page-footer.html" import page_footer %}
|
||||
|
||||
<div class="ajax-block-container" aria-labelledby='pill-selected-item'>
|
||||
<div class="ajax-block-container table-wrapper" aria-labelledby='pill-selected-item'>
|
||||
<div class="dashboard-table bottom-gutter-3-2">
|
||||
|
||||
{% call(item, row_number) list_table(
|
||||
|
||||
@@ -8,16 +8,16 @@
|
||||
<div class="grid-container">
|
||||
<div class="grid-row margin-top-5">
|
||||
{% if help %}
|
||||
<div class="grid-col-3">
|
||||
<div class="tablet:grid-col-3">
|
||||
{% else %}
|
||||
<div class="grid-col-3">
|
||||
<div class="tablet:grid-col-3 margin-bottom-4">
|
||||
{% endif %}
|
||||
{% include "settings_nav.html" %}
|
||||
</div>
|
||||
{% if help %}
|
||||
<div class="grid-col-8">
|
||||
<div class="tablet:grid-col-8">
|
||||
{% else %}
|
||||
<div class="grid-col-9 padding-left-4">
|
||||
<div class="tablet:grid-col-9 tablet:padding-left-4">
|
||||
{% endif %}
|
||||
{% block beforeContent %}
|
||||
{% block backLink %}{% endblock %}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
<div class="ajax-block-container" id='pill-selected-item'>
|
||||
|
||||
{% if notifications %}
|
||||
<div class="table-wrapper">
|
||||
<div class='dashboard-table'>
|
||||
{% endif %}
|
||||
{% call(item, row_number) list_table(
|
||||
@@ -29,6 +30,7 @@
|
||||
{% endcall %}
|
||||
{% if notifications %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if show_pagination %}
|
||||
|
||||
@@ -14,24 +14,26 @@
|
||||
|
||||
{% block maincolumn_content %}
|
||||
{{ page_header('Callbacks') }}
|
||||
<div class="bottom-gutter-3-2 dashboard-table body-copy-table">
|
||||
{% call mapping_table(
|
||||
caption='General',
|
||||
field_headings=['Label', 'Value', 'Action'],
|
||||
field_headings_visible=False,
|
||||
caption_visible=False
|
||||
) %}
|
||||
{% call row() %}
|
||||
{{ text_field('Delivery receipts') }}
|
||||
{{ optional_text_field(delivery_status_callback, truncate=true) }}
|
||||
{{ edit_field('Change', url_for('.delivery_status_callback', service_id=current_service.id)) }}
|
||||
{% endcall %}
|
||||
<div class="table-wrapper">
|
||||
<div class="bottom-gutter-3-2 dashboard-table body-copy-table">
|
||||
{% call mapping_table(
|
||||
caption='General',
|
||||
field_headings=['Label', 'Value', 'Action'],
|
||||
field_headings_visible=False,
|
||||
caption_visible=False
|
||||
) %}
|
||||
{% call row() %}
|
||||
{{ text_field('Delivery receipts') }}
|
||||
{{ optional_text_field(delivery_status_callback, truncate=true) }}
|
||||
{{ edit_field('Change', url_for('.delivery_status_callback', service_id=current_service.id)) }}
|
||||
{% endcall %}
|
||||
|
||||
{% call row() %}
|
||||
{{ text_field('Received text messages') }}
|
||||
{{ optional_text_field(received_text_messages_callback, truncate=true) }}
|
||||
{{ edit_field('Change', url_for('.received_text_messages_callback', service_id=current_service.id)) }}
|
||||
{% endcall %}
|
||||
{% endcall %}
|
||||
{% call row() %}
|
||||
{{ text_field('Received text messages') }}
|
||||
{{ optional_text_field(received_text_messages_callback, truncate=true) }}
|
||||
{{ edit_field('Change', url_for('.received_text_messages_callback', service_id=current_service.id)) }}
|
||||
{% endcall %}
|
||||
{% endcall %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
{% extends "withnav_template.html" %}
|
||||
{% from "components/banner.html" import banner_wrapper %}
|
||||
{% from "components/table.html" import list_table, field, text_field, index_field, hidden_field_heading %}
|
||||
{% from "components/page-header.html" import page_header %}
|
||||
{% from "components/components/button/macro.njk" import usaButton %}
|
||||
{% from "components/components/skip-link/macro.njk" import usaSkipLink %}
|
||||
@@ -9,7 +8,7 @@
|
||||
{% set file_contents_header_id = 'file-preview' %}
|
||||
|
||||
{% block service_page_title %}
|
||||
{{ "Preview of {}".format(template.name) }}
|
||||
{{ "Select delivery time" }}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
@@ -19,11 +18,11 @@
|
||||
|
||||
{% block maincolumn_content %}
|
||||
|
||||
{{ page_header('Preview of {}'.format(template.name)) }}
|
||||
{{ page_header('Select delivery time') }}
|
||||
|
||||
{{ template|string }}
|
||||
<div class="bottom-gutter-3-2">
|
||||
<form method="post" enctype="multipart/form-data" action="{{url_for('main.start_job', service_id=current_service.id, upload_id=upload_id)}}" class='page-footer'>
|
||||
<form method="post" enctype="multipart/form-data" action="{{url_for('main.preview_job', service_id=current_service.id, template_id=template_id, upload_id=upload_id)}}" class='page-footer'>
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
|
||||
{% if choose_time_form %}
|
||||
{{ choose_time_form.scheduled_for(param_extensions={
|
||||
@@ -37,55 +36,11 @@
|
||||
{% endif %}
|
||||
|
||||
{% set button_text %}
|
||||
Send {{ count_of_recipients|message_count(template.template_type) }}
|
||||
Preview
|
||||
{% endset %}
|
||||
{{ usaButton({ "text": button_text }) }}
|
||||
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{% if not request.args.from_test %}
|
||||
|
||||
<h2 class="font-body-lg" id="{{ file_contents_header_id }}">{{ original_file_name }}</h2>
|
||||
|
||||
<div class="fullscreen-content" data-module="fullscreen-table">
|
||||
{% call(item, row_number) list_table(
|
||||
recipients.displayed_rows,
|
||||
caption=original_file_name,
|
||||
caption_visible=False,
|
||||
field_headings=[
|
||||
'<span class="usa-sr-only">Row in file</span><span aria-hidden="true">1</span>'|safe
|
||||
] + recipients.column_headers
|
||||
) %}
|
||||
{% call index_field() %}
|
||||
<span>
|
||||
{% if (item.index + 2) == preview_row %}
|
||||
{{ item.index + 2 }}
|
||||
{% else %}
|
||||
<a class="usa-link" href="{{ url_for('.check_messages', service_id=current_service.id, template_id=template.id, upload_id=upload_id, row_index=(item.index + 2), original_file_name=original_file_name) }}">{{ item.index + 2 }}</a>
|
||||
{% endif %}
|
||||
</span>
|
||||
{% endcall %}
|
||||
{% for column in recipients.column_headers %}
|
||||
{% if item[column].ignore %}
|
||||
{{ text_field(item[column].data or '', status='default') }}
|
||||
{% else %}
|
||||
{{ text_field(item[column].data or '') }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% if item[None].data %}
|
||||
{% for column in item[None].data %}
|
||||
{{ text_field(column, status='default') }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endcall %}
|
||||
</div>
|
||||
|
||||
{% endif %}
|
||||
|
||||
{% if count_of_displayed_recipients < count_of_recipients %}
|
||||
<p class="table-show-more-link">
|
||||
Only showing the first {{ count_of_displayed_recipients }} rows
|
||||
</p>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
77
app/templates/views/check/preview.html
Normal file
77
app/templates/views/check/preview.html
Normal file
@@ -0,0 +1,77 @@
|
||||
{% extends "withnav_template.html" %}
|
||||
{% from "components/banner.html" import banner_wrapper %}
|
||||
{% from "components/table.html" import list_table, field, text_field, hidden_field_heading %}
|
||||
{% from "components/page-header.html" import page_header %}
|
||||
{% from "components/components/button/macro.njk" import usaButton %}
|
||||
{% from "components/components/skip-link/macro.njk" import usaSkipLink %}
|
||||
{% from "components/components/back-link/macro.njk" import usaBackLink %}
|
||||
|
||||
{% set file_contents_header_id = 'file-preview' %}
|
||||
|
||||
{% block service_page_title %}
|
||||
{{ "Preview of {}".format(template.name) }}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block backLink %}
|
||||
{{ usaBackLink({ "href": back_link_from_preview }) }}
|
||||
{% endblock %}
|
||||
|
||||
{% block maincolumn_content %}
|
||||
|
||||
{{ page_header('Preview') }}
|
||||
<div>
|
||||
<p class="sms-message-scheduler">Scheduled: {{ scheduled_for if scheduled_for else 'Now'}}</p>
|
||||
<p class="sms-message-file-name">File: {{original_file_name}}</p>
|
||||
<p class="sms-message-template">Template: {{template.name}}</p>
|
||||
<p class="sms-message-sender" >From: {{ template.sender }}</p>
|
||||
</div>
|
||||
|
||||
<h2 id="{{ file_contents_header_id }}">Message</h2>
|
||||
<div class="preview-message"> {{ simplifed_template|string }}</div>
|
||||
{% if not request.args.from_test %}
|
||||
<h2>Recipients list</h2>
|
||||
<div>
|
||||
<ul class="usa-icon-list">
|
||||
<li class="usa-icon-list__item">
|
||||
<img src="{{ url_for('static', filename='img/material-icons/description.svg') }}" alt="Description Icon">
|
||||
<div class="usa-icon-list__content">
|
||||
<h3>{{ original_file_name }}</h3>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="usa-table-container--scrollable" tabindex="0">
|
||||
{% call(item, row_number) list_table(
|
||||
recipients.displayed_rows,
|
||||
caption="Note: Only the first 5 rows are displayed here.",
|
||||
caption_visible=True,
|
||||
field_headings=recipients.column_headers
|
||||
) %}
|
||||
{% for column in recipients.column_headers %}
|
||||
{% if item[column].ignore %}
|
||||
{{ text_field(item[column].data or '', status='default') }}
|
||||
{% else %}
|
||||
{{ text_field(item[column].data or '') }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% if item[None].data %}
|
||||
{% for column in item[None].data %}
|
||||
{{ text_field(column, status='default') }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endcall %}
|
||||
</div>
|
||||
{% endif %}
|
||||
<!-- <div class="bottom-gutter-3-2">
|
||||
<p>This is a placeholder: This message will be delivered to <b>400 phone numbers</b> and will use a total of <b>800 message parts</b>, leaving Washington DSHS with <b>249,200 message parts remaining</b>.</p>
|
||||
</div> -->
|
||||
<form method="post" enctype="multipart/form-data" action="{{url_for('main.start_job', service_id=current_service.id, upload_id=upload_id)}}" class='page-footer'>
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
|
||||
<h3>Does everything look good?</h3>
|
||||
{% set button_text %}
|
||||
{{ "Schedule" if scheduled_for else 'Send'}}
|
||||
{% endset %}
|
||||
{{ usaButton({ "text": button_text }) }}
|
||||
</form>
|
||||
{% endblock %}
|
||||
@@ -1,115 +1,117 @@
|
||||
{% from "components/table.html" import list_table, field, right_aligned_field_heading, row_heading %}
|
||||
|
||||
<div class='dashboard-table ajax-block-container'>
|
||||
{% call(item, row_number) list_table(
|
||||
jobs,
|
||||
caption="Recent files uploaded",
|
||||
caption_visible=False,
|
||||
empty_message=(
|
||||
'You have not uploaded any files yet.'
|
||||
),
|
||||
field_headings=[
|
||||
'File',
|
||||
'Status'
|
||||
],
|
||||
field_headings_visible=False
|
||||
) %}
|
||||
{% call row_heading() %}
|
||||
<div class="file-list">
|
||||
<a class="file-list-filename-large usa-link" href="{{ url_for('.view_job', service_id=current_service.id, job_id=item.id) }}">{{ item.original_file_name }}</a>
|
||||
{% if item.scheduled %}
|
||||
<span class="file-list-hint-large">
|
||||
Sending {{
|
||||
item.scheduled_for|format_datetime_relative
|
||||
}}
|
||||
</span>
|
||||
{% else %}
|
||||
<span class="file-list-hint-large">
|
||||
Sent {{
|
||||
(item.scheduled_for or item.created_at)|format_datetime_relative
|
||||
}}
|
||||
</span>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
{% endcall %}
|
||||
{% call field() %}
|
||||
{% if item.scheduled %}
|
||||
{% if link %}
|
||||
<a class="usa-link display-flex" href="{{ link }}">
|
||||
{% endif %}
|
||||
<span class="big-number-smallest">
|
||||
<span class="big-number-number">
|
||||
{% if item.notification_count is number %}
|
||||
{% if currency %}
|
||||
{{ "{}{:,.2f}".format(currency, item.notification_count) }}
|
||||
{% else %}
|
||||
{{ "{:,}".format(item.notification_count) }}
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{{ item.notification_count }}
|
||||
{% endif %}
|
||||
</span>
|
||||
{% if item.notification_count %}
|
||||
<span class="big-number-label">{{ item.notification_count|message_count_label(item.template_type,suffix='waiting to send') }}</span>
|
||||
{% endif %}
|
||||
<div class="table-wrapper">
|
||||
<div class='dashboard-table ajax-block-container'>
|
||||
{% call(item, row_number) list_table(
|
||||
jobs,
|
||||
caption="Recent files uploaded",
|
||||
caption_visible=False,
|
||||
empty_message=(
|
||||
'You have not uploaded any files yet.'
|
||||
),
|
||||
field_headings=[
|
||||
'File',
|
||||
'Status'
|
||||
],
|
||||
field_headings_visible=False
|
||||
) %}
|
||||
{% call row_heading() %}
|
||||
<div class="file-list">
|
||||
<a class="file-list-filename-large usa-link" href="{{ url_for('.view_job', service_id=current_service.id, job_id=item.id) }}">{{ item.original_file_name }}</a>
|
||||
{% if item.scheduled %}
|
||||
<span class="file-list-hint-large">
|
||||
Sending {{
|
||||
item.scheduled_for|format_datetime_relative
|
||||
}}
|
||||
</span>
|
||||
{% if link %}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<div class="grid-row">
|
||||
<div class="grid-col-4">
|
||||
{% if link %}
|
||||
<a class="usa-link display-flex" href="{{ link }}">
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<span class="file-list-hint-large">
|
||||
Sent {{
|
||||
(item.scheduled_for or item.created_at)|format_datetime_relative
|
||||
}}
|
||||
</span>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
{% endcall %}
|
||||
{% call field() %}
|
||||
{% if item.scheduled %}
|
||||
{% if link %}
|
||||
<a class="usa-link display-flex" href="{{ link }}">
|
||||
{% endif %}
|
||||
<span class="big-number-smallest">
|
||||
<span class="big-number-number">
|
||||
{{ "{:,}".format(item.notifications_sending) }}
|
||||
</span>
|
||||
<span class="big-number-label">pending</span>
|
||||
</span>
|
||||
{% if link %}
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="grid-col-4">
|
||||
<span class="big-number-smallest">
|
||||
<span class="big-number-number">
|
||||
{% if item.notifications_delivered is number %}
|
||||
{{ "{:,}".format(item.notifications_delivered) }}
|
||||
{% else %}
|
||||
{{ item.notifications_delivered }}
|
||||
{% endif %}
|
||||
</span>
|
||||
<span class="big-number-label">delivered</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="grid-col-4">
|
||||
{% if link %}
|
||||
<a class="usa-link display-flex" href="{{ link }}">
|
||||
{% endif %}
|
||||
<span class="big-number-smallest">
|
||||
<span class="big-number-number">
|
||||
{% if item.notifications_failed is number %}
|
||||
{% if currency %}
|
||||
{{ "{}{:,.2f}".format(currency, item.notifications_failed) }}
|
||||
{% if item.notification_count is number %}
|
||||
{% if currency %}
|
||||
{{ "{}{:,.2f}".format(currency, item.notification_count) }}
|
||||
{% else %}
|
||||
{{ "{:,}".format(item.notification_count) }}
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{{ "{:,}".format(item.notifications_failed) }}
|
||||
{{ item.notification_count }}
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{{ item.notifications_failed }}
|
||||
</span>
|
||||
{% if item.notification_count %}
|
||||
<span class="big-number-label">{{ item.notification_count|message_count_label(item.template_type,suffix='waiting to send') }}</span>
|
||||
{% endif %}
|
||||
</span>
|
||||
<span class="big-number-label">failed</span>
|
||||
{% if link %}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<div class="grid-row">
|
||||
<div class="grid-col-4">
|
||||
{% if link %}
|
||||
<a class="usa-link display-flex" href="{{ link }}">
|
||||
{% endif %}
|
||||
<span class="big-number-smallest">
|
||||
<span class="big-number-number">
|
||||
{{ "{:,}".format(item.notifications_sending) }}
|
||||
</span>
|
||||
<span class="big-number-label">pending</span>
|
||||
</span>
|
||||
{% if link %}
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="grid-col-4">
|
||||
<span class="big-number-smallest">
|
||||
<span class="big-number-number">
|
||||
{% if item.notifications_delivered is number %}
|
||||
{{ "{:,}".format(item.notifications_delivered) }}
|
||||
{% else %}
|
||||
{{ item.notifications_delivered }}
|
||||
{% endif %}
|
||||
</span>
|
||||
<span class="big-number-label">delivered</span>
|
||||
</span>
|
||||
{% if link %}
|
||||
</a>
|
||||
</div>
|
||||
<div class="grid-col-4">
|
||||
{% if link %}
|
||||
<a class="usa-link display-flex" href="{{ link }}">
|
||||
{% endif %}
|
||||
<span class="big-number-smallest">
|
||||
<span class="big-number-number">
|
||||
{% if item.notifications_failed is number %}
|
||||
{% if currency %}
|
||||
{{ "{}{:,.2f}".format(currency, item.notifications_failed) }}
|
||||
{% else %}
|
||||
{{ "{:,}".format(item.notifications_failed) }}
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{{ item.notifications_failed }}
|
||||
{% endif %}
|
||||
</span>
|
||||
<span class="big-number-label">failed</span>
|
||||
</span>
|
||||
{% if link %}
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
</div></div>
|
||||
|
||||
{% endif %}
|
||||
|
||||
</div></div>
|
||||
|
||||
{% endif %}
|
||||
{% endcall %}
|
||||
{% endcall %}
|
||||
{% endcall %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
{{ ajax_block(partials, updates_url, 'template-statistics') }}
|
||||
<h2 class="margin-top-4 margin-bottom-1">Recent Batches</h2>
|
||||
<div>
|
||||
<div class="table-wrapper">
|
||||
<table class="usa-table usa-table--borderless job-table">
|
||||
<thead class="table-field-headings">
|
||||
<tr>
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
<h3>To create and format your message</h3>
|
||||
<ol class="list">
|
||||
<li>All messages start from a template</li>
|
||||
<li>Click “Send Messages”. You’ll see existing templates.</li>
|
||||
<li>Click “<a href={{ url_for('.choose_template', service_id=current_service.id) }}>Send Messages</a>”. You’ll see existing templates.</li>
|
||||
<li>Add a new template or choose an existing template and select Edit.</li>
|
||||
</ol>
|
||||
|
||||
@@ -120,7 +120,7 @@
|
||||
|
||||
|
||||
{# Identify your program #}
|
||||
<h2 class="padding-top-1" id="identify-program">Identify your program</h2>
|
||||
<h2 class="padding-top-1" id="indentify-program">Identify your program</h2>
|
||||
<p>You can help your recipients identify your texts as legitimate by customizing your messages to clearly state who they
|
||||
are from. Consider using the program or benefit name that is most familiar to your recipients.</p>
|
||||
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
<h1 class="font-body-2xl margin-bottom-3">Delivery status</h1>
|
||||
|
||||
<p>Notify’s real-time dashboard lets you check the status of any message.</p>
|
||||
<p>For <a class="usa-link" href="{{ url_for("main.security") }}">security</a>, this information is only available for seven days after a message has been sent. You can download a report, including a list of sent messages, for your own records.</p>
|
||||
<p>This page describes the statuses you’ll see when you’re signed in to Notify.</p>
|
||||
<p>For <a class="usa-link" href="{{ url_for('main.security') }}">security</a>, this information is only available for seven days after a message has been sent. You can download a report, including a list of sent messages, for your own records.</p>
|
||||
<p>This page describes the statuses you'll see when you're signed in to Notify.</p>
|
||||
|
||||
<!-- <p>If you’re using the Notify API, read our <a class="usa-link" href="{{ url_for('.documentation') }}">documentation</a> for a list of API statuses.<p>
|
||||
|
||||
@@ -53,13 +53,13 @@
|
||||
caption_visible=False
|
||||
) %}
|
||||
{% for message_status, description in [
|
||||
('Total', 'The total number of messages that have been sent during the last seven days.'),
|
||||
('Pending', 'Notify has sent the message to the provider. The provider will try to deliver the message to the recipient for up to 72 hours. Notify is waiting for delivery information.'),
|
||||
('Sent', 'The mobile networks may not provide any more delivery information.'),
|
||||
('Delivered', 'The message was successfully delivered. Notify cannot tell you if a user has opened or read a message.'),
|
||||
('Not delivered', ('The provider could not deliver the message. This can happen if the phone number was wrong or if the network operator rejects the message. If you’re sure that these phone numbers are correct, you should <a class="usa-link" href="' + url_for(".support") + '">contact us</a>. If not, you should remove them from your database. You’ll still be charged for text messages that cannot be delivered.')|safe),
|
||||
('Phone not accepting messages right now', 'The provider could not deliver the message. This can happen when the recipient’s phone is off, has no signal, or their text message inbox is full. You can try to send the message again. You’ll still be charged for text messages to phones that are not accepting messages.'),
|
||||
('Technical failure', 'Your message was not sent because there was a problem between Notify and the provider. You’ll have to try sending your messages again. You will not be charged for text messages that are affected by a technical failure.'),
|
||||
] %}
|
||||
('Failed', 'The provider could not deliver the message. This can happen if the phone number was wrong or if the network operator
|
||||
rejects the message. If you’re sure that these phone numbers are correct, you should <a class="usa-link" href="/support">contact us</a>. If not, you should remove them from your database. You’ll still be charged for text messages that
|
||||
cannot be delivered.' | safe),
|
||||
] %}
|
||||
{% call row() %}
|
||||
{{ text_field(message_status) }}
|
||||
{{ text_field(description) }}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
{% from "components/components/button/macro.njk" import usaButton %}
|
||||
|
||||
{% block service_page_title %}
|
||||
{{ "Error" if error else "Preview of ‘{}’".format(template.name) }}
|
||||
{{ "Error" if error else "Select delivery time" }}
|
||||
{% endblock %}
|
||||
|
||||
{% block backLink %}
|
||||
@@ -40,17 +40,16 @@
|
||||
{% endcall %}
|
||||
</div>
|
||||
{% else %}
|
||||
{{ page_header('Preview of ‘{}’'.format(template.name)) }}
|
||||
{{ page_header('Select delivery time') }}
|
||||
{% endif %}
|
||||
|
||||
{{ template|string }}
|
||||
|
||||
<div class="js-stick-at-bottom-when-scrolling">
|
||||
<form method="post" enctype="multipart/form-data" action="{{url_for(
|
||||
'main.send_notification',
|
||||
'main.preview_notification',
|
||||
service_id=current_service.id,
|
||||
template_id=template.id,
|
||||
help='3' if help else 0
|
||||
template_id=template.id
|
||||
)}}" class='page-footer'>
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
|
||||
{% if not error %}
|
||||
@@ -64,7 +63,9 @@
|
||||
}
|
||||
}) }}
|
||||
{% endif %}
|
||||
{% set button_text %}Send 1 {{ 1|message_count_label(template.template_type, suffix='') }}{% endset %}
|
||||
{% set button_text %}
|
||||
Preview
|
||||
{% endset %}
|
||||
{{ usaButton({ "text": button_text }) }}
|
||||
{% endif %}
|
||||
</form>
|
||||
|
||||
74
app/templates/views/notifications/preview.html
Normal file
74
app/templates/views/notifications/preview.html
Normal file
@@ -0,0 +1,74 @@
|
||||
{% extends "withnav_template.html" %}
|
||||
{% from "components/banner.html" import banner_wrapper %}
|
||||
{% from "components/page-header.html" import page_header %}
|
||||
{% from "components/components/back-link/macro.njk" import usaBackLink %}
|
||||
{% from "components/components/button/macro.njk" import usaButton %}
|
||||
|
||||
{% block service_page_title %}
|
||||
{{ "Error" if error else "Preview" }}
|
||||
{% endblock %}
|
||||
|
||||
{% block backLink %}
|
||||
{{ usaBackLink({ "href": back_link_from_preview }) }}
|
||||
{% endblock %}
|
||||
|
||||
{% block maincolumn_content %}
|
||||
{% if error == 'not-allowed-to-send-to' %}
|
||||
<div class="bottom-gutter">
|
||||
{% call banner_wrapper(type='dangerous') %}
|
||||
{% with
|
||||
count_of_recipients=1,
|
||||
template_type_label=(
|
||||
'phone number' if template.template_type == 'sms' else 'email address'
|
||||
)
|
||||
%}
|
||||
{% include "partials/check/not-allowed-to-send-to.html" %}
|
||||
{% endwith %}
|
||||
{% endcall %}
|
||||
</div>
|
||||
{% elif error == 'too-many-messages' %}
|
||||
<div class="bottom-gutter">
|
||||
{% call banner_wrapper(type='dangerous') %}
|
||||
{% include "partials/check/too-many-messages.html" %}
|
||||
{% endcall %}
|
||||
</div>
|
||||
{% elif error == 'message-too-long' %}
|
||||
{# the only row_errors we can get when sending one off messages is that the message is too long #}
|
||||
<div class="bottom-gutter">
|
||||
{% call banner_wrapper(type='dangerous') %}
|
||||
{% include "partials/check/message-too-long.html" %}
|
||||
{% endcall %}
|
||||
</div>
|
||||
{% else %}
|
||||
{{ page_header('Preview') }}
|
||||
{% endif %}
|
||||
<div>
|
||||
<p class="sms-message-scheduler">Scheduled: {{ scheduled_for if scheduled_for else 'Now'}}</p>
|
||||
<p class="sms-message-template">Template: {{template.name}}</p>
|
||||
<p class="sms-message-sender" >From: {{ template.sender }}</p>
|
||||
<p class="sms-message-sender" >To: {{ recipient }}</p>
|
||||
</div>
|
||||
|
||||
<h2 id="{{ file_contents_header_id }}">Message</h2>
|
||||
<div class="preview-message"> {{ simplifed_template|string }}</div>
|
||||
|
||||
<div class="js-stick-at-bottom-when-scrolling">
|
||||
<form method="post" enctype="multipart/form-data" action="{{url_for(
|
||||
'main.send_notification',
|
||||
service_id=current_service.id,
|
||||
template_id=template.id,
|
||||
help='3' if help else 0
|
||||
)}}" class='page-footer'>
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
|
||||
<!-- <p>Placeholder: This message will be delivered to <b>400 phone numbers</b> and will use a total of <b>800 message parts</b>, leaving Washington DSHS with <b>249,200 message parts remaining</b>.</p> -->
|
||||
<h3>Does everything look good?</h3>
|
||||
{% if not error %}
|
||||
{% set button_text %}
|
||||
{{ "Schedule" if scheduled_for else 'Send'}}
|
||||
{% endset %}
|
||||
{{ usaButton({ "text": button_text }) }}
|
||||
{% endif %}
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
@@ -44,31 +44,33 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dashboard-table">
|
||||
{% call(item, row_number) list_table(
|
||||
notifications_by_type|reverse,
|
||||
caption='Messages sent since May 2023',
|
||||
caption_visible=False,
|
||||
field_headings=[
|
||||
'Date',
|
||||
99|message_count_noun('email')|capitalize,
|
||||
99|message_count_noun('sms')|capitalize,
|
||||
],
|
||||
empty_message='No data to show'
|
||||
) %}
|
||||
{% call field() %}
|
||||
{{ item.date | format_date_normal }}
|
||||
<div class="table-wrapper">
|
||||
<div class="dashboard-table">
|
||||
{% call(item, row_number) list_table(
|
||||
notifications_by_type|reverse,
|
||||
caption='Messages sent since May 2023',
|
||||
caption_visible=False,
|
||||
field_headings=[
|
||||
'Date',
|
||||
99|message_count_noun('email')|capitalize,
|
||||
99|message_count_noun('sms')|capitalize,
|
||||
],
|
||||
empty_message='No data to show'
|
||||
) %}
|
||||
{% call field() %}
|
||||
{{ item.date | format_date_normal }}
|
||||
{% endcall %}
|
||||
{% call field() %}
|
||||
{{ item.emails|format_thousands }}
|
||||
{% endcall %}
|
||||
{% call field() %}
|
||||
{{ item.sms|format_thousands }}
|
||||
{% endcall %}
|
||||
{% endcall %}
|
||||
{% call field() %}
|
||||
{{ item.emails|format_thousands }}
|
||||
{% endcall %}
|
||||
{% call field() %}
|
||||
{{ item.sms|format_thousands }}
|
||||
{% endcall %}
|
||||
{% endcall %}
|
||||
<p class="table-show-more-link">
|
||||
Only showing the last {{ notifications_by_type|length }} days
|
||||
</p>
|
||||
<p class="table-show-more-link">
|
||||
Only showing the last {{ notifications_by_type|length }} days
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2 class="govuk-heading-m">
|
||||
@@ -82,26 +84,28 @@
|
||||
) }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="dashboard-table">
|
||||
{% call(item, row_number) list_table(
|
||||
processing_time | reverse,
|
||||
caption='Messages sent within 10 seconds',
|
||||
caption_visible=False,
|
||||
field_headings=[
|
||||
'Date', 'Percentage'
|
||||
],
|
||||
empty_message='No data to show'
|
||||
) %}
|
||||
{% call field() %}
|
||||
{{ item.date | format_date_normal }}
|
||||
<div class="table-wrapper">
|
||||
<div class="dashboard-table">
|
||||
{% call(item, row_number) list_table(
|
||||
processing_time | reverse,
|
||||
caption='Messages sent within 10 seconds',
|
||||
caption_visible=False,
|
||||
field_headings=[
|
||||
'Date', 'Percentage'
|
||||
],
|
||||
empty_message='No data to show'
|
||||
) %}
|
||||
{% call field() %}
|
||||
{{ item.date | format_date_normal }}
|
||||
{% endcall %}
|
||||
{% call field() %}
|
||||
{{ '{:.2f}%'.format(item.percentage_under_10_seconds) }}
|
||||
{% endcall %}
|
||||
{% endcall %}
|
||||
{% call field() %}
|
||||
{{ '{:.2f}%'.format(item.percentage_under_10_seconds) }}
|
||||
{% endcall %}
|
||||
{% endcall %}
|
||||
<p class="table-show-more-link">
|
||||
Only showing the last {{ processing_time|length }} days
|
||||
</p>
|
||||
<p class="table-show-more-link">
|
||||
Only showing the last {{ processing_time|length }} days
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2 class="govuk-heading-m">
|
||||
@@ -120,23 +124,25 @@
|
||||
<span class="usa-sr-only">using Notify.</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dashboard-table">
|
||||
{% call(item, row_number) list_table(
|
||||
organizations_using_notify,
|
||||
caption='Organizations using Notify',
|
||||
caption_visible=False,
|
||||
field_headings=[
|
||||
'Organization', 'Number of live services'
|
||||
],
|
||||
empty_message='No data to show'
|
||||
) %}
|
||||
{% call field() %}
|
||||
{{ item.organization_name }}
|
||||
<div class="table-wrapper">
|
||||
<div class="dashboard-table">
|
||||
{% call(item, row_number) list_table(
|
||||
organizations_using_notify,
|
||||
caption='Organizations using Notify',
|
||||
caption_visible=False,
|
||||
field_headings=[
|
||||
'Organization', 'Number of live services'
|
||||
],
|
||||
empty_message='No data to show'
|
||||
) %}
|
||||
{% call field() %}
|
||||
{{ item.organization_name }}
|
||||
{% endcall %}
|
||||
{% call field() %}
|
||||
{{ item.count_of_live_services }}
|
||||
{% endcall %}
|
||||
{% endcall %}
|
||||
{% call field() %}
|
||||
{{ item.count_of_live_services }}
|
||||
{% endcall %}
|
||||
{% endcall %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@@ -30,7 +30,7 @@ more parts towards the allowance if you:</p>
|
||||
</ul>
|
||||
|
||||
<h3 class="font-body-lg" id="long-text-messages">Long text messages</h3>
|
||||
<p>If a text message is longer than 160 characters (including spaces), it counts as more than one message.</p>
|
||||
<p>If a text message is longer than 160 characters (including spaces), it counts as more than one message part.</p>
|
||||
|
||||
<div class="bottom-gutter-3-2">
|
||||
{% call mapping_table(
|
||||
|
||||
@@ -50,9 +50,9 @@
|
||||
<li>Message send/failure analytics</li>
|
||||
</ul>
|
||||
|
||||
<h3id="next">Next</h3>
|
||||
<h3 id="next">Next</h3>
|
||||
|
||||
<p>If the pilot is successful, we hope to recruit additional high-impact partners to improve outcomes for low-income individuals and families.</p>
|
||||
<p>If the pilot is successful, we hope to recruit additional partners to improve outcomes for low-income individuals and families.</p>
|
||||
|
||||
<p>Goals during this stage:</p>
|
||||
|
||||
|
||||
@@ -65,9 +65,9 @@
|
||||
<h3 class="font-body-lg">Protect sensitive information</h3>
|
||||
<p>Some messages include sensitive information like security codes or password reset links.</p>
|
||||
<p>If you’re sending a message with sensitive information, you can choose to hide those details on the Notify dashboard once the message has been sent. This means that only the message recipient will be able to see that information.</p>
|
||||
<img src="{{ asset_url('images/product/security-review-message.png') }}"
|
||||
alt="Screenshot of a test message in review with the link to 'hide personalization after sending' circled.">
|
||||
|
||||
<h2 class="font-body-lg" id="user-permissions-signing-in">User permissions and signing in</h2>
|
||||
<p>You can set different user permissions in Notify. This lets you control who in your team has access to certain parts of the service.</p>
|
||||
<h3 class="font-body-lg">Two-factor authentication</h3>
|
||||
<p>To sign in to Notify, you’ll need to enter:</p>
|
||||
<ul class="list list-bullet">
|
||||
@@ -76,11 +76,6 @@
|
||||
</ul>
|
||||
<p>If signing in with a text message is a problem for your team, <a class="usa-link" href="{{ url_for('main.support') }}">contact us</a> to find out about using an email link instead.</p>
|
||||
|
||||
<img src="{{ asset_url('images/product/security-review-message.png') }}"
|
||||
alt="Screenshot of a teat message in review with the link to 'hide personalization after sending' circled.">
|
||||
|
||||
<h4>How to hide PII after sending a message</h4>
|
||||
|
||||
<h3>User permissions and signing in</h3>
|
||||
<p>You can set different user permissions in Notify. This lets you control who in your team has access to certain parts of
|
||||
the service.</p>
|
||||
@@ -93,32 +88,4 @@
|
||||
</ul>
|
||||
<p>If signing in with a text message is a problem for your team, <a href="https://beta.notify.gov/support">contact us</a> to find out about using an email link instead.</p>
|
||||
|
||||
<!-- <h2 class="font-body-lg" id="information-risk-management">Information risk management</h2>
|
||||
<p>Our approach to information risk management follows NCSC guidance. It assesses:</p>
|
||||
<ul class="list list-bullet">
|
||||
<li>how Notify is built</li>
|
||||
<li>the infrastructure Notify is built upon</li>
|
||||
<li>support for the Notify service</li>
|
||||
</ul>
|
||||
<p>This approach also applies to the service providers Notify uses to send messages.</p> -->
|
||||
|
||||
<!-- <h2 class="font-body-lg" id="how-we-manage-risk">How we manage risks on Notify</h2>
|
||||
<p>Things we do to manage risks on Notify include:</p>
|
||||
<ul class="list list-bullet">
|
||||
<li>formal risk assessments based on <a class="usa-link" href="http://www.iso.org/iso/catalogue_detail?csnumber=56742">ISO 27005:2011</a> and National Cyber Security Centre guidance</li>
|
||||
<li><a class="usa-link" href="https://www.ncsc.gov.uk/information/check-penetration-testing">CHECK</a>-based testing, both annually and when any major changes are made to Notify</li>
|
||||
<li>residual risk statement preparation and active management of the risk treatment plan</li>
|
||||
<li>regular updates to the Privacy Impact Assessment</li>
|
||||
<li>security impact assessments</li>
|
||||
</ul> -->
|
||||
|
||||
<!-- <h2 class="font-body-lg" id="cabinet-office-approval">Cabinet Office approval</h2>
|
||||
<p>Notify has been assessed and approved by the Cabinet Office Senior Information Risk Officer (SIRO). The SIRO checks this approval once a year.</p>
|
||||
<p>Notify also has approval from the Office of the Government’s SIRO to host data within the EEA.</p>
|
||||
|
||||
<h2 class="font-body-lg" id="classifications-and-security-vetting">Classifications and security vetting</h2>
|
||||
<p>You can use Notify to send messages classified as ‘OFFICIAL’ or ‘OFFICIAL-SENSITIVE’ under the <a class="usa-link" href="https://www.gov.uk/government/publications/government-security-classifications">Government Security Classifications</a> policy.</p>
|
||||
<p>Notify does not process data classified as ‘SECRET’ or ‘TOP SECRET’.</p>
|
||||
<p>The Notify team has Security Check (SC) level clearance from <a class="usa-link" href="https://www.gov.uk/government/organizations/united-kingdom-security-vetting">United Kingdom Security Vetting</a> (UKSV).</p> -->
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@@ -13,9 +13,9 @@
|
||||
<h1 class="font-body-2xl margin-bottom-3">Contact us</h1>
|
||||
<p>Notify is designed to be easy to use.</p>
|
||||
<ul class="list list-bullet">
|
||||
<li>For information on personalization and data preparation, see <a href="/using-notify/guidance">Guidance</a>.</li>
|
||||
<li>For help interpreting delivery reports, see <a href="/using-notify/delivery-status">Delivery Status</a>.</li>
|
||||
<li>For details on pricing and what counts as a message part, see <a href="/using-notify/pricing"></a>Pricing.</li>
|
||||
<li>For information on personalization and data preparation, see <a href={{ url_for("main.guidance_index") }}>Guidance</a>.</li>
|
||||
<li>For help interpreting delivery reports, see <a href={{ url_for("main.message_status") }}>Delivery Status</a>.</li>
|
||||
<li>For details on pricing and what counts as a message part, see <a href={{ url_for("main.pricing") }}>Pricing</a>.</li>
|
||||
</ul>
|
||||
|
||||
<p>If you have other questions, we are available at <a class="usa-link" href="mailto:notify-support@gsa.gov">notify-support@gsa.gov</a>.</p>
|
||||
|
||||
@@ -73,6 +73,7 @@
|
||||
</div> #}
|
||||
|
||||
{% if months %}
|
||||
<div class="table-wrapper">
|
||||
<div class="dashboard-table usage-table body-copy-table margin-top-4">
|
||||
{% call(item, row_index) list_table(
|
||||
months,
|
||||
@@ -129,6 +130,7 @@
|
||||
|
||||
{% endcall %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -9,16 +9,16 @@
|
||||
{% include "service_navigation.html" %}
|
||||
<div class="grid-row margin-top-5">
|
||||
{% if help %}
|
||||
<div class="grid-col-3">
|
||||
<div class="tablet:grid-col-3">
|
||||
{% else %}
|
||||
<div class="grid-col-3">
|
||||
<div class="tablet:grid-col-3">
|
||||
{% endif %}
|
||||
{% include "main_nav.html" %}
|
||||
</div>
|
||||
{% if help %}
|
||||
<div class="grid-col-8">
|
||||
{% else %}
|
||||
<div class="grid-col-9 padding-left-4">
|
||||
<div class="tablet:grid-col-9 tablet:padding-left-4">
|
||||
{% endif %}
|
||||
{% block beforeContent %}
|
||||
{% block backLink %}{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user