Merge branch 'main' of https://github.com/GSA/notifications-admin into 1676-individual-job-table-updates

# Conflicts:
#	app/templates/partials/jobs/notifications.html
This commit is contained in:
Jonathan Bobel
2024-07-29 15:55:59 -04:00
33 changed files with 305 additions and 209 deletions
+1 -11
View File
@@ -407,16 +407,6 @@
"is_secret": false "is_secret": false
} }
], ],
"app/main/views/sign_in.py": [
{
"type": "Private Key",
"filename": "app/main/views/sign_in.py",
"hashed_secret": "1348b145fa1a555461c1b790a2f66614781091e9",
"is_verified": false,
"line_number": 27,
"is_secret": false
}
],
"app/templates/new/components/head.html": [ "app/templates/new/components/head.html": [
{ {
"type": "Base64 High Entropy String", "type": "Base64 High Entropy String",
@@ -702,5 +692,5 @@
} }
] ]
}, },
"generated_at": "2024-07-11T16:37:23Z" "generated_at": "2024-07-24T14:13:02Z"
} }
@@ -327,6 +327,16 @@ td.table-empty-message {
bottom: 0; bottom: 0;
} }
@media (max-width: units('desktop-lg')) {
.table-overflow-x-auto {
overflow-x: auto;
table {
min-width: 768px;
}
}
}
// Dashboard // Dashboard
.dashboard { .dashboard {
@@ -376,9 +386,6 @@ td.table-empty-message {
background-image: url(../img/material-icons/description.svg); background-image: url(../img/material-icons/description.svg);
} }
} }
.table-wrapper {
overflow-x: scroll;
}
} }
.dashboard-table { .dashboard-table {
+6 -1
View File
@@ -230,7 +230,12 @@ def set_up_your_profile():
debug_msg( debug_msg(
f"Added user {usr.email_address} to service {invite_data['service_id']}" f"Added user {usr.email_address} to service {invite_data['service_id']}"
) )
return redirect(url_for("main.show_accounts_or_dashboard")) # notify-admin-1766
# redirect new users to templates area of new service instead of dashboard
service_id = invite_data["service_id"]
url = url_for(".service_dashboard", service_id=service_id)
url = f"{url}/templates"
return redirect(url)
elif login_gov_error: elif login_gov_error:
current_app.logger.error(f"login.gov error: {login_gov_error}") current_app.logger.error(f"login.gov error: {login_gov_error}")
+21 -7
View File
@@ -29,12 +29,18 @@ from notifications_utils.url_safe_token import generate_token
def _reformat_keystring(orig): def _reformat_keystring(orig):
new_keystring = orig.replace("-----BEGIN PRIVATE KEY-----", "") private_key = "PRIVATE " # pragma: allowlist secret
new_keystring = new_keystring.replace("-----END PRIVATE KEY-----", "") private_key = f"{private_key} KEY"
new_keystring = orig.replace(f"-----BEGIN {private_key}-----", "")
new_keystring = new_keystring.replace(f"-----END {private_key}-----", "")
new_keystring = new_keystring.strip() new_keystring = new_keystring.strip()
new_keystring = new_keystring.replace(" ", "\n") new_keystring = new_keystring.replace(" ", "\n")
new_keystring = "\n".join( new_keystring = "\n".join(
["-----BEGIN PRIVATE KEY-----", new_keystring, "-----END PRIVATE KEY-----"] [
f"-----BEGIN {private_key}-----",
new_keystring,
f"-----END {private_key}-----",
]
) )
new_keystring = f"{new_keystring}\n" new_keystring = f"{new_keystring}\n"
return new_keystring return new_keystring
@@ -65,7 +71,9 @@ def _get_access_token(code, state):
response = requests.post(url, headers=headers) response = requests.post(url, headers=headers)
if response.json().get("access_token") is None: if response.json().get("access_token") is None:
# Capture the response json here so it hopefully shows up in error reports # Capture the response json here so it hopefully shows up in error reports
current_app.logger.error(f"Error when getting access token {response.json()}") current_app.logger.error(
f"Error when getting access token {response.json()} #notify-admin-1505"
)
raise KeyError(f"'access_token' {response.json()}") raise KeyError(f"'access_token' {response.json()}")
access_token = response.json()["access_token"] access_token = response.json()["access_token"]
return access_token return access_token
@@ -90,7 +98,9 @@ def _do_login_dot_gov():
login_gov_error = request.args.get("error") login_gov_error = request.args.get("error")
if login_gov_error: if login_gov_error:
current_app.logger.error(f"login.gov error: {login_gov_error}") current_app.logger.error(
f"login.gov error: {login_gov_error} #notify-admin-1505"
)
raise Exception(f"Could not login with login.gov {login_gov_error}") raise Exception(f"Could not login with login.gov {login_gov_error}")
elif code and state: elif code and state:
@@ -100,12 +110,15 @@ def _do_login_dot_gov():
user_email, user_uuid = _get_user_email_and_uuid(access_token) user_email, user_uuid = _get_user_email_and_uuid(access_token)
if not is_gov_user(user_email): if not is_gov_user(user_email):
current_app.logger.error( current_app.logger.error(
"invited user has a non-government email address." "invited user has a non-government email address. #notify-admin-1505"
) )
flash("You must use a government email address.") flash("You must use a government email address.")
abort(403) abort(403)
redirect_url = request.args.get("next") redirect_url = request.args.get("next")
user = user_api_client.get_user_by_uuid_or_email(user_uuid, user_email) user = user_api_client.get_user_by_uuid_or_email(user_uuid, user_email)
current_app.logger.info(
f"Retrieved user {user['id']} from db #notify-admin-1505"
)
# Check if the email needs to be revalidated # Check if the email needs to be revalidated
is_fresh_email = is_less_than_days_ago( is_fresh_email = is_less_than_days_ago(
@@ -115,9 +128,10 @@ def _do_login_dot_gov():
return verify_email(user, redirect_url) return verify_email(user, redirect_url)
usr = User.from_email_address(user["email_address"]) usr = User.from_email_address(user["email_address"])
current_app.logger.info(f"activating user {usr.id} #notify-admin-1505")
activate_user(usr.id) activate_user(usr.id)
except BaseException as be: # noqa B036 except BaseException as be: # noqa B036
current_app.logger.error(be) current_app.logger.error(f"Error signing in: {be} #notify-admin-1505 ")
error(401) error(401)
return redirect(url_for("main.show_accounts_or_dashboard", next=redirect_url)) return redirect(url_for("main.show_accounts_or_dashboard", next=redirect_url))
+7
View File
@@ -38,6 +38,7 @@ def verify_email(token):
current_app.config["EMAIL_EXPIRY_SECONDS"], current_app.config["EMAIL_EXPIRY_SECONDS"],
) )
except SignatureExpired: except SignatureExpired:
current_app.logger.error("Email link expired #notify-admin-1505")
flash( flash(
"The link in the email we sent you has expired. We've sent you a new one." "The link in the email we sent you has expired. We've sent you a new one."
) )
@@ -50,6 +51,9 @@ def verify_email(token):
abort(404) abort(404)
if user.is_active: if user.is_active:
current_app.logger.error(
f"User is using an invite link but is already logged in {user.id} #notify-admin-1505"
)
flash("That verification link has expired.") flash("That verification link has expired.")
return redirect(url_for("main.sign_in")) return redirect(url_for("main.sign_in"))
@@ -59,6 +63,7 @@ def verify_email(token):
user.send_verify_code() user.send_verify_code()
session["user_details"] = {"email": user.email_address, "id": user.id} session["user_details"] = {"email": user.email_address, "id": user.id}
current_app.logger.info(f"Email verified for user {user.id} #notify-admin-1505")
return redirect(url_for("main.verify")) return redirect(url_for("main.verify"))
@@ -78,5 +83,7 @@ def activate_user(user_id):
return redirect(url_for("main.organization_dashboard", org_id=organization_id)) return redirect(url_for("main.organization_dashboard", org_id=organization_id))
else: else:
activated_user = user.activate() activated_user = user.activate()
current_app.logger.info(f"Activated user {user.id} #notify-admin-1505")
activated_user.login() activated_user.login()
current_app.logger.info(f"Logged in user {user.id} #notify-admin-1505")
return redirect(url_for("main.add_service", first="first")) return redirect(url_for("main.add_service", first="first"))
@@ -22,7 +22,7 @@
{% else %} {% else %}
{% if notifications %} {% if notifications %}
<div class="dashboard-table job-status-table"> <div class="dashboard-table job-status-table table-overflow-x-auto">
{% endif %} {% endif %}
{% if job.still_processing %} {% if job.still_processing %}
<p class="bottom-gutter hint"> <p class="bottom-gutter hint">
@@ -1,8 +1,8 @@
{% from "components/table.html" import list_table, field, right_aligned_field_heading, row_heading, notification_status_field %} {% 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 %} {% from "components/page-footer.html" import page_footer %}
<div class="ajax-block-container table-wrapper" aria-labelledby='pill-selected-item'> <div class="ajax-block-container" aria-labelledby='pill-selected-item'>
<div class="dashboard-table bottom-gutter-3-2"> <div class="dashboard-table table-overflow-x-auto">
{% call(item, row_number) list_table( {% call(item, row_number) list_table(
[notification], [notification],
@@ -5,7 +5,7 @@
<div class="ajax-block-container" id='pill-selected-item'> <div class="ajax-block-container" id='pill-selected-item'>
{% if notifications %} {% if notifications %}
<div class="table-wrapper"> <div class="table-overflow-x-auto">
<div class='dashboard-table'> <div class='dashboard-table'>
{% endif %} {% endif %}
{% call(item, row_number) list_table( {% call(item, row_number) list_table(
+2 -2
View File
@@ -14,8 +14,8 @@
{% block maincolumn_content %} {% block maincolumn_content %}
{{ page_header('Callbacks') }} {{ page_header('Callbacks') }}
<div class="table-wrapper"> <div class="table-overflow-x-auto">
<div class="bottom-gutter-3-2 dashboard-table body-copy-table"> <div class="bottom-gutter-3-2 dashboard-table body-copy-table table-overflow-x-auto">
{% call mapping_table( {% call mapping_table(
caption='General', caption='General',
field_headings=['Label', 'Value', 'Action'], field_headings=['Label', 'Value', 'Action'],
+1 -1
View File
@@ -41,7 +41,7 @@
</li> </li>
</ul> </ul>
</div> </div>
<div class="usa-table-container--scrollable" tabindex="0"> <div class="usa-table-container--scrollable table-overflow-x-auto" tabindex="0">
<table class="usa-table usa-table--borderless width-full"> <table class="usa-table usa-table--borderless width-full">
<caption class="font-body-lg table-heading"> <caption class="font-body-lg table-heading">
Note: Only the first 5 rows are displayed here. Note: Only the first 5 rows are displayed here.
+1 -1
View File
@@ -1,6 +1,6 @@
{% from "components/table.html" import list_table, field, right_aligned_field_heading, row_heading %} {% from "components/table.html" import list_table, field, right_aligned_field_heading, row_heading %}
<div class="table-wrapper"> <div class="table-overflow-x-auto">
<div class='dashboard-table ajax-block-container'> <div class='dashboard-table ajax-block-container'>
{% call(item, row_number) list_table( {% call(item, row_number) list_table(
jobs, jobs,
@@ -28,7 +28,7 @@
No messages sent No messages sent
</p> </p>
{% else %} {% else %}
<div class='template-statistics-table'> <div class='template-statistics-table table-overflow-x-auto'>
{% call(item, row_number) list_table( {% call(item, row_number) list_table(
month.templates_used, month.templates_used,
caption=month.name, caption=month.name,
+1 -1
View File
@@ -30,7 +30,7 @@
{{ ajax_block(partials, updates_url, 'template-statistics') }} {{ ajax_block(partials, updates_url, 'template-statistics') }}
<h2 class="margin-top-4 margin-bottom-1">Recent Batches</h2> <h2 class="margin-top-4 margin-bottom-1">Recent Batches</h2>
<div class="table-wrapper"> <div class="table-overflow-x-auto">
<table class="usa-table usa-table--borderless job-table"> <table class="usa-table usa-table--borderless job-table">
<thead class="table-field-headings"> <thead class="table-field-headings">
<tr> <tr>
@@ -2,7 +2,7 @@
<div class="ajax-block-container"> <div class="ajax-block-container">
{% if template_statistics|length > 1 %} {% if template_statistics|length > 1 %}
<div class='template-statistics-table'> <div class='template-statistics-table table-overflow-x-auto'>
{% call(item, row_number) list_table( {% call(item, row_number) list_table(
template_statistics, template_statistics,
caption="Messages sent by template", caption="Messages sent by template",
+4 -1
View File
@@ -24,6 +24,10 @@
<p class="usa-body">To send a text with Notify, you need to create a reusable message template first.</p> <p class="usa-body">To send a text with Notify, you need to create a reusable message template first.</p>
<p class="usa-body">Templates let you send the same thing to lots of people, as often as you need to, without writing a new message each time.</p> <p class="usa-body">Templates let you send the same thing to lots of people, as often as you need to, without writing a new message each time.</p>
<h2 class="heading-medium" id="organizing-content">Organizing your content</h2>
<p class="usa-body">You can organize your content by grouping templates, and putting them into folders.</p>
<p class="usa-body">Set up folders within the Send message section of Notify, where you find templates. You can choose to create a Folder before or after a template is made. If you want to change a users ability to view or edit folders, you may control this through their individual team member permissions.</p>
<h2 class="heading-medium" id="personalised-messages">Personalized content</h2> <h2 class="heading-medium" id="personalised-messages">Personalized content</h2>
<p class="usa-body">Notify makes it easy to send personalized messages from a single template.</p> <p class="usa-body">Notify makes it easy to send personalized messages from a single template.</p>
<p class="usa-body">See <a class="usa-link" href="{{ url_for('.guidance_index', _anchor='personalized-content') }}">how to personalize your content</a>.</p> <p class="usa-body">See <a class="usa-link" href="{{ url_for('.guidance_index', _anchor='personalized-content') }}">how to personalize your content</a>.</p>
@@ -44,7 +48,6 @@
<p class="usa-body">Notify lets you:</p> <p class="usa-body">Notify lets you:</p>
<ul class="list list-bullet"> <ul class="list list-bullet">
<li>set different permission levels for each team member</li> <li>set different permission levels for each team member</li>
<li>invite team members who do not have a government email address</li>
<li>choose who else can manage team members</li> <li>choose who else can manage team members</li>
</ul> </ul>
+51 -49
View File
@@ -162,55 +162,57 @@
location, the flag to receive the content needs to be captured in its own column with a Yes or No (Y/N) flag.</p> location, the flag to receive the content needs to be captured in its own column with a Yes or No (Y/N) flag.</p>
<h3>Example</h3> <h3>Example</h3>
<table class="usa-table"> <div class="table-overflow-x-auto">
<caption class="usa-sr-only"> <table class="usa-table">
Example <caption class="usa-sr-only">
</caption> Example
<thead> </caption>
<tr> <thead>
<th scope="col">Phone number</th> <tr>
<th scope="col">First name</th> <th scope="col">Phone number</th>
<th scope="col">Last name</th> <th scope="col">First name</th>
<th scope="col">Spanish</th> <th scope="col">Last name</th>
<th scope="col">English</th> <th scope="col">Spanish</th>
<th scope="col">Date</th> <th scope="col">English</th>
<th scope="col">Time</th> <th scope="col">Date</th>
<th scope="col">Location</th> <th scope="col">Time</th>
</tr> <th scope="col">Location</th>
</thead> </tr>
<tbody> </thead>
<tr> <tbody>
<td>123-456-7890</td> <tr>
<td>Lulu</td> <td>123-456-7890</td>
<td>Praether</td> <td>Lulu</td>
<td>N</td> <td>Praether</td>
<td>Y</td> <td>N</td>
<td>November 2, 2023</td> <td>Y</td>
<td>3:25</td> <td>November 2, 2023</td>
<td>123 Ford Rd.</td> <td>3:25</td>
</tr> <td>123 Ford Rd.</td>
<tr> </tr>
<td>234-567-8901</td> <tr>
<td>Sela</td> <td>234-567-8901</td>
<td>Appel</td> <td>Sela</td>
<td>Y</td> <td>Appel</td>
<td>N</td> <td>Y</td>
<td>November 2, 2023</td> <td>N</td>
<td>4:00</td> <td>November 2, 2023</td>
<td>123 Ford Rd.</td> <td>4:00</td>
</tr> <td>123 Ford Rd.</td>
<tr> </tr>
<td>123-456-7890</td> <tr>
<td>Dexter</td> <td>123-456-7890</td>
<td>Moseley</td> <td>Dexter</td>
<td>N</td> <td>Moseley</td>
<td>Y</td> <td>N</td>
<td>November 2, 2023</td> <td>Y</td>
<td>2:00</td> <td>November 2, 2023</td>
<td>123 Ford Rd.</td> <td>2:00</td>
</tr> <td>123 Ford Rd.</td>
</tbody> </tr>
</table> </tbody>
</table>
</div>
{# Preventing fraud #} {# Preventing fraud #}
<h2 class="padding-top-1" id="prevent-fraud">Preventing fraud</h2> <h2 class="padding-top-1" id="prevent-fraud">Preventing fraud</h2>
+28 -30
View File
@@ -23,36 +23,34 @@ Inbound SMS
<h1 class="font-body-2xl"> <h1 class="font-body-2xl">
Inbound SMS Inbound SMS
</h1> </h1>
<div class="table-overflow-x-auto">
<table class="usa-table usa-table--borderless inbound"> <table class="usa-table usa-table--borderless inbound width-full">
<thread> <thead>
<tr>
<th>{{table_headings.field_headings[0]}}</th>
<th>{{table_headings.field_headings[1]}}</th>
<th>{{table_headings.field_headings[2]}}</th>
</tr>
</thread>
<tbody>
{% for value in inbound_num_list.data: %}
<tr> <tr>
<td>{{value.number}}</td> <th>{{table_headings.field_headings[0]}}</th>
<td> <th>{{table_headings.field_headings[1]}}</th>
<th>{{table_headings.field_headings[2]}}</th>
{% if value.active %}
Active
{% elif not value.service.name %}
Not used
{% else %}
Inactive
{% endif %}
</td>
<td>
<a href="{{ url_for('main.service_dashboard', service_id=value.service.id) }}" class="usa-link bold">{{ value.service.name }}</a>
</td>
</tr> </tr>
{% endfor %} </thead>
</tbody> <tbody>
</table> {% for value in inbound_num_list.data: %}
<tr>
<td>{{value.number}}</td>
<td>
{% if value.active %}
Active
{% elif not value.service.name %}
Not used
{% else %}
Inactive
{% endif %}
</td>
<td>
<a href="{{ url_for('main.service_dashboard', service_id=value.service.id) }}" class="usa-link bold">{{ value.service.name }}</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endblock %} {% endblock %}
+95 -1
View File
@@ -15,7 +15,7 @@
{% block maincolumn_content %} {% block maincolumn_content %}
{{ page_header('Invite {}'.format(user_to_invite.name if user_to_invite else 'a team member')) }} {{ page_header('Invite {}'.format(user_to_invite.name if user_to_invite else 'a team member')) }}
<a href="#permissions">Learn more about user permissions.</a>
{% call form_wrapper() %} {% call form_wrapper() %}
{% if user_to_invite %} {% if user_to_invite %}
@@ -36,5 +36,99 @@
{{ page_footer('Send invitation email') }} {{ page_footer('Send invitation email') }}
{% endcall %} {% endcall %}
<h2 id="permissions">Set up user permissions</h2>
<p>Setting the right digital control is one of many aspects to keep content and information on a need to know basis. Permissions allow you to do this. Give team members access to important views related to their responsibilities.</p>
<h3 class="site-preview-heading">Permissions</h3>
<div class="usa-accordion usa-accordion--bordered margin-top-1">
<h4 class="usa-accordion__heading">
<button
type="button"
class="usa-accordion__button"
aria-expanded="true"
aria-controls="b-a1"
>
See dashboard
</button>
</h4>
<div id="b-a1" class="usa-accordion__content usa-prose">
<p>Allows users to view the dashboard but not have access to any other functions on the site.
Dashboard viewers can see: Weekly message quantity</p>
<ul>
<li>Message templates used</li>
<li>Recent batches of sent messages</li>
</ul>
<p>This also gives access to:</p>
<ul>
<li>List of templates used by month</li>
<li>Batch details with message status</li>
</ul>
</div>
<h4 class="usa-accordion__heading">
<button
type="button"
class="usa-accordion__button"
aria-expanded="false"
aria-controls="b-a2"
>
Send messages
</button>
</h4>
<div id="b-a2" class="usa-accordion__content usa-prose">
<p>
Allows users to view, select and send approved messages, but restricts users from editing templates
and/or folders. Team members responsible for sending message batches can use templates to prepare
and schedule texts. Actions they take in this section:
</p>
<ul>
<li>View and use templates</li>
<li>Enter personalized message field content</li>
<li>Schedule messages for sending</li>
<li>Send messages</li>
</ul>
</div>
<h4 class="usa-accordion__heading">
<button
type="button"
class="usa-accordion__button"
aria-expanded="false"
aria-controls="b-a3"
>
Add and edit templates
</button>
</h4>
<div id="b-a3" class="usa-accordion__content usa-prose">
<p>
Allows users to view, create or edit, and more or delete templates and folders. They will set up real
messages, but are restricted from uploading data or sending or seeing previous versions. Some actions
they can take:
</p>
<ul>
<li>Create a new template</li>
<li>Create a new folder</li>
<li>Move (or organize) folders and templates</li>
<li>Delete templates and/or folders</li>
</ul>
</div>
<h4 class="usa-accordion__heading">
<button
type="button"
class="usa-accordion__button"
aria-expanded="false"
aria-controls="b-a4"
>
Manage settings and team
</button>
</h4>
<div id="b-a4" class="usa-accordion__content usa-prose">
<p>Leads with service management responsibility may do so in Settings. Managed within Settings:</p>
<ul>
<li>Change service name</li>
<li>Invite team members</li>
<li>Remove unnecessary team members</li>
<li>Turn off the ability to send text messages</li>
<li>Turn off the ability to send service name with text message</li>
</ul>
</div>
</div>
{% endblock %} {% endblock %}
+1 -1
View File
@@ -49,7 +49,7 @@
<p>Notify cannot tell you if your users open an email or click on the links in an email. We do not track open rates and click-throughs because there are privacy issues. Tracking emails without asking permission from users could breach General Data Protection Regulations (GDPR).</p> --> <p>Notify cannot tell you if your users open an email or click on the links in an email. We do not track open rates and click-throughs because there are privacy issues. Tracking emails without asking permission from users could breach General Data Protection Regulations (GDPR).</p> -->
<h2 id="text-message-statuses" class="heading-medium">Text messages</h2> <h2 id="text-message-statuses" class="heading-medium">Text messages</h2>
<div class="margin-bottom-8"> <div class="margin-bottom-8 table-overflow-x-auto">
{% call mapping_table( {% call mapping_table(
caption='Message statuses text messages', caption='Message statuses text messages',
field_headings=['Status', 'Description'], field_headings=['Status', 'Description'],
+26 -28
View File
@@ -44,33 +44,31 @@
</div> </div>
</div> </div>
<div class="table-wrapper"> <div class="dashboard-table table-overflow-x-auto">
<div class="dashboard-table"> {% call(item, row_number) list_table(
{% call(item, row_number) list_table( notifications_by_type|reverse,
notifications_by_type|reverse, caption='Messages sent since May 2023',
caption='Messages sent since May 2023', caption_visible=False,
caption_visible=False, field_headings=[
field_headings=[ 'Date',
'Date', 99|message_count_noun('email')|capitalize,
99|message_count_noun('email')|capitalize, 99|message_count_noun('sms')|capitalize,
99|message_count_noun('sms')|capitalize, ],
], empty_message='No data to show'
empty_message='No data to show' ) %}
) %} {% call field() %}
{% call field() %} {{ item.date | format_date_normal }}
{{ item.date | format_date_normal }}
{% endcall %}
{% call field() %}
{{ item.emails|format_thousands }}
{% endcall %}
{% call field() %}
{{ item.sms|format_thousands }}
{% endcall %}
{% endcall %} {% endcall %}
<p class="table-show-more-link"> {% call field() %}
Only showing the last {{ notifications_by_type|length }} days {{ item.emails|format_thousands }}
</p> {% endcall %}
</div> {% call field() %}
{{ item.sms|format_thousands }}
{% endcall %}
{% endcall %}
<p class="table-show-more-link">
Only showing the last {{ notifications_by_type|length }} days
</p>
</div> </div>
<h2 class="govuk-heading-m"> <h2 class="govuk-heading-m">
@@ -84,7 +82,7 @@
) }} ) }}
</div> </div>
</div> </div>
<div class="table-wrapper"> <div class="table-overflow-x-auto">
<div class="dashboard-table"> <div class="dashboard-table">
{% call(item, row_number) list_table( {% call(item, row_number) list_table(
processing_time | reverse, processing_time | reverse,
@@ -124,7 +122,7 @@
<span class="usa-sr-only">using Notify.</span> <span class="usa-sr-only">using Notify.</span>
</div> </div>
</div> </div>
<div class="table-wrapper"> <div class="table-overflow-x-auto">
<div class="dashboard-table"> <div class="dashboard-table">
{% call(item, row_number) list_table( {% call(item, row_number) list_table(
organizations_using_notify, organizations_using_notify,
@@ -16,7 +16,7 @@
</ol> </ol>
</nav> </nav>
<div class="grid-row margin-bottom-10"> <div class="grid-row margin-bottom-10">
<div class="tablet:grid-col-3 margin-top-4"> <div class="tablet:grid-col-2 margin-top-4">
<nav class="navigation"> <nav class="navigation">
<ul class="usa-sidenav"> <ul class="usa-sidenav">
@@ -41,7 +41,7 @@
</ul> </ul>
</nav> </nav>
</div> </div>
<div class="grid-col-9 tablet:grid-col-9 padding-left-4 sub-content"> <div class="grid-col-10 tablet:grid-col-10 padding-left-4 sub-content">
{% block backLink %}{% endblock %} {% block backLink %}{% endblock %}
<main id="main-content" role="main"> <main id="main-content" role="main">
{% block content %} {% block content %}
@@ -13,26 +13,26 @@
Email complaints Email complaints
</h1> </h1>
<div class="table-overflow-x-auto">
{% call(item, row_number) list_table(
complaints,
caption="Complaints",
caption_visible=False,
empty_message='No complaints',
field_headings=['Notification Id', 'Service', 'Complaint type', 'Complaint Date'],
field_headings_visible=True
) %}
{% call(item, row_number) list_table( {{ link_field(item.notification_id, url_for('main.view_notification', service_id=item.service_id, notification_id=item.notification_id)) }}
complaints,
caption="Complaints",
caption_visible=False,
empty_message='No complaints',
field_headings=['Notification Id', 'Service', 'Complaint type', 'Complaint Date'],
field_headings_visible=True
) %}
{{ link_field(item.notification_id, url_for('main.view_notification', service_id=item.service_id, notification_id=item.notification_id)) }} {{ link_field(item.service_name, url_for('main.service_dashboard', service_id=item.service_id)) }}
{{ link_field(item.service_name, url_for('main.service_dashboard', service_id=item.service_id)) }} {{ text_field(item.complaint_type) }}
{{ text_field(item.complaint_type) }} {{ text_field(item.complaint_date|format_datetime_table if item.complaint_date else None) }}
{{ text_field(item.complaint_date|format_datetime_table if item.complaint_date else None) }}
{% endcall %}
{% endcall %}
</div>
{{ previous_next_navigation(prev_page, next_page) }} {{ previous_next_navigation(prev_page, next_page) }}
{% endblock %} {% endblock %}
@@ -7,6 +7,7 @@
{% from "components/components/details/macro.njk" import usaDetails %} {% from "components/components/details/macro.njk" import usaDetails %}
{% macro services_table(services, caption) %} {% macro services_table(services, caption) %}
<div class="table-overflow-x-auto">
{% call(item, row_number) mapping_table( {% call(item, row_number) mapping_table(
caption=caption, caption=caption,
caption_visible=False, caption_visible=False,
@@ -48,6 +49,7 @@
{% endfor %} {% endfor %}
{% endcall %} {% endcall %}
</div>
{% endmacro %} {% endmacro %}
+3 -3
View File
@@ -35,7 +35,7 @@ part.</p>
<h4>Calculation of message parts without special characters</h4> <h4>Calculation of message parts without special characters</h4>
<div class="bottom-gutter-3-2"> <div class="table-overflow-x-auto">
{% call mapping_table( {% call mapping_table(
caption='Text message pricing', caption='Text message pricing',
field_headings=['Message length', 'Charge'], field_headings=['Message length', 'Charge'],
@@ -86,7 +86,7 @@ recipients preferred language, but we are aware that, rarely, a phone carrier
to be calculated as detailed below.<p> to be calculated as detailed below.<p>
<h4>Calculation of message parts with special characters or non-romanized scripts</h4> <h4>Calculation of message parts with special characters or non-romanized scripts</h4>
{% set accentedChars %} {% set accentedChars %}
<div class="bottom-gutter-3-2"> <div class="table-overflow-x-auto">
{% call mapping_table( {% call mapping_table(
caption='Accented characters that affect text message charges', caption='Accented characters that affect text message charges',
field_headings=['Character', 'Description'], field_headings=['Character', 'Description'],
@@ -151,7 +151,7 @@ to be calculated as detailed below.<p>
"html": accentedChars "html": accentedChars
}) }} }) }}
<div class="bottom-gutter-3-2"> <div class="table-overflow-x-auto">
{% call mapping_table( {% call mapping_table(
caption='Pricing for text messages containing accented characters that affect the charges', caption='Pricing for text messages containing accented characters that affect the charges',
field_headings=['Message length', 'Charge'], field_headings=['Message length', 'Charge'],
+1 -1
View File
@@ -16,7 +16,7 @@
<h1 class="font-body-lg">Settings</h1> <h1 class="font-body-lg">Settings</h1>
<div class="bottom-gutter-3-2 settings-table body-copy-table"> <div class="bottom-gutter-3-2 settings-table body-copy-table table-overflow-x-auto">
{% call mapping_table( {% call mapping_table(
caption='General', caption='General',
+1 -1
View File
@@ -20,7 +20,7 @@ Notify.gov
<div class="desktop:grid-col-7 tablet:grid-col-12"> <div class="desktop:grid-col-7 tablet:grid-col-12">
<h1 class="font-serif-2xl usa-hero__heading">Reach people where they are with government-powered text messages</h1> <h1 class="font-serif-2xl usa-hero__heading">Reach people where they are with government-powered text messages</h1>
<p class="font-sans-lg">Notify.gov is a text message service that helps federal, state, local, tribal and territorial governments more effectively communicate with the people they serve.</p> <p class="font-sans-lg">Notify.gov is a text message service that helps federal, state, local, tribal and territorial governments more effectively communicate with the people they serve.</p>
<div class="usa-button-group margin-bottom-5"> <div class="usa-button-group margin-bottom-5 flex-align-center">
<a class="usa-button usa-button login-button login-button--primary margin-right-2" href="{{ initial_signin_url }}">Sign in with <img src="{{ asset_url('images/logo-login.svg') }}" alt="Login.gov logo"> <a class="usa-button usa-button login-button login-button--primary margin-right-2" href="{{ initial_signin_url }}">Sign in with <img src="{{ asset_url('images/logo-login.svg') }}" alt="Login.gov logo">
</a> </a>
if you are an existing pilot partner if you are an existing pilot partner
+1 -3
View File
@@ -70,8 +70,7 @@
</div> #} </div> #}
{% if months %} {% if months %}
<div class="table-wrapper"> <div class="dashboard-table usage-table body-copy-table table-overflow-x-auto margin-top-4">
<div class="dashboard-table usage-table body-copy-table margin-top-4">
{% call(item, row_index) list_table( {% call(item, row_index) list_table(
months, months,
caption="Total spend", caption="Total spend",
@@ -122,7 +121,6 @@
{% endcall %} {% endcall %}
</div> </div>
</div>
{% endif %} {% endif %}
</div> </div>
+2 -2
View File
@@ -30,7 +30,7 @@
{% block orgNavBreadcrumb %}{% include "/new/components/org_nav_breadcrumb.html" %}{% endblock %} {% block orgNavBreadcrumb %}{% include "/new/components/org_nav_breadcrumb.html" %}{% endblock %}
{% endif %} {% endif %}
<div class="grid-row margin-top-5"> <div class="grid-row margin-top-5">
<div class="tablet:grid-col-3"> <div class="tablet:grid-col-2">
{% block sideNavigation %} {% block sideNavigation %}
{% if current_org.name %} {% if current_org.name %}
{% include "/new/components/org_nav.html" %} {% include "/new/components/org_nav.html" %}
@@ -44,7 +44,7 @@
#} #}
{% endblock %} {% endblock %}
</div> </div>
<div class="tablet:grid-col-9 tablet:padding-left-4"> <div class="tablet:grid-col-10 tablet:padding-left-4">
{% block beforeContent %} {% block beforeContent %}
{% block backLink %}{% endblock %} {% block backLink %}{% endblock %}
{% endblock %} {% endblock %}
-6
View File
@@ -34,12 +34,6 @@ module "logo_upload_bucket" {
name = "${local.app_name}-logo-upload-bucket-${local.env}" name = "${local.app_name}-logo-upload-bucket-${local.env}"
} }
# ##########################################################################
# The following lines need to be commented out for the initial `terraform apply`
# It can be re-enabled after:
# 1) the api app has first been deployed
# 2) the admin app has first been deployed
###########################################################################
module "api_network_route" { module "api_network_route" {
source = "../shared/container_networking" source = "../shared/container_networking"
-1
View File
@@ -1,7 +1,6 @@
locals { locals {
cf_org_name = "gsa-tts-benefits-studio" cf_org_name = "gsa-tts-benefits-studio"
cf_space_name = "notify-local-dev" cf_space_name = "notify-local-dev"
recursive_delete = true
key_name = "${var.username}-admin-dev-key" key_name = "${var.username}-admin-dev-key"
} }
+10 -13
View File
@@ -34,12 +34,6 @@ module "logo_upload_bucket" {
name = "${local.app_name}-logo-upload-bucket-${local.env}" name = "${local.app_name}-logo-upload-bucket-${local.env}"
} }
# ##########################################################################
# The following lines need to be commented out for the initial `terraform apply`
# It can be re-enabled after:
# 1) the api app has first been deployed
# 2) the admin app has first been deployed
###########################################################################
module "api_network_route" { module "api_network_route" {
source = "../shared/container_networking" source = "../shared/container_networking"
@@ -50,22 +44,25 @@ module "api_network_route" {
} }
# ########################################################################## # ##########################################################################
# The following lines need to be commented out for the initial `terraform apply` # This governs the name of our main website. Because domain names are unique,
# It can be re-enabled after: # it only lives here in this one location in production. Resulting problem:
# 1) the app has first been deployed # it is hard to test. Create a temporary, similar code block (with a different
# 2) the route has been manually created by an OrgManager: # subdomain) in Sandbox, test your changes there, and bring them here.
#
# Dependencies:
# 1) an app named notify-admin-production in Cloud.gov
# 2) this route, manually created by an OrgManager:
# `cf create-domain gsa-tts-benefits-studio beta.notify.gov` # `cf create-domain gsa-tts-benefits-studio beta.notify.gov`
# 3) the acme-challenge CNAME record must be created # 3) the acme-challenge CNAME record
# https://cloud.gov/docs/services/external-domain-service/#how-to-create-an-instance-of-this-service # https://cloud.gov/docs/services/external-domain-service/#how-to-create-an-instance-of-this-service
########################################################################### ###########################################################################
module "domain" { module "domain" {
source = "github.com/18f/terraform-cloudgov//domain?ref=v0.7.1" # TODO: upgrade this source = "github.com/GSA-TTS/terraform-cloudgov//domain?ref=v1.0.0"
cf_org_name = local.cf_org_name cf_org_name = local.cf_org_name
cf_space_name = local.cf_space_name cf_space_name = local.cf_space_name
app_name_or_id = "${local.app_name}-${local.env}" app_name_or_id = "${local.app_name}-${local.env}"
name = "${local.app_name}-domain-${local.env}" name = "${local.app_name}-domain-${local.env}"
recursive_delete = false
cdn_plan_name = "domain" cdn_plan_name = "domain"
domain_name = "beta.notify.gov" domain_name = "beta.notify.gov"
} }
+7 -13
View File
@@ -34,17 +34,11 @@ module "logo_upload_bucket" {
name = "${local.app_name}-logo-upload-bucket-${local.env}" name = "${local.app_name}-logo-upload-bucket-${local.env}"
} }
# ########################################################################## module "api_network_route" { # API and Admin apps must both exist in Cloud
# The following lines need to be commented out for the initial `terraform apply` source = "../shared/container_networking"
# It can be re-enabled after:
# 1) the api app has first been deployed
# 2) the admin app has first been deployed
###########################################################################
# module "api_network_route" {
# source = "../shared/container_networking"
# cf_org_name = local.cf_org_name cf_org_name = local.cf_org_name
# cf_space_name = local.cf_space_name cf_space_name = local.cf_space_name
# source_app_name = "${local.app_name}-${local.env}" source_app_name = "${local.app_name}-${local.env}"
# destination_app_name = "notify-api-${local.env}" destination_app_name = "notify-api-${local.env}"
# } }
-6
View File
@@ -34,12 +34,6 @@ module "logo_upload_bucket" {
name = "${local.app_name}-logo-upload-bucket-${local.env}" name = "${local.app_name}-logo-upload-bucket-${local.env}"
} }
# ##########################################################################
# The following lines need to be commented out for the initial `terraform apply`
# It can be re-enabled after:
# 1) the api app has first been deployed
# 2) the admin app has first been deployed
###########################################################################
module "api_network_route" { module "api_network_route" {
source = "../shared/container_networking" source = "../shared/container_networking"