Compare commits

..

522 Commits

Author SHA1 Message Date
dependabot[bot]
02457af52d Bump rollup from 4.22.5 to 4.23.0
Bumps [rollup](https://github.com/rollup/rollup) from 4.22.5 to 4.23.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.22.5...v4.23.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-01 23:55:58 +00:00
Carlo Costino
0d04524525 Merge pull request #1966 from GSA/USN-COMPLY-49_Verify_Nonce_on_Login
Verify NONCE on login.
2024-10-01 11:05:09 -04:00
Carlo Costino
40a0418731 Merge pull request #1972 from GSA/dependabot/npm_and_yarn/rollup-4.22.5
Bump rollup from 4.22.4 to 4.22.5
2024-09-30 15:56:25 -04:00
Kenneth Kehl
6ff8be5d3e Merge pull request #1973 from GSA/add-awscli
Add awscli tools to the admin
2024-09-30 11:35:08 -07:00
Carlo Costino
2c7d090b82 Merge pull request #1964 from GSA/notify-admin-1956
make sure phone number is always last in the one-off csv
2024-09-30 13:03:09 -04:00
Carlo Costino
4f193e362c Add awscli tools to the admin
This changeset adds the awscli tools as a development dependency so that we are able to manage local connectivity to AWS resources when needed.  It is also needed to help with deploying the project to the sandbox environment and/or updating the Terraform to manage the infrastructure.

Signed-off-by: Carlo Costino <carlo.costino@gsa.gov>
2024-09-30 10:13:12 -04:00
dependabot[bot]
302a79b41f Bump rollup from 4.22.4 to 4.22.5
Bumps [rollup](https://github.com/rollup/rollup) from 4.22.4 to 4.22.5.
- [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.22.4...v4.22.5)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-27 23:20:05 +00:00
Kenneth Kehl
bdc5614fff Update app/main/views/send.py
Co-authored-by: Carlo Costino <ccostino@users.noreply.github.com>
2024-09-27 07:20:40 -07:00
Cliff Hill
a5e72ea4eb manifest.yml changed.
Signed-off-by: Cliff Hill <clifford.hill@gsa.gov>
2024-09-26 12:46:07 -04:00
Cliff Hill
63747515db Updated the sample.env with new var.
Signed-off-by: Cliff Hill <clifford.hill@gsa.gov>
2024-09-26 10:16:57 -04:00
Cliff Hill
55f588f13d Updated deploy workflows with new env var.
Signed-off-by: Cliff Hill <clifford.hill@gsa.gov>
2024-09-26 10:11:46 -04:00
Cliff Hill
81a629935c Ok, now working 100% correctly with nonce.
Signed-off-by: Cliff Hill <clifford.hill@gsa.gov>
2024-09-26 09:18:02 -04:00
Jonathan Bobel
49ec3dc559 Merge pull request #1963 from GSA/notify-admin-1937b
Fix moderate a11y violations reported by axe-core
2024-09-25 16:35:02 -04:00
Jonathan Bobel
647b37ae61 Removing moderate violations for the a11y scan 2024-09-25 16:19:00 -04:00
Cliff Hill
7b2d495c1b fixing so nonce check works correctly.
Signed-off-by: Cliff Hill <clifford.hill@gsa.gov>
2024-09-25 16:01:13 -04:00
Cliff Hill
47f0952125 Getting id_token to decode correctly.
Signed-off-by: Cliff Hill <clifford.hill@gsa.gov>
2024-09-25 15:34:31 -04:00
Jonathan Bobel
e0fb031cf5 Semantic update 2024-09-25 15:29:58 -04:00
Jonathan Bobel
72eff3235e Making sure it's testing against this code 2024-09-25 15:15:50 -04:00
Jonathan Bobel
67ad9148d6 Testing back button for a11y issues 2024-09-25 13:58:54 -04:00
Cliff Hill
4cdac9eba3 Making coverage not bother with sign in stuff since login.gov.
Signed-off-by: Cliff Hill <clifford.hill@gsa.gov>
2024-09-25 12:05:56 -04:00
Cliff Hill
76863cd4dc Some unit test cleanup.
Signed-off-by: Cliff Hill <clifford.hill@gsa.gov>
2024-09-25 11:23:36 -04:00
Cliff Hill
3d00b0c94f Making nonce passed through redis.
Signed-off-by: Cliff Hill <clifford.hill@gsa.gov>
2024-09-25 10:45:57 -04:00
Kenneth Kehl
4594dc317f merge from main 2024-09-25 07:03:02 -07:00
Kenneth Kehl
5e93f2d572 merge from main 2024-09-25 07:00:10 -07:00
Cliff Hill
a2dbb6c9e6 Typo fix.
Signed-off-by: Cliff Hill <clifford.hill@gsa.gov>
2024-09-24 20:46:48 -04:00
Cliff Hill
d097a0e2a6 I believe nonce is set up for sign_in.py.
Signed-off-by: Cliff Hill <clifford.hill@gsa.gov>
2024-09-24 20:39:08 -04:00
Carlo Costino
d3855f47ac Merge pull request #1959 from GSA/dependabot/npm_and_yarn/rollup-4.22.4
Bump rollup from 4.22.2 to 4.22.4
2024-09-24 15:57:00 -04:00
Kenneth Kehl
6f91d60030 make sure phone number is always last in the one-off csv 2024-09-24 10:35:22 -07:00
Kenneth Kehl
99ce42fc4a revert test setting to ignore moderate failures 2024-09-24 08:44:01 -07:00
Kenneth Kehl
899347d349 fix tests 2024-09-24 08:23:17 -07:00
Kenneth Kehl
14b37524ae revert to nav 2024-09-24 08:02:32 -07:00
Kenneth Kehl
5caa26e93c try adding service nav 2024-09-24 07:52:10 -07:00
Kenneth Kehl
5ee559da10 try adding service nav 2024-09-24 07:44:43 -07:00
Kenneth Kehl
73852a54f2 add aria-labels 2024-09-24 07:36:21 -07:00
Kenneth Kehl
ec806ce385 1937b 2024-09-24 07:11:08 -07:00
dependabot[bot]
8c3cdabb92 Bump rollup from 4.22.2 to 4.22.4
Bumps [rollup](https://github.com/rollup/rollup) from 4.22.2 to 4.22.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.22.2...v4.22.4)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-23 23:13:18 +00:00
Jonathan Bobel
a5889d3448 Merge pull request #1957 from GSA/notify-admin-1936
Fix serious a11y violations reported by axe-core
2024-09-23 13:40:37 -04:00
Carlo Costino
09a775fdad Merge pull request #1953 from GSA/dependabot/npm_and_yarn/sass-embedded-1.79.3
Bump sass-embedded from 1.78.0 to 1.79.3
2024-09-23 11:50:48 -04:00
Carlo Costino
62cfa2c39c Merge pull request #1954 from GSA/dependabot/npm_and_yarn/rollup-4.22.2
Bump rollup from 4.21.2 to 4.22.2
2024-09-23 11:43:42 -04:00
Kenneth Kehl
8b6f719510 try to fix li elements 2024-09-23 08:18:41 -07:00
Kenneth Kehl
b953d316c4 fix aria label issue 2024-09-23 08:01:56 -07:00
Kenneth Kehl
5c3b5790ae try overriding default govuk-frontend font weight 2024-09-23 07:50:52 -07:00
Carlo Costino
d0d3878881 Merge pull request #1931 from GSA/notify-admin-1926
Integrate axe-core a11y tests into our e2e tests
2024-09-23 10:26:44 -04:00
Kenneth Kehl
b6e0249adb merge from main 2024-09-23 07:10:31 -07:00
dependabot[bot]
4a8d768b6f Bump rollup from 4.21.2 to 4.22.2
Bumps [rollup](https://github.com/rollup/rollup) from 4.21.2 to 4.22.2.
- [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.21.2...v4.22.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-20 23:34:39 +00:00
dependabot[bot]
876db282d5 Bump sass-embedded from 1.78.0 to 1.79.3
Bumps [sass-embedded](https://github.com/sass/embedded-host-node) from 1.78.0 to 1.79.3.
- [Changelog](https://github.com/sass/embedded-host-node/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sass/embedded-host-node/compare/1.78.0...1.79.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-20 23:34:30 +00:00
Carlo Costino
cf39b32808 Merge pull request #1421 from GSA/notify-admin-1157
point to staging for e2e
2024-09-13 17:29:19 -04:00
Kenneth Kehl
84f3c61bc0 code review feedback 2024-09-13 07:50:24 -07:00
Kenneth Kehl
892705b125 revert fix for aria label 2024-09-11 14:19:31 -07:00
Kenneth Kehl
ef9e3aecbe set to critical for now 2024-09-11 14:11:19 -07:00
Kenneth Kehl
6da52f91b3 get list of serious violations 2024-09-11 14:01:57 -07:00
Kenneth Kehl
5eadcc0319 switch to serious and above try removing aria label 2024-09-11 13:54:42 -07:00
Kenneth Kehl
8d6478c3c1 switch to serious and above try removing aria label 2024-09-11 13:31:50 -07:00
Kenneth Kehl
a267cf3d4b expand to all tests 2024-09-11 13:16:58 -07:00
Kenneth Kehl
f82bcb0509 expand to all tests 2024-09-11 13:08:50 -07:00
Kenneth Kehl
c3a2441ade try expanding use of axe 2024-09-11 12:57:10 -07:00
Kenneth Kehl
8ef357ea61 try expanding use of axe 2024-09-11 12:36:58 -07:00
Kenneth Kehl
286166c2ad fix import 2024-09-11 11:07:59 -07:00
Kenneth Kehl
7b9e5288f8 fix import 2024-09-11 11:02:01 -07:00
Kenneth Kehl
df85f66a43 fix import 2024-09-11 10:48:26 -07:00
Kenneth Kehl
a91d6d25c8 initial test axe-core 2024-09-11 10:39:28 -07:00
Carlo Costino
88c47d15f6 Merge pull request #1924 from GSA/dependabot/npm_and_yarn/better-npm-audit-3.11.0
Bump better-npm-audit from 3.8.3 to 3.11.0
2024-09-10 14:41:20 -04:00
dependabot[bot]
6537eb2b1d Bump better-npm-audit from 3.8.3 to 3.11.0
Bumps [better-npm-audit](https://github.com/jeemok/better-npm-audit) from 3.8.3 to 3.11.0.
- [Release notes](https://github.com/jeemok/better-npm-audit/releases)
- [Changelog](https://github.com/jeemok/better-npm-audit/blob/master/CHANGELOG.md)
- [Commits](https://github.com/jeemok/better-npm-audit/compare/v3.8.3...v3.11.0)

---
updated-dependencies:
- dependency-name: better-npm-audit
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-10 18:28:31 +00:00
Carlo Costino
75727520c0 Merge pull request #1918 from GSA/dependabot/npm_and_yarn/uswds/compile-1.2.0
Bump @uswds/compile from 1.1.0 to 1.2.0
2024-09-10 14:27:14 -04:00
Kenneth Kehl
d6c8ee90e8 cleanup 2024-09-10 09:29:46 -07:00
Kenneth Kehl
a3ef868924 cleanup 2024-09-10 08:46:31 -07:00
Kenneth Kehl
bdf4c9597b clean up Makefile and checks.yml 2024-09-10 07:17:41 -07:00
Carlo Costino
6e3db9d59a Merge pull request #1914 from GSA/dependabot/npm_and_yarn/sass-embedded-1.78.0
Bump sass-embedded from 1.77.8 to 1.78.0
2024-09-09 18:13:01 -04:00
Carlo Costino
fc88ca55e7 Merge pull request #1912 from GSA/dependabot/npm_and_yarn/rollup-4.21.2
Bump rollup from 4.21.1 to 4.21.2
2024-09-09 17:47:08 -04:00
Kenneth Kehl
b8edfa8175 fix dead code 2024-09-09 13:57:45 -07:00
Kenneth Kehl
1a247260ff fix tests and flake8 2024-09-09 13:50:02 -07:00
Kenneth Kehl
92229707e2 comment out part of test that is still failing 2024-09-09 13:27:07 -07:00
Kenneth Kehl
5db3d4878f fix last test 2024-09-09 13:03:16 -07:00
Kenneth Kehl
b1f1005441 reenable e2e tests 2024-09-09 12:50:16 -07:00
Kenneth Kehl
6223341826 reenable e2e tests 2024-09-09 12:34:52 -07:00
Kenneth Kehl
6b3fde3356 merge from main 2024-09-09 12:09:53 -07:00
Kenneth Kehl
749e4cc708 Merge pull request #1911 from GSA/notify-api-1301
start load test work
2024-09-09 09:21:48 -07:00
alexjanousekGSA
a2d542c726 Upgraded vul package 2024-09-09 12:05:00 -04:00
dependabot[bot]
b67b15b6c9 Bump @uswds/compile from 1.1.0 to 1.2.0
Bumps [@uswds/compile](https://github.com/uswds/uswds-compile) from 1.1.0 to 1.2.0.
- [Release notes](https://github.com/uswds/uswds-compile/releases)
- [Commits](https://github.com/uswds/uswds-compile/compare/v1.1.0...v1.2.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-04 23:42:43 +00:00
Kenneth Kehl
d94a35cd30 get the error message in the url 2024-09-04 14:21:45 -07:00
Carlo Costino
26e53e76c5 Merge pull request #1910 from GSA/1487-display-data-most-used-templates-table
Display data: Most used templates table
2024-09-04 15:59:21 -04:00
Carlo Costino
6cf540be04 Merge pull request #1917 from GSA/cryptography-update
Update cryptography Python dependency
2024-09-04 15:37:26 -04:00
Carlo Costino
e3007f273e Update cryptography Python dependency
This changeset updates the Python cryptography package to the latest release to address a pip-audit finding.

Signed-off-by: Carlo Costino <carlo.costino@gsa.gov>
2024-09-04 15:18:02 -04:00
Carlo Costino
46a980c505 Merge pull request #1909 from GSA/1908-remove-folder-and-header-folder-componentscomponents-since-we-no-longer-need-it
Deleting unused files/folders
2024-09-04 15:05:06 -04:00
Kenneth Kehl
b32dde1a07 debug key 2024-09-04 08:19:38 -07:00
Kenneth Kehl
87581705c6 merge from main 2024-09-04 08:09:25 -07:00
dependabot[bot]
3aa68a3d25 Bump sass-embedded from 1.77.8 to 1.78.0
Bumps [sass-embedded](https://github.com/sass/embedded-host-node) from 1.77.8 to 1.78.0.
- [Changelog](https://github.com/sass/embedded-host-node/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sass/embedded-host-node/compare/1.77.8...1.78.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>
2024-09-03 23:41:20 +00:00
Kenneth Kehl
1308894191 cleanup 2024-09-03 14:01:17 -07:00
Kenneth Kehl
35dcfccd5f fix tests maybe 2024-09-03 11:03:33 -07:00
Kenneth Kehl
ddf9c6d3ec fix tests maybe 2024-09-03 10:37:05 -07:00
Kenneth Kehl
9e9ba9186a try mock 2024-09-02 10:30:29 -07:00
Kenneth Kehl
b5e2bf705d try mock 2024-09-02 10:02:28 -07:00
Kenneth Kehl
5ada9914b4 try mock 2024-09-02 09:57:22 -07:00
Kenneth Kehl
c075cacae9 try mock 2024-09-02 09:46:27 -07:00
Kenneth Kehl
da7d618460 try mock 2024-09-02 09:42:52 -07:00
Kenneth Kehl
9340069454 try mock 2024-09-02 09:33:23 -07:00
Kenneth Kehl
6621144061 add load_test to excluded 2024-09-02 09:17:51 -07:00
Kenneth Kehl
f6a1f1af5b add load_test to excluded 2024-09-02 09:01:42 -07:00
Kenneth Kehl
8b81c5ce7d add load_test to excluded 2024-09-02 08:43:23 -07:00
Kenneth Kehl
ce74175be1 clean up 2024-09-02 08:34:15 -07:00
Kenneth Kehl
f74bf271a5 clean up 2024-09-02 08:22:03 -07:00
Kenneth Kehl
e8b12de581 clean up 2024-09-02 08:13:05 -07:00
Kenneth Kehl
dcc8055377 more work on load test 2024-09-02 07:51:57 -07:00
dependabot[bot]
ab96955982 Bump rollup from 4.21.1 to 4.21.2
Bumps [rollup](https://github.com/rollup/rollup) from 4.21.1 to 4.21.2.
- [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.21.1...v4.21.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-30 23:35:24 +00:00
Kenneth Kehl
c36561ee4d start load test work 2024-08-30 09:50:01 -07:00
Beverly Nguyen
c51a5593a7 clean up 2024-08-29 17:34:24 -07:00
Beverly Nguyen
be14a4a0c4 fix testing 2024-08-29 17:10:51 -07:00
Beverly Nguyen
65ef22f26e removed template type 2024-08-29 15:19:51 -07:00
Beverly Nguyen
e704cb1b78 added template-stats.html 2024-08-29 15:10:08 -07:00
Beverly Nguyen
2cae3462d3 Merge branch 'main' into 1487-display-data-most-used-templates-table 2024-08-29 14:57:36 -07:00
Beverly Nguyen
5beb9fcce1 reverting changes 2024-08-29 14:56:12 -07:00
Beverly Nguyen
7fea566201 dashboard edit 2024-08-29 14:52:04 -07:00
Beverly Nguyen
3158b91378 new revised table 2024-08-29 14:28:37 -07:00
Beverly Nguyen
6f2c7be4ce fix testing 2024-08-29 12:24:44 -07:00
Beverly Nguyen
b15b3a67f6 delete unused files 2024-08-29 11:37:42 -07:00
Kenneth Kehl
8cc14ec4bf try using various commands in checks.yml to obtain admin logs while e2e tests are running 2024-08-29 11:37:21 -07:00
Kenneth Kehl
4dbd9ede92 try using various commands in checks.yml to obtain admin logs while e2e tests are running 2024-08-29 11:25:44 -07:00
Kenneth Kehl
38fde60348 try using various commands in checks.yml to obtain admin logs while e2e tests are running 2024-08-29 11:10:51 -07:00
Kenneth Kehl
b1ad888b65 try using various commands in checks.yml to obtain admin logs while e2e tests are running 2024-08-29 10:20:38 -07:00
Kenneth Kehl
43bafbb617 try using various commands in checks.yml to obtain admin logs while e2e tests are running 2024-08-29 10:07:21 -07:00
Kenneth Kehl
c5060d3f14 try putting default log handler back in place to get logs streaming to github job output 2024-08-29 09:40:36 -07:00
Kenneth Kehl
15a87ec026 try putting default log handler back in place to get logs streaming to github job output 2024-08-29 09:27:04 -07:00
Kenneth Kehl
2690a059d2 try putting default log handler back in place to get logs streaming to github job output 2024-08-29 09:15:23 -07:00
Kenneth Kehl
5cf80ec5dc try putting default log handler back in place to get logs streaming to github job output 2024-08-29 09:07:14 -07:00
Kenneth Kehl
6fffa3cf43 try putting default log handler back in place to get logs streaming to github job output 2024-08-29 08:54:53 -07:00
Kenneth Kehl
e4c99dc15b try to pass message back in 'next' field 2024-08-29 08:28:38 -07:00
Kenneth Kehl
33e616a8f1 try to pass message back in 'next' field 2024-08-29 08:07:37 -07:00
Kenneth Kehl
d4b9a95b29 hmmm 2024-08-28 13:39:35 -07:00
Kenneth Kehl
ed1ef5cd4a increase debug 2024-08-28 13:04:06 -07:00
Kenneth Kehl
caefc958df increase debug 2024-08-28 12:56:46 -07:00
Kenneth Kehl
981281f725 increase debug 2024-08-28 12:50:09 -07:00
Kenneth Kehl
4cacd108b1 increase debug 2024-08-28 12:36:32 -07:00
Kenneth Kehl
68c0763dec increase debug 2024-08-28 11:31:22 -07:00
Kenneth Kehl
3f7d1ffffe increase debug 2024-08-28 11:17:52 -07:00
Kenneth Kehl
7e38e28a5e increase debug 2024-08-28 11:04:25 -07:00
Kenneth Kehl
f5baf1cb79 increase debug 2024-08-28 10:50:47 -07:00
Kenneth Kehl
67f128c26e increase debug 2024-08-28 10:30:53 -07:00
Kenneth Kehl
88af5de89d increase debug 2024-08-28 10:14:31 -07:00
Kenneth Kehl
7d1a50a5b0 increase debug 2024-08-28 09:29:48 -07:00
Kenneth Kehl
758efa8215 increase debug 2024-08-28 08:02:40 -07:00
Kenneth Kehl
e9a8e2769a increase debug 2024-08-28 07:37:33 -07:00
Kenneth Kehl
f27d53116e increase debug 2024-08-28 07:17:13 -07:00
Beverly Nguyen
b20c8ec909 Merge pull request #1901 from GSA/notify-admin-1877
set timezone automatically
2024-08-27 17:35:58 -07:00
Kenneth Kehl
664d07b414 increase debug 2024-08-27 15:16:25 -07:00
Kenneth Kehl
da6e9f4e8c increase debug 2024-08-27 15:06:00 -07:00
Kenneth Kehl
549855c957 increase debug 2024-08-27 14:56:09 -07:00
Kenneth Kehl
20428f7485 increase debug 2024-08-27 14:43:28 -07:00
Kenneth Kehl
d00f4b5f46 try again 2024-08-27 14:22:07 -07:00
Kenneth Kehl
d756b2289f code review feedback 2024-08-27 12:47:15 -07:00
Beverly Nguyen
8f15a83415 Merge pull request #1898 from GSA/1862-data-visualization-bar-chart-isnt-show-clearly
1862 data visualization bar chart isnt show clearly
2024-08-27 12:26:47 -07:00
Beverly Nguyen
bb106c04f1 fixed testing 2024-08-27 12:19:55 -07:00
Carlo Costino
5a4402fd8b Merge pull request #1905 from GSA/dependabot/npm_and_yarn/rollup-4.21.1
Bump rollup from 4.21.0 to 4.21.1
2024-08-27 12:33:34 -04:00
Alex Janousek
3d808cd3e5 Merge pull request #1900 from GSA/dependabot/npm_and_yarn/babel/preset-env-7.25.4
Bump @babel/preset-env from 7.25.3 to 7.25.4
2024-08-27 08:41:46 -04:00
dependabot[bot]
87d4911f89 Bump rollup from 4.21.0 to 4.21.1
Bumps [rollup](https://github.com/rollup/rollup) from 4.21.0 to 4.21.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.21.0...v4.21.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-26 23:42:40 +00:00
Kenneth Kehl
407f049360 fix sort ugh 2024-08-26 11:11:59 -07:00
Kenneth Kehl
e15c89703b fix flake 8 2024-08-26 10:24:29 -07:00
Kenneth Kehl
9907409efd remove Change button for timezone 2024-08-26 10:06:29 -07:00
Kenneth Kehl
7b6b363532 add a call on the send page as well 2024-08-26 09:02:39 -07:00
Kenneth Kehl
f78fe350b0 fix flake 8 2024-08-26 07:59:39 -07:00
Kenneth Kehl
0c612581cd finish up 2024-08-26 07:50:24 -07:00
Beverly Nguyen
ca7c2e0c93 captialization 2024-08-23 18:36:16 -07:00
Beverly Nguyen
1768a4bbd9 changed scale y 2024-08-23 18:31:05 -07:00
Kenneth Kehl
3456bb47a1 move file around 2024-08-23 14:01:51 -07:00
Kenneth Kehl
8e3cd6324f fix some tests 2024-08-23 13:13:24 -07:00
Kenneth Kehl
c67407f090 fix some tests 2024-08-23 12:59:00 -07:00
Kenneth Kehl
50e67d2308 cleanup 2024-08-23 12:21:43 -07:00
Kenneth Kehl
85bafde78d fix flake 8 2024-08-23 12:13:14 -07:00
Kenneth Kehl
c7b83596ba add gulp reference 2024-08-23 11:47:43 -07:00
Kenneth Kehl
77d49754c6 initial 2024-08-23 11:03:32 -07:00
Carlo Costino
1361c2f836 Merge pull request #1834 from GSA/dependabot/pip/phonenumbers-8.13.43
Bump phonenumbers from 8.13.40 to 8.13.43
2024-08-23 11:42:54 -04:00
dependabot[bot]
5b14a4ea90 Bump @babel/preset-env from 7.25.3 to 7.25.4
Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.25.3 to 7.25.4.
- [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.25.4/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>
2024-08-22 23:36:50 +00:00
Beverly Nguyen
769e63ebc7 Merge branch 'main' into 1862-data-visualization-bar-chart-isnt-show-clearly 2024-08-22 15:46:40 -07:00
Beverly Nguyen
b40cb5a9ad updated width when sms is zero 2024-08-22 15:46:17 -07:00
Beverly Nguyen
6ae00cabfe Merge pull request #1887 from GSA/1886-cleaning-up-admin-and-fix-missing-files
Clean up admin folders/templates and fix missing files
2024-08-22 14:38:33 -07:00
Beverly Nguyen
13cdb10d88 Merge branch 'main' into 1886-cleaning-up-admin-and-fix-missing-files 2024-08-22 09:56:22 -07:00
dependabot[bot]
8c0fd48e06 Bump phonenumbers from 8.13.40 to 8.13.43
Bumps [phonenumbers](https://github.com/daviddrysdale/python-phonenumbers) from 8.13.40 to 8.13.43.
- [Commits](https://github.com/daviddrysdale/python-phonenumbers/compare/v8.13.40...v8.13.43)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-22 16:18:00 +00:00
Andrew Shumway
4f9f546f23 Merge pull request #1829 from GSA/dependabot/pip/boto3-1.34.156
Bump boto3 from 1.34.150 to 1.34.156
2024-08-22 10:16:06 -06:00
Jonathan Bobel
4ca7087218 Merge pull request #1882 from GSA/1863-fix-pagination-count
remove ellipsis in pagination
2024-08-21 16:45:03 -04:00
Jonathan Bobel
75f6ee6837 Merge pull request #1885 from GSA/1881-closed-button-is-missing
updated url for close.svg
2024-08-21 16:44:16 -04:00
Carlo Costino
25722deb68 Merge pull request #1873 from GSA/dependabot/npm_and_yarn/better-npm-audit-3.8.3
Bump better-npm-audit from 3.7.3 to 3.8.3
2024-08-21 12:37:27 -04:00
dependabot[bot]
27c6098cc8 Bump better-npm-audit from 3.7.3 to 3.8.3
Bumps [better-npm-audit](https://github.com/jeemok/better-npm-audit) from 3.7.3 to 3.8.3.
- [Release notes](https://github.com/jeemok/better-npm-audit/releases)
- [Changelog](https://github.com/jeemok/better-npm-audit/blob/master/CHANGELOG.md)
- [Commits](https://github.com/jeemok/better-npm-audit/compare/v3.7.3...v3.8.3)

---
updated-dependencies:
- dependency-name: better-npm-audit
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-21 16:26:19 +00:00
Carlo Costino
f553b863c6 Merge pull request #1874 from GSA/dependabot/npm_and_yarn/rollup-4.21.0
Bump rollup from 4.20.0 to 4.21.0
2024-08-21 12:24:59 -04:00
Carlo Costino
5243878ef7 Merge pull request #1880 from GSA/notify-admin-1870
Change phone number in Notify.gov w/o entering password
2024-08-20 21:57:36 -04:00
Kenneth Kehl
3b1bd05a10 remove commented out code 2024-08-20 13:52:47 -07:00
Beverly Nguyen
10b65efe1f removed new folder and moved components to components folder 2024-08-20 13:17:01 -07:00
Beverly Nguyen
91f79f694e update webmanifest url to fix broken link 2024-08-20 12:19:09 -07:00
Beverly Nguyen
1bfd619057 updated url for close.svg 2024-08-20 12:01:06 -07:00
Kenneth Kehl
03fae4d08f fix test 2024-08-20 07:14:45 -07:00
Beverly Nguyen
67b928085c remove jobs 2024-08-19 14:37:45 -07:00
Beverly Nguyen
cd98439ac3 remove ellipsis in pagination 2024-08-19 14:33:28 -07:00
Kenneth Kehl
f0ec9e3da9 fix imports 2024-08-19 13:57:07 -07:00
Kenneth Kehl
66a555a2d4 remove print statements 2024-08-19 13:11:02 -07:00
Kenneth Kehl
339797558a remove debug 2024-08-19 13:02:20 -07:00
Kenneth Kehl
672fed7e4b initial 2024-08-19 10:57:08 -07:00
Carlo Costino
5a3f3e5ca0 Merge pull request #1864 from GSA/1859-activity-page-recent-job-reports-not-displaying-for-some-one-off-sends
changed download conditionals
2024-08-19 12:37:52 -04:00
dependabot[bot]
6b236bfa0f Bump boto3 from 1.34.150 to 1.34.156
Bumps [boto3](https://github.com/boto/boto3) from 1.34.150 to 1.34.156.
- [Release notes](https://github.com/boto/boto3/releases)
- [Commits](https://github.com/boto/boto3/compare/1.34.150...1.34.156)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-19 16:32:56 +00:00
Andrew Shumway
0c1fc478da Merge pull request #1872 from GSA/add-andrew-assignee-dependabot
Add andrew assignee dependabot
2024-08-19 10:30:21 -06:00
Kenneth Kehl
01eb194a08 Merge pull request #1858 from GSA/1857-no-messages-sent-y-axis-fix
1857 - Activity chart updates
2024-08-19 09:25:45 -07:00
Andrew Shumway
2c7d9d48dc Pull main 2024-08-19 09:57:25 -06:00
dependabot[bot]
1d03d73642 Bump rollup from 4.20.0 to 4.21.0
Bumps [rollup](https://github.com/rollup/rollup) from 4.20.0 to 4.21.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.20.0...v4.21.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-19 15:52:59 +00:00
Alex Janousek
b61329f86d Merge pull request #1853 from GSA/dependabot/update
Added Alex to dependabot node assignee
2024-08-19 11:51:36 -04:00
Andrew Shumway
062eefcf7a Add Andrew to dependabot assignee/review 2024-08-19 09:20:51 -06:00
Beverly Nguyen
8b2a3c26fa changed download conditionals 2024-08-16 11:01:00 -07:00
Andrew Shumway
b7a0dc2225 pull main 2024-08-16 10:59:40 -06:00
Andrew Shumway
6dc9efd13d Poetry lock 2024-08-16 10:59:14 -06:00
Jonathan Bobel
1eb54f8252 Activity chart updates 2024-08-16 11:27:48 -04:00
alexjanousekGSA
eca30e5b20 Added Alex to dependabot node assignee 2024-08-15 15:36:09 -04:00
Carlo Costino
0b75486210 Merge pull request #1852 from GSA/fix-prod-terraform-formatting
Fix formatting in production main.tf file
2024-08-15 14:48:12 -04:00
Alex Janousek
5adc45d591 Merge pull request #1836 from GSA/dependabot/npm_and_yarn/uswds/uswds-3.8.2
Bump @uswds/uswds from 3.8.1 to 3.8.2
2024-08-15 14:35:13 -04:00
Carlo Costino
4cdcdc075c Fix formatting in production main.tf file
This changeset fixes formatting in the production main.tf file.  The terraform fmt -check command is currently failing because of extra whitespace.

Signed-off-by: Carlo Costino <carlo.costino@gsa.gov>
2024-08-15 14:34:56 -04:00
Jonathan Bobel
c5149f52d4 Merge pull request #1850 from GSA/arrow-img-missing
arrow img missing
2024-08-15 13:34:50 -04:00
Beverly Nguyen
529f5e76ba Merge pull request #1849 from GSA/JB-favicon-fix
Favicon - removed unnecessary line
2024-08-15 10:16:21 -07:00
Beverly Nguyen
f30363b473 updated url 2024-08-15 10:16:02 -07:00
Beverly Nguyen
0b7183a29d arrow img missing 2024-08-15 10:02:53 -07:00
Jonathan Bobel
3a38c1f94b Merge branch 'main' of https://github.com/GSA/notifications-admin into JB-favicon-fix 2024-08-15 12:30:02 -04:00
Jonathan Bobel
7962bfd85f Quick fix 2024-08-15 12:29:35 -04:00
Alex Janousek
d495840a67 Merge pull request #1848 from GSA/JB-favicon-fix
Favicon path fix
2024-08-15 12:11:58 -04:00
Jonathan Bobel
154e58a83d This worked for me locally, seeing if the change bubbles up to staging 2024-08-15 11:39:13 -04:00
Carlo Costino
45e4910372 Merge pull request #1847 from GSA/refactoring-fetch
Refactoring fetch
2024-08-15 11:18:11 -04:00
Beverly Nguyen
68fbdb2bb8 added fetch to jest 2024-08-14 15:59:44 -07:00
Beverly Nguyen
6175bfb2fb fixed pytesting 2024-08-14 15:47:50 -07:00
Beverly Nguyen
c2cb8e8f85 fixed flake8 2024-08-14 15:39:15 -07:00
Beverly Nguyen
1a76ff5dbe Merge branch 'main' into refactoring-fetch 2024-08-14 15:36:21 -07:00
Beverly Nguyen
cbffcff1cd removed fetch on resize and redraw tooltip 2024-08-14 15:33:22 -07:00
Kenneth Kehl
4960a49d11 Merge pull request #1845 from GSA/1843-qa-for-prod-push-aug-14
added 3 day download link in activity page
2024-08-14 15:07:52 -07:00
Kenneth Kehl
3a43dce715 Merge pull request #1846 from GSA/JB-QA-Updates
Updates from the QA review
2024-08-14 15:07:26 -07:00
Beverly Nguyen
b9d1ae5ad8 refactoring fetch for activity chart 2024-08-14 13:45:24 -07:00
Jonathan Bobel
a31e199147 Updates from the QA review 2024-08-14 16:11:00 -04:00
Beverly Nguyen
46124a45cc added 3 day download 2024-08-14 12:28:09 -07:00
Carlo Costino
56d9970ebe Merge pull request #1823 from GSA/1674-match-recent-batches-to-wireframe
1674 match recent batches to wireframe
2024-08-14 10:45:28 -04:00
Kenneth Kehl
3ce9097a02 Merge pull request #1837 from GSA/1817-activity-page-updates
1817 activity page updates
2024-08-14 07:33:15 -07:00
Jonathan Bobel
d57531a2b5 Merge branch 'main' of https://github.com/GSA/notifications-admin into 1674-match-recent-batches-to-wireframe 2024-08-13 16:51:18 -04:00
Carlo Costino
44126a2f0c Merge pull request #1839 from GSA/bugfix/images
Added back part that got removed that allowed png images
2024-08-13 16:19:04 -04:00
Jonathan Bobel
49e390dd5e Using rows so it passes the test 2024-08-13 15:29:19 -04:00
Jonathan Bobel
f17ee0147f Test updates 2024-08-13 15:16:19 -04:00
alexjanousekGSA
3397b0c380 Added back part that got removed that allowed png images 2024-08-13 14:15:02 -05:00
Jonathan Bobel
9a59a09bf8 Show templates even if there is just 1 2024-08-13 15:06:08 -04:00
Jonathan Bobel
0fabcabeb3 Merge branch 'main' of https://github.com/GSA/notifications-admin into 1674-match-recent-batches-to-wireframe
# Conflicts:
#	tests/app/main/views/test_dashboard.py
#	tests/javascripts/activityChart.test.js
2024-08-13 14:57:01 -04:00
Kenneth Kehl
6e77caee1a Merge pull request #1815 from GSA/notify-admin-1218
add front end for download all users
2024-08-13 11:52:13 -07:00
Kenneth Kehl
b5d4e12466 fix formatting 2024-08-13 11:37:08 -07:00
Kenneth Kehl
3a647ffa91 Merge branch 'main' into notify-admin-1218 2024-08-13 11:27:14 -07:00
Beverly Nguyen
832f18a1a9 added zeros to failed messages 2024-08-13 09:56:36 -07:00
Kenneth Kehl
556e381677 Merge pull request #1790 from GSA/dependabot/pip/redis-5.0.8
Bump redis from 5.0.7 to 5.0.8
2024-08-13 07:44:58 -07:00
Beverly Nguyen
0391c94677 adjusted width 2024-08-12 23:53:44 -07:00
Beverly Nguyen
5d211f7c59 fixed flake8 2024-08-12 23:50:13 -07:00
Beverly Nguyen
e3c78c5871 added download url and stat count in table 2024-08-12 23:45:01 -07:00
Beverly Nguyen
c5b71dfe8d added download url and stat count in table 2024-08-12 23:28:41 -07:00
dependabot[bot]
847057b2a8 Bump redis from 5.0.7 to 5.0.8
Bumps [redis](https://github.com/redis/redis-py) from 5.0.7 to 5.0.8.
- [Release notes](https://github.com/redis/redis-py/releases)
- [Changelog](https://github.com/redis/redis-py/blob/master/CHANGES)
- [Commits](https://github.com/redis/redis-py/compare/v5.0.7...v5.0.8)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-13 02:29:34 +00:00
dependabot[bot]
932963beb5 Bump @uswds/uswds from 3.8.1 to 3.8.2
Bumps [@uswds/uswds](https://github.com/uswds/uswds) from 3.8.1 to 3.8.2.
- [Release notes](https://github.com/uswds/uswds/releases)
- [Commits](https://github.com/uswds/uswds/compare/v3.8.1...v3.8.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-13 02:28:57 +00:00
Carlo Costino
d10a46c5ee Merge pull request #1832 from GSA/1810-clean-up-data-viz-ui-component
1810 clean up data viz UI component
2024-08-12 22:27:40 -04:00
Beverly Nguyen
cf5ea736e2 removed unused js file 2024-08-12 15:08:40 -07:00
Beverly Nguyen
aa1a738463 uninstalled sockeio client 2024-08-12 15:05:24 -07:00
Beverly Nguyen
bfb08173c2 Merge branch 'main' into 1810-clean-up-data-viz-ui-component 2024-08-12 15:02:48 -07:00
Carlo Costino
46c11285c6 Merge pull request #1831 from GSA/feature/updating-node-modules
Feature/updating node modules
2024-08-12 17:45:04 -04:00
alexjanousekGSA
57ee345451 Removed chart js 2024-08-12 15:29:35 -06:00
Beverly Nguyen
6d09d44a84 removed flask-socketio 2024-08-12 13:38:29 -07:00
Beverly Nguyen
a0e177b99b add comments 2024-08-12 11:51:20 -07:00
Beverly Nguyen
96d6415578 add comments 2024-08-12 11:47:56 -07:00
Kenneth Kehl
bda957f76a change delimiter back to comma 2024-08-12 07:27:32 -07:00
Kenneth Kehl
6cb03d962e merge from main 2024-08-12 07:15:46 -07:00
Beverly Nguyen
2f62a57c7c fixed pytest 2024-08-09 15:04:56 -07:00
Beverly Nguyen
de27071112 fixed js errors 2024-08-09 14:49:16 -07:00
Beverly Nguyen
2cbab8727f fixed isort 2024-08-09 14:33:53 -07:00
Beverly Nguyen
a6e7e15643 Merge branch 'main' into 1810-clean-up-data-viz-ui-component 2024-08-09 14:29:51 -07:00
Beverly Nguyen
9e6f87ace1 removed socket io 2024-08-09 14:29:14 -07:00
Carlo Costino
7d22f23217 Merge pull request #1830 from GSA/1824-data-viz-changes
1824 data viz changes
2024-08-09 15:59:11 -04:00
Carlo Costino
75a69dc8a6 Merge pull request #1780 from GSA/notify-compliance-48
Notify compliance 48
2024-08-09 15:32:29 -04:00
Kenneth Kehl
191b591ac0 merge from main 2024-08-09 11:40:44 -07:00
alexjanousekGSA
e5b4dd5a31 Merge branch 'feature/updating-node-modules' of https://github.com/GSA/notifications-admin into feature/updating-node-modules 2024-08-09 11:00:53 -06:00
alexjanousekGSA
d9d802cfb7 Added charts back 2024-08-09 10:57:59 -06:00
Alex Janousek
cb8e620343 Merge branch 'main' into feature/updating-node-modules 2024-08-09 10:50:30 -06:00
alexjanousekGSA
393ba67878 Added d3 2024-08-09 10:48:39 -06:00
alexjanousekGSA
39981c1f70 Added back new chart module 2024-08-09 10:43:56 -06:00
alexjanousekGSA
0a39077fc8 Added another missing file 2024-08-09 09:41:13 -06:00
alexjanousekGSA
220106ff6b Added another file 2024-08-09 09:18:50 -06:00
Jonathan Bobel
832932f3b8 Updates to styles and js 2024-08-09 10:33:51 -04:00
alexjanousekGSA
1acb37c012 Added styling files back 2024-08-09 07:40:58 -06:00
Jonathan Bobel
45bc6a9173 test updates 2024-08-08 16:53:56 -04:00
Jonathan Bobel
3e472e0a97 Test update 2024-08-08 16:35:09 -04:00
alexjanousekGSA
9b768846cb Updated rollup 2024-08-08 14:32:33 -06:00
Jonathan Bobel
a395ad0281 Multiples of 8 2024-08-08 16:30:34 -04:00
Jonathan Bobel
28bc4befbe Small changes to the data viz colors and chart sizing 2024-08-08 16:29:35 -04:00
Kenneth Kehl
193e1f7952 fix duplicate test name 2024-08-08 13:16:29 -07:00
Kenneth Kehl
38a6c35afd add test 2024-08-08 13:11:58 -07:00
Kenneth Kehl
ee1dcdeb03 strip commas from fields 2024-08-08 13:02:51 -07:00
alexjanousekGSA
25f75a98ec Replaced stream queue 2024-08-08 14:01:05 -06:00
alexjanousekGSA
cc310d8a23 Added simple package updates 2024-08-08 13:58:33 -06:00
alexjanousekGSA
e730c46525 Removed test command 2024-08-08 13:45:38 -06:00
Andrew Shumway
7d6b209c8d Merge pull request #1828 from GSA/dependabot/pip/botocore-1.34.156
Bump botocore from 1.34.150 to 1.34.156
2024-08-08 13:23:10 -06:00
Jonathan Bobel
064cae7c31 Merge branch 'main' of https://github.com/GSA/notifications-admin into 1674-match-recent-batches-to-wireframe
# Conflicts:
#	tests/javascripts/activityChart.test.js
2024-08-08 15:07:03 -04:00
Jonathan Bobel
a3d9c24d34 Fixing a test 2024-08-08 14:47:35 -04:00
Jonathan Bobel
c778ad092b Removing reports column and adjusting tests 2024-08-08 14:42:15 -04:00
Kenneth Kehl
f3cab5a6cc revert Makefile changes 2024-08-08 11:22:08 -07:00
Kenneth Kehl
b6dd9967ed code review feedback 2024-08-08 11:14:24 -07:00
Carlo Costino
b654bca6bf Merge pull request #1807 from GSA/1806-add-how-to-debug-in-staging-in-docs
add debug staging api to docs
2024-08-08 14:03:32 -04:00
Kenneth Kehl
1a75e56187 code review feedback 2024-08-08 10:59:57 -07:00
Carlo Costino
21da7ef7db Update docs/debug-issues-with-staging-api.md
Some additional formatting.
2024-08-08 13:49:49 -04:00
Kenneth Kehl
69e9c6d1ca code review changes 2024-08-08 10:47:18 -07:00
dependabot[bot]
3bafa48577 Bump botocore from 1.34.150 to 1.34.156
Bumps [botocore](https://github.com/boto/botocore) from 1.34.150 to 1.34.156.
- [Changelog](https://github.com/boto/botocore/blob/develop/CHANGELOG.rst)
- [Commits](https://github.com/boto/botocore/compare/1.34.150...1.34.156)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-08 16:39:07 +00:00
alexjanousekGSA
f8e85e5fa6 Added new package to see if it breaks pipeline 2024-08-07 21:58:55 -06:00
Beverly Nguyen
d1701ab946 Merge pull request #1819 from GSA/1810-clean-up-data-viz-ui-component
fix 400 errors
2024-08-07 17:23:11 -07:00
Beverly Nguyen
b426a17cf8 fix activity jtest test 2024-08-07 17:15:15 -07:00
Beverly Nguyen
86748c3317 js lint 2024-08-07 17:03:25 -07:00
Beverly Nguyen
1a75fc5142 fixed coverage report 2024-08-07 16:44:02 -07:00
Beverly Nguyen
2bb398a962 fixing console errors 2024-08-07 15:54:54 -07:00
Beverly Nguyen
92026a83aa fixing console errors 2024-08-07 15:54:21 -07:00
Beverly Nguyen
a6ac7086da fixing console errors 2024-08-07 15:48:35 -07:00
Beverly Nguyen
c6f1ae37a4 fixing console errors 2024-08-07 15:47:33 -07:00
Beverly Nguyen
f65655e56c fixing console errors 2024-08-07 15:42:46 -07:00
Beverly Nguyen
5797e989fd fixing console errors 2024-08-07 15:33:08 -07:00
Beverly Nguyen
4bfbd9f71f fixing console errors 2024-08-07 15:27:43 -07:00
Beverly Nguyen
65d339fb8a fix jest testing errors 2024-08-07 15:16:09 -07:00
Carlo Costino
0f75fbf11e Merge pull request #1813 from GSA/notify-admin-1267
E2E Bug Fix: Get Dependabot to work properly with end-to-end tests
2024-08-07 18:00:13 -04:00
alexjanousekGSA
b1668ebe57 Made update to trigger successful build 2024-08-07 14:51:38 -06:00
Jonathan Bobel
e7e3bac36c Updates to the activity table(s) on the dashboard 2024-08-07 16:20:40 -04:00
Kenneth Kehl
b77a142f6b try moving if block 2024-08-07 07:37:04 -07:00
Kenneth Kehl
2497679046 switch commas to tabs 2024-08-07 07:21:53 -07:00
Beverly Nguyen
078fbb7f4e Merge branch 'main' into 1810-clean-up-data-viz-ui-component 2024-08-06 17:32:12 -07:00
Beverly Nguyen
ad2151b637 fix 400 errors 2024-08-06 17:28:14 -07:00
Kenneth Kehl
aed9778a65 fix tests 2024-08-06 12:42:34 -07:00
Jonathan Bobel
1a528626de Merge branch '1669-toggle-between-recent-activity-and-all-activity' into 1674-match-recent-batches-to-wireframe 2024-08-06 15:28:46 -04:00
Kenneth Kehl
323ecf63f4 remove unused import 2024-08-06 12:27:42 -07:00
Jonathan Bobel
b01ca6bb15 Updates to the template table and removing the table being replicated 2 times 2024-08-06 15:03:57 -04:00
Kenneth Kehl
7b03d02447 add front end for download all users 2024-08-06 11:31:05 -07:00
Kenneth Kehl
89eb24c795 add ds.baseline 2024-08-06 08:37:43 -07:00
Jonathan Bobel
6cecd5903a Small test update 2024-08-06 10:17:50 -04:00
Kenneth Kehl
a48f4fa476 Merge pull request #1809 from GSA/notify-admin-1267
E2E Bug Fix: Get Dependabot to work properly with end-to-end tests
2024-08-05 14:10:02 -07:00
Kenneth Kehl
cf505faef9 Merge pull request #1808 from GSA/1795-bug-recent-batches-table-not-displaying-most-recent-jobs
avoid jobs with job_status = cancelled
2024-08-05 13:34:36 -07:00
Kenneth Kehl
7c89863eab add more environment variables for e2e tests 2024-08-05 13:20:21 -07:00
Beverly Nguyen
bdbdaaae8a avoid jobs with job status cancelled 2024-08-05 13:06:51 -07:00
Jonathan Bobel
6e3ad6b354 Merge branch 'main' of https://github.com/GSA/notifications-admin into 1669-toggle-between-recent-activity-and-all-activity 2024-08-05 16:06:15 -04:00
Jonathan Bobel
d6079a463d Adding the ability to switch tables in the js
Updating css
Testing updated js
2024-08-05 16:05:54 -04:00
Beverly Nguyen
cd1cedad69 add debug staging api to docs 2024-08-05 12:09:38 -07:00
Kenneth Kehl
bc853367d9 Merge pull request #1800 from GSA/update-dependabot
Update dependabot.yml config
2024-08-05 11:35:23 -07:00
Carlo Costino
9f79ae314f Update dependabot.yml config
This changeset updates our dependabot.yml configuration to match what the documentation currently says is current:

* Adjusts version referenced from 3 to 2 to match the docs
* Adjusts the formatting of values to match the docs

Signed-off-by: Carlo Costino <carlo.costino@gsa.gov>
2024-08-05 11:55:43 -04:00
Jonathan Bobel
7ac6400827 Merge branch 'main' of https://github.com/GSA/notifications-admin into 1669-toggle-between-recent-activity-and-all-activity 2024-08-05 09:34:50 -04:00
Kenneth Kehl
af59b13836 code review feedback 2024-08-02 14:30:35 -07:00
Kenneth Kehl
79786df09c merge from main 2024-08-02 14:29:46 -07:00
Carlo Costino
863979b726 Merge pull request #1797 from GSA/Logging_the_job_information_that_was_retrieved
Logging the job information that was retrieved
2024-08-02 16:37:43 -04:00
Cliff Hill
0fe73f6973 Trying this now.
Signed-off-by: Cliff Hill <clifford.hill@gsa.gov>
2024-08-02 15:14:17 -04:00
Cliff Hill
65da623458 Done stuff to make serialization work hopefully.
Signed-off-by: Cliff Hill <clifford.hill@gsa.gov>
2024-08-02 15:01:55 -04:00
Cliff Hill
6b8be20ed2 Black & isort.
Signed-off-by: Cliff Hill <clifford.hill@gsa.gov>
2024-08-02 14:50:52 -04:00
Cliff Hill
27ee039b2d iDumping object as a JSON.
Signed-off-by: Cliff Hill <clifford.hill@gsa.gov>
2024-08-02 14:42:29 -04:00
Cliff Hill
03466d0184 Removing pformat from the change.
Signed-off-by: Cliff Hill <clifford.hill@gsa.gov>
2024-08-02 14:37:06 -04:00
Cliff Hill
131d99a4df iAdding some logging for Bev.
Signed-off-by: Cliff Hill <clifford.hill@gsa.gov>
2024-08-02 14:02:44 -04:00
Andrew Shumway
136e82102b Pull main 2024-08-02 09:00:24 -06:00
Andrew Shumway
b84d00bfb1 poetry lock 2024-08-02 09:00:04 -06:00
Carlo Costino
22bc355562 Merge pull request #1623 from GSA/1484-dashboard-visualizations
1484 dashboard visualizations
2024-08-01 21:31:33 -04:00
Beverly Nguyen
9471174f20 removed extra template path in gulp 2024-08-01 15:44:05 -07:00
Beverly Nguyen
da33213730 Merge branch 'main' into 1484-dashboard-visualizations 2024-08-01 15:42:44 -07:00
Carlo Costino
da34acaf76 Merge pull request #1743 from GSA/1677-user-story-view-table-of-all-jobs-sent-with-my-service
new activity page
2024-08-01 15:00:38 -04:00
Beverly Nguyen
97ae46d57b table th color changed 2024-08-01 09:34:57 -07:00
Carlo Costino
989aaf747f Merge pull request #1784 from GSA/1676-individual-job-table-updates
1676 - Updates to Job status page
2024-07-31 23:35:41 -04:00
Carlo Costino
fd046e02e7 Merge pull request #1791 from GSA/notify-admin-1188
remove uk bank holidays
2024-07-31 16:50:49 -04:00
Jonathan Bobel
42db905026 Creating 2 tables to be put tied into the dropdown event 2024-07-31 16:00:06 -04:00
Andrew Shumway
0ea4d8897e Pull main 2024-07-31 12:18:03 -06:00
Jonathan Bobel
7715e6b275 Removing commented code - PR comment 2024-07-31 12:15:55 -04:00
Jonathan Bobel
cfc65d20c3 Too many blank lines? 2024-07-31 11:54:45 -04:00
Jonathan Bobel
634f3f9643 Removing a print command 2024-07-31 11:34:42 -04:00
Jonathan Bobel
71cca74779 Updates to dashboard and py tests for dashboard 2024-07-31 11:34:19 -04:00
Kenneth Kehl
a1180747d5 remove uk bank holidays 2024-07-31 08:17:29 -07:00
Jonathan Bobel
d12d2418a5 Addressing some PR comments 2024-07-31 10:25:59 -04:00
Jonathan Bobel
5680f5c98f Merge branch 'main' of https://github.com/GSA/notifications-admin into 1484-dashboard-visualizations 2024-07-31 10:01:35 -04:00
Carlo Costino
6225181087 Merge pull request #1787 from GSA/signin_mess
fix signin issue
2024-07-30 17:24:13 -04:00
Jonathan Bobel
cbb4254204 Merge branch 'main' of https://github.com/GSA/notifications-admin into 1484-dashboard-visualizations 2024-07-30 15:15:08 -04:00
Jonathan Bobel
6a5b033700 Needed to wrap this line 2024-07-30 14:35:30 -04:00
Jonathan Bobel
4df7eb57d7 Adjusting tests 2024-07-30 14:24:02 -04:00
Kenneth Kehl
86a2017f91 fix flake 8 2024-07-30 10:30:32 -07:00
Kenneth Kehl
a8fad883f1 fix keystring 2024-07-30 10:17:04 -07:00
Jonathan Bobel
1912ffbbcf Merge branch 'main' of https://github.com/GSA/notifications-admin into 1484-dashboard-visualizations
# Conflicts:
#	app/assets/sass/uswds/_uswds-theme-custom-styles.scss
#	app/templates/views/dashboard/dashboard.html
#	gulpfile.js
#	tests/app/main/views/test_dashboard.py
2024-07-30 10:45:53 -04:00
Carlo Costino
ae02972c6d Merge pull request #1783 from GSA/notify-admin-1690
try versioning-strategy
2024-07-29 17:57:02 -04:00
Carlo Costino
55e1664d04 Merge pull request #1785 from GSA/update-python-deps-7-30-24
Update Python dependencies - 7/30/24
2024-07-29 17:47:51 -04:00
Carlo Costino
d42cadd3b1 Update Python dependencies - 7/30/24
This changeset updates several Python dependencies that Dependabot has flagged for updating.  This includes a major version bump of notifications-python-client, which is mostly just formatted string updates that should not have any adverse effects for us.

Signed-off-by: Carlo Costino <carlo.costino@gsa.gov>
2024-07-29 17:39:27 -04:00
Jonathan Bobel
9638067e63 Merge branch 'main' of https://github.com/GSA/notifications-admin into 1676-individual-job-table-updates
# Conflicts:
#	app/templates/partials/jobs/notifications.html
2024-07-29 15:55:59 -04:00
Jonathan Bobel
99493acb86 Updates to Job status page 2024-07-29 15:32:53 -04:00
Kenneth Kehl
d3de0bb01d try versioning-strategy 2024-07-29 12:20:22 -07:00
Steven Reilly
044175bac6 Merge pull request #1777 from GSA/1490-responsive-tables
1490 responsive tables
2024-07-29 14:58:58 -04:00
Steven Reilly
78564acea7 Merge pull request #1774 from GSA/notify-admin-1505
Improve logging in the user sign-in flow
2024-07-29 14:52:58 -04:00
Steven Reilly
cbfeca5a1f Merge pull request #1752 from GSA/1708-content-add-new-content-to-features-page-about-organizing-content
added organizing content to features page
2024-07-29 14:48:25 -04:00
Carlo Costino
5eb9bcc843 Merge pull request #1781 from GSA/jskinne3-upgrade-terraform-cloudgov-domain
Upgrade terraform cloudgov domain
2024-07-29 14:35:48 -04:00
Steven Reilly
447046697e Merge pull request #1773 from GSA/notify-admin-1766
redirect new users to templates section
2024-07-29 12:37:53 -04:00
Carlo Costino
84657a691f Merge pull request #1779 from GSA/jskinne3-network-policy
Uncomment network policy resource
2024-07-29 12:31:04 -04:00
Steven Reilly
4b71b2550a Merge pull request #1753 from GSA/1687-add-content-as-a-user-id-like-understand-what-each-permission-grants-before-i-invite-team-members
add accordion to invite page
2024-07-29 12:09:08 -04:00
John Skiles Skinner
4982452bec Remove the deprecated arg from the domain code 2024-07-26 11:28:03 -07:00
John Skiles Skinner
3833533b5c Nix a stray recursive_delete local var 2024-07-26 11:25:16 -07:00
John Skiles Skinner
e637a8177c Upgrade terraform-cloudgov in the block for our domain name 2024-07-26 11:18:26 -07:00
Kenneth Kehl
e884f31fa0 write test 2024-07-26 09:49:12 -07:00
Kenneth Kehl
34d727748b cleanup notify-compliance-46 2024-07-26 09:15:09 -07:00
Jonathan Bobel
765920ef0a Change to the table width on overflow to have less wrapping text 2024-07-26 10:38:00 -04:00
John Skiles Skinner
0c1f824ca3 Remove outdated comments 2024-07-25 19:21:20 -07:00
John Skiles Skinner
883b39cd93 Update comment 2024-07-25 18:59:23 -07:00
John Skiles Skinner
2933ded509 Experiment with uncommenting use of api_network_route module 2024-07-25 17:46:19 -07:00
Beverly Nguyen
8fb019b93d added margin 2024-07-25 17:36:13 -07:00
Beverly Nguyen
8461c6156b created testing for activity page 2024-07-25 17:09:17 -07:00
Kenneth Kehl
42184a17cd fix flake8 2024-07-25 12:38:33 -07:00
Jonathan Bobel
d68df54bb9 Merge branch 'main' of https://github.com/GSA/notifications-admin into 1490-responsive-tables 2024-07-25 15:36:39 -04:00
Jonathan Bobel
dfcd71a6d3 Updates to table wrappers across the application
Grid update to dashboard
Small style updates
2024-07-25 15:36:31 -04:00
Beverly Nguyen
b37b95cdcf Merge branch 'main' into 1677-user-story-view-table-of-all-jobs-sent-with-my-service 2024-07-25 11:59:53 -07:00
Beverly Nguyen
a8ce0be98b test_nav 2024-07-25 11:59:42 -07:00
Steven Reilly
bd03d273fe Merge pull request #1772 from GSA/tune-production-manifest
Adjust production manifest settings
2024-07-25 14:02:03 -04:00
Kenneth Kehl
e900a7c81f redirect new users to templates section 2024-07-25 09:10:25 -07:00
Carlo Costino
37c5f84ee1 Adjust production manifest settings
This changeset adjusts our production manifest settings to strike a balance with memory usage and app instances.

Signed-off-by: Carlo Costino <carlo.costino@gsa.gov>
2024-07-25 11:55:50 -04:00
Carlo Costino
6e398e7a39 Merge pull request #1768 from GSA/comma-escape-b
don't allow commas in placeholder fields
2024-07-25 09:54:51 -04:00
Carlo Costino
88435155a5 Merge pull request #1770 from GSA/clean-up-service_dashboard-as-it-relates-to-table-on-dashboard
Fixed KeyError on processing_finished and removed notification api call
2024-07-25 09:18:28 -04:00
Beverly Nguyen
68d58116c4 updated css 2024-07-25 00:30:31 -07:00
Beverly Nguyen
2daaa6dd38 Fixed KeyError on processing_finished and removed notification api call 2024-07-24 15:03:51 -07:00
Beverly Nguyen
4e558ca0e7 cleaned code and added css spacing 2024-07-24 14:29:05 -07:00
Kenneth Kehl
55f1939fd5 don't allow commas in placeholder fields 2024-07-24 13:56:32 -07:00
Beverly Nguyen
a4e1446791 Merge branch 'main' into 1677-user-story-view-table-of-all-jobs-sent-with-my-service 2024-07-24 13:44:40 -07:00
Carlo Costino
a7cbccc209 Merge pull request #1767 from GSA/jskinne3-hardcode-missing-recursive-delete-var
Hard-code recursive_delete to false
2024-07-24 15:36:28 -04:00
John Skiles Skinner
3c30a14c53 Hard-code recursive_delete to false 2024-07-24 12:26:41 -07:00
Carlo Costino
508dfc5d84 Merge pull request #1764 from GSA/revert-1762-signout-page-image-fix
Revert "fixed image" - swap image from svg back to png
2024-07-24 15:05:06 -04:00
Carlo Costino
1f9cccede6 Revert "fixed image" 2024-07-24 14:58:15 -04:00
Jonathan Bobel
c7b90e3010 Merge branch 'main' of https://github.com/GSA/notifications-admin into 1484-dashboard-visualizations 2024-07-24 10:47:54 -04:00
Kenneth Kehl
1bdb291397 debug 2024-07-24 07:13:34 -07:00
Beverly Nguyen
b8c1373dde add accordion to invite page 2024-07-23 12:57:02 -07:00
Jonathan Bobel
ad2e5de01b Removing service_id from chart, and small style update 2024-07-23 15:55:09 -04:00
Jonathan Bobel
5b1516bdd1 Another test update 2024-07-23 15:27:03 -04:00
Beverly Nguyen
34ae71716b added organizing content to features page 2024-07-23 12:16:09 -07:00
Beverly Nguyen
5d287d236e convert time to sort 2024-07-23 12:01:40 -07:00
Jonathan Bobel
3dcc438542 Adjusting some tests 2024-07-23 14:58:13 -04:00
Jonathan Bobel
a0e64304d6 Merge branch 'main' of https://github.com/GSA/notifications-admin into 1484-dashboard-visualizations 2024-07-23 11:54:00 -04:00
Jonathan Bobel
4af04ff183 Merge branch 'main' of https://github.com/GSA/notifications-admin into 1484-dashboard-visualizations
# Conflicts:
#	package-lock.json
#	package.json
2024-07-23 11:39:49 -04:00
Beverly Nguyen
febd037bed created new activity page 2024-07-18 15:57:29 -07:00
Jonathan Bobel
5cf05bd4b6 Missing semicolons 2024-07-18 16:14:25 -04:00
Jonathan Bobel
5f5f6a948b Fixing build errors 2024-07-18 15:59:27 -04:00
Jonathan Bobel
c80645b11c Changes to fix a build error and removing dummy Failed data 2024-07-18 15:52:27 -04:00
Jonathan Bobel
20ab840f27 removing console.logs 2024-07-18 15:42:32 -04:00
Jonathan Bobel
2029c64ea6 case issue was failing the build 2024-07-18 15:39:30 -04:00
Jonathan Bobel
b3de594741 Removing duplicate js 2024-07-18 15:30:16 -04:00
Jonathan Bobel
afd82f3742 Separated the js to make it easier to test 2024-07-18 15:27:17 -04:00
Jonathan Bobel
2e81f6f45a Checking if the threshold is why this branch is failing 2024-07-18 13:14:36 -04:00
Jonathan Bobel
47bb1e4c8c Test updates - still need more coverage 2024-07-18 12:36:49 -04:00
Jonathan Bobel
f0b4fbe732 Updating an optional dependency 2024-07-17 13:02:04 -04:00
Jonathan Bobel
4113a2819d Merged dashboard.html 2024-07-17 12:18:05 -04:00
Jonathan Bobel
b718fbf791 Merge branch 'main' of https://github.com/GSA/notifications-admin into 1484-dashboard-visualizations 2024-07-17 11:24:00 -04:00
Jonathan Bobel
ba2b5fea37 Merge branch '1544-data-viz-total-message-allowance' into 1484-dashboard-visualizations
# Conflicts:
#	app/assets/sass/uswds/_data-visualization.scss
#	app/assets/sass/uswds/styles.scss
#	app/templates/views/dashboard/dashboard.html
#	app/templates/views/dashboard/template-statistics.html
#	gulpfile.js
#	package-lock.json
2024-07-17 11:23:53 -04:00
Jonathan Bobel
d301502957 JS cleanrup
Table styles and updates
Removing chart.js
2024-07-11 12:59:14 -04:00
Andrew Shumway
8a3bdd181d Pull main 2024-07-10 08:36:49 -06:00
Jonathan Bobel
7f288598ba Deleting chart.js
Updates to the dashboard html
Test updates
2024-07-10 10:13:24 -04:00
Jonathan Bobel
e8f274baa2 Merge branch 'main' of https://github.com/GSA/notifications-admin into 1544-data-viz-total-message-allowance 2024-07-08 13:19:54 -04:00
Jonathan Bobel
2e20b20912 Updates to the test 2024-07-05 15:23:34 -04:00
Andrew Shumway
a81dc83133 pull main 2024-07-05 09:07:08 -06:00
Jonathan Bobel
0b2868a8d3 Merge branch 'main' of https://github.com/GSA/notifications-admin into 1544-data-viz-total-message-allowance
# Conflicts:
#	app/main/views/dashboard.py
#	package-lock.json
#	package.json
2024-07-04 13:25:03 -04:00
Jonathan Bobel
74f4d730a8 Merge branch 'main' of https://github.com/GSA/notifications-admin into 1484-dashboard-visualizations
# Conflicts:
#	app/main/views/dashboard.py
#	app/templates/views/dashboard/dashboard.html
#	package-lock.json
#	package.json
2024-07-04 13:07:35 -04:00
Jonathan Bobel
e5204ef7c5 Missing semicolon I guess? 2024-06-27 14:51:54 -04:00
Jonathan Bobel
731a79fff8 Passed the testing threshold 2024-06-27 14:12:53 -04:00
Jonathan Bobel
0a17aaad0d Merge branch 'main' of https://github.com/GSA/notifications-admin into 1484-dashboard-visualizations 2024-06-27 11:03:27 -04:00
Jonathan Bobel
149c23b721 Updates to the tests, committing as they currently run but I need to work on the coverage 2024-06-27 11:01:31 -04:00
Andrew Shumway
cc24604aa4 Pull main 2024-06-27 08:47:01 -06:00
Jonathan Bobel
30eac798c2 A few updates fixing the failed status 2024-06-21 16:59:04 -04:00
Jonathan Bobel
55fe08beca Merge branch 'main' of https://github.com/GSA/notifications-admin into 1484-dashboard-visualizations 2024-06-21 14:06:43 -04:00
Jonathan Bobel
24252ecd39 Merge branch 'main' of https://github.com/GSA/notifications-admin into 1484-dashboard-visualizations
# Conflicts:
#	package-lock.json
2024-06-20 11:19:25 -04:00
Jonathan Bobel
20cb8021b8 Update for a second pair of eyes 2024-06-20 11:16:46 -04:00
Jonathan Bobel
e06c855f8e Committing before branch switch 2024-06-18 14:46:31 -04:00
Jonathan Bobel
90ed600615 Seeing if this fixes the build error 2024-06-18 09:52:33 -04:00
Jonathan Bobel
6c44a297b8 Was failing because of package-lock mismatch 2024-06-18 09:35:03 -04:00
Jonathan Bobel
fc15ab6a9f Merge branch 'main' of https://github.com/GSA/notifications-admin into 1544-data-viz-total-message-allowance
# Conflicts:
#	.ds.baseline
#	package-lock.json
#	poetry.lock
2024-06-18 08:26:26 -04:00
Jonathan Bobel
edbbb7289f Responsive updates 2024-06-18 08:23:50 -04:00
Jonathan Bobel
c5a467c417 tooltip adjustments 2024-06-17 13:57:54 -04:00
Jonathan Bobel
cef5f921c2 Merge branch 'main' of https://github.com/GSA/notifications-admin into 1484-dashboard-visualizations 2024-06-17 09:29:14 -04:00
Jonathan Bobel
f979d41565 Mostly ready to combine with actual data 2024-06-17 09:29:08 -04:00
Andrew Shumway
ea8cfbdadb poetry lock 2024-06-13 10:43:46 -06:00
Jonathan Bobel
0f738c8cb7 Moving from a tabbed area to switching on account dropdown 2024-06-13 10:29:59 -04:00
Andrew Shumway
d1ed7e1d0c Pull main 2024-06-12 09:29:37 -06:00
Jonathan Bobel
468f6c9fc6 Merge branch 'main' of https://github.com/GSA/notifications-admin into 1484-dashboard-visualizations 2024-06-10 11:07:41 -04:00
Jonathan Bobel
9af16b443a Small styling change for the table scroll 2024-06-10 10:31:43 -04:00
Jonathan Bobel
abcbb0eb7a This was a testing experiment, no longer needed 2024-06-10 10:23:35 -04:00
Jonathan Bobel
9d22b99e9b Updates after merge 2024-06-10 10:20:33 -04:00
Jonathan Bobel
9b84f59bf8 Merge branch 'main' of https://github.com/GSA/notifications-admin into 1544-data-viz-total-message-allowance 2024-06-10 10:02:24 -04:00
Jonathan Bobel
b9beb50313 Merge branch 'main' of https://github.com/GSA/notifications-admin into 1544-data-viz-total-message-allowance
# Conflicts:
#	.ds.baseline
#	app/assets/javascripts/chartDashboard.js
#	app/main/views/dashboard.py
2024-06-07 16:51:32 -04:00
Jonathan Bobel
726e8db5b2 Merge remote-tracking branch 'origin' into 1484-dashboard-visualizations 2024-06-07 11:07:12 -04:00
Jonathan Bobel
b759f8a87f Adding js to node_modules instead of the head 2024-06-07 09:44:30 -04:00
Jonathan Bobel
ae56ea1482 Merge branch 'main' of https://github.com/GSA/notifications-admin into 1544-data-viz-total-message-allowance 2024-06-06 15:01:14 -04:00
Jonathan Bobel
1f84db7861 This is covered in the js test now 2024-06-05 12:08:52 -04:00
Jonathan Bobel
568400941b Merge branch 'main' of https://github.com/GSA/notifications-admin into 1544-data-viz-total-message-allowance
# Conflicts:
#	poetry.lock
2024-06-05 11:25:47 -04:00
Jonathan Bobel
c02c9152d8 Writing tests for chartDashboard.js 2024-06-05 11:25:15 -04:00
Jonathan Bobel
30b31d61b6 adding chart.js 2024-06-04 16:46:54 -04:00
Andrew Shumway
f9169fcbcb poetry lock 2024-06-03 15:55:37 -06:00
Jonathan Bobel
ae78e5868a Merge branch 'main' of https://github.com/GSA/notifications-admin into 1484-dashboard-visualizations 2024-06-03 15:52:04 -04:00
Jonathan Bobel
bbbaf592aa Commit before branch switch 2024-05-31 11:29:10 -04:00
Andrew Shumway
17b21b2382 Pull main 2024-05-31 09:27:42 -06:00
Jonathan Bobel
8331456318 id update 2024-05-30 10:22:58 -04:00
Jonathan Bobel
f6e7402be9 Merge branch 'main' of https://github.com/GSA/notifications-admin into 1484-dashboard-visualizations
# Conflicts:
#	app/templates/new/components/head.html
2024-05-29 15:04:22 -04:00
Jonathan Bobel
4b622fcfdc Accessibility updates 2024-05-29 14:53:27 -04:00
Jonathan Bobel
40d62e5ff9 Merge remote-tracking branch 'origin' into 1544-data-viz-total-message-allowance 2024-05-29 12:09:07 -04:00
Jonathan Bobel
2a13aba768 Merge branch 'main' of https://github.com/GSA/notifications-admin into 1544-data-viz-total-message-allowance 2024-05-28 16:44:34 -04:00
Jonathan Bobel
5f4804f65a Creating a mid-width for the sent messages area so it's visible if the number is lower than 1% 2024-05-24 13:34:05 -04:00
Jonathan Bobel
473eefb5b0 Tentatively finished up the tabbed area with weekly/monthly/yearly stacked bar charts with dummy data 2024-05-24 10:29:01 -04:00
Beverly Nguyen
04f0a173ac added sms_sent and sms_remaining data to the bar chart 2024-05-23 18:12:57 -07:00
Beverly Nguyen
de37442aa0 Merge branch 'main' into 1544-data-viz-total-message-allowance 2024-05-23 16:15:23 -07:00
Jonathan Bobel
e49ee06ffa 1544 - Data visualization for Total Message Allowance 2024-05-16 16:10:48 -04:00
Jonathan Bobel
c6a116c9bb Commit before branch switch 2024-05-16 11:38:22 -04:00
Jonathan Bobel
ccfc1166a7 commit before branch switch 2024-05-15 14:48:46 -04:00
Jonathan Bobel
dcc7679380 Adding chart.js 2024-05-15 14:48:18 -04:00
Jonathan Bobel
f517180581 Experimenting with chart.js 2024-05-14 15:45:33 -04:00
Jonathan Bobel
b9d41dad86 Merge branch 'main' of https://github.com/GSA/notifications-admin into 1484-dashboard-visualizations 2024-05-10 12:41:57 -04:00
Jonathan Bobel
04a7b57990 Merge branch 'main' of https://github.com/GSA/notifications-admin into 1484-dashboard-visualizations 2024-05-09 12:42:54 -04:00
Jonathan Bobel
afdc0ed58d 1484 - Dashboard visualizations 2024-05-03 11:10:41 -04:00
Kenneth Kehl
170527cfdf add API HOST NAME to workaround 2024-04-29 12:05:25 -07:00
Kenneth Kehl
7b570bfbed ugh 2024-04-29 11:35:33 -07:00
Kenneth Kehl
bc72288d4d workaround in conftest for pytest.ini overriding all our env variables 2024-04-29 11:06:25 -07:00
Kenneth Kehl
e50b4bb5f7 try overriding pytest.ini variables 2024-04-29 10:42:31 -07:00
Kenneth Kehl
32f1bb4f87 revert 2024-04-29 09:42:32 -07:00
Kenneth Kehl
594ad74006 remove ampersand 2024-04-29 09:31:58 -07:00
Kenneth Kehl
3b9664f220 more debug 2024-04-29 09:09:49 -07:00
Kenneth Kehl
9897385837 try switching env to development 2024-04-29 08:59:08 -07:00
Kenneth Kehl
4ec3bc5892 merge from main 2024-04-29 07:44:10 -07:00
Kenneth Kehl
358971164e fix manifest 2024-04-15 13:07:53 -07:00
Kenneth Kehl
282611ec9c more debug about state.json 2024-04-15 10:39:37 -07:00
Kenneth Kehl
4a6539197f more debug about state.json 2024-04-15 10:27:25 -07:00
Kenneth Kehl
e92fd011ca more debug about state.json 2024-04-15 10:22:31 -07:00
Kenneth Kehl
9e0fe69d2b more debug 2024-04-15 09:25:46 -07:00
Kenneth Kehl
0ce3d5033c more debug 2024-04-15 09:20:15 -07:00
Kenneth Kehl
ff110de97f more debug 2024-04-15 09:08:47 -07:00
Kenneth Kehl
d54c339994 more debug 2024-04-15 08:56:38 -07:00
Kenneth Kehl
3dc73ab92e more debug 2024-04-15 08:24:20 -07:00
Kenneth Kehl
42570416da debug in case auth state path doesn't exist 2024-04-15 07:33:27 -07:00
Kenneth Kehl
e934e49137 fix 2024-04-12 15:07:47 -07:00
Kenneth Kehl
cec1e4cff0 fix 2024-04-12 14:57:37 -07:00
Kenneth Kehl
31762239dd fix 2024-04-12 14:46:49 -07:00
Kenneth Kehl
017198754f point to staging for e2e 2024-04-12 14:29:39 -07:00
151 changed files with 5729 additions and 8001 deletions

View File

@@ -133,15 +133,7 @@
"filename": ".github/workflows/checks.yml",
"hashed_secret": "5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8",
"is_verified": false,
"line_number": 67,
"is_secret": false
},
{
"type": "Basic Auth Credentials",
"filename": ".github/workflows/checks.yml",
"hashed_secret": "5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8",
"is_verified": false,
"line_number": 101,
"line_number": 66,
"is_secret": false
}
],
@@ -169,7 +161,7 @@
"filename": "app/config.py",
"hashed_secret": "577a4c667e4af8682ca431857214b3a920883efc",
"is_verified": false,
"line_number": 117,
"line_number": 118,
"is_secret": false
}
],
@@ -407,23 +399,13 @@
"is_secret": false
}
],
"app/main/views/sign_in.py": [
{
"type": "Private Key",
"filename": "app/main/views/sign_in.py",
"hashed_secret": "1348b145fa1a555461c1b790a2f66614781091e9",
"is_verified": false,
"line_number": 27,
"is_secret": false
}
],
"app/templates/new/components/head.html": [
{
"type": "Base64 High Entropy String",
"filename": "app/templates/new/components/head.html",
"hashed_secret": "ee5048791fc7ff45a1545e24f85bec3317371327",
"is_verified": false,
"line_number": 34,
"line_number": 33,
"is_secret": false
}
],
@@ -545,7 +527,7 @@
"filename": "tests/app/main/views/test_accept_invite.py",
"hashed_secret": "07f0a6c13923fc3b5f0c57ffa2d29b715eb80d71",
"is_verified": false,
"line_number": 626,
"line_number": 643,
"is_secret": false
}
],
@@ -611,7 +593,7 @@
"filename": "tests/app/main/views/test_user_profile.py",
"hashed_secret": "8072d7aad32964ec43fbcb699c75dc38890792f7",
"is_verified": false,
"line_number": 350,
"line_number": 336,
"is_secret": false
},
{
@@ -619,7 +601,7 @@
"filename": "tests/app/main/views/test_user_profile.py",
"hashed_secret": "4c9dbb972da179e4f66f023eaa5fb9451d835030",
"is_verified": false,
"line_number": 351,
"line_number": 337,
"is_secret": false
}
],
@@ -702,5 +684,5 @@
}
]
},
"generated_at": "2024-07-11T16:37:23Z"
"generated_at": "2024-09-03T17:36:57Z"
}

View File

@@ -3,14 +3,22 @@
# Please see the documentation for all configuration options:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 3
version: 2
updates:
- package-ecosystem: "pip" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "daily"
- package-ecosystem: 'npm'
directory: '/'
assignees:
- "A-Shumway42"
reviewers:
- "A-Shumway42"
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: 'daily'
interval: "daily"
versioning-strategy: increase
assignees:
- "alexjanousekGSA"
reviewers:
- "alexjanousekGSA"

View File

@@ -44,8 +44,6 @@ jobs:
run: poetry run isort --check-only ./app ./tests
- name: Check dead code
run: make dead-code
- name: Run js lint
run: npm run lint
- name: Run js tests
run: npm test
- name: Run py tests with coverage
@@ -54,6 +52,7 @@ jobs:
run: poetry run coverage report --fail-under=90
end-to-end-tests:
if: ${{ github.actor != 'dependabot[bot]' }}
permissions:
checks: write
pull-requests: write
@@ -84,41 +83,25 @@ jobs:
ports:
# Maps tcp port 6379 on service container to the host
- 6379:6379
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup-project
- uses: jwalton/gh-find-current-pr@v1
id: findPr
- name: Clone API
uses: actions/checkout@v4
with:
repository: GSA/notifications-api
path: "notifications-api"
- name: Install API dependencies
working-directory: "notifications-api"
run: make bootstrap
env:
DATABASE_URL: postgresql://user:password@localhost:5432/test_notification_api
SQLALCHEMY_DATABASE_TEST_URI: postgresql://user:password@localhost:5432/test_notification_api
REDIS_URL: redis://localhost:6379
NOTIFY_E2E_TEST_EMAIL: ${{ secrets.NOTIFY_E2E_TEST_EMAIL }}
NOTIFY_E2E_TEST_PASSWORD: ${{ secrets.NOTIFY_E2E_TEST_PASSWORD }}
NOTIFY_ENVIRONMENT: development
- name: Run API server
working-directory: "notifications-api"
run: make run-procfile &
env:
DATABASE_URL: postgresql://user:password@localhost:5432/test_notification_api
SQLALCHEMY_DATABASE_TEST_URI: postgresql://user:password@localhost:5432/test_notification_api
REDIS_URL: redis://localhost:6379
NOTIFY_E2E_TEST_EMAIL: ${{ secrets.NOTIFY_E2E_TEST_EMAIL }}
NOTIFY_E2E_TEST_PASSWORD: ${{ secrets.NOTIFY_E2E_TEST_PASSWORD }}
NOTIFY_ENVIRONMENT: development
- name: Check API Server availability
run: |
curl --fail -v https://notify-api-staging.app.cloud.gov || exit 1
- name: Run Admin server
run: make run-flask &
# If we want to log stuff and see what's broken,
# insert this line:
# tail -f admin-server.log &
# above make e2e-test
run: |
make run-flask > admin-server.log 2>&1 &
make e2e-test
env:
# API_HOST_NAME: https://notify-api-staging.app.cloud.gov
API_HOST_NAME: http://localhost:6011
API_HOST_NAME: https://notify-api-staging.app.cloud.gov/
DANGEROUS_SALT: ${{ secrets.DANGEROUS_SALT }}
SECRET_KEY: ${{ secrets.SECRET_KEY }}
ADMIN_CLIENT_SECRET: ${{ secrets.ADMIN_CLIENT_SECRET }}
@@ -127,20 +110,7 @@ jobs:
NOTIFY_E2E_AUTH_STATE_PATH: ${{ secrets.NOTIFY_E2E_AUTH_STATE_PATH }}
NOTIFY_E2E_TEST_EMAIL: ${{ secrets.NOTIFY_E2E_TEST_EMAIL }}
NOTIFY_E2E_TEST_PASSWORD: ${{ secrets.NOTIFY_E2E_TEST_PASSWORD }}
NOTIFY_E2E_TEST_URI: http://localhost:6012
- name: Run E2E tests
# Run the E2E tests against the code found in this PR.
# run: poetry run pytest -v --browser chromium --browser firefox --browser webkit tests/end_to_end
# --browser webkit doesn't work at this time.
run: make e2e-test
# Debugging for now to troubleshoot a connectivity issue to the local servers
# run: curl --request GET --url "http://localhost:6012"
env:
NOTIFY_ENVIRONMENT: e2etest
NOTIFY_E2E_AUTH_STATE_PATH: ${{ secrets.NOTIFY_E2E_AUTH_STATE_PATH }}
NOTIFY_E2E_TEST_EMAIL: ${{ secrets.NOTIFY_E2E_TEST_EMAIL }}
NOTIFY_E2E_TEST_PASSWORD: ${{ secrets.NOTIFY_E2E_TEST_PASSWORD }}
NOTIFY_E2E_TEST_URI: http://localhost:6012
NOTIFY_E2E_TEST_URI: http://localhost:6012/
validate-new-relic-config:
runs-on: ubuntu-latest

View File

@@ -63,6 +63,7 @@ jobs:
LOGIN_DOT_GOV_BASE_LOGOUT_URL: "https://secure.login.gov/openid_connect/logout?"
LOGIN_DOT_GOV_SIGNOUT_REDIRECT: "https://notify-demo.app.cloud.gov/sign-out"
LOGIN_DOT_GOV_INITIAL_SIGNIN_URL: "https://secure.login.gov/openid_connect/authorize?acr_values=http%3A%2F%2Fidmanagement.gov%2Fns%2Fassurance%2Fial%2F1&client_id=urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:notify-gov&nonce=NONCE&prompt=select_account&redirect_uri=https://notify-demo.app.cloud.gov/sign-in&response_type=code&scope=openid+email&state=STATE"
LOGIN_DOT_GOV_CERTS_URL: "https://secure.login.gov/api/openid_connect/certs"
with:
cf_username: ${{ secrets.CLOUDGOV_USERNAME }}
cf_password: ${{ secrets.CLOUDGOV_PASSWORD }}
@@ -85,6 +86,7 @@ jobs:
--var LOGIN_DOT_GOV_BASE_LOGOUT_URL="$LOGIN_DOT_GOV_BASE_LOGOUT_URL"
--var LOGIN_DOT_GOV_SIGNOUT_REDIRECT="$LOGIN_DOT_GOV_SIGNOUT_REDIRECT"
--var LOGIN_DOT_GOV_INITIAL_SIGNIN_URL="$LOGIN_DOT_GOV_INITIAL_SIGNIN_URL"
--var LOGIN_DOT_GOV_CERTS_URL="$LOGIN_DOT_GOV_CERTS_URL"
- name: Check for changes to egress config
id: changed-egress-config

View File

@@ -63,6 +63,7 @@ jobs:
LOGIN_DOT_GOV_BASE_LOGOUT_URL: "https://secure.login.gov/openid_connect/logout?"
LOGIN_DOT_GOV_SIGNOUT_REDIRECT: "https://beta.notify.gov/sign-out"
LOGIN_DOT_GOV_INITIAL_SIGNIN_URL: "https://secure.login.gov/openid_connect/authorize?acr_values=http%3A%2F%2Fidmanagement.gov%2Fns%2Fassurance%2Fial%2F1&client_id=urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:notify-gov&nonce=NONCE&prompt=select_account&redirect_uri=https://beta.notify.gov/sign-in&response_type=code&scope=openid+email&state=STATE"
LOGIN_DOT_GOV_CERTS_URL: "https://secure.login.gov/api/openid_connect/certs"
with:
cf_username: ${{ secrets.CLOUDGOV_USERNAME }}
cf_password: ${{ secrets.CLOUDGOV_PASSWORD }}
@@ -85,6 +86,7 @@ jobs:
--var LOGIN_DOT_GOV_BASE_LOGOUT_URL="$LOGIN_DOT_GOV_BASE_LOGOUT_URL"
--var LOGIN_DOT_GOV_SIGNOUT_REDIRECT="$LOGIN_DOT_GOV_SIGNOUT_REDIRECT"
--var LOGIN_DOT_GOV_INITIAL_SIGNIN_URL="$LOGIN_DOT_GOV_INITIAL_SIGNIN_URL"
--var LOGIN_DOT_GOV_CERTS_URL="$LOGIN_DOT_GOV_CERTS_URL"
- name: Check for changes to egress config
id: changed-egress-config

View File

@@ -69,6 +69,7 @@ jobs:
LOGIN_DOT_GOV_BASE_LOGOUT_URL: "https://secure.login.gov/openid_connect/logout?"
LOGIN_DOT_GOV_SIGNOUT_REDIRECT: "https://notify-staging.app.cloud.gov/sign-out"
LOGIN_DOT_GOV_INITIAL_SIGNIN_URL: "https://secure.login.gov/openid_connect/authorize?acr_values=http%3A%2F%2Fidmanagement.gov%2Fns%2Fassurance%2Fial%2F1&client_id=urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:notify-gov&nonce=NONCE&prompt=select_account&redirect_uri=https://notify-staging.app.cloud.gov/sign-in&response_type=code&scope=openid+email&state=STATEE"
LOGIN_DOT_GOV_CERTS_URL: "https://secure.login.gov/api/openid_connect/certs"
with:
cf_username: ${{ secrets.CLOUDGOV_USERNAME }}
cf_password: ${{ secrets.CLOUDGOV_PASSWORD }}
@@ -91,6 +92,7 @@ jobs:
--var LOGIN_DOT_GOV_BASE_LOGOUT_URL="$LOGIN_DOT_GOV_BASE_LOGOUT_URL"
--var LOGIN_DOT_GOV_SIGNOUT_REDIRECT="$LOGIN_DOT_GOV_SIGNOUT_REDIRECT"
--var LOGIN_DOT_GOV_INITIAL_SIGNIN_URL="$LOGIN_DOT_GOV_INITIAL_SIGNIN_URL"
--var LOGIN_DOT_GOV_CERTS_URL="$LOGIN_DOT_GOV_CERTS_URL"
- name: Check for changes to egress config

View File

@@ -88,7 +88,7 @@ dead-code: ## 60% is our aspirational goal, but currently breaks the build
.PHONY: e2e-test
e2e-test: export NEW_RELIC_ENVIRONMENT=test
e2e-test: ## Run end-to-end integration tests; note that --browser webkit isn't currently working
poetry run pytest -vv --browser chromium --browser firefox tests/end_to_end
DEBUG=pw:api,pw:browser poetry run pytest -vv --browser chromium --browser firefox tests/end_to_end
.PHONY: js-lint
js-lint: ## Run javascript linting scanners

View File

@@ -18,7 +18,6 @@ from flask import (
)
from flask.globals import request_ctx
from flask_login import LoginManager, current_user
from flask_socketio import SocketIO
from flask_talisman import Talisman
from flask_wtf import CSRFProtect
from flask_wtf.csrf import CSRFError
@@ -119,8 +118,6 @@ from notifications_utils.recipients import format_phone_number_human_readable
login_manager = LoginManager()
csrf = CSRFProtect()
talisman = Talisman()
socketio = SocketIO()
# The current service attached to the request stack.
current_service = LocalProxy(partial(getattr, request_ctx, "service"))
@@ -177,7 +174,6 @@ def create_app(application):
init_govuk_frontend(application)
init_jinja(application)
socketio.init_app(application)
for client in (
csrf,

View File

@@ -17,7 +17,7 @@
href="/static/images/apple-touch-icon.png?a0f7e1b728a42016b247dc54ee40d055">
<link rel="icon" type="image/png" sizes="32x32" href="/static/images/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/static/images/favicon-16x16.png">
<link rel="manifest" href="/static/images/site.webmanifest">
<link rel="manifest" href="{{ asset_url('images/site.webmanifest') }}">
<link rel="stylesheet" media="screen" href="/static/stylesheets/main.css?d077f86473501ab244de0b30600536ee" />
<link rel="stylesheet" media="print" href="/static/stylesheets/print.css?28010888ca5719dc83d7c2c80f6ed2b2" />

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 13.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 14948) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="719px" height="720px" viewBox="0 0 129.996 130.238" enable-background="new 0 0 129.996 130.238"
xml:space="preserve">
<rect fill="#00538E" width="129.996" height="130.238"/>
<path fill="#FFFFFF" d="M78.611,96.076c0,9.138-7.175,12.953-15.451,12.688c-2.938-0.095-5.799-0.838-8.916-1.855l0.707-6.578
c2.638,1.086,6.057,2.263,8.92,2.219c3.74-0.058,7.477-1.794,7.477-6.124c0-3.335-2.67-4.976-5.252-6.283
c-5.709-2.896-12.275-4.907-12.275-12.998c0-7.65,6.084-11.64,13.625-11.64c2.741,0,6.014,0.477,9.299,1.641l-0.985,6.478
c-3.577-1.403-5.14-1.901-8.078-1.901c-3.253,0-6.597,1.143-6.597,4.896c0,2.933,3.01,4.456,5.248,5.573
C72.398,85.218,78.611,87.703,78.611,96.076"/>
<path fill="#FFFFFF" d="M44.561,106.8c-2.632,0.901-6.662,1.964-13.092,1.964c-13.195,0-22.027-8.529-22.027-21.805
c0-12.882,9.375-21.455,22.086-21.455c6.036,0,8.727,0.896,12.541,2.212l-0.574,7.216c-3.51-2.294-6.498-3.211-11.674-3.211
c-9.09,0-15.158,6.578-15.115,15.412c0.043,9.506,6.648,15.416,14.822,15.416c2.437,0,4.508-0.178,5.996-0.569V91.15h-7.861v-6.103
h14.898V106.8"/>
<polyline fill="#FFFFFF" points="102.041,64.587 112.063,86.918 105.08,86.918 102.041,79.497 98.996,86.918 92.014,86.918
102.041,64.587 "/>
<polyline fill="#FFFFFF" points="112.178,87.179 121.507,108.029 114.176,108.029 107.203,91.839 112.178,87.179 "/>
<polyline fill="#FFFFFF" points="91.9,87.183 96.873,91.841 93.199,100.24 102.027,95.611 107.109,99.525 89.727,108.031
82.57,108.031 91.9,87.183 "/>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -1,712 +0,0 @@
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="100%" viewBox="0 0 1129 1483" enable-background="new 0 0 1129 1483" xml:space="preserve">
<path fill="#000000" opacity="1.000000" stroke="none"
d="
M539.000000,1484.000000
C359.711578,1484.000000 180.423172,1484.000000 1.000000,1484.000000
C1.000000,1105.639771 1.000000,727.279602 1.410214,348.316467
C4.569818,348.140381 7.319019,348.939209 10.068629,348.940613
C131.539047,349.002533 253.009537,349.019958 374.479858,348.902496
C379.495209,348.897644 382.964447,350.180725 385.438477,354.685120
C387.083405,357.679993 389.360779,360.359558 391.555206,363.014832
C400.272888,373.563141 409.100433,384.020660 417.827881,394.560944
C426.754944,405.342346 435.596100,416.194855 444.505066,426.991333
C453.315704,437.668549 462.350189,448.169037 470.874084,459.069550
C473.211273,462.058380 475.868622,462.668365 478.552521,461.668732
C479.870514,461.177765 480.891022,458.075714 480.899139,456.150421
C481.039185,422.992401 480.992554,389.833649 481.006348,356.675049
C481.009247,349.746185 481.792328,348.996246 488.738434,348.995605
C524.063171,348.992462 559.387878,348.997498 594.712646,348.991364
C602.408508,348.990021 602.999207,348.399902 602.999329,340.867279
C603.000366,227.578186 603.000000,114.289093 603.000000,1.000000
C778.587646,1.000000 954.175293,1.000000 1130.000000,1.000000
C1130.000000,136.026779 1130.000000,271.053680 1129.661621,406.458130
C1128.681885,406.105896 1127.790039,405.480469 1127.435913,404.630920
C1120.489746,387.961334 1107.422241,381.006897 1090.001465,381.005951
C910.382629,380.996246 730.763794,380.970703 551.145081,381.139465
C545.655090,381.144623 539.914307,382.532288 534.729980,384.458374
C523.458130,388.646179 512.165527,404.262543 512.742798,416.804962
C513.691650,437.422455 513.000000,458.115448 513.000000,478.776611
C513.000000,802.357117 512.996948,1125.937622 513.047546,1449.518188
C513.047974,1452.454102 513.316040,1455.538330 514.227844,1458.299561
C517.778442,1469.052002 525.487427,1476.064087 535.397400,1481.004028
C536.757935,1481.682251 537.806824,1482.985840 539.000000,1484.000000
z"/>
<path fill="#005EA2" opacity="1.000000" stroke="none"
d="
M602.531372,1.000000
C603.000000,114.289093 603.000366,227.578186 602.999329,340.867279
C602.999207,348.399902 602.408508,348.990021 594.712646,348.991364
C559.387878,348.997498 524.063171,348.992462 488.738434,348.995605
C481.792328,348.996246 481.009247,349.746185 481.006348,356.675049
C480.992554,389.833649 481.039185,422.992401 480.899139,456.150421
C480.891022,458.075714 479.870514,461.177765 478.552521,461.668732
C475.868622,462.668365 473.211273,462.058380 470.874084,459.069550
C462.350189,448.169037 453.315704,437.668549 444.505066,426.991333
C435.596100,416.194855 426.754944,405.342346 417.827881,394.560944
C409.100433,384.020660 400.272888,373.563141 391.555206,363.014832
C389.360779,360.359558 387.083405,357.679993 385.438477,354.685120
C382.964447,350.180725 379.495209,348.897644 374.479858,348.902496
C253.009537,349.019958 131.539047,349.002533 10.068629,348.940613
C7.319019,348.939209 4.569818,348.140381 1.410214,347.856750
C1.000000,232.386658 1.000000,116.773323 1.000000,1.000000
C201.354202,1.000000 401.708435,1.000000 602.531372,1.000000
z"/>
<path fill="#1B1B1B" opacity="1.000000" stroke="none"
d="
M539.468689,1484.000000
C537.806824,1482.985840 536.757935,1481.682251 535.397400,1481.004028
C525.487427,1476.064087 517.778442,1469.052002 514.227844,1458.299561
C513.316040,1455.538330 513.047974,1452.454102 513.047546,1449.518188
C512.996948,1125.937622 513.000000,802.357117 513.000000,478.776611
C513.000000,458.115448 513.691650,437.422455 512.742798,416.804962
C512.165527,404.262543 523.458130,388.646179 534.729980,384.458374
C539.914307,382.532288 545.655090,381.144623 551.145081,381.139465
C730.763794,380.970703 910.382629,380.996246 1090.001465,381.005951
C1107.422241,381.006897 1120.489746,387.961334 1127.435913,404.630920
C1127.790039,405.480469 1128.681885,406.105896 1129.661621,406.917847
C1130.000000,756.687561 1130.000000,1106.375122 1129.663086,1456.612671
C1128.761597,1457.741333 1127.970459,1458.220581 1127.666382,1458.913574
C1123.083496,1469.360596 1116.098755,1477.361450 1105.191040,1481.552490
C1104.281616,1481.901978 1103.722046,1483.162598 1103.000000,1484.000000
C915.312439,1484.000000 727.624878,1484.000000 539.468689,1484.000000
M715.500000,1342.999756
C840.560608,1342.999756 965.621216,1342.999756 1090.580322,1342.999756
C1090.580322,1053.180908 1090.580322,764.298523 1090.580322,475.379303
C910.605164,475.379303 731.049866,475.379303 551.330566,475.379303
C551.330566,764.641113 551.330566,1053.523560 551.330566,1342.999756
C605.939392,1342.999756 660.219727,1342.999756 715.500000,1342.999756
M848.995056,1439.501953
C861.071411,1426.778076 864.464417,1411.987305 857.870850,1395.782471
C851.682190,1380.572754 839.614075,1372.216919 823.238037,1371.120239
C804.724487,1369.880737 788.393555,1381.497681 782.851562,1399.040405
C777.381104,1416.357056 784.187378,1434.929688 799.618713,1444.794189
C814.984253,1454.616455 834.419617,1452.711060 848.995056,1439.501953
M827.500000,439.001709
C837.987183,439.001678 848.474426,439.019501 858.961548,438.995575
C866.404968,438.978607 871.095825,434.951721 870.987549,428.742432
C870.884216,422.809875 866.245544,419.012268 858.947205,419.006348
C833.644836,418.985809 808.342407,418.987823 783.040039,419.004883
C775.608643,419.009918 770.903564,423.058716 771.012451,429.256409
C771.116821,435.194092 775.748901,438.980286 783.053101,438.993744
C797.535339,439.020386 812.017700,439.001678 827.500000,439.001709
M725.402283,419.003754
C716.655090,419.947510 712.145569,427.803436 716.640198,434.366302
C719.365906,438.346191 723.255737,439.998871 727.861145,438.552002
C732.381165,437.131927 734.912048,433.752899 734.979553,428.883942
C735.046692,424.037842 732.110413,420.836304 725.402283,419.003754
z"/>
<path fill="#000000" opacity="1.000000" stroke="none"
d="
M1103.468628,1484.000000
C1103.722046,1483.162598 1104.281616,1481.901978 1105.191040,1481.552490
C1116.098755,1477.361450 1123.083496,1469.360596 1127.666382,1458.913574
C1127.970459,1458.220581 1128.761597,1457.741333 1129.663086,1457.081299
C1130.000000,1465.926392 1130.000000,1474.852905 1130.000000,1484.000000
C1121.313721,1484.000000 1112.625488,1484.000000 1103.468628,1484.000000
z"/>
<path fill="#005EA2" opacity="1.000000" stroke="none"
d="
M715.000000,1342.999756
C660.219727,1342.999756 605.939392,1342.999756 551.330566,1342.999756
C551.330566,1053.523560 551.330566,764.641113 551.330566,475.379303
C731.049866,475.379303 910.605164,475.379303 1090.580322,475.379303
C1090.580322,764.298523 1090.580322,1053.180908 1090.580322,1342.999756
C965.621216,1342.999756 840.560608,1342.999756 715.000000,1342.999756
M757.917419,532.016174
C757.710449,531.842957 757.503540,531.669800 757.225159,530.601135
C756.969604,527.453918 758.218384,526.051575 761.558044,526.301270
C763.955505,526.480469 766.398010,526.057617 768.820374,525.903442
C768.792847,525.411682 768.765259,524.919922 768.737732,524.428162
C764.196594,524.428162 759.655518,524.428162 755.034241,524.428162
C754.709106,527.381104 754.389282,530.187317 754.093750,532.996155
C753.744568,536.314331 754.919922,537.421631 758.021118,535.657959
C761.182068,533.860352 764.437500,533.753479 766.141663,537.173096
C766.929382,538.753906 766.444580,542.116272 765.214417,543.323975
C762.385193,546.101440 759.204468,545.085571 756.479553,542.327881
C755.735840,541.575317 754.459167,541.349365 752.383667,540.408936
C755.843872,546.609497 760.206726,548.611938 765.034119,546.895874
C769.052124,545.467529 770.526672,542.303223 770.059326,538.198303
C769.479248,533.103760 765.369141,531.126282 757.917419,532.016174
M716.917419,532.016174
C716.710449,531.842957 716.503540,531.669800 716.225159,530.601135
C715.969604,527.453918 717.218384,526.051575 720.558044,526.301270
C722.955505,526.480469 725.398010,526.057617 727.820374,525.903442
C727.792847,525.411682 727.765259,524.919922 727.737732,524.428162
C723.196594,524.428162 718.655518,524.428162 714.034180,524.428162
C713.709106,527.381104 713.389282,530.187317 713.093689,532.996155
C712.744568,536.314331 713.919922,537.421631 717.021118,535.657959
C720.182068,533.860352 723.437500,533.753479 725.141663,537.173096
C725.929443,538.753906 725.444580,542.116272 724.214417,543.323975
C721.385193,546.101440 718.204468,545.085571 715.479553,542.327942
C714.735840,541.575317 713.459167,541.349426 711.383728,540.408997
C714.843872,546.609497 719.206787,548.612000 724.034119,546.895996
C728.052124,545.467590 729.526733,542.303223 729.059387,538.198303
C728.479309,533.103760 724.369141,531.126282 716.917419,532.016174
M1058.999390,737.500000
C1058.999390,697.505127 1058.999512,657.510193 1058.999268,617.515320
C1058.999268,605.743347 1056.299927,603.000366 1044.711548,603.000366
C895.564270,603.000061 746.416931,603.000061 597.269653,603.000305
C585.816956,603.000305 583.000000,605.768921 582.999939,617.030762
C582.999451,736.348633 582.999512,855.666443 582.999878,974.984314
C582.999939,986.054382 585.894104,988.999268 596.785095,988.999329
C746.265686,989.000183 895.746216,989.000061 1045.226807,988.999451
C1056.137695,988.999451 1058.998901,986.083923 1058.998901,974.969299
C1058.999512,896.146179 1058.999390,817.323120 1058.999390,737.500000
M838.417786,528.949585
C839.083923,531.005493 839.750061,533.061462 840.461304,535.256653
C836.744202,539.386841 836.324524,542.075928 839.096497,544.925293
C842.052917,547.964355 849.674805,548.159302 852.937805,545.279297
C856.009766,542.567871 855.691833,539.842896 851.688904,534.575378
C855.016052,530.149902 854.543030,526.284058 850.432739,524.310608
C846.111816,522.236023 841.257324,523.850464 838.417786,528.949585
M745.891296,542.169128
C741.158203,548.675415 737.081299,541.854736 732.290100,540.637451
C734.869629,546.392883 739.339478,548.482056 744.370422,546.980408
C748.078552,545.873596 750.549316,541.850952 749.525513,537.959473
C749.089172,536.301086 748.107056,533.978394 746.814453,533.514832
C743.774109,532.424500 740.397705,532.271240 736.826416,531.684448
C736.926086,530.342651 737.066406,528.453491 737.205750,526.577209
C741.252563,526.391479 744.746399,526.231140 748.240173,526.070740
C748.184937,525.482117 748.129761,524.893555 748.074524,524.304932
C743.625916,524.304932 739.177307,524.304932 734.787659,524.304932
C734.573547,524.763489 734.342285,525.046631 734.316650,525.347290
C734.105042,527.827087 734.009277,530.318909 733.728027,532.790039
C733.313477,536.432983 734.160034,537.418396 737.825989,535.580933
C743.170105,532.902283 746.194824,535.156616 745.891296,542.169128
M788.188293,525.759277
C784.717834,530.639099 784.656189,536.005798 786.150879,541.487305
C787.114197,545.020081 789.453247,547.159302 793.298828,547.331177
C797.185181,547.504883 799.986816,545.518921 800.774292,542.027039
C801.591003,538.405762 801.956604,534.410889 801.318115,530.791321
C800.091431,523.837769 795.222107,521.941406 788.188293,525.759277
M834.572815,535.487549
C834.175781,533.219666 834.462341,530.423096 833.216309,528.811829
C831.489075,526.578247 828.606445,523.883728 826.225281,523.890198
C823.831055,523.896729 821.012573,526.617676 819.209534,528.826233
C818.030273,530.270691 818.069946,532.913330 818.009949,535.031982
C817.791504,542.747192 820.623962,547.034851 825.870239,547.300293
C831.111328,547.565491 833.492676,544.553772 834.572815,535.487549
M652.416321,544.217712
C650.345642,544.217712 648.274963,544.217712 646.204285,544.217712
C646.838684,541.790405 647.850464,540.372437 649.179138,539.377747
C651.032532,537.990356 653.289185,537.123291 655.073914,535.666748
C657.983093,533.292542 658.921265,530.116333 656.902344,526.778687
C654.763794,523.243286 651.190308,523.025208 647.608765,523.892029
C644.105835,524.739807 641.985535,526.860413 642.362366,532.300232
C644.056702,530.415955 644.736145,529.185913 645.777466,528.608765
C647.529846,527.637512 649.488220,526.639526 651.407715,526.535706
C652.368835,526.483582 654.366333,528.494934 654.258118,529.370850
C654.041260,531.125488 652.986816,533.012512 651.738647,534.336182
C650.537842,535.609619 648.631958,536.201843 647.078308,537.162109
C643.543457,539.346802 641.603027,542.293274 642.488464,546.431335
C647.810059,546.431335 652.869995,546.431335 657.929993,546.431335
C657.979187,545.915588 658.028320,545.399841 658.077515,544.884094
C656.491455,544.661987 654.905396,544.439941 652.416321,544.217712
M665.208252,524.673828
C659.711121,529.704834 660.278503,535.904724 662.117737,542.152100
C663.162598,545.701355 666.081421,548.122620 669.655273,547.114319
C672.280884,546.373535 675.324280,544.121033 676.420227,541.733643
C677.700012,538.945862 677.519348,535.229553 677.167480,532.003906
C676.408447,525.045532 672.824646,522.786072 665.208252,524.673828
M695.703430,526.824768
C692.833130,522.926819 688.881653,522.862183 684.933228,524.308411
C682.220398,525.302063 680.293274,527.395935 681.342651,532.130493
C682.861084,530.217041 683.638184,529.015991 684.630371,528.031738
C686.979187,525.701782 690.038818,525.102783 691.995117,527.725464
C692.869385,528.897522 691.875488,532.247437 690.726807,533.920532
C689.607971,535.549927 687.186890,536.266479 685.376343,537.443420
C682.032959,539.616821 680.202637,542.473877 681.275574,546.447998
C686.507141,546.447998 691.585876,546.447998 696.664612,546.447998
C696.714172,545.888977 696.763733,545.330017 696.813293,544.770996
C692.817261,544.451782 688.821289,544.132629 684.825256,543.813416
C684.609619,543.347107 684.393982,542.880859 684.178345,542.414551
C686.441528,540.805054 688.587158,538.986511 690.994141,537.632751
C695.067871,535.341797 697.437561,532.409607 695.703430,526.824768
M609.871765,543.254517
C612.920166,547.243774 616.962830,548.425964 620.513733,546.366577
C623.687622,544.525879 625.242493,539.522339 623.739685,535.243408
C622.335327,531.244812 619.265869,529.413025 615.161682,530.213440
C610.440857,531.134216 608.941650,534.718079 609.006042,539.121460
C609.022644,540.259460 609.365845,541.392761 609.871765,543.254517
M598.632568,523.897034
C596.280212,523.897034 593.927795,523.897034 591.575439,523.897034
C591.513794,524.568726 591.452087,525.240417 591.390442,525.912109
C593.818359,526.360107 596.246277,526.808167 598.972595,527.311218
C598.972595,532.780090 598.878113,538.252136 599.051880,543.715759
C599.086853,544.816223 600.139099,545.884399 600.722351,546.967468
C601.157043,545.901306 601.943298,544.843018 601.967590,543.767578
C602.093994,538.164673 602.026550,532.557373 602.026550,527.238770
C604.818420,526.726013 606.992249,526.326721 609.166077,525.927429
C609.066711,525.250671 608.967407,524.573853 608.868042,523.897095
C605.769836,523.897095 602.671631,523.897095 598.632568,523.897034
M809.033569,530.003784
C809.069824,534.804382 809.030823,539.607727 809.209595,544.403015
C809.242981,545.298035 810.150146,546.160522 810.653992,547.037964
C811.315491,546.497864 812.542786,545.968018 812.552734,545.416138
C812.682129,538.262817 812.638428,531.106323 812.638428,523.351135
C809.349365,525.146240 807.111572,526.367615 803.909729,528.115112
C806.222290,528.648987 807.433777,528.928711 809.033569,530.003784
z"/>
<path fill="#A8ADB0" opacity="1.000000" stroke="none"
d="
M848.743774,1439.752197
C834.419617,1452.711060 814.984253,1454.616455 799.618713,1444.794189
C784.187378,1434.929688 777.381104,1416.357056 782.851562,1399.040405
C788.393555,1381.497681 804.724487,1369.880737 823.238037,1371.120239
C839.614075,1372.216919 851.682190,1380.572754 857.870850,1395.782471
C864.464417,1411.987305 861.071411,1426.778076 848.743774,1439.752197
z"/>
<path fill="#A8ADB0" opacity="1.000000" stroke="none"
d="
M827.000000,439.001709
C812.017700,439.001678 797.535339,439.020386 783.053101,438.993744
C775.748901,438.980286 771.116821,435.194092 771.012451,429.256409
C770.903564,423.058716 775.608643,419.009918 783.040039,419.004883
C808.342407,418.987823 833.644836,418.985809 858.947205,419.006348
C866.245544,419.012268 870.884216,422.809875 870.987549,428.742432
C871.095825,434.951721 866.404968,438.978607 858.961548,438.995575
C848.474426,439.019501 837.987183,439.001678 827.000000,439.001709
z"/>
<path fill="#A5AAAD" opacity="1.000000" stroke="none"
d="
M725.800537,419.037415
C732.110413,420.836304 735.046692,424.037842 734.979553,428.883942
C734.912048,433.752899 732.381165,437.131927 727.861145,438.552002
C723.255737,439.998871 719.365906,438.346191 716.640198,434.366302
C712.145569,427.803436 716.655090,419.947510 725.800537,419.037415
z"/>
<path fill="#FFFFFF" opacity="1.000000" stroke="none"
d="
M1058.999390,738.000000
C1058.999390,817.323120 1058.999512,896.146179 1058.998901,974.969299
C1058.998901,986.083923 1056.137695,988.999451 1045.226807,988.999451
C895.746216,989.000061 746.265686,989.000183 596.785095,988.999329
C585.894104,988.999268 582.999939,986.054382 582.999878,974.984314
C582.999512,855.666443 582.999451,736.348633 582.999939,617.030762
C583.000000,605.768921 585.816956,603.000305 597.269653,603.000305
C746.416931,603.000061 895.564270,603.000061 1044.711548,603.000366
C1056.299927,603.000366 1058.999268,605.743347 1058.999268,617.515320
C1058.999512,657.510193 1058.999390,697.505127 1058.999390,738.000000
M624.634521,807.000000
C615.001221,807.007446 615.001099,807.007446 615.000061,816.777527
C614.999695,819.943726 614.998230,823.109863 615.000366,826.276062
C615.005981,834.757751 615.240906,834.999023 623.686462,834.999146
C737.835449,835.000488 851.984436,835.000000 966.133423,834.999634
C967.299866,834.999634 968.467224,834.963196 969.632629,834.996582
C973.453491,835.105896 975.055786,833.147644 975.007446,829.455505
C974.948547,824.957031 975.000732,820.457092 974.999451,815.957825
C974.996887,807.148193 974.851746,807.000610 966.046509,807.000549
C852.564087,806.999634 739.081665,807.000000 624.634521,807.000000
M615.000000,768.513550
C615.000000,769.846680 614.999939,771.179749 615.000000,772.512878
C615.000366,782.999634 615.000366,782.999939 625.422424,782.999939
C738.569519,783.000000 851.716614,783.000000 964.863708,782.999756
C966.363464,782.999756 967.865662,782.939331 969.362427,783.004944
C973.425171,783.182983 975.099243,781.165955 975.017639,777.225525
C974.924561,772.728271 975.001587,768.227478 974.999268,763.728271
C974.994873,755.243896 974.757751,755.000977 966.317139,755.000854
C853.336670,754.999512 740.356262,755.007507 627.375793,754.989807
C614.303772,754.987732 614.895813,754.115295 615.000000,768.513550
M972.647156,703.511108
C970.337830,703.340759 968.028564,703.021851 965.719177,703.021362
C851.890991,702.997009 738.062744,702.999939 624.234558,703.000366
C622.901306,703.000427 621.567627,703.030029 620.234863,703.006165
C616.713013,702.943176 615.004211,704.679993 615.009033,708.179688
C615.015320,712.679443 615.000549,717.179260 615.000732,721.679016
C615.001160,730.936829 615.060547,730.999451 624.091064,730.999512
C738.085938,731.000305 852.080811,731.000000 966.075684,730.999634
C967.242310,730.999634 968.409973,730.960449 969.575317,730.997681
C973.368164,731.118652 975.041687,729.232483 975.006409,725.511902
C974.952698,719.846008 975.103638,714.175598 974.918640,708.515320
C974.868958,706.995239 973.917297,705.504639 972.647156,703.511108
M790.500000,941.000000
C807.829590,941.000000 825.159180,941.000122 842.488770,941.000000
C874.981750,940.999878 907.474670,941.007996 939.967651,940.992310
C946.113892,940.989380 946.948303,940.146790 946.993530,934.122131
C947.024719,929.956604 947.002380,925.790649 946.999512,921.624878
C946.993591,913.216492 946.777954,913.000916 938.214478,913.000793
C833.403870,912.999634 728.593201,913.000000 623.782593,913.000183
C622.782837,913.000183 621.780640,913.048218 620.783691,912.996277
C616.810974,912.789429 614.915833,914.601990 614.985718,918.629822
C615.069580,923.460571 614.993530,928.294006 615.002014,933.126282
C615.015015,940.507080 615.506409,940.997253 623.036072,940.997803
C678.524048,941.001953 734.012024,941.000000 790.500000,941.000000
M905.500000,887.000000
C917.330811,886.999023 929.162292,886.922363 940.992004,887.037537
C945.216736,887.078613 947.143860,885.357971 947.025330,881.096191
C946.900269,876.600708 947.002197,872.098938 946.999268,867.599915
C946.993896,859.222290 946.770264,859.000916 938.189453,859.000793
C833.378845,858.999634 728.568237,859.000000 623.757629,859.000122
C622.757874,859.000122 621.755798,859.047729 620.758728,858.996765
C616.771606,858.792786 614.911072,860.640869 614.985229,864.654785
C615.071411,869.318848 614.997009,873.985718 615.001099,878.651367
C615.008179,886.674133 615.336792,886.998840 623.560913,886.998962
C717.207275,887.000610 810.853638,887.000000 905.500000,887.000000
M649.500000,679.000000
C670.492798,679.000000 691.485596,679.000122 712.478394,679.000061
C744.300781,678.999939 776.123230,679.007507 807.945618,678.992737
C813.972412,678.989929 814.922363,678.130737 814.986023,672.645508
C815.044006,667.647888 815.030090,662.648865 814.990784,657.650879
C814.945740,651.925720 814.056030,651.023560 808.241089,651.011108
C793.412903,650.979370 778.584595,650.999939 763.756348,650.999939
C716.605896,651.000000 669.455383,650.995667 622.304932,651.005432
C615.932983,651.006775 615.060730,651.799377 615.010559,657.605591
C614.965881,662.770142 615.112000,667.938354 614.969238,673.099304
C614.850403,677.398254 616.822083,679.083801 621.009888,679.032410
C630.171997,678.919861 639.336487,678.999084 649.500000,679.000000
z"/>
<path fill="#E9F1F7" opacity="1.000000" stroke="none"
d="
M838.538879,528.587646
C841.257324,523.850464 846.111816,522.236023 850.432739,524.310608
C854.543030,526.284058 855.016052,530.149902 851.688904,534.575378
C855.691833,539.842896 856.009766,542.567871 852.937805,545.279297
C849.674805,548.159302 842.052917,547.964355 839.096497,544.925293
C836.324524,542.075928 836.744202,539.386841 840.461304,535.256653
C839.750061,533.061462 839.083923,531.005493 838.538879,528.587646
M851.140991,542.700439
C850.508850,540.809998 850.249084,537.611389 849.166565,537.303345
C846.959412,536.675110 844.192810,537.277588 841.911621,538.089417
C841.221252,538.335144 840.763123,542.005554 841.238403,542.275269
C843.326233,543.460266 845.761108,544.134338 848.145447,544.635925
C848.876526,544.789795 849.847229,543.804626 851.140991,542.700439
M847.170837,525.929871
C845.322998,527.067627 843.475159,528.205322 841.627319,529.343018
C842.556335,530.688049 843.409424,533.080505 844.430603,533.154541
C846.401917,533.297424 848.468018,532.131409 850.494812,531.508057
C849.658997,529.686768 848.823120,527.865540 847.170837,525.929871
z"/>
<path fill="#E7F0F6" opacity="1.000000" stroke="none"
d="
M746.012695,541.742188
C746.194824,535.156616 743.170105,532.902283 737.825989,535.580933
C734.160034,537.418396 733.313477,536.432983 733.728027,532.790039
C734.009277,530.318909 734.105042,527.827087 734.316650,525.347290
C734.342285,525.046631 734.573547,524.763489 734.787659,524.304932
C739.177307,524.304932 743.625916,524.304932 748.074524,524.304932
C748.129761,524.893555 748.184937,525.482117 748.240173,526.070740
C744.746399,526.231140 741.252563,526.391479 737.205750,526.577209
C737.066406,528.453491 736.926086,530.342651 736.826416,531.684448
C740.397705,532.271240 743.774109,532.424500 746.814453,533.514832
C748.107056,533.978394 749.089172,536.301086 749.525513,537.959473
C750.549316,541.850952 748.078552,545.873596 744.370422,546.980408
C739.339478,548.482056 734.869629,546.392883 732.290100,540.637451
C737.081299,541.854736 741.158203,548.675415 746.012695,541.742188
z"/>
<path fill="#E3EDF5" opacity="1.000000" stroke="none"
d="
M788.456909,525.501526
C795.222107,521.941406 800.091431,523.837769 801.318115,530.791321
C801.956604,534.410889 801.591003,538.405762 800.774292,542.027039
C799.986816,545.518921 797.185181,547.504883 793.298828,547.331177
C789.453247,547.159302 787.114197,545.020081 786.150879,541.487305
C784.656189,536.005798 784.717834,530.639099 788.456909,525.501526
M788.678162,534.592041
C788.826111,536.579346 788.469177,538.804993 789.263428,540.487183
C790.065125,542.185181 791.953003,544.362366 793.467834,544.445007
C794.903198,544.523438 797.529236,542.330444 797.772949,540.833740
C798.372437,537.151489 798.224121,533.249084 797.689026,529.534241
C797.503540,528.246399 795.248535,526.301636 794.018555,526.359314
C792.454895,526.432556 790.637146,527.979858 789.565552,529.379211
C788.767761,530.420837 788.981445,532.236938 788.678162,534.592041
z"/>
<path fill="#EBF2F8" opacity="1.000000" stroke="none"
d="
M717.505798,532.572144
C724.369141,531.126282 728.479309,533.103760 729.059387,538.198303
C729.526733,542.303223 728.052124,545.467590 724.034119,546.895996
C719.206787,548.612000 714.843872,546.609497 711.383728,540.408997
C713.459167,541.349426 714.735840,541.575317 715.479553,542.327942
C718.204468,545.085571 721.385193,546.101440 724.214417,543.323975
C725.444580,542.116272 725.929443,538.753906 725.141663,537.173096
C723.437500,533.753479 720.182068,533.860352 717.021118,535.657959
C713.919922,537.421631 712.744568,536.314331 713.093689,532.996155
C713.389282,530.187317 713.709106,527.381104 714.034180,524.428162
C718.655518,524.428162 723.196594,524.428162 727.737732,524.428162
C727.765259,524.919922 727.792847,525.411682 727.820374,525.903442
C725.398010,526.057617 722.955505,526.480469 720.558044,526.301270
C717.218384,526.051575 715.969604,527.453918 716.169006,531.322876
C716.577148,532.220520 717.041504,532.396301 717.505798,532.572144
z"/>
<path fill="#EBF2F8" opacity="1.000000" stroke="none"
d="
M758.505798,532.572144
C765.369141,531.126282 769.479248,533.103760 770.059326,538.198303
C770.526672,542.303223 769.052124,545.467529 765.034119,546.895874
C760.206726,548.611938 755.843872,546.609497 752.383667,540.408936
C754.459167,541.349365 755.735840,541.575317 756.479553,542.327881
C759.204468,545.085571 762.385193,546.101440 765.214417,543.323975
C766.444580,542.116272 766.929382,538.753906 766.141663,537.173096
C764.437500,533.753479 761.182068,533.860352 758.021118,535.657959
C754.919922,537.421631 753.744568,536.314331 754.093750,532.996155
C754.389282,530.187317 754.709106,527.381104 755.034241,524.428162
C759.655518,524.428162 764.196594,524.428162 768.737732,524.428162
C768.765259,524.919922 768.792847,525.411682 768.820374,525.903442
C766.398010,526.057617 763.955505,526.480469 761.558044,526.301270
C758.218384,526.051575 756.969604,527.453918 757.169006,531.322876
C757.577148,532.220459 758.041504,532.396301 758.505798,532.572144
z"/>
<path fill="#EBF2F8" opacity="1.000000" stroke="none"
d="
M834.566406,535.899048
C833.492676,544.553772 831.111328,547.565491 825.870239,547.300293
C820.623962,547.034851 817.791504,542.747192 818.009949,535.031982
C818.069946,532.913330 818.030273,530.270691 819.209534,528.826233
C821.012573,526.617676 823.831055,523.896729 826.225281,523.890198
C828.606445,523.883728 831.489075,526.578247 833.216309,528.811829
C834.462341,530.423096 834.175781,533.219666 834.566406,535.899048
M821.967712,541.145508
C823.533264,542.343506 824.987488,544.210571 826.704895,544.496399
C827.789429,544.676880 830.234314,542.612671 830.387878,541.350769
C830.857849,537.488831 830.876343,533.473267 830.294739,529.635010
C830.088257,528.272278 827.649902,526.321838 826.236816,526.327271
C824.812927,526.332703 822.430786,528.252625 822.168091,529.652649
C821.515991,533.128479 821.832886,536.786072 821.967712,541.145508
z"/>
<path fill="#E4EEF5" opacity="1.000000" stroke="none"
d="
M652.867798,544.217773
C654.905396,544.439941 656.491455,544.661987 658.077515,544.884094
C658.028320,545.399841 657.979187,545.915588 657.929993,546.431335
C652.869995,546.431335 647.810059,546.431335 642.488464,546.431335
C641.603027,542.293274 643.543457,539.346802 647.078308,537.162109
C648.631958,536.201843 650.537842,535.609619 651.738647,534.336182
C652.986816,533.012512 654.041260,531.125488 654.258118,529.370850
C654.366333,528.494934 652.368835,526.483582 651.407715,526.535706
C649.488220,526.639526 647.529846,527.637512 645.777466,528.608765
C644.736145,529.185913 644.056702,530.415955 642.362366,532.300232
C641.985535,526.860413 644.105835,524.739807 647.608765,523.892029
C651.190308,523.025208 654.763794,523.243286 656.902344,526.778687
C658.921265,530.116333 657.983093,533.292542 655.073914,535.666748
C653.289185,537.123291 651.032532,537.990356 649.179138,539.377747
C647.850464,540.372437 646.838684,541.790405 646.204285,544.217712
C648.274963,544.217712 650.345642,544.217712 652.867798,544.217773
z"/>
<path fill="#E8F1F7" opacity="1.000000" stroke="none"
d="
M665.513367,524.462524
C672.824646,522.786072 676.408447,525.045532 677.167480,532.003906
C677.519348,535.229553 677.700012,538.945862 676.420227,541.733643
C675.324280,544.121033 672.280884,546.373535 669.655273,547.114319
C666.081421,548.122620 663.162598,545.701355 662.117737,542.152100
C660.278503,535.904724 659.711121,529.704834 665.513367,524.462524
M664.593872,531.367371
C664.728821,534.630981 664.454895,537.989563 665.180420,541.116089
C665.516235,542.562927 667.937683,544.644775 669.168518,544.489990
C670.818054,544.282593 673.140625,542.421326 673.547791,540.848938
C674.311768,537.898865 674.443481,534.531616 673.804932,531.559937
C673.371338,529.542358 671.176147,527.903442 669.768799,526.095154
C668.109924,527.587708 666.451050,529.080261 664.593872,531.367371
z"/>
<path fill="#E7F0F6" opacity="1.000000" stroke="none"
d="
M695.860229,527.158691
C697.437561,532.409607 695.067871,535.341797 690.994141,537.632751
C688.587158,538.986511 686.441528,540.805054 684.178345,542.414551
C684.393982,542.880859 684.609619,543.347107 684.825256,543.813416
C688.821289,544.132629 692.817261,544.451782 696.813293,544.770996
C696.763733,545.330017 696.714172,545.888977 696.664612,546.447998
C691.585876,546.447998 686.507141,546.447998 681.275574,546.447998
C680.202637,542.473877 682.032959,539.616821 685.376343,537.443420
C687.186890,536.266479 689.607971,535.549927 690.726807,533.920532
C691.875488,532.247437 692.869385,528.897522 691.995117,527.725464
C690.038818,525.102783 686.979187,525.701782 684.630371,528.031738
C683.638184,529.015991 682.861084,530.217041 681.342651,532.130493
C680.293274,527.395935 682.220398,525.302063 684.933228,524.308411
C688.881653,522.862183 692.833130,522.926819 695.860229,527.158691
z"/>
<path fill="#E8F1F7" opacity="1.000000" stroke="none"
d="
M609.715454,542.891357
C609.365845,541.392761 609.022644,540.259460 609.006042,539.121460
C608.941650,534.718079 610.440857,531.134216 615.161682,530.213440
C619.265869,529.413025 622.335327,531.244812 623.739685,535.243408
C625.242493,539.522339 623.687622,544.525879 620.513733,546.366577
C616.962830,548.425964 612.920166,547.243774 609.715454,542.891357
M612.838501,542.412964
C614.474487,543.208008 616.369873,544.919189 617.675903,544.549316
C619.078918,544.151855 620.587524,541.917725 620.880493,540.276306
C621.466370,536.994324 621.517395,533.000000 616.877258,532.743835
C612.226257,532.487183 612.023071,536.286316 612.082947,539.765015
C612.094055,540.410278 612.346252,541.051453 612.838501,542.412964
z"/>
<path fill="#F1F6FA" opacity="1.000000" stroke="none"
d="
M599.103027,523.897095
C602.671631,523.897095 605.769836,523.897095 608.868042,523.897095
C608.967407,524.573853 609.066711,525.250671 609.166077,525.927429
C606.992249,526.326721 604.818420,526.726013 602.026550,527.238770
C602.026550,532.557373 602.093994,538.164673 601.967590,543.767578
C601.943298,544.843018 601.157043,545.901306 600.722351,546.967468
C600.139099,545.884399 599.086853,544.816223 599.051880,543.715759
C598.878113,538.252136 598.972595,532.780090 598.972595,527.311218
C596.246277,526.808167 593.818359,526.360107 591.390442,525.912109
C591.452087,525.240417 591.513794,524.568726 591.575439,523.897034
C593.927795,523.897034 596.280212,523.897034 599.103027,523.897095
z"/>
<path fill="#CADDEB" opacity="1.000000" stroke="none"
d="
M808.839417,529.606079
C807.433777,528.928711 806.222290,528.648987 803.909729,528.115112
C807.111572,526.367615 809.349365,525.146240 812.638428,523.351135
C812.638428,531.106323 812.682129,538.262817 812.552734,545.416138
C812.542786,545.968018 811.315491,546.497864 810.654053,547.037964
C810.150146,546.160522 809.242981,545.298035 809.209595,544.403015
C809.030823,539.607727 809.069824,534.804382 808.839417,529.606079
z"/>
<path fill="#A9AEB1" opacity="1.000000" stroke="none"
d="
M625.116882,807.000000
C739.081665,807.000000 852.564087,806.999634 966.046509,807.000549
C974.851746,807.000610 974.996887,807.148193 974.999451,815.957825
C975.000732,820.457092 974.948547,824.957031 975.007446,829.455505
C975.055786,833.147644 973.453491,835.105896 969.632629,834.996582
C968.467224,834.963196 967.299866,834.999634 966.133423,834.999634
C851.984436,835.000000 737.835449,835.000488 623.686462,834.999146
C615.240906,834.999023 615.005981,834.757751 615.000366,826.276062
C614.998230,823.109863 614.999695,819.943726 615.000061,816.777527
C615.001099,807.007446 615.001221,807.007446 625.116882,807.000000
z"/>
<path fill="#A9AEB1" opacity="1.000000" stroke="none"
d="
M615.000000,768.024231
C614.895813,754.115295 614.303772,754.987732 627.375793,754.989807
C740.356262,755.007507 853.336670,754.999512 966.317139,755.000854
C974.757751,755.000977 974.994873,755.243896 974.999268,763.728271
C975.001587,768.227478 974.924561,772.728271 975.017639,777.225525
C975.099243,781.165955 973.425171,783.182983 969.362427,783.004944
C967.865662,782.939331 966.363464,782.999756 964.863708,782.999756
C851.716614,783.000000 738.569519,783.000000 625.422424,782.999939
C615.000366,782.999939 615.000366,782.999634 615.000000,772.512878
C614.999939,771.179749 615.000000,769.846680 615.000000,768.024231
z"/>
<path fill="#A9AEB1" opacity="1.000000" stroke="none"
d="
M973.013428,703.755859
C973.917297,705.504639 974.868958,706.995239 974.918640,708.515320
C975.103638,714.175598 974.952698,719.846008 975.006409,725.511902
C975.041687,729.232483 973.368164,731.118652 969.575317,730.997681
C968.409973,730.960449 967.242310,730.999634 966.075684,730.999634
C852.080811,731.000000 738.085938,731.000305 624.091064,730.999512
C615.060547,730.999451 615.001160,730.936829 615.000732,721.679016
C615.000549,717.179260 615.015320,712.679443 615.009033,708.179688
C615.004211,704.679993 616.713013,702.943176 620.234863,703.006165
C621.567627,703.030029 622.901306,703.000427 624.234558,703.000366
C738.062744,702.999939 851.890991,702.997009 965.719177,703.021362
C968.028564,703.021851 970.337830,703.340759 973.013428,703.755859
z"/>
<path fill="#A9AEB1" opacity="1.000000" stroke="none"
d="
M790.000000,941.000000
C734.012024,941.000000 678.524048,941.001953 623.036072,940.997803
C615.506409,940.997253 615.015015,940.507080 615.002014,933.126282
C614.993530,928.294006 615.069580,923.460571 614.985718,918.629822
C614.915833,914.601990 616.810974,912.789429 620.783691,912.996277
C621.780640,913.048218 622.782837,913.000183 623.782593,913.000183
C728.593201,913.000000 833.403870,912.999634 938.214478,913.000793
C946.777954,913.000916 946.993591,913.216492 946.999512,921.624878
C947.002380,925.790649 947.024719,929.956604 946.993530,934.122131
C946.948303,940.146790 946.113892,940.989380 939.967651,940.992310
C907.474670,941.007996 874.981750,940.999878 842.488770,941.000000
C825.159180,941.000122 807.829590,941.000000 790.000000,941.000000
z"/>
<path fill="#A9AEB1" opacity="1.000000" stroke="none"
d="
M905.000000,887.000000
C810.853638,887.000000 717.207275,887.000610 623.560913,886.998962
C615.336792,886.998840 615.008179,886.674133 615.001099,878.651367
C614.997009,873.985718 615.071411,869.318848 614.985229,864.654785
C614.911072,860.640869 616.771606,858.792786 620.758728,858.996765
C621.755798,859.047729 622.757874,859.000122 623.757629,859.000122
C728.568237,859.000000 833.378845,858.999634 938.189453,859.000793
C946.770264,859.000916 946.993896,859.222290 946.999268,867.599915
C947.002197,872.098938 946.900269,876.600708 947.025330,881.096191
C947.143860,885.357971 945.216736,887.078613 940.992004,887.037537
C929.162292,886.922363 917.330811,886.999023 905.000000,887.000000
z"/>
<path fill="#A9AEB1" opacity="1.000000" stroke="none"
d="
M649.000000,679.000000
C639.336487,678.999084 630.171997,678.919861 621.009888,679.032410
C616.822083,679.083801 614.850403,677.398254 614.969238,673.099304
C615.112000,667.938354 614.965881,662.770142 615.010559,657.605591
C615.060730,651.799377 615.932983,651.006775 622.304932,651.005432
C669.455383,650.995667 716.605896,651.000000 763.756348,650.999939
C778.584595,650.999939 793.412903,650.979370 808.241089,651.011108
C814.056030,651.023560 814.945740,651.925720 814.990784,657.650879
C815.030090,662.648865 815.044006,667.647888 814.986023,672.645508
C814.922363,678.130737 813.972412,678.989929 807.945618,678.992737
C776.123230,679.007507 744.300781,678.999939 712.478394,679.000061
C691.485596,679.000122 670.492798,679.000000 649.000000,679.000000
z"/>
<path fill="#1068A8" opacity="1.000000" stroke="none"
d="
M850.924561,543.020752
C849.847229,543.804626 848.876526,544.789795 848.145447,544.635925
C845.761108,544.134338 843.326233,543.460266 841.238403,542.275269
C840.763123,542.005554 841.221252,538.335144 841.911621,538.089417
C844.192810,537.277588 846.959412,536.675110 849.166565,537.303345
C850.249084,537.611389 850.508850,540.809998 850.924561,543.020752
z"/>
<path fill="#1068A8" opacity="1.000000" stroke="none"
d="
M847.579102,525.987061
C848.823120,527.865540 849.658997,529.686768 850.494812,531.508057
C848.468018,532.131409 846.401917,533.297424 844.430603,533.154541
C843.409424,533.080505 842.556335,530.688049 841.627319,529.343018
C843.475159,528.205322 845.322998,527.067627 847.579102,525.987061
z"/>
<path fill="#0964A5" opacity="1.000000" stroke="none"
d="
M788.711426,534.150085
C788.981445,532.236938 788.767761,530.420837 789.565552,529.379211
C790.637146,527.979858 792.454895,526.432556 794.018555,526.359314
C795.248535,526.301636 797.503540,528.246399 797.689026,529.534241
C798.224121,533.249084 798.372437,537.151489 797.772949,540.833740
C797.529236,542.330444 794.903198,544.523438 793.467834,544.445007
C791.953003,544.362366 790.065125,542.185181 789.263428,540.487183
C788.469177,538.804993 788.826111,536.579346 788.711426,534.150085
z"/>
<path fill="#005EA2" opacity="1.000000" stroke="none"
d="
M717.211609,532.294189
C717.041504,532.396301 716.577148,532.220520 716.204712,531.770691
C716.503540,531.669800 716.710449,531.842957 717.211609,532.294189
z"/>
<path fill="#005EA2" opacity="1.000000" stroke="none"
d="
M758.211609,532.294189
C758.041504,532.396301 757.577148,532.220459 757.204712,531.770691
C757.503540,531.669800 757.710449,531.842957 758.211609,532.294189
z"/>
<path fill="#0964A5" opacity="1.000000" stroke="none"
d="
M821.864136,540.758057
C821.832886,536.786072 821.515991,533.128479 822.168091,529.652649
C822.430786,528.252625 824.812927,526.332703 826.236816,526.327271
C827.649902,526.321838 830.088257,528.272278 830.294739,529.635010
C830.876343,533.473267 830.857849,537.488831 830.387878,541.350769
C830.234314,542.612671 827.789429,544.676880 826.704895,544.496399
C824.987488,544.210571 823.533264,542.343506 821.864136,540.758057
z"/>
<path fill="#0964A5" opacity="1.000000" stroke="none"
d="
M664.693054,530.970093
C666.451050,529.080261 668.109924,527.587708 669.768799,526.095154
C671.176147,527.903442 673.371338,529.542358 673.804932,531.559937
C674.443481,534.531616 674.311768,537.898865 673.547791,540.848938
C673.140625,542.421326 670.818054,544.282593 669.168518,544.489990
C667.937683,544.644775 665.516235,542.562927 665.180420,541.116089
C664.454895,537.989563 664.728821,534.630981 664.693054,530.970093
z"/>
<path fill="#0964A5" opacity="1.000000" stroke="none"
d="
M612.662842,542.053711
C612.346252,541.051453 612.094055,540.410278 612.082947,539.765015
C612.023071,536.286316 612.226257,532.487183 616.877258,532.743835
C621.517395,533.000000 621.466370,536.994324 620.880493,540.276306
C620.587524,541.917725 619.078918,544.151855 617.675903,544.549316
C616.369873,544.919189 614.474487,543.208008 612.662842,542.053711
z"/>
</svg>

Before

Width:  |  Height:  |  Size: 40 KiB

View File

@@ -0,0 +1,300 @@
(function (window) {
if (document.getElementById('activityChartContainer')) {
const COLORS = {
delivered: '#0076d6',
failed: '#fa9441',
text: '#666'
};
const FONT_SIZE = 16;
const FONT_WEIGHT = 'bold';
const MAX_Y = 120;
const createChart = function(containerId, labels, deliveredData, failedData) {
const container = d3.select(containerId);
container.selectAll('*').remove(); // Clear any existing content
const margin = { top: 60, right: 20, bottom: 40, left: 20 }; // Adjusted top margin for legend
const width = container.node().getBoundingClientRect().width - margin.left - margin.right;
const height = 400 - margin.top - margin.bottom;
const svg = container.append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
.append('g')
.attr('transform', `translate(${margin.left},${margin.top})`);
let tooltip = d3.select('#tooltip');
if (tooltip.empty()) {
tooltip = d3.select('body').append('div')
.attr('id', 'tooltip')
.style('display', 'none');
}
// Create legend
const legendContainer = d3.select('.chart-legend');
legendContainer.selectAll('*').remove(); // Clear any existing legend
const legendData = [
{ label: 'Delivered', color: COLORS.delivered },
{ label: 'Failed', color: COLORS.failed }
];
const legendItem = legendContainer.selectAll('.legend-item')
.data(legendData)
.enter()
.append('div')
.attr('class', 'legend-item');
legendItem.append('div')
.attr('class', 'legend-rect')
.style('background-color', d => d.color)
.style('display', 'inline-block')
.style('margin-right', '5px');
legendItem.append('span')
.attr('class', 'legend-label')
.text(d => d.label);
const x = d3.scaleBand()
.domain(labels)
.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()
.domain([0, maxY + 2]) // Add 2 units of space at the top
.nice()
.range([height, 0]);
svg.append('g')
.attr('class', 'x axis')
.attr('transform', `translate(0,${height})`)
.call(d3.axisBottom(x));
// 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
.tickFormat(d3.format('d')); // Ensure whole numbers on the y-axis
svg.append('g')
.attr('class', 'y axis')
.call(yAxis);
// Data for stacking
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
}));
// Stack the data
const stack = d3.stack()
.keys(['delivered', 'failed'])
.order(d3.stackOrderNone)
.offset(d3.stackOffsetNone);
const series = stack(stackData);
// Color scale
const color = d3.scaleOrdinal()
.domain(['delivered', 'failed'])
.range([COLORS.delivered, COLORS.failed]);
// Create bars with animation
const barGroups = svg.selectAll('.bar-group')
.data(series)
.enter()
.append('g')
.attr('class', 'bar-group')
.attr('fill', d => color(d.key));
barGroups.selectAll('rect')
.data(d => d)
.enter()
.append('rect')
.attr('x', d => x(d.data.label))
.attr('y', height)
.attr('height', 0)
.attr('width', x.bandwidth())
.on('mouseover', function(event, d) {
const key = d3.select(this.parentNode).datum().key;
const capitalizedKey = key.charAt(0).toUpperCase() + key.slice(1);
tooltip.style('display', 'block')
.html(`${d.data.label}<br>${capitalizedKey}: ${d.data[key]}`);
})
.on('mousemove', function(event) {
tooltip.style('left', `${event.pageX + 10}px`)
.style('top', `${event.pageY - 20}px`);
})
.on('mouseout', function() {
tooltip.style('display', 'none');
})
.transition()
.duration(1000)
.attr('y', d => y(d[1]))
.attr('height', d => y(d[0]) - y(d[1]));
};
// Function to create an accessible table
const createTable = function(tableId, chartType, labels, deliveredData, failedData) {
const table = document.getElementById(tableId);
table.innerHTML = ""; // Clear previous data
const captionText = document.querySelector(`#${chartType} .chart-subtitle`).textContent;
const caption = document.createElement('caption');
caption.textContent = captionText;
const thead = document.createElement('thead');
const tbody = document.createElement('tbody');
// Create table header
const headerRow = document.createElement('tr');
const headers = ['Day', 'Delivered', 'Failed'];
headers.forEach(headerText => {
const th = document.createElement('th');
th.textContent = headerText;
headerRow.appendChild(th);
});
thead.appendChild(headerRow);
// Create table body
labels.forEach((label, index) => {
const row = document.createElement('tr');
const cellDay = document.createElement('td');
cellDay.textContent = label;
row.appendChild(cellDay);
const cellDelivered = document.createElement('td');
cellDelivered.textContent = deliveredData[index];
row.appendChild(cellDelivered);
const cellFailed = document.createElement('td');
cellFailed.textContent = failedData[index];
row.appendChild(cellFailed);
tbody.appendChild(row);
});
table.appendChild(caption);
table.appendChild(thead);
table.append(tbody);
};
const fetchData = function(type) {
var ctx = document.getElementById('weeklyChart');
if (!ctx) {
return;
}
var url = type === 'service' ? `/daily_stats.json` : `/daily_stats_by_user.json`;
return fetch(url)
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(data => {
labels = [];
deliveredData = [];
failedData = [];
let totalMessages = 0;
for (var dateString in data) {
if (data.hasOwnProperty(dateString)) {
const dateParts = dateString.split('-');
const formattedDate = `${dateParts[1]}/${dateParts[2]}/${dateParts[0].slice(2)}`;
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;
}
}
// Check if there are no messages sent
const subTitle = document.querySelector(`#activityChartContainer .chart-subtitle`);
if (totalMessages === 0) {
// Remove existing chart and render the alert message
d3.select('#weeklyChart').selectAll('*').remove();
d3.select('#weeklyChart')
.append('div')
.html(`
<div class="usa-alert usa-alert--info usa-alert--slim">
<div class="usa-alert__body">
<p class="usa-alert__text">
No messages sent in the last 7 days
</p>
</div>
</div>
`);
// Hide the subtitle
if (subTitle) {
subTitle.style.display = 'none';
}
} 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));
};
const handleDropdownChange = function(event) {
const selectedValue = event.target.value;
const subTitle = document.querySelector(`#activityChartContainer .chart-subtitle`);
const selectElement = document.getElementById('options');
const selectedText = selectElement.options[selectElement.selectedIndex].text;
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
};
document.addEventListener('DOMContentLoaded', function() {
// Initialize activityChart chart and table with service data by default
fetchData('service');
// 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);
}
});
// Export functions for testing
window.createChart = createChart;
window.createTable = createTable;
window.handleDropdownChange = handleDropdownChange;
window.fetchData = fetchData;
}
})(window);

View File

@@ -1,67 +0,0 @@
(function (window) {
function initializeChartAndSocket() {
var ctx = document.getElementById('myChart');
if (!ctx) {
return;
}
var myBarChart = new Chart(ctx.getContext('2d'), {
type: 'bar',
data: {
labels: [],
datasets: [
{
label: 'Delivered',
data: [],
backgroundColor: '#0076d6',
stack: 'Stack 0'
},
]
},
options: {
scales: {
y: {
beginAtZero: true
}
}
}
});
var socket = io();
socket.on('connect', function() {
socket.emit('fetch_daily_stats_by_user');
});
socket.on('daily_stats_by_user_update', function(data) {
// console.log('Data received:', data);
var labels = [];
var deliveredData = [];
for (var date in data) {
labels.push(date);
deliveredData.push(data[date].sms.delivered);
}
myBarChart.data.labels = labels;
myBarChart.data.datasets[0].data = deliveredData;
myBarChart.update();
});
socket.on('error', function(data) {
// console.log('Error:', data);
});
var sevenDaysButton = document.getElementById('sevenDaysButton');
if (sevenDaysButton) {
sevenDaysButton.addEventListener('click', function() {
socket.emit('fetch_daily_stats_by_user');
// console.log('clicked');
});
}
}
document.addEventListener('DOMContentLoaded', initializeChartAndSocket);
})(window);

View File

@@ -0,0 +1,150 @@
(function (window) {
function createTotalMessagesChart() {
var chartContainer = document.getElementById('totalMessageChartContainer');
if (!chartContainer) return;
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;
// Update the message below the chart
document.getElementById('message').innerText = `${sms_sent.toLocaleString()} sent / ${sms_remaining_messages.toLocaleString()} remaining`;
// Calculate minimum width for "Messages Sent" as 1% of the total chart width
var minSentPercentage = (sms_sent === 0) ? 0 : 0.02;
var minSentValue = totalMessages * minSentPercentage;
var displaySent = Math.max(sms_sent, minSentValue);
var displayRemaining = totalMessages - displaySent;
var svg = d3.select("#totalMessageChart");
var width = chartContainer.clientWidth;
var height = 48;
// Ensure the width is set correctly
if (width === 0) {
console.error('Chart container width is 0, cannot set SVG width.');
return;
}
svg.attr("width", width).attr("height", height);
var x = d3.scaleLinear()
.domain([0, totalMessages])
.range([0, width]);
// Create tooltip dynamically
var tooltip = d3.select("body").append("div")
.attr("id", "tooltip");
// Create the initial bars
var sentBar = svg.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("height", height)
.attr("fill", '#0076d6')
.attr("width", 0) // Start with width 0 for animation
.on('mouseover', function(event) {
tooltip.style('display', 'block')
.html(`Messages Sent: ${sms_sent.toLocaleString()}`);
})
.on('mousemove', function(event) {
tooltip.style('left', `${event.pageX + 10}px`)
.style('top', `${event.pageY - 20}px`);
})
.on('mouseout', function() {
tooltip.style('display', 'none');
});
var remainingBar = svg.append("rect")
.attr("x", 0) // Initially set to 0, will be updated during animation
.attr("y", 0)
.attr("height", height)
.attr("fill", '#C7CACE')
.attr("width", 0) // Start with width 0 for animation
.on('mouseover', function(event) {
tooltip.style('display', 'block')
.html(`Remaining: ${sms_remaining_messages.toLocaleString()}`);
})
.on('mousemove', function(event) {
tooltip.style('left', `${event.pageX + 10}px`)
.style('top', `${event.pageY - 20}px`);
})
.on('mouseout', function() {
tooltip.style('display', 'none');
});
// Animate the bars together as a single cohesive line
svg.transition()
.duration(1000) // Total animation duration
.attr("width", width)
.tween("resize", function() {
var interpolator = d3.interpolate(0, width);
return function(t) {
var newWidth = interpolator(t);
var sentWidth = x(displaySent) / width * newWidth;
var remainingWidth = x(displayRemaining) / width * newWidth;
sentBar.attr("width", sentWidth);
remainingBar.attr("x", sentWidth).attr("width", remainingWidth);
};
});
// Create and populate the accessible table
var tableContainer = document.getElementById('totalMessageTable');
var table = document.createElement('table');
table.className = 'usa-sr-only usa-table';
var caption = document.createElement('caption');
caption.textContent = chartTitle;
table.appendChild(caption);
var thead = document.createElement('thead'); // Ensure thead is created
var theadRow = document.createElement('tr');
var thMessagesSent = document.createElement('th');
thMessagesSent.textContent = 'Messages Sent'; // First column header
var thRemaining = document.createElement('th');
thRemaining.textContent = 'Remaining'; // Second column header
theadRow.appendChild(thMessagesSent);
theadRow.appendChild(thRemaining);
thead.appendChild(theadRow); // Append theadRow to the thead
table.appendChild(thead);
var tbody = document.createElement('tbody');
var tbodyRow = document.createElement('tr');
var tdMessagesSent = document.createElement('td');
tdMessagesSent.textContent = sms_sent.toLocaleString(); // Value for Messages Sent
var tdRemaining = document.createElement('td');
tdRemaining.textContent = sms_remaining_messages.toLocaleString(); // Value for Remaining
tbodyRow.appendChild(tdMessagesSent);
tbodyRow.appendChild(tdRemaining);
tbody.appendChild(tbodyRow);
table.appendChild(tbody);
tableContainer.appendChild(table);
table.appendChild(tbody);
tableContainer.appendChild(table);
// Ensure the chart resizes correctly on window resize
window.addEventListener('resize', function () {
width = chartContainer.clientWidth;
x.range([0, width]);
svg.attr("width", width);
sentBar.attr("width", x(displaySent));
remainingBar.attr("x", x(displaySent)).attr("width", x(displayRemaining));
});
}
// Initialize total messages chart if the container exists
document.addEventListener('DOMContentLoaded', function() {
createTotalMessagesChart();
});
// Export function for testing
window.createTotalMessagesChart = createTotalMessagesChart;
})(window);

View File

@@ -0,0 +1,4 @@
document.addEventListener('DOMContentLoaded', function() {
var timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
document.cookie = `timezone=${timeZone}; path=/`;
})

View File

@@ -0,0 +1,159 @@
@use "uswds-core" as *;
$delivered: color('blue-50v');
$pending: color('green-cool-40v');
$failed: color('gray-cool-20');
.chart-container {
display: flex;
&.usage {
height: units(4);
}
svg {
overflow: visible;
}
}
#totalMessageChartContainer {
max-width: 600px;
}
.bar {
border-radius: units(0.5);
&.delivered, &.usage {
background-color: $delivered;
margin-right: 1px;
}
&.pending{
background-color: $pending;
margin-right: 1px;
}
&.failed, &.remaining {
background-color: $failed;
}
}
.legend {
display: flex;
margin: units(1) 0;
.legend-item {
display: flex;
align-items: flex-start;
margin-right: units(2);
}
.legend-color {
width: units(3);
height: units(3);
margin-right: 0;
padding: 0;
border-radius: 2px;
background-color: $delivered;
&.pending {
background-color: $pending;
}
&.failed, &.remaining {
background-color: $failed;
}
}
.legend-value {
margin: 0 units(1);
}
}
.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;
background-color: #eee;
border-radius: 5px;
margin-bottom: 10px;
}
.progress-bar-inner {
height: 100%;
background-color: #007bff;
border-radius: inherit;
}
.chart {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
.chart-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: units(1) units(1) 0;
}
.chart-subtitle {
font-size: size("body", 6);
font-weight: bold;
text-align: left;
width: 100%;
padding: 0;
margin: 0;
}
.axis text {
font-size: size("body", 1);
}
.axis line,
.axis path {
shape-rendering: crispEdges;
stroke: #000;
fill: none;
}
.bar {
fill-opacity: 0.8;
}
.chart-container {
width: 100%;
position: relative;
}
.chart-legend {
display: flex;
align-items: center;
}
.legend-item {
display: flex;
align-items: center;
margin-right: units(2);
.legend-rect {
width: units(2);
height: units(2);
margin-right: units(1);
}
}
#tooltip {
position: absolute;
display: none;
background: color('ink');
color: #FFF;
border: 1px solid #ccc;
padding: units(1);
border-radius: units(1);
pointer-events: none;
z-index: 100;
font-size: size("body", 3);
line-height: 1.3;
}

View File

@@ -344,11 +344,3 @@ h2.recipient-list {
}
}
}
.js-focus-style {
outline: 3px solid color("blue-40v");
box-shadow: 0 0 0 7px color("blue-40v");
*:focus {
outline: none;
}
}

View File

@@ -22,9 +22,8 @@ i.e.
@use "uswds-core" as *;
iframe:focus, [href]:focus, [tabindex]:focus, [contentEditable=true]:focus {
outline: units(1px) dotted color('blue-40v');
outline: units(1px) auto color('blue-40v');
iframe:focus, [href]:focus, [tabindex]:focus, [contentEditable=true]:focus, button:not([disabled]):focus {
outline: units(2px) solid color('blue-40v');
outline-offset: 0.3rem;
}
@@ -277,6 +276,12 @@ td.table-empty-message {
display: block;
}
.usa-table {
th {
border-bottom: 0 !important;
}
}
.js-stick-at-bottom-when-scrolling {
display: flex;
align-items: flex-end;
@@ -327,6 +332,22 @@ td.table-empty-message {
bottom: 0;
}
.table-overflow-x-auto {
&.hidden {
display: none;
}
}
@media (max-width: units('desktop-lg')) {
.table-overflow-x-auto {
overflow-x: auto;
table {
min-width: 768px;
}
}
}
// Dashboard
.dashboard {
@@ -376,9 +397,6 @@ td.table-empty-message {
background-image: url(../img/material-icons/description.svg);
}
}
.table-wrapper {
overflow-x: scroll;
}
}
.dashboard-table {
@@ -402,6 +420,30 @@ td.table-empty-message {
}
}
.job-status-table {
table-layout: fixed;
thead tr th {
border-bottom: 0;
}
thead,
tbody,
tr {
width: 100%;
}
th:first-child,
td:first-child {
width: 75%;
}
th:nth-child(2),R
td:nth-child(2) {
width: 25%;
}
}
.usage-table {
ul {
list-style: none;
@@ -421,27 +463,51 @@ td.table-empty-message {
width: 25%;
overflow-wrap: anywhere;
}
td.jobid {
width: 5%;
}
td.template {
width: 20%;
width: 25%;
}
td.time-sent {
width: 20%;
width: 15%;
}
td.sender {
width: 15%;
width: 20%;
overflow-wrap: break-word;
}
td.count-of-recipients {
width: 5%;
}
td.report {
width: 5%;
width: 2%;
text-align: center;
}
td.delivered {
width: 2%;
text-align: center;
}
td.failed {
width: 2%;
text-align: center;
}
td.report img {
padding-top: 5px;
}
th {
padding: 0.5rem 1rem
}
td {
padding: 0.5rem 1rem
}
}
@media (max-width: 768px) {
.usa-table-container--scrollable-mobile {
margin: 0;
overflow-y:hidden;
}
}
.usa-table th[data-sortable][aria-sort=ascending], .usa-table th[data-sortable][aria-sort=descending] {
background-color: #a1d3ff;
}
#template-list {
@@ -456,6 +522,10 @@ td.table-empty-message {
}
}
.usa-prose > p.max-width-full {
max-width: 100%;
}
// Tabs
.tabs {

View File

@@ -13,5 +13,6 @@ in the form $setting: value,
$theme-banner-max-width: "desktop-lg",
$theme-grid-container-max-width: "desktop-lg",
$theme-footer-max-width: "desktop-lg",
$theme-header-max-width: "desktop-lg"
$theme-header-max-width: "desktop-lg",
$theme-identifier-max-width: "desktop-lg"
);

View File

@@ -1,4 +1,5 @@
@forward "uswds-theme";
@forward "uswds";
@forward "uswds-theme-custom-styles";
@forward "legacy-styles";
@forward "legacy-styles";
@forward "data-visualization";

View File

@@ -34,6 +34,11 @@ $path: '/static/images/';
// Dependencies from GOV.UK Frontend, packaged to be specific to this application
@import './govuk-frontend/all';
// Custom overrides
.govuk-link {
font-weight: bold;
}
// Specific to this application
@import 'local/typography';
@import 'grids';

View File

@@ -8,6 +8,7 @@ from notifications_utils import DAILY_MESSAGE_LIMIT
class Config(object):
SIMULATED_SMS_NUMBERS = ("+14254147755", "+14254147167")
NOTIFY_APP_NAME = "admin"
NOTIFY_ENVIRONMENT = getenv("NOTIFY_ENVIRONMENT", "development")
API_HOST_NAME = getenv("API_HOST_NAME", "localhost")

View File

@@ -231,6 +231,11 @@ def naturaltime_without_indefinite_article(date):
)
def convert_time_unixtimestamp(date_string):
dt = datetime.fromisoformat(date_string)
return int(dt.timestamp())
def format_delta(date):
# This method assumes that date is in UTC
date = parse_naive_dt(date)

View File

@@ -3,6 +3,7 @@ from flask import Blueprint
main = Blueprint("main", __name__)
from app.main.views import ( # noqa isort:skip
activity,
add_service,
api_keys,
choose_account,

View File

@@ -50,6 +50,7 @@ from app.main.validators import (
CommonlyUsedPassword,
CsvFileValidator,
DoesNotStartWithDoubleZero,
FieldCannotContainComma,
LettersNumbersSingleQuotesFullStopsAndUnderscoresOnly,
MustContainAlphanumericCharacters,
NoCommasInPlaceHolders,
@@ -1650,7 +1651,11 @@ def get_placeholder_form_instance(
) # TODO: replace with us_mobile_number
else:
field = GovukTextInputField(
placeholder_name, validators=[DataRequired(message="Cannot be empty")]
placeholder_name,
validators=[
DataRequired(message="Cannot be empty"),
FieldCannotContainComma(),
],
)
PlaceholderForm.placeholder_value = field

View File

@@ -161,6 +161,15 @@ class DoesNotStartWithDoubleZero:
raise ValidationError(self.message)
class FieldCannotContainComma:
def __init__(self, message="Cannot contain a comma"):
self.message = message
def __call__(self, form, field):
if field.data and "," in field.data:
raise ValidationError(self.message)
class MustContainAlphanumericCharacters:
regex = re.compile(r".*[a-zA-Z0-9].*[a-zA-Z0-9].*")

124
app/main/views/activity.py Normal file
View File

@@ -0,0 +1,124 @@
from flask import abort, render_template, request, url_for
from app import current_service, job_api_client
from app.formatters import convert_time_unixtimestamp, get_time_left
from app.main import main
from app.utils.pagination import (
generate_next_dict,
generate_pagination_pages,
generate_previous_dict,
get_page_from_request,
)
from app.utils.user import user_has_permissions
@main.route("/activity/services/<uuid:service_id>")
@user_has_permissions("view_activity")
def all_jobs_activity(service_id):
service_data_retention_days = 7
page = get_page_from_request()
jobs = job_api_client.get_page_of_jobs(service_id, page=page)
all_jobs_dict = generate_job_dict(jobs)
prev_page, next_page, pagination = handle_pagination(jobs, service_id, page)
message_type = ("sms",)
return render_template(
"views/activity/all-activity.html",
all_jobs_dict=all_jobs_dict,
service_data_retention_days=service_data_retention_days,
next_page=next_page,
prev_page=prev_page,
pagination=pagination,
download_link_one_day=url_for(
".download_notifications_csv",
service_id=current_service.id,
message_type=message_type,
status=request.args.get("status"),
number_of_days="one_day",
),
download_link_three_day=url_for(
".download_notifications_csv",
service_id=current_service.id,
message_type=message_type,
status=request.args.get("status"),
number_of_days="three_day",
),
download_link_five_day=url_for(
".download_notifications_csv",
service_id=current_service.id,
message_type=message_type,
status=request.args.get("status"),
number_of_days="five_day",
),
download_link_seven_day=url_for(
".download_notifications_csv",
service_id=current_service.id,
message_type=message_type,
status=request.args.get("status"),
number_of_days="seven_day",
),
)
def handle_pagination(jobs, service_id, page):
if page is None:
abort(404, "Invalid page argument ({}).".format(request.args.get("page")))
prev_page = (
generate_previous_dict("main.all_jobs_activity", service_id, page)
if page > 1
else None
)
next_page = (
generate_next_dict("main.all_jobs_activity", service_id, page)
if jobs.get("links", {}).get("next")
else None
)
pagination = generate_pagination_pages(
jobs.get("total", {}), jobs.get("page_size", {}), page
)
return prev_page, next_page, pagination
def generate_job_dict(jobs):
return [
{
"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"],
"time_sent_data_value": convert_time_unixtimestamp(
job["processing_finished"]
if job["processing_finished"]
else (
job["processing_started"]
if job["processing_started"]
else job["created_at"]
)
),
"processing_finished": job["processing_finished"],
"processing_started": job["processing_started"],
"created_by": job["created_by"],
"template_name": job["template_name"],
"delivered_count": next(
(
stat["count"]
for stat in job.get("statistics", [])
if stat["status"] == "delivered"
),
None,
),
"failed_count": next(
(
stat["count"]
for stat in job.get("statistics", [])
if stat["status"] == "failed"
),
None,
),
}
for job in jobs["data"]
]

View File

@@ -1,25 +1,22 @@
import calendar
from collections import defaultdict
from datetime import datetime
from functools import partial
from itertools import groupby
from flask import Response, abort, jsonify, render_template, request, session, url_for
from flask_login import current_user
from flask_socketio import emit
from werkzeug.utils import redirect
from app import (
billing_api_client,
current_service,
job_api_client,
notification_api_client,
service_api_client,
socketio,
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,
@@ -34,38 +31,6 @@ from app.utils.user import user_has_permissions
from notifications_utils.recipients import format_phone_number_human_readable
@socketio.on("fetch_daily_stats")
def handle_fetch_daily_stats():
service_id = session.get("service_id")
if service_id:
date_range = get_stats_date_range()
daily_stats = service_api_client.get_service_notification_statistics_by_day(
service_id, start_date=date_range["start_date"], days=date_range["days"]
)
emit("daily_stats_update", daily_stats)
else:
emit("error", {"error": "No service_id provided"})
@socketio.on("fetch_daily_stats_by_user")
def handle_fetch_daily_stats_by_user():
service_id = session.get("service_id")
user_id = session.get("user_id")
if service_id and user_id:
date_range = get_stats_date_range()
daily_stats_by_user = (
service_api_client.get_user_service_notification_statistics_by_day(
service_id,
user_id,
start_date=date_range["start_date"],
days=date_range["days"],
)
)
emit("daily_stats_by_user_update", daily_stats_by_user)
else:
emit("error", {"error": "No service_id or user_id provided"})
@main.route("/services/<uuid:service_id>/dashboard")
@user_has_permissions("view_activity", "send_messages")
def old_service_dashboard(service_id):
@@ -75,6 +40,7 @@ def old_service_dashboard(service_id):
@main.route("/services/<uuid:service_id>")
@user_has_permissions()
def service_dashboard(service_id):
if session.get("invited_user_id"):
session.pop("invited_user_id", None)
session["service_id"] = service_id
@@ -82,19 +48,21 @@ 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"]
notifications_response = notification_api_client.get_notifications_for_service(
service_id
)["notifications"]
service_data_retention_days = 7
aggregate_notifications_by_job = defaultdict(list)
for notification in notifications_response:
job_id = notification.get("job", {}).get("id", None)
if job_id:
aggregate_notifications_by_job[job_id].append(notification)
job_and_notifications = [
jobs = [
{
"job_id": job["id"],
"time_left": get_time_left(job["created_at"]),
@@ -105,24 +73,52 @@ def service_dashboard(service_id):
".view_job", service_id=current_service.id, job_id=job["id"]
),
"created_at": job["created_at"],
"processing_finished": job["processing_finished"],
"processing_started": job["processing_started"],
"processing_finished": job.get("processing_finished"),
"processing_started": job.get("processing_started"),
"notification_count": job["notification_count"],
"created_by": job["created_by"],
"notifications": aggregate_notifications_by_job.get(job["id"], []),
"template_name": job["template_name"],
"original_file_name": job["original_file_name"],
}
for job in job_response
if aggregate_notifications_by_job.get(job["id"], [])
if job["job_status"] != "cancelled"
]
return render_template(
"views/dashboard/dashboard.html",
updates_url=url_for(".service_dashboard_updates", service_id=service_id),
partials=get_dashboard_partials(service_id),
job_and_notifications=job_and_notifications,
jobs=jobs,
service_data_retention_days=service_data_retention_days,
sms_sent=sms_sent,
sms_allowance_remaining=sms_allowance_remaining,
)
@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"]
)
return jsonify(stats)
@main.route("/daily_stats_by_user.json")
def get_daily_stats_by_user():
service_id = session.get("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,
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):
@@ -329,13 +325,19 @@ def aggregate_template_usage(template_statistics, sort_key="count"):
key=lambda x: x["template_id"],
):
template_stats = list(v)
first_stat = template_stats[0] if template_stats else None
templates.append(
{
"template_id": k,
"template_name": template_stats[0]["template_name"],
"template_type": template_stats[0]["template_type"],
"template_name": first_stat.get("template_name"),
"template_type": first_stat.get("template_type"),
"count": sum(s["count"] for s in template_stats),
"created_by": first_stat.get("created_by"),
"created_by_id": first_stat.get("created_by_id"),
"last_used": first_stat.get("last_used"),
"status": first_stat.get("status"),
"template_folder": first_stat.get("template_folder"),
"template_folder_id": first_stat.get("template_folder_id"),
}
)
@@ -408,11 +410,13 @@ def get_dashboard_partials(service_id):
def get_dashboard_totals(statistics):
for msg_type in statistics.values():
msg_type["failed_percentage"] = get_formatted_percentage(
msg_type["failed"], msg_type["requested"]
)
msg_type["show_warning"] = float(msg_type["failed_percentage"]) > 3
return statistics
@@ -471,6 +475,8 @@ def get_months_for_financial_year(year, time_format="%B"):
def get_current_month_for_financial_year(year):
# Setting the timezone here because we need to set it somewhere.
set_timezone()
current_month = datetime.now().month
return current_month

View File

@@ -1,6 +1,15 @@
import os
import secrets
from flask import abort, current_app, redirect, render_template, request, url_for
from flask import (
abort,
current_app,
redirect,
render_template,
request,
session,
url_for,
)
from flask_login import current_user
from app import status_api_client
@@ -23,8 +32,12 @@ def index():
)
url = os.getenv("LOGIN_DOT_GOV_INITIAL_SIGNIN_URL")
# handle unit tests
nonce = secrets.token_urlsafe()
session["nonce"] = nonce
if url is not None:
url = url.replace("NONCE", token)
url = url.replace("NONCE", nonce)
url = url.replace("STATE", token)
return render_template(
"views/signedout.html",

View File

@@ -1,9 +1,20 @@
import csv
import itertools
import json
from collections import OrderedDict
from datetime import datetime
from io import StringIO
from flask import abort, flash, render_template, request, url_for
from flask import (
Response,
abort,
current_app,
flash,
render_template,
request,
session,
url_for,
)
from notifications_python_client.errors import HTTPError
from app import (
@@ -23,6 +34,7 @@ from app.main.forms import (
DateFilterForm,
RequiredDateFilterForm,
)
from app.main.views.send import _send_notification
from app.statistics_utils import (
get_formatted_percentage,
get_formatted_percentage_two_dp,
@@ -70,6 +82,40 @@ def platform_admin():
)
@main.route("/platform-admin/download-all-users")
@user_is_platform_admin
def download_all_users():
# Create a CSV string from the user data
users = user_api_client.get_all_users_detailed()
if len(users) == 0:
return "No data to download."
output = StringIO()
header = ["Name", "Email Address", "Phone Number", "Service"]
fieldnames = ["name", "email_address", "mobile_number", "service"]
writer = csv.DictWriter(
output,
fieldnames=fieldnames,
delimiter=",",
)
# Write custom header
writer.writerow(dict(zip(fieldnames, header)))
for user in users:
user_no_commas = {key: value.replace(",", "") for key, value in user.items()}
if user_no_commas["name"].startswith("e2e"):
continue
writer.writerow(user_no_commas)
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=users.csv"
return response
def is_over_threshold(number, total, threshold):
percentage = number / total * 100 if total else 0
return percentage > threshold
@@ -735,3 +781,80 @@ def _get_user_row(r):
row.append(r["password_changed_at"])
row.append(r["state"])
return row
@main.route(
"/platform-admin/load-test",
methods=["POST", "GET"],
)
@user_is_platform_admin
def load_test():
"""
The load test assumes that a service called 'Test service' exists. It will make
the platform admin a member of this service if the platform is not already. All
messagese will be sent in this service.
"""
service = _find_load_test_service()
_prepare_load_test_service(service)
example_template = _find_example_template(service)
# Simulated success
for _ in range(0, 250):
session["recipient"] = current_app.config["SIMULATED_SMS_NUMBERS"][0]
session["placeholders"] = {
"day of week": "Monday",
"color": "blue",
"phone number": current_app.config["SIMULATED_SMS_NUMBERS"][0],
}
_send_notification(service["id"], example_template["id"])
# Simulated failure
for _ in range(0, 250):
session["recipient"] = current_app.config["SIMULATED_SMS_NUMBERS"][1]
session["placeholders"] = {
"day of week": "Wednesday",
"color": "orange",
"phone number": current_app.config["SIMULATED_SMS_NUMBERS"][1],
}
_send_notification(service["id"], example_template["id"])
# For now, just redirect to the splash page so we know it's done
return render_template(
"views/platform-admin/splash-page.html",
)
def _find_example_template(service):
templates = service_api_client.get_service_templates(service["id"])
templates = templates["data"]
for template in templates:
# template = json.loads(template)
if template["name"] == "Example text message template":
return template
raise Exception("Could not find example template for load test")
def _find_load_test_service():
services = service_api_client.find_services_by_name("Test service")
services = services["data"]
for service in services:
if service["name"] == "Test service":
return service
raise Exception("Could not find 'Test service' for load test")
def _prepare_load_test_service(service):
users = user_api_client.get_all_users()
for user in users:
if user["platform_admin"] == "t":
try:
user_api_client.add_user_to_service(
service["id"], user["id"], ["send messages"]
)
except Exception:
current_app.logger.exception(
"Couldnt add user, may already be part of service"
)
pass

View File

@@ -230,7 +230,12 @@ def set_up_your_profile():
debug_msg(
f"Added user {usr.email_address} to service {invite_data['service_id']}"
)
return redirect(url_for("main.show_accounts_or_dashboard"))
# notify-admin-1766
# redirect new users to templates area of new service instead of dashboard
service_id = invite_data["service_id"]
url = url_for(".service_dashboard", service_id=service_id)
url = f"{url}/templates"
return redirect(url)
elif login_gov_error:
current_app.logger.error(f"login.gov error: {login_gov_error}")

View File

@@ -1,3 +1,4 @@
import os
import time
import uuid
from string import ascii_uppercase
@@ -33,6 +34,7 @@ from app.main.forms import (
SetSenderForm,
get_placeholder_form_instance,
)
from app.main.views.user_profile import set_timezone
from app.models.user import Users
from app.s3_client.s3_csv_client import (
get_csv_metadata,
@@ -941,8 +943,8 @@ def preview_notification(service_id, template_id):
)
@user_has_permissions("send_messages", restrict_admin_usage=True)
def send_notification(service_id, template_id):
scheduled_for = session.pop("scheduled_for", "")
recipient = get_recipient()
if not recipient:
return redirect(
url_for(
@@ -951,53 +953,10 @@ def send_notification(service_id, template_id):
template_id=template_id,
)
)
upload_id = _send_notification(service_id, template_id)
keys = []
values = []
for k, v in session["placeholders"].items():
keys.append(k)
values.append(v)
data = ",".join(keys)
vals = ",".join(values)
data = f"{data}\r\n{vals}"
filename = (
f"one-off-{uuid.uuid4()}.csv" # {current_user.name} removed from filename
)
my_data = {"filename": filename, "template_id": template_id, "data": data}
upload_id = s3upload(service_id, my_data)
# To debug messages that the user reports have not been sent, we log
# the csv filename and the job id. The user will give us the file name,
# so we can search on that to obtain the job id, which we can use elsewhere
# on the API side to find out what happens to the message.
current_app.logger.info(
hilite(
f"One-off file: {filename} job_id: {upload_id} s3 location: service-{service_id}-notify/{upload_id}.csv"
)
)
form = CsvUploadForm()
form.file.data = my_data
form.file.name = filename
check_message_output = check_messages(service_id, template_id, upload_id, 2)
if "You cannot send to" in check_message_output:
return check_messages(service_id, template_id, upload_id, 2)
job_api_client.create_job(
upload_id,
service_id,
scheduled_for=scheduled_for,
template_id=template_id,
original_file_name=filename,
notification_count=1,
valid="True",
)
session.pop("recipient")
session.pop("placeholders")
session.pop("recipient", "")
session.pop("placeholders", "")
# We have to wait for the job to run and create the notification in the database
time.sleep(0.1)
@@ -1045,6 +1004,63 @@ def send_notification(service_id, template_id):
)
def _send_notification(service_id, template_id):
scheduled_for = session.pop("scheduled_for", "")
keys = []
values = []
# Guarantee that the real phone number comes last, because some
# users will have placeholders like "add your second phone number"
# or something like as custom placeholders.
for k, v in session["placeholders"].items():
if k != "phone number":
keys.append(k)
values.append(v)
if "phone number" in session["placeholders"].keys():
keys.append("phone number")
values.append(session["placeholders"]["phone number"])
data = ",".join(keys)
vals = ",".join(values)
data = f"{data}\r\n{vals}"
filename = (
f"one-off-{uuid.uuid4()}.csv" # {current_user.name} removed from filename
)
my_data = {"filename": filename, "template_id": template_id, "data": data}
upload_id = s3upload(service_id, my_data)
# To debug messages that the user reports have not been sent, we log
# the csv filename and the job id. The user will give us the file name,
# so we can search on that to obtain the job id, which we can use elsewhere
# on the API side to find out what happens to the message.
current_app.logger.info(
hilite(
f"One-off file: {filename} job_id: {upload_id} s3 location: service-{service_id}-notify/{upload_id}.csv"
)
)
# For load testing we want to skip these checks. They are doing some fine-grained
# comparison about what is in the preview, but the load test just blast messages
# and doesn't care about the preview.
if os.getenv("NOTIFY_ENVIRONMENT") not in ("development", "staging", "demo"):
form = CsvUploadForm()
form.file.data = my_data
form.file.name = filename
check_message_output = check_messages(service_id, template_id, upload_id, 2)
if "You cannot send to" in check_message_output:
return check_messages(service_id, template_id, upload_id, 2)
job_api_client.create_job(
upload_id,
service_id,
scheduled_for=scheduled_for,
template_id=template_id,
original_file_name=filename,
notification_count=1,
valid="True",
)
return upload_id
def get_email_reply_to_address_from_session():
if session.get("sender_id"):
return current_service.get_email_reply_to_address(session["sender_id"])[
@@ -1075,6 +1091,7 @@ def get_spreadsheet_column_headings_from_template(template):
def get_recipient():
set_timezone()
if {"recipient", "placeholders"} - set(session.keys()):
return None

View File

@@ -1,4 +1,6 @@
import json
import os
import secrets
import time
import uuid
@@ -12,6 +14,7 @@ from flask import (
redirect,
render_template,
request,
session,
url_for,
)
from flask_login import current_user
@@ -28,21 +31,19 @@ from app.utils.user import is_gov_user
from notifications_utils.url_safe_token import generate_token
def _reformat_keystring(orig):
new_keystring = orig.replace("-----BEGIN PRIVATE KEY-----", "")
new_keystring = new_keystring.replace("-----END PRIVATE KEY-----", "")
new_keystring = new_keystring.strip()
new_keystring = new_keystring.replace(" ", "\n")
new_keystring = "\n".join(
["-----BEGIN PRIVATE KEY-----", new_keystring, "-----END PRIVATE KEY-----"]
)
new_keystring = f"{new_keystring}\n"
def _reformat_keystring(orig): # pragma: no cover
arr = orig.split("-----")
begin = arr[1]
end = arr[3]
middle = arr[2].strip()
new_keystring = f"-----{begin}-----\n{middle}\n-----{end}-----\n"
return new_keystring
def _get_access_token(code, state):
def _get_access_token(code, state): # pragma: no cover
client_id = os.getenv("LOGIN_DOT_GOV_CLIENT_ID")
access_token_url = os.getenv("LOGIN_DOT_GOV_ACCESS_TOKEN_URL")
certs_url = os.getenv("LOGIN_DOT_GOV_CERTS_URL")
keystring = os.getenv("LOGIN_PEM")
if " " in keystring:
keystring = _reformat_keystring(keystring)
@@ -63,15 +64,48 @@ def _get_access_token(code, state):
url = f"{base_url}{cli_assert}&{cli_assert_type}&{code_param}&grant_type=authorization_code"
headers = {"Authorization": "Bearer %s" % token}
response = requests.post(url, headers=headers)
if response.json().get("access_token") is None:
response_json = response.json()
try:
encoded_id_token = response_json["id_token"]
except KeyError as e:
# Capture the response json here so it hopefully shows up in error reports
current_app.logger.error(f"Error when getting access token {response.json()}")
raise KeyError(f"'access_token' {response.json()}")
access_token = response.json()["access_token"]
current_app.logger.exception(f"Error when getting id token {response_json}")
raise KeyError(f"'access_token' {response.json()}") from e
# Getting Login.gov signing keys for unpacking the id_token correctly.
jwks = requests.get(certs_url).json()
public_keys = {
jwk["kid"]: {
"key": jwt.algorithms.RSAAlgorithm.from_jwk(json.dumps(jwk)),
"algo": jwk["alg"],
}
for jwk in jwks["keys"]
}
kid = jwt.get_unverified_header(encoded_id_token)["kid"]
pub_key = public_keys[kid]["key"]
algo = public_keys[kid]["algo"]
id_token = jwt.decode(
encoded_id_token, pub_key, audience=client_id, algorithms=[algo]
)
nonce = id_token["nonce"]
saved_nonce = session.pop("nonce")
if nonce != saved_nonce:
current_app.logger.error(f"Nonce Error: {nonce} != {saved_nonce}")
abort(403)
try:
access_token = response_json["access_token"]
except KeyError as e:
# Capture the response json here so it hopefully shows up in error reports
current_app.logger.exception(
f"Error when getting access token {response.json()} #notify-admin-1505"
)
raise KeyError(f"'access_token' {response.json()}") from e
return access_token
def _get_user_email_and_uuid(access_token):
def _get_user_email_and_uuid(access_token): # pragma: no cover
headers = {"Authorization": "Bearer %s" % access_token}
user_info_url = os.getenv("LOGIN_DOT_GOV_USER_INFO_URL")
user_attributes = requests.get(
@@ -83,14 +117,16 @@ def _get_user_email_and_uuid(access_token):
return user_email, user_uuid
def _do_login_dot_gov():
def _do_login_dot_gov(): # $ pragma: no cover
# start login.gov
code = request.args.get("code")
state = request.args.get("state")
login_gov_error = request.args.get("error")
if login_gov_error:
current_app.logger.error(f"login.gov error: {login_gov_error}")
current_app.logger.error(
f"login.gov error: {login_gov_error} #notify-admin-1505"
)
raise Exception(f"Could not login with login.gov {login_gov_error}")
elif code and state:
@@ -100,12 +136,15 @@ def _do_login_dot_gov():
user_email, user_uuid = _get_user_email_and_uuid(access_token)
if not is_gov_user(user_email):
current_app.logger.error(
"invited user has a non-government email address."
"invited user has a non-government email address. #notify-admin-1505"
)
flash("You must use a government email address.")
abort(403)
redirect_url = request.args.get("next")
user = user_api_client.get_user_by_uuid_or_email(user_uuid, user_email)
current_app.logger.info(
f"Retrieved user {user['id']} from db #notify-admin-1505"
)
# Check if the email needs to be revalidated
is_fresh_email = is_less_than_days_ago(
@@ -115,16 +154,17 @@ def _do_login_dot_gov():
return verify_email(user, redirect_url)
usr = User.from_email_address(user["email_address"])
current_app.logger.info(f"activating user {usr.id} #notify-admin-1505")
activate_user(usr.id)
except BaseException as be: # noqa B036
current_app.logger.error(be)
current_app.logger.exception(f"Error signing in: {be} #notify-admin-1505 ")
error(401)
return redirect(url_for("main.show_accounts_or_dashboard", next=redirect_url))
# end login.gov
def verify_email(user, redirect_url):
def verify_email(user, redirect_url): # pragma: no cover
user_api_client.send_verify_code(user["id"], "email", None, redirect_url)
title = "Email resent" if request.args.get("email_resent") else "Check your email"
redirect_url = request.args.get("next")
@@ -133,19 +173,35 @@ def verify_email(user, redirect_url):
)
def _handle_e2e_tests(redirect_url):
current_app.logger.warning("E2E TESTS ARE ENABLED.")
current_app.logger.warning(
"If you are getting a 404 on signin, comment out E2E vars in .env file!"
)
user = user_api_client.get_user_by_email(os.getenv("NOTIFY_E2E_TEST_EMAIL"))
activate_user(user["id"])
return redirect(url_for("main.show_accounts_or_dashboard", next=redirect_url))
def _handle_e2e_tests(redirect_url): # pragma: no cover
try:
current_app.logger.warning("E2E TESTS ARE ENABLED.")
current_app.logger.warning(
"If you are getting a 404 on signin, comment out E2E vars in .env file!"
)
user = user_api_client.get_user_by_email(os.getenv("NOTIFY_E2E_TEST_EMAIL"))
activate_user(user["id"])
return redirect(
url_for(
"main.show_accounts_or_dashboard",
next="EMAIL_IS_OK",
)
)
except Exception as e:
stre = str(e)
stre = stre.replace(" ", "_")
# Trying to get a message back to playwright somehow since we can't see the admin logs
return redirect(url_for(f"https://{stre}"))
@main.route("/sign-in", methods=(["GET", "POST"]))
@hide_from_search_engines
def sign_in():
def sign_in(): # pragma: no cover
redirect_url = request.args.get("next")
if os.getenv("NOTIFY_E2E_TEST_EMAIL"):
return _handle_e2e_tests(None)
# If we have to revalidated the email, send the message
# via email and redirect to the "verify your email page"
# and don't proceed further with login
@@ -157,11 +213,6 @@ def sign_in():
):
return email_verify_template
redirect_url = request.args.get("next")
if os.getenv("NOTIFY_E2E_TEST_EMAIL"):
return _handle_e2e_tests(redirect_url)
if current_user and current_user.is_authenticated:
if redirect_url and is_safe_redirect_url(redirect_url):
return redirect(redirect_url)
@@ -173,10 +224,15 @@ def sign_in():
current_app.config["DANGEROUS_SALT"],
)
url = os.getenv("LOGIN_DOT_GOV_INITIAL_SIGNIN_URL")
nonce = secrets.token_urlsafe()
session["nonce"] = nonce
# handle unit tests
if url is not None:
url = url.replace("NONCE", token)
url = url.replace("NONCE", nonce)
url = url.replace("STATE", token)
return render_template(
"views/signin.html",
again=bool(redirect_url),
@@ -185,5 +241,5 @@ def sign_in():
@login_manager.unauthorized_handler
def sign_in_again():
def sign_in_again(): # pragma: no cover
return redirect(url_for("main.sign_in", next=request.path))

View File

@@ -29,6 +29,7 @@ from app.main.forms import (
TwoFactorForm,
)
from app.models.user import User
from app.utils import hilite
from app.utils.user import user_is_gov_user, user_is_logged_in
from notifications_utils.url_safe_token import check_token
@@ -189,32 +190,19 @@ def user_profile_mobile_number_delete():
@main.route("/user-profile/mobile-number/authenticate", methods=["GET", "POST"])
@user_is_logged_in
def user_profile_mobile_number_authenticate():
# Validate password for form
def _check_password(pwd):
return user_api_client.verify_password(current_user.id, pwd)
form = ConfirmPasswordForm(_check_password)
if NEW_MOBILE not in session:
return redirect(url_for(".user_profile_mobile_number"))
if form.validate_on_submit():
session[NEW_MOBILE_PASSWORD_CONFIRMED] = True
current_user.send_verify_code(to=session[NEW_MOBILE])
create_mobile_number_change_event(
user_id=current_user.id,
updated_by_id=current_user.id,
original_mobile_number=current_user.mobile_number,
new_mobile_number=session[NEW_MOBILE],
)
return redirect(url_for(".user_profile_mobile_number_confirm"))
return render_template(
"views/user-profile/authenticate.html",
thing="mobile number",
form=form,
back_link=url_for(".user_profile_mobile_number_confirm"),
session[NEW_MOBILE_PASSWORD_CONFIRMED] = True
current_user.send_verify_code(to=session[NEW_MOBILE])
create_mobile_number_change_event(
user_id=current_user.id,
updated_by_id=current_user.id,
original_mobile_number=current_user.mobile_number,
new_mobile_number=session[NEW_MOBILE],
)
return redirect(url_for(".user_profile_mobile_number_confirm"))
@main.route("/user-profile/mobile-number/confirm", methods=["GET", "POST"])
@@ -288,3 +276,15 @@ def user_profile_disable_platform_admin_view():
return render_template(
"views/user-profile/disable-platform-admin-view.html", form=form
)
def set_timezone():
# Cookie is set in dashboard.html on page load
try:
timezone = request.cookies.get("timezone", "US/Eastern")
current_app.logger.debug(hilite(f"User's timezone is {timezone}"))
serialized_user = current_user.serialize()
if serialized_user["preferred_timezone"] is not timezone:
current_user.update(preferred_timezone=timezone)
except Exception:
current_app.logger.exception(hilite("Can't get timezone"))

View File

@@ -38,6 +38,7 @@ def verify_email(token):
current_app.config["EMAIL_EXPIRY_SECONDS"],
)
except SignatureExpired:
current_app.logger.error("Email link expired #notify-admin-1505")
flash(
"The link in the email we sent you has expired. We've sent you a new one."
)
@@ -50,6 +51,9 @@ def verify_email(token):
abort(404)
if user.is_active:
current_app.logger.error(
f"User is using an invite link but is already logged in {user.id} #notify-admin-1505"
)
flash("That verification link has expired.")
return redirect(url_for("main.sign_in"))
@@ -59,6 +63,7 @@ def verify_email(token):
user.send_verify_code()
session["user_details"] = {"email": user.email_address, "id": user.id}
current_app.logger.info(f"Email verified for user {user.id} #notify-admin-1505")
return redirect(url_for("main.verify"))
@@ -78,5 +83,7 @@ def activate_user(user_id):
return redirect(url_for("main.organization_dashboard", org_id=organization_id))
else:
activated_user = user.activate()
current_app.logger.info(f"Activated user {user.id} #notify-admin-1505")
activated_user.login()
current_app.logger.info(f"Logged in user {user.id} #notify-admin-1505")
return redirect(url_for("main.add_service", first="first"))

View File

@@ -1,3 +1,4 @@
import os
from datetime import datetime
from flask import abort, current_app, request, session
@@ -58,6 +59,7 @@ class User(JSONModel, UserMixin):
super().__init__(_dict)
self.permissions = _dict.get("permissions", {})
self._platform_admin = _dict["platform_admin"]
self.preferred_timezone = _dict.get("preferred_timezone", "America/New_York")
@classmethod
def from_id(cls, user_id):
@@ -218,6 +220,14 @@ class User(JSONModel, UserMixin):
def has_permissions(
self, *permissions, restrict_admin_usage=False, allow_org_user=False
):
# TODO need this for load test, but breaks unit tests
if self.platform_admin and os.getenv("NOTIFY_ENVIRONMENT") in (
"development",
"staging",
"demo",
):
return True
unknown_permissions = set(permissions) - all_ui_permissions
if unknown_permissions:
raise TypeError(

View File

@@ -123,6 +123,7 @@ class HeaderNavigation(Navigation):
"get_billing_report",
"get_users_report",
"get_daily_volumes",
"download_all_users",
"get_daily_sms_provider_volumes",
"get_volumes_by_service",
"organizations",
@@ -153,6 +154,9 @@ class HeaderNavigation(Navigation):
class MainNavigation(Navigation):
mapping = {
"activity": {
"all_jobs_activity",
},
"dashboard": {
"conversation",
"inbox",

View File

@@ -56,6 +56,9 @@ class NotifyAdminAPIClient(BaseAPIClient):
):
abort(403)
def is_calling_signin_url(self, arg):
return arg.startswith("('/user")
def check_inactive_user(self, *args):
still_signing_in = False
@@ -64,14 +67,7 @@ class NotifyAdminAPIClient(BaseAPIClient):
# and we only want to check the first arg
for arg in args:
arg = str(arg)
if (
"get-login-gov-user" in arg
or "user/email" in arg
or "/activate" in arg
or "/email-code" in arg
or "/verify/code" in arg
or "/user" in arg
):
if self.is_calling_signin_url(arg):
still_signing_in = True
# This seems to be a weird edge case that happens intermittently with invites

View File

@@ -27,9 +27,7 @@ class JobApiClient(NotifyAdminAPIClient):
def get_job(self, service_id, job_id):
params = {}
job = self.get(
url="/service/{}/job/{}".format(service_id, job_id), params=params
)
job = self.get(url=f"/service/{service_id}/job/{job_id}", params=params)
return job
@@ -40,13 +38,14 @@ class JobApiClient(NotifyAdminAPIClient):
if statuses is not None:
params["statuses"] = ",".join(statuses)
return self.get(url="/service/{}/job".format(service_id), params=params)
job = self.get(url=f"/service/{service_id}/job", params=params)
return job
def get_uploads(self, service_id, limit_days=None, page=1):
params = {"page": page}
if limit_days is not None:
params["limit_days"] = limit_days
return self.get(url="/service/{}/upload".format(service_id), params=params)
return self.get(url=f"/service/{service_id}/upload", params=params)
def has_sent_previously(
self, service_id, template_id, template_version, original_file_name

View File

@@ -2,6 +2,7 @@ from flask import current_app
from notifications_python_client.errors import HTTPError
from app.notify_client import NotifyAdminAPIClient, cache
from app.utils import hilite
from app.utils.user_permissions import translate_permissions_from_ui_to_db
ALLOWED_ATTRIBUTES = {
@@ -109,13 +110,14 @@ class UserApiClient(NotifyAdminAPIClient):
raise
def send_verify_code(self, user_id, code_type, to, next_string=None):
data = {"to": to}
if next_string:
data["next"] = next_string
if code_type == "email":
data["email_auth_link_host"] = self.admin_url
endpoint = "/user/{0}/{1}-code".format(user_id, code_type)
current_app.logger.warn(f"Sending verify_code {code_type} to {user_id}")
current_app.logger.warn(hilite(f"Sending verify_code {code_type} to {user_id}"))
self.post(endpoint, data=data)
def send_verify_email(self, user_id, to):
@@ -157,6 +159,10 @@ class UserApiClient(NotifyAdminAPIClient):
endpoint = "/user"
return self.get(endpoint)["data"]
def get_all_users_detailed(self):
endpoint = "/user/report-all-users"
return self.get(endpoint)["data"]
@cache.delete("service-{service_id}")
@cache.delete("service-{service_id}-template-folders")
@cache.delete("user-{user_id}")

View File

@@ -10,7 +10,7 @@
{% block per_page_title %}{% endblock %} Notify.gov
{% endblock %}
</title>
{% include "new/components/head.html" %}
{% include "components/head.html" %}
</head>
<body class="usa-template__body {{ bodyClasses }}">
<script nonce="{{ csp_nonce() }}">document.body.className = ((document.body.className) ? document.body.className + ' js-enabled' : 'js-enabled');</script>
@@ -32,8 +32,8 @@
{% block header %}
{% include 'new/components/usa_banner.html' %}
{% include 'new/components/header.html' %}
{% include 'components/usa_banner.html' %}
{% include 'components/header.html' %}
{% endblock %}
{% block main %}
@@ -47,7 +47,7 @@
{% endblock %}
{% block content %}
{% block flash_messages %}
{% include 'new/components/flash_messages.html' %}
{% include 'components/flash_messages.html' %}
{% endblock %}
{% block maincolumn_content %}
<div class="grid-row">
@@ -78,7 +78,7 @@
{% set meta_suffix = "Built by the " + long_link + commit_hash %}
{% endif %}
{% include "new/components/footer.html" %}
{% include "components/footer.html" %}
{% if current_user.is_authenticated %}
{% block sessionUserWarning %}

View File

@@ -1,2 +1,4 @@
<a href="{%- if params.href %}{{ params.href }}{% else %}#{% endif -%}" class="usa-link usa-back-link display-inline-flex margin-bottom-3 {%- if params.classes %} {{ params.classes }}{% endif -%}"
{%- for attribute, value in params.attributes %} {{attribute}}="{{value}}"{% endfor %}>{{ (params.html | safe if params.html else (params.text if params.text else 'Back')) }}</a>
<nav class="usa-breadcrumb" aria-label="Breadcrumb">
<a href="{{ params.href or '#' }}" class="usa-link usa-back-link display-inline-flex {{ params.classes or '' }}"
{%- for attribute, value in params.attributes %} {{ attribute }}="{{ value }}" {% endfor %}>{{ params.html | safe or params.text or 'Back' }}</a>
</nav>

View File

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

View File

@@ -1,115 +0,0 @@
[
{
"name": "meta",
"type": "object",
"required": false,
"description": "Object containing options for the meta navigation.",
"params": [
{
"name": "visuallyHiddenTitle",
"type": "string",
"description": "Title for a meta item section, which defaults to Support links"
},
{
"name": "html",
"type": "string",
"description": "HTML to add to the meta section of the footer, which will appear below any links specified using meta.items."
},
{
"name": "text",
"type": "string",
"description": "Text to add to the meta section of the footer, which will appear below any links specified using meta.items. If meta.html is specified, this option is ignored."
},
{
"name": "items",
"type": "array",
"required": false,
"description": "Array of items for use in the meta section of the footer.",
"params": [
{
"name": "text",
"type": "string",
"required": false,
"description": "List item text in the meta section of the footer."
},
{
"name": "href",
"type": "string",
"required": false,
"description": "List item href attribute in the meta section of the footer."
},
{
"name": "attributes",
"type": "object",
"required": false,
"description": "HTML attributes (for example data attributes) to add to the anchor in the footer meta section."
}
]
}
]
},
{
"name": "navigation",
"type": "array",
"required": false,
"description": "Array of items for use in the navigation section of the footer.",
"params": [
{
"name": "title",
"type": "string",
"required": false,
"description": "Title for a section"
},
{
"name": "columns",
"type": "integer",
"required": false,
"description": "Amount of columns to display items in navigation section of the footer."
},
{
"name": "items",
"type": "array",
"required": false,
"description": "Array of items to display in the list in navigation section of the footer.",
"params": [
{
"name": "text",
"type": "string",
"required": false,
"description": "List item text in the navigation section of the footer."
},
{
"name": "href",
"type": "string",
"required": false,
"description": "List item href attribute in the navigation section of the footer. Both `text` and `href` attributes need to be present to create a link."
},
{
"name": "attributes",
"type": "object",
"required": false,
"description": "HTML attributes (for example data attributes) to add to the anchor in the footer navigation section."
}
]
}
]
},
{
"name": "containerClasses",
"type": "string",
"required": false,
"description": "Classes that can be added to the inner container, useful if you want to make the footer full width."
},
{
"name": "classes",
"type": "string",
"required": false,
"description": "Classes to add to the footer component container."
},
{
"name": "attributes",
"type": "object",
"required": false,
"description": "HTML attributes (for example data attributes) to add to the footer component container."
}
]

View File

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

View File

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

View File

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

View File

@@ -1,88 +0,0 @@
[
{
"name": "homepageUrl",
"type": "string",
"required": false,
"description": "The url of the homepage. Defaults to /"
},
{
"name": "assetsPath",
"type": "string",
"required": false,
"description": "The public path for the assets folder. If not provided it defaults to /assets/images"
},
{
"name": "productName",
"type": "string",
"required": false,
"description": "Header title that is placed next to GOV.UK. Used for product names (i.e. Pay, Verify)"
},
{
"name": "serviceName",
"type": "string",
"required": false,
"description": "Header title that is placed next to GOV.UK. Used for product names (i.e. Pay, Verify)"
},
{
"name": "serviceUrl",
"type": "string",
"required": false,
"description": "Url for the service name anchor."
},
{
"name": "navigation",
"type": "array",
"required": false,
"description": "An array of navigation item objects.",
"params": [
{
"name": "text",
"type": "string",
"required": false,
"description": "Text of the navigation item."
},
{
"name": "href",
"type": "string",
"required": false,
"description": "Url of the navigation item anchor. Both `href` and `text` attributes for navigation items need to be provided to create an item."
},
{
"name": "active",
"type": "boolean",
"required": false,
"description": "Flag to mark the navigation item as active or not."
},
{
"name": "attributes",
"type": "object",
"required": false,
"description": "HTML attributes (for example data attributes) to add to the navigation item anchor."
}
]
},
{
"name": "navigationClasses",
"type": "string",
"required": false,
"description": "Classes for the navigation section of the header."
},
{
"name": "containerClasses",
"type": "string",
"required": false,
"description": "Classes for the container, useful if you want to make the header fixed width."
},
{
"name": "classes",
"type": "string",
"required": false,
"description": "Classes to add to the header container."
},
{
"name": "attributes",
"type": "object",
"required": false,
"description": "HTML attributes (for example data attributes) to add to the header container."
}
]

View File

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

View File

@@ -1,105 +0,0 @@
<!-- usa banner -->
<section class="usa-banner site-banner" aria-label="Official website of the United States government,,,,,,">
<div class="usa-accordion">
<header class="usa-banner__header">
<div class="usa-banner__inner">
<div class="grid-col-auto">
<img aria-hidden="true" class="usa-banner__header-flag" src="/static/img/us_flag_small.png" alt="" />
</div>
<div class="grid-col-fill tablet:grid-col-auto" aria-hidden="true">
<p class="usa-banner__header-text">An official website of the United States government</p>
<p class="usa-banner__header-action">Heres how you know</p>
</div>
<button type="button" class="usa-accordion__button usa-banner__button" aria-expanded="false" aria-controls="gov-banner">
<span class="usa-banner__button-text">Heres how you know</span>
</button>
</div>
</header>
<div class="usa-banner__content usa-accordion__content" id="gov-banner" hidden="">
<div class="grid-row grid-gap-lg">
<div class="usa-banner__guidance tablet:grid-col-6">
<img class="usa-banner__icon usa-media-block__img" src="/static/img/icon-dot-gov.svg" role="img" alt="" aria-hidden="true">
<div class="usa-media-block__body">
<p>
<strong>Official websites use .gov</strong>
<br>
A <strong>.gov</strong> website belongs to an official government organization in the United States.
</p>
</div>
</div>
<div class="usa-banner__guidance tablet:grid-col-6">
<img class="usa-banner__icon usa-media-block__img" src="/static/img/icon-https.svg" role="img" alt="" aria-hidden="true">
<div class="usa-media-block__body">
<p>
<strong>Secure .gov websites use HTTPS</strong>
<br>
A <strong>lock</strong> ( <span class="icon-lock"><svg xmlns="http://www.w3.org/2000/svg" width="52" height="64" viewBox="0 0 52 64" class="usa-banner__lock-image" role="img" aria-labelledby="banner-lock-description" focusable="false"><title id="banner-lock-title">Lock</title><desc id="banner-lock-description">Locked padlock</desc><path fill="#000000" fill-rule="evenodd" d="M26 0c10.493 0 19 8.507 19 19v9h3a4 4 0 0 1 4 4v28a4 4 0 0 1-4 4H4a4 4 0 0 1-4-4V32a4 4 0 0 1 4-4h3v-9C7 8.507 15.507 0 26 0zm0 8c-5.979 0-10.843 4.77-10.996 10.712L15 19v9h22v-9c0-6.075-4.925-11-11-11z"></path></svg></span> ) or <strong>https://</strong> means youve safely connected to the .gov website. Share sensitive information only on official, secure websites.
</p>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- usa header -->
<header class="usa-header usa-header--extended">
<div class="usa-nav-container">
<div class="usa-navbar">
<div class="usa-logo display-flex flex-align-center flex-justify" id="-logo">
<div class="logo-img display-flex">
<a href="/">
<span class="usa-sr-only">Notify.gov logo</span>
<image src="{{ params.assetsPath | default('/static/images') }}/notify-logo.png" alt="Notify.gov logo" xlink:href=""
class="usa-flag-logo margin-right-1"></image>
</a>
</div>
{% if params.navigation %}
<button type="button" class="usa-menu-btn">Menu</button>
{% endif %}
</div>
</div>
<nav aria-label="Primary navigation" class="usa-nav">
<div class="usa-nav__inner">
<button type="button" class="usa-nav__close">
<img src="/static/images/usa-icons/close.svg" role="img" alt="Close" />
</button>
<ul class="usa-nav__primary usa-accordion margin-right-1">
{% for item in params.navigation %}
{% if item.href and item.text %}
<li class="usa-nav__primary-item{{ ' is-current' if item.active }}">
<a class="usa-nav__link {{ ' usa-current' if item.active }}" href="{{ item.href }}" {% for attribute, value in
item.attributes %} {{attribute}}="{{value}}" {% endfor %}>
<span>{{ item.text }}</span>
</a>
</li>
{% endif %}
{% endfor %}
</ul>
<div class="usa-nav__secondary margin-bottom-6">
<ul class="usa-nav__secondary-links">
{% for item in params.secondaryNavigation %}
{% if item.href and item.text %}
<li class="usa-nav__secondary-item{{ ' is-current' if item.active }}">
<a class="usa-nav__link {{ ' usa-current' if item.active }}" href="{{ item.href }}" {% for attribute, value in
item.attributes %} {{attribute}}="{{value}}" {% endfor %}>
<span>{{ item.text }}</span>
</a>
</li>
{% endif %}
{% endfor %}
</ul>
<!-- <section aria-label="Search component">
<form class="usa-search usa-search--small margin-bottom-2" role="search">
<label class="usa-sr-only" for="search-field">Search</label>
<input class="usa-input" id="search-field" type="search" name="search" />
<button class="usa-button" type="submit">
<img src="/static/images/usa-icons-bg/search--white.svg" class="usa-search__submit-icon" alt="Search" />
</button>
</form>
</section> -->
</div>
</div>
</nav>
</div>
</header>

View File

@@ -11,17 +11,16 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<link rel="shortcut icon" href="{{ assetPath | default('/assets') }}/images/favicon.ico" />
<link rel="icon" type="image/png" sizes="32x32" href="{{ assetPath | default('/assets') }}/images/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="{{ assetPath | default('/assets') }}/images/favicon-16x16.png" />
<link rel="mask-icon" href="{{ assetPath | default('/assets') }}/images/safari-pinned-tab.svg" color="#5bbad5">
<link rel="apple-touch-icon" sizes="180x180" href="{{ assetPath | default('/assets') }}/images/apple-touch-icon.png">
<link rel="manifest" href="/site.webmanifest">
<link rel="shortcut icon" href="{{ assetPath | default('/static') }}/images/favicon.ico" />
<link rel="icon" type="image/png" sizes="32x32" href="{{ assetPath | default('/static') }}/images/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="{{ assetPath | default('/static') }}/images/favicon-16x16.png" />
<link rel="mask-icon" href="{{ assetPath | default('/static') }}/images/safari-pinned-tab.svg" color="#5bbad5">
<link rel="apple-touch-icon" sizes="180x180" href="{{ assetPath | default('/static') }}/images/apple-touch-icon.png">
<link rel="manifest" href="{{ asset_url('images/site.webmanifest') }}">
<meta name="msapplication-TileColor" content="#da532c">
<link href="{{ assetPath | default('/assets') }}/images/notify-dark-favicon.png" rel="icon" media="(prefers-color-scheme: dark)">
<link href="{{ assetPath | default('/static') }}/images/notify-dark-favicon.png" rel="icon" media="(prefers-color-scheme: dark)">
<meta name="theme-color" content="#ffffff">
<link rel="stylesheet" media="screen" href="{{ asset_url('css/styles.css') }}" />
{% block extra_stylesheets %}{% endblock %}
@@ -31,7 +30,7 @@
<meta property="og:image" content="/static/images/notify-og-image.png">
{# google #}
<script type="text/javascript" src="{{ asset_url('js/gtm_head.js') }}"></script>
<meta name="google-site-verification" content="niWnSqImOWz6mVQTYqNb5tFK8HaKSB4b3ED4Z9gtUQ0" />
<meta name="google-site-verification" content="niWnSqImOWz6mVQTYqNb5tFK8HaKSB4b3ED4Z9gtUQ0" /> {# pragma: allowlist secret #}
{% if g.hide_from_search_engines %}
<meta name="robots" content="noindex" />
{% endif %}

View File

@@ -1,4 +1,3 @@
{# setting navigation and secondarynavigation #}
{% 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')},
@@ -30,7 +29,6 @@
{% endif %}
{% endif %}
{# usa header #}
<header class="usa-header usa-header--extended">
<div class="usa-nav-container">
<div class="usa-navbar">
@@ -50,7 +48,7 @@
<nav aria-label="Primary navigation" class="usa-nav">
<div class="usa-nav__inner">
<button type="button" class="usa-nav__close">
<img src="/static/images/usa-icons/close.svg" role="img" alt="Close" />
<img src="{{ asset_url('img/usa-icons/close.svg') }}" role="img" alt="Close" />
</button>
<ul class="usa-nav__primary usa-accordion margin-right-1">
{% for item in navigation %}

View File

@@ -1,26 +1,18 @@
{% if help %}
{% include 'partials/tour.html' %}
{% else %}
<nav class="nav margin-bottom-4">
<nav id="nav-main-nav" aria-label="Main navigation" class="nav margin-bottom-4">
<a class="usa-button margin-top-1 margin-bottom-5 width-full"
href="{{ url_for('.choose_template', service_id=current_service.id) }}">Send messages</a>
<ul class="usa-sidenav">
{% if current_user.has_permissions() %}
{% if current_user.has_permissions('view_activity') %}
<li class="usa-sidenav__item"><a class="{{ main_navigation.is_selected('dashboard') }}" href="{{ url_for('.service_dashboard', service_id=current_service.id) }}">Dashboard</a></li>
<li class="usa-sidenav__item"><a class="{{ main_navigation.is_selected('activity') }}" href="{{ url_for('.all_jobs_activity', service_id=current_service.id) }}">Activity</a></li>
{% endif %}
{% if not current_user.has_permissions('view_activity') %}
<li class="usa-sidenav__item"><a class="{{ casework_navigation.is_selected('sent-messages') }}" href="{{ url_for('.view_notifications', service_id=current_service.id, status='sending,delivered,failed') }}">Sent messages</a></li>
{% endif %}
{% if current_user.has_permissions('manage_service', allow_org_user=True) %}
{# <li class="usa-sidenav__item"><a class="{{ main_navigation.is_selected('usage') }}" href="{{ url_for('.usage', service_id=current_service.id) }}">Usage</a></li> #}
{% endif %}
<!-- {% if current_user.has_permissions('manage_api_keys', 'manage_service') %}
<li class="usa-sidenav__item"><a class="{{ main_navigation.is_selected('settings') }}" href="{{ url_for('.service_settings', service_id=current_service.id) }}">Settings</a></li>
{% endif %} -->
{% if current_user.has_permissions('manage_api_keys') %}
<!-- <li><a class="usa-link{{ main_navigation.is_selected('api-integration') }}" href="{{ url_for('.api_integration', service_id=current_service.id) }}">API integration</a></li> -->
{% endif %}
{% elif current_user.has_permissions(allow_org_user=True) %}
<li class="usa-sidenav__item"><a class="usa-link{{ main_navigation.is_selected('usage') }}" href="{{ url_for('.usage', service_id=current_service.id) }}">Usage</a></li>
<li class="usa-sidenav__item"><a class="usa-link{{ main_navigation.is_selected('team-members') }}" href="{{ url_for('.manage_users', service_id=current_service.id) }}">Team members</a></li>

View File

@@ -1,4 +1,4 @@
<nav class="nav margin-bottom-4">
<nav id="nav-org-nav" aria-label="Organization navigation" class="nav margin-bottom-4">
<ul class="usa-sidenav">
<li class="usa-sidenav__item"><a class="usa-link{{ org_navigation.is_selected('dashboard') }}" href="{{ url_for('.organization_dashboard', org_id=current_org.id) }}">Usage</a></li>
<li class="usa-sidenav__item"><a class="usa-link{{ org_navigation.is_selected('team-members') }}" href="{{ url_for('.manage_org_users', org_id=current_org.id) }}">Team members</a></li>

View File

@@ -1,3 +1,4 @@
<nav id="nav-service-nav" aria-label="Service navigation">
<div class="navigation-service margin-top-5 display-flex flex-align-end flex-justify border-bottom padding-bottom-1">
{% if current_service.organization_id %}
{% if current_user.platform_admin or
@@ -13,3 +14,4 @@
</div>
<a href="{{ url_for('main.choose_account') }}" class="usa-link">Switch service</a>
</div>
</nav>

View File

@@ -6,7 +6,7 @@
<thead class="table-field-headings{% if field_headings_visible %}-visible{% endif %}">
<tr>
{% for field_heading in field_headings %}
<th scope="col" class="table-field-heading{% if loop.first %}-first{% endif %}" width="{% if equal_length %}{{ (100 / field_headings|length)|int }}%{% endif %}">
<th class="table-field-heading{% if loop.first %}-first{% endif %}" width="{% if equal_length %}{{ (100 / field_headings|length)|int }}%{% endif %}">
{% if field_headings_visible %}
{{ field_heading }}
{% else %}
@@ -79,9 +79,9 @@
{%- endmacro %}
{% macro row_heading() -%}
<th class="table-field">
<td>
{{ caller() }}
</th>
</td>
{%- endmacro %}
{% macro index_field(text=None, rowspan=None) -%}

View File

@@ -1,5 +1,3 @@
{# usa banner #}
<section class="usa-banner site-banner" aria-label="Official website of the United States government,,,,,,">
<div class="usa-accordion">
<header class="usa-banner__header">

View File

@@ -1,302 +0,0 @@
{% extends "main_template.html" %}
{% from "components/banner.html" import banner %}
{% block headIcons %}
<link rel="shortcut icon" sizes="16x16 32x32 48x48" href="{{ asset_url('images/favicon.ico') }}" type="image/x-icon" />
<link rel="mask-icon" href="{{ asset_url('images/usa-mask-icon.svg') }}" color="{{ themeColor | default('#F0F0F0') }}">
<link rel="apple-touch-icon" sizes="180x180" href="{{ asset_url('images/apple-touch-icon.png') }}">
<link rel="apple-touch-icon" href="{{ asset_url('images/apple-touch-icon.png') }}">
{% endblock %}
{% block head %}
<link rel="stylesheet" media="screen" href="{{ asset_url('css/styles.css') }}" />
{% block extra_stylesheets %}
{% endblock %}
{% if g.hide_from_search_engines %}
<meta name="robots" content="noindex" />
{% endif %}
<meta name="google-site-verification" content="niWnSqImOWz6mVQTYqNb5tFK8HaKSB4b3ED4Z9gtUQ0" />
{% block meta_format_detection %}
<meta name="format-detection" content="telephone=no">
{% endblock %}
{% block meta %}
<meta property="og:site_name" content="Notify.gov">
<meta property="og:image" content="{{ asset_url('images/notify-og-image.png') }}">
{% endblock %}
<script type="text/javascript" src="{{ asset_url('js/gtm_head.js') }}"></script>
{% endblock %}
{% block pageTitle %}
{% block per_page_title %}{% endblock %} Notify.gov
{% endblock %}
{% block bodyStart %}
{% block extra_javascripts_before_body %}
<!-- Google Tag Manager (noscript) -->
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-WX5NGWF"
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->
{% endblock %}
{% endblock %}
{% block header %}
{% if current_user.is_authenticated %}
{% if current_user.platform_admin %}
{% set navigation = [
{
"href": url_for("main.show_accounts_or_dashboard"),
"text": "Current service",
"active": header_navigation.is_selected('accounts-or-dashboard')
},
{
"href": url_for('main.get_started'),
"text": "Using Notify",
"active": header_navigation.is_selected('using_notify')
},
{
"href": url_for('main.features'),
"text": "Features",
"active": header_navigation.is_selected('features')
},
{
"href": url_for('main.platform_admin_splash_page'),
"text": "Platform admin",
"active": header_navigation.is_selected('platform-admin')
},
{
"href": url_for('main.support'),
"text": "Contact us",
"active": header_navigation.is_selected('support')
}
] %}
{% if current_service %}
{% set secondaryNavigation = [
{
"href": url_for('main.service_settings', service_id=current_service.id),
"text": "Settings",
"active": secondary_navigation.is_selected('settings')
},
{
"href": url_for('main.sign_out'),
"text": "Sign out"
}
] %}
{% else %}
{% set secondaryNavigation = [
{
"href": url_for('main.sign_out'),
"text": "Sign out"
}
] %}
{% endif %}
{% else %}
{% set navigation = [
{
"href": url_for("main.show_accounts_or_dashboard"),
"text": "Current service",
"active": header_navigation.is_selected('accounts-or-dashboard')
},
{
"href": url_for('main.get_started'),
"text": "Using Notify",
"active": header_navigation.is_selected('using_notify')
},
{
"href": url_for('main.features'),
"text": "Features",
"active": header_navigation.is_selected('features')
},
{
"href": url_for('main.support'),
"text": "Contact us",
"active": header_navigation.is_selected('support')
},
{
"href": url_for('main.user_profile'),
"text": "User profile",
"active": header_navigation.is_selected('user-profile')
}
] %}
{% if current_service %}
{% set secondaryNavigation = [
{
"href": url_for('main.service_settings', service_id=current_service.id),
"text": "Settings",
"active": secondary_navigation.is_selected('settings')
},
{
"href": url_for('main.sign_out'),
"text": "Sign out"
}
] %}
{% else %}
{% set secondaryNavigation = [
{
"href": url_for('main.sign_out'),
"text": "Sign out"
}
] %}
{% endif %}
{% endif %}
{% else %}
<!-- Add navigation back after pilot -->
{# {% set navigation = [
{
"href": url_for('main.get_started'),
"text": "Using Notify",
"active": header_navigation.is_selected('using_notify')
},
{
"href": url_for('main.features'),
"text": "Features",
"active": header_navigation.is_selected('features')
},
{
"href": url_for('main.support'),
"text": "Contact us",
"active": header_navigation.is_selected('support')
},
{
"href": url_for('main.sign_in'),
"text": "Sign in",
"active": header_navigation.is_selected('sign-in')
}
] %} #}
{% endif %}
{{ usaHeader({
"homepageUrl": url_for('main.show_accounts_or_dashboard'),
"productName": "Notify",
"navigation": navigation,
"navigationClasses": "govuk-header__navigation--end",
"secondaryNavigation": secondaryNavigation,
"assetsPath": asset_path + "images"
}) }}
{% endblock %}
{% block footer %}
<!-- lines 179 - 251 can all be deleted and replaced with {% include "new/components/footer.html" %} -->
{% if current_service and current_service.research_mode %}
{% set meta_suffix = 'Built by the <a href="https://www.gsa.gov/about-us/organization/federal-acquisition-service/technology-transformation-services/tts-solutions" class="usa-link">Technology Transformation Services</a><span id="research-mode" class="research-mode">research mode</span>' %}
{% else %}
{% set commit_hash = ", Latest version: " + config['COMMIT_HASH'] %}
{% set long_link = '<a href="https://www.gsa.gov/about-us/organization/federal-acquisition-service/technology-transformation-services/tts-solutions" class="usa-link">Technology Transformation Services</a>' %}
{% set meta_suffix = "Built by the " + long_link + commit_hash %}
{% endif %}
{{ usaFooter({
"classes": "js-footer",
"navigation": [
{
"title": "About Notify",
"columns": 1,
"items": [
{
"href": url_for("main.features"),
"text": "Features"
},
{
"href": url_for("main.roadmap"),
"text": "Roadmap"
},
{
"href": url_for("main.security"),
"text": "Security"
},
]
},
{
"title": "Using Notify",
"columns": 1,
"items": [
{
"href": url_for("main.get_started"),
"text": "Get started"
},
{
"href": url_for("main.pricing"),
"text": "Pricing"
},
{
"href": url_for("main.trial_mode_new"),
"text": "Trial mode"
},
{
"href": url_for("main.message_status"),
"text": "Delivery status"
},
{
"href": url_for("main.guidance_index"),
"text": "Guidance"
},
{
"href": url_for("main.documentation"),
"text": "API documentation"
}
]
},
{
"title": "Support",
"columns": 1,
"items": [
{
"href": url_for('main.support'),
"text": "Contact us"
}
]
},
],
"meta": {
"items": meta_items,
"html": meta_suffix
}
}) }}
{% if current_user.is_authenticated %}
{% block sessionUserWarning %}
<dialog class="usa-modal" id="sessionTimer" aria-labelledby="sessionTimerHeading" aria-describedby="timerWarning">
<div class="usa-modal__content">
<div class="usa-modal__main">
<h2 class="usa-modal__heading" id="sessionTimerHeading">
Your session will end soon.
<span class="usa-sr-only">Please choose to extend your session or sign out. Your session will expire in 5 minutes or less.</span>
</h2>
<div class="usa-prose">
<p>You have been inactive for too long.
Your session will expire in <span id="timeLeft" role="timer"></span>.
</p>
</div>
<div class="usa-modal__footer">
<ul class="usa-button-group">
<li class="usa-button-group__item">
<button type="button" class="usa-button" id="extendSessionTimer" data-close-modal>
Extend Session
</button>
</li>
<li class="usa-button-group__item">
<button type="button" class="usa-button usa-button--unstyled padding-105 text-center" id="logOutTimer"
data-close-modal>
Sign out
</button>
</li>
</ul>
</div>
</div>
</div>
</dialog>
{% endblock %}
{% endif %}
{% endblock %}
{% 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 %}

View File

@@ -1,28 +0,0 @@
{% extends "base.html" %}
{% from "components/sub-navigation.html" import sub_navigation %}
{% from "components/page-header.html" import page_header %}
{% block per_page_title %}
{{ content_page_title }}
{% endblock %}
{% block maincolumn_content %}
<div class="grid-row">
{% if navigation_links %}
<div class="tablet:grid-col-2 margin-bottom-4">
{{ sub_navigation(navigation_links) }}
</div>
<div class="tablet:grid-col-10 tablet:padding-left-4 usa-prose site-prose">
{% else %}
<div class="tablet:grid-col-10">
{% endif %}
{% block content_column_content %}
{% endblock %}
</div>
</div>
{% endblock %}

View File

@@ -1,32 +0,0 @@
{# This template is an old version #}
{% if help %}
{% include 'partials/tour.html' %}
{% else %}
<nav class="nav margin-bottom-4">
<a class="usa-button margin-top-1 margin-bottom-5 width-full"
href="{{ url_for('.choose_template', service_id=current_service.id) }}">Send messages</a>
<ul class="usa-sidenav">
{% if current_user.has_permissions() %}
{% if current_user.has_permissions('view_activity') %}
<li class="usa-sidenav__item"><a class="{{ main_navigation.is_selected('dashboard') }}" href="{{ url_for('.service_dashboard', service_id=current_service.id) }}">Dashboard</a></li>
{% endif %}
{# <li class="usa-sidenav__item"><a class="{{ main_navigation.is_selected('templates') }}" href="{{ url_for('.choose_template', service_id=current_service.id) }}">Send messages</a></li> #}
{% if not current_user.has_permissions('view_activity') %}
<li class="usa-sidenav__item"><a class="{{ casework_navigation.is_selected('sent-messages') }}" href="{{ url_for('.view_notifications', service_id=current_service.id, status='sending,delivered,failed') }}">Sent messages</a></li>
{% endif %}
{% if current_user.has_permissions('manage_service', allow_org_user=True) %}
{# <li class="usa-sidenav__item"><a class="{{ main_navigation.is_selected('usage') }}" href="{{ url_for('.usage', service_id=current_service.id) }}">Usage</a></li> #}
{% endif %}
<!-- {% if current_user.has_permissions('manage_api_keys', 'manage_service') %}
<li class="usa-sidenav__item"><a class="{{ main_navigation.is_selected('settings') }}" href="{{ url_for('.service_settings', service_id=current_service.id) }}">Settings</a></li>
{% endif %} -->
{% if current_user.has_permissions('manage_api_keys') %}
<!-- <li><a class="usa-link{{ main_navigation.is_selected('api-integration') }}" href="{{ url_for('.api_integration', service_id=current_service.id) }}">API integration</a></li> -->
{% endif %}
{% elif current_user.has_permissions(allow_org_user=True) %}
<li class="usa-sidenav__item"><a class="usa-link{{ main_navigation.is_selected('usage') }}" href="{{ url_for('.usage', service_id=current_service.id) }}">Usage</a></li>
<li class="usa-sidenav__item"><a class="usa-link{{ main_navigation.is_selected('team-members') }}" href="{{ url_for('.manage_users', service_id=current_service.id) }}">Team members</a></li>
{% endif %}
</ul>
</nav>
{% endif %}

View File

@@ -1,70 +0,0 @@
{% from "./components/components/skip-link/macro.njk" import usaSkipLink -%}
{% from "./components/components/header/macro.njk" import usaHeader -%}
{% from "./components/components/footer/macro.njk" import usaFooter -%}
{# specify absolute url for the static assets folder e.g. http://wwww.domain.com/assets #}
{%- set assetUrl = assetUrl | default(assetPath) -%}
<!DOCTYPE html>
<html lang="{{ htmlLang | default('en') }}" class="{{ htmlClasses }}">
<head>
<meta charset="utf-8" />
<title>{% block pageTitle %}Notify.gov{% endblock %}</title>
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<meta name="theme-color" media="(prefers-color-scheme: light)" content="f0f0f0" />
<meta name="theme-color" media="(prefers-color-scheme: dark)" content="1b1b1b" />
{% if config['NR_MONITOR_ON'] %}
{% include "partials/newrelic.html" -%}
{% endif %}
{# Ensure that older IE versions always render with the correct rendering engine #}
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
{% block headIcons %}
<link rel="shortcut icon" href="{{ assetPath | default('/assets') }}/images/favicon.ico" />
<link rel="icon" type="image/png" sizes="32x32"href="{{ assetPath | default('/assets') }}/images/favicon-32x32.png"/>
<link rel="icon" type="image/png" sizes="16x16" href="{{ assetPath | default('/assets') }}/images/favicon-16x16.png" />
<link rel="mask-icon" href="{{ assetPath | default('/assets') }}/images/safari-pinned-tab.svg" color="#5bbad5">
<link rel="apple-touch-icon" sizes="180x180" href="{{ assetPath | default('/assets') }}/images/apple-touch-icon.png">
<link rel="manifest" href="/site.webmanifest">
<meta name="msapplication-TileColor" content="#da532c">
<link href="{{ assetPath | default('/assets') }}/images/notify-dark-favicon.png" rel="icon" media="(prefers-color-scheme: dark)">
<meta name="theme-color" content="#ffffff">
{% endblock %}
{% block head %}{% endblock %}
{# The default og:image is added below head so that scrapers see any custom metatags first, and this is just a fallback #}
{% block meta %}
<meta property="og:site_name" content="Notify.gov">
<meta property="og:image" content="{{ asset_url('images/notify-og-image.png') }}">
{% endblock %}
</head>
<body class="usa-template__body {{ bodyClasses }}">
<script nonce="{{ csp_nonce() }}">document.body.className = ((document.body.className) ? document.body.className + ' js-enabled' : 'js-enabled');</script>
{% block bodyStart %}{% endblock %}
{% block skipLink %}
{{ usaSkipLink({
"href": '#main-content',
"text": 'Skip to main content'
}) }}
{% endblock %}
{% block header %}
{{ usaHeader({}) }}
{% endblock %}
{% block main %}
<div class="grid-container">
{% block beforeContent %}{% endblock %}
<main class="{{ mainClasses }}" id="main-content" role="main">
{% block content %}{% endblock %}
</main>
</div>
{% endblock %}
{% block footer %}
{{ usaFooter({}) }}
{% endblock %}
{% block bodyEnd %}{% endblock %}
</body>
</html>

View File

@@ -1,12 +0,0 @@
{# This template is an old version #}
<nav class="navigation">
<ul>
<li><a class="usa-link{{ org_navigation.is_selected('dashboard') }}" href="{{ url_for('.organization_dashboard', org_id=current_org.id) }}">Usage</a></li>
<li><a class="usa-link{{ org_navigation.is_selected('team-members') }}" href="{{ url_for('.manage_org_users', org_id=current_org.id) }}">Team members</a></li>
{% if current_user.platform_admin %}
<li><a class="usa-link{{ org_navigation.is_selected('settings') }}" href="{{ url_for('.organization_settings', org_id=current_org.id) }}">Settings</a></li>
<li><a class="usa-link{{ org_navigation.is_selected('trial-services') }}" href="{{ url_for('.organization_trial_mode_services', org_id=current_org.id) }}">Trial mode services</a></li>
<li><a class="usa-link{{ org_navigation.is_selected('billing') }}" href="{{ url_for('.organization_billing', org_id=current_org.id) }}">Billing</a></li>
{% endif %}
</ul>
</nav>

View File

@@ -1,35 +0,0 @@
{% extends "base.html" %}
{% block per_page_title %}
{% block org_page_title %}{% endblock %} {{ current_org.name }}
{% endblock %}
{% block main %}
<div class="grid-container">
<div class="navigation-service usa-breadcrumb">
{% if current_user.platform_admin %}
<a href="{{ url_for('.organizations') }}" class="usa-link navigation-organization-link">Organizations</a>
{% endif %}
<div class="navigation-service">
{{ current_org.name }}
</div>
<a href="{{ url_for('main.choose_account') }}" class="usa-link navigation-service">Switch service</a>
</div>
<div class="grid-row">
<div class="grid-col-3">
{% include "org_nav.html" %}
</div>
<div class="grid-col-9">
{% block beforeContent %}
{% block backLink %}{% endblock %}
{% endblock %}
<main class="main" id="main-content" role="main" >
{% block content %}
{% include 'flash_messages.html' %}
{% block maincolumn_content %}{% endblock %}
{% endblock %}
</main>
</div>
</div>
</div>
{% endblock %}

View File

@@ -1,20 +0,0 @@
{# This template is an old version #}
{% macro navigation_service_name(service) %}
<div class="font-body-2xl text-bold">
{{ service.name }}
{% if not service.active %}
<span class="navigation-service-name navigation-service-type--suspended">Suspended</span>
{% endif %}
</div>
{% endmacro %}
<div class="navigation-service margin-top-5 display-flex flex-align-end flex-justify border-bottom padding-bottom-1">
{% if current_service.organization_id %}
{% if current_user.platform_admin or
(current_user.belongs_to_organization(current_service.organization_id) and current_service.live) %}
<a href="{{ url_for('.organization_dashboard', org_id=current_service.organization_id) }}" class="usa-link navigation-organization-link">{{ current_service.organization_name }}</a>
{% endif %}
{% endif %}
{{ navigation_service_name(current_service) }}
<a href="{{ url_for('main.choose_account') }}" class="usa-link">Switch service</a>
</div>

View File

@@ -1,17 +0,0 @@
{# This template is an old version #}
{% if help %}
{% include 'partials/tour.html' %}
{% else %}
<nav class="nav">
<ul class="usa-sidenav">
{# {% if current_user.has_permissions() %} #}
<li class="usa-sidenav__item"><a class="{{ main_navigation.is_selected('settings') }}"
href="{{ url_for('main.service_settings', service_id=current_service.id) }}">General</a></li>
<li class="usa-sidenav__item"><a class="{{ main_navigation.is_selected('user-profile') }}"
href="{{ url_for('main.user_profile', service_id=current_service.id) }}">User profile</a></li>
<li class="usa-sidenav__item"><a class="{{ main_navigation.is_selected('team-members') }}"
href="{{ url_for('main.manage_users', service_id=current_service.id) }}">Team members</a></li>
{# {% endif %} #}
</ul>
</nav>
{% endif %}

View File

@@ -1,35 +0,0 @@
{# This template is an old version #}
{% extends "admin_template.html" %}
{% block per_page_title %}
{% block service_page_title %}{% endblock %} {{ current_service.name }}
{% endblock %}
{% block main %}
<div class="grid-container">
<div class="grid-row margin-top-5">
{% if help %}
<div class="tablet:grid-col-3">
{% else %}
<div class="tablet:grid-col-3 margin-bottom-4">
{% endif %}
{% include "settings_nav.html" %}
</div>
{% if help %}
<div class="tablet:grid-col-8">
{% else %}
<div class="tablet:grid-col-9 tablet:padding-left-4">
{% endif %}
{% block beforeContent %}
{% block backLink %}{% endblock %}
{% endblock %}
<main id="main-content" role="main" class="usa-prose site-prose margin-bottom-10">
{% block content %}
{% include 'flash_messages.html' %}
{% block maincolumn_content %}{% endblock %}
{% endblock %}
</main>
</div>
</div>
</div>
{% endblock %}

View File

@@ -1,36 +0,0 @@
{# This template is an old version #}
{% extends "admin_template.html" %}
{% block per_page_title %}
{% block service_page_title %}{% endblock %} {{ current_service.name }}
{% endblock %}
{% block main %}
<div class="grid-container">
{% include "service_navigation.html" %}
<div class="grid-row margin-top-5">
{% if help %}
<div class="tablet:grid-col-3">
{% else %}
<div class="tablet:grid-col-3">
{% endif %}
{% include "main_nav.html" %}
</div>
{% if help %}
<div class="grid-col-8">
{% else %}
<div class="tablet:grid-col-9 tablet:padding-left-4">
{% endif %}
{% block beforeContent %}
{% block backLink %}{% endblock %}
{% endblock %}
<main id="main-content" role="main" class="usa-prose site-prose margin-bottom-10">
{% block content %}
{% include 'flash_messages.html' %}
{% block maincolumn_content %}{% endblock %}
{% endblock %}
</main>
</div>
</div>
</div>
{% endblock %}

View File

@@ -1,17 +0,0 @@
{% extends "base.html" %}
{% set mainClasses = "margin-top-5 padding-bottom-5" %}
{% block beforeContent %}
{% if current_service and current_service.active and current_user.is_authenticated and current_user.belongs_to_service(current_service.id) %}
<!-- <div class="navigation-service usa-breadcrumb">
<a href="{{ url_for('main.show_accounts_or_dashboard') }}" class="usa-link usa-breadcrumb__link usa-breadcrumb-back-to">Back to {{ current_service.name }}</a>
</div> -->
{% endif %}
{% block backLink %}{% endblock %}
{% endblock %}
{% block content %}
{% include 'flash_messages.html' %}
{% block maincolumn_content %}{% endblock %}
{% endblock %}

View File

@@ -22,7 +22,7 @@
{% else %}
{% if notifications %}
<div class="dashboard-table bottom-gutter-3-2">
<div class="dashboard-table job-status-table table-overflow-x-auto">
{% endif %}
{% if job.still_processing %}
<p class="bottom-gutter hint">
@@ -40,15 +40,16 @@
notifications,
caption=uploaded_file_name,
caption_visible=False,
border_visible=True,
empty_message='No messages to show yet…' if job.awaiting_processing_or_recently_processed else 'These messages have been deleted because they were sent more than {} days ago'.format(service_data_retention_days),
field_headings=[
'Recipient',
'Status'
'Message status'
],
field_headings_visible=False
) %}
{% call row_heading() %}
<a class="usa-link file-list-filename" href="{{ url_for('.view_notification', service_id=current_service.id, notification_id=item.id, from_job=job.id) }}">{{ item.to }}</a>
<a class="usa-link file-list-filename" href="{{ url_for('.view_notification', service_id=current_service.id, notification_id=item.id, from_job=job.id) }}">{{ item.to | format_phone_number_human_readable }}</a>
<p class="file-list-hint">
{{ item.preview_of_content }}
</p>

View File

@@ -1,8 +1,8 @@
{% from "components/table.html" import list_table, field, right_aligned_field_heading, row_heading, notification_status_field %}
{% from "components/page-footer.html" import page_footer %}
<div class="ajax-block-container table-wrapper" aria-labelledby='pill-selected-item'>
<div class="dashboard-table bottom-gutter-3-2">
<div class="ajax-block-container" aria-labelledby='pill-selected-item'>
<div class="dashboard-table table-overflow-x-auto">
{% call(item, row_number) list_table(
[notification],

View File

@@ -0,0 +1,143 @@
{% extends "withnav_template.html" %}
{% block service_page_title %}
All activity
{% endblock %}
{% set show_pagination %}
{% if prev_page or next_page %}
<nav aria-label="Pagination" class="usa-pagination">
<ul class="usa-pagination__list">
{% if prev_page %}
<li class="usa-pagination__item usa-pagination__arrow">
<a
href="{{prev_page['url']}}"
class="usa-pagination__link usa-pagination__previous-page"
aria-label="Previous page"
>
<img src="{{ asset_url('img/usa-icons/navigate_before.svg') }}" alt="arrow">
<span class="usa-pagination__link-text">Previous</span></a
>
</li>
{% endif %}
{% if pagination %}
{% for page in pagination.pages %}
{% if page == pagination.current %}
<li class="usa-pagination__item usa-pagination__page-no">
<span class="usa-pagination__button usa-current" aria-label="Page {{ page }}" aria-current="true">
{{ page }}
</span>
</li>
{% else %}
<li class="usa-pagination__item">
<a class="usa-pagination__button" href="?page={{ page }}">
{{ page }}
</a>
</li>
{% endif %}
{% endfor %}
{% endif %}
{% if next_page %}
<li class="usa-pagination__item usa-pagination__arrow">
<a
href="{{ next_page['url'] }}"
class="usa-pagination__link usa-pagination__next-page"
aria-label="Next page"
>
<span class="usa-pagination__link-text">Next </span>
<img src="{{ asset_url('img/usa-icons/navigate_next.svg') }}" alt="arrow">
</a>
</li>
{% endif %}
</ul>
</nav>
{% endif %}
{% endset %}
{% block maincolumn_content %}
<div class="margin-bottom-8">
<h1 class="usa-sr-only">All activity</h1>
<h2 class="font-body-2xl line-height-sans-2 margin-0">All activity</h2>
<h2 class="margin-top-4 margin-bottom-1">Sent jobs</h2>
<div class="usa-table-container--scrollable-mobile">
<table class="usa-table usa-table--compact job-table">
<caption></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>Time sent</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>Report</span>
</th>
<th data-sortable scope="col" role="columnheader" class="table-field-heading">
<span>Delivered</span>
</th>
<th data-sortable scope="col" role="columnheader" class="table-field-heading">
<span>Failed</span>
</th>
</tr>
</thead>
<tbody>
{% if all_jobs_dict %}
{% for job in all_jobs_dict %}
<tr class="table-row">
<td class="table-field jobid" scope="row" 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 data-sort-value="{{job.time_sent_data_value}}" class="table-field time-sent">
{{ (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 report">
{% if job.time_left != "Data no longer available" %}
<a href="{{ job.download_link }}"><img src="{{ url_for('static', filename='img/material-icons/file_download.svg') }}" alt="File Download Icon"></a>
{% else %}
<span>N/A</span>
{% endif %}
</td>
<td class="table-field delivered">{{ job.delivered_count if job.delivered_count is not none else '0' }}</td>
<td class="table-field failed">{{ job.failed_count if job.failed_count is not none else '0' }}</td>
</tr>
{% endfor %}
{% else %}
<tr class="table-row">
<td class="table-empty-message" colspan="10">No messages found</td>
</tr>
{% endif %}
</tbody>
</table>
<div class="usa-sr-only usa-table__announcement-region" aria-live="polite"></div>
<p><b>Note: </b>Report data is only available for 7 days after your message has been sent</p>
</div>
{{show_pagination}}
{% if current_user.has_permissions('view_activity') %}
<h2 class="line-height-sans-2 margin-bottom-0 margin-top-4">Download recent reports</h2>
<p class="font-body-sm">
<a href="{{ download_link_one_day }}" download="download" class="usa-link">Download all data last 24 hours (<abbr title="Comma separated values">CSV</abbr>)</a>
</p>
<p class="font-body-sm">
<a href="{{ download_link_three_day }}" download="download" class="usa-link">Download all data last 3 days (<abbr title="Comma separated values">CSV</abbr>)</a>
&emsp;
</p>
<p class="font-body-sm">
<a href="{{ download_link_five_day }}" download="download" class="usa-link">Download all data last 5 days (<abbr title="Comma separated values">CSV</abbr>)</a>
</p>
<p class="font-body-sm">
<a href="{{ download_link_seven_day }}" download="download" class="usa-link">Download all data last 7 days (<abbr title="Comma separated values">CSV</abbr>)</a>
</p>
{% endif %}
</div>
{% endblock %}

View File

@@ -5,7 +5,7 @@
<div class="ajax-block-container" id='pill-selected-item'>
{% if notifications %}
<div class="table-wrapper">
<div class="table-overflow-x-auto">
<div class='dashboard-table'>
{% endif %}
{% call(item, row_number) list_table(

View File

@@ -14,8 +14,8 @@
{% block maincolumn_content %}
{{ page_header('Callbacks') }}
<div class="table-wrapper">
<div class="bottom-gutter-3-2 dashboard-table body-copy-table">
<div class="table-overflow-x-auto">
<div class="bottom-gutter-3-2 dashboard-table body-copy-table table-overflow-x-auto">
{% call mapping_table(
caption='General',
field_headings=['Label', 'Value', 'Action'],

View File

@@ -41,7 +41,7 @@
</li>
</ul>
</div>
<div class="usa-table-container--scrollable" tabindex="0">
<div class="usa-table-container--scrollable table-overflow-x-auto" tabindex="0">
<table class="usa-table usa-table--borderless width-full">
<caption class="font-body-lg table-heading">
Note: Only the first 5 rows are displayed here.

View File

@@ -1,6 +1,6 @@
{% from "components/table.html" import list_table, field, right_aligned_field_heading, row_heading %}
<div class="table-wrapper">
<div class="table-overflow-x-auto">
<div class='dashboard-table ajax-block-container'>
{% call(item, row_number) list_table(
jobs,

View File

@@ -28,7 +28,7 @@
No messages sent
</p>
{% else %}
<div class='template-statistics-table'>
<div class='template-statistics-table table-overflow-x-auto'>
{% call(item, row_number) list_table(
month.templates_used,
caption=month.name,

View File

@@ -1,3 +1,5 @@
{% 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 %}
@@ -8,8 +10,10 @@
{% endblock %}
{% block maincolumn_content %}
<script type="text/javascript" src="{{ asset_url('js/setTimezone.js') }}"></script>
<div class="dashboard margin-bottom-8">
<div class="dashboard margin-bottom-2">
<h1 class="usa-sr-only">Dashboard</h1>
{% if current_user.has_permissions('manage_templates') and not current_service.all_templates %}
@@ -18,97 +22,122 @@
{{ ajax_block(partials, updates_url, 'upcoming') }}
<h2 class="font-body-2xl line-height-sans-2 margin-0">
Messages sent
</h2>
<h2 class="font-body-xl margin-top-0">{{ current_service.name }} Dashboard</h2>
<!-- <button id="sevenDaysButton">7 Days</button>
<canvas id="myChart"></canvas> -->
{{ ajax_block(partials, updates_url, 'inbox') }}
{{ ajax_block(partials, updates_url, 'totals') }}
<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>
<div id="totalMessageTable" class="margin-0"></div>
{{ ajax_block(partials, updates_url, 'template-statistics') }}
<h2 class="margin-top-4 margin-bottom-1">Recent Batches</h2>
<div class="table-wrapper">
<table class="usa-table usa-table--borderless job-table">
<thead class="table-field-headings">
<tr>
<th scope="col" class="table-field-heading-first">
<span>File name</span>
</th>
<th scope="col" class="table-field-heading">
<span>Template</span>
</th>
<th scope="col" class="table-field-heading">
<span>Job status</span>
</th>
<th scope="col" class="table-field-heading">
<span>Sender</span>
</th>
<th scope="col" class="table-field-heading">
<span># of Recipients</span>
</th>
<th scope="col" class="table-field-heading">
<span>Report</span>
</th>
</tr>
</thead>
<tbody>
{% if job_and_notifications %}
{% for job in job_and_notifications[:5] %}
{% if job.job_id and job.notifications %}
<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>- 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>
{% 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">
<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" scope="row" 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>
<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">
<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 class="table-row" id="{{ job.job_id }}">
<td class="table-field file-name">
{{ notification.job.original_file_name[:12] if notification.job.original_file_name else 'Manually entered number'}}
<br>
<a class="usa-link file-list-filename" href="{{ job.view_job_link }}">View Batch</a>
<tr id="{{ job.job_id }}">
<td class="table-field jobid" scope="row" 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">
{{ notification.template.name }}
</td>
<td class="table-field time-sent">
<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">
{{ notification.created_by.name }}
</td>
<td class="table-field count-of-recipients">
{{ job.notification_count}}
</td>
<td class="table-field report">
{% if notification and job.time_left != "Data no longer available" %}
<a class="usa-link file-list-filename" href="{{ job.download_link }}">Download</a>
<span class="usa-hint">{{ job.time_left }}</span>
{% elif job %}
<span>{{ job.time_left }}</span>
{% endif %}
</td>
<td class="table-field sender">{{ job.created_by.name }}</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>
{% 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>
<h2 class="margin-top-4 margin-bottom-1">Message count</h2>
{% if current_user.has_permissions('manage_service') %}
<h3 class='margin-bottom-0' id="current-year"></h3>
{{ ajax_block(partials, updates_url, 'usage') }}
<p class="margin-top-0">During the pilot period, each service has an allowance of 250,000 message parts. Once this allowance is met, the
application will stop delivering messages. There's no monthly charge, no setup fee, and no procurement cost.</p>
<p class="align-with-heading-copy">
What counts as 1 text message part?<br />
See <a class="usa-link" href="{{ url_for('.pricing') }}">pricing</a>.
</p>
{% endif %}
{{ ajax_block(partials, updates_url, 'template-statistics') }}
</div>
{% endblock %}

View File

@@ -1,33 +1,45 @@
{% from "components/table.html" import list_table, field, right_aligned_field_heading, row_heading, spark_bar_field %}
<div class="ajax-block-container">
{% if template_statistics|length > 1 %}
<div class='template-statistics-table'>
{% call(item, row_number) list_table(
template_statistics,
caption="Messages sent by template",
caption_visible=True,
empty_message='',
field_headings=[
'Template',
'Messages sent'
],
field_headings_visible=False
) %}
{% call row_heading() %}
<a class="usa-link template-statistics-table-template-name" href="{{ url_for('.view_template', service_id=current_service.id, template_id=item.template_id) }}">{{ item.template_name }}</a>
<span class="template-statistics-table-hint">
{{ 1|message_count_label(item.template_type, suffix='template')|capitalize }}
</span>
{% endcall %}
{{ spark_bar_field(item.count, most_used_template_count, id=item.template_id) }}
{% endcall %}
<a
href="{{ url_for('.template_usage', service_id=current_service.id) }}"
class="usa-link show-more-no-border"
><span>See templates used by month</span></a>
<h2 class="margin-top-4 margin-bottom-1">Most Used Templates</h2>
<div class="template-statistics-table table-overflow-x-auto">
<table class="usa-table width-full">
<caption class="font-body-lg table-heading usa-sr-only">
Messages sent by template
</caption>
<thead class="table-field-headings">
<tr>
<th class="table-field-heading-first" width="">
<span>Template name</span>
</th>
<th class="table-field-heading" width="">
<span>Folder</span>
</th>
<th class="table-field-heading" width="">
<span>Last used</span>
</th>
<th class="table-field-heading" width="">
<span>Created by</span>
</th>
<th class="table-field-heading" width="">
<span># Times used</span>
</th>
</tr>
</thead>
<tbody>
{% for item in template_statistics[:8] %}
<tr class="table-row">
<td>
<a class="usa-link template-statistics-table-template-name" href="{{ url_for('.view_template', service_id=current_service.id, template_id=item.template_id) }}">{{ item.template_name }}</a>
</td>
<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>
</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>
</div>
{% endif %}
</div>

View File

@@ -24,6 +24,10 @@
<p class="usa-body">To send a text with Notify, you need to create a reusable message template first.</p>
<p class="usa-body">Templates let you send the same thing to lots of people, as often as you need to, without writing a new message each time.</p>
<h2 class="heading-medium" id="organizing-content">Organizing your content</h2>
<p class="usa-body">You can organize your content by grouping templates, and putting them into folders.</p>
<p class="usa-body">Set up folders within the Send message section of Notify, where you find templates. You can choose to create a Folder before or after a template is made. If you want to change a users ability to view or edit folders, you may control this through their individual team member permissions.</p>
<h2 class="heading-medium" id="personalised-messages">Personalized content</h2>
<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>
@@ -44,7 +48,6 @@
<p class="usa-body">Notify lets you:</p>
<ul class="list list-bullet">
<li>set different permission levels for each team member</li>
<li>invite team members who do not have a government email address</li>
<li>choose who else can manage team members</li>
</ul>

View File

@@ -162,55 +162,57 @@
location, the flag to receive the content needs to be captured in its own column with a Yes or No (Y/N) flag.</p>
<h3>Example</h3>
<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 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>
{# Preventing fraud #}
<h2 class="padding-top-1" id="prevent-fraud">Preventing fraud</h2>

View File

@@ -23,36 +23,34 @@ Inbound SMS
<h1 class="font-body-2xl">
Inbound SMS
</h1>
<table class="usa-table usa-table--borderless inbound">
<thread>
<tr>
<th>{{table_headings.field_headings[0]}}</th>
<th>{{table_headings.field_headings[1]}}</th>
<th>{{table_headings.field_headings[2]}}</th>
</tr>
</thread>
<tbody>
{% for value in inbound_num_list.data: %}
<div class="table-overflow-x-auto">
<table class="usa-table usa-table--borderless inbound width-full">
<thead>
<tr>
<td>{{value.number}}</td>
<td>
{% if value.active %}
Active
{% elif not value.service.name %}
Not used
{% else %}
Inactive
{% endif %}
</td>
<td>
<a href="{{ url_for('main.service_dashboard', service_id=value.service.id) }}" class="usa-link bold">{{ value.service.name }}</a>
</td>
<th>{{table_headings.field_headings[0]}}</th>
<th>{{table_headings.field_headings[1]}}</th>
<th>{{table_headings.field_headings[2]}}</th>
</tr>
{% endfor %}
</tbody>
</table>
</thead>
<tbody>
{% for value in inbound_num_list.data: %}
<tr>
<td>{{value.number}}</td>
<td>
{% if value.active %}
Active
{% elif not value.service.name %}
Not used
{% else %}
Inactive
{% endif %}
</td>
<td>
<a href="{{ url_for('main.service_dashboard', service_id=value.service.id) }}" class="usa-link bold">{{ value.service.name }}</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endblock %}

View File

@@ -15,7 +15,7 @@
{% block maincolumn_content %}
{{ page_header('Invite {}'.format(user_to_invite.name if user_to_invite else 'a team member')) }}
<a href="#permissions">Learn more about user permissions.</a>
{% call form_wrapper() %}
{% if user_to_invite %}
@@ -36,5 +36,99 @@
{{ page_footer('Send invitation email') }}
{% endcall %}
<h2 id="permissions">Set up user permissions</h2>
<p>Setting the right digital control is one of many aspects to keep content and information on a need to know basis. Permissions allow you to do this. Give team members access to important views related to their responsibilities.</p>
<h3 class="site-preview-heading">Permissions</h3>
<div class="usa-accordion usa-accordion--bordered margin-top-1">
<h4 class="usa-accordion__heading">
<button
type="button"
class="usa-accordion__button"
aria-expanded="true"
aria-controls="b-a1"
>
See dashboard
</button>
</h4>
<div id="b-a1" class="usa-accordion__content usa-prose">
<p>Allows users to view the dashboard but not have access to any other functions on the site.
Dashboard viewers can see: Weekly message quantity</p>
<ul>
<li>Message templates used</li>
<li>Recent batches of sent messages</li>
</ul>
<p>This also gives access to:</p>
<ul>
<li>List of templates used by month</li>
<li>Batch details with message status</li>
</ul>
</div>
<h4 class="usa-accordion__heading">
<button
type="button"
class="usa-accordion__button"
aria-expanded="false"
aria-controls="b-a2"
>
Send messages
</button>
</h4>
<div id="b-a2" class="usa-accordion__content usa-prose">
<p>
Allows users to view, select and send approved messages, but restricts users from editing templates
and/or folders. Team members responsible for sending message batches can use templates to prepare
and schedule texts. Actions they take in this section:
</p>
<ul>
<li>View and use templates</li>
<li>Enter personalized message field content</li>
<li>Schedule messages for sending</li>
<li>Send messages</li>
</ul>
</div>
<h4 class="usa-accordion__heading">
<button
type="button"
class="usa-accordion__button"
aria-expanded="false"
aria-controls="b-a3"
>
Add and edit templates
</button>
</h4>
<div id="b-a3" class="usa-accordion__content usa-prose">
<p>
Allows users to view, create or edit, and more or delete templates and folders. They will set up real
messages, but are restricted from uploading data or sending or seeing previous versions. Some actions
they can take:
</p>
<ul>
<li>Create a new template</li>
<li>Create a new folder</li>
<li>Move (or organize) folders and templates</li>
<li>Delete templates and/or folders</li>
</ul>
</div>
<h4 class="usa-accordion__heading">
<button
type="button"
class="usa-accordion__button"
aria-expanded="false"
aria-controls="b-a4"
>
Manage settings and team
</button>
</h4>
<div id="b-a4" class="usa-accordion__content usa-prose">
<p>Leads with service management responsibility may do so in Settings. Managed within Settings:</p>
<ul>
<li>Change service name</li>
<li>Invite team members</li>
<li>Remove unnecessary team members</li>
<li>Turn off the ability to send text messages</li>
<li>Turn off the ability to send service name with text message</li>
</ul>
</div>
</div>
{% endblock %}

View File

@@ -25,8 +25,8 @@
</div>
{% endif %}
<p class="notification-status">
Messages will remain in pending state until carrier status is received, typically 5 minutes.
<p class="notification-status max-width-full">
Messages are sent immediately to the cell phone carrier, but will remain in "pending" status until we hear back from the carrier they have received it and attempted deliver. More information on <a class="usa-link" href="{{ url_for('main.message_status') }}">delivery status</a>.
</p>
{% if not job.processing_finished %}
<div

View File

@@ -10,7 +10,7 @@
{% block serviceNavigation %}{% endblock %}
{% block sideNavigation %}
{% include "/new/components/settings_nav.html" %}
{% include "components/settings_nav.html" %}
{% endblock %}
{% block maincolumn_content %}

View File

@@ -49,7 +49,7 @@
<p>Notify cannot tell you if your users open an email or click on the links in an email. We do not track open rates and click-throughs because there are privacy issues. Tracking emails without asking permission from users could breach General Data Protection Regulations (GDPR).</p> -->
<h2 id="text-message-statuses" class="heading-medium">Text messages</h2>
<div class="margin-bottom-8">
<div class="margin-bottom-8 table-overflow-x-auto">
{% call mapping_table(
caption='Message statuses text messages',
field_headings=['Status', 'Description'],

View File

@@ -44,33 +44,31 @@
</div>
</div>
<div class="table-wrapper">
<div class="dashboard-table">
{% call(item, row_number) list_table(
notifications_by_type|reverse,
caption='Messages sent since May 2023',
caption_visible=False,
field_headings=[
'Date',
99|message_count_noun('email')|capitalize,
99|message_count_noun('sms')|capitalize,
],
empty_message='No data to show'
) %}
{% call field() %}
{{ item.date | format_date_normal }}
{% endcall %}
{% call field() %}
{{ item.emails|format_thousands }}
{% endcall %}
{% call field() %}
{{ item.sms|format_thousands }}
{% endcall %}
<div class="dashboard-table table-overflow-x-auto">
{% call(item, row_number) list_table(
notifications_by_type|reverse,
caption='Messages sent since May 2023',
caption_visible=False,
field_headings=[
'Date',
99|message_count_noun('email')|capitalize,
99|message_count_noun('sms')|capitalize,
],
empty_message='No data to show'
) %}
{% call field() %}
{{ item.date | format_date_normal }}
{% endcall %}
<p class="table-show-more-link">
Only showing the last {{ notifications_by_type|length }} days
</p>
</div>
{% call field() %}
{{ item.emails|format_thousands }}
{% endcall %}
{% call field() %}
{{ item.sms|format_thousands }}
{% endcall %}
{% endcall %}
<p class="table-show-more-link">
Only showing the last {{ notifications_by_type|length }} days
</p>
</div>
<h2 class="govuk-heading-m">
@@ -84,7 +82,7 @@
) }}
</div>
</div>
<div class="table-wrapper">
<div class="table-overflow-x-auto">
<div class="dashboard-table">
{% call(item, row_number) list_table(
processing_time | reverse,
@@ -124,7 +122,7 @@
<span class="usa-sr-only">using Notify.</span>
</div>
</div>
<div class="table-wrapper">
<div class="table-overflow-x-auto">
<div class="dashboard-table">
{% call(item, row_number) list_table(
organizations_using_notify,

View File

@@ -16,7 +16,7 @@
</ol>
</nav>
<div class="grid-row margin-bottom-10">
<div class="tablet:grid-col-3 margin-top-4">
<div class="tablet:grid-col-2 margin-top-4">
<nav class="navigation">
<ul class="usa-sidenav">
@@ -41,11 +41,11 @@
</ul>
</nav>
</div>
<div class="grid-col-9 tablet:grid-col-9 padding-left-4 sub-content">
<div class="grid-col-10 tablet:grid-col-10 padding-left-4 sub-content">
{% block backLink %}{% endblock %}
<main id="main-content" role="main">
{% block content %}
{% include 'new/components/flash_messages.html' %}
{% include 'components/flash_messages.html' %}
{% block platform_admin_content %}{% endblock %}
{% endblock %}
</main>

View File

@@ -13,26 +13,26 @@
Email complaints
</h1>
<div class="table-overflow-x-auto">
{% call(item, row_number) list_table(
complaints,
caption="Complaints",
caption_visible=False,
empty_message='No complaints',
field_headings=['Notification Id', 'Service', 'Complaint type', 'Complaint Date'],
field_headings_visible=True
) %}
{% call(item, row_number) list_table(
complaints,
caption="Complaints",
caption_visible=False,
empty_message='No complaints',
field_headings=['Notification Id', 'Service', 'Complaint type', 'Complaint Date'],
field_headings_visible=True
) %}
{{ link_field(item.notification_id, url_for('main.view_notification', service_id=item.service_id, notification_id=item.notification_id)) }}
{{ link_field(item.notification_id, url_for('main.view_notification', service_id=item.service_id, notification_id=item.notification_id)) }}
{{ link_field(item.service_name, url_for('main.service_dashboard', service_id=item.service_id)) }}
{{ link_field(item.service_name, url_for('main.service_dashboard', service_id=item.service_id)) }}
{{ text_field(item.complaint_type) }}
{{ text_field(item.complaint_type) }}
{{ text_field(item.complaint_date|format_datetime_table if item.complaint_date else None) }}
{% endcall %}
{{ text_field(item.complaint_date|format_datetime_table if item.complaint_date else None) }}
{% endcall %}
</div>
{{ previous_next_navigation(prev_page, next_page) }}
{% endblock %}

View File

@@ -31,4 +31,8 @@
<p>
<a class="usa-link" href="{{ url_for('main.get_users_report') }}">Users Report</a>
</p>
<p>
<a class="usa-link" href="{{ url_for('main.download_all_users') }}">Download All Users</a>
</p>
{% endblock %}

View File

@@ -7,6 +7,7 @@
{% from "components/components/details/macro.njk" import usaDetails %}
{% macro services_table(services, caption) %}
<div class="table-overflow-x-auto">
{% call(item, row_number) mapping_table(
caption=caption,
caption_visible=False,
@@ -27,6 +28,10 @@
{% if not service['active'] %}
<span class="heading-medium hint">&ensp;Archived</span>
{% endif %}
{% if service['name'] == 'Test service' %}
<a class="usa-link" href="{{ url_for('main.load_test') }}">Load Test</a>
{% endif %}
{% endcall %}
{% endcall %}
@@ -48,6 +53,7 @@
{% endfor %}
{% endcall %}
</div>
{% endmacro %}

View File

@@ -35,7 +35,7 @@ part.</p>
<h4>Calculation of message parts without special characters</h4>
<div class="bottom-gutter-3-2">
<div class="table-overflow-x-auto">
{% call mapping_table(
caption='Text message pricing',
field_headings=['Message length', 'Charge'],
@@ -86,7 +86,7 @@ recipients preferred language, but we are aware that, rarely, a phone carrier
to be calculated as detailed below.<p>
<h4>Calculation of message parts with special characters or non-romanized scripts</h4>
{% set accentedChars %}
<div class="bottom-gutter-3-2">
<div class="table-overflow-x-auto">
{% call mapping_table(
caption='Accented characters that affect text message charges',
field_headings=['Character', 'Description'],
@@ -151,7 +151,7 @@ to be calculated as detailed below.<p>
"html": accentedChars
}) }}
<div class="bottom-gutter-3-2">
<div class="table-overflow-x-auto">
{% call mapping_table(
caption='Pricing for text messages containing accented characters that affect the charges',
field_headings=['Message length', 'Charge'],

View File

@@ -4,6 +4,7 @@
{% from "components/file-upload.html" import file_upload %}
{% from "components/table.html" import list_table, text_field, index_field, index_field_heading %}
{% from "components/components/back-link/macro.njk" import usaBackLink %}
<script type="text/javascript" src="{{ asset_url('js/setTimezone.js') }}"></script>
{% block service_page_title %}
Upload a list of {{ 999|recipient_count_label(template.template_type) }}

View File

@@ -9,14 +9,14 @@
{% block serviceNavigation %}{% endblock %}
{% block sideNavigation %}
{% include "/new/components/settings_nav.html" %}
{% include "components/settings_nav.html" %}
{% endblock %}
{% block maincolumn_content %}
<h1 class="font-body-lg">Settings</h1>
<div class="bottom-gutter-3-2 settings-table body-copy-table">
<div class="bottom-gutter-3-2 settings-table body-copy-table table-overflow-x-auto">
{% call mapping_table(
caption='General',

View File

@@ -20,7 +20,7 @@ Notify.gov
<div class="desktop:grid-col-7 tablet:grid-col-12">
<h1 class="font-serif-2xl usa-hero__heading">Reach people where they are with government-powered text messages</h1>
<p class="font-sans-lg">Notify.gov is a text message service that helps federal, state, local, tribal and territorial governments more effectively communicate with the people they serve.</p>
<div class="usa-button-group margin-bottom-5">
<div class="usa-button-group margin-bottom-5 flex-align-center">
<a class="usa-button usa-button login-button login-button--primary margin-right-2" href="{{ initial_signin_url }}">Sign in with <img src="{{ asset_url('images/logo-login.svg') }}" alt="Login.gov logo">
</a>
if you are an existing pilot partner
@@ -28,8 +28,8 @@ Notify.gov
<p class="font-sans-md">Currently we are only working with select pilot partners. If you are interested in using Notify.gov in the future, please contact <br><a href="mailto:tts-benefits-studio@gsa.gov">tts-benefits-studio@gsa.gov</a> to learn more.</p>
</div>
<div class="desktop:grid-col-4 grid-offset-1 desktop:display-block display-none margin-x-5">
<img src="{{ asset_url('images/product/phone-text.svg') }}"
alt="An illustration of notifications to a phone.">
<img src="{{ asset_url('images/product/phone-text.png') }}"
alt="An illustration of notifications to a phone.">
</div>
</div>
</div>

View File

@@ -21,7 +21,7 @@
{% endif %}
</p>
{% else %}
<nav id="template-list">
<nav id="template-list" aria-label="Template list">
{% set checkboxes_data = [] %}
{% if not current_user.has_permissions('manage_templates') %}

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