Compare commits

..

345 Commits

Author SHA1 Message Date
ecayer
ad8bdc5fdf Create retrospective.md
Shell for summary of notifications and codebase recs.
2025-03-13 10:43:24 -07:00
ccostino
ff65471084 Merge pull request #2371 from GSA/2355-updateremove-unnecessary-componentsmacros-that-is-using-ajax-to-call-to-servers
remove dead code from dashboard
2025-03-12 16:25:16 -04:00
Beverly Nguyen
310ec3eae6 merge main 2025-03-12 13:17:03 -07:00
Beverly Nguyen
de67e441f9 removing comments 2025-03-12 12:11:46 -07:00
Beverly Nguyen
e3b35b5b83 quick update to imports and variables used 2025-03-12 10:08:48 -07:00
Beverly Nguyen
dd7d32e51c remove extra code 2025-03-11 12:45:29 -07:00
Jonathan Bobel
a5d66f02b5 Merge pull request #2356 from GSA/2214-client-side-validation
2214 client side validation
2025-03-11 14:45:51 -04:00
Jonathan Bobel
505e5792d2 Merge pull request #2273 from GSA/2200-contextual-tooltips
2200 - Adding tooltip to Total messages header for explanation
2025-03-11 14:42:43 -04:00
Jonathan Bobel
f73fe7fc9a Update _data-visualization.scss
Removing commented code
2025-03-11 14:09:22 -04:00
Jonathan Bobel
aa3d5757f1 Merge branch 'main' of https://github.com/GSA/notifications-admin into 2200-contextual-tooltips 2025-03-11 12:52:17 -04:00
Beverly Nguyen
85ae22a81a Merge pull request #2390 from GSA/2043-soften-our-word-choice-charge-since-we-are-not-making-people-pay-yet
Updated copy to be more verbose
2025-03-11 09:49:07 -07:00
Jonathan Bobel
ae619fff64 Merge branch 'main' of https://github.com/GSA/notifications-admin into 2200-contextual-tooltips 2025-03-11 12:48:49 -04:00
ccostino
9f88a3d84f Merge pull request #2349 from GSA/dependabot/pip/pre-commit-4.1.0
Bump pre-commit from 3.8.0 to 4.1.0
2025-03-11 11:52:57 -04:00
alexjanousekGSA
f828193a5c Updated copy to be more verbose 2025-03-11 11:46:46 -04:00
dependabot[bot]
bc9d8969fb Bump pre-commit from 3.8.0 to 4.1.0
Bumps [pre-commit](https://github.com/pre-commit/pre-commit) from 3.8.0 to 4.1.0.
- [Release notes](https://github.com/pre-commit/pre-commit/releases)
- [Changelog](https://github.com/pre-commit/pre-commit/blob/main/CHANGELOG.md)
- [Commits](https://github.com/pre-commit/pre-commit/compare/v3.8.0...v4.1.0)

---
updated-dependencies:
- dependency-name: pre-commit
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-11 14:35:46 +00:00
ccostino
7ab59b9d4a Merge pull request #2333 from GSA/dependabot/pip/numpy-2.2.3
Bump numpy from 1.26.4 to 2.2.3
2025-03-11 10:33:55 -04:00
dependabot[bot]
b7673ae673 Bump numpy from 1.26.4 to 2.2.3
Bumps [numpy](https://github.com/numpy/numpy) from 1.26.4 to 2.2.3.
- [Release notes](https://github.com/numpy/numpy/releases)
- [Changelog](https://github.com/numpy/numpy/blob/main/doc/RELEASE_WALKTHROUGH.rst)
- [Commits](https://github.com/numpy/numpy/compare/v1.26.4...v2.2.3)

---
updated-dependencies:
- dependency-name: numpy
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-11 14:16:14 +00:00
Kenneth Kehl
cee9b751bf Merge pull request #2389 from GSA/dependabot/npm_and_yarn/rollup-4.35.0
Bump rollup from 4.34.9 to 4.35.0
2025-03-11 07:14:38 -07:00
Kenneth Kehl
7b39aa4761 Merge pull request #2387 from GSA/dependabot/pip/python-json-logger-3.3.0
Bump python-json-logger from 2.0.7 to 3.3.0
2025-03-11 07:14:12 -07:00
dependabot[bot]
6a224528a5 Bump rollup from 4.34.9 to 4.35.0
Bumps [rollup](https://github.com/rollup/rollup) from 4.34.9 to 4.35.0.
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v4.34.9...v4.35.0)

---
updated-dependencies:
- dependency-name: rollup
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-11 00:28:56 +00:00
Beverly Nguyen
56440b4477 Merge branch 'main' into 2355-updateremove-unnecessary-componentsmacros-that-is-using-ajax-to-call-to-servers 2025-03-10 16:54:30 -07:00
Beverly Nguyen
0a75856c2d fix unit testing 2025-03-10 16:54:02 -07:00
dependabot[bot]
59da570302 Bump python-json-logger from 2.0.7 to 3.3.0
Bumps [python-json-logger](https://github.com/nhairs/python-json-logger) from 2.0.7 to 3.3.0.
- [Release notes](https://github.com/nhairs/python-json-logger/releases)
- [Changelog](https://github.com/nhairs/python-json-logger/blob/main/docs/changelog.md)
- [Commits](https://github.com/nhairs/python-json-logger/compare/v2.0.7...v3.3.0)

---
updated-dependencies:
- dependency-name: python-json-logger
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-10 18:14:16 +00:00
ccostino
714473b829 Merge pull request #2388 from GSA/cant_change_phone_number
fix code alignment issue resulting in missing endpoint
2025-03-10 14:12:10 -04:00
Kenneth Kehl
ee04cae75c merge from main 2025-03-10 09:59:20 -07:00
Alex Janousek
113f86b312 Merge pull request #2386 from GSA/dependabot/npm_and_yarn/uswds/uswds-3.12.0
Bump @uswds/uswds from 3.11.0 to 3.12.0
2025-03-10 12:17:59 -04:00
Kenneth Kehl
224c25a193 fix code alignment issue resulting in missing endpoint 2025-03-10 08:22:49 -07:00
dependabot[bot]
e6ded78be8 Bump @uswds/uswds from 3.11.0 to 3.12.0
Bumps [@uswds/uswds](https://github.com/uswds/uswds) from 3.11.0 to 3.12.0.
- [Release notes](https://github.com/uswds/uswds/releases)
- [Commits](https://github.com/uswds/uswds/compare/v3.11.0...v3.12.0)

---
updated-dependencies:
- dependency-name: "@uswds/uswds"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-07 23:42:00 +00:00
ccostino
f6952b28de Merge pull request #2381 from GSA/2307-update-message-part-content-on-trial-mode-page
Copy updates
2025-03-07 09:09:52 -05:00
Alex Janousek
3408cf0bd8 Merge pull request #2383 from GSA/dependabot/npm_and_yarn/playwright-1.51.0
Bump playwright from 1.50.1 to 1.51.0
2025-03-07 08:48:26 -05:00
Alex Janousek
8f0daa19b8 Merge pull request #2384 from GSA/dependabot/npm_and_yarn/rollup/plugin-commonjs-28.0.3
Bump @rollup/plugin-commonjs from 28.0.2 to 28.0.3
2025-03-07 08:48:10 -05:00
dependabot[bot]
59f766bb1e Bump @rollup/plugin-commonjs from 28.0.2 to 28.0.3
Bumps [@rollup/plugin-commonjs](https://github.com/rollup/plugins/tree/HEAD/packages/commonjs) from 28.0.2 to 28.0.3.
- [Changelog](https://github.com/rollup/plugins/blob/master/packages/commonjs/CHANGELOG.md)
- [Commits](https://github.com/rollup/plugins/commits/commonjs-v28.0.3/packages/commonjs)

---
updated-dependencies:
- dependency-name: "@rollup/plugin-commonjs"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-06 23:56:27 +00:00
dependabot[bot]
ec2b55c171 Bump playwright from 1.50.1 to 1.51.0
Bumps [playwright](https://github.com/microsoft/playwright) from 1.50.1 to 1.51.0.
- [Release notes](https://github.com/microsoft/playwright/releases)
- [Commits](https://github.com/microsoft/playwright/compare/v1.50.1...v1.51.0)

---
updated-dependencies:
- dependency-name: playwright
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-06 23:56:15 +00:00
alexjanousekGSA
3fbbc4d57e Removed a word 2025-03-06 15:58:58 -05:00
Jonathan Bobel
32c797e10b Update send.py 2025-03-06 15:56:47 -05:00
alexjanousekGSA
01a940681e Merge branch 'main' into 2307-update-message-part-content-on-trial-mode-page 2025-03-06 15:37:23 -05:00
Jonathan Bobel
bd96cc1d20 Update activityChart.test.js
Hmm this failed locally but not when committed. Reverting.
2025-03-06 15:19:27 -05:00
Jonathan Bobel
71f7ed051c Merge branch 'main' of https://github.com/GSA/notifications-admin into 2214-client-side-validation 2025-03-06 15:14:21 -05:00
Kenneth Kehl
221f1f30fd Merge pull request #2379 from GSA/dependabot/pip/jinja2-3.1.6
Bump jinja2 from 3.1.5 to 3.1.6
2025-03-06 12:13:43 -08:00
Jonathan Bobel
1696614995 Update activityChart.test.js 2025-03-06 15:00:21 -05:00
ccostino
8415665bb1 Merge pull request #2369 from GSA/2305-remove-message-parts-reference-on-contact-us-page
Removed text from logged in contact us page
2025-03-06 12:52:01 -05:00
alexjanousekGSA
0254dc41ca Copy updates 2025-03-06 12:43:23 -05:00
Jonathan Bobel
a18f981b1c Merge branch 'main' of https://github.com/GSA/notifications-admin into 2214-client-side-validation 2025-03-06 12:12:50 -05:00
dependabot[bot]
cb2a7b5716 Bump jinja2 from 3.1.5 to 3.1.6
Bumps [jinja2](https://github.com/pallets/jinja) from 3.1.5 to 3.1.6.
- [Release notes](https://github.com/pallets/jinja/releases)
- [Changelog](https://github.com/pallets/jinja/blob/main/CHANGES.rst)
- [Commits](https://github.com/pallets/jinja/compare/3.1.5...3.1.6)

---
updated-dependencies:
- dependency-name: jinja2
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-06 04:48:03 +00:00
Beverly Nguyen
1f14c1e570 isort 2025-03-05 14:00:05 -08:00
Deana VanCura
7d4e2cce25 Delete unusable media 4 2025-03-05 15:18:09 -05:00
Deana VanCura
e48a90a40c Delete unusable media 3 2025-03-05 15:17:42 -05:00
Deana VanCura
38ef9742c8 Delete unusable media 2 2025-03-05 15:17:24 -05:00
Deana VanCura
7dfb4294e1 Delete unusable media 1 2025-03-05 15:16:28 -05:00
ccostino
5be95b85c1 Merge pull request #2372 from GSA/new-pdf
new static files
2025-03-05 14:35:27 -05:00
Beverly Nguyen
2bec392a59 asap pdf 2025-03-05 10:53:08 -08:00
Beverly Nguyen
dd9cbe2f4b pdf 2025-03-05 10:48:40 -08:00
Deana VanCura
6fff2fb122 Add files via upload 2025-03-05 13:20:51 -05:00
Beverly Nguyen
3f7a84b17c flake8 2025-03-04 21:28:21 -08:00
Beverly Nguyen
e59516562f remove dead code from dashboard 2025-03-04 21:14:53 -08:00
alexjanousekGSA
9e01f3cd24 Removed text 2025-03-04 15:36:50 -05:00
Jonathan Bobel
241cf8c021 Merge branch 'main' of https://github.com/GSA/notifications-admin into 2214-client-side-validation 2025-03-04 15:07:24 -05:00
ccostino
2ef7f2e252 Merge pull request #2367 from GSA/BUG-ubuntu-version-a11y-scan
Update checks.yml
2025-03-04 14:22:33 -05:00
Jonathan Bobel
7144eaa00d Update .pa11yci 2025-03-04 14:04:35 -05:00
Jonathan Bobel
ceb13a9051 Update .pa11yci
Trying a fix for the ubuntu situation
2025-03-04 13:35:37 -05:00
Alex Janousek
e958e097aa Merge pull request #2366 from GSA/dependabot/npm_and_yarn/rollup-4.34.9
Bump rollup from 4.34.8 to 4.34.9
2025-03-04 12:09:28 -05:00
Jonathan Bobel
20012885b1 Update checks.yml 2025-03-04 11:26:21 -05:00
Jonathan Bobel
b1b4611c55 Update checks.yml
Seeing if this fixes the a11y scan
2025-03-04 11:24:50 -05:00
dependabot[bot]
9ae1f40376 Bump rollup from 4.34.8 to 4.34.9
Bumps [rollup](https://github.com/rollup/rollup) from 4.34.8 to 4.34.9.
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v4.34.8...v4.34.9)

---
updated-dependencies:
- dependency-name: rollup
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-04 00:04:45 +00:00
Jonathan Bobel
3dee60fcc0 Merge branch 'main' of https://github.com/GSA/notifications-admin into 2214-client-side-validation 2025-03-03 16:11:05 -05:00
Jonathan Bobel
94f881477e Focusing validation on individual form with one input 2025-03-03 13:33:45 -05:00
Jonathan Bobel
f4ca597964 Updates to validation.js and it's test - all are passing 2025-02-28 11:31:47 -05:00
Jonathan Bobel
a5146fc58a Fixing tests 2025-02-27 14:21:43 -05:00
Beverly Nguyen
0df04cfd89 Merge pull request #2325 from GSA/jb-add-accessibility-documentation
Create manual-a11y-checklist.md
2025-02-27 10:04:49 -08:00
Beverly Nguyen
34f7c6a4da Merge pull request #2361 from GSA/dependabot/npm_and_yarn/sass-embedded-1.85.1
Bump sass-embedded from 1.85.0 to 1.85.1
2025-02-27 10:04:10 -08:00
ccostino
cd78710753 Merge pull request #2359 from GSA/1543-review-and-update-total-messages-chart-on-dashboard-and-usagehtml-with-new-data-from-backend
Total message ratio update
2025-02-27 11:00:49 -05:00
Alex Janousek
102d005e0d Merge pull request #2362 from GSA/feat/fe-tz-change
Added local/utc time conversion for text metrics
2025-02-26 14:59:44 -05:00
Beverly Nguyen
3455235a8b fix test 2025-02-26 10:19:47 -08:00
Jonathan Bobel
f031f4bf18 Update template.njk
Experimenting with setting the "for"
2025-02-26 12:53:06 -05:00
Beverly Nguyen
c93d00dcbe fix test 2025-02-26 09:50:49 -08:00
Jonathan Bobel
ffda07b6bd Update test_tour.py 2025-02-26 12:24:18 -05:00
Jonathan Bobel
e769d421be Update test_tour.py 2025-02-26 12:19:15 -05:00
Jonathan Bobel
0daf140b39 Update test_manage_users.py 2025-02-26 12:09:34 -05:00
Jonathan Bobel
4225559677 Update test_manage_users.py 2025-02-26 11:57:26 -05:00
Jonathan Bobel
69457c873d Update test_manage_users.py 2025-02-26 11:41:30 -05:00
Jonathan Bobel
06c099b567 Update test_manage_users.py 2025-02-26 11:20:04 -05:00
alexjanousekGSA
a524ed0dc5 Fixed a test 2025-02-26 11:16:52 -05:00
alexjanousekGSA
c37bb44e0a Added unit tests for new utc conversion logic 2025-02-26 11:11:47 -05:00
dependabot[bot]
0b3177e3ff Bump sass-embedded from 1.85.0 to 1.85.1
Bumps [sass-embedded](https://github.com/sass/embedded-host-node) from 1.85.0 to 1.85.1.
- [Changelog](https://github.com/sass/embedded-host-node/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sass/embedded-host-node/compare/1.85.0...1.85.1)

---
updated-dependencies:
- dependency-name: sass-embedded
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-25 23:49:03 +00:00
Beverly Nguyen
baf9b69fbb fix pytest 2025-02-25 14:01:56 -08:00
Beverly Nguyen
df3e2264ec flake8 2025-02-25 13:19:59 -08:00
Beverly Nguyen
79192e29b6 fix jest testing 2025-02-25 13:12:55 -08:00
Jonathan Bobel
ed79352201 Update test_manage_users.py
Removed print
2025-02-25 14:18:51 -05:00
Jonathan Bobel
df4979277a Adjusting tests 2025-02-25 13:58:49 -05:00
Jonathan Bobel
becdfc79d2 Update test_service_settings.py
Removing a print
2025-02-25 12:47:54 -05:00
Jonathan Bobel
951cb0fef9 Update test_activity.py 2025-02-25 12:28:39 -05:00
Jonathan Bobel
2b07517e9c Fixing tests 2025-02-25 12:23:56 -05:00
Jonathan Bobel
bb0d9a25b7 Update __init__.py
Fixing import
2025-02-25 11:36:53 -05:00
Jonathan Bobel
dc1bff6d19 Update __init__.py
Moved the import statement
2025-02-25 11:14:15 -05:00
Beverly Nguyen
5cede78661 Merge branch 'main' into 1543-review-and-update-total-messages-chart-on-dashboard-and-usagehtml-with-new-data-from-backend 2025-02-24 14:49:28 -08:00
Beverly Nguyen
5c9d190021 total message ratio update 2025-02-24 14:44:43 -08:00
Beverly Nguyen
61fb59f1cb total message ratio update 2025-02-24 14:08:26 -08:00
Jonathan Bobel
2bb318493a Fixing tests, adding dynamic slug 2025-02-24 16:11:42 -05:00
Jonathan Bobel
b6f341ee04 Update test_organizations.py
Fixing tests
2025-02-24 15:34:52 -05:00
Jonathan Bobel
91c2ae6733 Update template.njk
Reverting this change
2025-02-24 15:27:51 -05:00
Jonathan Bobel
0fa8c571c8 Update test_organizations.py
Fixing a test
2025-02-24 14:50:29 -05:00
Jonathan Bobel
70dae9c208 Update test_organizations.py
Adjusting a test
2025-02-24 14:18:00 -05:00
Jonathan Bobel
46524aa5f8 Merge branch 'main' of https://github.com/GSA/notifications-admin into 2214-client-side-validation 2025-02-24 12:30:19 -05:00
Jonathan Bobel
adab5f1338 2214 Client-side validation 2025-02-24 12:29:58 -05:00
Kenneth Kehl
02f39bd077 Merge pull request #2354 from GSA/fix_runaway_report_generation
fix runaway report generation
2025-02-21 10:13:37 -08:00
Kenneth Kehl
1eb12b93f1 fix runaway report generation 2025-02-21 09:22:29 -08:00
ccostino
f9fd580c58 Merge pull request #2350 from GSA/fix-terraform-actions
Update Terraform installation and configuration
2025-02-20 10:52:30 -05:00
Carlo Costino
fe6921e243 Update Terraform installation and configuration
This changeset accounts for having to explicitly install Terraform and updates our user configuration in several environments to account for team member changes.

Signed-off-by: Carlo Costino <carlo.costino@gsa.gov>
2025-02-19 21:41:32 -05:00
Beverly Nguyen
d20f887f0b Merge pull request #2346 from GSA/update-github-actions 2025-02-19 14:24:01 -08:00
Carlo Costino
eadd8dddb5 Update GitHub Action References
This changeset updates many of our GitHub Action references to point to the latest versions to ensure they are kept up-to-date.  This helps address any improvements and security patches that have been made to them.

Signed-off-by: Carlo Costino <carlo.costino@gsa.gov>
2025-02-19 16:56:09 -05:00
ccostino
77248b8bf0 Merge pull request #2345 from GSA/2344-reverting-back-100000-messages-to-250000-messages-in-front-of-login
reverting back to 250,000
2025-02-19 14:23:59 -05:00
Andrew Shumway
a8d036a3e5 Merge pull request #2340 from GSA/dependabot/pip/pyproj-3.7.1
Bump pyproj from 3.6.1 to 3.7.1
2025-02-19 12:22:49 -07:00
dependabot[bot]
05a746a042 Bump pyproj from 3.6.1 to 3.7.1
Bumps [pyproj](https://github.com/pyproj4/pyproj) from 3.6.1 to 3.7.1.
- [Release notes](https://github.com/pyproj4/pyproj/releases)
- [Changelog](https://github.com/pyproj4/pyproj/blob/main/docs/history.rst)
- [Commits](https://github.com/pyproj4/pyproj/compare/3.6.1...3.7.1)

---
updated-dependencies:
- dependency-name: pyproj
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-19 18:58:30 +00:00
Andrew Shumway
84acce48c4 Merge pull request #2335 from GSA/dependabot/pip/newrelic-10.6.0
Bump newrelic from 10.2.0 to 10.6.0
2025-02-19 11:55:21 -07:00
Beverly Nguyen
1bbc49c0ad reverting back to 250,000 2025-02-19 10:06:41 -08:00
Alex Janousek
bcd7ead5ec Merge pull request #2338 from GSA/dependabot/npm_and_yarn/babel/core-7.26.9
Bump @babel/core from 7.26.8 to 7.26.9
2025-02-19 12:52:53 -05:00
ccostino
0405c92f69 Merge pull request #2311 from GSA/2171-clean-up-titles
Pluralization around text, fixing conditionals, making bar chart more visible
2025-02-19 12:45:20 -05:00
Beverly Nguyen
ad1f83e80e Merge branch 'main' into 2171-clean-up-titles 2025-02-19 09:05:16 -08:00
dependabot[bot]
44778349f0 Bump @babel/core from 7.26.8 to 7.26.9
Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.26.8 to 7.26.9.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.26.9/packages/babel-core)

---
updated-dependencies:
- dependency-name: "@babel/core"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-19 16:28:48 +00:00
Alex Janousek
5b46a4e1d7 Merge pull request #2337 from GSA/dependabot/npm_and_yarn/babel/preset-env-7.26.9
Bump @babel/preset-env from 7.26.7 to 7.26.9
2025-02-19 11:27:43 -05:00
Alex Janousek
91bea21607 Merge pull request #2339 from GSA/dependabot/npm_and_yarn/sass-embedded-1.85.0
Bump sass-embedded from 1.83.4 to 1.85.0
2025-02-19 11:27:33 -05:00
Alex Janousek
ab497be1f8 Merge pull request #2341 from GSA/dependabot/npm_and_yarn/rollup-4.34.8
Bump rollup from 4.34.6 to 4.34.8
2025-02-19 11:27:15 -05:00
dependabot[bot]
5b88eefe27 Bump rollup from 4.34.6 to 4.34.8
Bumps [rollup](https://github.com/rollup/rollup) from 4.34.6 to 4.34.8.
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v4.34.6...v4.34.8)

---
updated-dependencies:
- dependency-name: rollup
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-17 23:50:56 +00:00
dependabot[bot]
34e8647fba Bump sass-embedded from 1.83.4 to 1.85.0
Bumps [sass-embedded](https://github.com/sass/embedded-host-node) from 1.83.4 to 1.85.0.
- [Changelog](https://github.com/sass/embedded-host-node/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sass/embedded-host-node/compare/1.83.4...1.85.0)

---
updated-dependencies:
- dependency-name: sass-embedded
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-14 23:48:09 +00:00
dependabot[bot]
2fdedc1b4f Bump @babel/preset-env from 7.26.7 to 7.26.9
Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.26.7 to 7.26.9.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.26.9/packages/babel-preset-env)

---
updated-dependencies:
- dependency-name: "@babel/preset-env"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-14 23:47:50 +00:00
dependabot[bot]
db1f929dba Bump newrelic from 10.2.0 to 10.6.0
Bumps [newrelic](https://github.com/newrelic/newrelic-python-agent) from 10.2.0 to 10.6.0.
- [Release notes](https://github.com/newrelic/newrelic-python-agent/releases)
- [Commits](https://github.com/newrelic/newrelic-python-agent/compare/v10.2.0...v10.6.0)

---
updated-dependencies:
- dependency-name: newrelic
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-13 23:35:51 +00:00
Andrew Shumway
e7953262a3 Merge pull request #2329 from GSA/dependabot/pip/botocore-1.35.99
Bump botocore from 1.35.51 to 1.35.99
2025-02-13 10:44:03 -07:00
dependabot[bot]
89cfbd989d Bump botocore from 1.35.51 to 1.35.99
Bumps [botocore](https://github.com/boto/botocore) from 1.35.51 to 1.35.99.
- [Commits](https://github.com/boto/botocore/compare/1.35.51...1.35.99)

---
updated-dependencies:
- dependency-name: botocore
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-12 21:41:11 +00:00
Andrew Shumway
479cca3adb Merge pull request #2330 from GSA/dependabot/pip/cryptography-44.0.1
Bump cryptography from 43.0.3 to 44.0.1
2025-02-12 14:39:05 -07:00
dependabot[bot]
cc6a66f790 Bump cryptography from 43.0.3 to 44.0.1
Bumps [cryptography](https://github.com/pyca/cryptography) from 43.0.3 to 44.0.1.
- [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pyca/cryptography/compare/43.0.3...44.0.1)

---
updated-dependencies:
- dependency-name: cryptography
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-11 19:17:18 +00:00
Alex Janousek
0c1dd129e0 Merge pull request #2326 from GSA/dependabot/npm_and_yarn/rollup-4.34.6
Bump rollup from 4.34.4 to 4.34.6
2025-02-11 11:41:40 -05:00
Alex Janousek
d70a80f4a6 Merge pull request #2328 from GSA/dependabot/npm_and_yarn/babel/core-7.26.8
Bump @babel/core from 7.26.7 to 7.26.8
2025-02-11 11:41:25 -05:00
dependabot[bot]
78cfafae94 Bump @babel/core from 7.26.7 to 7.26.8
Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.26.7 to 7.26.8.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.26.8/packages/babel-core)

---
updated-dependencies:
- dependency-name: "@babel/core"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-10 23:18:16 +00:00
ccostino
3016aff185 Merge pull request #2261 from GSA/dependabot/pip/vulture-2.14
Bump vulture from 2.13 to 2.14
2025-02-10 16:13:02 -05:00
dependabot[bot]
4726143fb7 Bump vulture from 2.13 to 2.14
Bumps [vulture](https://github.com/jendrikseipp/vulture) from 2.13 to 2.14.
- [Release notes](https://github.com/jendrikseipp/vulture/releases)
- [Changelog](https://github.com/jendrikseipp/vulture/blob/main/CHANGELOG.md)
- [Commits](https://github.com/jendrikseipp/vulture/compare/v2.13...v2.14)

---
updated-dependencies:
- dependency-name: vulture
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-10 19:06:21 +00:00
ccostino
a4963b8263 Merge pull request #2321 from GSA/notify-admin-2315
dynamic scan warnings
2025-02-10 14:05:22 -05:00
ccostino
22f729d652 Merge pull request #2289 from GSA/notify-admin-2286
redis report
2025-02-10 12:45:44 -05:00
dependabot[bot]
38fad44a15 Bump rollup from 4.34.4 to 4.34.6
Bumps [rollup](https://github.com/rollup/rollup) from 4.34.4 to 4.34.6.
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v4.34.4...v4.34.6)

---
updated-dependencies:
- dependency-name: rollup
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-07 23:58:26 +00:00
Jonathan Bobel
c1fa099c12 Create manual-a11y-checklist.md
Adding .md file for a11y documentation
2025-02-07 16:25:11 -05:00
Kenneth Kehl
208891a71a ignore source code disclosure java as a false positive 2025-02-06 11:42:31 -08:00
Kenneth Kehl
afd3d94790 try again 2025-02-06 11:33:58 -08:00
Kenneth Kehl
515419e0a2 try to block serving node files 2025-02-06 11:26:44 -08:00
Kenneth Kehl
d16eb70765 try to fix svg content type 2025-02-06 10:52:53 -08:00
Kenneth Kehl
39fd87064b try to remove comments 2025-02-06 08:25:39 -08:00
Kenneth Kehl
5c6b4132a9 try to remove comments 2025-02-06 08:10:15 -08:00
Alex Janousek
b12f124b62 Merge pull request #2322 from GSA/dependabot/npm_and_yarn/rollup-4.34.4
Bump rollup from 4.34.1 to 4.34.4
2025-02-06 07:45:49 -05:00
dependabot[bot]
af8f8a9c17 Bump rollup from 4.34.1 to 4.34.4
Bumps [rollup](https://github.com/rollup/rollup) from 4.34.1 to 4.34.4.
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v4.34.1...v4.34.4)

---
updated-dependencies:
- dependency-name: rollup
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-05 23:35:14 +00:00
Kenneth Kehl
23e10b1529 tuple 2025-02-05 10:56:32 -08:00
Kenneth Kehl
545bf10e0d tuple 2025-02-05 10:47:25 -08:00
Kenneth Kehl
12a2290ea9 dynamic scan warnings 2025-02-05 10:35:30 -08:00
Alex Janousek
5ffb4ddacf Merge pull request #2319 from GSA/dependabot/npm_and_yarn/rollup-4.34.1
Bump rollup from 4.32.0 to 4.34.1
2025-02-04 11:30:10 -05:00
Alex Janousek
0a538c4c1d Merge pull request #2317 from GSA/dependabot/npm_and_yarn/playwright-1.50.1
Bump playwright from 1.50.0 to 1.50.1
2025-02-04 11:29:56 -05:00
dependabot[bot]
d29260d7b6 Bump rollup from 4.32.0 to 4.34.1
Bumps [rollup](https://github.com/rollup/rollup) from 4.32.0 to 4.34.1.
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v4.32.0...v4.34.1)

---
updated-dependencies:
- dependency-name: rollup
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-03 23:46:04 +00:00
Kenneth Kehl
bd619af421 merge from main 2025-02-03 09:01:12 -08:00
dependabot[bot]
91faaebcfd Bump playwright from 1.50.0 to 1.50.1
Bumps [playwright](https://github.com/microsoft/playwright) from 1.50.0 to 1.50.1.
- [Release notes](https://github.com/microsoft/playwright/releases)
- [Commits](https://github.com/microsoft/playwright/compare/v1.50.0...v1.50.1)

---
updated-dependencies:
- dependency-name: playwright
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-31 23:50:58 +00:00
Kenneth Kehl
9e1ec466f5 Merge pull request #2313 from GSA/enable-about-page-in-dynamic-scans
Update zaproxy-baseline GitHub Action reference
2025-01-31 08:44:38 -08:00
Carlo Costino
d627ba62de Update reference in daily check action
Signed-off-by: Carlo Costino <carlo.costino@gsa.gov>
2025-01-31 11:08:48 -05:00
Carlo Costino
0ba489df78 Update reference to zaproxy/action-baseline to 0.14.0
Signed-off-by: Carlo Costino <carlo.costino@gsa.gov>
2025-01-31 10:46:32 -05:00
Carlo Costino
4ae351b1f6 This changeset explicitly enables the FEATURE_ABOUT_PAGE_ENABLED feature flag for our dynamic scans to make sure that all pages are scanned, regardless if they are enabled on our production site or not.
Signed-off-by: Carlo Costino <carlo.costino@gsa.gov>
2025-01-30 16:32:12 -05:00
Beverly Nguyen
704fe322ba Merge pull request #2300 from GSA/2125-send-message-a11y-audit---unique-ids
Send Message A11Y Audit - Unique IDs and Required Field
2025-01-30 12:07:45 -08:00
Kenneth Kehl
41001b7f1b Merge pull request #2309 from GSA/clean_log_messages
clean log messages
2025-01-30 11:11:20 -08:00
Beverly Nguyen
2d896daa0e Merge branch 'main' into 2171-clean-up-titles 2025-01-30 10:24:54 -08:00
Beverly Nguyen
6ccf1bb983 fixed arrived from preview page 2025-01-30 10:24:11 -08:00
Beverly Nguyen
5020964fb1 height more visible for stacked bars 2025-01-30 09:56:53 -08:00
Carlo Costino
653f3f5644 Merge pull request #2293 from GSA/sheevdave-patch-3
Update issue_template.yml
2025-01-30 12:45:31 -05:00
Sheev Davé
057ed6f88c Update .github/ISSUE_TEMPLATE/issue_template.yml
Co-authored-by: Carlo Costino <ccostino@users.noreply.github.com>
2025-01-30 09:34:56 -08:00
Beverly Nguyen
5f8d5996aa cleaning up dup codes 2025-01-29 18:23:26 -08:00
Beverly Nguyen
229842b8c9 pluralization and fixing conditionals 2025-01-29 16:32:21 -08:00
Sheev Davé
3e379159a5 Merge pull request #2292 from GSA/sheevdave-patch-2
Update pull_request_template.md
2025-01-29 14:56:36 -08:00
Kenneth Kehl
4feace4cdc code review feedback 2025-01-29 13:51:17 -08:00
Kenneth Kehl
48b317ed52 format 2025-01-29 13:49:58 -08:00
Kenneth Kehl
709b7049c6 code review feedback 2025-01-29 13:22:22 -08:00
Kenneth Kehl
9630d085f4 Update .github/workflows/checks.yml
Co-authored-by: Carlo Costino <ccostino@users.noreply.github.com>
2025-01-29 13:21:28 -08:00
Kenneth Kehl
adf9c61002 clean log messages 2025-01-29 10:45:14 -08:00
Beverly Nguyen
6ea4da2617 Adding back classes 2025-01-28 16:11:07 -08:00
Beverly Nguyen
6c3d6ee77e Merge pull request #2250 from GSA/2199-add-pending-message-data-to-daily-and-user_daily-stats
Add pending to dashboard
2025-01-28 13:38:31 -08:00
Alex Janousek
088da55bb6 Merge pull request #2295 from GSA/dependabot/npm_and_yarn/babel/preset-env-7.26.7
Bump @babel/preset-env from 7.26.0 to 7.26.7
2025-01-28 12:45:16 -05:00
Beverly Nguyen
88b114f58b test 2025-01-27 19:19:41 -08:00
Beverly Nguyen
7776f9900a added id and required field 2025-01-27 19:15:37 -08:00
Sheev Davé
22fcc703f5 Update issue_template.yml 2025-01-27 12:53:47 -08:00
Beverly Nguyen
a5e2fd4e21 Merge pull request #2271 from GSA/1706-updaterevise-status-banner-on-confirmation-page 2025-01-27 09:35:48 -08:00
dependabot[bot]
03984bc10b Bump @babel/preset-env from 7.26.0 to 7.26.7
Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.26.0 to 7.26.7.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.26.7/packages/babel-preset-env)

---
updated-dependencies:
- dependency-name: "@babel/preset-env"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-27 14:16:51 +00:00
Alex Janousek
db13416e06 Merge pull request #2296 from GSA/dependabot/npm_and_yarn/babel/core-7.26.7
Bump @babel/core from 7.26.0 to 7.26.7
2025-01-27 09:15:43 -05:00
Alex Janousek
cc92642fa5 Merge pull request #2297 from GSA/dependabot/npm_and_yarn/rollup-4.32.0
Bump rollup from 4.31.0 to 4.32.0
2025-01-27 09:15:30 -05:00
dependabot[bot]
53b1e12b56 Bump rollup from 4.31.0 to 4.32.0
Bumps [rollup](https://github.com/rollup/rollup) from 4.31.0 to 4.32.0.
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v4.31.0...v4.32.0)

---
updated-dependencies:
- dependency-name: rollup
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-24 23:06:57 +00:00
dependabot[bot]
296a964913 Bump @babel/core from 7.26.0 to 7.26.7
Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.26.0 to 7.26.7.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.26.7/packages/babel-core)

---
updated-dependencies:
- dependency-name: "@babel/core"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-24 23:06:49 +00:00
Alex Janousek
c6dfc6c558 Merge pull request #2294 from GSA/dependabot/npm_and_yarn/playwright-1.50.0
Bump playwright from 1.49.1 to 1.50.0
2025-01-24 10:55:35 -05:00
dependabot[bot]
1566198343 Bump playwright from 1.49.1 to 1.50.0
Bumps [playwright](https://github.com/microsoft/playwright) from 1.49.1 to 1.50.0.
- [Release notes](https://github.com/microsoft/playwright/releases)
- [Commits](https://github.com/microsoft/playwright/compare/v1.49.1...v1.50.0)

---
updated-dependencies:
- dependency-name: playwright
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-23 23:10:47 +00:00
Beverly Nguyen
fc755c1b85 test 2025-01-23 14:03:59 -08:00
Beverly Nguyen
933348baa5 Fix jest 2025-01-23 13:24:11 -08:00
Sheev Davé
4e335561e3 Update issue_template.yml
added "This includes items related to accessibility." to 3 sections
2025-01-23 11:57:37 -08:00
Beverly Nguyen
cae2ec1a6d refactor naming 2025-01-23 11:53:27 -08:00
Beverly Nguyen
8f30f9320a Merge branch 'main' into 2199-add-pending-message-data-to-daily-and-user_daily-stats 2025-01-23 11:26:53 -08:00
Beverly Nguyen
954f1ccb55 Merge pull request #2266 from GSA/2168-when-a-user-schedules-a-job-they-should-see-scheduled-instead-of-sent-on-the-service-activity-page
Updating/Fix job status states
2025-01-23 11:21:04 -08:00
Beverly Nguyen
dafe86aad6 Merge pull request #2290 from GSA/2234-relabel-guidance-to-how-to
2234 relabel guidance to how to
2025-01-22 14:55:36 -08:00
Kenneth Kehl
5d565ab88b fix black and isort forever 2025-01-22 13:45:42 -08:00
Kenneth Kehl
cfb966ce53 redis report 2025-01-22 13:34:34 -08:00
Sheev Davé
c47033af7f Update pull_request_template.md
added more a11y specific checks
2025-01-22 13:23:05 -08:00
Beverly Nguyen
aa03fc1cc0 Sentence case 2025-01-22 10:55:42 -08:00
Beverly Nguyen
cb41d9e706 changing guidance to how-do 2025-01-22 10:41:29 -08:00
Beverly Nguyen
0dfad834bf Merge pull request #2288 from GSA/update-join-notify-template 2025-01-22 08:45:23 -08:00
Beverly Nguyen
86dfdb66df Merge pull request #2287 from GSA/revert-2206-2125-send-message-a11y-audit-unique-ids 2025-01-22 08:44:47 -08:00
Kenneth Kehl
a4f6dd7100 redis report 2025-01-22 08:44:34 -08:00
Carlo Costino
17c6cd6e03 Update no-cost service details
This changeset updates the no-cost service details to show the correct amount of messages for the first year (100,000 instead of 250,000).

Signed-off-by: Carlo Costino <carlo.costino@gsa.gov>
2025-01-22 10:32:28 -05:00
Beverly Nguyen
084a5e37fc Revert "Send Message A11Y Audit - Unique IDs and also refactor error message on templates page" 2025-01-21 22:20:44 -08:00
Beverly Nguyen
56af19637e Merge pull request #2283 from GSA/sheevdave-patch-1
Update pull_request_template.md
2025-01-21 16:43:47 -08:00
Beverly Nguyen
38675e88dd jest 2025-01-21 16:17:44 -08:00
Beverly Nguyen
fb1609e71c merge main 2025-01-21 16:12:21 -08:00
Beverly Nguyen
5cedf04269 Merge branch 'main' into 2199-add-pending-message-data-to-daily-and-user_daily-stats 2025-01-21 16:12:12 -08:00
Beverly Nguyen
6615d4fccb Merge pull request #2206 from GSA/2125-send-message-a11y-audit-unique-ids
Send Message A11Y Audit - Unique IDs and also refactor error message on templates page
2025-01-21 15:30:13 -08:00
Beverly Nguyen
24cad0f8a1 fix test 2025-01-21 14:49:26 -08:00
Carlo Costino
4a773a5917 Merge pull request #2284 from GSA/bugfix/copy-change
Reverted change that accidentally pushed to main
2025-01-21 16:14:27 -05:00
alexjanousekGSA
16b43fa5bc Removed whitespace to trigger file change for PR 2025-01-21 16:03:50 -05:00
alexjanousekGSA
564db4cd84 Removing redundant text 2025-01-21 16:01:46 -05:00
alexjanousekGSA
561b2a345c Reverted change that accidentally pushed to main 2025-01-21 16:00:59 -05:00
alexjanousekGSA
5cde70a465 Removed redundant customize copy 2025-01-21 15:36:18 -05:00
Sheev Davé
ac79edf613 Update pull_request_template.md
Added a11y checks to PR template
2025-01-21 12:03:35 -08:00
Beverly Nguyen
2ab7244beb fix test and arrival session 2025-01-21 10:46:30 -08:00
Alex Janousek
8248833645 Merge pull request #2279 from GSA/dependabot/npm_and_yarn/rollup-4.31.0
Bump rollup from 4.30.1 to 4.31.0
2025-01-21 11:41:56 -05:00
Beverly Nguyen
f91af94001 removed comments 2025-01-20 16:00:21 -08:00
Beverly Nguyen
7be1872993 clean up templates 2025-01-20 15:21:32 -08:00
dependabot[bot]
261c2311bd Bump rollup from 4.30.1 to 4.31.0
Bumps [rollup](https://github.com/rollup/rollup) from 4.30.1 to 4.31.0.
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v4.30.1...v4.31.0)

---
updated-dependencies:
- dependency-name: rollup
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-20 23:15:43 +00:00
Beverly Nguyen
82230f9e8b fixed testing 2025-01-20 14:34:11 -08:00
Beverly Nguyen
b6b110765a flake8 and fixing conditionals 2025-01-20 14:05:15 -08:00
Beverly Nguyen
f13595fd90 merge main 2025-01-20 12:52:51 -08:00
Jonathan Bobel
fafc1a1bfb Merge branch 'main' of https://github.com/GSA/notifications-admin into 2200-contextual-tooltips 2025-01-20 14:41:15 -05:00
Kenneth Kehl
028a154ea7 Merge pull request #2268 from GSA/notify-admin-2245
get rid of some noise in status updates
2025-01-20 11:27:09 -08:00
Beverly Nguyen
3f1058f9b6 jest testing 2025-01-17 17:03:22 -08:00
Beverly Nguyen
9942ecf53e ajax applied to status 2025-01-17 16:30:59 -08:00
Beverly Nguyen
0168fc02df Merge pull request #2276 from GSA/2274-remove-in-a-bubble-text-from-why-text-messaging
adjust styling and content cards
2025-01-17 13:58:25 -08:00
Beverly Nguyen
b40999c646 adjust styling and content cards 2025-01-17 13:42:59 -08:00
Jonathan Bobel
935c5fd62c Adding sr-only context to the tooltip 2025-01-17 13:54:13 -05:00
Jonathan Bobel
565e6ad509 2200 - Adding tooltip to Total messages header for explanation 2025-01-17 13:38:36 -05:00
Beverly Nguyen
7422b6160a Reformat 2025-01-16 18:22:13 -08:00
Beverly Nguyen
839bd0b3a9 remove service 2025-01-16 18:20:32 -08:00
Beverly Nguyen
79e8f02ec6 refactor code partial 2025-01-16 18:17:27 -08:00
Beverly Nguyen
dfe421b361 refactor code 2025-01-16 17:33:55 -08:00
Beverly Nguyen
7c5179994b update verbage 2025-01-16 12:09:09 -08:00
Beverly Nguyen
c6ed4a8661 changed pending state 2025-01-16 12:05:14 -08:00
Kenneth Kehl
1c77f8d60f fix test 2025-01-16 09:27:32 -08:00
Kenneth Kehl
6f7202eb30 fix test 2025-01-16 09:02:48 -08:00
Kenneth Kehl
3e8015ed50 get rid of some noise in status updates 2025-01-16 08:38:51 -08:00
Beverly Nguyen
ae083f4b52 update banner with different status states 2025-01-15 17:08:21 -08:00
Beverly Nguyen
8754c03a00 updating job status states 2025-01-15 14:28:47 -08:00
Beverly Nguyen
607e112905 updated fetching interval 2025-01-15 12:07:40 -08:00
Beverly Nguyen
92935867ea Merge pull request #2247 from GSA/2227-acceptable-use-policy
2227 - Adding Acceptable Use Policy page
2025-01-15 10:12:33 -08:00
Beverly Nguyen
b8252aec66 Merge pull request #2264 from GSA/jb-add-link-to-about-studio
Added link to /studio and made some small style updates
2025-01-15 09:52:39 -08:00
Alex Janousek
920c9f4d33 Merge pull request #2263 from GSA/dependabot/npm_and_yarn/sass-embedded-1.83.4
Bump sass-embedded from 1.83.1 to 1.83.4
2025-01-15 12:47:13 -05:00
Jonathan Bobel
f5fe608f41 Added link to /studio and made some small style updates 2025-01-15 12:15:05 -05:00
dependabot[bot]
31c67abd36 Bump sass-embedded from 1.83.1 to 1.83.4
Bumps [sass-embedded](https://github.com/sass/embedded-host-node) from 1.83.1 to 1.83.4.
- [Changelog](https://github.com/sass/embedded-host-node/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sass/embedded-host-node/compare/1.83.1...1.83.4)

---
updated-dependencies:
- dependency-name: sass-embedded
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-14 23:23:07 +00:00
Beverly Nguyen
cd90682958 fixed drop down 2025-01-14 13:49:40 -08:00
Beverly Nguyen
5e61fb831d jest test 2025-01-14 13:10:48 -08:00
Beverly Nguyen
82179c0ad8 Merge branch 'main' into 2199-add-pending-message-data-to-daily-and-user_daily-stats 2025-01-14 12:20:13 -08:00
Beverly Nguyen
5c0eb91dd4 poetry 2025-01-14 12:14:35 -08:00
Beverly Nguyen
2159a9ddc5 clean up 2025-01-14 11:48:23 -08:00
Jonathan Bobel
b926b2da6f Merge pull request #2256 from GSA/feature/sidenav-expand-sublinks
Updated code to allow sidenav expand on click
2025-01-14 14:08:38 -05:00
alexjanousekGSA
dadfbc81e9 More content changes 2025-01-14 13:45:36 -05:00
alexjanousekGSA
8269fbbfa2 More content updates 2025-01-14 13:42:29 -05:00
alexjanousekGSA
ac211f3da1 Fixed one minor active state bug 2025-01-14 12:56:32 -05:00
alexjanousekGSA
a4968eae83 Added new backstop reference files 2025-01-14 12:38:44 -05:00
alexjanousekGSA
5a4021f625 Fixed last test hopefully 2025-01-14 12:10:23 -05:00
Jonathan Bobel
ab911ce942 Merge branch 'feature/sidenav-expand-sublinks' of https://github.com/GSA/notifications-admin into feature/sidenav-expand-sublinks 2025-01-14 09:55:56 -05:00
alexjanousekGSA
d5f4301747 More test fixes 2025-01-14 09:55:44 -05:00
alexjanousekGSA
cdf5d56938 Fixed more tests and pr comments 2025-01-14 09:30:52 -05:00
alexjanousekGSA
9666740af9 Fixing tests 2025-01-14 09:17:01 -05:00
Beverly Nguyen
ce87e2162b clean up 2025-01-13 22:40:58 -08:00
Beverly Nguyen
6d008d4e45 clean up 2025-01-13 15:54:04 -08:00
Jonathan Bobel
872dbfd59a Merge branch 'main' of https://github.com/GSA/notifications-admin into feature/sidenav-expand-sublinks 2025-01-13 16:35:08 -05:00
alexjanousekGSA
369307f45a More test fixes 2025-01-13 16:07:32 -05:00
Jonathan Bobel
c59039d61d Merge branch 'feature/sidenav-expand-sublinks' of https://github.com/GSA/notifications-admin into feature/sidenav-expand-sublinks 2025-01-13 15:59:14 -05:00
alexjanousekGSA
b8735ea622 Fixed more tests 2025-01-13 15:53:57 -05:00
alexjanousekGSA
5994183c71 Fixing tests 2025-01-13 15:38:23 -05:00
alexjanousekGSA
2cfb069c9c Added line 2025-01-13 15:24:17 -05:00
alexjanousekGSA
66dfca579a Fixed test 2025-01-13 15:17:02 -05:00
alexjanousekGSA
0982e70a54 Fixed indention 2025-01-13 15:02:46 -05:00
alexjanousekGSA
52791cab7b Updated navigation links and backstopjs 2025-01-13 14:47:41 -05:00
Jonathan Bobel
ecadc49a55 Style and format updates 2025-01-13 13:57:59 -05:00
Jonathan Bobel
6d3bf9ae49 Merge branch 'feature/sidenav-expand-sublinks' of https://github.com/GSA/notifications-admin into feature/sidenav-expand-sublinks 2025-01-13 13:48:50 -05:00
alexjanousekGSA
6e0dd9e9ec Removed test that will break with next updates anyways 2025-01-13 12:53:44 -05:00
Jonathan Bobel
d580e131e3 Merge branch 'feature/sidenav-expand-sublinks' of https://github.com/GSA/notifications-admin into feature/sidenav-expand-sublinks 2025-01-13 12:45:12 -05:00
alexjanousekGSA
f047965ad4 Updating test 2025-01-13 12:45:01 -05:00
alexjanousekGSA
41ffb27d03 Trying one more test 2025-01-13 12:33:09 -05:00
Andrew Shumway
bb65ddb6a7 Merge pull request #2208 from GSA/dependabot/pip/phonenumbers-8.13.52
Bump phonenumbers from 8.13.48 to 8.13.52
2025-01-13 10:28:11 -07:00
dependabot[bot]
3666853f11 Bump phonenumbers from 8.13.48 to 8.13.52
Bumps [phonenumbers](https://github.com/daviddrysdale/python-phonenumbers) from 8.13.48 to 8.13.52.
- [Commits](https://github.com/daviddrysdale/python-phonenumbers/compare/v8.13.48...v8.13.52)

---
updated-dependencies:
- dependency-name: phonenumbers
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-13 17:22:21 +00:00
alexjanousekGSA
0ba7146012 Fixing end to end tests 2025-01-13 12:16:58 -05:00
alexjanousekGSA
cbfa11575d updated backstop files 2025-01-13 11:50:16 -05:00
alexjanousekGSA
e1b9985064 updated backstop files 2025-01-13 11:49:44 -05:00
alexjanousekGSA
36b9d2f772 Updated width of sidenav and fixed test 2025-01-13 11:42:48 -05:00
Jonathan Bobel
47cb8c27cc Merge branch 'main' of https://github.com/GSA/notifications-admin into feature/sidenav-expand-sublinks 2025-01-13 11:00:20 -05:00
Jonathan Bobel
50eeac806a Small content update 2025-01-13 10:32:49 -05:00
Jonathan Bobel
af5687349c Merge branch 'main' of https://github.com/GSA/notifications-admin into 2227-acceptable-use-policy 2025-01-13 10:31:49 -05:00
alexjanousekGSA
05e73da4e8 Fixing unit tests
'
2025-01-12 18:07:22 -05:00
alexjanousekGSA
c0d24b838a Fixed lint tests 2025-01-12 17:55:21 -05:00
alexjanousekGSA
b6353c41e5 Removed best practices flag 2025-01-12 17:42:15 -05:00
alexjanousekGSA
6441ae8ca7 Fixed missing login button on sublinks 2025-01-12 16:56:22 -05:00
alexjanousekGSA
817477bd72 Fixed bugs with sidenav 2025-01-10 16:06:55 -04:00
Beverly Nguyen
30293d9774 draft dash 2025-01-09 16:20:33 -08:00
Beverly Nguyen
9d09f75855 draft dash 2025-01-09 16:18:47 -08:00
alexjanousekGSA
168d93a170 Updated code to allow sidenav expand on click 2025-01-09 12:00:50 -04:00
Alex Janousek
f012ab178f Merge pull request #2249 from GSA/dependabot/npm_and_yarn/sass-embedded-1.83.1
Bump sass-embedded from 1.83.0 to 1.83.1
2025-01-09 11:58:07 -04:00
dependabot[bot]
cd0507d199 Bump sass-embedded from 1.83.0 to 1.83.1
Bumps [sass-embedded](https://github.com/sass/embedded-host-node) from 1.83.0 to 1.83.1.
- [Changelog](https://github.com/sass/embedded-host-node/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sass/embedded-host-node/compare/1.83.0...1.83.1)

---
updated-dependencies:
- dependency-name: sass-embedded
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-09 15:10:04 +00:00
Alex Janousek
907aeca70c Merge pull request #2254 from GSA/dependabot/npm_and_yarn/rollup-4.30.1
Bump rollup from 4.29.1 to 4.30.1
2025-01-09 10:51:15 -04:00
Jonathan Bobel
7b1018e867 Merge branch 'main' of https://github.com/GSA/notifications-admin into 2227-acceptable-use-policy 2025-01-08 12:29:30 -05:00
Jonathan Bobel
7534de06e3 Merge pull request #2246 from GSA/2240-add-join-notify-to-the-main-nav
add side nav to join notify and contact page
2025-01-08 09:19:12 -05:00
Beverly Nguyen
227675c2c6 Merge branch 'main' into 2240-add-join-notify-to-the-main-nav 2025-01-07 15:54:44 -08:00
Beverly Nguyen
89ed31475d contact us side nav 2025-01-07 15:54:03 -08:00
Beverly Nguyen
322c23ab2f contact us 2025-01-07 15:49:32 -08:00
Beverly Nguyen
1aa589bbde Merge pull request #2225 from GSA/2224-fix-links-in-about-us-page
Quick fix: Links on the about us and join-notify needs to be updated
2025-01-07 15:41:36 -08:00
dependabot[bot]
85b19c4e2c Bump rollup from 4.29.1 to 4.30.1
Bumps [rollup](https://github.com/rollup/rollup) from 4.29.1 to 4.30.1.
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v4.29.1...v4.30.1)

---
updated-dependencies:
- dependency-name: rollup
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-07 23:40:58 +00:00
Carlo Costino
98e4334f2a Merge pull request #2251 from GSA/fix_broke_poetry
pin poetry to 1.8.5 for now
2025-01-07 13:30:53 -05:00
Kenneth Kehl
e9ba1c7726 pin poetry to 1.8.5 for now 2025-01-07 10:03:06 -08:00
Beverly Nguyen
a3db0a9fc0 first round 2025-01-06 16:31:52 -08:00
Jonathan Bobel
02bf68989d Adjusting width in mobile 2025-01-06 12:49:39 -05:00
Jonathan Bobel
9ca9c1d504 2227 - Adding Acceptable Use Policy page 2025-01-06 10:40:35 -05:00
Beverly Nguyen
f176e86546 add side nav 2025-01-03 15:05:55 -08:00
Beverly Nguyen
5a437c91f8 Merge branch 'main' into 2224-fix-links-in-about-us-page 2025-01-03 14:01:56 -08:00
Beverly Nguyen
41ead43094 Merge pull request #2243 from GSA/2240-add-join-notify-to-the-main-nav
Add Join-Notify link to top nav
2025-01-03 13:56:34 -08:00
Beverly Nguyen
cbb2ecd6e4 Merge pull request #2232 from GSA/2210-user-story-update-home-page-phase-2-1
Updated css to add color for home page, updated backstop references
2025-01-03 13:50:04 -08:00
Beverly Nguyen
fd92d85636 add link to card 2025-01-03 13:21:07 -08:00
Beverly Nguyen
a1c76bc579 removed file 2025-01-03 13:07:49 -08:00
Beverly Nguyen
94c5eb5234 added side nav 2025-01-03 13:05:47 -08:00
Beverly Nguyen
3b2fa4314b add join notify link 2025-01-03 11:48:45 -08:00
alexjanousekGSA
b5d72a2062 Updated css to add color for home page, updated backstop references 2024-12-27 13:29:19 -05:00
Beverly Nguyen
c65352166d Merge pull request #2222 from GSA/2072-benefits-studio-about-page 2024-12-24 11:16:43 -08:00
Alex Janousek
44bf5d1478 Merge pull request #2216 from GSA/dependabot/npm_and_yarn/uswds/compile-1.2.1
Bump @uswds/compile from 1.2.0 to 1.2.1
2024-12-24 11:09:29 -05:00
Alex Janousek
7e8c9bcfc5 Merge pull request #2217 from GSA/dependabot/npm_and_yarn/uswds/uswds-3.11.0
Bump @uswds/uswds from 3.10.0 to 3.11.0
2024-12-24 11:08:59 -05:00
Alex Janousek
57e18d1e21 Merge pull request #2231 from GSA/dependabot/npm_and_yarn/rollup-4.29.1
Bump rollup from 4.28.1 to 4.29.1
2024-12-24 11:08:45 -05:00
dependabot[bot]
bc265ff4fd Bump rollup from 4.28.1 to 4.29.1
Bumps [rollup](https://github.com/rollup/rollup) from 4.28.1 to 4.29.1.
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v4.28.1...v4.29.1)

---
updated-dependencies:
- dependency-name: rollup
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-23 23:37:54 +00:00
Carlo Costino
231ce9f5a3 Merge pull request #2228 from GSA/bump-jinja2@3.1.5
Bump jinja2 from 3.1.4 to 3.1.5
2024-12-23 14:29:23 -05:00
Andrew Shumway
b60bbeb994 Bump jinja2 from 3.1.4 to 3.1.5 2024-12-23 12:14:35 -07:00
Beverly Nguyen
3da0ecc5f9 add links 2024-12-20 13:57:28 -08:00
Steven Reilly
e3674d1897 Merge pull request #2215 from GSA/feature/added-about-nav-button
Added nav button for about page
2024-12-20 16:25:16 -05:00
Jonathan Bobel
459ca8bed1 Updated navigation test 2024-12-20 14:46:13 -05:00
Jonathan Bobel
86060a5e02 White space updates 2024-12-20 14:04:25 -05:00
Jonathan Bobel
168a7b6446 2072 - Benefits Studio About Page 2024-12-20 13:38:01 -05:00
dependabot[bot]
5a4e755adc Bump @uswds/uswds from 3.10.0 to 3.11.0
Bumps [@uswds/uswds](https://github.com/uswds/uswds) from 3.10.0 to 3.11.0.
- [Release notes](https://github.com/uswds/uswds/releases)
- [Commits](https://github.com/uswds/uswds/compare/v3.10.0...v3.11.0)

---
updated-dependencies:
- dependency-name: "@uswds/uswds"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-19 23:54:42 +00:00
dependabot[bot]
92db1bf426 Bump @uswds/compile from 1.2.0 to 1.2.1
Bumps [@uswds/compile](https://github.com/uswds/uswds-compile) from 1.2.0 to 1.2.1.
- [Release notes](https://github.com/uswds/uswds-compile/releases)
- [Commits](https://github.com/uswds/uswds-compile/compare/v1.2.0...v1.2.1)

---
updated-dependencies:
- dependency-name: "@uswds/compile"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-19 23:54:33 +00:00
alexjanousekGSA
ea20512f41 Added nav button for about page 2024-12-19 12:44:58 -05:00
Beverly Nguyen
1e261fd353 fixed testing 2024-12-18 15:30:48 -08:00
Beverly Nguyen
1df019e78c adding required html field 2024-12-18 12:40:45 -08:00
Beverly Nguyen
ca5e5ce04c Merge branch 'main' into 2125-send-message-a11y-audit-unique-ids 2024-12-17 11:48:05 -08:00
Beverly Nguyen
56381f5b27 adding id 2024-12-17 10:06:10 -08:00
Beverly Nguyen
12636d6a69 adding error annoucement 2024-12-12 23:31:22 -08:00
Beverly Nguyen
b731cd62bb adding error annoucement 2024-12-12 23:08:25 -08:00
Beverly Nguyen
17f758bace added id and also refactor error message on templates page 2024-12-12 18:42:03 -08:00
155 changed files with 3265 additions and 4711 deletions

View File

@@ -161,7 +161,7 @@
"filename": "app/config.py",
"hashed_secret": "577a4c667e4af8682ca431857214b3a920883efc",
"is_verified": false,
"line_number": 123,
"line_number": 120,
"is_secret": false
}
],
@@ -527,7 +527,7 @@
"filename": "tests/app/main/views/test_accept_invite.py",
"hashed_secret": "07f0a6c13923fc3b5f0c57ffa2d29b715eb80d71",
"is_verified": false,
"line_number": 643,
"line_number": 631,
"is_secret": false
}
],
@@ -555,7 +555,7 @@
"filename": "tests/app/main/views/test_register.py",
"hashed_secret": "5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8",
"is_verified": false,
"line_number": 201,
"line_number": 199,
"is_secret": false
},
{
@@ -563,7 +563,7 @@
"filename": "tests/app/main/views/test_register.py",
"hashed_secret": "bb5b7caa27d005d38039e3797c3ddb9bcd22c3c8",
"is_verified": false,
"line_number": 274,
"line_number": 272,
"is_secret": false
}
],
@@ -684,5 +684,5 @@
}
]
},
"generated_at": "2024-11-21T23:08:45Z"
"generated_at": "2025-02-26T18:19:37Z"
}

View File

@@ -64,6 +64,17 @@ body:
validations:
required: false
- type: markdown
attributes:
value: '**Accessibility:**'
- type: textarea
id: accessibility
attributes:
label: "List any specific accessibility guidance or tests that need to be considered for this user story."
description: "List what type of accessibility tests need to pass."
validations:
required: false
- type: markdown
attributes:
value: '**Notes:**'

View File

@@ -15,7 +15,7 @@ runs:
python-version: "3.12.3"
- name: Install poetry
shell: bash
run: pip install poetry
run: pip install poetry==1.8.5
- name: Install application dependencies
shell: bash
run: make bootstrap

View File

@@ -20,3 +20,10 @@ Please enter a detailed description here.
* Consideration 1
* Consideration 2
* Consideration ...
## A11y Checks (if applicable)
* Double check work is getting picked up by the automated E2E tests
* Conduct browser-based tests through [AxeDevTools](https://www.deque.com/axe/devtools/) and [WAVE](https://wave.webaim.org/)
* Review the [Manual Checklist](https://docs.google.com/document/d/192bBXStebdXWtYhZQ73qaWMJhGcuSB1W6c9YBXhWZvc/edit?usp=sharing)
* Make sure there are no linting errors in VSCode or other IDE of choice

View File

@@ -10,7 +10,7 @@ env:
FLASK_APP: application.py
WERKZEUG_DEBUG_PIN: off
REDIS_ENABLED: 0
NODE_VERSION: 16.15.1
NODE_VERSION: 22.3.0
AWS_US_TOLL_FREE_NUMBER: "+18556438890"
ADMIN_BASE_URL: http://localhost:6012
@@ -38,10 +38,10 @@ jobs:
output: report-markdown
annotations: failed-tests
prnumber: ${{ steps.findPr.outputs.number }}
- name: Run style checks
run: poetry run flake8 .
- name: Check imports alphabetized
run: poetry run isort --check-only ./app ./tests
- name: Run style checks
run: poetry run flake8 .
- name: Check dead code
run: make dead-code
- name: Run js tests
@@ -139,7 +139,7 @@ jobs:
- uses: ./.github/actions/setup-project
- name: Create requirements.txt
run: poetry export --without-hashes --format=requirements.txt > requirements.txt
- uses: pypa/gh-action-pip-audit@v1.0.8
- uses: pypa/gh-action-pip-audit@v1.1.0
with:
inputs: requirements.txt
ignore-vulns: |
@@ -165,8 +165,9 @@ jobs:
run: make run-flask &
env:
NOTIFY_ENVIRONMENT: scanning
FEATURE_ABOUT_PAGE_ENABLED: true
- name: Run OWASP Baseline Scan
uses: zaproxy/action-baseline@v0.9.0
uses: zaproxy/action-baseline@v0.14.0
with:
docker_name: "ghcr.io/zaproxy/zaproxy:weekly"
target: "http://localhost:6012"
@@ -176,7 +177,7 @@ jobs:
cmd_options: "-I"
a11y-scan:
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup-project

View File

@@ -16,7 +16,7 @@ env:
FLASK_APP: application.py
WERKZEUG_DEBUG_PIN: off
REDIS_ENABLED: 0
NODE_VERSION: 16.15.1
NODE_VERSION: 22.3.0
jobs:
dependency-audits:
@@ -26,7 +26,7 @@ jobs:
- uses: ./.github/actions/setup-project
- name: Create requirements.txt
run: poetry export --without-hashes --format=requirements.txt > requirements.txt
- uses: pypa/gh-action-pip-audit@v1.0.6
- uses: pypa/gh-action-pip-audit@v1.1.0
with:
inputs: requirements.txt
- name: Run npm audit
@@ -50,7 +50,7 @@ jobs:
env:
NOTIFY_ENVIRONMENT: scanning
- name: Run OWASP Full Scan
uses: zaproxy/action-full-scan@v0.7.0
uses: zaproxy/action-full-scan@v0.12.0
with:
docker_name: 'ghcr.io/zaproxy/zaproxy:weekly'
target: 'http://localhost:6012'

View File

@@ -16,9 +16,17 @@ jobs:
with:
fetch-depth: 2
# Looks like we need to install Terraform ourselves now!
# https://github.com/actions/runner-images/issues/10796#issuecomment-2417064348
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_version: "^1.7.5"
terraform_wrapper: false
- name: Check for changes to Terraform
id: changed-terraform-files
uses: tj-actions/changed-files@v44
uses: tj-actions/changed-files@v45
with:
files: |
terraform/demo/**
@@ -93,7 +101,7 @@ jobs:
- name: Check for changes to egress config
id: changed-egress-config
uses: tj-actions/changed-files@v44
uses: tj-actions/changed-files@v45
with:
files: |
deploy-config/egress_proxy/notify-admin-demo.*.acl

View File

@@ -16,9 +16,17 @@ jobs:
with:
fetch-depth: 2
# Looks like we need to install Terraform ourselves now!
# https://github.com/actions/runner-images/issues/10796#issuecomment-2417064348
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_version: "^1.7.5"
terraform_wrapper: false
- name: Check for changes to Terraform
id: changed-terraform-files
uses: tj-actions/changed-files@v44
uses: tj-actions/changed-files@v45
with:
files: |
terraform/production/**
@@ -93,7 +101,7 @@ jobs:
- name: Check for changes to egress config
id: changed-egress-config
uses: tj-actions/changed-files@v44
uses: tj-actions/changed-files@v45
with:
files: |
deploy-config/egress_proxy/notify-admin-production.*.acl

View File

@@ -21,9 +21,17 @@ jobs:
with:
fetch-depth: 2
# Looks like we need to install Terraform ourselves now!
# https://github.com/actions/runner-images/issues/10796#issuecomment-2417064348
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_version: "^1.7.5"
terraform_wrapper: false
- name: Check for changes to Terraform
id: changed-terraform-files
uses: tj-actions/changed-files@v44
uses: tj-actions/changed-files@v45
with:
files: |
terraform/staging/**
@@ -100,7 +108,7 @@ jobs:
- name: Check for changes to egress config
id: changed-egress-config
uses: tj-actions/changed-files@v44
uses: tj-actions/changed-files@v45
with:
files: |
deploy-config/egress_proxy/notify-admin-staging.*.acl

View File

@@ -15,6 +15,14 @@ jobs:
- name: Checkout
uses: actions/checkout@v4
# Looks like we need to install Terraform ourselves now!
# https://github.com/actions/runner-images/issues/10796#issuecomment-2417064348
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_version: "^1.7.5"
terraform_wrapper: false
- name: Check for drift
uses: dflook/terraform-check@v1
env:
@@ -35,6 +43,14 @@ jobs:
with:
ref: 'production'
# Looks like we need to install Terraform ourselves now!
# https://github.com/actions/runner-images/issues/10796#issuecomment-2417064348
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_version: "^1.7.5"
terraform_wrapper: false
- name: Check for drift
uses: dflook/terraform-check@v1
env:
@@ -55,6 +71,14 @@ jobs:
with:
ref: 'production'
# Looks like we need to install Terraform ourselves now!
# https://github.com/actions/runner-images/issues/10796#issuecomment-2417064348
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_version: "^1.7.5"
terraform_wrapper: false
- name: Check for drift
uses: dflook/terraform-check@v1
env:

View File

@@ -18,6 +18,14 @@ jobs:
- name: Checkout
uses: actions/checkout@v4
# Looks like we need to install Terraform ourselves now!
# https://github.com/actions/runner-images/issues/10796#issuecomment-2417064348
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_version: "^1.7.5"
terraform_wrapper: false
- name: Terraform format
id: format
run: terraform fmt -check
@@ -51,7 +59,7 @@ jobs:
# inspiration: https://learn.hashicorp.com/tutorials/terraform/github-actions#review-actions-workflow
- name: Update PR
uses: actions/github-script@v6
uses: actions/github-script@v7
# we would like to update the PR even when a prior step failed
if: ${{ always() }}
with:

View File

@@ -18,6 +18,14 @@ jobs:
- name: Checkout
uses: actions/checkout@v4
# Looks like we need to install Terraform ourselves now!
# https://github.com/actions/runner-images/issues/10796#issuecomment-2417064348
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_version: "^1.7.5"
terraform_wrapper: false
- name: Terraform format
id: format
run: terraform fmt -check
@@ -51,7 +59,7 @@ jobs:
# inspiration: https://learn.hashicorp.com/tutorials/terraform/github-actions#review-actions-workflow
- name: Update PR
uses: actions/github-script@v6
uses: actions/github-script@v7
# we would like to update the PR even when a prior step failed
if: ${{ always() }}
with:

View File

@@ -18,6 +18,14 @@ jobs:
- name: Checkout
uses: actions/checkout@v4
# Looks like we need to install Terraform ourselves now!
# https://github.com/actions/runner-images/issues/10796#issuecomment-2417064348
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_version: "^1.7.5"
terraform_wrapper: false
- name: Terraform format
id: format
run: terraform fmt -check
@@ -51,7 +59,7 @@ jobs:
# inspiration: https://learn.hashicorp.com/tutorials/terraform/github-actions#review-actions-workflow
- name: Update PR
uses: actions/github-script@v6
uses: actions/github-script@v7
# we would like to update the PR even when a prior step failed
if: ${{ always() }}
with:

4
.gitignore vendored
View File

@@ -15,6 +15,10 @@
## Non user files allowed to be commited
!app/assets/pdf/tcpa_overview.pdf
!app/assets/pdf/investing-notifications-tts-public-benefits-memo.pdf
!app/assets/pdf/out-of-pilot-announcement.pdf
!app/assets/pdf/studio-research-snapshot-2022-07-external.pdf
!app/assets/pdf/TCPA-Overview.pdf
!tests/test_pdf_files/no_eof_marker.pdf
!tests/test_pdf_files/multi_page_pdf.pdf
!tests/test_pdf_files/big.pdf

View File

@@ -1,7 +1,10 @@
{
"defaults": {
"standard": "WCAG2AA",
"runners": ["htmlcs"],
"runners": ["htmlcs"],
"chromeLaunchConfig": {
"executablePath": "/usr/bin/google-chrome"
},
"concurrency": 1,
"hideElements": [
"nav > ol a",

View File

@@ -15,7 +15,6 @@ NVMSH := $(shell [ -f "$(HOME)/.nvm/nvm.sh" ] && echo "$(HOME)/.nvm/nvm.sh" || e
.PHONY: bootstrap
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
@@ -63,6 +62,13 @@ py-lint: ## Run python linting scanners and black
poetry run flake8 .
poetry run isort --check-only ./app ./tests
.PHONY: tada
tada: ## Run python linting scanners and black
poetry run isort ./app ./tests
poetry run black .
poetry run flake8 .
.PHONY: avg-complexity
avg-complexity:
echo "*** Shows average complexity in radon of all code ***"
@@ -156,3 +162,8 @@ upload-static:
# @cf map-route notify-admin ${DNS_NAME} --hostname www
# @cf unmap-route notify-admin-failwhale ${DNS_NAME} --hostname www
# @echo "Failwhale is disabled"
.PHONY: test-single
test-single: export NEW_RELIC_ENVIRONMENT=test
test-single: ## Run a single test file
poetry run pytest $(TEST_FILE)

View File

@@ -1,5 +1,6 @@
import os
import pathlib
import re
import secrets
from functools import partial
from time import monotonic
@@ -168,20 +169,12 @@ def _csp(config):
def create_app(application):
application.config["FEATURE_BEST_PRACTICES_ENABLED"] = (
os.getenv("FEATURE_BEST_PRACTICES_ENABLED", "false").lower() == "true"
)
@application.context_processor
def inject_feature_flags():
feature_best_practices_enabled = application.config.get(
"FEATURE_BEST_PRACTICES_ENABLED", False
)
feature_about_page_enabled = application.config.get(
"FEATURE_ABOUT_PAGE_ENABLED", False
)
return dict(
FEATURE_BEST_PRACTICES_ENABLED=feature_best_practices_enabled,
FEATURE_ABOUT_PAGE_ENABLED=feature_about_page_enabled,
)
@@ -624,6 +617,8 @@ def setup_event_handlers():
def add_template_filters(application):
application.add_template_filter(slugify)
for fn in [
format_auth_type,
format_billions,
@@ -681,3 +676,10 @@ def init_jinja(application):
]
jinja_loader = jinja2.FileSystemLoader(template_folders)
application.jinja_loader = jinja_loader
def slugify(text):
"""
Converts text to lowercase, replaces spaces with hyphens, and removes invalid characters.
"""
return re.sub(r'[^a-z0-9-]', '', re.sub(r'\s+', '-', text.lower()))

View File

@@ -1,10 +1,14 @@
(function (window) {
if (document.getElementById('activityChartContainer')) {
let currentType = 'service';
const tableContainer = document.getElementById('activityContainer');
const currentUserName = tableContainer.getAttribute('data-currentUserName');
const currentServiceId = tableContainer.getAttribute('data-currentServiceId');
const COLORS = {
delivered: '#0076d6',
failed: '#fa9441',
pending: '#C7CACE',
text: '#666'
};
@@ -12,7 +16,7 @@
const FONT_WEIGHT = 'bold';
const MAX_Y = 120;
const createChart = function(containerId, labels, deliveredData, failedData) {
const createChart = function(containerId, labels, deliveredData, failedData, pendingData) {
const container = d3.select(containerId);
container.selectAll('*').remove(); // Clear any existing content
@@ -35,7 +39,7 @@
}
// Calculate total messages
const totalMessages = d3.sum(deliveredData) + d3.sum(failedData);
const totalMessages = d3.sum(deliveredData) + d3.sum(failedData) + d3.sum(pendingData);
// Create legend only if there are messages
const legendContainer = d3.select('.chart-legend');
@@ -45,7 +49,8 @@
// Show legend if there are messages
const legendData = [
{ label: 'Delivered', color: COLORS.delivered },
{ label: 'Failed', color: COLORS.failed }
{ label: 'Failed', color: COLORS.failed },
{ label: 'Pending', color: COLORS.pending }
];
const legendItem = legendContainer.selectAll('.legend-item')
@@ -76,8 +81,9 @@
.range([0, width])
.padding(0.1);
// Adjust the y-axis domain to add some space above the tallest bar
const maxY = d3.max(deliveredData.map((d, i) => d + (failedData[i] || 0)));
const y = d3.scaleSqrt()
const maxY = d3.max(deliveredData.map((d, i) => d + (failedData[i] || 0) + (pendingData[i] || 0)));
const y = d3.scaleSymlog()
.domain([0, maxY + 2]) // Add 2 units of space at the top
.nice()
.range([height, 0]);
@@ -89,7 +95,7 @@
// Generate the y-axis with whole numbers
const yAxis = d3.axisLeft(y)
.ticks(Math.min(maxY + 2, 10)) // Generate up to 10 ticks based on the data
.ticks(Math.min(maxY + 2, 3))
.tickFormat(d3.format('d')); // Ensure whole numbers on the y-axis
svg.append('g')
@@ -100,12 +106,13 @@
const stackData = labels.map((label, i) => ({
label: label,
delivered: deliveredData[i],
failed: failedData[i] || 0 // Ensure there's a value for failed, even if it's 0
failed: failedData[i] || 0,
pending: pendingData[i] || 0
}));
// Stack the data
const stack = d3.stack()
.keys(['delivered', 'failed'])
.keys(['delivered', 'failed', 'pending'])
.order(d3.stackOrderNone)
.offset(d3.stackOffsetNone);
@@ -113,8 +120,8 @@
// Color scale
const color = d3.scaleOrdinal()
.domain(['delivered', 'failed'])
.range([COLORS.delivered, COLORS.failed]);
.domain(['delivered', 'failed', 'pending'])
.range([COLORS.delivered, COLORS.failed, COLORS.pending]);
// Create bars with animation
const barGroups = svg.selectAll('.bar-group')
@@ -123,11 +130,12 @@
.append('g')
.attr('class', 'bar-group')
.attr('fill', d => color(d.key));
const minBarHeight = 5;
barGroups.selectAll('rect')
.data(d => d)
.enter()
.append('rect')
.filter(d => d[1] - d[0] > 0)
.attr('x', d => x(d.data.label))
.attr('y', height)
.attr('height', 0)
@@ -148,11 +156,13 @@
.transition()
.duration(1000)
.attr('y', d => y(d[1]))
.attr('height', d => y(d[0]) - y(d[1]));
};
.attr('height', d => {
const calculatedHeight = y(d[0]) - y(d[1]);
return calculatedHeight < minBarHeight ? minBarHeight : calculatedHeight;
}); };
// Function to create an accessible table
const createTable = function(tableId, chartType, labels, deliveredData, failedData) {
const createTable = function(tableId, chartType, labels, deliveredData, failedData, pendingData) {
const table = document.getElementById(tableId);
table.innerHTML = ""; // Clear previous data
@@ -164,7 +174,7 @@
// Create table header
const headerRow = document.createElement('tr');
const headers = ['Day', 'Delivered', 'Failed'];
const headers = ['Day', 'Delivered', 'Failed', 'Pending'];
headers.forEach(headerText => {
const th = document.createElement('th');
th.textContent = headerText;
@@ -187,6 +197,10 @@
cellFailed.textContent = failedData[index];
row.appendChild(cellFailed);
const cellPending = document.createElement('td');
cellPending.textContent = pendingData[index];
row.appendChild(cellPending);
tbody.appendChild(row);
});
@@ -196,12 +210,19 @@
};
const fetchData = function(type) {
var ctx = document.getElementById('weeklyChart');
if (!ctx) {
return;
}
var url = type === 'service' ? `/daily_stats.json` : `/daily_stats_by_user.json`;
var userTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
var url = type === 'service'
? `/services/${currentServiceId}/daily-stats.json?timezone=${encodeURIComponent(userTimezone)}`
: `/services/${currentServiceId}/daily-stats-by-user.json`;
return fetch(url)
.then(response => {
if (!response.ok) {
@@ -213,7 +234,7 @@
labels = [];
deliveredData = [];
failedData = [];
pendingData = [];
let totalMessages = 0;
for (var dateString in data) {
@@ -224,9 +245,8 @@
labels.push(formattedDate);
deliveredData.push(data[dateString].sms.delivered);
failedData.push(data[dateString].sms.failure);
// Calculate the total number of messages
totalMessages += data[dateString].sms.delivered + data[dateString].sms.failure;
pendingData.push(data[dateString].sms.pending || 0);
totalMessages += data[dateString].sms.delivered + data[dateString].sms.failure + data[dateString].sms.pending;
}
}
@@ -252,17 +272,18 @@
}
} else {
// If there are messages, create the chart and table
createChart('#weeklyChart', labels, deliveredData, failedData);
createTable('weeklyTable', 'activityChart', labels, deliveredData, failedData);
}
return data;
})
.catch(error => console.error('Error fetching daily stats:', error));
};
createChart('#weeklyChart', labels, deliveredData, failedData, pendingData);
createTable('weeklyTable', 'activityChart', labels, deliveredData, failedData, pendingData);
}
return data;
})
.catch(error => console.error('Error fetching daily stats:', error));
};
setInterval(() => fetchData(currentType), 25000);
const handleDropdownChange = function(event) {
const selectedValue = event.target.value;
currentType = selectedValue;
const subTitle = document.querySelector(`#activityChartContainer .chart-subtitle`);
const selectElement = document.getElementById('options');
const selectedText = selectElement.options[selectElement.selectedIndex].text;
@@ -270,36 +291,67 @@
subTitle.textContent = `${selectedText} - last 7 days`;
fetchData(selectedValue);
// Update ARIA live region
const liveRegion = document.getElementById('aria-live-account');
liveRegion.textContent = `Data updated for ${selectedText} - last 7 days`;
// Switch tables based on dropdown selection
const selectedTable = selectedValue === "individual" ? "table1" : "table2";
const tables = document.querySelectorAll('.table-overflow-x-auto');
tables.forEach(function(table) {
table.classList.add('hidden'); // Hide all tables by adding the hidden class
table.classList.remove('visible'); // Ensure they are not visible
});
const tableToShow = document.getElementById(selectedTable);
tableToShow.classList.remove('hidden'); // Remove hidden class
tableToShow.classList.add('visible'); // Add visible class
const tableHeading = document.querySelector('#tableActivity h2');
const senderColumns = document.querySelectorAll('.sender-column');
const allRows = document.querySelectorAll('#activity-table tbody tr');
const caption = document.querySelector('#activity-table caption');
if (selectedValue === 'individual') {
tableHeading.textContent = 'My activity';
caption.textContent = `Table showing the sent jobs for ${currentUserName}`;
senderColumns.forEach(col => {
col.style.display = 'none';
});
allRows.forEach(row => row.style.display = 'none');
const userRows = Array.from(allRows).filter(row => {
const senderCell = row.querySelector('.sender-column');
const rowSender = senderCell ? senderCell.textContent.trim() : '';
return rowSender === currentUserName;
});
userRows.slice(0, 5).forEach(row => {
row.style.display = '';
});
} else {
tableHeading.textContent = 'Service activity';
caption.textContent = `Table showing the sent jobs for service`;
senderColumns.forEach(col => {
col.style.display = '';
});
allRows.forEach((row, index) => {
row.style.display = (index < 5) ? '' : 'none';
});
}
};
document.addEventListener('DOMContentLoaded', function() {
// Initialize activityChart chart and table with service data by default
fetchData('service');
fetchData(currentType);
const allRows = Array.from(document.querySelectorAll('#activity-table tbody tr'));
allRows.forEach((row, index) => {
row.style.display = (index < 5) ? '' : 'none';
});
// Add event listener to the dropdown
const dropdown = document.getElementById('options');
dropdown.addEventListener('change', handleDropdownChange);
});
// Resize chart on window resize
window.addEventListener('resize', function() {
if (labels.length > 0 && deliveredData.length > 0 && failedData.length > 0) {
createChart('#weeklyChart', labels, deliveredData, failedData);
createTable('weeklyTable', 'activityChart', labels, deliveredData, failedData);
if (labels.length > 0 && deliveredData.length > 0 && failedData.length > 0 && pendingData.length > 0) {
createChart('#weeklyChart', labels, deliveredData, failedData, pendingData);
createTable('weeklyTable', 'activityChart', labels, deliveredData, failedData, pendingData);
}
});

View File

@@ -0,0 +1,57 @@
document.addEventListener('DOMContentLoaded', () => {
const sidenavItems = document.querySelectorAll('.usa-sidenav__item > .parent-link');
let lastPath = window.location.pathname;
let debounceTimeout = null;
sidenavItems.forEach((link) => {
const parentItem = link.parentElement;
const sublist = parentItem.querySelector('.usa-sidenav__sublist');
const targetHref = link.getAttribute('href');
// initialize the menu to open the correct submenu based on the current route
if (window.location.pathname.startsWith(targetHref)) {
parentItem.classList.add('open');
link.setAttribute('aria-expanded', 'true');
}
link.addEventListener('click', (event) => {
const currentPath = window.location.pathname;
// prevent default behavior only if navigating to the same route
if (currentPath === targetHref) {
event.preventDefault();
return;
}
if (sublist && !parentItem.classList.contains('open')) {
// debounce the menu update to avoid flickering
clearTimeout(debounceTimeout);
debounceTimeout = setTimeout(() => {
parentItem.classList.add('open');
link.setAttribute('aria-expanded', 'true');
}, 50);
}
});
});
// handle browser back/forward navigation
window.addEventListener('popstate', () => {
const currentPath = window.location.pathname;
// sync menu state
sidenavItems.forEach((link) => {
const parentItem = link.parentElement;
const targetHref = link.getAttribute('href');
if (currentPath.startsWith(targetHref)) {
parentItem.classList.add('open');
link.setAttribute('aria-expanded', 'true');
} else {
parentItem.classList.remove('open');
link.setAttribute('aria-expanded', 'false');
}
});
lastPath = currentPath;
});
});

View File

@@ -6,17 +6,17 @@
var chartTitle = document.getElementById('chartTitle').textContent;
// Access data attributes from the HTML
var sms_sent = parseInt(chartContainer.getAttribute('data-sms-sent'));
var sms_remaining_messages = parseInt(chartContainer.getAttribute('data-sms-allowance-remaining'));
var totalMessages = sms_sent + sms_remaining_messages;
var messagesSent = parseInt(chartContainer.getAttribute('data-messages-sent'));
var messagesRemaining = parseInt(chartContainer.getAttribute('data-messages-remaining'));
var totalMessages = messagesSent + messagesRemaining;
// Update the message below the chart
document.getElementById('message').innerText = `${sms_sent.toLocaleString()} sent / ${sms_remaining_messages.toLocaleString()} remaining`;
document.getElementById('message').innerText = `${messagesSent.toLocaleString()} sent / ${messagesRemaining.toLocaleString()} remaining`;
// Calculate minimum width for "Messages Sent" as 1% of the total chart width
var minSentPercentage = (sms_sent === 0) ? 0 : 0.02;
var minSentPercentage = (messagesSent === 0) ? 0 : 0.02;
var minSentValue = totalMessages * minSentPercentage;
var displaySent = Math.max(sms_sent, minSentValue);
var displaySent = Math.max(messagesSent, minSentValue);
var displayRemaining = totalMessages - displaySent;
var svg = d3.select("#totalMessageChart");
@@ -48,7 +48,7 @@
.attr("width", 0) // Start with width 0 for animation
.on('mouseover', function(event) {
tooltip.style('display', 'block')
.html(`Messages Sent: ${sms_sent.toLocaleString()}`);
.html(`Messages Sent: ${messagesSent.toLocaleString()}`);
})
.on('mousemove', function(event) {
tooltip.style('left', `${event.pageX + 10}px`)
@@ -66,7 +66,7 @@
.attr("width", 0) // Start with width 0 for animation
.on('mouseover', function(event) {
tooltip.style('display', 'block')
.html(`Remaining: ${sms_remaining_messages.toLocaleString()}`);
.html(`Remaining: ${messagesRemaining.toLocaleString()}`);
})
.on('mousemove', function(event) {
tooltip.style('left', `${event.pageX + 10}px`)
@@ -115,9 +115,9 @@
var tbodyRow = document.createElement('tr');
var tdMessagesSent = document.createElement('td');
tdMessagesSent.textContent = sms_sent.toLocaleString(); // Value for Messages Sent
tdMessagesSent.textContent = messagesSent.toLocaleString(); // Value for Messages Sent
var tdRemaining = document.createElement('td');
tdRemaining.textContent = sms_remaining_messages.toLocaleString(); // Value for Remaining
tdRemaining.textContent = messagesRemaining.toLocaleString(); // Value for Remaining
tbodyRow.appendChild(tdMessagesSent);
tbodyRow.appendChild(tdRemaining);

View File

@@ -0,0 +1,93 @@
function showError(input, errorElement, message) {
errorElement.textContent = ""; // Clear existing message
errorElement.style.display = "block";
// Small delay to ensure screen readers pick up the change
setTimeout(() => {
errorElement.textContent = message;
}, 10);
input.classList.add("usa-input--error");
input.setAttribute("aria-describedby", errorElement.id);
}
function hideError(input, errorElement) {
errorElement.style.display = "none";
input.classList.remove("usa-input--error");
input.removeAttribute("aria-describedby");
}
function getFieldLabel(input) {
const label = document.querySelector(`label[for="${input.id}"]`);
return label ? label.textContent.trim() : "This field";
}
// Attach validation logic to forms
function attachValidation() {
const forms = document.querySelectorAll("form.send-one-off-form");
forms.forEach((form) => {
const inputs = form.querySelectorAll("input, textarea, select");
form.addEventListener("submit", function (event) {
let isValid = true;
let firstInvalidInput = null;
inputs.forEach((input) => {
const errorId = input.id ? `${input.id}-error` : `${input.name}-error`;
let errorElement = document.getElementById(errorId);
if (!errorElement) {
errorElement = document.createElement("span");
errorElement.id = errorId;
errorElement.classList.add("usa-error-message");
errorElement.setAttribute("aria-live", "polite");
input.insertAdjacentElement("afterend", errorElement);
}
if (input.type === "radio") {
// Find all radio buttons with the same name
const radioGroup = document.querySelectorAll(`input[name="${input.name}"]`);
const isChecked = Array.from(radioGroup).some(radio => radio.checked);
if (!isChecked) {
showError(input, errorElement, `Error: ${getFieldLabel(input)} must be selected.`);
isValid = false;
if (!firstInvalidInput) {
firstInvalidInput = input;
}
}
} else if (input.value.trim() === "") {
showError(input, errorElement, `Error: ${getFieldLabel(input)} is required.`);
isValid = false;
if (!firstInvalidInput) {
firstInvalidInput = input;
}
}
});
if (!isValid) {
event.preventDefault();
if (firstInvalidInput) firstInvalidInput.focus();
}
});
inputs.forEach((input) => {
input.addEventListener("input", function () {
const errorElement = document.getElementById(`${input.id}-error`);
if (input.value.trim() !== "" && errorElement) {
hideError(input, errorElement);
}
});
});
});
}
// Automatically attach validation only in the browser
if (typeof window !== "undefined") {
document.addEventListener("DOMContentLoaded", attachValidation);
}
// ✅ Check if we're in a Node.js environment (for Jest) before using `module.exports`
if (typeof module !== "undefined" && typeof module.exports !== "undefined") {
module.exports = { showError, hideError, getFieldLabel, attachValidation };
}

Binary file not shown.

Binary file not shown.

View File

@@ -60,17 +60,6 @@ $failed: color('gray-cool-20');
}
}
.usa-tooltip {
line-height: 1;
.usa-tooltip__body {
width: units(mobile);
font-size: size("body", 2);
height: auto;
white-space: wrap;
line-height: units(1);
}
}
.progress-bar {
width: 300px;
height: 20px;

View File

@@ -8,7 +8,7 @@
width: 100%;
max-width: 464px;
box-sizing: border-box;
padding: units(1);
padding: units(2);
background: color('gray-cool-10');
border: 1px solid color('gray-cool-10');
border-radius: 5px;

View File

@@ -198,8 +198,8 @@ td.table-empty-message {
word-wrap: break-word;
}
border: 1px solid color('gray-cool-10');
padding: units(2);
// border: 1px solid color('gray-cool-10');
// padding: units(2);
.tick-cross-list-permissions {
margin: units(1) 0;
@@ -288,7 +288,8 @@ td.table-empty-message {
@include u-width('mobile-lg');
margin-top: units(2);
}
input#search {
input#search-by-name {
margin-top: units(1);
width: 100%;
border: 1px solid color('gray-60');
}
@@ -552,6 +553,24 @@ td.table-empty-message {
max-width: 100%;
}
.usa-tooltip {
&__information {
background: color('ink');
margin-top: 4px;
color: white;
border-radius: 50%;
height: units(3);
width: units(3);
border: 1px solid color('ink');
}
.usa-tooltip__body {
min-width: units('card-lg');
max-width: units('mobile');
white-space: normal;
word-wrap: break-word;
}
}
// Tabs
.tabs {
@@ -852,21 +871,36 @@ $do-dont-top-bar-width: 1;
}
}
.linked-content:hover {
cursor: pointer;
transform: scale(1.05);
transition: transform 0.3s ease, background-color 0.3s ease;
}
.linked-card a {
text-decoration: none;
.usa-card__header, .usa-card__media {
@include at-media('tablet') {
padding-top: units(1);
}
}
&:visited {
color: color('ink');
}
&:focus .usa-card__container {
outline: units(2px) solid color('blue-40v');
outline-offset: 0.3rem;
}
&:hover .usa-card__container, &:focus .usa-card__container {
border-color: color('blue-60v');
background: color('blue-60v');
p, h3 {
color: white;
}
svg {
filter: brightness(0) invert(1);
}
}
li.linked-card:hover > div:first-child:hover {
border-color: #005ea2;
}
&.linked-content:hover, &.linked-content:focus {
cursor: pointer;
transition: transform 0.3s ease, background-color 0.3s ease;
}
li.linked-card:hover h4,
li.linked-card:hover p,
li.linked-card:hover svg,
.best-practices_card_img:hover {
color: #005ea2;
}
.best-practices_card_img {
@@ -876,10 +910,6 @@ li.linked-card:hover svg,
margin: 0 auto;
}
.best-practices_link {
text-decoration: none;
}
.usa-link--downloadable {
position: relative;
}
@@ -914,17 +944,25 @@ li.linked-card:hover svg,
mask-size: 1.75ex 1.75ex;
}
nav.nav {
position: sticky;
top: units(3);
}
.usa-sidenav__sublist {
display: none;
}
.usa-sidenav__item:hover .usa-sidenav__sublist,
.usa-sidenav__item:focus-within .usa-sidenav__sublist {
.usa-sidenav__item.open .usa-sidenav__sublist {
display: block;
}
.usa-sidenav__item a {
display: block;
.usa-sidenav__sublist .bold {
font-weight: bold;
}
.usa-sidenav__sublist li[role="menuitem"] {
border-top: 1px solid #dfe1e2;
}
.icon-list {
@@ -957,6 +995,9 @@ li.linked-card:hover svg,
.usa-card__container {
align-items: center;
text-align: center;
border-radius: 4px;
overflow: hidden;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
img {
margin: units(4) auto 0;
width: units(15);
@@ -965,9 +1006,17 @@ li.linked-card:hover svg,
.usa-card__body {
margin-bottom: units(2);
}
.blue-bar {
background-color: #005eb8;
height: 1.3em;
width: 100%;
margin: 0;
border-radius: 0;
}
}
}
.contact-us-card {
border: 2px solid color("ink");
padding: units(2);
@@ -994,3 +1043,7 @@ li.linked-card:hover svg,
font-size: units(3);
font-weight: bold;
}
.form-control-error {
border: 4px solid #b10e1e
}

View File

@@ -87,9 +87,6 @@ class Config(object):
"tts-benefits-studio@gsa.gov",
],
}
FEATURE_BEST_PRACTICES_ENABLED = (
getenv("FEATURE_BEST_PRACTICES_ENABLED", "false") == "true"
)
FEATURE_ABOUT_PAGE_ENABLED = getenv("FEATURE_ABOUT_PAGE_ENABLED", "false") == "true"

View File

@@ -5,7 +5,7 @@
Explore Notify, add team members, and practice [sending messages to teammates](/using-notify/trial-mode).
2. ## Personalize content
Learn how to [personalize messages](/using-notify/guidance) to increase response.
Learn how to [personalize messages](/using-notify/how-to) to increase response.
3. ## Check delivery status
[Analyze the delivery](/using-notify/delivery-status) of your messages and download reports

View File

@@ -41,14 +41,8 @@ class ValidGovEmail:
if field.data == "":
return
from flask import url_for
message = "Enter a public sector email address."
message = """
Enter a public sector email address or
<a class="usa-link" href="{}">find out who can use Notify</a>
""".format(
url_for("main.features")
)
if not is_gov_user(field.data.lower()):
raise ValidationError(message)

View File

@@ -1,34 +1,25 @@
import calendar
from datetime import datetime
from datetime import datetime, timedelta
from functools import partial
from itertools import groupby
from zoneinfo import ZoneInfo
from flask import Response, abort, jsonify, render_template, request, session, url_for
from flask import abort, jsonify, render_template, request, session, url_for
from flask_login import current_user
from werkzeug.utils import redirect
from app import (
billing_api_client,
current_service,
job_api_client,
service_api_client,
template_statistics_client,
)
from app.formatters import format_date_numeric, format_datetime_numeric, get_time_left
from app.main import main
from app.main.views.user_profile import set_timezone
from app.statistics_utils import get_formatted_percentage
from app.utils import (
DELIVERED_STATUSES,
FAILURE_STATUSES,
REQUESTED_STATUSES,
service_has_permission,
)
from app.utils.csv import Spreadsheet
from app.utils.pagination import generate_next_dict, generate_previous_dict
from app.utils import DELIVERED_STATUSES, FAILURE_STATUSES, REQUESTED_STATUSES
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")
@@ -48,89 +39,108 @@ def service_dashboard(service_id):
if not current_user.has_permissions("view_activity"):
return redirect(url_for("main.choose_template", service_id=service_id))
yearly_usage = billing_api_client.get_annual_usage_for_service(
service_id,
get_current_financial_year(),
)
free_sms_allowance = billing_api_client.get_free_sms_fragment_limit_for_year(
current_service.id,
)
usage_data = get_annual_usage_breakdown(yearly_usage, free_sms_allowance)
sms_sent = usage_data["sms_sent"]
sms_allowance_remaining = usage_data["sms_allowance_remaining"]
job_response = job_api_client.get_jobs(service_id)["data"]
service_data_retention_days = 7
jobs = [
{
"job_id": job["id"],
"time_left": get_time_left(job["created_at"]),
"download_link": url_for(
".view_job_csv", service_id=current_service.id, job_id=job["id"]
),
"view_job_link": url_for(
".view_job", service_id=current_service.id, job_id=job["id"]
),
"created_at": job["created_at"],
"processing_finished": job.get("processing_finished"),
"processing_started": job.get("processing_started"),
"notification_count": job["notification_count"],
"created_by": job["created_by"],
"template_name": job["template_name"],
"original_file_name": job["original_file_name"],
}
for job in job_response
if job["job_status"] != "cancelled"
active_jobs = [job for job in job_response if job["job_status"] != "cancelled"]
sorted_jobs = sorted(active_jobs, key=lambda job: job["created_at"], reverse=True)
job_lists = [
{**job_dict, "finished_processing": job_is_finished(job_dict)}
for job_dict in sorted_jobs
]
total_messages = service_api_client.get_service_message_ratio(service_id)
messages_remaining = total_messages.get("messages_remaining", 0)
messages_sent = total_messages.get("messages_sent", 0)
all_statistics = template_statistics_client.get_template_statistics_for_service(
service_id, limit_days=7
)
template_statistics = aggregate_template_usage(all_statistics)
return render_template(
"views/dashboard/dashboard.html",
updates_url=url_for(".service_dashboard_updates", service_id=service_id),
partials=get_dashboard_partials(service_id),
jobs=jobs,
jobs=job_lists,
service_data_retention_days=service_data_retention_days,
sms_sent=sms_sent,
sms_allowance_remaining=sms_allowance_remaining,
messages_remaining=messages_remaining,
messages_sent=messages_sent,
template_statistics=template_statistics,
most_used_template_count=max(
[row["count"] for row in template_statistics] or [0]
),
)
@main.route("/daily_stats.json")
def get_daily_stats():
service_id = session.get("service_id")
date_range = get_stats_date_range()
stats = service_api_client.get_service_notification_statistics_by_day(
service_id, start_date=date_range["start_date"], days=date_range["days"]
def job_is_finished(job_dict):
done_statuses = DELIVERED_STATUSES + FAILURE_STATUSES + ["cancelled"]
processed_count = sum(
stat["count"]
for stat in job_dict["statistics"]
if stat["status"] in done_statuses
)
return jsonify(stats)
return job_dict["notification_count"] == processed_count
@main.route("/daily_stats_by_user.json")
def get_daily_stats_by_user():
service_id = session.get("service_id")
@main.route("/services/<uuid:service_id>/daily-stats.json")
@user_has_permissions()
def get_daily_stats(service_id):
date_range = get_stats_date_range()
days = date_range["days"]
user_timezone = request.args.get("timezone", "UTC")
stats_utc = service_api_client.get_service_notification_statistics_by_day(
service_id,
start_date=date_range["start_date"],
days=days,
)
local_stats = get_local_daily_stats_for_last_x_days(stats_utc, user_timezone, days)
return jsonify(local_stats)
def get_local_daily_stats_for_last_x_days(stats_utc, user_timezone, days):
tz = ZoneInfo(user_timezone)
today_local = datetime.now(tz).date()
start_local = today_local - timedelta(days=days - 1)
# Generate exactly days local dates, each with zeroed stats
days_list = [
(start_local + timedelta(days=i)).strftime("%Y-%m-%d") for i in range(days)
]
aggregator = {
d: {
"sms": {"delivered": 0, "failure": 0, "pending": 0, "requested": 0},
"email": {"delivered": 0, "failure": 0, "pending": 0, "requested": 0},
}
for d in days_list
}
# Convert each UTC timestamp to local date and iterate
for utc_ts, data in stats_utc.items():
utc_dt = datetime.strptime(utc_ts, "%Y-%m-%dT%H:%M:%SZ").replace(
tzinfo=ZoneInfo("UTC")
)
local_day = utc_dt.astimezone(tz).strftime("%Y-%m-%d")
if local_day in aggregator:
for msg_type in ["sms", "email"]:
for status in ["delivered", "failure", "pending", "requested"]:
aggregator[local_day][msg_type][status] += data[msg_type][status]
return aggregator
@main.route("/services/<uuid:service_id>/daily-stats-by-user.json")
@user_has_permissions()
def get_daily_stats_by_user(service_id):
date_range = get_stats_date_range()
user_id = current_user.id
stats = service_api_client.get_user_service_notification_statistics_by_day(
service_id,
user_id,
user_id=current_user.id,
start_date=date_range["start_date"],
days=date_range["days"],
)
return jsonify(stats)
@main.route("/services/<uuid:service_id>/dashboard.json")
@user_has_permissions("view_activity")
def service_dashboard_updates(service_id):
return jsonify(**get_dashboard_partials(service_id))
@main.route("/services/<uuid:service_id>/template-activity")
@user_has_permissions("view_activity")
def template_history(service_id):
return redirect(url_for("main.template_usage", service_id=service_id), code=301)
@main.route("/services/<uuid:service_id>/template-usage")
@user_has_permissions("view_activity")
def template_usage(service_id):
@@ -216,103 +226,6 @@ def usage(service_id):
)
@main.route("/services/<uuid:service_id>/monthly")
@user_has_permissions("view_activity")
def monthly(service_id):
year, current_financial_year = requested_and_current_financial_year(request)
return render_template(
"views/dashboard/monthly.html",
months=format_monthly_stats_to_list(
service_api_client.get_monthly_notification_stats(service_id, year)["data"]
),
years=get_tuples_of_financial_years(
partial_url=partial(url_for, ".monthly", service_id=service_id),
start=current_financial_year - 2,
end=current_financial_year,
),
selected_year=year,
)
@main.route("/services/<uuid:service_id>/inbox")
@user_has_permissions("view_activity")
@service_has_permission("inbound_sms")
def inbox(service_id):
return render_template(
"views/dashboard/inbox.html",
partials=get_inbox_partials(service_id),
updates_url=url_for(
".inbox_updates", service_id=service_id, page=request.args.get("page")
),
)
@main.route("/services/<uuid:service_id>/inbox.json")
@user_has_permissions("view_activity")
@service_has_permission("inbound_sms")
def inbox_updates(service_id):
return jsonify(get_inbox_partials(service_id))
@main.route("/services/<uuid:service_id>/inbox.csv")
@user_has_permissions("view_activity")
def inbox_download(service_id):
return Response(
Spreadsheet.from_rows(
[
[
"Phone number",
"Message",
"Received",
]
]
+ [
[
format_phone_number_human_readable(message["user_number"]),
message["content"].lstrip(("=+-@")),
format_datetime_numeric(message["created_at"]),
]
for message in service_api_client.get_inbound_sms(service_id)["data"]
]
).as_csv_data,
mimetype="text/csv",
headers={
"Content-Disposition": 'inline; filename="Received text messages {}.csv"'.format(
format_date_numeric(datetime.utcnow().isoformat())
)
},
)
def get_inbox_partials(service_id):
page = int(request.args.get("page", 1))
inbound_messages_data = service_api_client.get_most_recent_inbound_sms(
service_id, page=page
)
inbound_messages = inbound_messages_data["data"]
if not inbound_messages:
inbound_number = current_service.inbound_number
else:
inbound_number = None
prev_page = None
if page > 1:
prev_page = generate_previous_dict("main.inbox", service_id, page)
next_page = None
if inbound_messages_data["has_next"]:
next_page = generate_next_dict("main.inbox", service_id, page)
return {
"messages": render_template(
"views/dashboard/_inbox_messages.html",
messages=inbound_messages,
inbound_number=inbound_number,
prev_page=prev_page,
next_page=next_page,
)
}
def filter_out_cancelled_stats(template_statistics):
return [s for s in template_statistics if s["status"] != "cancelled"]
@@ -344,71 +257,6 @@ def aggregate_template_usage(template_statistics, sort_key="count"):
return sorted(templates, key=lambda x: x[sort_key], reverse=True)
def aggregate_notifications_stats(template_statistics):
template_statistics = filter_out_cancelled_stats(template_statistics)
notifications = {
template_type: {status: 0 for status in ("requested", "delivered", "failed")}
for template_type in ["sms", "email"]
}
for stat in template_statistics:
notifications[stat["template_type"]]["requested"] += stat["count"]
if stat["status"] in DELIVERED_STATUSES:
notifications[stat["template_type"]]["delivered"] += stat["count"]
elif stat["status"] in FAILURE_STATUSES:
notifications[stat["template_type"]]["failed"] += stat["count"]
return notifications
def get_dashboard_partials(service_id):
all_statistics = template_statistics_client.get_template_statistics_for_service(
service_id, limit_days=7
)
template_statistics = aggregate_template_usage(all_statistics)
stats = aggregate_notifications_stats(all_statistics)
dashboard_totals = (get_dashboard_totals(stats),)
free_sms_allowance = billing_api_client.get_free_sms_fragment_limit_for_year(
current_service.id,
)
# These 2 calls will update the dashboard sms allowance count while in trial mode.
billing_api_client.get_monthly_usage_for_service(
service_id, get_current_financial_year()
)
billing_api_client.create_or_update_free_sms_fragment_limit(
service_id, free_sms_fragment_limit=free_sms_allowance
)
yearly_usage = billing_api_client.get_annual_usage_for_service(
service_id,
get_current_financial_year(),
)
return {
"upcoming": render_template(
"views/dashboard/_upcoming.html",
),
"inbox": render_template(
"views/dashboard/_inbox.html",
),
"totals": render_template(
"views/dashboard/_totals.html",
service_id=service_id,
statistics=dashboard_totals[0],
),
"template-statistics": render_template(
"views/dashboard/template-statistics.html",
template_statistics=template_statistics,
most_used_template_count=max(
[row["count"] for row in template_statistics] or [0]
),
),
"usage": render_template(
"views/dashboard/_usage.html",
**get_annual_usage_breakdown(yearly_usage, free_sms_allowance),
),
}
def get_dashboard_totals(statistics):
for msg_type in statistics.values():

View File

@@ -15,8 +15,6 @@ from app.main import main
from app.main.views.pricing import CURRENT_SMS_RATE
from app.main.views.sub_navigation_dictionaries import (
about_notify_nav,
best_practices_nav,
features_nav,
using_notify_nav,
)
from app.utils.user import user_is_logged_in
@@ -25,11 +23,6 @@ from app.utils.user import user_is_logged_in
# Hook to check for feature flags
@main.before_request
def check_feature_flags():
if request.path.startswith("/guides") and not current_app.config.get(
"FEATURE_BEST_PRACTICES_ENABLED", False
):
abort(404)
if request.path.startswith("/about") and not current_app.config.get(
"FEATURE_ABOUT_PAGE_ENABLED", False
):
@@ -39,11 +32,7 @@ def check_feature_flags():
@main.route("/test/feature-flags")
def test_feature_flags():
return jsonify(
{
"FEATURE_BEST_PRACTICES_ENABLED": current_app.config[
"FEATURE_BEST_PRACTICES_ENABLED"
]
}
{"FEATURE_ABOUT_PAGE_ENABLED": current_app.config["FEATURE_ABOUT_PAGE_ENABLED"]}
)
@@ -111,44 +100,6 @@ def callbacks():
return redirect(url_for("main.documentation"), 301)
# --- Features page set --- #
@main.route("/features")
@user_is_logged_in
def features():
return render_template("views/features.html", navigation_links=features_nav())
@main.route("/features/roadmap", endpoint="roadmap")
@user_is_logged_in
def roadmap():
return render_template("views/roadmap.html", navigation_links=features_nav())
@main.route("/features/sms")
@user_is_logged_in
def features_sms():
return render_template(
"views/features/text-messages.html", navigation_links=features_nav()
)
@main.route("/features/security", endpoint="security")
@user_is_logged_in
def security():
return render_template("views/security.html", navigation_links=features_nav())
@main.route("/features/using_notify")
@user_is_logged_in
def using_notify():
return (
render_template("views/using-notify.html", navigation_links=features_nav()),
410,
)
@main.route("/using-notify/delivery-status")
@user_is_logged_in
def message_status():
@@ -203,78 +154,75 @@ def trial_mode_new():
)
@main.route("/guides/best-practices")
@main.route("/using-notify/best-practices")
@user_is_logged_in
def best_practices():
return render_template(
"views/guides/best-practices.html",
navigation_links=best_practices_nav(),
navigation_links=using_notify_nav(),
)
@main.route("/guides/clear-goals")
@main.route("/using-notify/best-practices/clear-goals")
@user_is_logged_in
def clear_goals():
return render_template(
"views/guides/clear-goals.html",
navigation_links=best_practices_nav(),
navigation_links=using_notify_nav(),
)
@main.route("/guides/rules-and-regulations")
@main.route("/using-notify/best-practices/rules-and-regulations")
@user_is_logged_in
def rules_and_regulations():
return render_template(
"views/guides/rules-and-regulations.html",
navigation_links=best_practices_nav(),
navigation_links=using_notify_nav(),
)
@main.route("/guides/establish-trust")
@main.route("/using-notify/best-practices/establish-trust")
@user_is_logged_in
def establish_trust():
return render_template(
"views/guides/establish-trust.html",
navigation_links=best_practices_nav(),
navigation_links=using_notify_nav(),
)
@main.route("/guides/write-for-action")
@main.route("/using-notify/best-practices/write-for-action")
@user_is_logged_in
def write_for_action():
return render_template(
"views/guides/write-for-action.html",
navigation_links=best_practices_nav(),
navigation_links=using_notify_nav(),
)
@main.route("/guides/multiple-languages")
@main.route("/using-notify/best-practices/multiple-languages")
@user_is_logged_in
def multiple_languages():
return render_template(
"views/guides/multiple-languages.html",
navigation_links=best_practices_nav(),
navigation_links=using_notify_nav(),
)
@main.route("/guides/benchmark-performance")
@main.route("/using-notify/best-practices/benchmark-performance")
@user_is_logged_in
def benchmark_performance():
return render_template(
"views/guides/benchmark-performance.html",
navigation_links=best_practices_nav(),
navigation_links=using_notify_nav(),
)
@main.route("/guides/using-notify/guidance")
@main.route("/using-notify/how-to")
@user_is_logged_in
def guidance_index():
def how_to():
return render_template(
"views/guidance/index.html",
"views/how-to/index.html",
navigation_links=using_notify_nav(),
feature_best_practices_enabled=current_app.config[
"FEATURE_BEST_PRACTICES_ENABLED"
],
)
@@ -282,6 +230,7 @@ def guidance_index():
def contact():
return render_template(
"views/contact.html",
navigation_links=about_notify_nav(),
)
@@ -313,47 +262,60 @@ def why_text_messaging():
def join_notify():
return render_template(
"views/join-notify.html",
navigation_links=about_notify_nav(),
)
@main.route("/using-notify/guidance/create-and-send-messages")
@main.route("/using-notify/how-to/create-and-send-messages")
@user_is_logged_in
def create_and_send_messages():
return render_template(
"views/guidance/create-and-send-messages.html",
"views/how-to/create-and-send-messages.html",
navigation_links=using_notify_nav(),
)
@main.route("/using-notify/guidance/edit-and-format-messages")
@main.route("/using-notify/how-to/edit-and-format-messages")
@user_is_logged_in
def edit_and_format_messages():
return render_template(
"views/guidance/edit-and-format-messages.html",
"views/how-to/edit-and-format-messages.html",
navigation_links=using_notify_nav(),
)
@main.route("/using-notify/guidance/send-files-by-email")
@main.route("/using-notify/how-to/send-files-by-email")
@user_is_logged_in
def send_files_by_email():
return render_template(
"views/guidance/send-files-by-email.html",
"views/how-to/send-files-by-email.html",
navigation_links=using_notify_nav(),
)
@main.route("/studio")
def studio():
return render_template(
"views/studio.html",
)
@main.route("/acceptable-use-policy")
def acceptable_use_policy():
return render_template(
"views/acceptable-use-policy.html",
)
# --- Redirects --- #
@main.route("/roadmap", endpoint="old_roadmap")
@main.route("/information-security", endpoint="information_security")
@main.route("/using_notify", endpoint="old_using_notify")
@main.route("/information-risk-management", endpoint="information_risk_management")
@main.route("/integration_testing", endpoint="old_integration_testing")
def old_page_redirects():
redirects = {
"main.old_roadmap": "main.roadmap",
"main.information_security": "main.using_notify",
"main.old_using_notify": "main.using_notify",
"main.information_risk_management": "main.security",

View File

@@ -57,7 +57,6 @@ def view_job(service_id, job_id):
filter_args = parse_filter_args(request.args)
filter_args["status"] = set_status_filters(filter_args)
return render_template(
"views/jobs/job.html",
job=job,
@@ -402,7 +401,9 @@ def get_job_partials(job):
)
if request.referrer is not None:
session["arrived_from_preview_page"] = "check" in request.referrer
session["arrived_from_preview_page"] = ("check" in request.referrer) or (
"help=0" in request.referrer
)
else:
session["arrived_from_preview_page"] = False

View File

@@ -116,6 +116,70 @@ def download_all_users():
return response
@main.route("/platform-admin/get-redis-report")
@user_is_platform_admin
def get_redis_report():
memory_info = redis_client.info("memory")
memory_used = memory_info.get("used_memory_human", "N/A")
max_memory = memory_info.get("maxmemory_human", "N/A")
if max_memory == "0B":
max_memory = "No set limit"
mem_fragmentation = memory_info.get("mem_fragmentation_ratio", "N/A")
frag_quality = "Swapping (bad)"
if mem_fragmentation >= 1.0:
frag_quality = "Healthy"
if mem_fragmentation > 1.5:
frag_quality = "Problematic"
if mem_fragmentation > 2.0:
frag_quality = "Severe fragmentation"
frag_note = ""
if mem_fragmentation > 2.0:
frag_note = "Use MEMORY PURGE.\nReplace multiple small keys with hashes.\nAvoid long keys.\nSet max_memory."
elif mem_fragmentation < 1.0:
frag_note = "Allocate more RAM.\nSet max_memory."
keys = redis_client.keys("*")
key_details = []
for key in keys:
key_type = redis_client.type(key).decode("utf-8")
ttl = redis_client.ttl(key)
ttl_str = "No Expiry" if ttl == -1 else f"{ttl} seconds"
key_details.append(
{"Key": key.decode("utf-8"), "Type": key_type, "TTL": ttl_str}
)
output = StringIO()
writer = csv.writer(
output,
)
writer.writerow(["Redis Report"])
writer.writerow([])
writer.writerow(["Memory"])
writer.writerow(["", "Memory Used", memory_used])
writer.writerow(["", "Max Memory", max_memory])
writer.writerow(["", "Memory Fragmentation Ratio", mem_fragmentation])
writer.writerow(["", "Memory Fragmentation Quality", frag_quality, frag_note])
writer.writerow([])
writer.writerow(["Keys Overview"])
writer.writerow(["", "TTL", "Type", "Key"])
for key_detail in key_details:
writer.writerow(
["", key_detail["TTL"], key_detail["Type"], key_detail["Key"][0:50]]
)
csv_data = output.getvalue()
# Create a direct download response with the CSV data and appropriate headers
response = Response(csv_data, content_type="text/csv; charset=utf-8")
response.headers["Content-Disposition"] = "attachment; filename=redis.csv"
return response
def is_over_threshold(number, total, threshold):
percentage = number / total * 100 if total else 0
return percentage > threshold

View File

@@ -167,7 +167,7 @@ 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]
error_message = '<span class="usa-error-message">'
error_message = '<span class="error-message usa-error-message">'
error_message = f"{error_message}{first_field_errors[0]}"
error_message = f"{error_message}</span>"
error_message = Markup(error_message)
@@ -483,7 +483,7 @@ def send_one_off_step(service_id, template_id, step_index):
link_to_upload=(
request.endpoint == "main.send_one_off_step" and step_index == 0
),
errors=form.errors if form.errors else None
errors=form.errors if form.errors else None,
)

View File

@@ -68,11 +68,12 @@ def _get_access_token(code): # pragma: no cover
id_token = get_id_token(response_json)
nonce = id_token["nonce"]
nonce_key = f"login-nonce-{unquote(nonce)}"
stored_nonce = redis_client.get(nonce_key).decode("utf8")
if not os.getenv("NOTIFY_ENVIRONMENT") == "development":
stored_nonce = redis_client.get(nonce_key).decode("utf8")
if nonce != stored_nonce:
current_app.logger.error(f"Nonce Error: {nonce} != {stored_nonce}")
abort(403)
if nonce != stored_nonce:
current_app.logger.error(f"Nonce Error: {nonce} != {stored_nonce}")
abort(403)
try:
access_token = response_json["access_token"]
@@ -112,7 +113,7 @@ def _do_login_dot_gov(): # $ pragma: no cover
verify_key = f"login-verify_email-{unquote(state)}"
verify_path = bool(redis_client.get(verify_key))
if not verify_path:
if not verify_path and not os.getenv("NOTIFY_ENVIRONMENT") == "development":
state_key = f"login-state-{unquote(state)}"
stored_state = unquote(redis_client.get(state_key).decode("utf8"))
if state != stored_state:

View File

@@ -1,88 +1,44 @@
from flask import current_app
def features_nav():
return [
{
"name": "Features",
"link": "main.features",
"sub_navigation_items": [
# {
# "name": "Text messages",
# "link": "main.features_sms",
# },
],
},
{
"name": "Roadmap",
"link": "main.roadmap",
},
{
"name": "Security",
"link": "main.security",
},
]
def using_notify_nav():
nav_items = [
{"name": "Get started", "link": "main.get_started"},
{"name": "Guides", "link": "main.best_practices"},
{"name": "Trial mode", "link": "main.trial_mode_new"},
{"name": "Tracking usage", "link": "main.pricing"},
{"name": "Delivery Status", "link": "main.message_status"},
{"name": "Guidance", "link": "main.guidance_index"},
]
if not current_app.config.get("FEATURE_BEST_PRACTICES_ENABLED"):
nav_items = [
item for item in nav_items if item["link"] != "main.best_practices"
]
return nav_items
def best_practices_nav():
return [
{
"name": "Best Practices",
"name": "Best practices",
"link": "main.best_practices",
},
{
"name": "Clear goals",
"link": "main.clear_goals",
},
{
"name": "Rules and regulations",
"link": "main.rules_and_regulations",
},
{
"name": "Establish trust",
"link": "main.establish_trust",
"sub_navigation_items": [
{
"name": "Get the word out",
"link": "main.establish_trust#get-the-word-out",
"name": "Clear goals",
"link": "main.clear_goals",
},
{
"name": "As people receive texts",
"link": "main.establish_trust#as-people-receive-texts",
"name": "Rules and regulations",
"link": "main.rules_and_regulations",
},
{
"name": "Establish trust",
"link": "main.establish_trust",
},
{
"name": "Write for action",
"link": "main.write_for_action",
},
{
"name": "Multiple languages",
"link": "main.multiple_languages",
},
{
"name": "Benchmark performance",
"link": "main.benchmark_performance",
},
],
},
{
"name": "Write for action",
"link": "main.write_for_action",
},
{
"name": "Multiple languages",
"link": "main.multiple_languages",
},
{
"name": "Benchmark performance",
"link": "main.benchmark_performance",
},
{"name": "Trial mode", "link": "main.trial_mode_new"},
{"name": "Tracking usage", "link": "main.pricing"},
{"name": "Delivery status", "link": "main.message_status"},
{"name": "How to", "link": "main.how_to"},
]
return nav_items
def about_notify_nav():
return [
@@ -93,20 +49,6 @@ def about_notify_nav():
{
"name": "Why text messaging",
"link": "main.why_text_messaging",
"sub_sub_navigation_items": [
{
"name": "Reach people using a common method",
"link": "main.why_text_messaging#reach-people-using-a-common-method",
},
{
"name": "Improve customer experience",
"link": "main.why_text_messaging#improve-customer-experience",
},
{
"name": "What texting is best for",
"link": "main.why_text_messaging#what-texting-is-best-for",
},
],
},
{
"name": "Security",
@@ -115,7 +57,11 @@ def about_notify_nav():
],
},
{
"name": "Contact",
"name": "Join Notify",
"link": "main.join_notify",
},
{
"name": "Contact us",
"link": "main.contact",
},
]

View File

@@ -40,12 +40,6 @@ class HeaderNavigation(Navigation):
"support": {
"support",
},
"features": {
"features",
"features_sms",
"roadmap",
"security",
},
"best_practices": {
"best_practices",
"clear_goals",
@@ -57,16 +51,13 @@ class HeaderNavigation(Navigation):
},
"using_notify": {
"get_started",
"using_notify",
"pricing",
"trial_mode_new",
"message_status",
"guidance_index",
"how_to",
},
"accounts-or-dashboard": {
"conversation",
"inbox",
"monthly",
"service_dashboard",
"template_usage",
"view_notification",
@@ -168,8 +159,6 @@ class MainNavigation(Navigation):
},
"dashboard": {
"conversation",
"inbox",
"monthly",
"service_dashboard",
"template_usage",
"view_notification",

View File

@@ -1,24 +1,55 @@
import json
from app.extensions import redis_client
from app.notify_client import NotifyAdminAPIClient
class BillingAPIClient(NotifyAdminAPIClient):
def get_monthly_usage_for_service(self, service_id, year):
return self.get(
monthly_usage = redis_client.get(f"monthly-usage-summary-{service_id}-{year}")
if monthly_usage is not None:
return json.loads(monthly_usage.decode("utf-8"))
result = self.get(
"/service/{0}/billing/monthly-usage".format(service_id),
params=dict(year=year),
)
redis_client.set(
f"monthly-usage-summary-{service_id}-{year}",
json.dumps(result),
ex=30,
)
return result
def get_annual_usage_for_service(self, service_id, year=None):
return self.get(
annual_usage = redis_client.get(f"yearly-usage-summary-{service_id}-{year}")
if annual_usage is not None:
return json.loads(annual_usage.decode("utf-8"))
result = self.get(
"/service/{0}/billing/yearly-usage-summary".format(service_id),
params=dict(year=year),
)
redis_client.set(
f"yearly-usage-summary-{service_id}-{year}",
json.dumps(result),
ex=30,
)
return result
def get_free_sms_fragment_limit_for_year(self, service_id, year=None):
frag_limit = redis_client.get(f"free-sms-fragment-limit-{service_id}-{year}")
if frag_limit is not None:
return json.loads(frag_limit.decode("utf-8"))
result = self.get(
"/service/{0}/billing/free-sms-fragment-limit".format(service_id),
params=dict(financial_year_start=year),
)
redis_client.set(
f"free-sms-fragment-limit-{service_id}-{year}",
json.dumps(result["free_sms_fragment_limit"]),
ex=30,
)
return result["free_sms_fragment_limit"]
def create_or_update_free_sms_fragment_limit(
@@ -36,13 +67,28 @@ class BillingAPIClient(NotifyAdminAPIClient):
)
def get_data_for_billing_report(self, start_date, end_date):
return self.get(
x_start_date = str(start_date)
x_start_date = x_start_date.replace(" ", "_")
x_end_date = str(end_date)
x_end_date = x_end_date.replace(" ", "_")
billing_data = redis_client.get(
f"get-data-for-billing-report-{x_start_date}-{x_end_date}"
)
if billing_data is not None:
return json.loads(billing_data.decode("utf-8"))
result = self.get(
url="/platform-stats/data-for-billing-report",
params={
"start_date": str(start_date),
"end_date": str(end_date),
},
)
redis_client.set(
f"get-data-for-billing-report-{x_start_date}-{x_end_date}",
json.dumps(result),
ex=30,
)
return result
def get_data_for_volumes_by_service_report(self, start_date, end_date):
return self.get(

View File

@@ -1,3 +1,6 @@
import json
from app.extensions import redis_client
from app.notify_client import NotifyAdminAPIClient, _attach_current_user
@@ -41,7 +44,7 @@ class NotificationApiClient(NotifyAdminAPIClient):
if job_id:
return method(
url="/service/{}/job/{}/notifications".format(service_id, job_id),
**kwargs
**kwargs,
)
else:
if limit_days is not None:
@@ -96,9 +99,20 @@ class NotificationApiClient(NotifyAdminAPIClient):
)
def get_notification_count_for_job_id(self, *, service_id, job_id):
return self.get(
counts = redis_client.get(
f"notification-count-for-job-id-{service_id}-{job_id}"
)
if counts is not None:
return json.loads(counts.decode("utf-8"))
result = self.get(
url="/service/{}/job/{}/notification_count".format(service_id, job_id)
)["count"]
)
redis_client.set(
f"notification-count-for-job-id-{service_id}-{job_id}",
json.dumps(result["count"]),
ex=30,
)
return result["count"]
notification_api_client = NotificationApiClient()

View File

@@ -1,3 +1,4 @@
import json
from datetime import datetime, timezone
from app.extensions import redis_client
@@ -517,7 +518,18 @@ class ServiceAPIClient(NotifyAdminAPIClient):
return int(count)
def get_global_notification_count(self, service_id):
return self.get("/service/{}/notification-count".format(service_id))
notification_count = redis_client.get(f"notification-count-{service_id}")
if notification_count is not None:
return json.loads(notification_count.decode("utf-8"))
notification_count = self.get(
"/service/{}/notification-count".format(service_id)
)
redis_client.set(
f"notification-count-{service_id}", json.dumps(notification_count), ex=30
)
return notification_count
def get_service_invite_data(self, redis_key):
"""
@@ -525,6 +537,11 @@ class ServiceAPIClient(NotifyAdminAPIClient):
"""
return self.get("/service/invite/redis/{0}".format(redis_key))
def get_service_message_ratio(self, service_id):
return self.get(
url="service/get-service-message-ratio?service_id={0}".format(service_id),
)
service_api_client = ServiceAPIClient()

View File

@@ -116,7 +116,7 @@ class UserApiClient(NotifyAdminAPIClient):
data["next"] = next_string
if code_type == "email":
data["email_auth_link_host"] = self.admin_url
endpoint = f"/user/{user_id}/{code_type}-code"
endpoint = f"/user/{user_id}/{code_type}-code"
current_app.logger.warn(hilite(f"Sending verify_code {code_type} to {user_id}"))
self.post(endpoint, data=data)

View File

@@ -14,10 +14,8 @@
<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 %}
@@ -56,28 +54,19 @@
{% for item in navigation_links %}
<li class="usa-sidenav__item">
<a href="{{ url_for(item.link) }}"
class="parent-link {% if item['link'] == request.endpoint %} usa-current {% endif %}"
aria-haspopup="true" aria-expanded="false">
class="parent-link {% if request.endpoint.startswith(item['link']) or item.sub_navigation_items | selectattr('link', 'equalto', request.endpoint) | list | length > 0 %} usa-current {% endif %}"
aria-haspopup="true"
aria-expanded="{{ 'true' if request.endpoint.startswith(item['link']) else 'false' }}">
{{ item.name }}
</a>
{% if item.sub_navigation_items %}
<ul class="usa-sidenav__sublist" role="menu">
{% for sub_item in item.sub_navigation_items %}
<li role="menuitem">
<a href="{{ url_for(sub_item.link.split('#')[0]) }}#{{ sub_item.link.split('#')[1] }}">
<a href="{{ url_for(sub_item.link.split('#')[0]) }}#{{ sub_item.link.split('#')[1] }}"
class="{% if request.endpoint == sub_item['link'] %}usa-current bold{% endif %}">
{{ sub_item.name }}
</a>
{% if sub_item.sub_sub_navigation_items %}
<ul class="usa-sidenav__sublist usa-sidenav__sub-sublist" role="menu">
{% for sub_sub_item in sub_item.sub_sub_navigation_items %}
<li role="menuitem">
<a href="{{ url_for(sub_sub_item.link.split('#')[0]) }}#{{ sub_sub_item.link.split('#')[1] }}">
{{ sub_sub_item.name }}
</a>
</li>
{% endfor %}
</ul>
{% endif %}
</li>
{% endfor %}
</ul>
@@ -85,6 +74,8 @@
</li>
{% endfor %}
</ul>
</nav>
</div>
<div class="tablet:grid-col-10 tablet:padding-left-4 usa-prose site-prose">
@@ -152,10 +143,8 @@
{% 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

@@ -12,7 +12,7 @@
classes: params.label.classes,
isPageHeading: params.label.isPageHeading,
attributes: params.label.attributes,
for: params.id
for: params.text
}) | indent(2) | trim }}
{% if params.hint %}
{% set hintId = params.id + '-hint' %}
@@ -26,7 +26,7 @@
}) | indent(2) | trim }}
{% endif %}
{% if params.errorMessage %}
{% set errorId = params.id + '-error' %}
{% set errorId = params.label.text + '-error' %}
{% set describedBy = describedBy + ' ' + errorId if describedBy else errorId %}
{{ usaErrorMessage({
id: errorId,
@@ -34,13 +34,19 @@
attributes: params.errorMessage.attributes,
html: params.errorMessage.html,
text: params.errorMessage.text,
visuallyHiddenText: params.errorMessage.visuallyHiddenText
visuallyHiddenText: params.errorMessage.visuallyHiddenText,
}) | indent(2) | trim }}
{% endif %}
<input class="usa-input {%- if params.classes %} {{ params.classes }}{% endif %} {%- if params.errorMessage %} usa-input--error{% endif %}" id="{{ params.id }}" name="{{ params.name }}" type="{{ params.type | default('text') }}"
{%- if params.value %} value="{{ params.value}}"{% endif %}
{%- if describedBy %} aria-describedby="{{ describedBy }}"{% endif %}
{%- if params.autocomplete %} autocomplete="{{ params.autocomplete}}"{% endif %}
{%- if params.pattern %} pattern="{{ params.pattern }}"{% endif %}
{%- for attribute, value in params.attributes %} {{ attribute }}="{{ value }}"{% endfor -%}>
<input
class="usa-input {%- if params.classes %} {{ params.classes }}{% endif %} {%- if params.errorMessage %} usa-input--error{% endif %}"
id="{{ params.label.text | default('unknown') | slugify }}"
name="{{ params.name }}"
type="{{ params.type | default('text') }}"
{%- if params.value %} value="{{ params.value }}"{% endif %}
{%- if describedBy %} aria-describedby="{{ describedBy }}"{% endif %}
{%- if params.autocomplete %} autocomplete="{{ params.autocomplete }}"{% endif %}
{%- if params.pattern %} pattern="{{ params.pattern }}"{% endif %}
{%- for attribute, value in params.attributes %} {{ attribute }}="{{ value }}"{% endfor -%}
{%- if params.required %} required{% endif %}
/>
</div>

View File

@@ -2,7 +2,7 @@
{% set labelHtml %}
<label class="usa-label{%- if params.classes %} {{ params.classes }}{% endif %}"
{%- for attribute, value in params.attributes %} {{attribute}}="{{value}}"{% endfor %}
{%- if params.for %} for="{{ params.for }}"{% endif %}>
{%- if params.text %} for="{{ params.text | slugify }}"{% endif %}>
{{ params.html | safe if params.html else params.text }}
</label>
{% endset %}

View File

@@ -20,7 +20,7 @@
</span>
{% endif %}
{% if field.errors and show_errors %}
<span class="error-message">
<span class="error-message usa-error-message">
{{ field.errors[0] }}
</span>
{% endif %}

View File

@@ -32,6 +32,9 @@
<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="/studio" class="usa-identifier__required-link">About the Public Benefits Studio</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>
@@ -51,6 +54,9 @@
<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="/acceptable-use-policy" class="usa-identifier__required-link">Acceptable use policy</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>
@@ -58,16 +64,20 @@
</ul>
</div>
</nav>
<section class="usa-identifier__section usa-identifier__section--usagov"
<section class="usa-identifier__section"
aria-label="Github Repos">
<div class="usa-identifier__container">
<div class="usa-identifier__required-links-item">
Find us on Github:
</div>
<ul>
<li><a href="https://github.com/gsa/notifications-admin" class="usa-identifier__required-link">Notify.gov Admin repo</a></li>
<li><a href="https://github.com/gsa/notifications-api" class="usa-identifier__required-link">Notify.gov API repo</a></li>
</ul>
<ul class="usa-identifier__required-links-list">
<li class="usa-identifier__required-links-item">
<a href="https://github.com/gsa/notifications-admin" class="usa-identifier__required-link">Notify.gov Admin repo</a>
</li>
<li class="usa-identifier__required-links-item">
<a href="https://github.com/gsa/notifications-api" class="usa-identifier__required-link">Notify.gov API repo</a>
</li>
</ul>
</div>
</section>

View File

@@ -1,18 +1,17 @@
<ul class="usa-card-group">
<ul class="usa-card-group grid-row flex-row">
{% for item in card_contents %}
<li class="usa-card grid-col tablet:grid-col-4 {% if item.link %}linked-card linked-content{% endif %}">
<li class="usa-card grid-col mobile-lg:grid-col-12 tablet:grid-col-6 desktop:grid-col-4 {% if item.link %}linked-card linked-content{% endif %}">
{% if item.link %}
<a href="{{ item.link }}">
{% endif %}
<div class="usa-card__container">
{% if item.card_heading %}
<div class="usa-card__header">
{% if item.link %}
<a href="{{ item.link }}">
{% endif %}
<h3
class="usa-card__heading {% if text_align != 'left' or text_align is not defined %}text-center{% else %}text-left{% endif %} {% if item.link %}linked-card{% endif %}">
{{ item.card_heading }}
</h3>
{% if item.link %}
</a>
{% endif %}
</div>
{% endif %}
@@ -37,6 +36,9 @@
</div>
{% endif %}
</div>
{% if item.link %}
</a>
{% endif %}
</li>
{% endfor %}
</ul>

View File

@@ -1,49 +1,54 @@
{% set is_about_page = request.path.startswith('/about') %}
{% set is_join_notify_page = request.path.startswith('/join-notify') %}
{% set is_contact_page = request.path.startswith('/contact') %}
{% set is_information_section = is_about_page or is_join_notify_page or is_contact_page %}
{% if current_user.is_authenticated %}
{% 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')}
] %}
{% if FEATURE_BEST_PRACTICES_ENABLED %}
{% set navigation = navigation + [{"href": url_for('main.best_practices'), "text": "Guides", "active":
header_navigation.is_selected('best_practices')}] %}
{% endif %}
{% set navigation = navigation + [
{"href": url_for('main.features'), "text": "Features", "active": header_navigation.is_selected('features')},
{"href": url_for('main.support'), "text": "Contact us", "active": header_navigation.is_selected('support')}
{"href": url_for("main.show_accounts_or_dashboard"), "text": "Current service", "active": request.path.startswith('/accounts')},
{"href": url_for('main.get_started'), "text": "Using Notify", "active": request.path.startswith('/using-notify')},
{"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')}] %}
{% 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')}] %}
{% set navigation = navigation + [{"href": url_for('main.user_profile'), "text": "User profile", "active": header_navigation.is_selected('user-profile')}] %}
{% endif %}
{% if current_service %}
{% if current_user.has_permissions('manage_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"}
{"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"}
{"href": url_for('main.sign_out'), "text": "Sign out"}
] %}
{% endif %}
{% else %}
{% set secondaryNavigation = [{"href": url_for('main.sign_out'), "text": "Sign out"}] %}
{% endif %}
{% else %}
{% set navigation = [
{"href": url_for('main.about_notify'), "text": "About Notify", "active": is_about_page},
{"href": url_for('main.join_notify'), "text": "Join Notify", "active": is_join_notify_page},
{"href": url_for('main.contact'), "text": "Contact us", "active": is_contact_page}
] %}
{% endif %}
<header class="usa-header usa-header--extended">
<div class="usa-nav-container">
<div class="usa-navbar">
<div class="display-flex flex-align-center flex-justify">
<div class="display-flex flex-align-center flex-justify width-full">
<div class="usa-logo display-flex flex-align-center flex-justify" id="-logo">
<div class="logo-img display-flex">
<a href="/">
@@ -56,7 +61,7 @@ secondary_navigation.is_selected('settings')},
<button type="button" class="usa-menu-btn">Menu</button>
{% endif %}
</div>
{% if not current_user.is_authenticated and FEATURE_ABOUT_PAGE_ENABLED and request.path == '/about'%}
{% if not current_user.is_authenticated and is_information_section%}
<div class="usa-nav__login">
<a class="usa-button usa-button login-button login-button--primary margin-right-2"
href="{{ initial_signin_url }}">Sign

View File

@@ -32,7 +32,7 @@
<legend class="form-label {% if bold_legend %}bold{% endif %}">
{{ field.label.text }}
{% if field.errors %}
<span class="error-message" data-module="track-error" data-error-type="{{ field.errors[0] }}" data-error-label="{{ field.name }}">
<span class="error-message usa-error-message" data-module="track-error" data-error-type="{{ field.errors[0] }}" data-error-label="{{ field.name }}">
{{ field.errors[0] }}
</span>
{% endif %}

View File

@@ -52,7 +52,7 @@
</span>
{% endif %}
{% if field.errors %}
<span class="error-message" data-module="track-error" data-error-type="{{ field.errors[0] }}" data-error-label="{{ field.name }}">
<span class="error-message usa-error-message" data-module="track-error" data-error-type="{{ field.errors[0] }}" data-error-label="{{ field.name }}">
{{ field.errors[0] }}
</span>
{% endif %}

View File

@@ -16,19 +16,9 @@
placeholder=''
) %}
<div
class="form-group{% if field.errors %} form-group-error{% endif %} {{ extra_form_group_classes }}"
class="usa-form-group{% if field.errors %} usa-form-group--error{% endif %} {{ extra_form_group_classes }}"
data-module="{% if autofocus %}autofocus{% elif colour_preview %}colour-preview{% endif %}"
>
{% if field.errors %}
<div class="usa-alert usa-alert--error edit-textbox-error-mt" role="alert">
<div class="usa-alert__body">
<h4 class="usa-alert__heading">Error message</h4>
<p class="usa-alert__text" data-module="track-error" data-error-type="{{ field.errors[0] }}" data-error-label="{{ field.name }}">
{% if not safe_error_message %}{{ field.errors[0] }}{% else %}{{ field.errors[0]|safe }}{% endif %}
</p>
</div>
</div>
{% endif %}
<label class="usa-label" for="{{ field.name }}">
{% if label %}
{{ label }}
@@ -41,6 +31,12 @@
{{ hint }}
</div>
{% endif %}
{% if field.errors %}
<span id="{{ field.name}}-error" class="error-message usa-error-message" data-module="track-error" data-error-type="{{ field.errors[0] }}" data-error-label="{{ field.name }}" tabindex="-1" aria-live="assertive" role="alert">
<span class="usa-sr-only">Error:</span>
{% if not safe_error_message %}{{ field.errors[0] }}{% else %}{{ field.errors[0]|safe }}{% endif %}
</span>
{% endif %}
{%
if highlight_placeholders or autosize
%}
@@ -59,6 +55,8 @@
data_highlight_placeholders='true' if highlight_placeholders else 'false',
rows=rows|string,
placeholder=placeholder,
aria_describedby=field.name+"-error",
required='required' if required else None,
**kwargs
) }}
{% if suffix %}

View File

@@ -21,11 +21,11 @@
<div class="ajax-block-container">
<p class='bottom-gutter'>
{% if job.still_processing or arrived_from_preview_page_url %}
{% if not job.finished_processing %}
{% if job.scheduled_for %}
<div class="usa-alert usa-alert--info">
<div class="usa-alert__body">
<h2 class="usa-alert__heading">Your text has been scheduled</h2>
<h2 class="usa-alert__heading">Your {{ 'message has' if job.notification_count == 1 else 'messages have' }} been scheduled</h2>
<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>
@@ -33,18 +33,46 @@
</div>
{{display_message_status}}
{% else %}
<div class="usa-alert usa-alert--success">
<div class="usa-alert__body">
<h2 class="usa-alert__heading">Your text has been sent</h2>
<p class="usa-alert__text">
{{ job.template_name }} - {{ current_service.name }} was sent on {% if job.processing_started %}
{{ job.processing_started|format_datetime_table }} {% else %}
{{ job.created_at|format_datetime_table }} {% endif %} by {{ job.created_by.name }}
</p>
{% if job.processing_started %}
<div class="usa-alert usa-alert--success">
<div class="usa-alert__body">
<h2 class="usa-alert__heading">
Your {{ 'message is' if job.notification_count == 1 else 'messages are' }} sending
</h2>
<p class="usa-alert__text">
{{ job.template_name }} - {{ current_service.name }}
has been sending since {{job.processing_started| format_datetime_normal}} by {{ job.created_by.name }}
</p>
</div>
</div>
</div>
{% else %}
<div class="usa-alert usa-alert--info">
<div class="usa-alert__body">
<h2 class="usa-alert__heading">
Your {{ 'message is' if job.notification_count == 1 else 'messages are' }} pending
</h2>
<p class="usa-alert__text">
{{ job.template_name }} - {{ current_service.name }}
has been pending since {{job.created_at|format_datetime_normal}} by {{ job.created_by.name }}
</p>
</div>
</div>
{% endif %}
{{display_message_status}}
{% endif %}
{% elif arrived_from_preview_page_url %}
<div class="usa-alert usa-alert--success">
<div class="usa-alert__body">
<h2 class="usa-alert__heading">
Your {{ 'message has' if job.notification_count == 1 else 'messages have' }} been sent
</h2>
<p class="usa-alert__text">
{{ job.template_name }} - {{ current_service.name }}
was sent on {{job.processing_started|format_datetime_normal}} by {{ job.created_by.name }}
</p>
</div>
</div>
{{display_message_status}}
{% endif %}
</p>
{% if job.status == 'sending limits exceeded'%}

View File

@@ -5,10 +5,6 @@
</h3>
<div id="message-length" class="usa-accordion__content usa-prose">
<p class="usa-body">
If your message is long then it will
cost more.
</p>
<p class="usa-body">
See <a class="usa-link" href="{{ url_for('.pricing') }}">pricing</a> for details.
Each message sent to a recipient counts as one text. Keep track on your Dashboard of how many messages have sent and are remaining.
</p>
</div>

View File

@@ -1,7 +1,6 @@
{% extends "base.html" %}
{% set page_title = "About Notify" %}
{% block per_page_title %}
{{page_title}}
{% endblock %}
@@ -27,7 +26,7 @@
{% set product_highlights = [
{
"svg_src": "#send",
"card_heading": "Send customized one-way customized messages",
"card_heading": "Send customized one-way messages",
"p_text": "Upload a file with recipient phone numbers and Notify.gov sends customized messages",
},
{
@@ -68,7 +67,7 @@
{% endfor %}
</ul>
<p><a href="/join-notify">See if Notify is right for you</a></p>
<p>Notify.gov is a product of the <a href="#">Public Benefits Studio</a>, a product accelerator inside
<p>Notify.gov is a product of the <a href="/studio">Public Benefits Studio</a>, a product accelerator inside
the federal government. </p>
</section>
{% endblock %}

View File

@@ -40,7 +40,7 @@
</p>
<p>
Texting not only helps programs reach people using a nearly-universal communication method, it is a cost effective
way to do so. With Notify.gov <a href="#">you can get started for free</a>, allowing you to try out
way to do so. With Notify.gov <a href="/join-notify">you can get started for free</a>, allowing you to try out
texting to complement your existing communications and outreach strategies.
</p>
<h2 id="what-texting-is-best-for">What texting is best for</h2>
@@ -51,35 +51,37 @@
{
"image_src": asset_url('images/calendar.svg'),
"card_heading": "Reminders",
"p_text": "In a text bubble // Your Quality Control food phone interview is on ((date)) at ((time)). Failure to
"p_text": "Your Quality Control food phone interview is on ((date)) at ((time)). Failure to
attend may lead to closure of your benefits. Call 1-800-222-3333 with questions.",
"alt_text": "reminder text example"
},
{
"image_src": asset_url('images/alert.svg'),
"card_heading": "Alerts to take action",
"p_text": "In a text bubble // Your household's Medicaid coverage is expiring. To keep getting Medicaid, you must
"p_text": "Your household's Medicaid coverage is expiring. To keep getting Medicaid, you must
complete your renewal by ((date)). You can renew online at dhs.state.gov…",
"alt_text": "alerts text example"
},
{
"image_src": asset_url('images/alarm.svg'),
"card_heading": "Important status updates",
"p_text": "In a text bubble // Your passport has been issued at the Los Angeles Passport Agency. Please come to the
"p_text": "Your passport has been issued at the Los Angeles Passport Agency. Please come to the
desk between 1:30pm and 2:30pm today to pick up your passport…",
"alt_text": "status update text example"
},
] %}
{% for item in card_contents %}
<div class="radius-lg border-2px maxw-tablet">
<div class="grid-row grid-gap-4 padding-4 padding-y-3 flex-align-center">
<div class="grid-col flex-3">
<p><b>{{item.card_heading}}</b></p>
<p>{{item.p_text}}</p>
<div class="grid-container-tablet padding-3 radius-lg border-2px margin-left-0 ">
<h3 class="usa-card__heading padding-y-2">{{item.card_heading}}</h3>
<div class="grid-row grid-gap-3 flex-align-center">
<div class="grid-col-auto ">
<p class="sms-message-wrapper">{{item.p_text}}</p>
</div>
<div class="grid-col-fill">
{% if item.image_src %}
<img src="{{ item.image_src }}" alt="{{ item.alt_text }}" class="height-15" />
{% endif %}
</div>
{% if item.image_src %}
<img src="{{item.image_src}}" alt="{{ item.alt_text }}" class="height-15" />
{% endif %}
</div>
</div>
{% endfor %}

View File

@@ -0,0 +1,38 @@
{% extends "base.html" %}
{% set page_title = "Acceptable Use Policy" %}
{% block per_page_title %}{{page_title}}{% endblock %}
{% block content_column_content %}
<section class="usa-prose">
<h1>{{page_title}}</h1>
<p>Use of the application indicates agreement with this acceptable use policy.</p>
<p>Acceptable uses of Notify.gov for this trial period include:</p>
<ul>
<li>Configuring test services and sending test SMS messages to learn and demonstrate how Notify.gov works.</li>
<li>Reviewing data provided by Notify.gov and configuring available settings.</li>
</ul>
<p>
In order to help us keep Notify.gov secure, we require that you use your account appropriately. When you use Notify.gov, you agree that youll respect these rules of behavior:
</p>
<ul>
<li>Conduct only authorized business on the system.</li>
<li>Maintain the confidentiality of your authentication credentials; a Notify.gov operator should never ask you to reveal them. We recommend using a password manager and strong credentials.</li>
<li>Log out when you no longer need session access. Never leave your computer unattended while logged into Notify.gov.</li>
<li>Report all security incidents or suspected incidents (such as improper or suspicious acts) related to Notify.gov systems and networks to Notify.gov support.</li>
<li>Safeguard system resources against waste, loss, abuse, unauthorized use or disclosure, and misappropriation.</li>
<li>Dont process U.S. classified national security information on the system.</li>
<li>Dont browse, search or reveal information hosted by Notify.gov except as required to perform your legitimate tasks or assigned duties.</li>
<li>Dont retrieve information, or in any other way disclose information, for someone who does not have authority to access that information.</li>
<li>Dont intentionally use a client that makes use of obsolete or insecure encryption algorithms.</li>
<li>Dont configure your browser to ignore security warnings which may involve your connection with Notify.gov; report warnings that you cant explain to Notify.gov support.</li>
<li>If you believe youve been granted more access than necessary to perform your legitimate tasks or assigned duties, immediately notify Notify.gov support.</li>
<li>Dont share your account with another person or create anonymous or group accounts. Your account is just for you.</li>
</ul>
<p>Access to systems and networks owned by Notify.gov is governed by, and subject to, all federal laws, including, but not limited to, the Privacy Act, 5 U.S.C. 552a, if the applicable Notify.gov system maintains individual Privacy Act information. Access to Notify.gov systems constitutes consent to the retrieval and disclosure of the information within the scope of your authorized access, subject to the Privacy Act, and applicable state and federal laws.</p>
<p>Please contact Notify.gov support if you have questions about these rules or dont understand them.</p>
</section>
{% endblock %}

View File

@@ -15,7 +15,7 @@
aria-label="For partnership inquiries">
<div class="usa-summary-box__body">
<div class="usa-summary-box__text">
<p>If you are a current Notify.gov partner and have technical issues or questions, we are available at <a
<p class="margin-0">If you are a current Notify.gov partner and have technical issues or questions, we are available at <a
href="mailto:notify-support@gsa.gov"
aria-label="Email Notify for technical questions at notify-support@gsa.gov">notify-support@gsa.gov</a></p>
</div>

View File

@@ -8,7 +8,7 @@
{% endblock %}
{% block backLink %}
{{ usaBackLink({ "href": url_for("main.inbox", service_id=current_service.id) }) }}
{{ usaBackLink({ "href": url_for("main.service_dashboard", service_id=current_service.id) }) }}
{% endblock %}
{% block maincolumn_content %}

View File

@@ -1,17 +0,0 @@
<div class="ajax-block">
{% if current_service.inbound_sms_summary != None %}
<a id="total-received" class="usa-link banner-dashboard" class="banner-dashboard" href="{{ url_for('.inbox', service_id=current_service.id) }}">
<span class="banner-dashboard-count">
{{ current_service.inbound_sms_summary.count|format_thousands }}
</span>
<span class="banner-dashboard-count-label">
{{ current_service.inbound_sms_summary.count|message_count_label('sms', suffix='received') }}
</span>
{% if current_service.inbound_sms_summary.most_recent %}
<span class="banner-dashboard-meta">
latest message {{ current_service.inbound_sms_summary.most_recent | format_delta }}
</span>
{% endif %}
</a>
{% endif %}
</div>

View File

@@ -1,38 +0,0 @@
{% from "components/table.html" import list_table, field, hidden_field_heading, right_aligned_field_heading, row_heading %}
{% from "components/previous-next-navigation.html" import previous_next_navigation %}
<div class="ajax-block-container">
{% if messages %}
<p class="bottom-gutter-2-3 top-gutter-1-2">
<a href="{{ url_for('.inbox_download', service_id=current_service.id) }}" download class="usa-link bold">Download these messages</a>
</p>
{% endif %}
{% call(item, row_number) list_table(
messages,
caption="Inbox",
caption_visible=False,
empty_message='When users text your services phone number ({}) youll see the messages here'.format(inbound_number),
field_headings=[
'From',
'First two lines of message'
],
field_headings_visible=False
) %}
{% call field() %}
<a
class="usa-link file-list-filename"
href="{{ url_for('.conversation', service_id=current_service.id, notification_id=item.id) }}#n{{ item.id }}"
>
{{ item.user_number | format_phone_number_human_readable }}
</a>
<span class="file-list-hint">{{ item.content }}</span>
{% endcall %}
{% call field(align='right') %}
<span class="align-with-message-body">
{{ item.created_at | format_delta }}
</span>
{% endcall %}
{% endcall %}
{{ previous_next_navigation(prev_page, next_page) }}
</div>

View File

@@ -1,25 +0,0 @@
<div class="ajax-block-container">
<div class="grid-row grid-gap">
<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']) }}
{% else %}
{{ statistics['sms']['requested'] }}
{% endif %}
</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>
{# 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>
</div>

View File

@@ -0,0 +1,74 @@
<h2 class="line-height-sans-2 margin-bottom-0 margin-top-4">Recent activity</h2>
<div id="activityChartContainer">
<form class="usa-form">
<label class="usa-label" for="options">Account</label>
<select class="usa-select margin-bottom-2" name="options" id="options">
<option value disabled>- Select -</option>
<option value="service" selected>{{ current_service.name }}</option>
<option value="individual">{{ current_user.name }}</option>
</select>
</form>
<div id="activityChart">
<div class="chart-header">
<div class="chart-subtitle">{{ current_service.name }} - last 7 days</div>
<div class="chart-legend" role="region" aria-label="Legend"></div>
</div>
<div class="chart-container" id="weeklyChart"></div>
<table id="weeklyTable" class="usa-sr-only usa-table"></table>
</div>
</div>
<div id="aria-live-account" class="usa-sr-only" aria-live="polite"></div>
<div class="table-container" id="activityContainer" data-currentUserName="{{ current_user.name }}" data-currentServiceId="{{current_service.id}}">
<div id="tableActivity" class="table-overflow-x-auto">
<h2 id="table-heading" class="margin-top-4 margin-bottom-1">Service activity</h2>
<table class="usa-table job-table" id="activity-table">
<caption class="usa-sr-only">Table showing the sent jobs for {{current_service.name}}</caption>
<thead class="table-field-headings">
<tr>
<th scope="col" class="table-field-heading-first" id="jobId">Job ID#</th>
<th data-sortable scope="col" class="table-field-heading" scope="col">Template</th>
<th data-sortable scope="col" class="table-field-heading">Job status</th>
<th data-sortable scope="col" role="columnheader" class="table-field-heading sender-column">Sender
</th>
<th data-sortable scope="col" class="table-field-heading"># of Recipients</th>
</tr>
</thead>
<tbody>
{% if jobs %}
{% for job in jobs %}
<tr id="{{ job.id }}">
<td class="table-field jobid" role="rowheader">
<a class="usa-link" href="{{ url_for('.view_job', service_id=current_service.id, job_id=job.id )}}">
{{ job.id[:8] if job.id else 'Manually entered number' }}
</a>
</td>
<td class="table-field template">{{ job.template_name }}</td>
<td class="table-field time-sent">
{% if not job.finished_processing %}
{% if job.scheduled_for%}
Scheduled for {{ job.scheduled_for|format_datetime_table }}
{% elif job.processing_started %}
Sending since {{ job.processing_started|format_datetime_table }}
{% else %}
Pending since {{ job.created_at|format_datetime_table }}
{% endif %}
{% else %}
Sent on {{ job.processing_started|format_datetime_table }}
{% endif %}
</td>
<td class="table-field sender sender-column">{{ job.created_by.name }}</td>
<td class="table-field count-of-recipients">{{ job.notification_count }}</td>
</tr>
{% endfor %}
{% else %}
<tr class="table-row">
<td class="table-empty-message" colspan="10">No batched job messages found &thinsp;(messages are
kept for {{ service_data_retention_days }} days).</td>
</tr>
{% endif %}
</tbody>
</table>
</div>
</div>

View File

@@ -1,145 +1,52 @@
{% extends "withnav_template.html" %}
{% from "components/table.html" import list_table, field, text_field, link_field, right_aligned_field_heading, hidden_field_heading, row_heading, notification_status_field, notification_carrier_field, notification_carrier_message_field %}
{% from "components/table.html" import list_table, field, text_field, link_field, right_aligned_field_heading,
hidden_field_heading, row_heading, notification_status_field, notification_carrier_field,
notification_carrier_message_field %}
{% from "components/ajax-block.html" import ajax_block %}
{% block service_page_title %}
Dashboard
Dashboard
{% endblock %}
{% block maincolumn_content %}
<script type="text/javascript" src="{{ asset_url('js/setTimezone.js') }}"></script>
<script type="text/javascript" src="{{ asset_url('js/setTimezone.js') }}"></script>
<div class="dashboard margin-top-0 margin-bottom-2">
<div class="dashboard margin-top-0 margin-bottom-2">
<h1 class="usa-sr-only">Dashboard</h1>
{% if current_user.has_permissions('manage_templates') and not current_service.all_templates %}
{% include 'views/dashboard/write-first-messages.html' %}
{% include 'views/dashboard/write-first-messages.html' %}
{% endif %}
{{ ajax_block(partials, updates_url, 'upcoming') }}
{% include 'views/dashboard/_upcoming.html' %}
<h2 class="font-body-2xl line-height-sans-2 margin-top-0">{{ current_service.name }} Dashboard</h2>
{{ ajax_block(partials, updates_url, 'inbox') }}
<div id="totalMessageChartContainer" data-sms-sent="{{ sms_sent }}" data-sms-allowance-remaining="{{ sms_allowance_remaining }}">
<h2 id="chartTitle">Total messages</h2>
<svg id="totalMessageChart"></svg>
<div id="message"></div>
<div id="totalMessageChartContainer" data-messages-sent="{{ messages_sent }}" data-messages-remaining="{{ messages_remaining }}">
<div class="grid-row flex-align-center">
<h2 id="chartTitle" class="margin-right-1">Total messages</h2>
<button
type="button"
class="usa-tooltip usa-tooltip__information margin-right-0"
data-position="top"
title="Total messages track the sum of messages for the service: pending, failed, or delivered"
>
<span class="usa-sr-only">More information</span>
i
</button>
</div>
<svg id="totalMessageChart"></svg>
<div id="message"></div>
</div>
<div id="totalMessageTable" class="margin-0"></div>
<h2 class="line-height-sans-2 margin-bottom-0 margin-top-4">Recent activity</h2>
<div id="activityChartContainer">
<form class="usa-form">
<label class="usa-label" for="options">Account</label>
<select class="usa-select margin-bottom-2" name="options" id="options">
<option value disabled>- Select -</option>
<option value="service" selected>{{ current_service.name }}</option>
<option value="individual">{{ current_user.name }}</option>
</select>
</form>
<div id="activityChart">
<div class="chart-header">
<div class="chart-subtitle">{{ current_service.name }} - last 7 days</div>
<div class="chart-legend" role="region" aria-label="Legend"></div>
</div>
<div class="chart-container" id="weeklyChart"></div>
<table id="weeklyTable" class="usa-sr-only usa-table"></table>
</div>
</div>
<div id="aria-live-account" class="usa-sr-only" aria-live="polite"></div>
{% include 'views/dashboard/activity-table.html' %}
{% if current_user.has_permissions('manage_service') %}{% endif %}
<div class="table-container">
<div id="table1" class="table-overflow-x-auto hidden">
<h2 class="margin-top-4 margin-bottom-1">My activity</h2>
<table class="usa-table job-table">
<caption class="usa-sr-only">Table showing the sent jobs for {{current_user.name}}</caption>
<thead class="table-field-headings">
<tr>
<th scope="col" class="table-field-heading-first" id="jobId"><span>Job ID#</span></th>
<th data-sortable scope="col" class="table-field-heading"><span>Template</span></th>
<th data-sortable scope="col" class="table-field-heading"><span>Job status</span></th>
<th data-sortable scope="col" class="table-field-heading"><span># of Recipients</span></th>
</tr>
</thead>
<tbody>
{% if jobs %}
{% for job in jobs[:5] %}
{% if job.created_by.name == current_user.name %}
{% set notification = job.notifications[0] %}
<tr id="{{ job.job_id }}">
<td class="table-field jobid" role="rowheader">
<a class="usa-link" href="{{ job.view_job_link }}">
{{ job.job_id[:8] if job.job_id else 'Manually entered number' }}
</a>
</td>
<td class="table-field template">{{ job.template_name }}</td>
<td class="table-field time-sent">Sent on
{{ (job.processing_finished if job.processing_finished else job.processing_started
if job.processing_started else job.created_at)|format_datetime_table }}
</td>
<td class="table-field count-of-recipients">{{ job.notification_count }}</td>
</tr>
{% endif %}
{% endfor %}
{% else %}
<tr class="table-row">
<td class="table-empty-message" colspan="10">No batched job messages found &thinsp;(messages are kept for {{ service_data_retention_days }} days).</td>
</tr>
{% endif %}
</tbody>
</table>
</div>
{% include 'views/dashboard/most-used-templates.html' %}
<div id="table2" class="table-overflow-x-auto visible">
<h2 class="margin-top-4 margin-bottom-1">Service activity</h2>
<table class="usa-table job-table">
<caption class="usa-sr-only">Table showing the sent jobs for this service</caption>
<thead class="table-field-headings">
<tr>
<th scope="col" role="columnheader" class="table-field-heading-first" id="jobId"><span>Job ID#</span></th>
<th data-sortable scope="col" role="columnheader" class="table-field-heading"><span>Template</span></th>
<th data-sortable scope="col" role="columnheader" class="table-field-heading"><span>Job status</span></th>
<th data-sortable scope="col" role="columnheader" class="table-field-heading"><span>Sender</span></th>
<th data-sortable scope="col" role="columnheader" class="table-field-heading"><span># of Recipients</span></th>
</tr>
</thead>
<tbody>
{% if jobs %}
{% for job in jobs[:5] %}
{% set notification = job.notifications[0] %}
<tr id="{{ job.job_id }}">
<td class="table-field jobid" role="rowheader">
<a class="usa-link" href="{{ job.view_job_link }}">
{{ job.job_id[:8] if job.job_id else 'Manually entered number' }}
</a>
</td>
<td class="table-field template">{{ job.template_name }}</td>
<td class="table-field time-sent">Sent on
{{ (job.processing_finished if job.processing_finished else job.processing_started
if job.processing_started else job.created_at)|format_datetime_table }}
</td>
<td class="table-field sender">{{ job.created_by.name }}</td>
<td class="table-field count-of-recipients">{{ job.notification_count }}</td>
</tr>
{% endfor %}
{% else %}
<tr class="table-row">
<td class="table-empty-message" colspan="10">No batched job messages found &thinsp;(messages are kept for {{ service_data_retention_days }} days).</td>
</tr>
{% endif %}
</tbody>
</table>
</div>
</div>
{{ ajax_block(partials, updates_url, 'template-statistics') }}
</div>
</div>
{% endblock %}

View File

@@ -1,25 +0,0 @@
{% extends "withnav_template.html" %}
{% from "components/ajax-block.html" import ajax_block %}
{% from "components/page-header.html" import page_header %}
{% from "components/components/back-link/macro.njk" import usaBackLink %}
{% block service_page_title %}
Received text messages
{% endblock %}
{% block backLink %}
{{ usaBackLink({ "href": url_for('main.service_dashboard', service_id=current_service.id) }) }}
{% endblock %}
{% block maincolumn_content %}
{{ page_header('Received text messages') }}
{{ ajax_block(
partials,
updates_url,
'messages',
) }}
{% endblock %}

View File

@@ -34,12 +34,13 @@
<td><p>{{ item.template_folder }}</p></td>
<td><p>{{ item.last_used|format_datetime_table}}</p></td>
<td><p>{{ item.created_by }}</p></td>
<td><p>{{ item.count }}</p></td>
<td><p>{{ '{:,.0f}'.format(item.count) }}</p></td>
</tr>
{% endfor %}
</tbody>
</table>
<a href="/services/78409625-0c0a-485e-b82c-b19c8f4b1bdb/template-usage" class="usa-link show-more-no-border"><span>See templates by month</span></a>
<a
href="{{ url_for('.template_usage', service_id=current_service.id) }}" class="usa-link show-more-no-border"><span>See templates by month</span></a>
</div>
{% endif %}
</div>

View File

@@ -32,6 +32,8 @@
<div class="tablet:grid-col-9 mobile-lg:grid-col-12">
{{ form.name(param_extensions={
"extra_form_group_classes": "margin-bottom-2",
"id": "name",
"required": True,
"hint": {"text": "Your recipients will not see this"}
}) }}
{{ textbox(
@@ -41,7 +43,8 @@
hint=content_hint,
rows=5,
extra_form_group_classes='margin-bottom-1',
placeholder='Edit me! Check out the Personalization section below for details on cool ((stuff)) you can do with your messages!'
placeholder='Edit me! Check out the Personalization section below for details on cool ((stuff)) you can do with your messages!',
required=True
) }}
{% if current_user.platform_admin %}
{{ form.process_type }}

View File

@@ -11,10 +11,8 @@
<h1 class="font-body-2xl">Features</h1>
<p class="usa-body">If you work for the government, you can use Notify.gov to keep your users updated.</p>
<p class="usa-body">Notify makes it easy to create, customize, and send <a class="usa-link" href="{{ url_for('main.features_sms') }}">text messages</a>.</p>
<!-- <ul class="list list-bullet">
<li><a class="usa-link" href="{{ url_for('main.features_sms') }}">text messages</a></li>
</ul> -->
<p class="usa-body">Notify makes it easy to create, customize, and send text messages</a>.</p>
<p class="usa-body">You do not need any technical knowledge to use Notify.</p>
{% if not current_user.is_authenticated %}
<p class="usa-body"><a class="usa-link" href="{{ url_for('main.register') }}">Create an account</a> for free and try it yourself.</p>
@@ -30,7 +28,7 @@
<h2 class="heading-medium" id="personalised-messages">Personalized content</h2>
<p class="usa-body">Notify makes it easy to send personalized messages from a single template.</p>
<p class="usa-body">See <a class="usa-link" href="{{ url_for('.guidance_index', _anchor='personalized-content') }}">how to personalize your content</a>.</p>
<p class="usa-body">See <a class="usa-link" href="{{ url_for('.how_to', _anchor='personalized-content') }}">how to personalize your content</a>.</p>
<h2 class="heading-medium" id="bulk-sending">Bulk sending</h2>
<p class="usa-body">To send a batch of messages at once, upload a list of contact details to Notify. You can also schedule the date and time you want them to be sent.</p>

View File

@@ -1,252 +0,0 @@
{% extends "base.html" %}
{% from "components/table.html" import mapping_table, row, text_field, edit_field, field with context %}
{% from "components/sub-navigation.html" import sub_navigation %}
{% from "components/service-link.html" import service_link %}
{% block per_page_title %}
Guidance
{% endblock %}
{% block content_column_content %}
<h1 class="font-body-2xl margin-bottom-3">Guidance</h1>
<p>Notify allows you easily to create templates for messages for your recipients. You can customize messages to encourage
your recipient to manage their benefits and increase follow through.</p>
<p>Below we explain how to:</p>
<ul class="list list-bullet">
{% if not feature_best_practices_enabled %}
<li><a class="usa-link" href="#format-content">Format your content</a></li>
<li><a class="usa-link" href="#add-links">Add links</a></li>
{% endif %}
<li><a class="usa-link" href="#personalize-content">Personalize your content</a></li>
<li><a class="usa-link" href="#conditional-content">Add conditional content</a></li>
<li><a class="usa-link" href="#indentify-program">Identify your program</a></li>
<li><a class="usa-link" href="#prepare-data">Prepare your data</a></li>
{% if not feature_best_practices_enabled %}
<li><a class="usa-link" href="#prevent-fraud">Prevent fraud</a></li>
{% endif%}
</ul>
{# Format content #}
<h2 class="padding-top-1" id="format-content">Format your content</h2>
{% if not feature_best_practices_enabled %}
<p>Effective texts will help your message recipients take the steps needed to secure and keep the benefits and services
they depend on. To craft an effective text:</p>
<ul class="list list-bullet">
<li>Choose your messages thoughtfully. Text messages are best to remind/nudge someone to take a specific action or
communicate information at a particular time rather than pushing out broad or overly general information.</li>
<li>Clearly state the issue and expected response. Tell your recipient what you expect from them.</li>
<li>Say only one important thing per message.</li>
<li>Frame the message to encourage action and explain the consequences of not completing the desired action.</li>
</ul>
{% endif %}
<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>". You'll see existing templates.</li>
<li>Add a new template or choose an existing template and select Edit.</li>
</ol>
{% if not feature_best_practices_enabled %}
{# Add links #}
<h2 class="padding-top-1" id="add-links">Add links</h2>
<p>When composing a text message, links to websites or online applications can help your recipient respond quickly.</p>
<ul class="list list-bullet">
<li>Write URLs in full and Notify will convert them into links for you. Note that you cannot hyperlink text in Notify
messages.</li>
<li>For link click tracking, you can consider adding campaign parameters to URLs.</li>
<li>All links should point to a government domain.</li>
<li>Link directly to where your recipient needs to take action, not to more information.</li>
</ul>
<h3>About link-shortening services</h3>
<p>We do not recommend using a third-party link-shortening service because:</p>
<ul class="list list-bullet">
<li>Your recipient cannot see where the link will take them, which could make them suspect the link is spam/scam.</li>
<li>Your link might stop working if theres a service outage.</li>
<li>You can no longer control where the redirect goes.</li>
</ul>
{% endif %}
{# Personalize content #}
<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 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>
<h3>To personalize your content</h3>
<ol class="list">
<li>Add a placeholder to your content by placing two brackets around the personalized elements.</li>
<li>You can manually enter the personalized content or you can upload a spreadsheet with the details and let Notify do the
work for you. See <a href="#prepare-data">data preparation</a>.</li>
</ol>
<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 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>
<p>Conditional (or optional) content appears only when a recipient meets certain criteria. This feature allows you to make
all or part of the message contingent upon specific criteria associated with the recipient.</p>
<h3>To add conditional content</h3>
<ol class="list">
<li>Use two brackets and ?? to define the conditional content.</li>
<li>You can manually enter the conditional content or you can upload a spreadsheet with the personal details and let Notify
do the work for you. See <a href="#prepare-data">data preparation</a>.</li>
</ol>
<h4>Examples</h4>
<ol class="list">
<li>If you only want to show something to people who are under 18:
</br>
<p class="padding-2 bg-base-lightest">State SNAP: Renewal applications are due by March 15. ((under18??Please get your application signed by a parent or
guardian.))</p>
</li>
<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 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:
</br>
<p class="padding-2 bg-base-lightest">((English??Weve identified unauthorized use on your EBT account. Call the phone number on the back of your card to
cancel or go to your local CSO for immediate replacement.))((Spanish??Hemos identificado un uso no autorizado en su
cuenta EBT. Llame al número de teléfono que aparece en el reverso de su tarjeta para cancelarla o diríjase a su CSO
local para que se la sustituyan inmediatamente.))</p>
</li>
</ol>
{# Identify your program #}
<h2 class="padding-top-1" id="indentify-program">Identify your program</h2>
<p>You can help your recipients identify your texts as legitimate by customizing your messages to clearly state who they
are from. Consider using the program or benefit name that is most familiar to your recipients.</p>
<h3>To customize your program name</h3>
<p>To change the text message sender from the default service name:</p>
<ol class="list">
<li>Go to the <a href="{{ url_for('main.service_settings', service_id=current_service.id) }}">Settings page</a></li>
<li>Select “Start text messages with service name.”</li>
<li>Change the service name to a familiar program or benefit.</li>
</ol>
{# Prepare your data #}
<h2 class="padding-top-1" id="prepare-data">Prepare your data</h2>
<p>The easiest and most efficient way to personalize your content is by uploading a spreadsheet. Notify can accommodate
many file formats and structures.</p>
<h3>File format</h3>
<p>Notify can accept files in the following formats: CSV, TSV, ODS, and Microsoft Excel.</p>
<h3>File structure</h3>
<ul class="list">
<li>The phone number must be in the first column (Column A) and must be labeled <strong>Phone number</strong>.</li>
<li>Each column must have a unique name.</li>
<li>If you are using Excel, you must either disable the”auto-date/time format” for time and date columns or convert the file
to a CSV prior to loading. (If you do not, Excel will display date/time data in a confusing format.)</li>
</ul>
<h3>Formatting personalized content</h3>
<p>If you are sending a message with personalized content, such as the first name of the recipient or the appointment time
and location, the names of the column headings have to match the indicator included in the message template.</p>
<p>For example, if the personalized content is the first name of the recipient, and we are using the spreadsheet below, the
indicator in the message needs to be ((First name)), not ((firstname)) or ((name)).</p>
<h3>Formatting conditional content</h3>
<p>If you are sending messages with conditional content, such as content based on the recipients preferred language or
location, the flag to receive the content needs to be captured in its own column with a Yes or No (Y/N) flag.</p>
<h3>Example</h3>
<div class="table-overflow-x-auto">
<table class="usa-table">
<caption class="usa-sr-only">
Example
</caption>
<thead>
<tr>
<th scope="col">Phone number</th>
<th scope="col">First name</th>
<th scope="col">Last name</th>
<th scope="col">Spanish</th>
<th scope="col">English</th>
<th scope="col">Date</th>
<th scope="col">Time</th>
<th scope="col">Location</th>
</tr>
</thead>
<tbody>
<tr>
<td>123-456-7890</td>
<td>Lulu</td>
<td>Praether</td>
<td>N</td>
<td>Y</td>
<td>November 2, 2023</td>
<td>3:25</td>
<td>123 Ford Rd.</td>
</tr>
<tr>
<td>234-567-8901</td>
<td>Sela</td>
<td>Appel</td>
<td>Y</td>
<td>N</td>
<td>November 2, 2023</td>
<td>4:00</td>
<td>123 Ford Rd.</td>
</tr>
<tr>
<td>123-456-7890</td>
<td>Dexter</td>
<td>Moseley</td>
<td>N</td>
<td>Y</td>
<td>November 2, 2023</td>
<td>2:00</td>
<td>123 Ford Rd.</td>
</tr>
</tbody>
</table>
</div>
{% if not feature_best_practices_enabled %}
{# Preventing fraud #}
<h2 class="padding-top-1" id="prevent-fraud">Preventing fraud</h2>
<p>Texting fraud is ever prevalent and while we cant eliminate the risk, we can reduce the chances that recipients will
fall victim to fraud.</p>
<ol class="list">
<li>Never send unnecessary or protected private information.</li>
<li>Remind your recipients that text messaging is not a secure means of communication and they should not send you private
information via text.</li>
<li>Only include links to websites and online applications that are secure.</li>
<li>Send an introductory text letting recipients know you will be texting them and to save the number in their phone.</li>
<li>Include an auto-response with the name of your organization and a contact phone number.</li>
</ol>
<h3>Examples</h3>
<ol class="list">
<li>Introductory text:
</br>
<p class="padding-2 bg-base-lightest">State Transit Dept: We're piloting a new way to get important reminders to our staff. Save this number to your phone,
you'll receive updates from us here.</p>
</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 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>
{% endif %}
{% endblock %}

View File

@@ -8,7 +8,7 @@
{% endblock %}
{% block content_column_content %}
{{ breadcrumbs.breadcrumb(page_title, "Guides", "main.best_practices") }}
{{ breadcrumbs.breadcrumb(page_title, "Best practices", "main.best_practices") }}
<section class="usa-prose">
<h1>{{page_title}}</h1>
@@ -27,58 +27,46 @@
quantity of messages delivered, how people engage with messages, and how they take action.
</p>
<h3 class="padding-left-5">Message delivery</h3>
<p class="font-body-lg text-light padding-left-5">Benchmark: <span class="text-bold">80%</span> of texts are
<h3>Message delivery</h3>
<p class="text-light">Benchmark: <span class="text-bold">80%</span> of texts are
successfully delivered
</p>
<div class="padding-left-2 measure-4">
<ul>
<li>
<p>
You may discover that some numbers are temporarily or permanently unavailable due to service being
discontinued,
numbers changing, or being a landline.
</p>
</li>
</ul>
</div>
<ul>
<li>
<p>
You may discover that some numbers are temporarily or permanently unavailable due to service being
discontinued,
numbers changing, or being a landline.
</p>
</li>
</ul>
<h3>Engagement</h3>
<p class="text-light">Benchmark: Engagement rates with texts ranged from <span
class="text-bold">17%</span>
to <span class="text-bold">26%</span></p>
<ul>
<li>
<p>
The highest rate of engagement with a text comes within hours of sending. Engagement rates include any kind
of
action taken due to a text, including replying “STOP” to prevent future texts.
</p>
</li>
</ul>
<h3 class="padding-left-5">Engagement</h3>
<div class="measure-4 padding-left-5">
<p class="font-body-lg text-light">Benchmark: Engagement rates with texts ranged from <span
class="text-bold">17%</span>
to <span class="text-bold">26%</span></p>
</div>
<div class="padding-left-2 measure-4">
<ul>
<li>
<p>
The highest rate of engagement with a text comes within hours of sending. Engagement rates include any kind
of
action taken due to a text, including replying “STOP” to prevent future texts.
</p>
</li>
</ul>
</div>
<h3 class="padding-left-5">Appointment requests</h3>
<div class="measure-4 padding-left-5">
<p class="font-body-lg text-light">Benchmark: Requesting appointments after receiving texts ranged
<h3>Appointment requests</h3>
<p class="text-light">Benchmark: Requesting appointments after receiving texts ranged
from <span class="text-bold">4%</span> to <span class="text-bold">9%</span></p>
</div>
<div class="padding-left-2 measure-4">
<ul>
<li>
<p>
Requesting appointments is a specific type of engagement. Provide a phone number or link to an online
appointment
request form.
</p>
</li>
</ul>
</div>
<ul>
<li>
<p>
Requesting appointments is a specific type of engagement. Provide a phone number or link to an online
appointment
request form.
</p>
</li>
</ul>
<p>
The <a class="use-link usa-link--external" href="https://codeforamerica.org/resources/texting-playbook/"
target="_blank">Code for Americas Texting
@@ -86,63 +74,42 @@
reported specific learnings around appointment reminders, completing
document submission, and maintenance reminders.
</p>
<h3 class="padding-left-5">Appointment reminders</h3>
<div class="measure-4 padding-left-5">
<p class="font-body-lg text-light">Benchmark: Clients were <span class="text-bold">79%</span> more
likely
to keep
their appointment after receiving a text reminder.</p>
</div>
<div class="padding-left-2 measure-4">
<ul>
<li>
<p>You will likely see more completed appointments.</p>
</li>
</ul>
</div>
<h3>Appointment reminders</h3>
<p class="text-light">Benchmark: Clients were <span class="text-bold">79%</span> more
likely to keep their appointment after receiving a text reminder.</p>
<ul>
<li>
<p>You will likely see more completed appointments.</p>
</li>
</ul>
<p class="text-light">Benchmark: Clients were <span class="text-bold">55%</span> more
likely to complete an interview after receiving an interview reminder</p>
<ul>
<li>
<p>You will likely see more completed interviews.</p>
</li>
</ul>
<div class="measure-4 padding-left-5">
<p class="font-body-lg text-light">Benchmark: Clients were <span class="text-bold">55%</span> more
likely
to complete
an interview after receiving an interview reminder</p>
</div>
<div class="padding-left-2 measure-4">
<ul>
<li>
<p>You will likely see more completed interviews.</p>
</li>
</ul>
</div>
<h3 class="padding-left-5">Document submission</h3>
<div class="measure-4 padding-left-5">
<p class="font-body-lg text-light">
<h3>Document submission</h3>
<p class="text-light">
Benchmark: Clients were <span class="text-bold">6%</span> more likely to complete document submission after
receiving a customized list of required documents via text
</p>
</div>
<div class="padding-left-2 measure-4">
<ul>
<li>
<p>To encourage response, provide a custom list of the needed documents and information about how to submit
them.
</p>
</li>
</ul>
</div>
<ul>
<li>
<p>To encourage response, provide a custom list of the needed documents and information about how to submit
them.
</p>
</li>
</ul>
<h3 class="padding-left-5">Reminders</h3>
<div class="measure-4 padding-left-5">
<p class="font-body-lg text-light">Benchmark: Text reminders improved case maintenance rates by <span
class="text-bold">21%</span></p>
</div>
<div class="padding-left-2 measure-4">
<ul>
<li>
<p>You may see less turnover in your case rates.</p>
</li>
</ul>
</div>
<h3 class="font-body-xl">Reminders</h3>
<p class="text-light">Benchmark: Text reminders improved case maintenance rates by <span
class="text-bold">21%</span></p>
<ul>
<li>
<p>You may see less turnover in your case rates.</p>
</li>
</ul>
</section>
{% endblock %}

View File

@@ -1,6 +1,6 @@
{% extends "base.html" %}
{% set page_title = "Best Practices" %}
{% set page_title = "Best practices" %}
{% block per_page_title %}
{{page_title}}
@@ -8,14 +8,14 @@
{% block content_column_content %}
<section class="usa-prose">
<h1>Best Practices</h1>
<h1>Best practices</h1>
<p class="font-sans-lg text-base">For texting the public</p>
<p>Effectively reaching your audience and supporting your programs goals starts with strategically planning out what
text messages can help you achieve and how to approach a thoughtful rollout.
</p>
<p>This set of best practices will help you get an effective texting initiative up and running.</p>
<h2 class="padding-bottom-2">
<h2 class="padding-bottom-4">
Key elements of a texting campaign
</h2>
@@ -24,37 +24,37 @@
"svg_src": "goal",
"card_heading": "Establish clear goals",
"p_text": "Start with a singular purpose. Make explicit what you want to achieve.",
"link": "/guides/clear-goals"
"link": "/using-notify/best-practices/clear-goals"
},
{
"svg_src": "compliant",
"card_heading": "Follow rules & regulations",
"p_text": "Understand what is required when texting the public.",
"link": "/guides/rules-and-regulations"
"link": "/using-notify/best-practices/rules-and-regulations"
},
{
"svg_src": "trust",
"card_heading": "Establish trust",
"p_text": "Help your audience anticipate and welcome your texts.",
"link": "/guides/establish-trust"
"link": "/using-notify/best-practices/establish-trust"
},
{
"svg_src": "runner",
"card_heading": "Write texts that provoke action",
"p_text": "Help your audience know what to do with the information you send.",
"link": "/guides/write-for-action"
"link": "/using-notify/best-practices/write-for-action"
},
{
"svg_src": "language",
"card_heading": "Send texts in multiple languages",
"p_text": "What to know as you plan translated texts.",
"link": "/guides/multiple-languages"
"link": "/using-notify/best-practices/multiple-languages"
},
{
"svg_src": "chart",
"card_heading": "Measure performance with benchmarking",
"p_text": "Learn how effective your texting program can be.",
"link": "/guides/benchmark-performance"
"link": "/using-notify/best-practices/benchmark-performance"
}
] %}

View File

@@ -8,7 +8,7 @@
{% endblock %}
{% block content_column_content %}
{{ breadcrumbs.breadcrumb(page_title, "Guides", "main.best_practices") }}
{{ breadcrumbs.breadcrumb(page_title, "Best practices", "main.best_practices") }}
<section class="usa-prose">
<h1>{{page_title}}</h1>
@@ -162,7 +162,7 @@
</div>
<p>
Review your drafted hypothesis with your team to make sure everyone is aligned on your desired goals. A clear and
concise hypothesis can help you decide how to <a href="../guides/write-for-action">write text message
concise hypothesis can help you decide how to <a href="../best-practices/write-for-action">write text message
content
that provokes action</a>.
</p>

View File

@@ -10,7 +10,7 @@
{% endblock %}
{% block content_column_content %}
{{ breadcrumbs.breadcrumb(page_title, "Guides", "main.best_practices") }}
{{ breadcrumbs.breadcrumb(page_title, "Best practices", "main.best_practices") }}
<section class="usa-prose">
<h1>{{page_title}}</h1>

View File

@@ -8,7 +8,7 @@
{% endblock %}
{% block content_column_content %}
{{ breadcrumbs.breadcrumb(page_title, "Guides", "main.best_practices") }}
{{ breadcrumbs.breadcrumb(page_title, "Best practices", "main.best_practices") }}
<section class="usa-prose">
<h1>{{page_title}}</h1>

View File

@@ -8,7 +8,7 @@
{% endblock %}
{% block content_column_content %}
{{ breadcrumbs.breadcrumb(page_title, "Guides", "main.best_practices") }}
{{ breadcrumbs.breadcrumb(page_title, "Best practices", "main.best_practices") }}
<section class="usa-prose">
<h1>{{page_title}}</h1>
@@ -22,7 +22,7 @@
If you do need expressed consent, consider including a pre-checked plain language opt-in (i.e. “Its OK to text
me.”) on
digital forms. Be sure to ask for an up-to-date phone number and include a question about the recipients preferred
language for text messages if you expect to <a href="../guides/multiple-languages">translate your text
language for text messages if you expect to <a href="../best-practices/multiple-languages">translate your text
messages</a> in
languages other than English.
</p>
@@ -81,7 +81,7 @@
<h3>Opting out</h3>
<p>
There is no policy requirement for senders to communicate opt-out options, but <a
href="../guides/establish-trust#as-people-receive-texts"> including instructions in introductory and/or
href="../best-practices/establish-trust#as-people-receive-texts"> including instructions in introductory and/or
auto-response texts </a> on how to opt out and opt back in are effective ways to establish trust with your
audience.
</p>

View File

@@ -9,7 +9,7 @@
{% endblock %}
{% block content_column_content %}
{{ breadcrumbs.breadcrumb(page_title, "Guides", "main.best_practices") }}
{{ breadcrumbs.breadcrumb(page_title, "Best practices", "main.best_practices") }}
<section class="usa-prose">
<h1>{{page_title}}</h1>

View File

@@ -0,0 +1,110 @@
{% extends "base.html" %}
{% from "components/table.html" import mapping_table, row, text_field, edit_field, field with context %}
{% from "components/sub-navigation.html" import sub_navigation %}
{% from "components/service-link.html" import service_link %}
{% block per_page_title %}
How to
{% endblock %}
{% block content_column_content %}
<h1 class="font-body-2xl margin-bottom-3">How to</h1>
<p>Notify allows you to easily create templates for messages for your recipients. You can customize messages to encourage
your recipient to manage their benefits and increase follow-through.</p>
<p>Below we explain how to:</p>
<ul class="list list-bullet">
<li><a class="usa-link" href="#personalize-content">Personalize your content</a></li>
<li><a class="usa-link" href="#conditional-content">Add conditional content</a></li>
</ul>
{# Format content #}
<h2 class="padding-top-1" id="format-content">Format your content</h2>
<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>". You'll see existing templates.</li>
<li>Add a new template or choose an existing template and select Edit.</li>
</ol>
{# Add links #}
<p>When composing a text message, links to websites or online applications can help your recipient respond quickly.</p>
<ul class="list list-bullet">
<li>Write URLs in full and Notify will convert them into links for you. Note that you cannot hyperlink text in Notify
messages.</li>
<li>For link click tracking, you can consider adding campaign parameters to URLs.</li>
<li>All links should point to a government domain.</li>
<li>Link directly to where your recipient needs to take action, not to more information.</li>
</ul>
<h3>About link-shortening services</h3>
<p>We do not recommend using a third-party link-shortening service because:</p>
<ul class="list list-bullet">
<li>Your recipient cannot see where the link will take them, which could make them suspect the link is spam/scam.</li>
<li>Your link might stop working if theres a service outage.</li>
<li>You can no longer control where the redirect goes.</li>
</ul>
{# Personalize content #}
<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 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>
<h3>To personalize your content</h3>
<ol class="list">
<li>Add a placeholder to your content by placing two brackets around the personalized elements.</li>
<li>You can manually enter the personalized content or you can upload a spreadsheet with the details and let Notify do the
work for you.</li>
</ol>
<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 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>
<p>Conditional (or optional) content appears only when a recipient meets certain criteria. This feature allows you to make
all or part of the message contingent upon specific criteria associated with the recipient.</p>
<h3>To add conditional content</h3>
<ol class="list">
<li>Use two brackets and ?? to define the conditional content.</li>
<li>You can manually enter the conditional content or you can upload a spreadsheet with the personal details and let Notify
do the work for you.</li>
</ol>
<h4>Examples</h4>
<ol class="list">
<li>If you only want to show something to people who are under 18:
</br>
<p class="padding-2 bg-base-lightest">State SNAP: Renewal applications are due by March 15. ((under18??Please get your application signed by a parent or
guardian.))</p>
</li>
<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 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:
</br>
<p class="padding-2 bg-base-lightest">((English??Weve identified unauthorized use on your EBT account. Call the phone number on the back of your card to
cancel or go to your local CSO for immediate replacement.))((Spanish??Hemos identificado un uso no autorizado en su
cuenta EBT. Llame al número de teléfono que aparece en el reverso de su tarjeta para cancelarla o diríjase a su CSO
local para que se la sustituyan inmediatamente.))</p>
</li>
</ol>
...
{% endblock %}

View File

@@ -11,7 +11,18 @@
{% block maincolumn_content %}
{{ page_header("Message status") }}
{{ partials['status']|safe }}
{% if not job.finished_processing %}
<div
data-module="update-content"
data-resource="{{ updates_url }}"
data-key="status"
data-form=""
>
{% endif %}
{{ partials['status']|safe }}
{% if not job.finished_processing %}
</div>
{% endif %}
{% if not finished %}
<div
data-module="update-content"

View File

@@ -72,14 +72,14 @@
aria-labelledby="summary-box-how-to-get-started">
<div class="usa-summary-box__body">
<div class="usa-summary-box__text">
<p>Interested in trying Notify.gov before signing an agreement? We can provide qualifying partners with access
<p class="margin-0">Interested in trying Notify.gov before signing an agreement? We can provide qualifying partners with access
to Trial Mode to review Notify.gov features before deciding. In Trial Mode, you can test sending messages,
explore the personalization and customization features, and review sample delivery reports.</p>
</div>
</div>
</div>
<h4>Tell us about your program</h4>
<h3 class="font-body-md">Tell us about your program</h3>
<p class="padding-bottom-3">
Lets determine if Notify.gov is a good fit for your organization. To <b>get started</b>, well ask you for
information such as:
@@ -126,14 +126,14 @@
{
"heading": "What if I need more than 250,000 messages?",
"p_text": "Plans that include additional messages for a fee will be available soon. We want to design these plans
based on our partners needs, so please <a href='mailto:tts-notify@gsa.gov'>contact us</a> if you hope to be able to
based on our partners needs, so please <a class='use-link usa-link--external' href='mailto:tts-notify@gsa.gov'>contact us</a> if you hope to be able to
send more messages. Wed like to talk with you.",
"position": "b-a1"
},
{
"heading": "What phone numbers can my agency send to?",
"p_text": "Right now, Notify.gov supports sending messages to North American numbers (+1). If youd like to send to
international numbers, we want to <u>hear from you</u>.",
international numbers, we want to <a href='/contact'>hear from you</a>.",
"position": "b-a2",
},
{
@@ -146,7 +146,7 @@
{
"heading": "Can we use API integrations with Notify?",
"p_text": "While public API integrations are not yet available, we are working on enabling these. If you're looking
for this feature <a href='/about/contact'>we want to hear from you</a>.",
for this feature <a href='/contact'>we want to hear from you</a>.",
"position": "b-a4",
},
{
@@ -164,7 +164,7 @@
{
"heading": "My OGC is asking about consent, where can I get more information for them?",
"p_text": "Text message notifications are governed by the Telephone Consumer Protection Act. Different levels of
government have different consent requirements. Download and share our <a
government have different consent requirements. Download and share our <a class='use-link usa-link--external'
href='https://github.com/GSA/notifications-admin/files/15100120/TCPA.Overview_Notify.gov.pdf'>overview of the
TCPA</a> with your legal counsel as
a starting point.",

View File

@@ -77,7 +77,7 @@
<p>Sometimes Notify receives more detailed information from the carriers on the status of messages, and these can be found
in the downloadable reports. Not all carriers provide the same level of detail regarding delivery and some delivery
statutes have a slight variation in word choice. Notify includes this information in the reports to provide you as much
detail as possible. Remember, for <a class="usa-link" href="/features/security">security</a> purposes, detailed information is only available for seven days after a
detail as possible. Remember, for security purposes, detailed information is only available for seven days after a
message has been sent.</p>
<h2 class="heading-medium">Opting out</h2>

View File

@@ -34,5 +34,8 @@
<p>
<a class="usa-link" href="{{ url_for('main.download_all_users') }}">Download All Users</a>
</p>
<p>
<a class="usa-link" href="{{ url_for('main.get_redis_report') }}">Get Redis Report</a>
</p>
{% endblock %}

View File

@@ -1,87 +0,0 @@
{% extends "base.html" %}
{% from "components/table.html" import mapping_table, row, text_field, edit_field, field with context %}
{% from "components/content-metadata.html" import content_metadata %}
{% block per_page_title %}
Roadmap
{% endblock %}
{% block content_column_content %}
<h1 class="font-body-2xl margin-bottom-3">Roadmap</h1>
<!-- {{ content_metadata(
data={
"Last updated": "1 November 2022",
"Next review due on": "15 December 2022"
}
) }} -->
<p>The Notify roadmap shows what were working on and what we're planning to do next.</p>
<p>This roadmap is only a guide. It does not cover everything we do, and some things may change.</p>
<p>You can <a class="usa-link" href="{{url_for('.support')}}">contact us</a> if you have any questions about the roadmap or suggestions for new features.</p>
<h2 id="things-we-are-working-on">What were working on</h2>
<h3 id="now">Now</h3>
<p>We are investigating the Notify concept, building on the notifications tool pioneered by the UK.</p>
<p>To do this, we are convening a pilot with a small set of partners.</p>
<p>Goals during this stage:</p>
<ul class="list list-bullet">
<li>Achieve compliance to begin piloting, such as ATO and privacy standards.
<svg class="usa-icon" aria-hidden="true" focusable="false" role="img">
<use xlink:href="/assets/img/sprite.svg#check"></use>
</svg>
</li>
<li>Demonstrate that a government-run notifications tool provides a unique value.</li>
<li>Gather data from the pilot to improve the product.</li>
</ul>
<p>Features prioritized during this stage:</p>
<ul class="list list-bullet">
<li>Bulk, individually customizable one-way SMS sending via web UI</li>
<li>Organization permissions settings for various team members to edit/send</li>
<li>Reusable message templates</li>
<li>Seven-day records deletion</li>
<li>Message send/failure analytics</li>
</ul>
<h3 id="next">Next</h3>
<p>If the pilot is successful, we hope to recruit additional partners to improve outcomes for low-income individuals and families.</p>
<p>Goals during this stage:</p>
<ul class="list list-bullet">
<li>Complement Notify with practical guidance and support services.</li>
<li>Iterate on existing features and implement new features based on what we've learned so far.</li>
<li>Hone our measurement approaches to better quantify impact.</li>
</ul>
<p>Features prioritized during this stage:</p>
<ul class="list">
<li>SMS sending via API integration</li>
<li>Self-service account creation</li>
<li>Application status page</li>
<li>Improved scheduled send option</li>
</ul>
<h3 id="later">Later</h3>
<p>In the future, we may decide to expand beyond SMS, or to offer the service government-wide.</p>
<p>Features to be considered during this stage:</p>
<ul class="list">
<li>Two-way messaging</li>
<li>Multilingual interface and content library options</li>
<li>Recurring scheduled send (such as “Send each Monday for 3 weeks”)</li>
</ul>
{% endblock %}

View File

@@ -37,8 +37,10 @@
data_kwargs={'force-focus': True}
) %}
<div class="grid-row">
<div class="grid-col-12 {% if form.placeholder_value.label.text == 'phone number' %}extra-tracking{% endif %}" aria-live="polite" role="alert">
{{ form.placeholder_value(param_extensions={"classes": ""}) }}
{% set extra_class = "extra-tracking" if form.placeholder_value.label.text == "phone number" else "" %}
{% set placeholder_id = "phone number" if form.placeholder_value.label.text == "phone number" else "" %}
<div class="grid-col-12 {% if form.placeholder_value.label.text == 'phone number' %}extra-tracking{% endif %}">
{{ form.placeholder_value(param_extensions={"classes": "", "id": "phone-number"}) }}
</div>
{% if skip_link or link_to_upload %}
<div class="grid-col-12 margin-top-1">

View File

@@ -6,6 +6,7 @@ import usaButton %} {% block meta %}
/>
{% endblock %} {% block pageTitle %} Notify.gov {% endblock %} {% block main %}
{% block beforeContent %}{% endblock %}
<main id="main-content" role="main">
{% block content %}
<section class="usa-section--dark usa-hero" aria-label="Introduction">
@@ -57,13 +58,19 @@ import usaButton %} {% block meta %}
</p>
</section>
<section class="grid-container usa-section usa-section__home usa-prose padding-bottom-1">
<section
class="grid-container usa-section usa-section__home usa-prose padding-bottom-1"
>
<h2 class="font-sans-xl margin-top-0">Key features</h2>
<div class="home-cards margin-top-5">
<ul class="usa-card-group display-flex margin-bottom-4">
<li class="usa-card tablet:grid-col-4 mobile-lg:grid-col-12">
<div class="usa-card__container">
<img src="{{ asset_url('images/internet.svg') }}" alt="Globe on top of a web browser" />
<div class="blue-bar"></div>
<img
src="{{ asset_url('images/internet.svg') }}"
alt="Globe on top of a web browser"
/>
<div class="usa-card__header">
<h3 class="font-heading-md">Web-based</h3>
</div>
@@ -74,7 +81,11 @@ import usaButton %} {% block meta %}
</li>
<li class="usa-card tablet:grid-col-4 mobile-lg:grid-col-12">
<div class="usa-card__container">
<img src="{{ asset_url('images/fast.svg') }}" alt="Stopwatch with a notification speech bubble with a star inside" />
<div class="blue-bar"></div>
<img
src="{{ asset_url('images/fast.svg') }}"
alt="Stopwatch with a notification speech bubble with a star inside"
/>
<div class="usa-card__header">
<h3 class="font-heading-md">Fast and easy</h3>
</div>
@@ -85,7 +96,11 @@ import usaButton %} {% block meta %}
</li>
<li class="usa-card tablet:grid-col-4 mobile-lg:grid-col-12">
<div class="usa-card__container">
<img src="{{ asset_url('images/status.svg') }}" alt="3 status messages, 2 successes and one failure" />
<div class="blue-bar"></div>
<img
src="{{ asset_url('images/status.svg') }}"
alt="3 status messages, 2 successes and one failure"
/>
<div class="usa-card__header">
<h3 class="font-heading-md">Track message delivery</h3>
</div>
@@ -98,12 +113,15 @@ import usaButton %} {% block meta %}
<ul class="usa-card-group">
<li class="usa-card tablet:grid-col-4 mobile-lg:grid-col-12">
<div class="usa-card__container">
<div class="blue-bar"></div>
<img
src="{{ asset_url('images/translation.svg') }}"
alt="Speech bubbles with the letter A and the Chinese character for language"
/>
<div class="usa-card__header">
<h3 class="font-heading-md">Send in recipients' preferred language</h3>
<h3 class="font-heading-md">
Send in recipients' preferred language
</h3>
</div>
<div class="usa-card__body">
<p>Notify.gov has support for more than 30 character sets</p>
@@ -112,28 +130,38 @@ import usaButton %} {% block meta %}
</li>
<li class="usa-card tablet:grid-col-4 mobile-lg:grid-col-12">
<div class="usa-card__container">
<img src="{{ asset_url('images/security.svg') }}" alt="Lock with code icon inside on top of a web browser" />
<div class="blue-bar"></div>
<img
src="{{ asset_url('images/security.svg') }}"
alt="Lock with code icon inside on top of a web browser"
/>
<div class="usa-card__header">
<h3 class="font-heading-md">Security and privacy</h3>
</div>
<div class="usa-card__body">
<p>
Limited data retention, encryption, and multi-factor authentication
protect user data and manage risk
Limited data retention, encryption, and multi-factor
authentication protect user data and manage risk with <br><a href="/about/security">our security efforts</a>
</p>
</div>
</div>
</li>
<li class="usa-card tablet:grid-col-4 mobile-lg:grid-col-12">
<div class="usa-card__container">
<img src="{{ asset_url('images/send.svg') }}" alt="Paper airplane and a notification icon with the number 1 inside" />
<div class="blue-bar"></div>
<img
src="{{ asset_url('images/send.svg') }}"
alt="Paper airplane and a notification icon with the number 1 inside"
/>
<div class="usa-card__header">
<h3 class="font-heading-md">Send bulk, customized, one-way messages</h3>
<h3 class="font-heading-md">
Send bulk, customized, one-way messages
</h3>
</div>
<div class="usa-card__body">
<p>
Send hundreds or thousands of individually customized messages with
just a few clicks
Send hundreds or thousands of individually customized messages
with just a few clicks
</p>
</div>
</div>
@@ -142,7 +170,9 @@ import usaButton %} {% block meta %}
</div>
</section>
<section class="grid-container usa-section usa-section__home usa-prose grid-container padding-bottom-10">
<section
class="grid-container usa-section usa-section__home usa-prose grid-container padding-bottom-10"
>
<h2 class="font-heading-xl margin-top-0 margin-bottom-3">
Who can use Notify.gov?
</h2>
@@ -159,7 +189,10 @@ import usaButton %} {% block meta %}
</p>
<div class="grid-container margin-top-4 padding-left-0 padding-right-0">
<div class="grid-row grid-gap-3">
<a class="text-no-underline tablet:grid-col-4 mobile-lg:grid-col-12" href="mailto:tts-notify@gsa.gov">
<a
class="text-no-underline tablet:grid-col-4 mobile-lg:grid-col-12"
href="mailto:tts-notify@gsa.gov"
>
<div class="contact-us-card">
<div class="grid-row flex-align-center grid-gap-2">
<div class="grid-col-auto">

View File

@@ -0,0 +1,42 @@
{% extends "base.html" %}
{% set page_title = "About the Public Benefits Studio" %}
{% block per_page_title %}{{page_title}}{% endblock %}
{% block content_column_content %}
<section class="usa-prose">
<h1>{{page_title}}</h1>
<p>The Benefits Studio is a product accelerator inside the federal government.
We collaborate with benefits programs and the people they serve to build and
scale shared tools that reduce burden within the social safety net.
</p>
<p>
As a part of GSAs <a href="https://tts.gsa.gov/">Technology Transformation Services (TTS)</a>, the Studio is uniquely
positioned to work between and across agencies and programs, to make it easier and
cheaper for programs with the same challenges to use the same tools.
</p>
<p>
We focus on benefits programs first, because we believe solving problems for people
who face the highest challenges when interacting with the government will result in
solutions that serve everyone else well, too.
</p>
<p class="margin-bottom-4">
<strong>The Studios first product offering is <a href="/">Notify.gov</a></strong>, a text message service that helps government agencies at all levels more effectively communicate with the people they serve.
</p>
<p class="font-body-lg">Were currently exploring two new product spaces:</p>
<ul>
<li><strong>Better Document Submission:</strong> How might we enable simple digital submission and document processing for benefits agencies?</li>
<li><strong>Automated Enrollment Checks:</strong> How might we improve how enrollment information is shared across benefit programs, especially where enrollment in one program provides partial or full eligibility for another program?</li>
</ul>
<p class="margin-bottom-4">If youre interested in providing feedback on where we should go next, reach us at <a href="mailto:public-benefits-studio@gsa.gov">public-benefits-studio@gsa.gov.</a></p>
<p class="font-body-lg">Read more about the Studio:</p>
<ul>
<li><a href="https://digital.gov/2023/02/07/collaborate-with-the-tts-public-benefits-studio/">Collaborate with the Public Benefits Studio</a> — Digital.gov, Feb 2023</li>
<li><a href="https://federalnewsnetwork.com/it-modernization/2023/10/gsa-considers-text-messages-as-new-frontier-for-better-customer-experience/">GSA considers text messages as new frontier for better customer experience</a> — Federal News Network, Oct 2023</li>
<li><a href="https://www.gsa.gov/blog/2024/03/18/notifygov-is-helping-government-meet-families-where-they-are">Notify.gov is helping government meet families where they are</a> — GSA.gov blog, Mar 2024</li>
</ul>
</section>
{% endblock %}

View File

@@ -13,9 +13,8 @@
<h1 class="font-body-2xl margin-bottom-3">Contact us</h1>
<p>Notify is designed to be easy to use.</p>
<ul class="list list-bullet">
<li>For information on personalization and data preparation, see <a href={{ url_for("main.guidance_index") }}>Guidance</a>.</li>
<li>For help interpreting delivery reports, see <a href={{ url_for("main.message_status") }}>Delivery Status</a>.</li>
<li>For details on pricing and what counts as a message part, see <a href={{ url_for("main.pricing") }}>Pricing</a>.</li>
<li>For information on personalization, see <a href={{ url_for("main.how_to") }}>How to</a>.</li>
<li>For help interpreting delivery reports, see <a href={{ url_for("main.message_status") }}>Delivery status</a>.</li>
</ul>
<p>If you have other questions, we are available at <a class="usa-link" href="mailto:notify-support@gsa.gov">notify-support@gsa.gov</a>.</p>

View File

@@ -90,8 +90,4 @@
{% endif %}
</div>
<!--<div class="">
{{ copy_to_clipboard(template.id, name="Template ID", thing='template ID') }}
</div>-->
{% endblock %}

View File

@@ -12,16 +12,9 @@
<h2 class="font-body-lg">Limits while in trial mode</h2>
<p>While your service is in trial mode you can only:</p>
<ul class="list list-bullet">
<li>send 50 text message parts per day</li>
<li>send 50 text messages per day</li>
<li>send messages to yourself and other people in your team</li>
</ul>
<p>Each text message is made up of one or more parts.</p>
<ul class="list list-bullet">
<li>Generally, 160 characters is one part (and one text message).</li>
<li>A text message of 160-306 characters is two parts.</li>
</ul>
<p>For more information on how message parts are calculated, see
<a href="/using-notify/pricing">Tracking usage</a>.</p>
<h2 class="font-body-lg">Before going Live</h2>
<p>Before you request to make your service live so you can send messages to clients:</p>

View File

@@ -21,7 +21,6 @@
<ul class="list list-bullet">
<li><a class="usa-link" href="{{ url_for('main.trial_mode_new') }}">trial mode</a></li>
<li><a class="usa-link" href="{{ url_for('main.message_status') }}">message status types</a></li>
<li><a class="usa-link" href="{{ url_for('main.features_sms') }}">text message replies</a></li>
</ul>
</div>

View File

@@ -128,6 +128,13 @@ def generate_notifications_csv(**kwargs):
notifications_resp = notification_api_client.get_notifications_for_service(
**kwargs
)
# Stop if we are finished
if (
notifications_resp.get("notifications") is None
or len(notifications_resp["notifications"]) == 0
):
return
for notification in notifications_resp["notifications"]:
preferred_tz_created_at = convert_report_date_to_preferred_timezone(
notification["created_at"]

View File

@@ -1,7 +1,10 @@
from flask import Flask
from werkzeug.serving import WSGIRequestHandler
from app import create_app
WSGIRequestHandler.version_string = lambda self: "SecureServer"
application = Flask("app")
create_app(application)

Binary file not shown.

After

Width:  |  Height:  |  Size: 337 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

After

Width:  |  Height:  |  Size: 357 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

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