Compare commits

...

230 Commits

Author SHA1 Message Date
Carlo Costino
57e9fc9ba1 Localize notification_utils to the Admin!
This changeset copies everything in notifications_utils to turn into a local project module.  It includes dependency changes to account for this adjustment and the notifications_utils tests.  It also fixes all of the import statements.

Signed-off-by: Carlo Costino <carlo.costino@gsa.gov>
2024-04-05 12:51:31 -04:00
Carlo Costino
2b6b58c93a Merge pull request #1349 from GSA/810-hide-personalization-after-sending
810 hide personalization after sending
2024-04-05 10:59:04 -04:00
Carlo Costino
3e1c06f75c Merge pull request #1386 from GSA/login_gov_env_vars
fix login.gov stuff in sample.env
2024-04-05 09:44:23 -04:00
Carlo Costino
2ede41ce6a Merge pull request #1347 from GSA/1260-dashboard-step-15-use-full-width-base-template-for-landing-page
move footer and head to base.html
2024-04-04 17:13:59 -04:00
Kenneth Kehl
40efb4b561 fix login.gov stuff in sample.env 2024-04-04 13:34:40 -07:00
Beverly Nguyen
51845ec0b0 delete notes 2024-04-04 13:23:21 -07:00
Beverly Nguyen
2075400342 merge main 2024-04-04 13:00:03 -07:00
Steven Reilly
11b970adf8 Merge pull request #1385 from GSA/update-gh-actions
Update GitHub Actions
2024-04-04 09:20:38 -04:00
Carlo Costino
14508b2d97 Update GitHub Actions
This changeset updates all references to GitHub Actions to be version 4 due to a mandatory Node.js update.

Signed-off-by: Carlo Costino <carlo.costino@gsa.gov>
2024-04-04 08:40:24 -04:00
Carlo Costino
1f4573628f Merge pull request #1379 from GSA/login_gov_state
change state to something non-arbitrary
2024-04-03 19:13:40 -04:00
Carlo Costino
2cf3ddb6ff Merge pull request #1371 from GSA/1365-update-date-on-preview-page-and-status-page
Update datetime on preview to be more human readable
2024-04-03 19:12:27 -04:00
Carlo Costino
cc20c02839 Merge pull request #1370 from GSA/1213-clean-up-usage-content
1213 - Removed the Usage page and updated the Dashboard Landing page
2024-04-03 19:10:33 -04:00
Carlo Costino
2d7fba03f5 Merge pull request #1356 from GSA/1252-missing-blue-boxes
1252 missing blue boxes
2024-04-03 19:09:22 -04:00
Carlo Costino
1c0c60a917 Merge pull request #1344 from GSA/1328-base-template-header-component
Converting Header from macro to html
2024-04-03 19:05:02 -04:00
Carlo Costino
6b2ab54870 Merge pull request #1326 from GSA/805-make-dashboard-links-clickable
805 make dashboard links clickable
2024-04-03 19:03:41 -04:00
Carlo Costino
7f54232f21 Merge pull request #1382 from GSA/notify-admin-151
remove template preview stuff
2024-04-03 18:54:03 -04:00
Carlo Costino
8b17adeb34 Merge pull request #1296 from GSA/1215-clean-up-macros-table-on-preview-page
Converting 'preview page' table Macro to HTML
2024-04-03 18:38:31 -04:00
Carlo Costino
c4611a291f Merge pull request #1383 from GSA/dependabot-updates
Update several recent dependencies
2024-04-03 17:52:42 -04:00
Carlo Costino
616957c103 Added an explicit sync of the poetry.lock file to make bootstrap to make sure dependency updates will work
Signed-off-by: Carlo Costino <carlo.costino@gsa.gov>
2024-04-03 17:31:15 -04:00
Carlo Costino
e411774ada Added a few more minor version bumps
Signed-off-by: Carlo Costino <carlo.costino@gsa.gov>
2024-04-03 17:24:26 -04:00
Carlo Costino
748dbc12fd Update several recent dependencies
This changeset updates several dependencies that Dependabot has flagged for us but cannot merge due to the E2E test bug with it.

Signed-off-by: Carlo Costino <carlo.costino@gsa.gov>
2024-04-03 17:09:35 -04:00
Kenneth Kehl
9021530a1a remove template preview stuff 2024-04-03 13:38:54 -07:00
Kenneth Kehl
cde0ab018a fix null on some unit tests 2024-04-02 14:02:04 -07:00
Kenneth Kehl
10ed1dab22 fixes 2024-04-02 13:46:47 -07:00
Kenneth Kehl
817c01faff change state to something non-arbitrary 2024-04-02 13:32:32 -07:00
Carlo Costino
e572d789e0 Merge pull request #1374 from GSA/debug_staging
remove debug
2024-04-01 16:43:32 -04:00
Kenneth Kehl
e4990cfd70 remove debug 2024-04-01 12:55:37 -07:00
Jonathan Bobel
23903b7d29 Merge branch 'main' of https://github.com/GSA/notifications-admin into 1213-clean-up-usage-content 2024-04-01 11:10:41 -04:00
Carlo Costino
0fa9910e2c Merge pull request #1372 from GSA/debug_staging
Handle new user case
2024-03-29 12:27:42 -04:00
Kenneth Kehl
e5136d9fa7 return None if no user 2024-03-29 07:45:16 -07:00
Beverly Nguyen
a2ab04018d fix imports 2024-03-28 18:05:12 -07:00
Beverly Nguyen
5854176ec8 add prase_dt function 2024-03-28 18:01:42 -07:00
Carlo Costino
3abe3352df Merge pull request #1369 from GSA/debug_staging
handle case where existing user is invited to another service
2024-03-28 17:03:51 -04:00
Kenneth Kehl
f092ad8f98 handle case where existing user is invited to another service 2024-03-28 13:20:22 -07:00
Jonathan Bobel
2c57d0d373 1213 - Removed the Usage page and updated the Dashboard Landing page 2024-03-28 15:58:02 -04:00
Carlo Costino
bc79e4dd2c Merge pull request #1368 from GSA/debug_staging
debug staging
2024-03-28 15:18:45 -04:00
Kenneth Kehl
9b1c9b8ccf ugh 2024-03-28 11:35:25 -07:00
Kenneth Kehl
b29c2a3a62 debug staging 2024-03-28 11:15:10 -07:00
Carlo Costino
245bcaecb3 Merge pull request #1363 from GSA/mitchell-login-gov-doc-update
Reformat nested list in Login.gov documentation
2024-03-28 12:32:51 -04:00
Carlo Costino
2ca4c1b029 Merge branch 'mh-patch-1' into mitchell-login-gov-doc-update
Merging in Login.gov documentation updates from @mitchellhenke - thank
you!
2024-03-28 12:18:24 -04:00
Carlo Costino
faaf602dea Merge branch 'patch-1' of github.com:mitchellhenke/notifications-admin into mh-patch-1
Merging in Login.gov documentation updates from @mitchellhenke - thank
you!
2024-03-28 12:16:47 -04:00
Jonathan Bobel
c6af0957e9 Merge branch 'main' of https://github.com/GSA/notifications-admin into 1252-missing-blue-boxes 2024-03-27 09:57:34 -04:00
Beverly Nguyen
d8e9aaee0f updated header nav and seperated out usabanner 2024-03-26 18:00:01 -07:00
Beverly Nguyen
4b9ec67345 merge main 2024-03-26 13:41:42 -07:00
Jonathan Bobel
6c1090ecb2 1252 - add back blue boxes for pending and delivered 2024-03-26 16:06:13 -04:00
Carlo Costino
fd1ab3bf2e Merge pull request #1212 from GSA/1210-create-confirmation-status-page-the-last-page-in-the-preview-send-flow
Create confirmation status alert (the last page in the preview send flow) #1211
2024-03-26 15:55:11 -04:00
Beverly Nguyen
89e7de0ab6 fixed the testing 2024-03-26 12:37:06 -07:00
Beverly Nguyen
e743ab41ff merge main and fixed conflicts 2024-03-26 10:53:01 -07:00
Carlo Costino
6d1efcadba Merge pull request #1325 from GSA/1283-standardize-the-dates-from-format_datetime_short-to-either-numerical-or-text
Standardizing Datetimes to Numeric and Table Formats
2024-03-26 13:29:13 -04:00
Jonathan Bobel
07ee8dfe1e Updated image 2024-03-26 12:15:56 -04:00
Jonathan Bobel
9d673a4d77 Merge branch 'main' of https://github.com/GSA/notifications-admin into 810-hide-personalization-after-sending 2024-03-26 11:23:52 -04:00
Jonathan Bobel
8ca32f476c 810 - Content updates to link area in Send Message flow 2024-03-26 11:23:39 -04:00
Carlo Costino
0e0ee8ae0c Merge pull request #1341 from GSA/notify-admin-1304
Add error handling for successful Login.gov authentications without a…
2024-03-26 11:07:52 -04:00
Mitchell Henke
4428df7dd5 Reformat nested list 2024-03-26 15:06:26 +00:00
Carlo Costino
9beb0a732c Merge pull request #1310 from GSA/1250-login-gov-add-new-user
1250 - Login.gov - New User flow
2024-03-26 10:53:40 -04:00
Beverly Nguyen
39c19594cd updated testing 2024-03-25 15:56:33 -07:00
Beverly Nguyen
4af4fecd45 revert extends 2024-03-25 15:32:27 -07:00
Beverly Nguyen
05563a7051 moved footer and head to base.html 2024-03-25 15:26:12 -07:00
Jonathan Bobel
6e64df0c0b Content update 2024-03-25 15:07:29 -04:00
Jonathan Bobel
e24f77ec63 810 - Changed the link text on this page and also had to update the image for the Security page 2024-03-25 15:07:20 -04:00
Carlo Costino
db5c259818 Merge pull request #1333 from GSA/1293-bug-team-members-with-long-names
Edited style sheet to wrap long emails that overflow the parent div
2024-03-25 12:43:44 -04:00
Steven Reilly
bf4f134538 Merge pull request #1345 from GSA/adjust-e2e-test-fixtures
Adjust E2E test fixtures
2024-03-25 12:38:14 -04:00
Carlo Costino
1aa2501d1d Adjust E2E test fixtures
This changeset adjusts the scope of the end-to-end (E2E) test fixtures so that they behave as intended and we have truly isolated tests.  We may adjust these again in the future depending on our needs, but for right now this will hopefully keep things simple for us as we start to build out an E2E test library.

Thank you, @terrazoon, for flagging this!

Signed-off-by: Carlo Costino <carlo.costino@gsa.gov>
2024-03-25 12:08:34 -04:00
Kenneth Kehl
addc078cbe merge from main 2024-03-22 11:57:47 -07:00
Kenneth Kehl
8362c6b975 fix invites for service and organization 2024-03-22 11:08:49 -07:00
Beverly Nguyen
7c179cadca header marco to html 2024-03-21 21:03:32 -07:00
Carlo Costino
c83655c48c Merge pull request #1342 from GSA/fix-python-deploy
Update Cloud Foundry runtime.txt file
2024-03-21 17:54:15 -04:00
Carlo Costino
c4d738d70b Update Cloud Foundry runtime.txt file
This changeset updates the runtime.txt file so that Cloud Foundry picks up Python 3.12.x instead of Python 3.9.x

Signed-off-by: Carlo Costino <carlo.costino@gsa.gov>
2024-03-21 17:45:01 -04:00
Carlo Costino
a1736b5a4d Merge pull request #1284 from GSA/notify-admin-818
updates with python upgrade
2024-03-21 17:26:04 -04:00
samathad2023
b006e2cacf utils dependencies updates 2024-03-21 13:01:14 -07:00
Carlo Costino
8e75110892 Merge pull request #1294 from GSA/1258-base-template-head
1258 updated base template <head>
2024-03-21 15:51:45 -04:00
Kenneth Kehl
099345fe1e Add error handling for successful Login.gov authentications without a Notify account 2024-03-21 10:58:36 -07:00
Jonathan Bobel
8afacfa89b Moved the <head> and removed the comment for base.html 2024-03-21 12:48:36 -04:00
Jonathan Bobel
ccd3e44eb1 Merge branch 'main' of https://github.com/GSA/notifications-admin into 1258-base-template-head 2024-03-21 12:42:38 -04:00
Kenneth Kehl
6efb16120d Add error handling for successful Login.gov authentications without a Notify account 2024-03-21 09:34:55 -07:00
Jonathan Bobel
24a0dd8c3b Few style updates per PR feedback 2024-03-21 11:00:44 -04:00
samathad2023
00f81a74fd latest utils commit and sync with main 2024-03-20 14:11:16 -07:00
Anastasia Gradova
c7821f2f73 Edited style to wrap long emails that overflow the
parent div.
2024-03-20 09:13:32 -06:00
Kenneth Kehl
04eaec6dd6 fix register 2024-03-19 14:36:43 -07:00
Kenneth Kehl
b12e638592 fix registration 2024-03-19 14:34:41 -07:00
Kenneth Kehl
c053cc7402 change timezone to hidden field with default for now 2024-03-19 13:30:50 -07:00
Carlo Costino
e1611eebec Merge pull request #1327 from GSA/fix-git-hash-deploy
Add missing env var from cf push
2024-03-19 16:01:28 -04:00
Carlo Costino
4b1db40cbd Fix failing tests
This includes a fix for the commit hash test, which was not referencing the Flask app in a valid way.

Signed-off-by: Carlo Costino <carlo.costino@gsa.gov>
2024-03-19 15:53:38 -04:00
Kenneth Kehl
197ef11075 fix tests 2024-03-19 11:32:36 -07:00
Carlo Costino
b4986e4170 Enable Login.gov for all environments
Signed-off-by: Carlo Costino <carlo.costino@gsa.gov>
2024-03-19 13:59:30 -04:00
Carlo Costino
eb599344ad Add missing env var from cf push
This changeset adds a missing environment variable in the cf push command for the production and demo deployments.

Signed-off-by: Carlo Costino <carlo.costino@gsa.gov>
2024-03-19 13:38:34 -04:00
Kenneth Kehl
57fa8dad5d revert change to bypass signin page 2024-03-19 10:15:11 -07:00
Kenneth Kehl
4e6f143675 fix register from join service 2024-03-19 09:30:20 -07:00
Jonathan Bobel
27448d90a0 Merge branch 'main' of https://github.com/GSA/notifications-admin into 1258-base-template-head 2024-03-19 12:29:17 -04:00
samathad2023
da5da1abe9 README updates 2024-03-19 09:19:26 -07:00
Jonathan Bobel
de003a8722 Removed "failure" area so the user can get to all the statuses from the Dashboard Landing screen. A better way to view all statuses will come with a Dashboard redesign. 2024-03-19 12:10:43 -04:00
Jonathan Bobel
f391b5af2c Merge branch 'main' of https://github.com/GSA/notifications-admin into 805-make-dashboard-links-clickable 2024-03-19 10:10:14 -04:00
Beverly Nguyen
e9b9072ae6 updated datetimes based on style guide 2024-03-18 15:10:26 -07:00
Beverly Nguyen
077b28bb15 Merge branch 'main' into 1210-create-confirmation-status-page-the-last-page-in-the-preview-send-flow 2024-03-18 14:33:23 -07:00
Beverly Nguyen
10c9b86547 updating datetimes to match styleguides 2024-03-18 14:32:32 -07:00
Jonathan Bobel
05495f4d42 Updating the route 2024-03-18 13:50:23 -04:00
Jonathan Bobel
ee1c4efb03 805 - Making the clickable area on dashboard a button 2024-03-18 12:43:56 -04:00
Beverly Nguyen
bc71549d12 added a conditional to limit only 5 rows to display 2024-03-15 10:33:39 -07:00
Carlo Costino
6b31e5fba0 Merge pull request #1320 from GSA/1319-bug-js-broken-on-non-sign-in-pages
1319 - BUG - adjusting the js to only run when the targeted DOM element exists
2024-03-15 13:25:04 -04:00
Carlo Costino
88c7f9590f Removing extraneous space 2024-03-15 13:07:50 -04:00
Carlo Costino
dea7f28d5a Merge pull request #1322 from GSA/1321-bug-create-login-gov-button-doesnt-work
1321 - Updated link on the sign-in page
2024-03-15 13:05:40 -04:00
Jonathan Bobel
0e608599c3 1321 - Updated link on the sign-in page 2024-03-15 11:41:30 -04:00
Jonathan Bobel
be2dc9a165 1319 - BUG - adjusting the js to only run when the targeted DOM element exists 2024-03-15 10:42:20 -04:00
Carlo Costino
58a1d8e0c4 Merge pull request #1312 from GSA/update-recent-deps
Update Dependabot flagged dependencies
2024-03-14 18:34:50 -04:00
Beverly Nguyen
02ee1a81d1 Merge branch 'main' into 1210-create-confirmation-status-page-the-last-page-in-the-preview-send-flow 2024-03-14 15:07:20 -07:00
Carlo Costino
c43ed20c9e Update Dependabot flagged dependencies
This changeset updates a handful of dependencies that Dependabot has flagged but is not able to test successfully due to a bug with the E2E configuration.

Signed-off-by: Carlo Costino <carlo.costino@gsa.gov>
2024-03-14 17:31:27 -04:00
Carlo Costino
18be5861d5 Merge pull request #1307 from GSA/notify-admin-1254
hide csv reports in job view if no data available (notify-admin-1254)
2024-03-14 17:28:53 -04:00
Carlo Costino
2afaca6ff1 Merge pull request #1281 from GSA/1266-updated-notify-logo
1266 updated notify logo
2024-03-14 17:17:34 -04:00
Carlo Costino
f0eebfc4f2 Merge pull request #1268 from GSA/1216-login-gov-pre-sign-in-page
1216 login gov pre sign in page
2024-03-14 17:04:25 -04:00
Carlo Costino
cd06337574 Merge pull request #1278 from GSA/1259-base-template-footer
Base template: <footer> #1259
2024-03-14 16:54:44 -04:00
Carlo Costino
85215f45a8 Merge pull request #1311 from GSA/fix_commit_hash
Fix commit hash
2024-03-14 16:36:10 -04:00
Kenneth Kehl
e4087ca9d5 fix commit hash 2024-03-14 13:26:05 -07:00
Kenneth Kehl
3aacdcb3ef fix_commit_hash 2024-03-14 13:25:02 -07:00
Carlo Costino
a09e813f80 Merge pull request #1309 from GSA/update-utils-0-3-0
Update utils to 0.3.0
2024-03-14 16:17:15 -04:00
Carlo Costino
476ba798fc This changeset updates notifications_utils to 0.3.0 to account for other dependency updates.
Signed-off-by: Carlo Costino <carlo.costino@gsa.gov>
2024-03-14 15:18:40 -04:00
Jonathan Bobel
ef53fe7e09 Adding a hint area if needed, removing test code 2024-03-14 14:37:37 -04:00
Beverly Nguyen
a05e09d345 fixed testing 2024-03-14 11:02:02 -07:00
Jonathan Bobel
04f76cd817 Merge branch 'main' of https://github.com/GSA/notifications-admin into 1250-login-gov-add-new-user 2024-03-14 13:18:39 -04:00
Carlo Costino
b2b6b4f612 Merge pull request #1306 from GSA/add-sim-numbers-totrial
Add simulated numbers to allow list in trial mode
2024-03-14 12:51:19 -04:00
Jonathan Bobel
d67923dafc Added the "set up your profile" page and added a select component 2024-03-14 12:26:12 -04:00
Andrew Shumway
69fce499c3 Merge branch 'main' of https://github.com/GSA/notifications-admin into add-sim-numbers-totrial 2024-03-14 10:17:07 -06:00
Andrew Shumway
119b7fc832 Specify username with fail/success 2024-03-14 10:16:56 -06:00
Carlo Costino
4dc442c4b8 Merge pull request #1245 from GSA/notify-admin-204
Add latest version commit hash to footer (notify-admin-204)
2024-03-14 12:13:19 -04:00
Kenneth Kehl
e35168bd40 hide csv reports in job view if no data available 2024-03-14 08:57:58 -07:00
Andrew Shumway
590a599aa4 Merge branch 'main' of https://github.com/GSA/notifications-admin into add-sim-numbers-totrial 2024-03-13 16:31:48 -06:00
Andrew Shumway
599652256e Add simulated SMS numbers to allow list in trial mode 2024-03-13 16:31:21 -06:00
samathad2023
94e05705f8 Merge remote-tracking branch 'origin' into notify-admin-818 2024-03-13 14:48:19 -07:00
samathad2023
b3998c532e formatting updates 2024-03-13 14:46:34 -07:00
Jonathan Bobel
2717feab7b Using a PNG instead of an SVG for the main logo as it was noted the SVG's font looked pixelated. I also changed the color of the text on the home page as well as its responsive size. 2024-03-13 16:07:39 -04:00
Carlo Costino
ae084617fa Merge pull request #1257 from GSA/974-content-personalization
974 - Content updates on Pricing and Guidance pages
2024-03-13 14:29:26 -04:00
Jonathan Bobel
e1981d3c65 Auto reformat looks like 2024-03-13 13:33:44 -04:00
samathad2023
37f6579ca8 removed comments and updated test-service_settings 2024-03-13 10:28:07 -07:00
Kenneth Kehl
cb0ba15571 merge from main and code review feedback 2024-03-13 10:17:38 -07:00
Jonathan Bobel
6cdc14433b Content updated per PR comments 2024-03-13 11:46:13 -04:00
Jonathan Bobel
8d9c030d90 Merge branch 'main' of https://github.com/GSA/notifications-admin into 974-content-personalization 2024-03-13 10:53:30 -04:00
Jonathan Bobel
45c9018365 Update of head from another issue focusing on the favicon area 2024-03-13 10:51:38 -04:00
Beverly Nguyen
86a6b4ff8e marco to table 2024-03-12 13:41:05 -07:00
Jonathan Bobel
7deaf71463 Updated test 2024-03-12 14:29:16 -04:00
Jonathan Bobel
429a456704 Updating conditional logic 2024-03-12 13:37:44 -04:00
Jonathan Bobel
0ae369d54b Updated the date to April 16th instead of April 9 2024-03-12 13:27:37 -04:00
Jonathan Bobel
46ef759a92 Merge branch 'main' of https://github.com/GSA/notifications-admin into 1216-login-gov-pre-sign-in-page 2024-03-12 13:25:06 -04:00
Jonathan Bobel
ed5902b38e Updates to favicon 2024-03-12 13:23:13 -04:00
Jonathan Bobel
924ff82b03 Merge branch 'main' of https://github.com/GSA/notifications-admin into 1266-updated-notify-logo 2024-03-12 12:45:00 -04:00
Jonathan Bobel
bd7ef33c63 Update to one of the favicon-adjacent files 2024-03-12 12:44:42 -04:00
Jonathan Bobel
f10d854617 1258 - Updated the <head> template 2024-03-12 12:28:30 -04:00
Jonathan Bobel
83d83469e4 Merge branch '1266-updated-notify-logo' of https://github.com/GSA/notifications-admin into 1258-base-template-head 2024-03-11 16:17:31 -04:00
samathad2023
7ff7455022 updated to yml 2024-03-11 11:32:49 -07:00
samathad2023
f0bd88b36a additional comments with test_find_users 2024-03-11 11:24:34 -07:00
samathad2023
f5dbf6ad9e Merge branch 'main' of github.com:GSA/notifications-admin into notify-admin-818 2024-03-11 10:51:04 -07:00
Carlo Costino
e9148851fb Merge pull request #1276 from GSA/notify-admin-1237
Use USWDS error formatting for file too big error (notify-admin-1237)
2024-03-11 12:28:26 -04:00
Carlo Costino
bdea60adb9 Merge pull request #1282 from GSA/notify-admin-1277
update login.gov stuff to use user uuid instead of email (notify-admi…
2024-03-11 11:55:04 -04:00
Carlo Costino
b489f49179 Merge pull request #1184 from GSA/1169-display-confirmation-summary-on-the-preview-page
Create and Display Summary on the Preview page #1169
2024-03-11 09:23:12 -04:00
Beverly Nguyen
d1fe8a2332 updated testing 2024-03-08 16:03:08 -08:00
Beverly Nguyen
7479a6c4ea made changes to the token and removed extra handling errors 2024-03-08 15:05:44 -08:00
samathad2023
c3bb44129c tests and orderedset updates for 3.12 upgrade 2024-03-08 15:00:48 -08:00
Carlo Costino
25c8026d89 Merge pull request #1264 from GSA/1234-dashboard-step-1-create-new-base-template
base.html created
2024-03-08 16:59:53 -05:00
Beverly Nguyen
2822b63a49 Merge branch 'main' into 1169-display-confirmation-summary-on-the-preview-page 2024-03-08 12:34:44 -08:00
Beverly Nguyen
cb3fc0f039 update test 2024-03-08 12:25:09 -08:00
Beverly Nguyen
335b01279b fix sorted imports 2024-03-08 11:54:19 -08:00
Beverly Nguyen
b1b416db8c update datetime naming 2024-03-08 11:43:54 -08:00
Beverly Nguyen
ee5fe38001 update testing 2024-03-08 11:30:49 -08:00
Beverly Nguyen
58a067358e with reivew request 2024-03-08 10:32:29 -08:00
Kenneth Kehl
492f798274 update login.gov stuff to use user uuid instead of email (notify-admin-1277) 2024-03-08 09:04:56 -08:00
Jonathan Bobel
99b2fdc28a Few more updates to the <head> 2024-03-08 10:56:17 -05:00
Jonathan Bobel
5c39814daa Small style updates 2024-03-08 10:34:09 -05:00
Jonathan Bobel
f379f5de15 Updated favicon package and header styling 2024-03-08 10:11:51 -05:00
Jonathan Bobel
6a2f54367c Updated logo and og:image 2024-03-07 16:42:57 -05:00
Carlo Costino
7e2977f519 Merge pull request #1279 from GSA/dashboard_bug
dashboard bug
2024-03-07 13:52:33 -05:00
Kenneth Kehl
2be3db10d9 fix the right way 2024-03-07 10:25:11 -08:00
Kenneth Kehl
b3966ae800 revert previous commit 2024-03-07 10:17:37 -08:00
Kenneth Kehl
f42cf3bd22 rebreak the tests to reflect reality 2024-03-07 09:59:58 -08:00
Kenneth Kehl
de38f38892 fix test 2024-03-07 09:45:21 -08:00
Kenneth Kehl
111c6fd93a more fixing 2024-03-07 09:42:06 -08:00
Kenneth Kehl
c587bbee71 dashboard bug 2024-03-07 09:29:51 -08:00
Carlo Costino
0af4545436 Merge pull request #1275 from GSA/stvnrlly/add-logo-to-readme
Update README.md with logo
2024-03-07 08:21:00 -05:00
Beverly Nguyen
87b47d94df moved usa-identifier to footer tag 2024-03-06 13:04:41 -08:00
Kenneth Kehl
68163aa6d7 Use USWDS error formatting for file too big error (notify-admin-1237) 2024-03-06 11:49:19 -08:00
Beverly Nguyen
0bf289e23d removed macro and added footer.html 2024-03-06 11:49:04 -08:00
Steven Reilly
5b8377faae Update README.md 2024-03-06 14:15:37 -05:00
Beverly Nguyen
d0b37cabd6 Merge remote-tracking branch 'origin/main' into 1210-create-confirmation-status-page-the-last-page-in-the-preview-send-flow 2024-03-04 11:45:06 -08:00
Beverly Nguyen
cea10446f7 added notes and org_template 2024-03-04 11:03:22 -08:00
Jonathan Bobel
81d89f464a small responsive styling change 2024-03-04 09:51:13 -05:00
Beverly Nguyen
996db0d60b relative path update 2024-03-01 10:38:07 -08:00
Jonathan Bobel
507973f9ae Edited the test, updated content 2024-03-01 09:34:25 -05:00
Beverly Nguyen
ebd04e44c2 revert main_Template 2024-02-29 16:38:41 -08:00
Beverly Nguyen
98297515d8 updated base.html template to also include admin-template 2024-02-29 16:36:11 -08:00
Jonathan Bobel
7d8d988f62 Merge branch 'main' of https://github.com/GSA/notifications-admin into 1216-login-gov-pre-sign-in-page 2024-02-29 14:37:05 -05:00
Beverly Nguyen
dd37e277a5 updated relative path 2024-02-29 10:56:50 -08:00
Beverly Nguyen
51085cfc40 base.hmtl created 2024-02-29 10:49:30 -08:00
Jonathan Bobel
067b637f75 Changed the title of the pricing page to Message parts 2024-02-28 16:15:33 -05:00
Jonathan Bobel
65f365ab6d Guidance content update 2024-02-28 16:11:18 -05:00
Jonathan Bobel
f49f27bcdf Content updates to pricing page 2024-02-28 16:07:49 -05:00
Jonathan Bobel
a7db80ab04 Merge branch 'main' of https://github.com/GSA/notifications-admin into 1216-login-gov-pre-sign-in-page 2024-02-27 11:53:57 -05:00
Jonathan Bobel
d5f40dffe1 Commit before merge 2024-02-27 11:53:51 -05:00
Jonathan Bobel
3c525b18a5 Merge branch 'main' of https://github.com/GSA/notifications-admin into 1216-login-gov-pre-sign-in-page 2024-02-27 10:26:49 -05:00
Beverly Nguyen
58e252f092 updated Delivery Status title 2024-02-26 12:25:53 -08:00
Kenneth Kehl
f332587b37 handle case where we are trying to build on github and haven't deployed yet 2024-02-26 11:48:06 -08:00
Kenneth Kehl
f245c25e59 merge from main 2024-02-26 11:10:37 -08:00
Kenneth Kehl
141c24e371 add latest version commit hash to footer (notify-admin-204) 2024-02-26 11:07:16 -08:00
Jonathan Bobel
6d05c1ab75 Merge branch 'main' of https://github.com/GSA/notifications-admin into 1216-login-gov-pre-sign-in-page 2024-02-26 11:10:58 -05:00
Jonathan Bobel
fff440a6ff 1216 - sign-in page redesign with login.gov 2024-02-26 11:10:50 -05:00
Beverly Nguyen
039b7cdece update test 2024-02-23 15:14:54 -08:00
Beverly Nguyen
d08968a348 update test 2024-02-23 15:02:18 -08:00
Beverly Nguyen
fd340ed105 update test 2024-02-23 14:59:25 -08:00
Beverly Nguyen
0981990086 update testing 2024-02-23 14:50:26 -08:00
Beverly Nguyen
fff70d4edc fixed spacing 2024-02-23 13:37:48 -08:00
Beverly Nguyen
2ee744e56d fixed spacing 2024-02-23 13:31:49 -08:00
Beverly Nguyen
7fc5b88ea3 updated testing 2024-02-23 13:26:46 -08:00
Beverly Nguyen
8ce0091cc8 Merge remote-tracking branch 'origin/main' into 1210-create-confirmation-status-page-the-last-page-in-the-preview-send-flow 2024-02-23 10:09:18 -08:00
Beverly Nguyen
df47a6ea8a Merge remote-tracking branch 'origin/main' into 1169-display-confirmation-summary-on-the-preview-page 2024-02-23 10:05:53 -08:00
Beverly Nguyen
1e60da1a37 add job status 2024-02-22 11:59:12 -08:00
Beverly Nguyen
2e7aeb1dbf fixed test 2024-02-16 18:05:13 -08:00
Beverly Nguyen
02d1c2b26e fixed test 2024-02-16 18:01:36 -08:00
Beverly Nguyen
c383884017 fixed test 2024-02-16 17:19:03 -08:00
Beverly Nguyen
442ff5dab2 merge main 2024-02-16 11:54:59 -08:00
Beverly Nguyen
44ec565d57 Merge remote-tracking branch 'origin/main' into 1169-display-confirmation-summary-on-the-preview-page 2024-02-16 11:50:01 -08:00
Beverly Nguyen
eccf8cb3e7 added new change from ux meeting as requested 2024-02-16 11:44:25 -08:00
Beverly Nguyen
4eb886d8c0 update test 2024-02-13 18:18:13 -08:00
Beverly Nguyen
c2d7828138 created an alert for confirmation during send flow 2024-02-13 17:48:06 -08:00
Beverly Nguyen
843e4bb2fa fixed testing and removed recipient 2024-02-13 16:18:50 -08:00
Beverly Nguyen
01eac988f1 fixed testing 2024-02-13 14:02:05 -08:00
Beverly Nguyen
49d270521a revision requestsed changes 2024-02-13 13:29:38 -08:00
Beverly Nguyen
b019dc3fe9 updated pytest 2024-02-08 18:43:17 -08:00
Beverly Nguyen
fbf902e168 fixed style checks 2024-02-06 17:14:46 -08:00
Beverly Nguyen
15105bf506 fixed style checks 2024-02-06 17:06:18 -08:00
Beverly Nguyen
8eb329b444 removing comments 2024-02-06 16:42:11 -08:00
Beverly Nguyen
10d3a87bcb Merge remote-tracking branch 'origin/main' into 1169-display-confirmation-summary-on-the-preview-page 2024-02-06 16:09:15 -08:00
Beverly Nguyen
d6f012ee9e editing and creating new preview functions and backlinks 2024-02-06 16:08:14 -08:00
Beverly Nguyen
c073d2efac revising message preview 2024-02-02 16:14:50 -08:00
Beverly Nguyen
3ac65b122c inital preview page 2024-02-02 14:52:48 -08:00
225 changed files with 52390 additions and 1712 deletions

View File

@@ -9,10 +9,10 @@ runs:
sudo apt-get update \
&& sudo apt-get install -y --no-install-recommends \
libcurl4-openssl-dev
- name: Set up Python 3.9
uses: actions/setup-python@v3
- name: Set up Python 3.12
uses: actions/setup-python@v4
with:
python-version: "3.9"
python-version: "3.12"
- name: Install poetry
shell: bash
run: pip install poetry

View File

@@ -22,7 +22,7 @@ jobs:
contents: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: ./.github/actions/setup-project
- uses: jwalton/gh-find-current-pr@v1
id: findPr
@@ -79,12 +79,12 @@ jobs:
# Maps tcp port 6379 on service container to the host
- 6379:6379
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: ./.github/actions/setup-project
- uses: jwalton/gh-find-current-pr@v1
id: findPr
- name: Clone API
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
repository: GSA/notifications-api
path: 'notifications-api'
@@ -140,7 +140,7 @@ jobs:
runs-on: ubuntu-latest
environment: staging
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: ./.github/actions/setup-project
- name: Validate NewRelic config
env:
@@ -153,7 +153,7 @@ jobs:
dependency-audits:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: ./.github/actions/setup-project
- name: Create requirements.txt
run: poetry export --without-hashes --format=requirements.txt > requirements.txt
@@ -166,7 +166,7 @@ jobs:
static-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: ./.github/actions/setup-project
- name: Run scan
run: poetry run bandit -r app/ --confidence-level medium
@@ -174,7 +174,7 @@ jobs:
dynamic-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: ./.github/actions/setup-project
- name: Run server
run: make run-flask &
@@ -193,7 +193,7 @@ jobs:
a11y-scan:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: ./.github/actions/setup-project
- name: Run server
run: make run-flask &

View File

@@ -22,7 +22,7 @@ jobs:
dependency-audits:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: ./.github/actions/setup-project
- name: Create requirements.txt
run: poetry export --without-hashes --format=requirements.txt > requirements.txt
@@ -35,7 +35,7 @@ jobs:
static-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: ./.github/actions/setup-project
- name: Run scan
run: poetry run bandit -r app/ --confidence-level medium
@@ -43,7 +43,7 @@ jobs:
dynamic-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: ./.github/actions/setup-project
- name: Run server
run: make run-flask &

View File

@@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
environment: demo
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 2
@@ -54,6 +54,7 @@ jobs:
ADMIN_CLIENT_SECRET: ${{ secrets.ADMIN_CLIENT_SECRET }}
NEW_RELIC_LICENSE_KEY: ${{ secrets.NEW_RELIC_LICENSE_KEY }}
NR_BROWSER_KEY: ${{ secrets.NR_BROWSER_KEY }}
COMMIT_HASH: ${{ github.sha }}
LOGIN_PEM: ${{ secrets.LOGIN_PEM }}
LOGIN_DOT_GOV_CLIENT_ID: "urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:notify-gov"
LOGIN_DOT_GOV_USER_INFO_URL: "https://secure.login.gov/api/openid_connect/userinfo"
@@ -61,7 +62,7 @@ jobs:
LOGIN_DOT_GOV_LOGOUT_URL: "https://secure.login.gov/openid_connect/logout?client_id=urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:notify-gov&post_logout_redirect_uri=https://notify-demo.app.cloud.gov/sign-out"
LOGIN_DOT_GOV_BASE_LOGOUT_URL: "https://secure.login.gov/openid_connect/logout?"
LOGIN_DOT_GOV_SIGNOUT_REDIRECT: "https://notify-demo.app.cloud.gov/sign-out"
LOGIN_DOT_GOV_INITIAL_SIGNIN_URL: "https://secure.login.gov/openid_connect/authorize?acr_values=http%3A%2F%2Fidmanagement.gov%2Fns%2Fassurance%2Fial%2F1&client_id=urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:notify-gov&nonce=01234567890123456789012345&prompt=select_account&redirect_uri=https://notify-demo.app.cloud.gov/sign-in&response_type=code&scope=openid+email&state=abcdefghijklmnopabcdefghijklmnop"
LOGIN_DOT_GOV_INITIAL_SIGNIN_URL: "https://secure.login.gov/openid_connect/authorize?acr_values=http%3A%2F%2Fidmanagement.gov%2Fns%2Fassurance%2Fial%2F1&client_id=urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:notify-gov&nonce=NONCE&prompt=select_account&redirect_uri=https://notify-demo.app.cloud.gov/sign-in&response_type=code&scope=openid+email&state=STATE"
with:
cf_username: ${{ secrets.CLOUDGOV_USERNAME }}
cf_password: ${{ secrets.CLOUDGOV_PASSWORD }}
@@ -75,6 +76,7 @@ jobs:
--var ADMIN_CLIENT_SECRET="$ADMIN_CLIENT_SECRET"
--var NEW_RELIC_LICENSE_KEY="$NEW_RELIC_LICENSE_KEY"
--var NR_BROWSER_KEY="$NR_BROWSER_KEY"
--var COMMIT_HASH="$COMMIT_HASH"
--var LOGIN_PEM="$LOGIN_PEM"
--var LOGIN_DOT_GOV_CLIENT_ID="$LOGIN_DOT_GOV_CLIENT_ID"
--var LOGIN_DOT_GOV_USER_INFO_URL="$LOGIN_DOT_GOV_USER_INFO_URL"

View File

@@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 2
@@ -54,6 +54,7 @@ jobs:
ADMIN_CLIENT_SECRET: ${{ secrets.ADMIN_CLIENT_SECRET }}
NEW_RELIC_LICENSE_KEY: ${{ secrets.NEW_RELIC_LICENSE_KEY }}
NR_BROWSER_KEY: ${{ secrets.NR_BROWSER_KEY }}
COMMIT_HASH: ${{ github.sha }}
LOGIN_PEM: ${{ secrets.LOGIN_PEM }}
LOGIN_DOT_GOV_CLIENT_ID: "urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:notify-gov"
LOGIN_DOT_GOV_USER_INFO_URL: "https://secure.login.gov/api/openid_connect/userinfo"
@@ -61,7 +62,7 @@ jobs:
LOGIN_DOT_GOV_LOGOUT_URL: "https://secure.login.gov/openid_connect/logout?client_id=urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:notify-gov&post_logout_redirect_uri=https://beta.notify.gov/sign-out"
LOGIN_DOT_GOV_BASE_LOGOUT_URL: "https://secure.login.gov/openid_connect/logout?"
LOGIN_DOT_GOV_SIGNOUT_REDIRECT: "https://beta.notify.gov/sign-out"
LOGIN_DOT_GOV_INITIAL_SIGNIN_URL: "https://secure.login.gov/openid_connect/authorize?acr_values=http%3A%2F%2Fidmanagement.gov%2Fns%2Fassurance%2Fial%2F1&client_id=urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:notify-gov&nonce=01234567890123456789012345&prompt=select_account&redirect_uri=https://beta.notify.gov/sign-in&response_type=code&scope=openid+email&state=abcdefghijklmnopabcdefghijklmnop"
LOGIN_DOT_GOV_INITIAL_SIGNIN_URL: "https://secure.login.gov/openid_connect/authorize?acr_values=http%3A%2F%2Fidmanagement.gov%2Fns%2Fassurance%2Fial%2F1&client_id=urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:notify-gov&nonce=NONCE&prompt=select_account&redirect_uri=https://beta.notify.gov/sign-in&response_type=code&scope=openid+email&state=STATE"
with:
cf_username: ${{ secrets.CLOUDGOV_USERNAME }}
cf_password: ${{ secrets.CLOUDGOV_PASSWORD }}
@@ -75,6 +76,7 @@ jobs:
--var ADMIN_CLIENT_SECRET="$ADMIN_CLIENT_SECRET"
--var NEW_RELIC_LICENSE_KEY="$NEW_RELIC_LICENSE_KEY"
--var NR_BROWSER_KEY="$NR_BROWSER_KEY"
--var COMMIT_HASH="$COMMIT_HASH"
--var LOGIN_PEM="$LOGIN_PEM"
--var LOGIN_DOT_GOV_CLIENT_ID="$LOGIN_DOT_GOV_CLIENT_ID"
--var LOGIN_DOT_GOV_USER_INFO_URL="$LOGIN_DOT_GOV_USER_INFO_URL"

View File

@@ -17,7 +17,7 @@ jobs:
environment: staging
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 2
@@ -60,6 +60,7 @@ jobs:
ADMIN_CLIENT_SECRET: ${{ secrets.ADMIN_CLIENT_SECRET }}
NEW_RELIC_LICENSE_KEY: ${{ secrets.NEW_RELIC_LICENSE_KEY }}
NR_BROWSER_KEY: ${{ secrets.NR_BROWSER_KEY }}
COMMIT_HASH: ${{ github.sha }}
LOGIN_PEM: ${{ secrets.LOGIN_PEM }}
LOGIN_DOT_GOV_CLIENT_ID: "urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:notify-gov"
LOGIN_DOT_GOV_USER_INFO_URL: "https://secure.login.gov/api/openid_connect/userinfo"
@@ -67,7 +68,7 @@ jobs:
LOGIN_DOT_GOV_LOGOUT_URL: "https://secure.login.gov/openid_connect/logout?client_id=urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:notify-gov&post_logout_redirect_uri=https://notify-staging.app.cloud.gov/sign-out"
LOGIN_DOT_GOV_BASE_LOGOUT_URL: "https://secure.login.gov/openid_connect/logout?"
LOGIN_DOT_GOV_SIGNOUT_REDIRECT: "https://notify-staging.app.cloud.gov/sign-out"
LOGIN_DOT_GOV_INITIAL_SIGNIN_URL: "https://secure.login.gov/openid_connect/authorize?acr_values=http%3A%2F%2Fidmanagement.gov%2Fns%2Fassurance%2Fial%2F1&client_id=urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:notify-gov&nonce=01234567890123456789012345&prompt=select_account&redirect_uri=https://notify-staging.app.cloud.gov/sign-in&response_type=code&scope=openid+email&state=abcdefghijklmnopabcdefghijklmnop"
LOGIN_DOT_GOV_INITIAL_SIGNIN_URL: "https://secure.login.gov/openid_connect/authorize?acr_values=http%3A%2F%2Fidmanagement.gov%2Fns%2Fassurance%2Fial%2F1&client_id=urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:notify-gov&nonce=NONCE&prompt=select_account&redirect_uri=https://notify-staging.app.cloud.gov/sign-in&response_type=code&scope=openid+email&state=STATEE"
with:
cf_username: ${{ secrets.CLOUDGOV_USERNAME }}
cf_password: ${{ secrets.CLOUDGOV_PASSWORD }}
@@ -81,6 +82,7 @@ jobs:
--var ADMIN_CLIENT_SECRET="$ADMIN_CLIENT_SECRET"
--var NEW_RELIC_LICENSE_KEY="$NEW_RELIC_LICENSE_KEY"
--var NR_BROWSER_KEY="$NR_BROWSER_KEY"
--var COMMIT_HASH="$COMMIT_HASH"
--var LOGIN_PEM="$LOGIN_PEM"
--var LOGIN_DOT_GOV_CLIENT_ID="$LOGIN_DOT_GOV_CLIENT_ID"
--var LOGIN_DOT_GOV_USER_INFO_URL="$LOGIN_DOT_GOV_USER_INFO_URL"

View File

@@ -13,7 +13,7 @@ jobs:
environment: staging
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Check for drift
uses: dflook/terraform-check@v1
@@ -31,7 +31,7 @@ jobs:
environment: demo
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
ref: 'production'
@@ -51,7 +51,7 @@ jobs:
environment: production
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
ref: 'production'

View File

@@ -16,7 +16,7 @@ jobs:
environment: demo
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: Terraform format
id: format

View File

@@ -16,7 +16,7 @@ jobs:
environment: production
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: Terraform format
id: format

View File

@@ -16,7 +16,7 @@ jobs:
environment: staging
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: Terraform format
id: format

View File

@@ -17,6 +17,7 @@ NVMSH := $(shell [ -f "$(HOME)/.nvm/nvm.sh" ] && echo "$(HOME)/.nvm/nvm.sh" || e
bootstrap: generate-version-file ## Set up everything to run the app
poetry self update
poetry self add poetry-dotenv-plugin
poetry lock --no-update
poetry install --sync --no-root
poetry run playwright install --with-deps
poetry run pre-commit install

View File

@@ -1,3 +1,5 @@
![notify-logo](https://github.com/GSA/notifications-admin/assets/4156602/31b74039-4d87-4f89-a35e-71e9f3152342)
# Notify.gov Admin UI
This is the Notify front-end for government users and admins. To see it in
@@ -183,12 +185,12 @@ session to make the changes take effect.
Now we're ready to install the Python version we need with `pyenv`, like so:
```sh
pyenv install 3.9
pyenv install 3.12
```
This will install the latest version of Python 3.9.
This will install the latest version of Python 3.12.
_NOTE: This project currently runs on Python 3.9.x._
_NOTE: This project currently runs on Python 3.12.x._
#### [API Step] Python Dependency Installation
@@ -232,7 +234,7 @@ Once all of pre-requisites for the project are installed and you have a
cloud.gov account, you can now set up the admin project and get things running
locally!
First, clone the respository in the directory of your choosing on your machine:
First, clone the repository in the directory of your choosing on your machine:
```sh
git clone git@github.com:GSA/notifications-admin.git
@@ -240,12 +242,12 @@ git clone git@github.com:GSA/notifications-admin.git
Now go into the project directory (`notifications-admin` by default), create a
virtual environment, and set the local Python version to point to the virtual
environment (assumes version Python `3.9.18` is what is installed on your
environment (assumes version Python `3.12.2` is what is installed on your
machine):
```sh
cd notifications-admin
pyenv virtualenv 3.9.18 notify-admin
pyenv virtualenv 3.12.2 notify-admin
pyenv local notify-admin
```
@@ -272,6 +274,39 @@ In addition to some infrastructure setup, this will also create a local `.env`
file for you in the project's root directory, which will include a handful of
project-specific environment variables.
#### Upgrading Python in existing projects
If you're upgrading an existing project to a newer version of Python, you can
follow these steps to get yourself up-to-date.
First, use `pyenv` to install the newer version of Python you'd like to use;
we'll use `3.12` in our example here since we recently upgraded to this version:
```sh
pyenv install 3.12
```
Next, delete the virtual environment you previously had set up. If you followed
the instructions above with the first-time set up, you can do this with `pyenv`:
```sh
pyenv virtualenv-delete notify-admin
```
Now, make sure you are in your project directory and recreate the same virtual
environment with the newer version of Python you just installed:
```sh
cd notifications-admin
pyenv virtualenv 3.12.2 notify-admin
pyenv local notify-admin
```
At this point, proceed with the rest of the instructions here in the README and
you'll be set with an upgraded version of Python.
_If you're not sure about the details of your current virtual environment, you can run `poetry env info` to get more information. If you've been using `pyenv` for everything, you can also see all available virtual environments with `pyenv virtualenvs`._
#### Updating the .env file for E2E tests
With the newly created `.env` file in place, you'll need to make one more
@@ -378,52 +413,6 @@ You can do this by going through these steps:
- Make a new PR with the change
- Have the PR get reviewed and merged
### Python dependency management
We're using [`Poetry`](https://python-poetry.org/) for managing our Python
dependencies and local virtual environments. When it comes to managing the
Python dependencies, there are a couple of things to bear in mind.
For situations where you manually manipulate the `pyproject.toml` file, you
should use the `make py-lock` command to sync the `poetry.lock` file. This will
ensure that you don't inadvertently bring in other transitive dependency updates
that have not been fully tested with the project yet.
If you're just trying to update a dependency to a newer (or the latest) version,
you should let Poetry take care of that for you by running the following:
```sh
poetry update <dependency> [<dependency>...]
```
You can specify more than one dependency together. With this command, Poetry
will do the following for you:
- Find the latest compatible version(s) of the specified dependency/dependencies
- Install the new versions
- Update and sync the `poetry.lock` file
In either situation, once you are finished and have verified the dependency
changes are working, please be sure to commit both the `pyproject.toml` and
`poetry.lock` files.
### Keeping the notification-utils dependency up-to-date
The `notifications-utils` dependency references the other repository we have at
https://github.com/GSA/notifications-utils - this dependency requires a bit of
extra legwork to ensure it stays up-to-date.
Whenever a PR is merged in the `notifications-utils` repository, we need to make
sure the changes are pulled in here and committed to this repository as well.
You can do this by going through these steps:
- Make sure your local `main` branch is up-to-date
- Create a new branch to work in
- Run `make update-utils`
- Commit the updated `poetry.lock` file and push the changes
- Make a new PR with the change
- Have the PR get reviewed and merged
## Known Installation Issues
### Python Installation Errors

View File

@@ -23,12 +23,6 @@ from flask_wtf import CSRFProtect
from flask_wtf.csrf import CSRFError
from itsdangerous import BadSignature
from notifications_python_client.errors import HTTPError
from notifications_utils import logging, request_helper
from notifications_utils.formatters import (
formatted_list,
get_lines_with_normalised_whitespace,
)
from notifications_utils.recipients import format_phone_number_human_readable
from werkzeug.exceptions import HTTPException as WerkzeugHTTPException
from werkzeug.exceptions import abort
from werkzeug.local import LocalProxy
@@ -52,8 +46,7 @@ from app.formatters import (
format_datetime_human,
format_datetime_normal,
format_datetime_relative,
format_datetime_short,
format_datetime_short_america,
format_datetime_table,
format_day_of_week,
format_delta,
format_delta_days,
@@ -114,6 +107,12 @@ from app.notify_client.upload_api_client import upload_api_client
from app.notify_client.user_api_client import user_api_client
from app.url_converters import SimpleDateTypeConverter, TemplateTypeConverter
from app.utils.govuk_frontend_jinja.flask_ext import init_govuk_frontend
from notifications_utils import logging, request_helper
from notifications_utils.formatters import (
formatted_list,
get_lines_with_normalised_whitespace,
)
from notifications_utils.recipients import format_phone_number_human_readable
login_manager = LoginManager()
csrf = CSRFProtect()
@@ -552,8 +551,7 @@ def add_template_filters(application):
format_datetime,
format_datetime_24h,
format_datetime_normal,
format_datetime_short,
format_datetime_short_america,
format_datetime_table,
valid_phone_number,
linkable_name,
format_date,

View File

@@ -22,7 +22,7 @@
<link rel="stylesheet" media="screen" href="/static/stylesheets/main.css?d077f86473501ab244de0b30600536ee" />
<link rel="stylesheet" media="print" href="/static/stylesheets/print.css?28010888ca5719dc83d7c2c80f6ed2b2" />
<meta property="og:image" content="/static/images/usa-opengraph-image.png">
<meta property="og:image" content="/static/images/notify-og-image.png">
</head>
<body class="usa-template__body">
@@ -107,4 +107,4 @@
</body>
</html>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 283 B

After

Width:  |  Height:  |  Size: 994 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 384 B

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@@ -0,0 +1,7 @@
<svg width="187" height="230" viewBox="0 0 187 230" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M89.1324 230V203.125H57.936C60.0752 226.499 79.7735 230 89.1324 230Z" fill="#2491FF"/>
<path d="M89.1324 230V203.125H121.22C119.02 226.499 98.7587 230 89.1324 230Z" fill="#005EA2"/>
<path d="M71.4869 19.5185C71.4869 3.35574 82.3456 0 89.1324 0V194.166H0V174.861L22.1703 153.759V87.3122C22.1703 47.8033 53.8415 24.1578 71.4869 19.5185Z" fill="#2491FF"/>
<path d="M89.1324 0C95.9192 0 106.778 3.35574 106.778 19.5185C112.193 20.9422 118.928 24.1557 125.664 29.0681C118.405 36.2889 113.911 46.2873 113.911 57.3353C113.911 79.3543 131.761 97.2043 153.78 97.2043C154.557 97.2043 155.293 97.2481 156.058 97.2043L156.094 153.759L178.265 174.861V194.166H89.1324V0Z" fill="#005EA2"/>
<path d="M186.435 57.1535C186.435 74.9784 171.985 89.4283 154.16 89.4283C136.335 89.4283 121.885 74.9784 121.885 57.1535C121.885 39.3286 136.335 24.8786 154.16 24.8786C171.985 24.8786 186.435 39.3286 186.435 57.1535Z" fill="#D54309"/>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@@ -0,0 +1,17 @@
<svg width="1596" height="400" viewBox="0 0 1596 400" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M155.013 400V353.26H100.758C104.479 393.912 138.736 400 155.013 400Z" fill="#58B4FF"/>
<path d="M155.013 400V353.26H210.818C206.991 393.912 171.754 400 155.013 400Z" fill="#0076D6"/>
<path d="M124.325 33.9452C124.325 5.83608 143.21 0 155.013 0V337.68H0V304.105L38.5571 267.407V151.847C38.5571 83.1361 93.6374 42.0136 124.325 33.9452Z" fill="#58B4FF"/>
<path d="M155.013 0C166.816 0 185.701 5.83608 185.701 33.9452C195.118 36.4212 206.832 42.0098 218.547 50.5533C205.922 63.1111 198.106 80.4997 198.106 99.7136C198.106 138.008 229.15 169.051 267.444 169.051C268.795 169.051 270.074 169.127 271.406 169.051L271.469 267.407L310.026 304.105V337.68H155.013V0Z" fill="#0076D6"/>
<path d="M324.234 99.3973C324.234 130.397 299.104 155.528 268.104 155.528C237.104 155.528 211.974 130.397 211.974 99.3973C211.974 68.3975 237.104 43.2672 268.104 43.2672C299.104 43.2672 324.234 68.3975 324.234 99.3973Z" fill="#D54309"/>
<path d="M416.684 290.469C414.747 290.469 413.25 289.94 412.194 288.884C411.137 287.651 410.609 286.154 410.609 284.393V111.909C410.609 109.972 411.137 108.475 412.194 107.418C413.25 106.186 414.747 105.569 416.684 105.569H428.835C430.948 105.569 432.532 106.098 433.589 107.154C434.646 108.035 435.262 108.739 435.438 109.267L523.133 243.98V111.909C523.133 109.972 523.662 108.475 524.718 107.418C525.775 106.186 527.271 105.569 529.208 105.569H541.887C543.824 105.569 545.321 106.186 546.378 107.418C547.61 108.475 548.227 109.972 548.227 111.909V284.129C548.227 285.89 547.61 287.387 546.378 288.62C545.321 289.852 543.912 290.469 542.151 290.469H529.473C527.536 290.469 526.039 289.94 524.982 288.884C524.102 287.827 523.485 287.123 523.133 286.771L435.702 153.379V284.393C435.702 286.154 435.086 287.651 433.853 288.884C432.797 289.94 431.3 290.469 429.363 290.469H416.684Z" fill="#EDEFF0"/>
<path d="M636.275 293.11C622.892 293.11 611.71 290.557 602.729 285.45C593.748 280.343 586.88 273.299 582.126 264.319C577.371 255.162 574.73 244.772 574.201 233.15C574.025 230.156 573.937 226.37 573.937 221.792C573.937 217.037 574.025 213.251 574.201 210.434C574.73 198.635 577.371 188.246 582.126 179.265C587.056 170.284 594.012 163.24 602.993 158.133C611.974 153.027 623.068 150.473 636.275 150.473C649.482 150.473 660.576 153.027 669.557 158.133C678.537 163.24 685.405 170.284 690.16 179.265C695.09 188.246 697.82 198.635 698.348 210.434C698.524 213.251 698.612 217.037 698.612 221.792C698.612 226.37 698.524 230.156 698.348 233.15C697.82 244.772 695.178 255.162 690.424 264.319C685.669 273.299 678.802 280.343 669.821 285.45C660.84 290.557 649.658 293.11 636.275 293.11ZM636.275 272.771C647.193 272.771 655.909 269.337 662.425 262.47C668.94 255.426 672.462 245.212 672.99 231.829C673.167 229.188 673.255 225.842 673.255 221.792C673.255 217.742 673.167 214.396 672.99 211.754C672.462 198.371 668.94 188.246 662.425 181.378C655.909 174.334 647.193 170.812 636.275 170.812C625.357 170.812 616.552 174.334 609.861 181.378C603.345 188.246 599.911 198.371 599.559 211.754C599.383 214.396 599.295 217.742 599.295 221.792C599.295 225.842 599.383 229.188 599.559 231.829C599.911 245.212 603.345 255.426 609.861 262.47C616.552 269.337 625.357 272.771 636.275 272.771Z" fill="#EDEFF0"/>
<path d="M781.377 290.469C771.516 290.469 763.504 288.62 757.341 284.922C751.177 281.048 746.687 275.677 743.869 268.809C741.052 261.765 739.643 253.489 739.643 243.98V174.246H719.04C717.279 174.246 715.782 173.718 714.549 172.661C713.493 171.429 712.965 169.932 712.965 168.171V159.19C712.965 157.429 713.493 156.02 714.549 154.964C715.782 153.731 717.279 153.115 719.04 153.115H739.643V109.003C739.643 107.242 740.171 105.833 741.228 104.777C742.461 103.544 743.957 102.928 745.718 102.928H758.133C759.894 102.928 761.303 103.544 762.359 104.777C763.592 105.833 764.208 107.242 764.208 109.003V153.115H796.962C798.723 153.115 800.132 153.731 801.188 154.964C802.421 156.02 803.037 157.429 803.037 159.19V168.171C803.037 169.932 802.421 171.429 801.188 172.661C800.132 173.718 798.723 174.246 796.962 174.246H764.208V242.131C764.208 250.407 765.617 256.923 768.434 261.677C771.252 266.432 776.271 268.809 783.491 268.809H799.603C801.364 268.809 802.773 269.425 803.83 270.658C805.062 271.715 805.678 273.123 805.678 274.884V284.393C805.678 286.154 805.062 287.651 803.83 288.884C802.773 289.94 801.364 290.469 799.603 290.469H781.377Z" fill="#EDEFF0"/>
<path d="M834.371 290.469C832.61 290.469 831.113 289.94 829.88 288.884C828.824 287.651 828.296 286.154 828.296 284.393V159.19C828.296 157.429 828.824 156.02 829.88 154.964C831.113 153.731 832.61 153.115 834.371 153.115H847.05C848.811 153.115 850.219 153.731 851.276 154.964C852.333 156.02 852.861 157.429 852.861 159.19V284.393C852.861 286.154 852.333 287.651 851.276 288.884C850.219 289.94 848.811 290.469 847.05 290.469H834.371ZM832.258 127.493C830.497 127.493 829 126.965 827.767 125.908C826.711 124.675 826.182 123.179 826.182 121.418V107.154C826.182 105.393 826.711 103.984 827.767 102.928C829 101.695 830.497 101.079 832.258 101.079H848.899C850.66 101.079 852.068 101.695 853.125 102.928C854.358 103.984 854.974 105.393 854.974 107.154V121.418C854.974 123.179 854.358 124.675 853.125 125.908C852.068 126.965 850.66 127.493 848.899 127.493H832.258Z" fill="#EDEFF0"/>
<path d="M912.078 290.469C910.317 290.469 908.82 289.94 907.588 288.884C906.531 287.651 906.003 286.154 906.003 284.393V174.246H883.551C881.79 174.246 880.293 173.718 879.06 172.661C878.004 171.429 877.476 169.932 877.476 168.171V159.19C877.476 157.429 878.004 156.02 879.06 154.964C880.293 153.731 881.79 153.115 883.551 153.115H906.003V139.908C906.003 130.927 907.5 123.091 910.493 116.399C913.487 109.531 918.242 104.249 924.757 100.551C931.449 96.8525 940.341 95.0036 951.435 95.0036H967.284C969.045 95.0036 970.454 95.6199 971.51 96.8525C972.567 97.9091 973.095 99.3179 973.095 101.079V110.06C973.095 111.821 972.567 113.317 971.51 114.55C970.454 115.607 969.045 116.135 967.284 116.135H951.964C943.687 116.135 938.052 118.336 935.059 122.738C932.065 126.965 930.568 133.128 930.568 141.228V153.115H964.642C966.403 153.115 967.812 153.731 968.869 154.964C969.925 156.02 970.454 157.429 970.454 159.19V168.171C970.454 169.932 969.925 171.429 968.869 172.661C967.812 173.718 966.403 174.246 964.642 174.246H930.568V284.393C930.568 286.154 929.952 287.651 928.719 288.884C927.663 289.94 926.254 290.469 924.493 290.469H912.078Z" fill="#EDEFF0"/>
<path d="M1017.94 340.656C1016.53 340.656 1015.3 340.127 1014.24 339.071C1013.18 338.014 1012.65 336.782 1012.65 335.373C1012.65 334.668 1012.74 333.964 1012.92 333.26C1013.09 332.555 1013.45 331.675 1013.98 330.618L1034.31 282.28L983.863 163.152C982.982 161.039 982.542 159.542 982.542 158.662C982.542 157.077 983.071 155.756 984.127 154.7C985.184 153.643 986.504 153.115 988.089 153.115H1001.03C1002.79 153.115 1004.2 153.555 1005.26 154.435C1006.31 155.316 1007.02 156.373 1007.37 157.605L1047.52 253.753L1088.73 157.605C1089.26 156.373 1089.96 155.316 1090.84 154.435C1091.9 153.555 1093.39 153.115 1095.33 153.115H1107.75C1109.33 153.115 1110.65 153.643 1111.71 154.7C1112.76 155.756 1113.29 156.989 1113.29 158.398C1113.29 159.278 1112.85 160.863 1111.97 163.152L1036.96 336.165C1036.43 337.398 1035.63 338.454 1034.58 339.335C1033.7 340.215 1032.29 340.656 1030.35 340.656H1017.94Z" fill="#EDEFF0"/>
<path d="M1128.65 290.469C1126.89 290.469 1125.39 289.94 1124.16 288.884C1123.1 287.651 1122.57 286.154 1122.57 284.393V265.375C1122.57 263.614 1123.1 262.205 1124.16 261.149C1125.39 259.916 1126.89 259.3 1128.65 259.3H1147.67C1149.43 259.3 1150.84 259.916 1151.89 261.149C1153.13 262.205 1153.74 263.614 1153.74 265.375V284.393C1153.74 286.154 1153.13 287.651 1151.89 288.884C1150.84 289.94 1149.43 290.469 1147.67 290.469H1128.65Z" fill="#EDEFF0"/>
<path d="M1244.66 348.58C1232.68 348.58 1222.82 346.995 1215.07 343.825C1207.5 340.656 1201.51 336.782 1197.11 332.203C1192.89 327.801 1189.8 323.31 1187.87 318.732C1186.11 314.329 1185.14 310.808 1184.96 308.166C1184.79 306.405 1185.31 304.908 1186.55 303.676C1187.96 302.443 1189.45 301.827 1191.04 301.827H1203.72C1205.3 301.827 1206.62 302.179 1207.68 302.883C1208.73 303.588 1209.61 305.084 1210.32 307.374C1211.38 310.015 1212.96 312.921 1215.07 316.09C1217.19 319.436 1220.44 322.254 1224.85 324.543C1229.25 327.008 1235.41 328.241 1243.34 328.241C1251.61 328.241 1258.57 327.096 1264.2 324.807C1269.84 322.694 1274.07 318.82 1276.88 313.185C1279.88 307.726 1281.37 300.154 1281.37 290.469V272.507C1277.15 277.966 1271.6 282.544 1264.73 286.242C1257.86 289.764 1249.24 291.525 1238.85 291.525C1228.99 291.525 1220.53 289.852 1213.49 286.507C1206.45 282.985 1200.63 278.318 1196.06 272.507C1191.65 266.52 1188.31 259.74 1186.02 252.168C1183.91 244.596 1182.67 236.672 1182.32 228.395C1182.14 223.465 1182.14 218.622 1182.32 213.867C1182.67 205.591 1183.91 197.667 1186.02 190.095C1188.31 182.523 1191.65 175.743 1196.06 169.756C1200.63 163.769 1206.45 159.102 1213.49 155.756C1220.53 152.234 1228.99 150.473 1238.85 150.473C1249.41 150.473 1258.13 152.498 1265 156.549C1272.04 160.423 1277.76 165.177 1282.17 170.812V159.454C1282.17 157.693 1282.69 156.196 1283.75 154.964C1284.98 153.731 1286.48 153.115 1288.24 153.115H1300.39C1302.15 153.115 1303.65 153.731 1304.88 154.964C1306.11 156.196 1306.73 157.693 1306.73 159.454V287.563C1306.73 299.361 1304.71 309.839 1300.66 318.996C1296.78 328.153 1290.27 335.373 1281.11 340.656C1272.13 345.938 1259.98 348.58 1244.66 348.58ZM1244.13 270.394C1252.93 270.394 1259.98 268.369 1265.26 264.319C1270.72 260.268 1274.68 255.162 1277.15 248.998C1279.79 242.835 1281.2 236.584 1281.37 230.244C1281.55 227.779 1281.64 224.785 1281.64 221.263C1281.64 217.565 1281.55 214.484 1281.37 212.018C1281.2 205.679 1279.79 199.428 1277.15 193.264C1274.68 187.101 1270.72 181.994 1265.26 177.944C1259.98 173.894 1252.93 171.869 1244.13 171.869C1235.32 171.869 1228.28 173.894 1223 177.944C1217.72 181.818 1213.93 187.013 1211.64 193.529C1209.35 199.868 1208.03 206.736 1207.68 214.132C1207.5 218.71 1207.5 223.377 1207.68 228.131C1208.03 235.527 1209.35 242.483 1211.64 248.998C1213.93 255.338 1217.72 260.533 1223 264.583C1228.28 268.457 1235.32 270.394 1244.13 270.394Z" fill="#EDEFF0"/>
<path d="M1394.34 293.11C1380.96 293.11 1369.78 290.557 1360.8 285.45C1351.81 280.343 1344.95 273.299 1340.19 264.319C1335.44 255.162 1332.8 244.772 1332.27 233.15C1332.09 230.156 1332 226.37 1332 221.792C1332 217.037 1332.09 213.251 1332.27 210.434C1332.8 198.635 1335.44 188.246 1340.19 179.265C1345.12 170.284 1352.08 163.24 1361.06 158.133C1370.04 153.027 1381.13 150.473 1394.34 150.473C1407.55 150.473 1418.64 153.027 1427.62 158.133C1436.6 163.24 1443.47 170.284 1448.23 179.265C1453.16 188.246 1455.89 198.635 1456.42 210.434C1456.59 213.251 1456.68 217.037 1456.68 221.792C1456.68 226.37 1456.59 230.156 1456.42 233.15C1455.89 244.772 1453.25 255.162 1448.49 264.319C1443.74 273.299 1436.87 280.343 1427.89 285.45C1418.91 290.557 1407.72 293.11 1394.34 293.11ZM1394.34 272.771C1405.26 272.771 1413.98 269.337 1420.49 262.47C1427.01 255.426 1430.53 245.212 1431.06 231.829C1431.23 229.188 1431.32 225.842 1431.32 221.792C1431.32 217.742 1431.23 214.396 1431.06 211.754C1430.53 198.371 1427.01 188.246 1420.49 181.378C1413.98 174.334 1405.26 170.812 1394.34 170.812C1383.42 170.812 1374.62 174.334 1367.93 181.378C1361.41 188.246 1357.98 198.371 1357.63 211.754C1357.45 214.396 1357.36 217.742 1357.36 221.792C1357.36 225.842 1357.45 229.188 1357.63 231.829C1357.98 245.212 1361.41 255.426 1367.93 262.47C1374.62 269.337 1383.42 272.771 1394.34 272.771Z" fill="#EDEFF0"/>
<path d="M1524.74 290.469C1522.1 290.469 1519.98 289.852 1518.4 288.62C1516.99 287.387 1515.85 285.802 1514.97 283.865L1466.1 161.303C1465.75 160.423 1465.57 159.542 1465.57 158.662C1465.57 157.077 1466.1 155.756 1467.16 154.7C1468.21 153.643 1469.53 153.115 1471.12 153.115H1483.8C1485.73 153.115 1487.23 153.643 1488.29 154.7C1489.34 155.756 1489.96 156.725 1490.14 157.605L1530.55 261.677L1570.7 157.605C1571.05 156.725 1571.67 155.756 1572.55 154.7C1573.61 153.643 1575.1 153.115 1577.04 153.115H1589.98C1591.39 153.115 1592.62 153.643 1593.68 154.7C1594.91 155.756 1595.53 157.077 1595.53 158.662C1595.53 159.542 1595.35 160.423 1595 161.303L1545.87 283.865C1545.17 285.802 1544.02 287.387 1542.44 288.62C1541.03 289.852 1538.91 290.469 1536.1 290.469H1524.74Z" fill="#EDEFF0"/>
</svg>

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View File

@@ -0,0 +1,17 @@
<svg width="1596" height="400" viewBox="0 0 1596 400" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M155.013 400V353.26H100.759C104.479 393.912 138.737 400 155.013 400Z" fill="#2491FF"/>
<path d="M155.013 400V353.26H210.818C206.992 393.912 171.755 400 155.013 400Z" fill="#005EA2"/>
<path d="M124.326 33.9452C124.326 5.83608 143.21 0 155.014 0V337.68H0V304.105L38.5573 267.407V151.847C38.5573 83.1361 93.6378 42.0136 124.326 33.9452Z" fill="#2491FF"/>
<path d="M155.014 0C166.817 0 185.702 5.83608 185.702 33.9452C195.119 36.4212 206.833 42.0098 218.548 50.5533C205.923 63.1111 198.107 80.4997 198.107 99.7136C198.107 138.008 229.151 169.051 267.445 169.051C268.796 169.051 270.075 169.127 271.407 169.051L271.47 267.407L310.027 304.105V337.68H155.014V0Z" fill="#005EA2"/>
<path d="M324.236 99.3973C324.236 130.397 299.105 155.528 268.105 155.528C237.105 155.528 211.975 130.397 211.975 99.3973C211.975 68.3975 237.105 43.2672 268.105 43.2672C299.105 43.2672 324.236 68.3975 324.236 99.3973Z" fill="#D54309"/>
<path d="M416.686 290.469C414.749 290.469 413.252 289.94 412.195 288.884C411.139 287.651 410.61 286.154 410.61 284.393V111.909C410.61 109.972 411.139 108.475 412.195 107.418C413.252 106.186 414.749 105.569 416.686 105.569H428.836C430.95 105.569 432.534 106.098 433.591 107.154C434.647 108.035 435.264 108.739 435.44 109.267L523.135 243.98V111.909C523.135 109.972 523.664 108.475 524.72 107.418C525.777 106.186 527.274 105.569 529.211 105.569H541.89C543.827 105.569 545.323 106.186 546.38 107.418C547.613 108.475 548.229 109.972 548.229 111.909V284.129C548.229 285.89 547.613 287.387 546.38 288.62C545.323 289.852 543.915 290.469 542.154 290.469H529.475C527.538 290.469 526.041 289.94 524.984 288.884C524.104 287.827 523.488 287.123 523.135 286.771L435.704 153.379V284.393C435.704 286.154 435.088 287.651 433.855 288.884C432.798 289.94 431.302 290.469 429.365 290.469H416.686Z" fill="#2D2E2F"/>
<path d="M636.277 293.11C622.894 293.11 611.712 290.557 602.731 285.45C593.75 280.343 586.883 273.299 582.128 264.319C577.373 255.162 574.732 244.772 574.204 233.15C574.028 230.156 573.94 226.37 573.94 221.792C573.94 217.037 574.028 213.251 574.204 210.434C574.732 198.635 577.373 188.246 582.128 179.265C587.059 170.284 594.014 163.24 602.995 158.133C611.976 153.027 623.07 150.473 636.277 150.473C649.485 150.473 660.579 153.027 669.559 158.133C678.54 163.24 685.408 170.284 690.163 179.265C695.093 188.246 697.823 198.635 698.351 210.434C698.527 213.251 698.615 217.037 698.615 221.792C698.615 226.37 698.527 230.156 698.351 233.15C697.823 244.772 695.181 255.162 690.427 264.319C685.672 273.299 678.804 280.343 669.824 285.45C660.843 290.557 649.661 293.11 636.277 293.11ZM636.277 272.771C647.195 272.771 655.912 269.337 662.428 262.47C668.943 255.426 672.465 245.212 672.993 231.829C673.169 229.188 673.257 225.842 673.257 221.792C673.257 217.742 673.169 214.396 672.993 211.754C672.465 198.371 668.943 188.246 662.428 181.378C655.912 174.334 647.195 170.812 636.277 170.812C625.359 170.812 616.555 174.334 609.863 181.378C603.348 188.246 599.914 198.371 599.562 211.754C599.385 214.396 599.297 217.742 599.297 221.792C599.297 225.842 599.385 229.188 599.562 231.829C599.914 245.212 603.348 255.426 609.863 262.47C616.555 269.337 625.359 272.771 636.277 272.771Z" fill="#2D2E2F"/>
<path d="M781.381 290.469C771.519 290.469 763.507 288.62 757.344 284.922C751.18 281.048 746.69 275.677 743.872 268.809C741.055 261.765 739.646 253.489 739.646 243.98V174.246H719.043C717.282 174.246 715.785 173.718 714.552 172.661C713.496 171.429 712.968 169.932 712.968 168.171V159.19C712.968 157.429 713.496 156.02 714.552 154.964C715.785 153.731 717.282 153.115 719.043 153.115H739.646V109.003C739.646 107.242 740.174 105.833 741.231 104.777C742.464 103.544 743.96 102.928 745.721 102.928H758.136C759.897 102.928 761.306 103.544 762.362 104.777C763.595 105.833 764.211 107.242 764.211 109.003V153.115H796.965C798.726 153.115 800.135 153.731 801.191 154.964C802.424 156.02 803.04 157.429 803.04 159.19V168.171C803.04 169.932 802.424 171.429 801.191 172.661C800.135 173.718 798.726 174.246 796.965 174.246H764.211V242.131C764.211 250.407 765.62 256.923 768.438 261.677C771.255 266.432 776.274 268.809 783.494 268.809H799.607C801.368 268.809 802.776 269.425 803.833 270.658C805.066 271.715 805.682 273.123 805.682 274.884V284.393C805.682 286.154 805.066 287.651 803.833 288.884C802.776 289.94 801.368 290.469 799.607 290.469H781.381Z" fill="#2D2E2F"/>
<path d="M834.374 290.469C832.613 290.469 831.117 289.94 829.884 288.884C828.827 287.651 828.299 286.154 828.299 284.393V159.19C828.299 157.429 828.827 156.02 829.884 154.964C831.117 153.731 832.613 153.115 834.374 153.115H847.053C848.814 153.115 850.223 153.731 851.28 154.964C852.336 156.02 852.864 157.429 852.864 159.19V284.393C852.864 286.154 852.336 287.651 851.28 288.884C850.223 289.94 848.814 290.469 847.053 290.469H834.374ZM832.261 127.493C830.5 127.493 829.004 126.965 827.771 125.908C826.714 124.675 826.186 123.179 826.186 121.418V107.154C826.186 105.393 826.714 103.984 827.771 102.928C829.004 101.695 830.5 101.079 832.261 101.079H848.902C850.663 101.079 852.072 101.695 853.129 102.928C854.361 103.984 854.978 105.393 854.978 107.154V121.418C854.978 123.179 854.361 124.675 853.129 125.908C852.072 126.965 850.663 127.493 848.902 127.493H832.261Z" fill="#2D2E2F"/>
<path d="M912.082 290.469C910.321 290.469 908.824 289.94 907.592 288.884C906.535 287.651 906.007 286.154 906.007 284.393V174.246H883.555C881.794 174.246 880.297 173.718 879.064 172.661C878.008 171.429 877.479 169.932 877.479 168.171V159.19C877.479 157.429 878.008 156.02 879.064 154.964C880.297 153.731 881.794 153.115 883.555 153.115H906.007V139.908C906.007 130.927 907.504 123.091 910.497 116.399C913.491 109.531 918.245 104.249 924.761 100.551C931.453 96.8525 940.345 95.0035 951.439 95.0035H967.288C969.049 95.0035 970.458 95.6199 971.514 96.8525C972.571 97.9091 973.099 99.3179 973.099 101.079V110.06C973.099 111.821 972.571 113.317 971.514 114.55C970.458 115.607 969.049 116.135 967.288 116.135H951.968C943.691 116.135 938.056 118.336 935.062 122.738C932.069 126.965 930.572 133.128 930.572 141.228V153.115H964.646C966.407 153.115 967.816 153.731 968.873 154.964C969.929 156.02 970.458 157.429 970.458 159.19V168.171C970.458 169.932 969.929 171.429 968.873 172.661C967.816 173.718 966.407 174.246 964.646 174.246H930.572V284.393C930.572 286.154 929.956 287.651 928.723 288.884C927.667 289.94 926.258 290.469 924.497 290.469H912.082Z" fill="#2D2E2F"/>
<path d="M1017.94 340.656C1016.53 340.656 1015.3 340.127 1014.24 339.071C1013.19 338.014 1012.66 336.782 1012.66 335.373C1012.66 334.668 1012.75 333.964 1012.92 333.26C1013.1 332.555 1013.45 331.675 1013.98 330.618L1034.32 282.28L983.867 163.152C982.987 161.039 982.546 159.542 982.546 158.662C982.546 157.077 983.075 155.756 984.131 154.7C985.188 153.643 986.509 153.115 988.093 153.115H1001.04C1002.8 153.115 1004.21 153.555 1005.26 154.435C1006.32 155.316 1007.02 156.373 1007.38 157.605L1047.53 253.753L1088.73 157.605C1089.26 156.373 1089.96 155.316 1090.84 154.435C1091.9 153.555 1093.4 153.115 1095.34 153.115H1107.75C1109.33 153.115 1110.66 153.643 1111.71 154.7C1112.77 155.756 1113.3 156.989 1113.3 158.398C1113.3 159.278 1112.86 160.863 1111.98 163.152L1036.96 336.165C1036.43 337.398 1035.64 338.454 1034.58 339.335C1033.7 340.215 1032.29 340.656 1030.36 340.656H1017.94Z" fill="#2D2E2F"/>
<path d="M1128.65 290.469C1126.89 290.469 1125.4 289.94 1124.16 288.884C1123.11 287.651 1122.58 286.154 1122.58 284.393V265.375C1122.58 263.614 1123.11 262.205 1124.16 261.149C1125.4 259.916 1126.89 259.3 1128.65 259.3H1147.67C1149.43 259.3 1150.84 259.916 1151.9 261.149C1153.13 262.205 1153.75 263.614 1153.75 265.375V284.393C1153.75 286.154 1153.13 287.651 1151.9 288.884C1150.84 289.94 1149.43 290.469 1147.67 290.469H1128.65Z" fill="#2D2E2F"/>
<path d="M1244.66 348.58C1232.69 348.58 1222.83 346.995 1215.08 343.825C1207.51 340.656 1201.52 336.782 1197.12 332.203C1192.89 327.801 1189.81 323.31 1187.87 318.732C1186.11 314.329 1185.14 310.808 1184.97 308.166C1184.79 306.405 1185.32 304.908 1186.55 303.676C1187.96 302.443 1189.46 301.827 1191.04 301.827H1203.72C1205.31 301.827 1206.63 302.179 1207.68 302.883C1208.74 303.588 1209.62 305.084 1210.32 307.374C1211.38 310.015 1212.97 312.921 1215.08 316.09C1217.19 319.436 1220.45 322.254 1224.85 324.543C1229.25 327.008 1235.42 328.241 1243.34 328.241C1251.62 328.241 1258.57 327.096 1264.21 324.807C1269.84 322.694 1274.07 318.82 1276.89 313.185C1279.88 307.726 1281.38 300.154 1281.38 290.469V272.507C1277.15 277.966 1271.61 282.544 1264.74 286.242C1257.87 289.764 1249.24 291.525 1238.85 291.525C1228.99 291.525 1220.54 289.852 1213.49 286.507C1206.45 282.985 1200.64 278.318 1196.06 272.507C1191.66 266.52 1188.31 259.74 1186.02 252.168C1183.91 244.596 1182.68 236.672 1182.33 228.395C1182.15 223.465 1182.15 218.622 1182.33 213.867C1182.68 205.591 1183.91 197.667 1186.02 190.095C1188.31 182.523 1191.66 175.743 1196.06 169.756C1200.64 163.769 1206.45 159.102 1213.49 155.756C1220.54 152.234 1228.99 150.473 1238.85 150.473C1249.42 150.473 1258.13 152.498 1265 156.549C1272.05 160.423 1277.77 165.177 1282.17 170.812V159.454C1282.17 157.693 1282.7 156.196 1283.76 154.964C1284.99 153.731 1286.49 153.115 1288.25 153.115H1300.4C1302.16 153.115 1303.65 153.731 1304.89 154.964C1306.12 156.196 1306.74 157.693 1306.74 159.454V287.563C1306.74 299.361 1304.71 309.839 1300.66 318.996C1296.79 328.153 1290.27 335.373 1281.11 340.656C1272.13 345.938 1259.98 348.58 1244.66 348.58ZM1244.13 270.394C1252.94 270.394 1259.98 268.369 1265.27 264.319C1270.73 260.268 1274.69 255.162 1277.15 248.998C1279.79 242.835 1281.2 236.584 1281.38 230.244C1281.55 227.779 1281.64 224.785 1281.64 221.263C1281.64 217.565 1281.55 214.484 1281.38 212.018C1281.2 205.679 1279.79 199.428 1277.15 193.264C1274.69 187.101 1270.73 181.994 1265.27 177.944C1259.98 173.894 1252.94 171.869 1244.13 171.869C1235.33 171.869 1228.29 173.894 1223 177.944C1217.72 181.818 1213.93 187.013 1211.65 193.529C1209.36 199.868 1208.04 206.736 1207.68 214.132C1207.51 218.71 1207.51 223.377 1207.68 228.131C1208.04 235.527 1209.36 242.483 1211.65 248.998C1213.93 255.338 1217.72 260.533 1223 264.583C1228.29 268.457 1235.33 270.394 1244.13 270.394Z" fill="#2D2E2F"/>
<path d="M1394.35 293.11C1380.96 293.11 1369.78 290.557 1360.8 285.45C1351.82 280.343 1344.95 273.299 1340.2 264.319C1335.44 255.162 1332.8 244.772 1332.27 233.15C1332.1 230.156 1332.01 226.37 1332.01 221.792C1332.01 217.037 1332.1 213.251 1332.27 210.434C1332.8 198.635 1335.44 188.246 1340.2 179.265C1345.13 170.284 1352.08 163.24 1361.07 158.133C1370.05 153.027 1381.14 150.473 1394.35 150.473C1407.55 150.473 1418.65 153.027 1427.63 158.133C1436.61 163.24 1443.48 170.284 1448.23 179.265C1453.16 188.246 1455.89 198.635 1456.42 210.434C1456.6 213.251 1456.69 217.037 1456.69 221.792C1456.69 226.37 1456.6 230.156 1456.42 233.15C1455.89 244.772 1453.25 255.162 1448.5 264.319C1443.74 273.299 1436.87 280.343 1427.89 285.45C1418.91 290.557 1407.73 293.11 1394.35 293.11ZM1394.35 272.771C1405.27 272.771 1413.98 269.337 1420.5 262.47C1427.01 255.426 1430.53 245.212 1431.06 231.829C1431.24 229.188 1431.33 225.842 1431.33 221.792C1431.33 217.742 1431.24 214.396 1431.06 211.754C1430.53 198.371 1427.01 188.246 1420.5 181.378C1413.98 174.334 1405.27 170.812 1394.35 170.812C1383.43 170.812 1374.62 174.334 1367.93 181.378C1361.42 188.246 1357.98 198.371 1357.63 211.754C1357.46 214.396 1357.37 217.742 1357.37 221.792C1357.37 225.842 1357.46 229.188 1357.63 231.829C1357.98 245.212 1361.42 255.426 1367.93 262.47C1374.62 269.337 1383.43 272.771 1394.35 272.771Z" fill="#2D2E2F"/>
<path d="M1524.75 290.469C1522.1 290.469 1519.99 289.852 1518.41 288.62C1517 287.387 1515.85 285.802 1514.97 283.865L1466.11 161.303C1465.75 160.423 1465.58 159.542 1465.58 158.662C1465.58 157.077 1466.11 155.756 1467.16 154.7C1468.22 153.643 1469.54 153.115 1471.12 153.115H1483.8C1485.74 153.115 1487.24 153.643 1488.29 154.7C1489.35 155.756 1489.97 156.725 1490.14 157.605L1530.56 261.677L1570.71 157.605C1571.06 156.725 1571.67 155.756 1572.56 154.7C1573.61 153.643 1575.11 153.115 1577.05 153.115H1589.99C1591.4 153.115 1592.63 153.643 1593.69 154.7C1594.92 155.756 1595.54 157.077 1595.54 158.662C1595.54 159.542 1595.36 160.423 1595.01 161.303L1545.88 283.865C1545.17 285.802 1544.03 287.387 1542.44 288.62C1541.03 289.852 1538.92 290.469 1536.1 290.469H1524.75Z" fill="#2D2E2F"/>
</svg>

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 84 KiB

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="260.000000pt" height="260.000000pt" viewBox="0 0 260.000000 260.000000"
preserveAspectRatio="xMidYMid meet">
<metadata>
Created by potrace 1.14, written by Peter Selinger 2001-2017
</metadata>
<g transform="translate(0.000000,260.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M1177 2586 c-67 -29 -117 -107 -117 -183 0 -12 -15 -24 -48 -37 -175
-69 -342 -229 -426 -406 -74 -156 -77 -183 -78 -657 l-1 -423 -123 -116 -124
-117 0 -109 0 -108 996 2 997 3 -1 105 -1 105 -123 117 -123 118 -1 310 c0
170 -3 313 -7 316 -4 4 -26 8 -50 9 -105 6 -216 59 -292 140 -152 161 -161
417 -22 583 35 42 31 48 -71 97 -42 20 -84 41 -92 45 -8 4 -18 28 -22 54 -14
100 -80 158 -182 162 -34 1 -74 -4 -89 -10z"/>
<path d="M1854 2297 c-64 -28 -95 -48 -134 -89 -65 -68 -99 -163 -94 -265 11
-271 317 -431 547 -286 163 103 215 311 122 482 -35 64 -91 114 -165 149 -77
37 -202 41 -276 9z"/>
<path d="M910 282 c0 -32 32 -116 59 -154 114 -161 427 -170 566 -16 42 47 75
119 75 165 l0 23 -350 0 c-332 0 -350 -1 -350 -18z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -1 +1,19 @@
{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}
{
"name": "",
"short_name": "",
"icons": [
{
"src": "/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/android-chrome-256x256.png",
"sizes": "256x256",
"type": "image/png"
}
],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone"
}

View File

@@ -0,0 +1,41 @@
(function (window) {
// Set the target date (10 days before March 15th, 2024)
const targetDate = new Date("April 16, 2024 00:00:00").getTime();
// Set a const for the container to write a conditional statement and only run if it exists
const countdownContainer = document.getElementById("countdown-container");
// Function to update the countdown display
function updateCountdown() {
if (countdownContainer) {
const now = new Date().getTime();
const difference = targetDate - now;
// Time calculations for days only
const days = Math.floor(difference / (1000 * 60 * 60 * 24));
// Visibility logic
if (days < 0 || days > 10) {
// Hide if more than 10 days away OR if already past the date
countdownContainer.style.display = "none";
} else {
// Show if 10 days or less remaining
countdownContainer.style.display = "block";
document.getElementById("countdown").innerHTML = days + " days ";
}
}
}
// Expose the updateCountdown function to the outside world
window.updateCountdown = updateCountdown;
// Initial display update
updateCountdown();
// Update the countdown every second (inside the IIFE)
setInterval(updateCountdown, 1000);
})(window);

View File

@@ -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 {
@@ -62,6 +62,14 @@
margin: -20px units(1) 20px units(1);
}
h2.sms-message-header {
margin-bottom: 0.5rem;
}
h2.recipient-list {
margin-bottom: 0.5rem;
}
.sms-message-status-outbound {
text-align: right;
}
@@ -131,7 +139,7 @@
&-label,
&-button-label {
font-weight: bold;
font-size: 19px;
font-size: 19px;
display: block;
margin: 0 0 10px 0;
}

View File

@@ -22,26 +22,35 @@ i.e.
@use "uswds-core" as *;
iframe:focus, [href]:focus, [tabindex]:focus, [contentEditable=true]:focus {
outline: units(1px) dotted color('blue-40v');
outline: units(1px) auto color('blue-40v');
outline-offset: 0.3rem;
}
.usa-header--extended {
.usa-logo {
font-family: family("sans");
margin: units(4) 0;
margin: units(3) 0;
@include at-media-max('desktop') {
margin: units(4) 0 units(4) units(2);
}
img {
@include at-media($theme-header-min-width) {
width: 80px;
height: 70px;
a {
display: flex;
img {
height: 35px;
@include at-media('desktop') {
height: 60px;
}
}
}
}
.usa-nav__secondary {
.usa-nav__link {
padding: 0;
&.usa-current {
text-decoration: underline;
}
padding: 0;
&.usa-current {
text-decoration: underline;
}
}
@include at-media-max('desktop') {
padding: 0 units(2);
@@ -62,18 +71,23 @@ i.e.
.usa-nav__primary {
&> .usa-nav__primary-item > a {
font-size: size("body", 4);
font-size: size("body", 5);
font-weight: 400;
cursor: pointer;
}
// &> .usa-nav__primary-item:last-child {
// margin-left: auto;
// @include u-margin-right(-4);
// }
}
h1 {
font-weight: bold !important;
}
.usa-section--dark h1 {
color: color('blue-30v');
@include at-media-max('desktop') {
font-size: size("body", 12);
}
}
p,
.list li {
font-family: family("sans");
@@ -154,26 +168,33 @@ td.table-empty-message {
flex-wrap: wrap;
justify-content: space-between;
padding: 1rem;
.user-list-item {
@include at-media(desktop) {
width: calc(50% - units(1));
margin-bottom: 1rem;
box-sizing: border-box;
}
// flex: 1 1 calc(100% - units(2));
border: 1px solid color('gray-cool-10');
padding: units(2);
.tick-cross-list-permissions {
margin: units(1) 0;
padding-left: units(2);
}
.hint {
display: block;
font-size: size("body", "sm");
font-weight: normal;
}
}
.user-list-item {
@include at-media(desktop) {
width: calc(50% - units(1));
margin-bottom: 1rem;
box-sizing: border-box;
}
}
.user-list-item-heading .live-search-relevant {
display: block;
word-wrap: break-word;
}
border: 1px solid color('gray-cool-10');
padding: units(2);
.tick-cross-list-permissions {
margin: units(1) 0;
padding-left: units(2);
}
.hint {
display: block;
font-size: size("body", "sm");
font-weight: normal;
}
}
.template-list-item-without-ancestors .template-list-folder:active,
.template-list-item-without-ancestors .template-list-folder:active::before,
@@ -298,27 +319,15 @@ td.table-empty-message {
.dashboard {
.big-number-with-status {
a {
background: color("blue-60v");
padding: units(2);
margin-bottom: units(1);
text-decoration: none;
&:hover{
background: color("blue-warm-70v");
}
}
span {
color: white;
}
.big-number-smaller {
display: flex;
flex-direction: column;
.big-number-number {
font-size: units(5);
line-height: units(6);
line-height: units(5);
}
.big-number-label {
font-size: units(3);
font-size: units(2.5);
}
}
.big-number-status {
@@ -568,3 +577,9 @@ details form {
.edit-textbox-error-mt {
margin-top: 1.5rem;
}
// Login page
#countdown-container {
display: none; // Hide the countdown timer
}

View File

@@ -9,5 +9,9 @@ in the form $setting: value,
@use "uswds-core" with (
$theme-font-type-sans: "public-sans",
$theme-show-notifications: false
$theme-show-notifications: false,
$theme-banner-max-width: "desktop-lg",
$theme-grid-container-max-width: "desktop-lg",
$theme-footer-max-width: "desktop-lg",
$theme-header-max-width: "desktop-lg"
);

View File

@@ -2,9 +2,9 @@ import json
from os import getenv
import newrelic.agent
from notifications_utils import DAILY_MESSAGE_LIMIT
from app.cloudfoundry_config import cloud_config
from notifications_utils import DAILY_MESSAGE_LIMIT
class Config(object):
@@ -36,11 +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
TEMPLATE_PREVIEW_API_HOST = getenv(
"TEMPLATE_PREVIEW_API_HOST", "http://localhost:9999"
)
TEMPLATE_PREVIEW_API_KEY = getenv("TEMPLATE_PREVIEW_API_KEY", "my-secret-key")
COMMIT_HASH = getenv("COMMIT_HASH", "--------")[0:7]
GOVERNMENT_EMAIL_DOMAIN_NAMES = ["gov"]

View File

@@ -15,15 +15,15 @@ import pytz
from bs4 import BeautifulSoup
from flask import Markup, render_template_string, url_for
from flask.helpers import get_root_path
from app.utils.csv import get_user_preferred_timezone
from app.utils.time import parse_dt, parse_naive_dt
from notifications_utils.field import Field
from notifications_utils.formatters import make_quotes_smart
from notifications_utils.formatters import nl2br as utils_nl2br
from notifications_utils.recipients import InvalidPhoneError, validate_phone_number
from notifications_utils.take import Take
from app.utils.csv import get_user_preferred_timezone
from app.utils.time import parse_naive_dt
def apply_html_class(tags, html_file):
new_html = html_file
@@ -87,37 +87,22 @@ def format_time(date):
def format_datetime_normal(date):
# example: February 20, 2024 at 07:00 PM US/Eastern, used for datetimes that's not within tables
return "{} at {} {}".format(
format_date_normal(date), format_time_24h(date), get_user_preferred_timezone()
format_date_normal(date), format_time_12h(date), get_user_preferred_timezone()
)
def format_datetime_short(date):
return "{} at {} {}".format(
format_date_short(date), format_time_24h(date), get_user_preferred_timezone()
)
def format_datetime_short_america(date):
return "{} at {}".format(format_date_numeric_america(date), format_time_12h(date))
def format_date_numeric_america(date):
date = parse_naive_dt(date)
preferred_tz = pytz.timezone(get_user_preferred_timezone())
return (
date.replace(tzinfo=timezone.utc).astimezone(preferred_tz).strftime("%m-%d-%Y")
)
def format_datetime_table(date):
# example: 03-18-2024 at 04:53 PM, intended for datetimes in tables
return "{} at {}".format(format_date_numeric(date), format_time_12h(date))
def format_time_12h(date):
date = parse_naive_dt(date)
date = parse_dt(date)
preferred_tz = pytz.timezone(get_user_preferred_timezone())
return (
date.replace(tzinfo=timezone.utc).astimezone(preferred_tz).strftime("%I:%M %p")
)
return date.astimezone(preferred_tz).strftime("%I:%M %p")
def format_datetime_relative(date):
@@ -137,7 +122,7 @@ def format_date_numeric(date):
preferred_tz = pytz.timezone(get_user_preferred_timezone())
return (
date.replace(tzinfo=timezone.utc).astimezone(preferred_tz).strftime("%Y-%m-%d")
date.replace(tzinfo=timezone.utc).astimezone(preferred_tz).strftime("%m-%d-%Y")
)
@@ -186,7 +171,7 @@ def format_date(date):
def format_date_normal(date):
date = parse_naive_dt(date)
return date.strftime("%d %B %Y").lstrip("0")
return date.strftime("%B %d, %Y").lstrip("0")
def format_date_short(date):

View File

@@ -10,9 +10,6 @@ from flask_wtf import FlaskForm as Form
from flask_wtf.file import FileAllowed
from flask_wtf.file import FileField as FileField_wtf
from flask_wtf.file import FileSize
from notifications_utils.formatters import strip_all_whitespace
from notifications_utils.insensitive_dict import InsensitiveDict
from notifications_utils.recipients import InvalidPhoneError, validate_phone_number
from werkzeug.utils import cached_property
from wtforms import (
BooleanField,
@@ -65,6 +62,9 @@ from app.models.organization import Organization
from app.utils import merge_jsonlike
from app.utils.csv import get_user_preferred_timezone
from app.utils.user_permissions import all_ui_permissions, permission_options
from notifications_utils.formatters import strip_all_whitespace
from notifications_utils.insensitive_dict import InsensitiveDict
from notifications_utils.recipients import InvalidPhoneError, validate_phone_number
def get_time_value_and_label(future_time):
@@ -605,6 +605,15 @@ class RegisterUserForm(StripWhitespaceForm):
auth_type = HiddenField("auth_type", default="sms_auth")
class SetupUserProfileForm(StripWhitespaceForm):
name = GovukTextInputField(
"Full name", validators=[DataRequired(message="Cannot be empty")]
)
mobile_number = international_phone_number()
# TODO This should be replaced with a select widget when one is available.
preferred_timezone = HiddenField("preferred_timezone", default="US/Eastern")
class RegisterUserFromInviteForm(RegisterUserForm):
def __init__(self, invited_user):
super().__init__(
@@ -1226,7 +1235,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
],
)

View File

@@ -1,15 +1,15 @@
import re
from abc import ABC, abstractmethod
from notifications_utils.field import Field
from notifications_utils.formatters import formatted_list
from notifications_utils.recipients import InvalidEmailError, validate_email_address
from notifications_utils.sanitise_text import SanitiseSMS
from wtforms import ValidationError
from app.main._commonly_used_passwords import commonly_used_passwords
from app.models.spreadsheet import Spreadsheet
from app.utils.user import is_gov_user
from notifications_utils.field import Field
from notifications_utils.formatters import formatted_list
from notifications_utils.recipients import InvalidEmailError, validate_email_address
from notifications_utils.sanitise_text import SanitiseSMS
class CommonlyUsedPassword:

View File

@@ -1,14 +1,14 @@
from flask import jsonify, redirect, render_template, session, url_for
from flask_login import current_user
from notifications_python_client.errors import HTTPError
from notifications_utils.recipients import format_phone_number_human_readable
from notifications_utils.template import SMSPreviewTemplate
from app import current_service, notification_api_client, service_api_client
from app.main import main
from app.main.forms import SearchByNameForm
from app.models.template_list import TemplateList
from app.utils.user import user_has_permissions
from notifications_utils.recipients import format_phone_number_human_readable
from notifications_utils.template import SMSPreviewTemplate
@main.route("/services/<uuid:service_id>/conversation/<uuid:notification_id>")

View File

@@ -6,7 +6,6 @@ from itertools import groupby
from flask import Response, abort, jsonify, render_template, request, session, url_for
from flask_login import current_user
from notifications_utils.recipients import format_phone_number_human_readable
from werkzeug.utils import redirect
from app import (
@@ -30,6 +29,7 @@ from app.utils.csv import Spreadsheet
from app.utils.pagination import generate_next_dict, generate_previous_dict
from app.utils.time import get_current_financial_year
from app.utils.user import user_has_permissions
from notifications_utils.recipients import format_phone_number_human_readable
@main.route("/services/<uuid:service_id>/dashboard")

View File

@@ -1,3 +1,5 @@
import os
from flask import abort, redirect, render_template, request, url_for
from flask_login import current_user
@@ -8,6 +10,8 @@ from app.main.views.pricing import CURRENT_SMS_RATE
from app.main.views.sub_navigation_dictionaries import features_nav, using_notify_nav
from app.utils.user import user_is_logged_in
login_dot_gov_url = os.getenv("LOGIN_DOT_GOV_INITIAL_SIGNIN_URL")
@main.route("/")
def index():
@@ -18,6 +22,7 @@ def index():
"views/signedout.html",
sms_rate=CURRENT_SMS_RATE,
counts=status_api_client.get_count_of_live_services_and_organizations(),
login_dot_gov_url=login_dot_gov_url,
)

View File

@@ -10,15 +10,15 @@ from flask import (
redirect,
render_template,
request,
session,
stream_with_context,
url_for,
)
from flask_login import current_user
from notifications_utils.template import EmailPreviewTemplate, SMSBodyPreviewTemplate
from app import (
current_service,
format_datetime_short,
format_datetime_table,
notification_api_client,
service_api_client,
)
@@ -34,6 +34,7 @@ from app.utils.pagination import (
get_page_from_request,
)
from app.utils.user import user_has_permissions
from notifications_utils.template import EmailPreviewTemplate, SMSBodyPreviewTemplate
@main.route("/services/<uuid:service_id>/jobs")
@@ -93,7 +94,7 @@ def view_job_csv(service_id, job_id):
mimetype="text/csv",
headers={
"Content-Disposition": 'inline; filename="{} - {}.csv"'.format(
job.template["name"], format_datetime_short(job.created_at)
job.template["name"], format_datetime_table(job.created_at)
)
},
)
@@ -276,12 +277,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 +301,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
]
@@ -369,6 +374,13 @@ def get_job_partials(job):
job.template_type
)
if request.referrer is not None:
session["arrived_from_preview_page"] = "check" in request.referrer
else:
session["arrived_from_preview_page"] = False
arrived_from_preview_page_url = session.get("arrived_from_preview_page", False)
return {
"counts": counts,
"notifications": render_template(
@@ -392,6 +404,7 @@ def get_job_partials(job):
"status": render_template(
"partials/jobs/status.html",
job=job,
arrived_from_preview_page_url=arrived_from_preview_page_url,
),
}

View File

@@ -10,12 +10,12 @@ from flask import (
url_for,
)
from itsdangerous import SignatureExpired
from notifications_utils.url_safe_token import check_token
from app.main import main
from app.main.forms import NewPasswordForm
from app.models.user import User
from app.utils.login import log_in_user
from notifications_utils.url_safe_token import check_token
@main.route("/new-password/<path:token>", methods=["GET", "POST"])

View File

@@ -1,11 +1,11 @@
from flask import current_app, render_template
from flask_login import current_user
from notifications_utils.international_billing_rates import INTERNATIONAL_BILLING_RATES
from app.main import main
from app.main.forms import SearchByNameForm
from app.main.views.sub_navigation_dictionaries import using_notify_nav
from app.utils.user import user_is_logged_in
from notifications_utils.international_billing_rates import INTERNATIONAL_BILLING_RATES
CURRENT_SMS_RATE = "1.72"

View File

@@ -1,14 +1,26 @@
import uuid
from datetime import datetime, timedelta
from flask import abort, redirect, render_template, session, url_for
from flask import (
abort,
current_app,
redirect,
render_template,
request,
session,
url_for,
)
from flask_login import current_user
from app import user_api_client
from app.main import main
from app.main.forms import (
RegisterUserForm,
RegisterUserFromInviteForm,
RegisterUserFromOrgInviteForm,
SetupUserProfileForm,
)
from app.main.views import sign_in
from app.main.views.verify import activate_user
from app.models.user import InvitedOrgUser, InvitedUser, User
from app.utils import hide_from_search_engines
@@ -120,4 +132,47 @@ def _do_registration(form, send_sms=True, send_email=True, organization_id=None)
def registration_continue():
if not session.get("user_details"):
return redirect(url_for(".show_accounts_or_dashboard"))
return render_template("views/registration-continue.html")
else:
raise Exception("Unexpected routing in registration_continue")
@main.route("/set-up-your-profile", methods=["GET", "POST"])
@hide_from_search_engines
def set_up_your_profile():
form = SetupUserProfileForm()
if form.validate_on_submit():
# start login.gov
code = request.args.get("code")
state = request.args.get("state")
login_gov_error = request.args.get("error")
if code and state:
access_token = sign_in._get_access_token(code, state)
user_email, user_uuid = sign_in._get_user_email_and_uuid(access_token)
redirect_url = request.args.get("next")
elif login_gov_error:
current_app.logger.error(f"login.gov error: {login_gov_error}")
raise Exception(f"Could not login with login.gov {login_gov_error}")
# end login.gov
# create the user
# TODO we have to provide something for password until that column goes away
# TODO ideally we would set the user's preferred timezone here as well
user = user_api_client.get_user_by_uuid_or_email(user_uuid, user_email)
if user is None:
user = User.register(
name=form.name.data,
email_address=user_email,
mobile_number=form.mobile_number.data,
password=str(uuid.uuid4()),
auth_type="sms_auth",
)
# activate the user
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))
return render_template("views/set-up-your-profile.html", form=form)

View File

@@ -1,4 +1,3 @@
import itertools
import time
import uuid
from string import ascii_uppercase
@@ -6,11 +5,8 @@ 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
from notifications_utils.recipients import RecipientCSV, first_column_headings
from notifications_utils.sanitise_text import SanitiseASCII
from xlrd.biffh import XLRDError
from xlrd.xldate import XLDateError
@@ -39,6 +35,10 @@ from app.utils import PermanentRedirect, should_skip_template_page, unicode_trun
from app.utils.csv import Spreadsheet, get_errors_for_csv
from app.utils.templates import get_template
from app.utils.user import user_has_permissions
from notifications_utils import SMS_CHAR_COUNT_LIMIT
from notifications_utils.insensitive_dict import InsensitiveDict
from notifications_utils.recipients import RecipientCSV, first_column_headings
from notifications_utils.sanitise_text import SanitiseASCII
def get_example_csv_fields(column_headers, use_example_as_example, submitted_fields):
@@ -151,8 +151,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,23 +507,38 @@ 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,
)
allow_list = []
if current_service.trial_mode:
# Adding the simulated numbers to allow list
# so they can be sent in trial mode
for user in Users(service_id):
allow_list.extend([user.name, user.mobile_number, user.email_address])
# Failed sms number
allow_list.extend(
["simulated user (fail)", "+14254147167", "simulated@simulated.gov"]
)
# Success sms number
allow_list.extend(
["simulated user (success)", "+14254147755", "simulatedtwo@simulated.gov"]
)
else:
allow_list = None
recipients = RecipientCSV(
contents,
template=template,
template=template or simplifed_template,
max_initial_rows_shown=50,
max_errors_shown=50,
guestlist=(
itertools.chain.from_iterable(
[user.name, user.mobile_number, user.email_address]
for user in Users(service_id)
)
if current_service.trial_mode
else None
),
guestlist=allow_list,
remaining_messages=remaining_messages,
allow_international_sms=current_service.has_permission("international_sms"),
)
@@ -530,11 +548,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 +569,9 @@ 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 +592,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 +647,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 +733,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 +846,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 +871,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 +904,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 +971,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,
@@ -909,8 +1012,9 @@ def send_notification(service_id, template_id):
return redirect(
url_for(
".view_notification",
".view_job",
service_id=service_id,
job_id=upload_id,
from_job=upload_id,
notification_id=notifications["notifications"][0]["id"],
# used to show the final step of the tour (help=3) or not show

View File

@@ -20,10 +20,12 @@ from flask_login import current_user
from app import login_manager, user_api_client
from app.main import main
from app.main.forms import LoginForm
from app.main.views.index import error
from app.main.views.verify import activate_user
from app.models.user import InvitedUser, User
from app.utils import hide_from_search_engines
from app.utils.login import is_safe_redirect_url
from notifications_utils.url_safe_token import generate_token
def _reformat_keystring(orig):
@@ -53,22 +55,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,24 +75,28 @@ 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"]))
@hide_from_search_engines
def sign_in():
def _do_login_dot_gov():
# start login.gov
code = request.args.get("code")
state = request.args.get("state")
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)
activate_user(user["id"])
try:
user = user_api_client.get_user_by_uuid_or_email(user_uuid, user_email)
activate_user(user["id"])
except BaseException as be: # noqa B036
current_app.logger.error(be)
error(401)
return redirect(url_for("main.show_accounts_or_dashboard", next=redirect_url))
elif login_gov_error:
@@ -101,6 +104,11 @@ def sign_in():
raise Exception(f"Could not login with login.gov {login_gov_error}")
# end login.gov
@main.route("/sign-in", methods=(["GET", "POST"]))
@hide_from_search_engines
def sign_in():
_do_login_dot_gov()
redirect_url = request.args.get("next")
if os.getenv("NOTIFY_E2E_TEST_EMAIL"):
@@ -167,40 +175,26 @@ def sign_in():
)
other_device = current_user.logged_in_elsewhere()
notify_env = os.getenv("NOTIFY_ENVIRONMENT")
current_app.logger.info("should render the sign in template")
# TODO REMOVE THIS INFO ONCE STAGING WORKS WITH LOGIN DOT GOV
current_app.logger.info(f"NOTIFY ENV = {notify_env}")
current_app.logger.info(
f"LOGIN_DOT_GOV_CLIENT_ID={os.getenv('LOGIN_DOT_GOV_CLIENT_ID')}"
token = generate_token(
str(request.remote_addr),
current_app.config["SECRET_KEY"],
current_app.config["DANGEROUS_SALT"],
)
current_app.logger.info(
f"LOGIN_DOT_GOV_USER_INFO_URL={os.getenv('LOGIN_DOT_GOV_USER_INFO_URL')}"
)
current_app.logger.info(
f"LOGIN_DOT_GOV_ACCESS_TOKEN_URL={os.getenv('LOGIN_DOT_GOV_ACCESS_TOKEN_URL')}"
)
current_app.logger.info(
f"LOGIN_DOT_GOV_LOGOUT_URL={os.getenv('LOGIN_DOT_GOV_LOGOUT_URL')}"
)
current_app.logger.info(
f"LOGIN_DOT_GOV_BASE_LOGOUT_URL={os.getenv('LOGIN_DOT_GOV_BASE_LOGOUT_URL')}"
)
current_app.logger.info(
f"LOGIN_DOT_GOV_SIGNOUT_REDIRECT={os.getenv('LOGIN_DOT_GOV_SIGNOUT_REDIRECT')}"
)
initial_signin_url = os.getenv("LOGIN_DOT_GOV_INITIAL_SIGNIN_URL")
current_app.logger.info(f"LOGIN_DOT_GOV_INITIAL_SIGNIN_URL={initial_signin_url}")
url = os.getenv("LOGIN_DOT_GOV_INITIAL_SIGNIN_URL")
# handle unit tests
if url is not None:
url = url.replace("NONCE", token)
url = url.replace("STATE", token)
return render_template(
"views/signin.html",
form=form,
again=bool(redirect_url),
other_device=other_device,
login_gov_enabled=bool(notify_env in ["development", "staging", "demo"]),
login_gov_enabled=True,
password_reset_url=password_reset_url,
initial_signin_url=initial_signin_url,
initial_signin_url=url,
)

View File

@@ -6,10 +6,6 @@ from flask_login import current_user
from app.main import main
# ask login.gov if we really need manual logout and what's up with one hour sessions
# ask login.gov how they recommend approaching dev environment
# ask Tim Donaworth the same for #2
def _sign_out_at_login_dot_gov():
base_url = os.getenv("LOGIN_DOT_GOV_BASE_LOGOUT_URL")

View File

@@ -4,7 +4,6 @@ from flask import abort, flash, jsonify, redirect, render_template, request, url
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 app import (
current_service,
@@ -30,6 +29,7 @@ from app.models.template_list import TemplateList, TemplateLists
from app.utils import NOTIFICATION_TYPES, should_skip_template_page
from app.utils.templates import get_template
from app.utils.user import user_has_permissions
from notifications_utils import SMS_CHAR_COUNT_LIMIT
form_objects = {
"email": EmailTemplateForm,

View File

@@ -3,7 +3,6 @@ import json
from flask import current_app, redirect, render_template, request, session, url_for
from flask_login import current_user
from itsdangerous import SignatureExpired
from notifications_utils.url_safe_token import check_token
from app import user_api_client
from app.main import main
@@ -15,6 +14,7 @@ from app.utils.login import (
redirect_to_sign_in,
redirect_when_logged_in,
)
from notifications_utils.url_safe_token import check_token
@main.route("/two-factor-email-sent", methods=["GET"])

View File

@@ -11,7 +11,6 @@ from flask import (
url_for,
)
from flask_login import current_user
from notifications_utils.url_safe_token import check_token
from app import user_api_client
from app.event_handlers import (
@@ -31,6 +30,7 @@ from app.main.forms import (
)
from app.models.user import User
from app.utils.user import user_is_gov_user, user_is_logged_in
from notifications_utils.url_safe_token import check_token
NEW_EMAIL = "new-email"
NEW_MOBILE = "new-mob"

View File

@@ -2,13 +2,14 @@ import json
from flask import abort, current_app, flash, redirect, render_template, session, url_for
from itsdangerous import SignatureExpired
from notifications_utils.url_safe_token import check_token
from app import user_api_client
from app.extensions import redis_client
from app.main import main
from app.main.forms import TwoFactorForm
from app.models.user import InvitedOrgUser, InvitedUser, User
from app.utils.login import redirect_to_sign_in
from notifications_utils.url_safe_token import check_token
@main.route("/verify", methods=["GET", "POST"])
@@ -64,20 +65,46 @@ def verify_email(token):
def activate_user(user_id):
user = User.from_id(user_id)
# the user will have a new current_session_id set by the API - store it in the cookie for future requests
# This is the login.gov path
login_gov_invite_data = redis_client.get(f"service-invite-{user.email_address}")
if login_gov_invite_data:
login_gov_invite_data = json.loads(login_gov_invite_data.decode("utf8"))
# This is the deprecated path for organization invites where we get id from session
session["current_session_id"] = user.current_session_id
organization_id = session.get("organization_id")
activated_user = user.activate()
activated_user.login()
# TODO when login.gov is mandatory, get rid of the if clause, it is deprecated.
invited_user = InvitedUser.from_session()
if invited_user:
service_id = _add_invited_user_to_service(invited_user)
return redirect(url_for("main.service_dashboard", service_id=service_id))
elif login_gov_invite_data:
service_id = login_gov_invite_data["service_id"]
user.add_to_service(
service_id,
login_gov_invite_data["permissions"],
login_gov_invite_data["folder_permissions"],
login_gov_invite_data["from_user_id"],
)
return redirect(url_for("main.service_dashboard", service_id=service_id))
# TODO when login.gov is mandatory, git rid of the if clause, it is deprecated.
invited_org_user = InvitedOrgUser.from_session()
if invited_org_user:
user_api_client.add_user_to_organization(invited_org_user.organization, user_id)
elif redis_client.get(f"organization-invite-{user.email_address}"):
organization_id = redis_client.raw_get(
f"organization-invite-{user.email_address}"
)
user_api_client.add_user_to_organization(
organization_id.decode("utf8"), user_id
)
if organization_id:
return redirect(url_for("main.organization_dashboard", org_id=organization_id))

View File

@@ -1,6 +1,7 @@
from abc import abstractmethod
from flask import abort
from notifications_utils.serialised_model import (
SerialisedModel,
SerialisedModelCollection,

View File

@@ -1,10 +1,9 @@
from abc import ABC, abstractmethod
from notifications_utils.formatters import formatted_list
from app.formatters import format_thousands
from app.models import ModelList
from app.notify_client.service_api_client import service_api_client
from notifications_utils.formatters import formatted_list
class Event(ABC):

View File

@@ -1,5 +1,4 @@
from flask import abort, current_app
from notifications_utils.serialised_model import SerialisedModelCollection
from werkzeug.utils import cached_property
from app.models import JSONModel, SortByNameMixin
@@ -15,6 +14,7 @@ from app.notify_client.organizations_api_client import organizations_client
from app.notify_client.service_api_client import service_api_client
from app.notify_client.template_folder_api_client import template_folder_api_client
from app.utils import get_default_sms_sender
from notifications_utils.serialised_model import SerialisedModelCollection
class Service(JSONModel, SortByNameMixin):

View File

@@ -2,9 +2,9 @@ from flask import abort, has_request_context, request
from flask_login import current_user
from notifications_python_client import __version__
from notifications_python_client.base import BaseAPIClient
from notifications_utils.clients.redis import RequestCache
from app.extensions import redis_client
from notifications_utils.clients.redis import RequestCache
cache = RequestCache(redis_client)

View File

@@ -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 or user_data.get("data") is None:
return None
return user_data["data"]
def get_user_by_email_or_none(self, email_address):
try:
return self.get_user_by_email(email_address)

View File

@@ -1,7 +1,6 @@
import uuid
from flask import current_app
from notifications_utils.s3 import s3upload as utils_s3upload
from app.s3_client import (
get_s3_contents,
@@ -9,6 +8,7 @@ from app.s3_client import (
get_s3_object,
set_s3_metadata,
)
from notifications_utils.s3 import s3upload as utils_s3upload
FILE_LOCATION_STRUCTURE = "service-{}-notify/{}.csv"

View File

@@ -2,9 +2,9 @@ import uuid
from boto3 import Session
from flask import current_app
from notifications_utils.s3 import s3upload as utils_s3upload
from app.s3_client import get_s3_object
from notifications_utils.s3 import s3upload as utils_s3upload
TEMP_TAG = "temp-{user_id}_"
EMAIL_LOGO_LOCATION_STRUCTURE = "{temp}{unique_id}-{filename}"

View File

@@ -21,7 +21,7 @@
{% endblock %}
{% block meta %}
<meta property="og:site_name" content="Notify.gov">
<meta property="og:image" content="{{ asset_url('images/usa-opengraph-image.png') }}">
<meta property="og:image" content="{{ asset_url('images/notify-og-image.png') }}">
{% endblock %}
<script type="text/javascript" src="{{ asset_url('js/gtm_head.js') }}"></script>
{% endblock %}
@@ -175,11 +175,13 @@
{% endblock %}
{% block footer %}
<!-- lines 179 - 251 can all be deleted and replaced with {% include "new/components/footer.html" %} -->
{% 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>' %}
{% set commit_hash = ", Latest version: " + config['COMMIT_HASH'] %}
{% set long_link = '<a href="https://www.gsa.gov/about-us/organization/federal-acquisition-service/technology-transformation-services/tts-solutions" class="usa-link">Technology Transformation Services</a>' %}
{% set meta_suffix = "Built by the " + long_link + commit_hash %}
{% endif %}
{{ usaFooter({
@@ -240,7 +242,7 @@
{
"href": url_for('main.support'),
"text": "Contact us"
},
}
]
},
],
@@ -249,7 +251,6 @@
"html": meta_suffix
}
}) }}
{% if current_user.is_authenticated %}
{% block sessionUserWarning %}
<dialog class="usa-modal" id="sessionTimer" aria-labelledby="sessionTimerHeading" aria-describedby="timerWarning">

View File

@@ -4,38 +4,6 @@
</div> #}
<div class="usa-footer__primary-section">
<div class="grid-container">
<div class="grid-row">
<div class="grid-col">
{# {% if params.navigation %}
<nav class="usa-footer__nav" aria-label="Footer navigation,,">
<div class="grid-row grid-gap-2">
{% for nav in params.navigation %}
<div class="mobile-lg:grid-col-6 desktop:grid-col-4">
<section class="
usa-footer__primary-content
usa-footer__primary-content--collapsible
">
<h4 class="usa-footer__primary-link">{{ nav.title }}</h4>
<ul class="usa-list usa-list--unstyled">
{% for item in nav.items %}
{% if item.href and item.text %}
<li class="usa-footer__secondary-link">
<a href="{{ item.href }}" {% for attribute, value in item.attributes %}
{{attribute}}="{{value}}" {% endfor %}>
{{ item.text }}
</a>
</li>
{% endif %}
{% endfor %}
</ul>
</section>
</div>
{% endfor %}
</div>
</nav>
{% endif %} #}
</div>
</div>
<div class="padding-y-1">
{% if params.meta %}
<h2 class="usa-sr-only">{{ params.meta.visuallyHiddenTitle | default("Support links") }}</h2>
@@ -114,4 +82,4 @@
<a href="https://www.usa.gov/" class="usa-link">Visit USA.gov</a>
</div>
</section>
</div>
</div>

View File

@@ -47,14 +47,13 @@
<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>
<div class="logo-img display-flex">
<a href="/">
<span class="usa-sr-only">Notify.gov logo</span>
<image src="{{ params.assetsPath | default('/static/images') }}/notify-logo.png" alt="Notify.gov logo" xlink:href=""
class="usa-flag-logo margin-right-1"></image>
</a>
</div>
{% if params.navigation %}
<button type="button" class="usa-menu-btn">Menu</button>
{% endif %}
@@ -77,7 +76,7 @@
{% endif %}
{% endfor %}
</ul>
<div class="usa-nav__secondary margin-bottom-2">
<div class="usa-nav__secondary margin-bottom-6">
<ul class="usa-nav__secondary-links">
{% for item in params.secondaryNavigation %}
{% if item.href and item.text %}

View File

@@ -0,0 +1,13 @@
# Select
## Installation
See the [main README quick start guide](https://github.com/alphagov/govuk-frontend#quick-start) for how to install this component.
## Guidance and Examples
Find out when to use the select component in your service in the [GOV.UK Design System](https://design-system.service.gov.uk/components/select/).
## Component options
Use options to customize the appearance, content and behavior of a component when using a macro, for example, changing the text.

View File

@@ -0,0 +1,58 @@
[
{
"name": "id",
"type": "string",
"required": true,
"description": "ID for each select box."
},
{
"name": "name",
"type": "string",
"required": true,
"description": "Name property for the select."
},
{
"name": "items",
"type": "array",
"required": true,
"description": "The items within the select component."
},
{
"name": "value",
"type": "string",
"required": false,
"description": "Value for the option which should be selected. Use this as an alternative to setting the selected option on each individual item."
},
{
"name": "disabled",
"type": "boolean",
"required": false,
"description": "If true, select box will be disabled. Use the disabled option on each individual item to only disable certain options."
},
{
"name": "describedBy",
"type": "string",
"required": false,
"description": "One or more element IDs to add to the `aria-describedby` attribute, used to provide additional descriptive information for screenreader users."
},
{
"name": "label",
"type": "object",
"required": true,
"description": "The label used by the select component.",
"isComponent": true
},
{
"name": "hint",
"type": "object",
"required": false,
"description": "Can be used to add a hint to the select component.",
"isComponent": true
},
{
"name": "classes",
"type": "string",
"required": false,
"description": "Classes to add to the select."
}
]

View File

@@ -0,0 +1,3 @@
{% macro usaSelect(params) %}
{%- include "./template.njk" -%}
{% endmacro %}

View File

@@ -0,0 +1,15 @@
{% set describedBy = params.describedBy if params.describedBy else "" %}
<div class="usa-form-group">
<label class="usa-label" for="{{ params.name }}">{{params.label}}</label>
{% if params.hint %}
<div class="usa-hint" id="{{ params.describedBy }}">{{ params.hint }}</div>
{% endif %}
<select class="usa-select {%- if params.classes %} {{ params.classes }}{% endif %}" id="{{ params.id }}" name="{{ params.name }}"
{%- if params.value %} value="{{ params.value}}"{% endif %}
{%- if describedBy %} aria-describedby="{{ describedBy }}"{% endif %}
{%- for attribute, value in params.attributes %} {{ attribute }}="{{ value }}"{% endfor -%}>
{% for item in params.items %}
<option value="{{ item.value }}" {%- if item.disabled == true %} selected disabled {% endif %}>{{ item.text }}</option>
{% endfor %}
</select>
</div>

View File

@@ -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_america,
(notification.sent_at or notification.created_at)|format_datetime_short_america
notification.created_at|format_datetime_table,
(notification.sent_at or notification.created_at)|format_datetime_table
) }}
</p>
{% if displayed_on_single_line %}</span>{% endif %}

View File

@@ -4,6 +4,7 @@
<div class="grid-row">
<div class="grid-col-8">
<h1>Youre not authorized to see this page</h1>
<p class="usa-body"><a class="usa-link" href="{{ url_for('main.sign_in' )}}">Sign in</a> to Notify.gov and try again.</p>
<p class="usa-body">If you have been invited to join Notify.gov, <a class="usa-link" href="{{ url_for('main.sign_in' )}}">sign in</a> to Notify.gov using your Login.gov account and try again.</p>
</div>
{% endblock %}
e

View File

@@ -14,7 +14,7 @@
<li class="usa-sidenav__item"><a class="{{ casework_navigation.is_selected('sent-messages') }}" href="{{ url_for('.view_notifications', service_id=current_service.id, status='sending,delivered,failed') }}">Sent messages</a></li>
{% endif %}
{% if current_user.has_permissions('manage_service', allow_org_user=True) %}
<li class="usa-sidenav__item"><a class="{{ main_navigation.is_selected('usage') }}" href="{{ url_for('.usage', service_id=current_service.id) }}">Usage</a></li>
{# <li class="usa-sidenav__item"><a class="{{ main_navigation.is_selected('usage') }}" href="{{ url_for('.usage', service_id=current_service.id) }}">Usage</a></li> #}
{% endif %}
<!-- {% if current_user.has_permissions('manage_api_keys', 'manage_service') %}
<li class="usa-sidenav__item"><a class="{{ main_navigation.is_selected('settings') }}" href="{{ url_for('.service_settings', service_id=current_service.id) }}">Settings</a></li>

View File

@@ -19,17 +19,22 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
{% block headIcons %}
<link rel="shortcut icon" sizes="16x16 32x32 48x48" href="{{ assetPath | default('/assets') }}/images/favicon.ico" type="image/x-icon" />
<link rel="mask-icon" href="{{ assetPath | default('/assets') }}/images/usa-mask-icon.svg" color="{{ themeColor | default('#F0F0F0') }}">
<link rel="shortcut icon" href="{{ assetPath | default('/assets') }}/images/favicon.ico" />
<link rel="icon" type="image/png" sizes="32x32"href="{{ assetPath | default('/assets') }}/images/favicon-32x32.png"/>
<link rel="icon" type="image/png" sizes="16x16" href="{{ assetPath | default('/assets') }}/images/favicon-16x16.png" />
<link rel="mask-icon" href="{{ assetPath | default('/assets') }}/images/safari-pinned-tab.svg" color="#5bbad5">
<link rel="apple-touch-icon" sizes="180x180" href="{{ assetPath | default('/assets') }}/images/apple-touch-icon.png">
<link rel="apple-touch-icon" href="{{ assetPath | default('/assets') }}/images/apple-touch-icon.png">
<link rel="manifest" href="/site.webmanifest">
<meta name="msapplication-TileColor" content="#da532c">
<link href="{{ assetPath | default('/assets') }}/images/notify-dark-favicon.png" rel="icon" media="(prefers-color-scheme: dark)">
<meta name="theme-color" content="#ffffff">
{% endblock %}
{% block head %}{% endblock %}
{# The default og:image is added below head so that scrapers see any custom metatags first, and this is just a fallback #}
{% block meta %}
<meta property="og:site_name" content="Notify.gov">
<meta property="og:image" content="{{ asset_url('images/usa-opengraph-image.png') }}">
<meta property="og:image" content="{{ asset_url('images/notify-og-image.png') }}">
{% endblock %}
</head>
<body class="usa-template__body {{ bodyClasses }}">

126
app/templates/new/base.html Normal file
View File

@@ -0,0 +1,126 @@
{% from "../components/banner.html" import banner %}
{% from "../components/components/skip-link/macro.njk" import usaSkipLink -%}
<!DOCTYPE html>
<html lang="{{ htmlLang | default('en') }}" class="{{ htmlClasses }}">
{% include "new/components/head.html" %}
<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 %}
{% block header %}
{% if current_user.is_authenticated %}
{% include 'new/components/usa_banner.html' %}
{% include 'new/components/header.html' %}
{% endif %}
{% endblock %}
{% 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 %}
{% 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 commit_hash = ", Latest version: " + config['COMMIT_HASH'] %}
{% set long_link = '<a href="https://www.gsa.gov/about-us/organization/federal-acquisition-service/technology-transformation-services/tts-solutions" class="usa-link">Technology Transformation Services</a>' %}
{% set meta_suffix = "Built by the " + long_link + commit_hash %}
{% endif %}
{% include "new/components/footer.html" %}
{% 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 %}
{% 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>

View File

@@ -0,0 +1,71 @@
<footer class="usa-footer usa-footer--big">
<div class="usa-footer__primary-section">
<div class="grid-container">
<div class="padding-y-1">
{% if meta_suffix %}
<h2 class="usa-sr-only">Support links</h2>
{% if meta_suffix %}
<div>
{{ meta_suffix | safe if meta_suffix else '' }}
</div>
{% endif %}
{% endif %}
</div>
</div>
</div>
</div>
<div class="usa-identifier site-identifier">
<section class="usa-identifier__section usa-identifier__section--masthead" aria-label="Agency identifier,,,,,,">
<div class="usa-identifier__container">
<div class="usa-identifier__logos"><a href="javascript:void(0);" class="usa-identifier__logo">
<img class="usa-identifier__logo-img" src="{{ (asset_path | default('/static')) + 'images/gsa-logo.svg' }}" alt="GSA Logo" role="img" width="48" height="48">
</a></div>
<section class="usa-identifier__identity" aria-label="Agency description">
<p class="usa-identifier__identity-domain">beta.notify.gov</p>
<p class="usa-identifier__identity-disclaimer">An official website of the <a href="https://gsa.gov">General Services Administration</a></p>
</section>
</div>
</section>
<nav class="usa-identifier__section usa-identifier__section--required-links" aria-label="Important links">
<div class="usa-identifier__container">
<ul class="usa-identifier__required-links-list">
<li class="usa-identifier__required-links-item">
<a href="https://www.gsa.gov/about-us" class="usa-identifier__required-link">About GSA</a>
</li>
<li class="usa-identifier__required-links-item">
<a href="https://www.gsa.gov/website-information/accessibility-aids"
class="usa-identifier__required-link">Accessibility support</a>
</li>
<li class="usa-identifier__required-links-item">
<a href="https://www.gsa.gov/reference/freedom-of-information-act-foia"
class="usa-identifier__required-link">FOIA requests</a>
</li>
<li class="usa-identifier__required-links-item">
<a href="https://www.gsa.gov/reference/civil-rights-programs/notification-and-federal-employee-antidiscrimination-and-retaliation-act-of-2002"
class="usa-identifier__required-link">No FEAR Act data</a>
</li>
<li class="usa-identifier__required-links-item">
<a href="https://www.gsaig.gov/" class="usa-identifier__required-link">Office of the Inspector General</a>
</li>
<li class="usa-identifier__required-links-item">
<a href="https://www.gsa.gov/reference/reports/budget-performance"
class="usa-identifier__required-link">Performance reports</a>
</li>
<li class="usa-identifier__required-links-item">
<a href="https://www.gsa.gov/website-information/website-policies"
class="usa-identifier__required-link">Privacy policy</a>
</li>
</ul>
</div>
</nav>
<section class="usa-identifier__section usa-identifier__section--usagov"
aria-label="Government information and services">
<div class="usa-identifier__container">
<div class="usa-identifier__usagov-description">
Looking for U.S. government information and services?
</div>
<a href="https://www.usa.gov/" class="usa-link">Visit USA.gov</a>
</div>
</section>
</div>
</footer>

View File

@@ -0,0 +1,46 @@
<head>
<meta charset="utf-8" />
<title>{% block pageTitle %}Notify.gov{% endblock %}</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="Reach people where they are with a text message platform built just for government">
<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" href="{{ assetPath | default('/assets') }}/images/favicon.ico" />
<link rel="icon" type="image/png" sizes="32x32" href="{{ assetPath | default('/assets') }}/images/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="{{ assetPath | default('/assets') }}/images/favicon-16x16.png" />
<link rel="mask-icon" href="{{ assetPath | default('/assets') }}/images/safari-pinned-tab.svg" color="#5bbad5">
<link rel="apple-touch-icon" sizes="180x180" href="{{ assetPath | default('/assets') }}/images/apple-touch-icon.png">
<link rel="manifest" href="/site.webmanifest">
<meta name="msapplication-TileColor" content="#da532c">
<link href="{{ assetPath | default('/assets') }}/images/notify-dark-favicon.png" rel="icon" media="(prefers-color-scheme: dark)">
<meta name="theme-color" content="#ffffff">
{% endblock %}
<link rel="stylesheet" media="screen" href="{{ asset_url('css/styles.css') }}" />
{% block extra_stylesheets %}{% endblock %}
{% block meta_format_detection %}
<meta name="format-detection" content="telephone=no">
{% endblock %}
{% block og_image %}
<meta property="og:title" content="Notify.gov">
<meta property="og:description" content="Reach people where they are with a text message platform built just for government">
<meta property="og:image" content="/static/images/notify-og-image.png">
{% endblock %}
{# google #}
<script type="text/javascript" src="{{ asset_url('js/gtm_head.js') }}"></script>
<meta name="google-site-verification" content="niWnSqImOWz6mVQTYqNb5tFK8HaKSB4b3ED4Z9gtUQ0" />
{% if g.hide_from_search_engines %}
<meta name="robots" content="noindex" />
{% endif %}
{% block head %}{% endblock %}
</head>

View File

@@ -0,0 +1,76 @@
{# setting navigation and secondarynavigation #}
{% 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')}
] %}
{% if current_user.platform_admin %}
{% set navigation = navigation + [{"href": url_for('main.platform_admin_splash_page'), "text": "Platform admin", "active": header_navigation.is_selected('platform-admin')}] %}
{% else %}
{% set navigation = navigation + [{"href": url_for('main.user_profile'), "text": "User profile", "active": header_navigation.is_selected('user-profile')}] %}
{% endif %}
{% 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 %}
{# usa header #}
<header class="usa-header usa-header--extended">
<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">
<a href="/">
<span class="usa-sr-only">Notify.gov logo</span>
<img src="{{ (asset_path | default('/static')) + 'images/notify-logo.png' }}" alt="Notify.gov logo" class="usa-flag-logo margin-right-1">
</a>
</div>
{% if navigation %}
<button type="button" class="usa-menu-btn">Menu</button>
{% endif %}
</div>
</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 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-6">
<ul class="usa-nav__secondary-links">
{% if secondaryNavigation %}
{% for item in 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 %}
{% endif %}
</ul>
</div>
</div>
</nav>
</div>
</header>

View File

@@ -0,0 +1,44 @@
{# usa banner #}
<section class="usa-banner site-banner" aria-label="Official website of the United States government,,,,,,">
<div class="usa-accordion">
<header class="usa-banner__header">
<div class="usa-banner__inner">
<div class="grid-col-auto">
<img aria-hidden="true" class="usa-banner__header-flag" src="/static/img/us_flag_small.png" alt="" />
</div>
<div class="grid-col-fill tablet:grid-col-auto" aria-hidden="true">
<p class="usa-banner__header-text">An official website of the United States government</p>
<p class="usa-banner__header-action">Heres how you know</p>
</div>
<button type="button" class="usa-accordion__button usa-banner__button" aria-expanded="false" aria-controls="gov-banner">
<span class="usa-banner__button-text">Heres how you know</span>
</button>
</div>
</header>
<div class="usa-banner__content usa-accordion__content" id="gov-banner" hidden="">
<div class="grid-row grid-gap-lg">
<div class="usa-banner__guidance tablet:grid-col-6">
<img class="usa-banner__icon usa-media-block__img" src="/static/img/icon-dot-gov.svg" role="img" alt="" aria-hidden="true">
<div class="usa-media-block__body">
<p>
<strong>Official websites use .gov</strong>
<br>
A <strong>.gov</strong> website belongs to an official government organization in the United States.
</p>
</div>
</div>
<div class="usa-banner__guidance tablet:grid-col-6">
<img class="usa-banner__icon usa-media-block__img" src="/static/img/icon-https.svg" role="img" alt="" aria-hidden="true">
<div class="usa-media-block__body">
<p>
<strong>Secure .gov websites use HTTPS</strong>
<br>
A <strong>lock</strong> ( <span class="icon-lock"><svg xmlns="http://www.w3.org/2000/svg" width="52" height="64" viewBox="0 0 52 64" class="usa-banner__lock-image" role="img" aria-labelledby="banner-lock-description" focusable="false"><title id="banner-lock-title">Lock</title><desc id="banner-lock-description">Locked padlock</desc><path fill="#000000" fill-rule="evenodd" d="M26 0c10.493 0 19 8.507 19 19v9h3a4 4 0 0 1 4 4v28a4 4 0 0 1-4 4H4a4 4 0 0 1-4-4V32a4 4 0 0 1 4-4h3v-9C7 8.507 15.507 0 26 0zm0 8c-5.979 0-10.843 4.77-10.996 10.712L15 19v9h22v-9c0-6.075-4.925-11-11-11z"></path></svg></span> ) or <strong>https://</strong> means youve safely connected to the .gov website. Share sensitive information only on official, secure websites.
</p>
</div>
</div>
</div>
</div>
</div>
</section>

View 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 %}

View 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 %}

View File

@@ -0,0 +1,38 @@
# 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`, `settings_templates`, and `content_template`.
- **/layouts**: Contains 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)
- `head.html`: Template for the site's <head>, included in `base.html`.
- `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. This is also true for `usaHeader` macro component.
### 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

View File

@@ -1,49 +1,7 @@
{% from "components/pill.html" import pill %}
<div class="ajax-block-container">
{% if notifications_deleted %}
<div class="grid-row ">
{% for label, query_param, url, count in counts %}
{% if query_param == 'pending' %}
<div class="grid-col-3">
<span class="big-number-smaller">
<span class="big-number-number">
{% if count is number %}
{% if currency %}
{{ "{}{:,.2f}".format(currency, count) }}
{% else %}
{{ "{:,}".format(count) }}
{% endif %}
{% else %}
{{ count }}
{% endif %}
</span>
<span class="big-number-label">{{ query_param }}</span>
</span>
</div>
{% else %}
<div class="grid-col-3">
<span class="big-number-smaller">
<span class="big-number-number">
{% if count is number %}
{% if currency %}
{{ "{}{:,.2f}".format(currency, count) }}
{% else %}
{{ "{:,}".format(count) }}
{% endif %}
{% else %}
{{ count }}
{% endif %}
</span>
<span class="big-number-label">{{ label }}</span>
</span>
</div>
{% endif %}
{% endfor %}
</div>
{% else %}
<div class="tabs">
{{ pill(counts, request.args.get('status', '')) }}
</div>
{% endif %}
<div class="tabs">
{{ pill(counts, request.args.get('status', '')) }}
</div>
</div>

View File

@@ -28,14 +28,14 @@
<p class="bottom-gutter hint">
Report is {{ "{:.0f}%".format(job.percentage_complete * 0.99) }} complete…
</p>
{% elif notifications %}
{% elif notifications and time_left != "Data no longer available" %}
<p class="bottom-gutter">
<a href="{{ download_link }}" download class="usa-link heading-small">Download this report (<abbr title="Comma separated values">CSV</abbr>)</a>
&emsp;
<span id="time-left">{{ time_left }}</span>
</p>
{% endif %}
{% call(item, row_number) list_table(
notifications,
caption=uploaded_file_name,

View File

@@ -1,13 +1,48 @@
{% set display_message_status %}
{% if job.template.content %}
<h2 class="sms-message-header">Message</h2>
<div class="sms-message-wrapper">{{ current_service.name }}: {{job.template.content}}</div>
{% endif %}
{% if job.original_file_name %}
<h2 class="recipient-list">Recipient 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>{{ job.original_file_name }}</h3>
</div>
</li>
</ul>
</div>
{% endif %}
<h2>Delivery Status</h2>
{% endset %}
<div class="ajax-block-container">
<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_america }}
{% if job.still_processing or arrived_from_preview_page_url %}
{% if job.scheduled_for %}
<div class="usa-alert usa-alert--info">
<div class="usa-alert__body">
<h4 class="usa-alert__heading">Your text has been scheduled</h4>
<p class="usa-alert__text">
{{ job.template_name }} - {{ current_service.name }} was scheduled on {{ job.scheduled_for|format_datetime_normal }} by {{ job.created_by.name }}
</p>
</div>
</div>
{{display_message_status}}
{% else %}
Uploaded by {{ job.created_by.name }} on {{ job.created_at|format_datetime_short_america }}
<div class="usa-alert usa-alert--success">
<div class="usa-alert__body">
<h4 class="usa-alert__heading">Your text has been sent</h4>
<p class="usa-alert__text">
{{ job.template_name }} - {{ current_service.name }} was sent on {{ job.created_at|format_datetime_normal }} by {{ job.created_by.name }}
</p>
</div>
</div>
{{display_message_status}}
{% endif %}
{% else %}
Sent by {{ job.created_by.name }} on {{ job.created_at|format_datetime_short_america }}
{% endif %}
</p>
{% if job.status == 'sending limits exceeded'%}

View File

@@ -44,7 +44,7 @@
{% endcall %}
{% if item.expiry_date %}
{% call field(align='right') %}
<span class='hint'>Revoked {{ item.expiry_date|format_datetime_short }}</span>
<span class='hint'>Revoked {{ item.expiry_date|format_datetime_table }}</span>
{% endcall %}
{% else %}
{% call field(align='right', status='error') %}

View File

@@ -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 %}

View File

@@ -0,0 +1,107 @@
{% 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 |format_datetime_normal 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">
<table class="usa-table usa-table--borderless width-full">
<caption class="font-body-lg table-heading">
Note: Only the first 5 rows are displayed here.
</caption>
<thead class="table-field-headings-visible">
<tr>
{% for header in recipients.column_headers %}
<th scope="col" class="table-field-heading-first">
{{ header }}
</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for item in recipients.displayed_rows %}
{% if loop.index <= 5 %}
<tr class="table-row">
{% for column in recipients.column_headers %}
<td class="table-field-left-aligned">
<div>
{% set column_data = item[column].data or '' %}
{% if column_data is iterable and column_data is not string %}
<ul>
{% for data_item in column_data %}
{% if data_item is not none %}
<li>{{ data_item }}</li>
{% endif %}
{% endfor %}
</ul>
{% else %}
{{ column_data }}
{% endif %}
</div>
</td>
{% endfor %}
{% if item[None].data %}
{% for column in item[None].data %}
<td class="table-field-left-aligned">
<div class="">
{{ column }}
</div>
</td>
{% endfor %}
{% endif %}
</tr>
{% endif %}
{% endfor %}
</tbody>
</table>
</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 %}

View File

@@ -1,43 +1,25 @@
<div class="ajax-block-container">
<div class="grid-row grid-gap">
<div id="total-sms" class="grid-col-12">
<span class="big-number-with-status">
<a class="usa-link display-flex" href="{{ url_for('.view_notifications', service_id=service_id, message_type='sms', status='sending,delivered,failed') }}">
<div id="total-sms" class="grid-col-12 margin-top-2">
<span class="big-number-with-status display-block margin-bottom-2">
<p>
<span class="big-number-smaller">
<span class="big-number-number">
{% if statistics['sms']['requested'] is number %}
{{ "{:,}".format(statistics['sms']['requested']) }}
{{ "{:,}".format(statistics['sms']['requested']) }}
{% else %}
{{ statistics['sms']['requested'] }}
{{ statistics['sms']['requested'] }}
{% endif %}
</span>
<span class="big-number-label">{{ statistics['sms']['requested']|message_count_label('sms', suffix='sent') }}</span>
<span class="big-number-label">{{ statistics['sms']['requested']|message_count_label('sms', suffix='sent') }} in the last seven days</span>
</span>
</p>
<a class="usa-button usa-button--outline" href="{{ url_for('.view_notifications', service_id=service_id, message_type='sms', status='sending,delivered,failed') }}">
Details
</a>
{% if show_failures %}
<span class="big-number-status{% if statistics['sms']['show_warning'] is sameas true %} big-number-status--failing{% endif %}">
{% if statistics['sms']['failed'] %}
<a class="usa-link" href="{{ url_for('.view_notifications', service_id=service_id, message_type='sms', status='failed') }}">
{{ "{:,}".format(statistics['sms']['failed']) }}
failed {{ statistics['sms']['failed_percentage'] }}%
</a>
{% else %}
No failures
{% endif %}
</span>
{% endif %}
{# Removing the failures area for now, as the user can click on the above link to see all the details.
In the future state of the dashboard, the all statuses will be more apparent with data visualizations #}
</span>
</div>
<!-- <div id="total-email" class="grid-col-6">
<span class="big-number-with-status pilot-disabled">
<a class="usa-link display-block margin-bottom-1">
<span class="big-number-smaller">
<span class="big-number-number">0</span>
<span class="big-number-label">emails sent</span>
</span>
</a>
<span class="big-number-status">No failures</span>
</span>
</div> -->
</div>
</div>

View File

@@ -7,7 +7,7 @@
</caption>
<thead>
<tr>
<th scope="col">Usage</th>
<th scope="col">Sent</th>
<th scope="col">Remaining</th>
</tr>
</thead>

View File

@@ -18,10 +18,9 @@
{{ ajax_block(partials, updates_url, 'upcoming') }}
<h2 class="font-body-xl margin-0">
<h2 class="font-body-2xl line-height-sans-2 margin-0">
Messages sent
</h2>
<p class="margin-top-0">In the last seven days</p>
{{ ajax_block(partials, updates_url, 'inbox') }}
@@ -68,7 +67,7 @@
{{ notification.template.name }}
</td>
<td class="table-field time-sent">
{{ job.created_at | format_datetime_short_america }}
{{ job.created_at | format_datetime_table }}
</td>
<td class="table-field sender">
{{ notification.created_by.name }}
@@ -96,36 +95,16 @@
</table>
</div>
<h2 class="margin-top-4 margin-bottom-1">Usage</h2>
<h3 class="margin-bottom-0">Daily</h3>
<p class="margin-0">Across all services</p>
<table class="usage-table usa-table usa-table--borderless margin-top-1 margin-bottom-5">
<caption class="usa-sr-only">
Daily
</caption>
<thead>
<tr>
<th scope="col">Usage</th>
<th scope="col">Remaining</th>
</tr>
</thead>
<tbody>
<tr>
<td>{{ global_message_limit - daily_global_messages_remaining }}</td>
<td>
{{ daily_global_messages_remaining }}
</td>
</tr>
</tbody>
</table>
<h2 class="margin-top-4 margin-bottom-1">Message count</h2>
{% if current_user.has_permissions('manage_service') %}
<h3 class='margin-bottom-0' id="current-year"></h3>
{{ ajax_block(partials, updates_url, 'usage') }}
<a
href="{{ url_for('.usage', service_id=current_service['id']) }}"
class="usa-link show-more"
><span>See all usage</span></a>
<p class="margin-top-0">During the pilot period, each service has an allowance of 250,000 message parts. Once this allowance is met, the
application will stop delivering messages. There's no monthly charge, no setup fee, and no procurement cost.</p>
<p class="align-with-heading-copy">
What counts as 1 text message part?<br />
See <a class="usa-link" href="{{ url_for('.pricing') }}">pricing</a>.
</p>
{% endif %}
</div>

View File

@@ -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 <a href={{ url_for('.choose_template', service_id=current_service.id) }}>Send Messages</a>. Youll 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>
@@ -69,7 +69,7 @@
<h2 class="padding-top-1" id="personalize-content">Personalize your content</h2>
<p>Personalizing your content can increase response rates and help the recipient know the text is legitimate.</p>
<ul class="list list-bullet">
<li>Including a persons first name increases response rates.</li>
<li>Including a person's first name increases response rates.</li>
<li>Specific details such as time and location of an appointment or where suspected fraud use occurred encourages action.</li>
</ul>
@@ -82,7 +82,10 @@
<h4>Example</h4>
<p>To personalize with the recipient's first name and include a reference number:</p>
<p class="padding-2 bg-base-lightest">State WIC: Hello ((first name)), your reference is ((ref number)). Please provide this number when you call 123-123-1234 to make an appointment.</p>
<p class="padding-2 bg-base-lightest">State WIC: Hello ((first name)), your reference is ((ref number)). Please provide this number when you call 555-123-1234 to make an appointment.</p>
<p>Note that variations in the length of personalized content can impact the length of specific messages, and may affect
the number of parts used.</p>
{# Add conditional content #}
<h2 class="padding-top-1" id="conditional-content">Add conditional content</h2>
@@ -106,7 +109,7 @@
<li>
If you want to make people who are homebound aware of the option of virtual visits (but not other message recipients):
</br>
<p class="padding-2 bg-base-lightest">State Medicaid: Please call 123-123-1234 to schedule an appointment. ((homebound??Virtual visits are available.))</p>
<p class="padding-2 bg-base-lightest">State Medicaid: Please call 555-123-1234 to schedule an appointment. ((homebound??Virtual visits are available.))</p>
</li>
<li>
If you want to send a messages in different languages to different recipients:
@@ -231,7 +234,7 @@
</li>
<li>Auto-response text:
</br>
<p class="padding-2 bg-base-lightest">State Agency: This number is unmonitored. To contact us, call us at 123-123-1234. We will never ask for personal details
<p class="padding-2 bg-base-lightest">State Agency: This number is unmonitored. To contact us, call us at 555-123-1234. We will never ask for personal details
in a text. If you have questions about how to protect your privacy, see statename.gov/privacy.</p>
</li>
</ol>

View File

@@ -5,26 +5,13 @@
{% from "components/components/back-link/macro.njk" import usaBackLink %}
{% block service_page_title %}
{{ job.original_file_name }}
{{ "Message status" }}
{% endblock %}
{% block maincolumn_content %}
{{ page_header(job.original_file_name) }}
{% if not job.processing_finished %}
<div
data-module="update-content"
data-resource="{{ updates_url }}"
data-key="status"
data-form=""
>
{% endif %}
{{ partials['status']|safe }}
{% if not job.processing_finished %}
</div>
{% endif %}
{{ page_header("Message status") }}
{{ partials['status']|safe }}
{% if not finished %}
<div
data-module="update-content"

View File

@@ -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>

View 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 |format_datetime_normal 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 %}

View File

@@ -29,7 +29,7 @@
{{ text_field(item.complaint_type) }}
{{ text_field(item.complaint_date|format_datetime_short if item.complaint_date else None) }}
{{ text_field(item.complaint_date|format_datetime_table if item.complaint_date else None) }}
{% endcall %}

View File

@@ -6,16 +6,16 @@
{% extends "content_template.html" %}
{% block per_page_title %}
Pricing
Message parts
{% endblock %}
{% block content_column_content %}
<h1 class="font-body-2xl margin-bottom-3">Pricing</h1>
<h1 class="font-body-2xl margin-bottom-3">Message parts</h1>
{{ content_metadata(
data={
"Last updated": "January 25, 2024"
"Last updated": "February 5, 2024"
}
) }}
@@ -30,7 +30,8 @@ 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 part.</p>
<p>If a text message is longer than 160 characters (including spaces and service name), it counts as more than one message
part.</p>
<div class="bottom-gutter-3-2">
{% call mapping_table(
@@ -55,6 +56,13 @@ more parts towards the allowance if you:</p>
{% endcall %}
</div>
<h3 class="font-body-lg" id="Personalization">Personalization</h3>
<p>Personalization can change the length of messages. For example, if you are personalizing with a first name, Fred is
significantly shorter than Alexander. When you are evaluating how long a message is, you need to consider variations in
message length based on personalization.</p>
<p>Before you send messages, Notify will let you know how many messages you are sending, the number of parts you are using,
and the number of parts youll have left.</p>
<h3 class="font-body-lg" id="symbols">Signs and symbols</h3>
<p>
@@ -158,9 +166,4 @@ more parts towards the allowance if you:</p>
{% endcall %}
</div>
<h3 class="font-body-lg" id="international-numbers">Trial mode</h3>
<p>When a new service is added, it will start in <a href="/using-notify/trial-mode">trial mode</a>. Moving a service out of trial mode is subject to approval by
the Notify.gov team. Additional unique services may be added, although moving each service out of trial mode is subject
to approval by the Notify.gov team.</p>
{% endblock %}

View File

@@ -66,7 +66,7 @@
<p>Some messages include sensitive information like security codes or password reset links.</p>
<p>If youre 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.">
alt="Screenshot of a test message in review with the link to 'Hide all personalized and conditional content after sending for increased privacy protection' emphasized.">
<h3 class="font-body-lg">Two-factor authentication</h3>
<p>To sign in to Notify, youll need to enter:</p>

View File

@@ -0,0 +1,77 @@
{% extends "withoutnav_template.html" %}
{% from "components/page-footer.html" import page_footer %}
{% from "components/form.html" import form_wrapper %}
{% from "components/components/select/macro.njk" import usaSelect -%}
{% block per_page_title %}
Set up your profile
{% endblock %}
{% block maincolumn_content %}
<div class="grid-row">
<div class="grid-col-8">
<h1 class="font-body-2xl margin-bottom-3">Set up your profile</h1>
{% call form_wrapper(autocomplete=True) %}
{{ form.name(param_extensions={}) }}
<div class="extra-tracking">
{{ form.mobile_number(param_extensions={
"hint": {"text": "We'll send you a security code by text message"},
}) }}
</div>
<!--{{ usaSelect({
"id": "time-zone",
"name": "time-zone",
"label": "Time zone",
"items": [
{
"value": "",
"disabled": true,
"text": "- Select -"
},
{
"value": "America/Puerto_Rico",
"text": "America/Puerto_Rico"
},
{
"value": "US/Eastern",
"text": "US/Eastern"
},
{
"value": "US/Central",
"text": "US/Central"
},
{
"value": "US/Mountain",
"text": "US/Mountain"
},
{
"value": "US/Pacific",
"text": "US/Pacific"
},
{
"value": "US/Alaska",
"text": "US/Alaska"
},
{
"value": "US/Hawaii",
"text": "US/Hawaii"
},
{
"value": "US/Aleutian",
"text": "US/Aleutian"
},
{
"value": "US/Samoa",
"text": "US/Samoa"
},
]
})
}}-->
{{form.auth_type}}
{{ page_footer("Save") }}
{% endcall %}
</div>
</div>
{% endblock %}

View File

@@ -21,7 +21,7 @@ Notify.gov
<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>
<div class="usa-button-group margin-bottom-5">
<a class="usa-button usa-button--big margin-right-2" href="{{ url_for('main.sign_in' )}}">Sign in</a>
<a class="usa-button usa-button--big margin-right-2" href="{{ url_for('main.sign_in' ) }}">Sign in</a>
if you are an existing pilot partner
</div>
<p class="font-sans-md">Currently we are only working with select pilot partners. If you are interested in using Notify.gov in the future, please contact <br><a href="mailto:tts-benefits-studio@gsa.gov">tts-benefits-studio@gsa.gov</a> to learn more.</p>

View File

@@ -12,9 +12,20 @@
{% block maincolumn_content %}
{% if login_gov_enabled %}
<div class="grid-row">
<div class="tablet:grid-col-12">
<div id="countdown-container" class="usa-alert usa-alert--warning width-full margin-bottom-4">
<div class="usa-alert__body">
<h4 class="usa-alert__heading">Login.gov is required by April 16, 2024</h4>
<p class="usa-alert__text">
You have <span id="countdown"></span> left to use Login.gov to sign in
</p>
</div>
</div>
</div>
{% endif %}
<div class="grid-row margin-bottom-4">
<div class="tablet:grid-col-5">
{% if again %}
<h1 class="font-body-2xl margin-bottom-3">You need to sign in again</h1>
{% if other_device %}
@@ -29,9 +40,9 @@
{% else %}
<h1 class="font-body-2xl margin-bottom-3">Sign in</h1>
{% if login_gov_enabled %}
<a class="usa-link" href="{{ initial_signin_url }}">Sign in with Login.gov</a>
</p>
<h4 class="margin-bottom-3">Or:</h4>
<p>You can access your account by signing in with one of the options below:</p>
<a class="usa-link usa-button usa-button--outline" href="{{ initial_signin_url }}">Sign in with Login.gov</a>
<p class="margin-y-3"><strong>Or:</strong></p>
{% endif %}
{% endif %}
@@ -41,6 +52,24 @@
{{ page_footer("Continue", secondary_link=password_reset_url, secondary_link_text="Forgot your password?") }}
{% endcall %}
</div>
</div>
{% if login_gov_enabled %}
<div class="tablet:grid-col-6 tablet:grid-offset-1 margin-top-2 padding-y-2 padding-x-4 bg-base-lightest">
<h2 class="font-body-lg">Notify.gov is changing the sign-in experience to Login.gov effective<br>April 16, 2024</h2>
<p>Why are we doing this?</p>
<ul class="usa-list">
<li><strong>Enhanced security:</strong> Login.gov is really secure and trustworthy</li>
<li><strong>One single source for signing in:</strong> You can use Login.gov for other services within the federal government</li>
<li><strong>2FA flexibility:</strong> Login.gov supports multiple methods for users to verify their identity.</li>
</ul>
<p>What do I need to do?</p>
<ul class="usa-list">
<li>If you have a Login.gov account, start using it to sign in to Notify today.</li>
<li>If you dont have a Login.gov account, you must create one by April 16, 2024 to continue to access Notify.</li>
</ul>
<div class="border-bottom border-base-lighter margin-y-4"></div>
<a class="usa-link usa-button margin-bottom-3" href="{{ initial_signin_url }}">Create Login.gov account</a>
</div>
</div>
{% endif %}
{% endblock %}

View File

@@ -14,7 +14,7 @@
{% if show_redaction_message %}
<div class="bottom-gutter">
{% call banner_wrapper(type='dangerous', subhead='Are you sure you want to hide personalization after sending?') %}
{% call banner_wrapper(type='dangerous', subhead='Are you sure you want to hide all personalized and conditional content after sending for increased privacy protection?') %}
<ul class="list list-bullet">
<li>
You will not be able to see personalized content in Notify for this template
@@ -56,18 +56,31 @@
{{ template._template.updated_at|format_delta }}
</time>
</h2>
<a class="usa-link margin-right-3" href="{{ url_for('.view_template_versions', service_id=current_service.id, template_id=template.id) }}">See previous versions</a>
{% endif %}
{% if current_user.has_permissions('manage_templates') and user_has_template_permission %}
{% if not template._template.archived %}
<a class="usa-link margin-right-3" href="{{ url_for('.delete_service_template', service_id=current_service.id, template_id=template.id) }}">Delete this template</a>
{% endif %}
{% if not template._template.redact_personalisation %}
<a class="usa-link" href="{{ url_for('.confirm_redact_template', service_id=current_service.id, template_id=template.id) }}">Hide personalization after sending</a>
{% else %}
<h3 class="margin-bottom-0">More actions</h3>
<ul class="usa-list usa-list--unstyled">
<li>
<a class="usa-link margin-right-3"
href="{{ url_for('.view_template_versions', service_id=current_service.id, template_id=template.id) }}">See previous
versions</a>
</li>
{% endif %}
{% if current_user.has_permissions('manage_templates') and user_has_template_permission %}
{% if not template._template.archived %}
<li>
<a class="usa-link margin-right-3"
href="{{ url_for('.delete_service_template', service_id=current_service.id, template_id=template.id) }}">Delete this
template</a>
</li>
{% endif %}
{% if not template._template.redact_personalisation %}
<li>
<a class="usa-link" href="{{ url_for('.confirm_redact_template', service_id=current_service.id, template_id=template.id) }}">Hide all personalized and conditional content after sending</a> for increased privacy protection
</li>
{% else %}
<p class="hint">Personalization is hidden after sending</p>
{% endif %}
{% endif %}
</ul>
</div>
<!--<div class="">

View File

@@ -21,9 +21,6 @@
<div class='grid-row'>
<div class='grid-col-12'>
<h2 class="heading-small margin-bottom-1">Daily messages across all services</h2>
<p class="margin-0">You have sent {{ global_message_limit - daily_global_messages_remaining }} of your {{ global_message_limit }} daily messages allowance.</p>
<p class="margin-0"></p>You have {{ daily_global_messages_remaining }} messages remaining.</p>
<h2 class='heading-small margin-bottom-1'>Text message parts</h2>
<div class="keyline-block">
You have sent
@@ -82,8 +79,8 @@
empty_message='',
field_headings=[
'By month',
hidden_field_heading('Text messages'),
hidden_field_heading('Cost'),
'Text message parts',
'Total message allowance',
],
field_headings_visible=True
) %}
@@ -109,14 +106,9 @@
{% endcall %}
{% endfor %}
{% call field(align='left') %}
{{ big_number(
item.sms_cost,
currency="$",
smallest=True
) }}
<ul>
{% if item.sms_free_allowance_used %}
<li class="tabular-numbers">{{ item.sms_free_allowance_used|format_thousands }} free {{ item.sms_free_count|message_count_label('sms', suffix='') }}</li>
<li class="tabular-numbers">{{ item.sms_free_allowance_used|format_thousands }} total {{ item.sms_free_count|message_count_label('parts', suffix='') }}</li>
{% endif %}
{% for sms in item.sms_breakdown %}
<li class="tabular-numbers">{{ sms.charged_units|message_count('sms') }} at

View File

@@ -3,11 +3,12 @@ from itertools import chain
from flask import abort, g, make_response, request
from flask_login import current_user
from notifications_utils.field import Field
from orderedset._orderedset import OrderedSet
from ordered_set import OrderedSet
from werkzeug.datastructures import MultiDict
from werkzeug.routing import RequestRedirect
from notifications_utils.field import Field
SENDING_STATUSES = ["created", "pending", "sending"]
DELIVERED_STATUSES = ["delivered", "sent"]
FAILURE_STATUSES = [

Some files were not shown because too many files have changed in this diff Show More