From d6ef51ae7da77a3bd8e77d3ec4c458b9c25b9316 Mon Sep 17 00:00:00 2001 From: Nichelle Date: Fri, 29 Mar 2024 21:28:01 +0100 Subject: [PATCH 01/28] ungate offset picker in production environment (#1273) --- .../AgencySettingsEmailNotifications.tsx | 83 ++++++++----------- 1 file changed, 34 insertions(+), 49 deletions(-) diff --git a/publisher/src/components/Settings/AgencySettingsEmailNotifications.tsx b/publisher/src/components/Settings/AgencySettingsEmailNotifications.tsx index 676f94e5a..470fa7774 100644 --- a/publisher/src/components/Settings/AgencySettingsEmailNotifications.tsx +++ b/publisher/src/components/Settings/AgencySettingsEmailNotifications.tsx @@ -21,8 +21,6 @@ import { observer } from "mobx-react-lite"; import React, { useRef, useState } from "react"; import { useStore } from "../../stores"; -import { gateToAllowedEnvironment } from "../../utils/featureFlags"; -import { Environment } from "../AdminPanel"; import { AgencySettingsBlock, AgencySettingsBlockDescription, @@ -34,7 +32,7 @@ import { } from "./AgencySettings.styles"; export const AgencySettingsEmailNotifications: React.FC = observer(() => { - const { agencyStore, api } = useStore(); + const { agencyStore } = useStore(); const { updateEmailSubscriptionDetails, isUserSubscribedToEmails, @@ -55,13 +53,7 @@ export const AgencySettingsEmailNotifications: React.FC = observer(() => { const handleSubscribeUnsubscribe = () => { updateEmailSubscriptionDetails( !isUserSubscribedToEmails, - /* TODO(#1251) Ungate feature */ - gateToAllowedEnvironment(api.environment, [ - Environment.LOCAL, - Environment.STAGING, - ]) - ? Number(currentOffsetDays) - : daysAfterTimePeriodToSendEmail + Number(currentOffsetDays) ); }; @@ -101,45 +93,38 @@ export const AgencySettingsEmailNotifications: React.FC = observer(() => { - - {/* TODO(#1251) Ungate feature */} - {gateToAllowedEnvironment(api.environment, [ - Environment.LOCAL, - Environment.STAGING, - ]) && ( - <> - {isUserSubscribedToEmails && ( - <> - - Below, you can choose how soon after the end of each reporting - period to receive an upload data reminder email. For instance, - if you enter 15, you would receive a reminder to upload any - missing data for the month of March on April 15th. - - - Enter the number of days after the end of the reporting period - to receive a reminder email: - - { - setReminderEmailOffsetDays(e.target.value); - debouncedSaveOffsetDays( - e.target.value, - !isValidInput(e.target.value) - ); - }} - /> - - - - )} - {isUserSubscribedToEmails && !isValidInput(currentOffsetDays) && ( - Please enter a number between 1-1000 - )} - - )} + <> + {isUserSubscribedToEmails && ( + <> + + Below, you can choose how soon after the end of each reporting + period to receive an upload data reminder email. For instance, + if you enter 15, you would receive a reminder to upload any + missing data for the month of March on April 15th. + + + Enter the number of days after the end of the reporting period + to receive a reminder email: + + { + setReminderEmailOffsetDays(e.target.value); + debouncedSaveOffsetDays( + e.target.value, + !isValidInput(e.target.value) + ); + }} + /> + + + + )} + {isUserSubscribedToEmails && !isValidInput(currentOffsetDays) && ( + Please enter a number between 1-1000 + )} + ); From fef901f20f23aa609de8b11764681527739be70e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Apr 2024 07:37:36 +0000 Subject: [PATCH 02/28] Bump @babel/core from 7.24.0 to 7.24.3 (#1278) Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.24.0 to 7.24.3. - [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.24.3/packages/babel-core) --- updated-dependencies: - dependency-name: "@babel/core" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- agency-dashboard/package.json | 2 +- publisher/package.json | 2 +- yarn.lock | 118 +++++++++++++++++++++------------- 3 files changed, 75 insertions(+), 47 deletions(-) diff --git a/agency-dashboard/package.json b/agency-dashboard/package.json index 67aa85c4e..3723df4c4 100644 --- a/agency-dashboard/package.json +++ b/agency-dashboard/package.json @@ -48,7 +48,7 @@ "@testing-library/jest-dom": "^6.4.2", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^14.5.2", - "@babel/core": "^7.24.0", + "@babel/core": "^7.24.3", "@types/jest": "^27.5.2", "@types/node": "^20.6.2", "@types/react": "^18.2.64", diff --git a/publisher/package.json b/publisher/package.json index a46400efc..b2e864ab8 100644 --- a/publisher/package.json +++ b/publisher/package.json @@ -58,7 +58,7 @@ "homepage": "/", "devDependencies": { "@auth0/auth0-spa-js": "^1.20.1", - "@babel/core": "^7.24.0", + "@babel/core": "^7.24.3", "@babel/plugin-syntax-flow": "^7.22.5", "@babel/plugin-transform-react-jsx": "^7.23.4", "@recidiviz/eslint-config": "^3.0.0", diff --git a/yarn.lock b/yarn.lock index 29ebbfcf0..e9add2a93 100644 --- a/yarn.lock +++ b/yarn.lock @@ -61,13 +61,13 @@ dependencies: default-browser-id "3.0.0" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.16.0", "@babel/code-frame@^7.16.7", "@babel/code-frame@^7.23.5", "@babel/code-frame@^7.8.3": - version "7.23.5" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.23.5.tgz#9009b69a8c602293476ad598ff53e4562e15c244" - integrity sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA== +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.16.0", "@babel/code-frame@^7.16.7", "@babel/code-frame@^7.23.5", "@babel/code-frame@^7.24.1", "@babel/code-frame@^7.24.2", "@babel/code-frame@^7.8.3": + version "7.24.2" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.24.2.tgz#718b4b19841809a58b29b68cde80bc5e1aa6d9ae" + integrity sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ== dependencies: - "@babel/highlight" "^7.23.4" - chalk "^2.4.2" + "@babel/highlight" "^7.24.2" + picocolors "^1.0.0" "@babel/compat-data@^7.17.7", "@babel/compat-data@^7.18.8", "@babel/compat-data@^7.19.3": version "7.19.3" @@ -94,20 +94,20 @@ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.23.5.tgz#ffb878728bb6bdcb6f4510aa51b1be9afb8cfd98" integrity sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw== -"@babel/core@^7.1.0", "@babel/core@^7.11.1", "@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.16.0", "@babel/core@^7.18.9", "@babel/core@^7.23.0", "@babel/core@^7.23.2", "@babel/core@^7.24.0", "@babel/core@^7.7.2", "@babel/core@^7.8.0": - version "7.24.0" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.24.0.tgz#56cbda6b185ae9d9bed369816a8f4423c5f2ff1b" - integrity sha512-fQfkg0Gjkza3nf0c7/w6Xf34BW4YvzNfACRLmmb7XRLa6XHdR+K9AlJlxneFfWYf6uhOzuzZVTjF/8KfndZANw== +"@babel/core@^7.1.0", "@babel/core@^7.11.1", "@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.16.0", "@babel/core@^7.18.9", "@babel/core@^7.23.0", "@babel/core@^7.23.2", "@babel/core@^7.24.3", "@babel/core@^7.7.2", "@babel/core@^7.8.0": + version "7.24.3" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.24.3.tgz#568864247ea10fbd4eff04dda1e05f9e2ea985c3" + integrity sha512-5FcvN1JHw2sHJChotgx8Ek0lyuh4kCKelgMTTqhYJJtloNvUfpAFMeNQUtdlIaktwrSV9LtCdqwk48wL2wBacQ== dependencies: "@ampproject/remapping" "^2.2.0" - "@babel/code-frame" "^7.23.5" - "@babel/generator" "^7.23.6" + "@babel/code-frame" "^7.24.2" + "@babel/generator" "^7.24.1" "@babel/helper-compilation-targets" "^7.23.6" "@babel/helper-module-transforms" "^7.23.3" - "@babel/helpers" "^7.24.0" - "@babel/parser" "^7.24.0" + "@babel/helpers" "^7.24.1" + "@babel/parser" "^7.24.1" "@babel/template" "^7.24.0" - "@babel/traverse" "^7.24.0" + "@babel/traverse" "^7.24.1" "@babel/types" "^7.24.0" convert-source-map "^2.0.0" debug "^4.1.0" @@ -124,14 +124,14 @@ eslint-visitor-keys "^2.1.0" semver "^6.3.0" -"@babel/generator@^7.23.0", "@babel/generator@^7.23.6", "@babel/generator@^7.7.2": - version "7.23.6" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.6.tgz#9e1fca4811c77a10580d17d26b57b036133f3c2e" - integrity sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw== +"@babel/generator@^7.23.0", "@babel/generator@^7.24.1", "@babel/generator@^7.7.2": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.24.1.tgz#e67e06f68568a4ebf194d1c6014235344f0476d0" + integrity sha512-DfCRfZsBcrPEHUfuBMgbJ1Ut01Y/itOs+hY2nFLgqsqXd52/iSiVq5TITtUasIUgm+IIKdY2/1I7auiQOEeC9A== dependencies: - "@babel/types" "^7.23.6" - "@jridgewell/gen-mapping" "^0.3.2" - "@jridgewell/trace-mapping" "^0.3.17" + "@babel/types" "^7.24.0" + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" jsesc "^2.5.1" "@babel/helper-annotate-as-pure@^7.16.0", "@babel/helper-annotate-as-pure@^7.18.6", "@babel/helper-annotate-as-pure@^7.22.5": @@ -567,28 +567,29 @@ "@babel/traverse" "^7.22.5" "@babel/types" "^7.22.5" -"@babel/helpers@^7.24.0": - version "7.24.0" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.24.0.tgz#a3dd462b41769c95db8091e49cfe019389a9409b" - integrity sha512-ulDZdc0Aj5uLc5nETsa7EPx2L7rM0YJM8r7ck7U73AXi7qOV44IHHRAYZHY6iU1rr3C5N4NtTmMRUJP6kwCWeA== +"@babel/helpers@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.24.1.tgz#183e44714b9eba36c3038e442516587b1e0a1a94" + integrity sha512-BpU09QqEe6ZCHuIHFphEFgvNSrubve1FtyMton26ekZ85gRGi6LrTF7zArARp2YvyFxloeiRmtSCq5sjh1WqIg== dependencies: "@babel/template" "^7.24.0" - "@babel/traverse" "^7.24.0" + "@babel/traverse" "^7.24.1" "@babel/types" "^7.24.0" -"@babel/highlight@^7.23.4": - version "7.23.4" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.23.4.tgz#edaadf4d8232e1a961432db785091207ead0621b" - integrity sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A== +"@babel/highlight@^7.24.2": + version "7.24.2" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.24.2.tgz#3f539503efc83d3c59080a10e6634306e0370d26" + integrity sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA== dependencies: "@babel/helper-validator-identifier" "^7.22.20" chalk "^2.4.2" js-tokens "^4.0.0" + picocolors "^1.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.23.0", "@babel/parser@^7.24.0", "@babel/parser@^7.7.0": - version "7.24.0" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.24.0.tgz#26a3d1ff49031c53a97d03b604375f028746a9ac" - integrity sha512-QuP/FxEAzMSjXygs8v4N9dvdXzEHN4W1oF3PxuWAtPo08UdM17u89RDMgjLn/mlc56iM0HlLmVkO/wgR+rDgHg== +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.23.0", "@babel/parser@^7.24.0", "@babel/parser@^7.24.1", "@babel/parser@^7.7.0": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.24.1.tgz#1e416d3627393fab1cb5b0f2f1796a100ae9133a" + integrity sha512-Zo9c7N3xdOIQrNip7Lc9wvRPzlRtovHVE4lkz8WEDr7uYh/GMQhSiIgFxGIArRHYdJE5kxtZjAf8rT0xhdLCzg== "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6": version "7.18.6" @@ -2451,23 +2452,23 @@ "@babel/parser" "^7.24.0" "@babel/types" "^7.24.0" -"@babel/traverse@^7.18.9", "@babel/traverse@^7.19.0", "@babel/traverse@^7.19.1", "@babel/traverse@^7.22.5", "@babel/traverse@^7.23.2", "@babel/traverse@^7.24.0", "@babel/traverse@^7.4.5", "@babel/traverse@^7.7.0", "@babel/traverse@^7.7.2": - version "7.24.0" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.24.0.tgz#4a408fbf364ff73135c714a2ab46a5eab2831b1e" - integrity sha512-HfuJlI8qq3dEDmNU5ChzzpZRWq+oxCZQyMzIMEqLho+AQnhMnKQUzH6ydo3RBl/YjPCuk68Y6s0Gx0AeyULiWw== +"@babel/traverse@^7.18.9", "@babel/traverse@^7.19.0", "@babel/traverse@^7.19.1", "@babel/traverse@^7.22.5", "@babel/traverse@^7.23.2", "@babel/traverse@^7.24.1", "@babel/traverse@^7.4.5", "@babel/traverse@^7.7.0", "@babel/traverse@^7.7.2": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.24.1.tgz#d65c36ac9dd17282175d1e4a3c49d5b7988f530c" + integrity sha512-xuU6o9m68KeqZbQuDt2TcKSxUw/mrsvavlEqQ1leZ/B+C9tk6E4sRWy97WaXgvq5E+nU3cXMxv3WKOCanVMCmQ== dependencies: - "@babel/code-frame" "^7.23.5" - "@babel/generator" "^7.23.6" + "@babel/code-frame" "^7.24.1" + "@babel/generator" "^7.24.1" "@babel/helper-environment-visitor" "^7.22.20" "@babel/helper-function-name" "^7.23.0" "@babel/helper-hoist-variables" "^7.22.5" "@babel/helper-split-export-declaration" "^7.22.6" - "@babel/parser" "^7.24.0" + "@babel/parser" "^7.24.1" "@babel/types" "^7.24.0" debug "^4.3.1" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.12.6", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.19.0", "@babel/types@^7.19.3", "@babel/types@^7.20.7", "@babel/types@^7.22.15", "@babel/types@^7.22.19", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.4", "@babel/types@^7.23.6", "@babel/types@^7.24.0", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4", "@babel/types@^7.7.0": +"@babel/types@^7.0.0", "@babel/types@^7.12.6", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.19.0", "@babel/types@^7.19.3", "@babel/types@^7.20.7", "@babel/types@^7.22.15", "@babel/types@^7.22.19", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.4", "@babel/types@^7.24.0", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4", "@babel/types@^7.7.0": version "7.24.0" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.24.0.tgz#3b951f435a92e7333eba05b7566fd297960ea1bf" integrity sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w== @@ -3341,7 +3342,7 @@ "@types/yargs" "^17.0.8" chalk "^4.0.0" -"@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": +"@jridgewell/gen-mapping@^0.3.0": version "0.3.2" resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9" integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A== @@ -3350,16 +3351,35 @@ "@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/trace-mapping" "^0.3.9" +"@jridgewell/gen-mapping@^0.3.5": + version "0.3.5" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36" + integrity sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg== + dependencies: + "@jridgewell/set-array" "^1.2.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.24" + "@jridgewell/resolve-uri@3.1.0", "@jridgewell/resolve-uri@^3.0.3": version "3.1.0" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== + "@jridgewell/set-array@^1.0.1": version "1.1.2" resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== +"@jridgewell/set-array@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280" + integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== + "@jridgewell/source-map@^0.3.2": version "0.3.2" resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.2.tgz#f45351aaed4527a298512ec72f81040c998580fb" @@ -3381,7 +3401,7 @@ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== -"@jridgewell/sourcemap-codec@^1.4.15": +"@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.4.15": version "1.4.15" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== @@ -3410,6 +3430,14 @@ "@jridgewell/resolve-uri" "3.1.0" "@jridgewell/sourcemap-codec" "1.4.14" +"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": + version "0.3.25" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" + integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + "@juggle/resize-observer@^3.3.1": version "3.4.0" resolved "https://registry.yarnpkg.com/@juggle/resize-observer/-/resize-observer-3.4.0.tgz#08d6c5e20cf7e4cc02fd181c4b0c225cd31dbb60" From ebcc411e22d6e7f7a74962eec75817e765a30d1d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Apr 2024 07:40:53 +0000 Subject: [PATCH 03/28] Bump @babel/preset-react from 7.23.3 to 7.24.1 (#1281) Bumps [@babel/preset-react](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-react) from 7.23.3 to 7.24.1. - [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.24.1/packages/babel-preset-react) --- updated-dependencies: - dependency-name: "@babel/preset-react" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- common/package.json | 2 +- yarn.lock | 48 ++++++++++++++++++++++----------------------- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/common/package.json b/common/package.json index da9850343..b2cd5f54c 100644 --- a/common/package.json +++ b/common/package.json @@ -42,7 +42,7 @@ }, "devDependencies": { "@babel/preset-env": "^7.22.20", - "@babel/preset-react": "^7.23.3", + "@babel/preset-react": "^7.24.1", "@babel/preset-typescript": "^7.23.2", "@recidiviz/tsconfig": "^2.0.0", "@storybook/addon-essentials": "^7.6.14", diff --git a/yarn.lock b/yarn.lock index e9add2a93..ebd75fb2e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -412,10 +412,10 @@ dependencies: "@babel/types" "^7.22.5" -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.19.0", "@babel/helper-plugin-utils@^7.22.5", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz#dd7ee3735e8a313b9f7b05a773d892e88e6d7295" - integrity sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg== +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.19.0", "@babel/helper-plugin-utils@^7.22.5", "@babel/helper-plugin-utils@^7.24.0", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": + version "7.24.0" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.0.tgz#945681931a52f15ce879fd5b86ce2dae6d3d7f2a" + integrity sha512-9cUznXMG0+FxRuJfvL82QlTqIzhVW9sL0KjMPHhAOOvpQGL8QtdxnBKILjBqxlHyliz0yCa1G903ZXI/FuHy2w== "@babel/helper-remap-async-to-generator@^7.18.6", "@babel/helper-remap-async-to-generator@^7.18.9": version "7.18.9" @@ -1809,12 +1809,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.9" -"@babel/plugin-transform-react-display-name@^7.16.0", "@babel/plugin-transform-react-display-name@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.23.3.tgz#70529f034dd1e561045ad3c8152a267f0d7b6200" - integrity sha512-GnvhtVfA2OAtzdX58FJxU19rhoGeQzyVndw3GgtdECQvQFXPEZIOVULHVZGAYmOgmqjXpVpfocAbSjh99V/Fqw== +"@babel/plugin-transform-react-display-name@^7.16.0", "@babel/plugin-transform-react-display-name@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.24.1.tgz#554e3e1a25d181f040cf698b93fd289a03bfdcdb" + integrity sha512-mvoQg2f9p2qlpDQRBC7M3c3XTr0k7cp/0+kFKKO/7Gtu0LSw16eKB+Fabe2bDT/UpsyasTBBkAnbdsLrkD5XMw== dependencies: - "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-plugin-utils" "^7.24.0" "@babel/plugin-transform-react-jsx-development@^7.22.5": version "7.22.5" @@ -1823,7 +1823,7 @@ dependencies: "@babel/plugin-transform-react-jsx" "^7.22.5" -"@babel/plugin-transform-react-jsx@^7.22.15", "@babel/plugin-transform-react-jsx@^7.22.5", "@babel/plugin-transform-react-jsx@^7.23.4": +"@babel/plugin-transform-react-jsx@^7.22.5", "@babel/plugin-transform-react-jsx@^7.23.4": version "7.23.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.23.4.tgz#393f99185110cea87184ea47bcb4a7b0c2e39312" integrity sha512-5xOpoPguCZCRbo/JeHlloSkTA8Bld1J/E1/kLfD1nsuiW1m8tduTA1ERCgIZokDflX/IBzKcqR3l7VlRgiIfHA== @@ -1834,13 +1834,13 @@ "@babel/plugin-syntax-jsx" "^7.23.3" "@babel/types" "^7.23.4" -"@babel/plugin-transform-react-pure-annotations@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.23.3.tgz#fabedbdb8ee40edf5da96f3ecfc6958e3783b93c" - integrity sha512-qMFdSS+TUhB7Q/3HVPnEdYJDQIk57jkntAwSuz9xfSE4n+3I+vHYCli3HoHawN1Z3RfCz/y1zXA/JXjG6cVImQ== +"@babel/plugin-transform-react-pure-annotations@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.24.1.tgz#c86bce22a53956331210d268e49a0ff06e392470" + integrity sha512-+pWEAaDJvSm9aFvJNpLiM2+ktl2Sn2U5DdyiWdZBxmLc6+xGt88dvFqsHiAiDS+8WqUwbDfkKz9jRxK3M0k+kA== dependencies: "@babel/helper-annotate-as-pure" "^7.22.5" - "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-plugin-utils" "^7.24.0" "@babel/plugin-transform-regenerator@^7.18.6": version "7.18.6" @@ -2386,17 +2386,17 @@ "@babel/types" "^7.4.4" esutils "^2.0.2" -"@babel/preset-react@^7.12.5", "@babel/preset-react@^7.16.0", "@babel/preset-react@^7.22.15", "@babel/preset-react@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.23.3.tgz#f73ca07e7590f977db07eb54dbe46538cc015709" - integrity sha512-tbkHOS9axH6Ysf2OUEqoSZ6T3Fa2SrNH6WTWSPBboxKzdxNc9qOICeLXkNG0ZEwbQ1HY8liwOce4aN/Ceyuq6w== +"@babel/preset-react@^7.12.5", "@babel/preset-react@^7.16.0", "@babel/preset-react@^7.22.15", "@babel/preset-react@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.24.1.tgz#2450c2ac5cc498ef6101a6ca5474de251e33aa95" + integrity sha512-eFa8up2/8cZXLIpkafhaADTXSnl7IsUFCYenRWrARBz0/qZwcT0RBXpys0LJU4+WfPoF2ZG6ew6s2V6izMCwRA== dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-validator-option" "^7.22.15" - "@babel/plugin-transform-react-display-name" "^7.23.3" - "@babel/plugin-transform-react-jsx" "^7.22.15" + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/helper-validator-option" "^7.23.5" + "@babel/plugin-transform-react-display-name" "^7.24.1" + "@babel/plugin-transform-react-jsx" "^7.23.4" "@babel/plugin-transform-react-jsx-development" "^7.22.5" - "@babel/plugin-transform-react-pure-annotations" "^7.23.3" + "@babel/plugin-transform-react-pure-annotations" "^7.24.1" "@babel/preset-typescript@^7.16.0", "@babel/preset-typescript@^7.23.2": version "7.23.2" From 98a56bb585f00c6275317a27a9cc79c0351f0d29 Mon Sep 17 00:00:00 2001 From: Ilya Date: Mon, 1 Apr 2024 15:44:20 +0300 Subject: [PATCH 04/28] [Publisher][Admin Panel] Auto-add users/agencies created via second flow (#1269) * Auto-add logic for users/agencies created via second flow * Get created user response dynamically * Get created agency response dynamically * Add explanation comments --- .../AdminPanel/AgencyProvisioning.tsx | 18 +++++++++++- .../AdminPanel/AgencyProvisioningOverview.tsx | 4 +++ .../AdminPanel/UserProvisioning.tsx | 21 ++++++++++++-- .../AdminPanel/UserProvisioningOverview.tsx | 4 +++ publisher/src/components/AdminPanel/types.ts | 2 ++ publisher/src/stores/AdminPanelStore.ts | 29 ++++++++++++++++++- 6 files changed, 74 insertions(+), 4 deletions(-) diff --git a/publisher/src/components/AdminPanel/AgencyProvisioning.tsx b/publisher/src/components/AdminPanel/AgencyProvisioning.tsx index 2a51ba0ff..33317d748 100644 --- a/publisher/src/components/AdminPanel/AgencyProvisioning.tsx +++ b/publisher/src/components/AdminPanel/AgencyProvisioning.tsx @@ -68,6 +68,8 @@ export const AgencyProvisioning: React.FC = observer( activeSecondaryModal, openSecondaryModal, closeModal, + secondaryCreatedId, + setSecondaryCreatedId, }) => { const { adminPanelStore, api, userStore } = useStore(); const { @@ -302,8 +304,15 @@ export const AgencyProvisioning: React.FC = observer( show: false, errorMessage: undefined, })); - if (response && "status" in response && response.status === 200) + if (response && "status" in response && response.status === 200) { + const agencyResponse = adminPanelStore.createdAgencyResponse; + if (setSecondaryCreatedId && agencyResponse) { + /** If this view is the secondary create modal, then we'll store the newly created ID for the purpose of auto-adding after creation */ + const createdAgencyId = agencyResponse.id; + setSecondaryCreatedId(createdAgencyId); + } closeModal(true); + } setIsSaveInProgress(false); }, 2000); }; @@ -500,12 +509,19 @@ export const AgencyProvisioning: React.FC = observer( }; }); } + + /** Here we are making the auto-adding if something was created via the secondary modal */ + if (secondaryCreatedId) + setSelectedTeamMembersToAdd((prev) => + toggleAddRemoveSetItem(prev, +secondaryCreatedId) + ); }, [ selectedAgency, adminPanelStore, api, csgAndRecidivizUsers, csgAndRecidivizDefaultRole, + secondaryCreatedId, ]); return ( diff --git a/publisher/src/components/AdminPanel/AgencyProvisioningOverview.tsx b/publisher/src/components/AdminPanel/AgencyProvisioningOverview.tsx index a6e3fa046..4763ea90e 100644 --- a/publisher/src/components/AdminPanel/AgencyProvisioningOverview.tsx +++ b/publisher/src/components/AdminPanel/AgencyProvisioningOverview.tsx @@ -65,6 +65,7 @@ export const AgencyProvisioningOverview = observer(() => { const [selectedAgencyID, setSelectedAgencyID] = useState(); const [activeSecondaryModal, setActiveSecondaryModal] = useState(); + const [userId, setUserId] = useState(); const searchByKeys = ["name", "id", "state"] as AgencyKey[]; const superagenciesAndAgenciesWithLiveDashboards = agencies.filter( @@ -103,6 +104,7 @@ export const AgencyProvisioningOverview = observer(() => { const openModal = () => setIsModalOpen(true); const openSecondaryModal = () => setActiveSecondaryModal(Setting.USERS); + const setSecondaryCreatedId = (id: string | number) => setUserId(id); const closeModal = (resetSearchInput?: boolean) => { if (!activeSecondaryModal) { setSelectedAgencyID(undefined); @@ -154,6 +156,7 @@ export const AgencyProvisioningOverview = observer(() => { selectedIDToEdit={selectedAgencyID} activeSecondaryModal={activeSecondaryModal} openSecondaryModal={openSecondaryModal} + secondaryCreatedId={userId} /> @@ -164,6 +167,7 @@ export const AgencyProvisioningOverview = observer(() => { diff --git a/publisher/src/components/AdminPanel/UserProvisioning.tsx b/publisher/src/components/AdminPanel/UserProvisioning.tsx index 6e7de2c47..18635ead8 100644 --- a/publisher/src/components/AdminPanel/UserProvisioning.tsx +++ b/publisher/src/components/AdminPanel/UserProvisioning.tsx @@ -23,7 +23,7 @@ import { validateEmail, } from "@justice-counts/common/utils"; import { observer } from "mobx-react-lite"; -import React, { useState } from "react"; +import React, { useEffect, useState } from "react"; import { useStore } from "../../stores"; import AdminPanelStore from "../../stores/AdminPanelStore"; @@ -47,6 +47,8 @@ export const UserProvisioning: React.FC = observer( activeSecondaryModal, openSecondaryModal, closeModal, + secondaryCreatedId, + setSecondaryCreatedId, }) => { const { adminPanelStore } = useStore(); const { @@ -105,6 +107,14 @@ export const UserProvisioning: React.FC = observer( : []), ]; + /** Here we are making the auto-adding if something was created via the secondary modal */ + useEffect(() => { + if (secondaryCreatedId) + setAddedAgenciesIDs((prev) => + toggleAddRemoveSetItem(prev, +secondaryCreatedId) + ); + }, [secondaryCreatedId]); + /** Whether or not we are performing an add/delete action on an agencies' list */ const isAddAction = addOrDeleteAgencyAction === InteractiveSearchListActions.ADD; @@ -227,8 +237,15 @@ export const UserProvisioning: React.FC = observer( /** After showing the confirmation screen, either return to modal (on error) or close modal (on success) */ setTimeout(() => { setShowSaveConfirmation((prev) => ({ ...prev, show: false })); - if (response && "status" in response && response.status === 200) + if (response && "status" in response && response.status === 200) { + const userResponse = adminPanelStore.createdUserResponse; + if (setSecondaryCreatedId && userResponse) { + /** If this view is the secondary create modal, then we'll store the newly created ID for the purpose of auto-adding after creation */ + const createdUserId = userResponse.users[0].id; + setSecondaryCreatedId(createdUserId); + } closeModal(true); + } setIsSaveInProgress(false); }, 2000); }; diff --git a/publisher/src/components/AdminPanel/UserProvisioningOverview.tsx b/publisher/src/components/AdminPanel/UserProvisioningOverview.tsx index 62eea8c37..16fddcfca 100644 --- a/publisher/src/components/AdminPanel/UserProvisioningOverview.tsx +++ b/publisher/src/components/AdminPanel/UserProvisioningOverview.tsx @@ -58,6 +58,7 @@ export const UserProvisioningOverview = observer(() => { show: boolean; user?: UserWithAgenciesByID; }>({ show: false }); + const [agencyId, setAgencyId] = useState(); const [searchInput, setSearchInput] = useState(""); @@ -70,6 +71,7 @@ export const UserProvisioningOverview = observer(() => { const openModal = () => setIsModalOpen(true); const openSecondaryModal = () => setActiveSecondaryModal(Setting.AGENCIES); + const setSecondaryCreatedId = (id: string | number) => setAgencyId(id); const closeModal = (resetSearchInput?: boolean) => { if (!activeSecondaryModal) { resetUserProvisioningUpdates(); @@ -131,6 +133,7 @@ export const UserProvisioningOverview = observer(() => { selectedIDToEdit={selectedUserID} activeSecondaryModal={activeSecondaryModal} openSecondaryModal={openSecondaryModal} + secondaryCreatedId={agencyId} /> @@ -141,6 +144,7 @@ export const UserProvisioningOverview = observer(() => { diff --git a/publisher/src/components/AdminPanel/types.ts b/publisher/src/components/AdminPanel/types.ts index 6b08a5ac2..fa6a85599 100644 --- a/publisher/src/components/AdminPanel/types.ts +++ b/publisher/src/components/AdminPanel/types.ts @@ -48,6 +48,8 @@ export type ProvisioningProps = { activeSecondaryModal?: SettingType; openSecondaryModal?: () => void; closeModal: (resetSearchInput?: boolean) => void; + secondaryCreatedId?: string | number; + setSecondaryCreatedId?: (id: string | number) => void; }; export enum SelectionInputBoxTypes { diff --git a/publisher/src/stores/AdminPanelStore.ts b/publisher/src/stores/AdminPanelStore.ts index 7428811dd..48a7fe5fa 100644 --- a/publisher/src/stores/AdminPanelStore.ts +++ b/publisher/src/stores/AdminPanelStore.ts @@ -80,6 +80,10 @@ class AdminPanelStore { agencyProvisioningUpdates: AgencyProvisioningUpdates; + userResponse?: UserResponse; + + agencyResponse?: Agency; + constructor(api: API) { makeAutoObservable(this, {}, { autoBind: true }); this.api = api; @@ -151,6 +155,14 @@ class AdminPanelStore { }); } + get createdUserResponse(): UserResponse | undefined { + return this.userResponse; + } + + get createdAgencyResponse(): Agency | undefined { + return this.agencyResponse; + } + async fetchUsers() { try { const response = (await this.api.request({ @@ -247,6 +259,10 @@ class AdminPanelStore { /** User Provisioning */ + setCreatedUserResponse(userResponse: UserResponse) { + this.userResponse = userResponse; + } + updateUsername(username: string) { this.userProvisioningUpdates.name = username.trimStart(); } @@ -288,7 +304,10 @@ class AdminPanelStore { | ErrorResponse; if (response.status === 200) { - runInAction(() => this.updateUsers(userResponse as UserResponse)); + runInAction(() => { + this.updateUsers(userResponse as UserResponse); + this.setCreatedUserResponse(userResponse as UserResponse); + }); return response; } @@ -323,6 +342,10 @@ class AdminPanelStore { /** Agency Provisioning */ + setCreatedAgencyResponse(agencyResponse: Agency) { + this.agencyResponse = agencyResponse; + } + updateAgencyID(id: number) { this.agencyProvisioningUpdates.agency_id = id; } @@ -393,6 +416,10 @@ class AdminPanelStore { const agencyResponse = (await response.json()) as Agency | ErrorResponse; if (response.status === 200) { + runInAction(() => + this.setCreatedAgencyResponse(agencyResponse as Agency) + ); + if (!refetch) { runInAction(() => this.updateAgencies(agencyResponse as Agency)); } else { From ec3604f646eb61df430638bcd777268228a90b58 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Apr 2024 08:05:15 +0000 Subject: [PATCH 05/28] Bump @babel/plugin-syntax-flow from 7.22.5 to 7.24.1 (#1287) Bumps [@babel/plugin-syntax-flow](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-syntax-flow) from 7.22.5 to 7.24.1. - [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.24.1/packages/babel-plugin-syntax-flow) --- updated-dependencies: - dependency-name: "@babel/plugin-syntax-flow" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- publisher/package.json | 2 +- yarn.lock | 17 +++++------------ 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/publisher/package.json b/publisher/package.json index b2e864ab8..85a14e274 100644 --- a/publisher/package.json +++ b/publisher/package.json @@ -59,7 +59,7 @@ "devDependencies": { "@auth0/auth0-spa-js": "^1.20.1", "@babel/core": "^7.24.3", - "@babel/plugin-syntax-flow": "^7.22.5", + "@babel/plugin-syntax-flow": "^7.24.1", "@babel/plugin-transform-react-jsx": "^7.23.4", "@recidiviz/eslint-config": "^3.0.0", "@recidiviz/tsconfig": "^2.0.0", diff --git a/yarn.lock b/yarn.lock index ebd75fb2e..c945c8098 100644 --- a/yarn.lock +++ b/yarn.lock @@ -841,19 +841,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" -"@babel/plugin-syntax-flow@^7.18.6", "@babel/plugin-syntax-flow@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.22.5.tgz#163b820b9e7696ce134df3ee716d9c0c98035859" - integrity sha512-9RdCl0i+q0QExayk2nOS7853w08yLucnnPML6EN9S8fgMPVtdLDCdx/cOQ/i44Lb9UeQX9A35yaqBBOMMZxPxQ== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-syntax-flow@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.23.3.tgz#084564e0f3cc21ea6c70c44cff984a1c0509729a" - integrity sha512-YZiAIpkJAwQXBJLIQbRFayR5c+gJ35Vcz3bg954k7cd73zqjvhacJuL9RbrzPz8qPmZdgqP6EUKwy0PCNhaaPA== +"@babel/plugin-syntax-flow@^7.18.6", "@babel/plugin-syntax-flow@^7.23.3", "@babel/plugin-syntax-flow@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.24.1.tgz#875c25e3428d7896c87589765fc8b9d32f24bd8d" + integrity sha512-sxi2kLTI5DeW5vDtMUsk4mTPwvlUDbjOnoWayhynCwrw4QXRld4QEYwqzY8JmQXaJUtgUuCIurtSRH5sn4c7mA== dependencies: - "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-plugin-utils" "^7.24.0" "@babel/plugin-syntax-import-assertions@^7.18.6": version "7.18.6" From 555db94979dcd7854aca4bfee643022aa024faf0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Apr 2024 08:06:22 +0000 Subject: [PATCH 06/28] Bump @auth0/auth0-spa-js from 1.22.5 to 1.22.6 (#1288) Bumps [@auth0/auth0-spa-js](https://github.com/auth0/auth0-spa-js) from 1.22.5 to 1.22.6. - [Release notes](https://github.com/auth0/auth0-spa-js/releases) - [Changelog](https://github.com/auth0/auth0-spa-js/blob/main/CHANGELOG.md) - [Commits](https://github.com/auth0/auth0-spa-js/compare/v1.22.5...v1.22.6) --- updated-dependencies: - dependency-name: "@auth0/auth0-spa-js" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- publisher/package.json | 2 +- yarn.lock | 23 +++++++++-------------- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/publisher/package.json b/publisher/package.json index 85a14e274..c14f940de 100644 --- a/publisher/package.json +++ b/publisher/package.json @@ -57,7 +57,7 @@ }, "homepage": "/", "devDependencies": { - "@auth0/auth0-spa-js": "^1.20.1", + "@auth0/auth0-spa-js": "^1.22.6", "@babel/core": "^7.24.3", "@babel/plugin-syntax-flow": "^7.24.1", "@babel/plugin-transform-react-jsx": "^7.23.4", diff --git a/yarn.lock b/yarn.lock index c945c8098..a4416d823 100644 --- a/yarn.lock +++ b/yarn.lock @@ -36,14 +36,14 @@ dependencies: "@auth0/auth0-spa-js" "^2.1.3" -"@auth0/auth0-spa-js@^1.20.1": - version "1.22.5" - resolved "https://registry.yarnpkg.com/@auth0/auth0-spa-js/-/auth0-spa-js-1.22.5.tgz#e48be3a6abfa6f63ab6e4b869d04788b4f229f23" - integrity sha512-6gaQcd+Eb8ZBcdQkrrm9undM7dY/rPvVdQN8s7rxxrviUCs7OopEygsfSkHf67IP4HtlCiE8dSW5/AipRUOw/A== +"@auth0/auth0-spa-js@^1.22.6": + version "1.22.6" + resolved "https://registry.yarnpkg.com/@auth0/auth0-spa-js/-/auth0-spa-js-1.22.6.tgz#482c7cf546649e856020d20843cd496258bd9fa0" + integrity sha512-iL3O0vWanfKFVgy1J2ZHDPlAUK6EVHWEHWS6mUXwHEuPiK39tjlQtyUKQIJI1F5YsZB75ijGgRWMTawSDXlwCA== dependencies: abortcontroller-polyfill "^1.7.3" browser-tabs-lock "^1.2.15" - core-js "^3.25.1" + core-js "^3.25.4" es-cookie "~1.3.2" fast-text-encoding "^1.0.6" promise-polyfill "^8.2.3" @@ -7879,15 +7879,10 @@ core-js-pure@^3.8.1: resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.25.5.tgz#79716ba54240c6aa9ceba6eee08cf79471ba184d" integrity sha512-oml3M22pHM+igfWHDfdLVq2ShWmjM2V4L+dQEBs0DWVIqEm9WHCwGAlZ6BmyBQGy5sFrJmcx+856D9lVKyGWYg== -core-js@^3.19.2: - version "3.25.5" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.25.5.tgz#e86f651a2ca8a0237a5f064c2fe56cef89646e27" - integrity sha512-nbm6eZSjm+ZuBQxCUPQKQCoUEfFOXjUZ8dTTyikyKaWrTYmAVbykQfwsKE5dBK88u3QCkCrzsx/PPlKfhsvgpw== - -core-js@^3.25.1: - version "3.26.1" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.26.1.tgz#7a9816dabd9ee846c1c0fe0e8fcad68f3709134e" - integrity sha512-21491RRQVzUn0GGM9Z1Jrpr6PNPxPi+Za8OM9q4tksTSnlbXXGKK1nXNg/QvwFYettXvSX6zWKCtHHfjN4puyA== +core-js@^3.19.2, core-js@^3.25.4: + version "3.36.1" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.36.1.tgz#c97a7160ebd00b2de19e62f4bbd3406ab720e578" + integrity sha512-BTvUrwxVBezj5SZ3f10ImnX2oRByMxql3EimVqMysepbC9EeMUOpLwdy6Eoili2x6E4kf+ZUB5k/+Jv55alPfA== core-util-is@~1.0.0: version "1.0.3" From b1c5045ccbb489e69a87889732dec7ffb76d229e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Apr 2024 08:10:22 +0000 Subject: [PATCH 07/28] Bump eslint-config-prettier from 9.0.0 to 9.1.0 (#1290) Bumps [eslint-config-prettier](https://github.com/prettier/eslint-config-prettier) from 9.0.0 to 9.1.0. - [Changelog](https://github.com/prettier/eslint-config-prettier/blob/main/CHANGELOG.md) - [Commits](https://github.com/prettier/eslint-config-prettier/compare/v9.0.0...v9.1.0) --- updated-dependencies: - dependency-name: eslint-config-prettier dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- publisher/package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/publisher/package.json b/publisher/package.json index c14f940de..102b465f4 100644 --- a/publisher/package.json +++ b/publisher/package.json @@ -88,7 +88,7 @@ "customize-cra": "^1.0.0", "eslint": "^8.57.0", "eslint-config-airbnb": "^19.0.4", - "eslint-config-prettier": "^9.0.0", + "eslint-config-prettier": "^9.1.0", "eslint-config-react-app": "^7.0.0", "eslint-import-resolver-typescript": "^3.6.1", "eslint-plugin-header": "^3.1.1", diff --git a/yarn.lock b/yarn.lock index a4416d823..4bf62b534 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9325,10 +9325,10 @@ eslint-config-prettier@^8.5.0: resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz#5a81680ec934beca02c7b1a61cf8ca34b66feab1" integrity sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q== -eslint-config-prettier@^9.0.0: - version "9.0.0" - resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-9.0.0.tgz#eb25485946dd0c66cd216a46232dc05451518d1f" - integrity sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw== +eslint-config-prettier@^9.1.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz#31af3d94578645966c082fcb71a5846d3c94867f" + integrity sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw== eslint-config-react-app@^7.0.0, eslint-config-react-app@^7.0.1: version "7.0.1" From e695ea5cda6c015e5982f86853cbd579b74e061c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Apr 2024 08:15:08 +0000 Subject: [PATCH 08/28] Bump recharts from 2.12.3 to 2.12.4 (#1291) Bumps [recharts](https://github.com/recharts/recharts) from 2.12.3 to 2.12.4. - [Release notes](https://github.com/recharts/recharts/releases) - [Changelog](https://github.com/recharts/recharts/blob/3.x/CHANGELOG.md) - [Commits](https://github.com/recharts/recharts/compare/v2.12.3...v2.12.4) --- updated-dependencies: - dependency-name: recharts dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- common/package.json | 2 +- publisher/package.json | 2 +- yarn.lock | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/common/package.json b/common/package.json index b2cd5f54c..5a06abff0 100644 --- a/common/package.json +++ b/common/package.json @@ -23,7 +23,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-tooltip": "^5.26.3", - "recharts": "^2.12.3", + "recharts": "^2.12.4", "styled-components": "^5.3.11", "typescript": "^5.2.2" }, diff --git a/publisher/package.json b/publisher/package.json index 102b465f4..40be95380 100644 --- a/publisher/package.json +++ b/publisher/package.json @@ -25,7 +25,7 @@ "react-is": "^18.2.0", "react-router-dom": "^6", "react-tooltip": "^5.26.3", - "recharts": "^2.12.3", + "recharts": "^2.12.4", "recharts-to-png": "^2.3.1", "styled-components": "^5.3.11", "typescript": "^5.2.2" diff --git a/yarn.lock b/yarn.lock index 4bf62b534..b539fb9be 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14742,10 +14742,10 @@ recharts-to-png@^2.3.1: dependencies: html2canvas "^1.2.0" -recharts@^2.12.3: - version "2.12.3" - resolved "https://registry.yarnpkg.com/recharts/-/recharts-2.12.3.tgz#c059bd34eaf1c80d23fc2b953ad3abfa9474694a" - integrity sha512-vE/F7wTlokf5mtCqVDJlVKelCjliLSJ+DJxj79XlMREm7gpV7ljwbrwE3CfeaoDlOaLX+6iwHaVRn9587YkwIg== +recharts@^2.12.4: + version "2.12.4" + resolved "https://registry.yarnpkg.com/recharts/-/recharts-2.12.4.tgz#e560a57cd44ab554c99a0d93bdd58d059b309a2e" + integrity sha512-dM4skmk4fDKEDjL9MNunxv6zcTxePGVEzRnLDXALRpfJ85JoQ0P0APJ/CoJlmnQI0gPjBlOkjzrwrfQrRST3KA== dependencies: clsx "^2.0.0" eventemitter3 "^4.0.1" From 0644e76c36ae794963647e5d4992246578fe37e2 Mon Sep 17 00:00:00 2001 From: Mahmoud O <59492998+mxosman@users.noreply.github.com> Date: Mon, 8 Apr 2024 12:42:21 -0500 Subject: [PATCH 09/28] Temp fix for bug crashing records page (#1293) Co-authored-by: Mahmoud --- publisher/src/stores/FormStore.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/publisher/src/stores/FormStore.ts b/publisher/src/stores/FormStore.ts index 6d71d1991..3faec9da0 100644 --- a/publisher/src/stores/FormStore.ts +++ b/publisher/src/stores/FormStore.ts @@ -356,7 +356,8 @@ class FormStore { dimensionKey ].error; } else { - delete this.metricsValues[reportID][metricKey].error; + // TODO(#28666) Investigate why metricKey is undefined for certain records + delete this.metricsValues[reportID]?.[metricKey]?.error; } } }; From df32c39392002085ac0d78e3816f41ad16fce0af Mon Sep 17 00:00:00 2001 From: Nichelle Date: Tue, 9 Apr 2024 00:16:51 +0200 Subject: [PATCH 10/28] send agency id with dashboard url (#1285) --- agency-dashboard/src/App.tsx | 4 +-- agency-dashboard/src/Home/Home.tsx | 4 ++- agency-dashboard/src/Protected/Protected.tsx | 28 +++++++++++++------ .../src/stores/AgencyDataStore.ts | 7 +++-- .../HelpCenter/LinkToPublisherDashboard.tsx | 5 ++-- publisher/src/components/Menu/Menu.tsx | 2 +- 6 files changed, 34 insertions(+), 16 deletions(-) diff --git a/agency-dashboard/src/App.tsx b/agency-dashboard/src/App.tsx index 67bed6c11..2cfae12fa 100644 --- a/agency-dashboard/src/App.tsx +++ b/agency-dashboard/src/App.tsx @@ -29,7 +29,7 @@ function App() { } /> @@ -37,7 +37,7 @@ function App() { } /> diff --git a/agency-dashboard/src/Home/Home.tsx b/agency-dashboard/src/Home/Home.tsx index fdbc228a2..85ca25800 100644 --- a/agency-dashboard/src/Home/Home.tsx +++ b/agency-dashboard/src/Home/Home.tsx @@ -66,7 +66,9 @@ export const Home = observer(() => { {agenciesMetadataSortedByName.map((agency) => ( navigate(`/agency/${encodeURI(agency.name)}`)} + onClick={() => + navigate(`/agency/${agency.id}/${encodeURI(agency.name)}`) + } > {agency.name} diff --git a/agency-dashboard/src/Protected/Protected.tsx b/agency-dashboard/src/Protected/Protected.tsx index be3bd8aba..5e1295fb0 100644 --- a/agency-dashboard/src/Protected/Protected.tsx +++ b/agency-dashboard/src/Protected/Protected.tsx @@ -30,27 +30,39 @@ export const Protected: React.FC = observer( ({ children }) => { const navigate = useNavigate(); const { agencyDataStore, api } = useStore(); - const { slug } = useParams(); + const { agencyId, slug } = useParams(); const isProductionEnv = api.environment === environment.PRODUCTION; const isDenied = - agencyDataStore.agency && - agencyDataStore.agency.is_dashboard_enabled !== true; - + (agencyDataStore.agency && + agencyDataStore.agency.is_dashboard_enabled !== true) || + !agencyId; const [loading, setLoading] = useState(true); useAsyncEffect(async () => { try { - await agencyDataStore.fetchAgencyData(slug as string); - setLoading(false); + if (!agencyId) { + setLoading(false); + showToast({ + message: `No agency ID was specified in the URL path.`, + color: "red", + timeout: 4000, + }); + } else { + await agencyDataStore.fetchAgencyData( + parseInt(agencyId), + slug as string + ); + setLoading(false); + } } catch (error) { navigate("/404"); showToast({ - message: `No agency found with path /${slug}.`, + message: `No agency found with path ${agencyId}/${slug}.`, color: "red", timeout: 4000, }); } - }, [slug]); + }, [agencyId, slug]); if (loading) { return ; diff --git a/agency-dashboard/src/stores/AgencyDataStore.ts b/agency-dashboard/src/stores/AgencyDataStore.ts index d1b52f158..6842172c1 100644 --- a/agency-dashboard/src/stores/AgencyDataStore.ts +++ b/agency-dashboard/src/stores/AgencyDataStore.ts @@ -196,13 +196,16 @@ class AgencyDataStore { return result; } - async fetchAgencyData(agencySlug: string): Promise { + async fetchAgencyData( + agencyId: number, + agencySlug: string + ): Promise { try { runInAction(() => { this.loading = true; }); const response = (await API.request({ - path: `/api/v2/agencies/${encodeURIComponent( + path: `/api/agencies/${agencyId}/${encodeURIComponent( agencySlug )}/published_data`, method: "GET", diff --git a/publisher/src/components/HelpCenter/LinkToPublisherDashboard.tsx b/publisher/src/components/HelpCenter/LinkToPublisherDashboard.tsx index 86c5299ea..204283ab5 100644 --- a/publisher/src/components/HelpCenter/LinkToPublisherDashboard.tsx +++ b/publisher/src/components/HelpCenter/LinkToPublisherDashboard.tsx @@ -49,7 +49,7 @@ export const LinkToDashboard: React.FC< if (!agencyName) return <>{children}; - const url = generateDashboardURL(api.environment, agencyName); + const url = generateDashboardURL(api.environment, agencyId, agencyName); return ( @@ -60,8 +60,9 @@ export const LinkToDashboard: React.FC< export const generateDashboardURL = ( env: string | undefined, + agencyId: string | undefined, agencyName: string | undefined ) => `https://dashboard-${ env !== "production" ? "staging" : "demo" - }.justice-counts.org/agency/${encodeURI(agencyName || "")}`; + }.justice-counts.org/agency/${agencyId}/${encodeURI(agencyName || "")}`; diff --git a/publisher/src/components/Menu/Menu.tsx b/publisher/src/components/Menu/Menu.tsx index 0ebcfe57a..88f9858c2 100644 --- a/publisher/src/components/Menu/Menu.tsx +++ b/publisher/src/components/Menu/Menu.tsx @@ -137,7 +137,7 @@ const Menu: React.FC = () => { label: "Agency Dashboard", onClick: () => window.open( - generateDashboardURL(api.environment, agencyName), + generateDashboardURL(api.environment, agencyId, agencyName), "_blank" ), }, From c37fa9b60e83f93a68ea7ef6253240f04574b414 Mon Sep 17 00:00:00 2001 From: Ilya Date: Wed, 10 Apr 2024 15:40:30 +0300 Subject: [PATCH 11/28] [Publisher][Admin Panel] Add functionality to select all filtered items from InteractiveSearchList (#1286) * Add functionality to select all filtered items from InteractiveSearchList * Create filteredSet from filteredList w/o filter func --- .../AdminPanel/AgencyProvisioning.tsx | 9 +++++++- .../AdminPanel/InteractiveSearchList.tsx | 23 +++++++++++++++---- .../AdminPanel/UserProvisioning.tsx | 15 ++++++++++-- publisher/src/components/AdminPanel/types.ts | 2 +- 4 files changed, 40 insertions(+), 9 deletions(-) diff --git a/publisher/src/components/AdminPanel/AgencyProvisioning.tsx b/publisher/src/components/AdminPanel/AgencyProvisioning.tsx index 33317d748..5535f79f4 100644 --- a/publisher/src/components/AdminPanel/AgencyProvisioning.tsx +++ b/publisher/src/components/AdminPanel/AgencyProvisioning.tsx @@ -52,6 +52,7 @@ import { SaveConfirmation, SaveConfirmationType, SaveConfirmationTypes, + SearchableListItem, SelectionInputBoxType, SelectionInputBoxTypes, Setting, @@ -226,8 +227,14 @@ export const AgencyProvisioning: React.FC = observer( return [ { label: "Select All", - onClick: () => { + onClick: (filteredList: SearchableListItem[] | undefined) => { setState(selectAllSet); + if (filteredList) { + const filteredSet = new Set( + filteredList.map((obj) => obj.id) + ) as Set; + setState(filteredSet); + } if (selectAllCallback) selectAllCallback(); }, }, diff --git a/publisher/src/components/AdminPanel/InteractiveSearchList.tsx b/publisher/src/components/AdminPanel/InteractiveSearchList.tsx index 56b9e8c13..bed432670 100644 --- a/publisher/src/components/AdminPanel/InteractiveSearchList.tsx +++ b/publisher/src/components/AdminPanel/InteractiveSearchList.tsx @@ -142,11 +142,24 @@ export const InteractiveSearchList = ({ {metadata?.listBoxLabel} {buttons.length > 0 && isActiveBox && ( - {buttons.map((button) => ( - - {button.label} - - ))} + {buttons.map((button) => { + const isFilteredSelectAllEnabled = + button.label === "Select All" && !!searchInputValue; + return ( + { + if (isFilteredSelectAllEnabled) { + button.onClick(filteredList); + } else { + button.onClick(); + } + }} + > + {button.label} {isFilteredSelectAllEnabled && "(filtered)"} + + ); + })} )} diff --git a/publisher/src/components/AdminPanel/UserProvisioning.tsx b/publisher/src/components/AdminPanel/UserProvisioning.tsx index 18635ead8..a72cfe281 100644 --- a/publisher/src/components/AdminPanel/UserProvisioning.tsx +++ b/publisher/src/components/AdminPanel/UserProvisioning.tsx @@ -37,6 +37,7 @@ import { SaveConfirmation, SaveConfirmationType, SaveConfirmationTypes, + SearchableListItem, Setting, } from "."; import * as Styled from "./AdminPanel.styles"; @@ -132,7 +133,11 @@ export const UserProvisioning: React.FC = observer( /** Search List Buttons (Select All/Deselect All/Close) */ const interactiveSearchListButtons = [ - { label: "Select All", onClick: () => selectAll() }, + { + label: "Select All", + onClick: (filteredList: SearchableListItem[] | undefined) => + selectAll(filteredList), + }, { label: "Deselect All", onClick: () => deselectAll() }, { label: "Close", @@ -159,13 +164,19 @@ export const UserProvisioning: React.FC = observer( ); } }; - const selectAll = () => { + const selectAll = (filteredList: SearchableListItem[] | undefined) => { if (isAddAction) { setAddedAgenciesIDs(availableAgenciesIDsSet); } if (isDeleteAction) { setDeletedAgenciesIDs(selectedUserAgenciesIDsSet); } + if (filteredList) { + const filteredSet = new Set( + filteredList.map((obj) => obj.id) + ) as Set; + setAddedAgenciesIDs(filteredSet); + } }; const deselectAll = () => { if (isAddAction) { diff --git a/publisher/src/components/AdminPanel/types.ts b/publisher/src/components/AdminPanel/types.ts index fa6a85599..892209a33 100644 --- a/publisher/src/components/AdminPanel/types.ts +++ b/publisher/src/components/AdminPanel/types.ts @@ -202,7 +202,7 @@ export type SearchableListItemKey = keyof SearchableListItem; export type InteractiveSearchListButtons = { label: string; - onClick: () => void; + onClick: (filteredList?: SearchableListItem[]) => void; }[]; export type InteractiveSearchListProps = { From ef21cb4a996214219be4fc7912b12737371e5f3a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 11 Apr 2024 09:52:15 +0000 Subject: [PATCH 12/28] Bump tar from 6.1.15 to 6.2.1 (#1297) Bumps [tar](https://github.com/isaacs/node-tar) from 6.1.15 to 6.2.1. - [Release notes](https://github.com/isaacs/node-tar/releases) - [Changelog](https://github.com/isaacs/node-tar/blob/main/CHANGELOG.md) - [Commits](https://github.com/isaacs/node-tar/compare/v6.1.15...v6.2.1) --- updated-dependencies: - dependency-name: tar dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index b539fb9be..fc9c2d261 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15975,9 +15975,9 @@ tar-stream@^2.1.4: readable-stream "^3.1.1" tar@^6.1.13: - version "6.1.15" - resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.15.tgz#c9738b0b98845a3b344d334b8fa3041aaba53a69" - integrity sha512-/zKt9UyngnxIT/EAGYuxaMYgOIJiP81ab9ZfkILq4oNLPFX50qyYmu7jRj9qeXoxmJHjGlbH0+cm2uy1WCs10A== + version "6.2.1" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.1.tgz#717549c541bc3c2af15751bea94b1dd068d4b03a" + integrity sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A== dependencies: chownr "^2.0.0" fs-minipass "^2.0.0" From 2fac1be7328bb8875f037e0eb37872319ba83028 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Apr 2024 07:19:50 +0000 Subject: [PATCH 13/28] Bump @types/react from 18.2.64 to 18.2.78 (#1301) Bumps [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) from 18.2.64 to 18.2.78. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react) --- updated-dependencies: - dependency-name: "@types/react" dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- agency-dashboard/package.json | 2 +- common/package.json | 2 +- publisher/package.json | 2 +- yarn.lock | 14 ++++---------- 4 files changed, 7 insertions(+), 13 deletions(-) diff --git a/agency-dashboard/package.json b/agency-dashboard/package.json index 3723df4c4..6dd3a48b2 100644 --- a/agency-dashboard/package.json +++ b/agency-dashboard/package.json @@ -51,7 +51,7 @@ "@babel/core": "^7.24.3", "@types/jest": "^27.5.2", "@types/node": "^20.6.2", - "@types/react": "^18.2.64", + "@types/react": "^18.2.78", "@types/react-dom": "^18.0.6", "@types/react-router-dom": "^5.3.3", "customize-cra": "^1.0.0", diff --git a/common/package.json b/common/package.json index 5a06abff0..9fb8d41a1 100644 --- a/common/package.json +++ b/common/package.json @@ -15,7 +15,7 @@ "@types/jest": "^27.5.2", "@types/lodash": "^4.14.202", "@types/node": "^20.6.2", - "@types/react": "^18.2.64", + "@types/react": "^18.2.78", "@types/react-dom": "^18.0.6", "@types/styled-components": "^5.1.34", "crypto-browserify": "^3.12.0", diff --git a/publisher/package.json b/publisher/package.json index 40be95380..a125d5240 100644 --- a/publisher/package.json +++ b/publisher/package.json @@ -75,7 +75,7 @@ "@types/node": "^20.6.2", "@types/qs": "^6.9.11", "@types/reach__router": "^1.3.15", - "@types/react": "^18.2.64", + "@types/react": "^18.2.78", "@types/react-dom": "^18.0.6", "@types/react-modal": "^3.16.3", "@types/react-router-dom": "^5.3.3", diff --git a/yarn.lock b/yarn.lock index fc9c2d261..d68184133 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5784,13 +5784,12 @@ dependencies: "@types/react" "*" -"@types/react@*", "@types/react@>=16", "@types/react@^18.2.64": - version "18.2.64" - resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.64.tgz#3700fbb6b2fa60a6868ec1323ae4cbd446a2197d" - integrity sha512-MlmPvHgjj2p3vZaxbQgFUQFvD8QiZwACfGqEdDSWou5yISWxDQ4/74nCAwsUiX7UFLKZz3BbVSPj+YxeoGGCfg== +"@types/react@*", "@types/react@>=16", "@types/react@^18.2.78": + version "18.2.78" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.78.tgz#94aec453d0ccca909998a2b4b2fd78af15a7d2fe" + integrity sha512-qOwdPnnitQY4xKlKayt42q5W5UQrSHjgoXNVEtxeqdITJ99k4VXJOP3vt8Rkm9HmgJpH50UNU+rlqfkfWOqp0A== dependencies: "@types/prop-types" "*" - "@types/scheduler" "*" csstype "^3.0.2" "@types/resolve@1.17.1": @@ -5810,11 +5809,6 @@ resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d" integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA== -"@types/scheduler@*": - version "0.16.2" - resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39" - integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew== - "@types/semver@^7.3.12", "@types/semver@^7.3.4": version "7.5.0" resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.0.tgz#591c1ce3a702c45ee15f47a42ade72c2fd78978a" From fb4da2a1d316eeb61a7691beaaadf289415c87f0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Apr 2024 07:25:05 +0000 Subject: [PATCH 14/28] Bump eslint-plugin-simple-import-sort from 12.0.0 to 12.1.0 (#1302) Bumps [eslint-plugin-simple-import-sort](https://github.com/lydell/eslint-plugin-simple-import-sort) from 12.0.0 to 12.1.0. - [Changelog](https://github.com/lydell/eslint-plugin-simple-import-sort/blob/main/CHANGELOG.md) - [Commits](https://github.com/lydell/eslint-plugin-simple-import-sort/compare/v12.0.0...v12.1.0) --- updated-dependencies: - dependency-name: eslint-plugin-simple-import-sort dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- publisher/package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/publisher/package.json b/publisher/package.json index a125d5240..0583b14ab 100644 --- a/publisher/package.json +++ b/publisher/package.json @@ -97,7 +97,7 @@ "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-react": "^7.34.1", "eslint-plugin-react-hooks": "^4.3.0", - "eslint-plugin-simple-import-sort": "^12.0.0", + "eslint-plugin-simple-import-sort": "^12.1.0", "file-saver": "^2.0.5", "jest-fetch-mock": "^3.0.3", "postcss": "^8.4.33", diff --git a/yarn.lock b/yarn.lock index d68184133..eace9d5de 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9482,10 +9482,10 @@ eslint-plugin-react@^7.27.1, eslint-plugin-react@^7.28.0, eslint-plugin-react@^7 semver "^6.3.1" string.prototype.matchall "^4.0.10" -eslint-plugin-simple-import-sort@^12.0.0: - version "12.0.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-12.0.0.tgz#3cfa05d74509bd4dc329a956938823812194dbb6" - integrity sha512-8o0dVEdAkYap0Cn5kNeklaKcT1nUsa3LITWEuFk3nJifOoD+5JQGoyDUW2W/iPWwBsNBJpyJS9y4je/BgxLcyQ== +eslint-plugin-simple-import-sort@^12.1.0: + version "12.1.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-12.1.0.tgz#8186ad55474d2f5c986a2f1bf70625a981e30d05" + integrity sha512-Y2fqAfC11TcG/WP3TrI1Gi3p3nc8XJyEOJYHyEPEGI/UAgNx6akxxlX74p7SbAQdLcgASKhj8M0GKvH3vq/+ig== eslint-plugin-simple-import-sort@^8.0.0: version "8.0.0" From 309131437810f452ad814b5b00b82d1ffe5db9de Mon Sep 17 00:00:00 2001 From: Ilya Date: Thu, 18 Apr 2024 16:43:32 +0300 Subject: [PATCH 15/28] [Publisher][Superagencies] Save new child agencies data in AgencyStore (#1294) * Add child agencies persistence for superagencies * Fix lint * Fixes * cleanup --- common/types.ts | 6 ++++ publisher/src/components/Home/types.ts | 5 ++++ publisher/src/components/Menu/Menu.tsx | 10 ++++++- publisher/src/stores/AgencyStore.ts | 39 ++++++++++++++++++++++++++ 4 files changed, 59 insertions(+), 1 deletion(-) diff --git a/common/types.ts b/common/types.ts index 0d35c6cdf..30b179b97 100644 --- a/common/types.ts +++ b/common/types.ts @@ -103,6 +103,12 @@ export interface UserAgency { super_agency_id: number | null | undefined; } +export type ChildAgency = { + id: number; + name: string; + systems: AgencySystem[]; +}; + export type ReportFrequency = "MONTHLY" | "ANNUAL"; export type ReportStatus = "NOT_STARTED" | "DRAFT" | "PUBLISHED"; diff --git a/publisher/src/components/Home/types.ts b/publisher/src/components/Home/types.ts index 73db5da2f..df5a5c27e 100644 --- a/publisher/src/components/Home/types.ts +++ b/publisher/src/components/Home/types.ts @@ -17,6 +17,7 @@ import { AgencySystem, + ChildAgency, Metric, Report, ReportFrequency, @@ -39,6 +40,10 @@ export type LatestRecordsAgencyMetrics = { monthly_report: Report; }; +export type ChildAgenciesRecord = { + child_agencies: ChildAgency[]; +}; + export type TaskCardMetadata = { key: string; title: string; diff --git a/publisher/src/components/Menu/Menu.tsx b/publisher/src/components/Menu/Menu.tsx index 88f9858c2..74d8436f9 100644 --- a/publisher/src/components/Menu/Menu.tsx +++ b/publisher/src/components/Menu/Menu.tsx @@ -33,7 +33,7 @@ import { generateDashboardURL } from "../HelpCenter/LinkToPublisherDashboard"; import * as Styled from "./Menu.styles"; const Menu: React.FC = () => { - const { userStore, authStore, api } = useStore(); + const { userStore, authStore, api, agencyStore } = useStore(); const { agencyId } = useParams() as { agencyId: string }; const navigate = useNavigate(); const location = useLocation(); @@ -194,6 +194,14 @@ const Menu: React.FC = () => { } }, [windowWidth]); + /* Here we're getting and storing a list of child agencies if the user is in a superagency. * */ + useEffect(() => { + const superagencyId = userStore.isAgencySuperagency(agencyId) + ? agencyId + : undefined; + agencyStore.loadChildAgencies(superagencyId); + }, [agencyId, agencyStore, userStore]); + return ( diff --git a/publisher/src/stores/AgencyStore.ts b/publisher/src/stores/AgencyStore.ts index 844dc4020..7b72edc1a 100644 --- a/publisher/src/stores/AgencyStore.ts +++ b/publisher/src/stores/AgencyStore.ts @@ -21,11 +21,13 @@ import { AgencySystem, AgencyTeamMember, AgencyTeamMemberRole, + ChildAgency, UserAgency, } from "@justice-counts/common/types"; import { makeAutoObservable, runInAction } from "mobx"; import { SYSTEMS_LOWERCASE } from "../components/Global/constants"; +import { ChildAgenciesRecord } from "../components/Home"; import { AgencySettingType } from "../components/Settings"; import API from "./API"; import UserStore from "./UserStore"; @@ -50,6 +52,8 @@ class AgencyStore { daysAfterTimePeriodToSendEmail: number | null; + childAgencies: ChildAgency[]; + constructor(userStore: UserStore, api: API) { makeAutoObservable(this); @@ -60,6 +64,7 @@ class AgencyStore { this.loadingSettings = true; this.isUserSubscribedToEmails = false; this.daysAfterTimePeriodToSendEmail = null; + this.childAgencies = []; } get currentAgency(): UserAgency | undefined { @@ -69,6 +74,13 @@ class AgencyStore { return undefined; } + get superagencyChildAgencies(): ChildAgency[] | undefined { + if (this.childAgencies.length > 0) { + return this.childAgencies; + } + return undefined; + } + get currentAgencySystems(): AgencySystem[] | undefined { return this.currentAgency?.systems; } @@ -135,6 +147,25 @@ class AgencyStore { } } + async getChildAgencies(superagencyId: string): Promise { + try { + const response = (await this.api.request({ + path: `/api/agencies/${superagencyId}/children`, + method: "GET", + })) as Response; + + if (response.status !== 200) { + throw new Error("There was an issue getting a list of child agencies."); + } + const responseJson = (await response.json()) as ChildAgenciesRecord; + runInAction(() => { + this.childAgencies = responseJson.child_agencies; + }); + } catch (error) { + if (error instanceof Error) return new Error(error.message); + } + } + saveAgencySettings = async ( settings: { settings: AgencySetting[] }, agencyId: string @@ -245,6 +276,14 @@ class AgencyStore { return { settings: newSettings }; }; + loadChildAgencies(superagencyId: string | undefined) { + if (superagencyId) { + this.getChildAgencies(superagencyId); + } else { + this.childAgencies = []; + } + } + updateAgencySystems = ( systems: AgencySystem[] ): { systems: AgencySystem[] } => { From dacf54bce4a07c46e5d1e54d7e782d40076926cc Mon Sep 17 00:00:00 2001 From: Ilya Date: Fri, 19 Apr 2024 14:43:31 +0300 Subject: [PATCH 16/28] [Publisher] Properly add to agency new user created via secondary modal (#1298) * Add role update when creating new user via secondary modal * Add separate useEffect for user auto-adding * Update role distribution logic * Change"filter" to "find" --- .../AdminPanel/AgencyProvisioning.tsx | 25 +++++++++++++------ .../AdminPanel/UserProvisioning.tsx | 2 +- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/publisher/src/components/AdminPanel/AgencyProvisioning.tsx b/publisher/src/components/AdminPanel/AgencyProvisioning.tsx index 5535f79f4..66c2e0194 100644 --- a/publisher/src/components/AdminPanel/AgencyProvisioning.tsx +++ b/publisher/src/components/AdminPanel/AgencyProvisioning.tsx @@ -516,21 +516,32 @@ export const AgencyProvisioning: React.FC = observer( }; }); } - - /** Here we are making the auto-adding if something was created via the secondary modal */ - if (secondaryCreatedId) - setSelectedTeamMembersToAdd((prev) => - toggleAddRemoveSetItem(prev, +secondaryCreatedId) - ); }, [ selectedAgency, adminPanelStore, api, csgAndRecidivizUsers, csgAndRecidivizDefaultRole, - secondaryCreatedId, ]); + /** Here we are making the auto-adding if user was created via the secondary modal */ + useEffect(() => { + const newMember = users.find((user) => user.id === secondaryCreatedId); + if (secondaryCreatedId && newMember) { + setSelectedTeamMembersToAdd((prev) => + toggleAddRemoveSetItem(prev, +secondaryCreatedId) + ); + setTeamMemberRoleUpdates((prev) => { + return { + ...prev, + [secondaryCreatedId]: isCSGOrRecidivizUserByEmail(newMember.email) + ? csgAndRecidivizDefaultRole + : AgencyTeamMemberRole.AGENCY_ADMIN, + }; + }); + } + }, [users, secondaryCreatedId, csgAndRecidivizDefaultRole]); + return ( {showSaveConfirmation.show ? ( diff --git a/publisher/src/components/AdminPanel/UserProvisioning.tsx b/publisher/src/components/AdminPanel/UserProvisioning.tsx index a72cfe281..a7717cdcd 100644 --- a/publisher/src/components/AdminPanel/UserProvisioning.tsx +++ b/publisher/src/components/AdminPanel/UserProvisioning.tsx @@ -108,7 +108,7 @@ export const UserProvisioning: React.FC = observer( : []), ]; - /** Here we are making the auto-adding if something was created via the secondary modal */ + /** Here we are making the auto-adding if agency was created via the secondary modal */ useEffect(() => { if (secondaryCreatedId) setAddedAgenciesIDs((prev) => From 9c4424f9332f5272102ad5ab54edc5958b7c7218 Mon Sep 17 00:00:00 2001 From: Ilya Date: Wed, 24 Apr 2024 13:54:48 +0300 Subject: [PATCH 17/28] [Publisher][Superagencies] Add dropdown for child agencies to Set Up Metrics page (#1295) * Add child agencies persistence for superagencies * Fix lint * Add dropdown UI for child agencies to Set Up Metrics page * Keep dropdown visible for child agencies * Clean up * Add superagency to dropdown if viewing child agency --- .../components/Dropdown/Dropdown.styled.tsx | 22 ++++++- common/components/Dropdown/Dropdown.tsx | 24 +++++++ publisher/src/components/Menu/Menu.tsx | 9 +-- .../MetricsOverview.styled.tsx | 31 ++++++++- .../MetricsConfiguration/MetricsOverview.tsx | 63 ++++++++++++++++++- publisher/src/stores/AgencyStore.ts | 2 - 6 files changed, 139 insertions(+), 12 deletions(-) diff --git a/common/components/Dropdown/Dropdown.styled.tsx b/common/components/Dropdown/Dropdown.styled.tsx index 7eb30f91a..dc2cb2eed 100644 --- a/common/components/Dropdown/Dropdown.styled.tsx +++ b/common/components/Dropdown/Dropdown.styled.tsx @@ -194,18 +194,34 @@ export const CustomInputWrapper = styled.div` padding: 20px 16px 8px 16px; `; -export const CustomInput = styled.input` +export const CustomInput = styled.input<{ + customClearButton?: boolean; +}>` ${typography.sizeCSS.normal} width: 100%; - padding: 4px 8px 4px 8px; + padding: 4px 10px; border: 1px solid ${palette.solid.lightgrey3}; - color: ${palette.highlight.grey9}; + color: ${palette.solid.darkgrey}; background: ${palette.solid.lightgrey2}; border-radius: 2px; &::placeholder { color: ${palette.highlight.grey5}; } + + ${({ customClearButton }) => + customClearButton && + `&::-webkit-search-cancel-button { + -webkit-appearance: none; + }`} +`; + +export const CustomClearButton = styled.button` + border: 0; + padding: 0; + background: transparent; + color: ${palette.highlight.grey8}; + cursor: pointer; `; export const NoResultsFoundWrapper = styled.div` diff --git a/common/components/Dropdown/Dropdown.tsx b/common/components/Dropdown/Dropdown.tsx index b28598e44..7b05eb0cc 100644 --- a/common/components/Dropdown/Dropdown.tsx +++ b/common/components/Dropdown/Dropdown.tsx @@ -15,9 +15,11 @@ // along with this program. If not, see . // ============================================================================= +import { Icon, IconSVG } from "@recidiviz/design-system"; import React, { useEffect, useRef, useState } from "react"; import dropdownCaret from "../../assets/dropdown-caret.svg"; +import { palette } from "../GlobalStyles"; import * as Styled from "./Dropdown.styled"; import { DropdownMenuAlignment, @@ -40,6 +42,7 @@ type DropdownProps = { fullHeight?: boolean; highlightIcon?: React.ReactNode; typeaheadSearch?: { placeholder: string }; + customClearSearchButton?: string; }; /** @@ -71,6 +74,7 @@ export function Dropdown({ fullHeight, highlightIcon, typeaheadSearch, + customClearSearchButton, }: DropdownProps) { const [filteredOptions, setFilteredOptions] = useState(); const [inputValue, setInputValue] = useState(""); @@ -133,8 +137,17 @@ export function Dropdown({ <> {typeaheadSearch && ( + {customClearSearchButton && ( + + )} + {customClearSearchButton && ( + { + setInputValue(""); + updateFilteredOptions(""); + }} + > + {customClearSearchButton} + + )} )} diff --git a/publisher/src/components/Menu/Menu.tsx b/publisher/src/components/Menu/Menu.tsx index 74d8436f9..f9fef7668 100644 --- a/publisher/src/components/Menu/Menu.tsx +++ b/publisher/src/components/Menu/Menu.tsx @@ -42,6 +42,9 @@ const Menu: React.FC = () => { const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false); const pathWithoutAgency = removeAgencyFromPath(location.pathname); + const userSuperagency = userStore.userAgencies?.find( + (agency) => agency.is_superagency + ); const currentAgency = userStore.getAgency(agencyId); const hasDashboardEnabled = currentAgency?.is_dashboard_enabled; const agencyName = currentAgency?.name; @@ -196,11 +199,9 @@ const Menu: React.FC = () => { /* Here we're getting and storing a list of child agencies if the user is in a superagency. * */ useEffect(() => { - const superagencyId = userStore.isAgencySuperagency(agencyId) - ? agencyId - : undefined; + const superagencyId = userSuperagency && String(userSuperagency.id); agencyStore.loadChildAgencies(superagencyId); - }, [agencyId, agencyStore, userStore]); + }, [agencyStore, userSuperagency]); return ( diff --git a/publisher/src/components/MetricsConfiguration/MetricsOverview.styled.tsx b/publisher/src/components/MetricsConfiguration/MetricsOverview.styled.tsx index 868fe067c..e2fcf460b 100644 --- a/publisher/src/components/MetricsConfiguration/MetricsOverview.styled.tsx +++ b/publisher/src/components/MetricsConfiguration/MetricsOverview.styled.tsx @@ -15,6 +15,12 @@ // along with this program. If not, see . // ============================================================================= +import { + CustomDropdown, + CustomDropdownMenu, + CustomInput, + CustomInputWrapper, +} from "@justice-counts/common/components/Dropdown"; import { HEADER_BAR_HEIGHT, palette, @@ -56,7 +62,7 @@ export const OverviewHeader = styled.div` export const OverviewDescription = styled.div` ${typography.body}; max-width: 470px; - margin-bottom: 32px; + margin-bottom: 24px; a, a:visited { @@ -191,3 +197,26 @@ export const TabbedBarWrapper = styled.div` gap: 8px 16px; } `; + +export const DropdownWrapper = styled.div` + padding-bottom: 14px; + + & ${CustomDropdown} { + width: 400px; + border: 1px solid ${palette.highlight.grey2}; + padding: 0 12px; + } + & ${CustomDropdownMenu} { + box-shadow: none; + border: 1px solid ${palette.highlight.grey2}; + } + & ${CustomInputWrapper} { + padding: 12px 16px; + border-bottom: 1px solid ${palette.highlight.grey2}; + } + & ${CustomInput} { + border: 0; + outline: 0; + background: transparent; + } +`; diff --git a/publisher/src/components/MetricsConfiguration/MetricsOverview.tsx b/publisher/src/components/MetricsConfiguration/MetricsOverview.tsx index ee4b44693..7fbefce0b 100644 --- a/publisher/src/components/MetricsConfiguration/MetricsOverview.tsx +++ b/publisher/src/components/MetricsConfiguration/MetricsOverview.tsx @@ -15,6 +15,10 @@ // along with this program. If not, see . // ============================================================================= +import { + Dropdown, + DropdownOption, +} from "@justice-counts/common/components/Dropdown"; import { TabbedBar } from "@justice-counts/common/components/TabbedBar"; import { AgencySystem, @@ -23,7 +27,7 @@ import { import { frequencyString } from "@justice-counts/common/utils/helperUtils"; import { observer } from "mobx-react-lite"; import React from "react"; -import { useParams } from "react-router-dom"; +import { useNavigate, useParams } from "react-router-dom"; import { NotFound } from "../../pages/NotFound"; import { useStore } from "../../stores"; @@ -38,11 +42,14 @@ import * as Styled from "./MetricsOverview.styled"; export const MetricsOverview = observer(() => { const [settingsSearchParams, setSettingsSearchParams] = useSettingsSearchParams(); + const navigate = useNavigate(); const { agencyId } = useParams() as { agencyId: string }; - const { userStore, metricConfigStore } = useStore(); + const { userStore, metricConfigStore, agencyStore } = useStore(); const { getMetricsBySystem } = metricConfigStore; + const { superagencyChildAgencies } = agencyStore; + const { system: systemSearchParam } = settingsSearchParams; const handleSystemClick = (option: AgencySystem) => { @@ -102,6 +109,38 @@ export const MetricsOverview = observer(() => { }; }) || []; + const isChildAgency = superagencyChildAgencies?.find( + (childAgency) => childAgency.id === currentAgency?.id + ); + + const userSuperagency = userStore.userAgencies?.find( + (agency) => agency.is_superagency + ); + + const superagencyDropdownOptions: DropdownOption[] = + userSuperagency && isChildAgency + ? [ + { + key: userSuperagency.id, + label: `${userSuperagency.name} (Superagency)`, + onClick: () => + navigate(`/agency/${userSuperagency.id}/metric-config`), + }, + ] + : []; + + const childAgenciesDropdownOptions: DropdownOption[] = + superagencyChildAgencies + ? superagencyChildAgencies + .map((agency) => ({ + key: agency.id, + label: agency.name, + onClick: () => navigate(`/agency/${agency.id}/metric-config`), + highlight: agency.id === currentAgency?.id, + })) + .sort((a, b) => a.label.localeCompare(b.label)) + : []; + if (systemSearchParam && !currentAgency?.systems.includes(systemSearchParam)) return ; @@ -129,6 +168,26 @@ export const MetricsOverview = observer(() => { + {childAgenciesDropdownOptions.length > 0 && + (isSuperagency || isChildAgency) && ( + + + + )} + {/* System Selection */} {showSystems && ( diff --git a/publisher/src/stores/AgencyStore.ts b/publisher/src/stores/AgencyStore.ts index 7b72edc1a..a85252af5 100644 --- a/publisher/src/stores/AgencyStore.ts +++ b/publisher/src/stores/AgencyStore.ts @@ -279,8 +279,6 @@ class AgencyStore { loadChildAgencies(superagencyId: string | undefined) { if (superagencyId) { this.getChildAgencies(superagencyId); - } else { - this.childAgencies = []; } } From 61055ccad43dbca4950d0fe0400d4eb1b3ec46a5 Mon Sep 17 00:00:00 2001 From: Ilya Date: Wed, 24 Apr 2024 14:18:08 +0300 Subject: [PATCH 18/28] [Publisher][Superagencies] Add dropdown for child agencies to Explore Data page (#1310) * Add `ChildAgenciesDropdown` component to explore data page * Fix lint * Align sectors section to start --- .../DataViz/MetricsDataChart.styled.tsx | 27 +++--- .../components/DataViz/MetricsDataChart.tsx | 2 + .../ChildAgenciesDropdown.styled.tsx | 49 ++++++++++ .../ChildAgenciesDropdown.tsx | 90 +++++++++++++++++++ .../MetricsOverview.styled.tsx | 29 ------ .../MetricsConfiguration/MetricsOverview.tsx | 65 ++------------ 6 files changed, 161 insertions(+), 101 deletions(-) create mode 100644 publisher/src/components/MetricsConfiguration/ChildAgenciesDropdown.styled.tsx create mode 100644 publisher/src/components/MetricsConfiguration/ChildAgenciesDropdown.tsx diff --git a/publisher/src/components/DataViz/MetricsDataChart.styled.tsx b/publisher/src/components/DataViz/MetricsDataChart.styled.tsx index 4dfb90b75..b0f2726d1 100644 --- a/publisher/src/components/DataViz/MetricsDataChart.styled.tsx +++ b/publisher/src/components/DataViz/MetricsDataChart.styled.tsx @@ -56,6 +56,16 @@ export const MetricsViewPanel = styled.div<{ } `; +export const DisclaimerContainer = styled.div` + display: flex; + flex-direction: column; + justify-content: flex-end; + padding-top: 15px; + padding-bottom: 37px; + width: ${INNER_PANEL_LEFT_CONTAINER_MAX_WIDTH}px; + min-height: 200px; +`; + export const PanelContainerLeft = styled.div` width: 25%; min-width: calc(314px + 24px + 95px); @@ -63,8 +73,11 @@ export const PanelContainerLeft = styled.div` overflow-y: auto; display: flex; flex-direction: column; - justify-content: space-between; - padding: 46px 0 0 24px; + padding: 46px 24px 0; + + & > ${DisclaimerContainer} { + margin-top: auto; + } @media only screen and (max-width: ${MIN_DESKTOP_WIDTH}px) { display: none; @@ -135,16 +148,6 @@ export const MetricItem = styled.div<{ selected?: boolean }>` } `; -export const DisclaimerContainer = styled.div` - display: flex; - flex-direction: column; - justify-content: flex-end; - padding-top: 15px; - padding-bottom: 37px; - width: ${INNER_PANEL_LEFT_CONTAINER_MAX_WIDTH}px; - min-height: 200px; -`; - export const DisclaimerTitle = styled.div` ${typography.sizeCSS.small} `; diff --git a/publisher/src/components/DataViz/MetricsDataChart.tsx b/publisher/src/components/DataViz/MetricsDataChart.tsx index 4df7639f5..651764176 100644 --- a/publisher/src/components/DataViz/MetricsDataChart.tsx +++ b/publisher/src/components/DataViz/MetricsDataChart.tsx @@ -54,6 +54,7 @@ import { ReactComponent as SwitchToDataTableIcon } from "../assets/switch-to-dat import { AppGuideKeys, GuideKeys } from "../HelpCenter/types"; import { createURLToGuide } from "../HelpCenter/utils"; import { Loading } from "../Loading"; +import { ChildAgenciesDropdown } from "../MetricsConfiguration/ChildAgenciesDropdown"; import { DisclaimerBanner } from "../primitives"; import { useSettingsSearchParams } from "../Settings"; import ConnectedDatapointsView from "./ConnectedDatapointsView"; @@ -250,6 +251,7 @@ export const MetricsDataChart: React.FC = observer(() => { {/* List Of Metrics */} +
. +// ============================================================================= + +import { + CustomDropdown, + CustomDropdownMenu, + CustomInput, + CustomInputWrapper, +} from "@justice-counts/common/components/Dropdown"; +import { palette } from "@justice-counts/common/components/GlobalStyles"; +import styled from "styled-components/macro"; + +export const DropdownWrapper = styled.div` + padding-bottom: 14px; + + & ${CustomDropdown} { + width: 100%; + max-width: 360px; + border: 1px solid ${palette.highlight.grey2}; + padding: 0 12px; + } + & ${CustomDropdownMenu} { + box-shadow: none; + border: 1px solid ${palette.highlight.grey2}; + } + & ${CustomInputWrapper} { + padding: 12px 16px; + border-bottom: 1px solid ${palette.highlight.grey2}; + } + & ${CustomInput} { + border: 0; + outline: 0; + background: transparent; + } +`; diff --git a/publisher/src/components/MetricsConfiguration/ChildAgenciesDropdown.tsx b/publisher/src/components/MetricsConfiguration/ChildAgenciesDropdown.tsx new file mode 100644 index 000000000..b07236253 --- /dev/null +++ b/publisher/src/components/MetricsConfiguration/ChildAgenciesDropdown.tsx @@ -0,0 +1,90 @@ +// Recidiviz - a data platform for criminal justice reform +// Copyright (C) 2023 Recidiviz, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// ============================================================================= + +import { + Dropdown, + DropdownOption, +} from "@justice-counts/common/components/Dropdown"; +import { observer } from "mobx-react-lite"; +import React from "react"; +import { useNavigate, useParams } from "react-router-dom"; + +import { useStore } from "../../stores"; +import * as Styled from "./ChildAgenciesDropdown.styled"; + +export const ChildAgenciesDropdown: React.FC<{ + view: string; +}> = observer(({ view }) => { + const navigate = useNavigate(); + const { agencyId } = useParams() as { agencyId: string }; + const { userStore, agencyStore } = useStore(); + + const { superagencyChildAgencies } = agencyStore; + const currentAgency = userStore.getAgency(agencyId); + const isSuperagency = userStore.isAgencySuperagency(agencyId); + + const isChildAgency = superagencyChildAgencies?.find( + (childAgency) => childAgency.id === currentAgency?.id + ); + + const userSuperagency = userStore.userAgencies?.find( + (agency) => agency.is_superagency + ); + + const superagencyDropdownOptions: DropdownOption[] = + userSuperagency && isChildAgency + ? [ + { + key: userSuperagency.id, + label: `${userSuperagency.name} (Superagency)`, + onClick: () => navigate(`/agency/${userSuperagency.id}/${view}`), + }, + ] + : []; + + const childAgenciesDropdownOptions: DropdownOption[] = + superagencyChildAgencies + ? superagencyChildAgencies + .map((agency) => ({ + key: agency.id, + label: agency.name, + onClick: () => navigate(`/agency/${agency.id}/${view}`), + highlight: agency.id === currentAgency?.id, + })) + .sort((a, b) => a.label.localeCompare(b.label)) + : []; + + return ( + childAgenciesDropdownOptions.length > 0 && + (isSuperagency || isChildAgency) && ( + + + + ) + ); +}); diff --git a/publisher/src/components/MetricsConfiguration/MetricsOverview.styled.tsx b/publisher/src/components/MetricsConfiguration/MetricsOverview.styled.tsx index e2fcf460b..ad2e5edca 100644 --- a/publisher/src/components/MetricsConfiguration/MetricsOverview.styled.tsx +++ b/publisher/src/components/MetricsConfiguration/MetricsOverview.styled.tsx @@ -15,12 +15,6 @@ // along with this program. If not, see . // ============================================================================= -import { - CustomDropdown, - CustomDropdownMenu, - CustomInput, - CustomInputWrapper, -} from "@justice-counts/common/components/Dropdown"; import { HEADER_BAR_HEIGHT, palette, @@ -197,26 +191,3 @@ export const TabbedBarWrapper = styled.div` gap: 8px 16px; } `; - -export const DropdownWrapper = styled.div` - padding-bottom: 14px; - - & ${CustomDropdown} { - width: 400px; - border: 1px solid ${palette.highlight.grey2}; - padding: 0 12px; - } - & ${CustomDropdownMenu} { - box-shadow: none; - border: 1px solid ${palette.highlight.grey2}; - } - & ${CustomInputWrapper} { - padding: 12px 16px; - border-bottom: 1px solid ${palette.highlight.grey2}; - } - & ${CustomInput} { - border: 0; - outline: 0; - background: transparent; - } -`; diff --git a/publisher/src/components/MetricsConfiguration/MetricsOverview.tsx b/publisher/src/components/MetricsConfiguration/MetricsOverview.tsx index 7fbefce0b..4c7b2ca68 100644 --- a/publisher/src/components/MetricsConfiguration/MetricsOverview.tsx +++ b/publisher/src/components/MetricsConfiguration/MetricsOverview.tsx @@ -15,10 +15,6 @@ // along with this program. If not, see . // ============================================================================= -import { - Dropdown, - DropdownOption, -} from "@justice-counts/common/components/Dropdown"; import { TabbedBar } from "@justice-counts/common/components/TabbedBar"; import { AgencySystem, @@ -27,7 +23,7 @@ import { import { frequencyString } from "@justice-counts/common/utils/helperUtils"; import { observer } from "mobx-react-lite"; import React from "react"; -import { useNavigate, useParams } from "react-router-dom"; +import { useParams } from "react-router-dom"; import { NotFound } from "../../pages/NotFound"; import { useStore } from "../../stores"; @@ -37,19 +33,18 @@ import { AppGuideKeys, GuideKeys } from "../HelpCenter/types"; import { createURLToGuide } from "../HelpCenter/utils"; import { DisclaimerBanner } from "../primitives"; import { useSettingsSearchParams } from "../Settings"; +import { ChildAgenciesDropdown } from "./ChildAgenciesDropdown"; import * as Styled from "./MetricsOverview.styled"; export const MetricsOverview = observer(() => { const [settingsSearchParams, setSettingsSearchParams] = useSettingsSearchParams(); - const navigate = useNavigate(); + const { agencyId } = useParams() as { agencyId: string }; - const { userStore, metricConfigStore, agencyStore } = useStore(); + const { userStore, metricConfigStore } = useStore(); const { getMetricsBySystem } = metricConfigStore; - const { superagencyChildAgencies } = agencyStore; - const { system: systemSearchParam } = settingsSearchParams; const handleSystemClick = (option: AgencySystem) => { @@ -109,38 +104,6 @@ export const MetricsOverview = observer(() => { }; }) || []; - const isChildAgency = superagencyChildAgencies?.find( - (childAgency) => childAgency.id === currentAgency?.id - ); - - const userSuperagency = userStore.userAgencies?.find( - (agency) => agency.is_superagency - ); - - const superagencyDropdownOptions: DropdownOption[] = - userSuperagency && isChildAgency - ? [ - { - key: userSuperagency.id, - label: `${userSuperagency.name} (Superagency)`, - onClick: () => - navigate(`/agency/${userSuperagency.id}/metric-config`), - }, - ] - : []; - - const childAgenciesDropdownOptions: DropdownOption[] = - superagencyChildAgencies - ? superagencyChildAgencies - .map((agency) => ({ - key: agency.id, - label: agency.name, - onClick: () => navigate(`/agency/${agency.id}/metric-config`), - highlight: agency.id === currentAgency?.id, - })) - .sort((a, b) => a.label.localeCompare(b.label)) - : []; - if (systemSearchParam && !currentAgency?.systems.includes(systemSearchParam)) return ; @@ -168,25 +131,7 @@ export const MetricsOverview = observer(() => { - {childAgenciesDropdownOptions.length > 0 && - (isSuperagency || isChildAgency) && ( - - - - )} + {/* System Selection */} {showSystems && ( From 508c2418296552e1054f5946ab49366051da6599 Mon Sep 17 00:00:00 2001 From: Mahmoud O <59492998+mxosman@users.noreply.github.com> Date: Wed, 24 Apr 2024 09:08:47 -0500 Subject: [PATCH 19/28] Include agency id in link to category overview (#1311) Co-authored-by: Mahmoud --- agency-dashboard/src/AgencyOverview/AgencyOverview.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/agency-dashboard/src/AgencyOverview/AgencyOverview.tsx b/agency-dashboard/src/AgencyOverview/AgencyOverview.tsx index 2d03b2235..67929b5a7 100644 --- a/agency-dashboard/src/AgencyOverview/AgencyOverview.tsx +++ b/agency-dashboard/src/AgencyOverview/AgencyOverview.tsx @@ -65,6 +65,7 @@ export const AgencyOverview = observer(() => { const { slug } = useParams(); const { agencyDataStore } = useStore(); const { + agency, agencyName, agencyDescription, agencyHomepageUrl, @@ -91,8 +92,8 @@ export const AgencyOverview = observer(() => { category: string, currSystem: string | undefined ) => { - if (isClickable) { - navigate(`/agency/${slug}/${slugify(category)}`, { + if (isClickable && agency?.id) { + navigate(`/agency/${agency.id}/${slug}/${slugify(category)}`, { state: { currentSystem: currSystem }, }); } From 7a861e155dae69754f6971cdeed6064493a3fcf7 Mon Sep 17 00:00:00 2001 From: Mahmoud O <59492998+mxosman@users.noreply.github.com> Date: Wed, 24 Apr 2024 10:33:12 -0500 Subject: [PATCH 20/28] =?UTF-8?q?Revert=20"[Publisher][Superagencies]=20Ad?= =?UTF-8?q?d=20dropdown=20for=20child=20agencies=20to=20Explore=E2=80=A6"?= =?UTF-8?q?=20(#1312)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 61055ccad43dbca4950d0fe0400d4eb1b3ec46a5. --- .../DataViz/MetricsDataChart.styled.tsx | 27 +++--- .../components/DataViz/MetricsDataChart.tsx | 2 - .../ChildAgenciesDropdown.styled.tsx | 49 ---------- .../ChildAgenciesDropdown.tsx | 90 ------------------- .../MetricsOverview.styled.tsx | 29 ++++++ .../MetricsConfiguration/MetricsOverview.tsx | 65 ++++++++++++-- 6 files changed, 101 insertions(+), 161 deletions(-) delete mode 100644 publisher/src/components/MetricsConfiguration/ChildAgenciesDropdown.styled.tsx delete mode 100644 publisher/src/components/MetricsConfiguration/ChildAgenciesDropdown.tsx diff --git a/publisher/src/components/DataViz/MetricsDataChart.styled.tsx b/publisher/src/components/DataViz/MetricsDataChart.styled.tsx index b0f2726d1..4dfb90b75 100644 --- a/publisher/src/components/DataViz/MetricsDataChart.styled.tsx +++ b/publisher/src/components/DataViz/MetricsDataChart.styled.tsx @@ -56,16 +56,6 @@ export const MetricsViewPanel = styled.div<{ } `; -export const DisclaimerContainer = styled.div` - display: flex; - flex-direction: column; - justify-content: flex-end; - padding-top: 15px; - padding-bottom: 37px; - width: ${INNER_PANEL_LEFT_CONTAINER_MAX_WIDTH}px; - min-height: 200px; -`; - export const PanelContainerLeft = styled.div` width: 25%; min-width: calc(314px + 24px + 95px); @@ -73,11 +63,8 @@ export const PanelContainerLeft = styled.div` overflow-y: auto; display: flex; flex-direction: column; - padding: 46px 24px 0; - - & > ${DisclaimerContainer} { - margin-top: auto; - } + justify-content: space-between; + padding: 46px 0 0 24px; @media only screen and (max-width: ${MIN_DESKTOP_WIDTH}px) { display: none; @@ -148,6 +135,16 @@ export const MetricItem = styled.div<{ selected?: boolean }>` } `; +export const DisclaimerContainer = styled.div` + display: flex; + flex-direction: column; + justify-content: flex-end; + padding-top: 15px; + padding-bottom: 37px; + width: ${INNER_PANEL_LEFT_CONTAINER_MAX_WIDTH}px; + min-height: 200px; +`; + export const DisclaimerTitle = styled.div` ${typography.sizeCSS.small} `; diff --git a/publisher/src/components/DataViz/MetricsDataChart.tsx b/publisher/src/components/DataViz/MetricsDataChart.tsx index 651764176..4df7639f5 100644 --- a/publisher/src/components/DataViz/MetricsDataChart.tsx +++ b/publisher/src/components/DataViz/MetricsDataChart.tsx @@ -54,7 +54,6 @@ import { ReactComponent as SwitchToDataTableIcon } from "../assets/switch-to-dat import { AppGuideKeys, GuideKeys } from "../HelpCenter/types"; import { createURLToGuide } from "../HelpCenter/utils"; import { Loading } from "../Loading"; -import { ChildAgenciesDropdown } from "../MetricsConfiguration/ChildAgenciesDropdown"; import { DisclaimerBanner } from "../primitives"; import { useSettingsSearchParams } from "../Settings"; import ConnectedDatapointsView from "./ConnectedDatapointsView"; @@ -251,7 +250,6 @@ export const MetricsDataChart: React.FC = observer(() => { {/* List Of Metrics */} -
. -// ============================================================================= - -import { - CustomDropdown, - CustomDropdownMenu, - CustomInput, - CustomInputWrapper, -} from "@justice-counts/common/components/Dropdown"; -import { palette } from "@justice-counts/common/components/GlobalStyles"; -import styled from "styled-components/macro"; - -export const DropdownWrapper = styled.div` - padding-bottom: 14px; - - & ${CustomDropdown} { - width: 100%; - max-width: 360px; - border: 1px solid ${palette.highlight.grey2}; - padding: 0 12px; - } - & ${CustomDropdownMenu} { - box-shadow: none; - border: 1px solid ${palette.highlight.grey2}; - } - & ${CustomInputWrapper} { - padding: 12px 16px; - border-bottom: 1px solid ${palette.highlight.grey2}; - } - & ${CustomInput} { - border: 0; - outline: 0; - background: transparent; - } -`; diff --git a/publisher/src/components/MetricsConfiguration/ChildAgenciesDropdown.tsx b/publisher/src/components/MetricsConfiguration/ChildAgenciesDropdown.tsx deleted file mode 100644 index b07236253..000000000 --- a/publisher/src/components/MetricsConfiguration/ChildAgenciesDropdown.tsx +++ /dev/null @@ -1,90 +0,0 @@ -// Recidiviz - a data platform for criminal justice reform -// Copyright (C) 2023 Recidiviz, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// ============================================================================= - -import { - Dropdown, - DropdownOption, -} from "@justice-counts/common/components/Dropdown"; -import { observer } from "mobx-react-lite"; -import React from "react"; -import { useNavigate, useParams } from "react-router-dom"; - -import { useStore } from "../../stores"; -import * as Styled from "./ChildAgenciesDropdown.styled"; - -export const ChildAgenciesDropdown: React.FC<{ - view: string; -}> = observer(({ view }) => { - const navigate = useNavigate(); - const { agencyId } = useParams() as { agencyId: string }; - const { userStore, agencyStore } = useStore(); - - const { superagencyChildAgencies } = agencyStore; - const currentAgency = userStore.getAgency(agencyId); - const isSuperagency = userStore.isAgencySuperagency(agencyId); - - const isChildAgency = superagencyChildAgencies?.find( - (childAgency) => childAgency.id === currentAgency?.id - ); - - const userSuperagency = userStore.userAgencies?.find( - (agency) => agency.is_superagency - ); - - const superagencyDropdownOptions: DropdownOption[] = - userSuperagency && isChildAgency - ? [ - { - key: userSuperagency.id, - label: `${userSuperagency.name} (Superagency)`, - onClick: () => navigate(`/agency/${userSuperagency.id}/${view}`), - }, - ] - : []; - - const childAgenciesDropdownOptions: DropdownOption[] = - superagencyChildAgencies - ? superagencyChildAgencies - .map((agency) => ({ - key: agency.id, - label: agency.name, - onClick: () => navigate(`/agency/${agency.id}/${view}`), - highlight: agency.id === currentAgency?.id, - })) - .sort((a, b) => a.label.localeCompare(b.label)) - : []; - - return ( - childAgenciesDropdownOptions.length > 0 && - (isSuperagency || isChildAgency) && ( - - - - ) - ); -}); diff --git a/publisher/src/components/MetricsConfiguration/MetricsOverview.styled.tsx b/publisher/src/components/MetricsConfiguration/MetricsOverview.styled.tsx index ad2e5edca..e2fcf460b 100644 --- a/publisher/src/components/MetricsConfiguration/MetricsOverview.styled.tsx +++ b/publisher/src/components/MetricsConfiguration/MetricsOverview.styled.tsx @@ -15,6 +15,12 @@ // along with this program. If not, see . // ============================================================================= +import { + CustomDropdown, + CustomDropdownMenu, + CustomInput, + CustomInputWrapper, +} from "@justice-counts/common/components/Dropdown"; import { HEADER_BAR_HEIGHT, palette, @@ -191,3 +197,26 @@ export const TabbedBarWrapper = styled.div` gap: 8px 16px; } `; + +export const DropdownWrapper = styled.div` + padding-bottom: 14px; + + & ${CustomDropdown} { + width: 400px; + border: 1px solid ${palette.highlight.grey2}; + padding: 0 12px; + } + & ${CustomDropdownMenu} { + box-shadow: none; + border: 1px solid ${palette.highlight.grey2}; + } + & ${CustomInputWrapper} { + padding: 12px 16px; + border-bottom: 1px solid ${palette.highlight.grey2}; + } + & ${CustomInput} { + border: 0; + outline: 0; + background: transparent; + } +`; diff --git a/publisher/src/components/MetricsConfiguration/MetricsOverview.tsx b/publisher/src/components/MetricsConfiguration/MetricsOverview.tsx index 4c7b2ca68..7fbefce0b 100644 --- a/publisher/src/components/MetricsConfiguration/MetricsOverview.tsx +++ b/publisher/src/components/MetricsConfiguration/MetricsOverview.tsx @@ -15,6 +15,10 @@ // along with this program. If not, see . // ============================================================================= +import { + Dropdown, + DropdownOption, +} from "@justice-counts/common/components/Dropdown"; import { TabbedBar } from "@justice-counts/common/components/TabbedBar"; import { AgencySystem, @@ -23,7 +27,7 @@ import { import { frequencyString } from "@justice-counts/common/utils/helperUtils"; import { observer } from "mobx-react-lite"; import React from "react"; -import { useParams } from "react-router-dom"; +import { useNavigate, useParams } from "react-router-dom"; import { NotFound } from "../../pages/NotFound"; import { useStore } from "../../stores"; @@ -33,18 +37,19 @@ import { AppGuideKeys, GuideKeys } from "../HelpCenter/types"; import { createURLToGuide } from "../HelpCenter/utils"; import { DisclaimerBanner } from "../primitives"; import { useSettingsSearchParams } from "../Settings"; -import { ChildAgenciesDropdown } from "./ChildAgenciesDropdown"; import * as Styled from "./MetricsOverview.styled"; export const MetricsOverview = observer(() => { const [settingsSearchParams, setSettingsSearchParams] = useSettingsSearchParams(); - + const navigate = useNavigate(); const { agencyId } = useParams() as { agencyId: string }; - const { userStore, metricConfigStore } = useStore(); + const { userStore, metricConfigStore, agencyStore } = useStore(); const { getMetricsBySystem } = metricConfigStore; + const { superagencyChildAgencies } = agencyStore; + const { system: systemSearchParam } = settingsSearchParams; const handleSystemClick = (option: AgencySystem) => { @@ -104,6 +109,38 @@ export const MetricsOverview = observer(() => { }; }) || []; + const isChildAgency = superagencyChildAgencies?.find( + (childAgency) => childAgency.id === currentAgency?.id + ); + + const userSuperagency = userStore.userAgencies?.find( + (agency) => agency.is_superagency + ); + + const superagencyDropdownOptions: DropdownOption[] = + userSuperagency && isChildAgency + ? [ + { + key: userSuperagency.id, + label: `${userSuperagency.name} (Superagency)`, + onClick: () => + navigate(`/agency/${userSuperagency.id}/metric-config`), + }, + ] + : []; + + const childAgenciesDropdownOptions: DropdownOption[] = + superagencyChildAgencies + ? superagencyChildAgencies + .map((agency) => ({ + key: agency.id, + label: agency.name, + onClick: () => navigate(`/agency/${agency.id}/metric-config`), + highlight: agency.id === currentAgency?.id, + })) + .sort((a, b) => a.label.localeCompare(b.label)) + : []; + if (systemSearchParam && !currentAgency?.systems.includes(systemSearchParam)) return ; @@ -131,7 +168,25 @@ export const MetricsOverview = observer(() => { - + {childAgenciesDropdownOptions.length > 0 && + (isSuperagency || isChildAgency) && ( + + + + )} {/* System Selection */} {showSystems && ( From 7631500215081d817a7a618e3cea5842ceb485b8 Mon Sep 17 00:00:00 2001 From: Mahmoud O <59492998+mxosman@users.noreply.github.com> Date: Wed, 24 Apr 2024 11:27:56 -0500 Subject: [PATCH 21/28] =?UTF-8?q?Revert=20"[Publisher][Superagencies]=20Ad?= =?UTF-8?q?d=20dropdown=20for=20child=20agencies=20to=20Set=20Up=20?= =?UTF-8?q?=E2=80=A6"=20(#1313)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 9c4424f9332f5272102ad5ab54edc5958b7c7218. --- .../components/Dropdown/Dropdown.styled.tsx | 22 +------ common/components/Dropdown/Dropdown.tsx | 24 ------- publisher/src/components/Menu/Menu.tsx | 9 ++- .../MetricsOverview.styled.tsx | 31 +-------- .../MetricsConfiguration/MetricsOverview.tsx | 63 +------------------ publisher/src/stores/AgencyStore.ts | 2 + 6 files changed, 12 insertions(+), 139 deletions(-) diff --git a/common/components/Dropdown/Dropdown.styled.tsx b/common/components/Dropdown/Dropdown.styled.tsx index dc2cb2eed..7eb30f91a 100644 --- a/common/components/Dropdown/Dropdown.styled.tsx +++ b/common/components/Dropdown/Dropdown.styled.tsx @@ -194,34 +194,18 @@ export const CustomInputWrapper = styled.div` padding: 20px 16px 8px 16px; `; -export const CustomInput = styled.input<{ - customClearButton?: boolean; -}>` +export const CustomInput = styled.input` ${typography.sizeCSS.normal} width: 100%; - padding: 4px 10px; + padding: 4px 8px 4px 8px; border: 1px solid ${palette.solid.lightgrey3}; - color: ${palette.solid.darkgrey}; + color: ${palette.highlight.grey9}; background: ${palette.solid.lightgrey2}; border-radius: 2px; &::placeholder { color: ${palette.highlight.grey5}; } - - ${({ customClearButton }) => - customClearButton && - `&::-webkit-search-cancel-button { - -webkit-appearance: none; - }`} -`; - -export const CustomClearButton = styled.button` - border: 0; - padding: 0; - background: transparent; - color: ${palette.highlight.grey8}; - cursor: pointer; `; export const NoResultsFoundWrapper = styled.div` diff --git a/common/components/Dropdown/Dropdown.tsx b/common/components/Dropdown/Dropdown.tsx index 7b05eb0cc..b28598e44 100644 --- a/common/components/Dropdown/Dropdown.tsx +++ b/common/components/Dropdown/Dropdown.tsx @@ -15,11 +15,9 @@ // along with this program. If not, see . // ============================================================================= -import { Icon, IconSVG } from "@recidiviz/design-system"; import React, { useEffect, useRef, useState } from "react"; import dropdownCaret from "../../assets/dropdown-caret.svg"; -import { palette } from "../GlobalStyles"; import * as Styled from "./Dropdown.styled"; import { DropdownMenuAlignment, @@ -42,7 +40,6 @@ type DropdownProps = { fullHeight?: boolean; highlightIcon?: React.ReactNode; typeaheadSearch?: { placeholder: string }; - customClearSearchButton?: string; }; /** @@ -74,7 +71,6 @@ export function Dropdown({ fullHeight, highlightIcon, typeaheadSearch, - customClearSearchButton, }: DropdownProps) { const [filteredOptions, setFilteredOptions] = useState(); const [inputValue, setInputValue] = useState(""); @@ -137,17 +133,8 @@ export function Dropdown({ <> {typeaheadSearch && ( - {customClearSearchButton && ( - - )} - {customClearSearchButton && ( - { - setInputValue(""); - updateFilteredOptions(""); - }} - > - {customClearSearchButton} - - )} )} diff --git a/publisher/src/components/Menu/Menu.tsx b/publisher/src/components/Menu/Menu.tsx index f9fef7668..74d8436f9 100644 --- a/publisher/src/components/Menu/Menu.tsx +++ b/publisher/src/components/Menu/Menu.tsx @@ -42,9 +42,6 @@ const Menu: React.FC = () => { const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false); const pathWithoutAgency = removeAgencyFromPath(location.pathname); - const userSuperagency = userStore.userAgencies?.find( - (agency) => agency.is_superagency - ); const currentAgency = userStore.getAgency(agencyId); const hasDashboardEnabled = currentAgency?.is_dashboard_enabled; const agencyName = currentAgency?.name; @@ -199,9 +196,11 @@ const Menu: React.FC = () => { /* Here we're getting and storing a list of child agencies if the user is in a superagency. * */ useEffect(() => { - const superagencyId = userSuperagency && String(userSuperagency.id); + const superagencyId = userStore.isAgencySuperagency(agencyId) + ? agencyId + : undefined; agencyStore.loadChildAgencies(superagencyId); - }, [agencyStore, userSuperagency]); + }, [agencyId, agencyStore, userStore]); return ( diff --git a/publisher/src/components/MetricsConfiguration/MetricsOverview.styled.tsx b/publisher/src/components/MetricsConfiguration/MetricsOverview.styled.tsx index e2fcf460b..868fe067c 100644 --- a/publisher/src/components/MetricsConfiguration/MetricsOverview.styled.tsx +++ b/publisher/src/components/MetricsConfiguration/MetricsOverview.styled.tsx @@ -15,12 +15,6 @@ // along with this program. If not, see . // ============================================================================= -import { - CustomDropdown, - CustomDropdownMenu, - CustomInput, - CustomInputWrapper, -} from "@justice-counts/common/components/Dropdown"; import { HEADER_BAR_HEIGHT, palette, @@ -62,7 +56,7 @@ export const OverviewHeader = styled.div` export const OverviewDescription = styled.div` ${typography.body}; max-width: 470px; - margin-bottom: 24px; + margin-bottom: 32px; a, a:visited { @@ -197,26 +191,3 @@ export const TabbedBarWrapper = styled.div` gap: 8px 16px; } `; - -export const DropdownWrapper = styled.div` - padding-bottom: 14px; - - & ${CustomDropdown} { - width: 400px; - border: 1px solid ${palette.highlight.grey2}; - padding: 0 12px; - } - & ${CustomDropdownMenu} { - box-shadow: none; - border: 1px solid ${palette.highlight.grey2}; - } - & ${CustomInputWrapper} { - padding: 12px 16px; - border-bottom: 1px solid ${palette.highlight.grey2}; - } - & ${CustomInput} { - border: 0; - outline: 0; - background: transparent; - } -`; diff --git a/publisher/src/components/MetricsConfiguration/MetricsOverview.tsx b/publisher/src/components/MetricsConfiguration/MetricsOverview.tsx index 7fbefce0b..ee4b44693 100644 --- a/publisher/src/components/MetricsConfiguration/MetricsOverview.tsx +++ b/publisher/src/components/MetricsConfiguration/MetricsOverview.tsx @@ -15,10 +15,6 @@ // along with this program. If not, see . // ============================================================================= -import { - Dropdown, - DropdownOption, -} from "@justice-counts/common/components/Dropdown"; import { TabbedBar } from "@justice-counts/common/components/TabbedBar"; import { AgencySystem, @@ -27,7 +23,7 @@ import { import { frequencyString } from "@justice-counts/common/utils/helperUtils"; import { observer } from "mobx-react-lite"; import React from "react"; -import { useNavigate, useParams } from "react-router-dom"; +import { useParams } from "react-router-dom"; import { NotFound } from "../../pages/NotFound"; import { useStore } from "../../stores"; @@ -42,14 +38,11 @@ import * as Styled from "./MetricsOverview.styled"; export const MetricsOverview = observer(() => { const [settingsSearchParams, setSettingsSearchParams] = useSettingsSearchParams(); - const navigate = useNavigate(); const { agencyId } = useParams() as { agencyId: string }; - const { userStore, metricConfigStore, agencyStore } = useStore(); + const { userStore, metricConfigStore } = useStore(); const { getMetricsBySystem } = metricConfigStore; - const { superagencyChildAgencies } = agencyStore; - const { system: systemSearchParam } = settingsSearchParams; const handleSystemClick = (option: AgencySystem) => { @@ -109,38 +102,6 @@ export const MetricsOverview = observer(() => { }; }) || []; - const isChildAgency = superagencyChildAgencies?.find( - (childAgency) => childAgency.id === currentAgency?.id - ); - - const userSuperagency = userStore.userAgencies?.find( - (agency) => agency.is_superagency - ); - - const superagencyDropdownOptions: DropdownOption[] = - userSuperagency && isChildAgency - ? [ - { - key: userSuperagency.id, - label: `${userSuperagency.name} (Superagency)`, - onClick: () => - navigate(`/agency/${userSuperagency.id}/metric-config`), - }, - ] - : []; - - const childAgenciesDropdownOptions: DropdownOption[] = - superagencyChildAgencies - ? superagencyChildAgencies - .map((agency) => ({ - key: agency.id, - label: agency.name, - onClick: () => navigate(`/agency/${agency.id}/metric-config`), - highlight: agency.id === currentAgency?.id, - })) - .sort((a, b) => a.label.localeCompare(b.label)) - : []; - if (systemSearchParam && !currentAgency?.systems.includes(systemSearchParam)) return ; @@ -168,26 +129,6 @@ export const MetricsOverview = observer(() => { - {childAgenciesDropdownOptions.length > 0 && - (isSuperagency || isChildAgency) && ( - - - - )} - {/* System Selection */} {showSystems && ( diff --git a/publisher/src/stores/AgencyStore.ts b/publisher/src/stores/AgencyStore.ts index a85252af5..7b72edc1a 100644 --- a/publisher/src/stores/AgencyStore.ts +++ b/publisher/src/stores/AgencyStore.ts @@ -279,6 +279,8 @@ class AgencyStore { loadChildAgencies(superagencyId: string | undefined) { if (superagencyId) { this.getChildAgencies(superagencyId); + } else { + this.childAgencies = []; } } From 4788cf63350e5d976e2a88ef48e57f36b5aee065 Mon Sep 17 00:00:00 2001 From: Ilya Date: Mon, 29 Apr 2024 15:35:47 +0300 Subject: [PATCH 22/28] [Publisher][Superagencies] Add dropdown for child agencies (#1314) * Add dropdown UI for child agencies to Set Up Metrics page * Keep dropdown visible for child agencies * Clean up * Add superagency to dropdown if viewing child agency * Fix superagency id getting * Create dropdown component * Add dropdown to Explore Data page * Clean up * Fixes * More fixes * Minor fix --- .../components/Dropdown/Dropdown.styled.tsx | 22 ++++- common/components/Dropdown/Dropdown.tsx | 24 +++++ .../DataViz/MetricsDataChart.styled.tsx | 26 +++--- .../components/DataViz/MetricsDataChart.tsx | 3 + publisher/src/components/Menu/Menu.tsx | 11 ++- .../ChildAgenciesDropdown.styled.tsx | 55 +++++++++++ .../ChildAgenciesDropdown.tsx | 93 +++++++++++++++++++ .../MetricsOverview.styled.tsx | 2 +- .../MetricsConfiguration/MetricsOverview.tsx | 3 + publisher/src/stores/AgencyStore.ts | 16 ++-- 10 files changed, 229 insertions(+), 26 deletions(-) create mode 100644 publisher/src/components/MetricsConfiguration/ChildAgenciesDropdown.styled.tsx create mode 100644 publisher/src/components/MetricsConfiguration/ChildAgenciesDropdown.tsx diff --git a/common/components/Dropdown/Dropdown.styled.tsx b/common/components/Dropdown/Dropdown.styled.tsx index 7eb30f91a..dc2cb2eed 100644 --- a/common/components/Dropdown/Dropdown.styled.tsx +++ b/common/components/Dropdown/Dropdown.styled.tsx @@ -194,18 +194,34 @@ export const CustomInputWrapper = styled.div` padding: 20px 16px 8px 16px; `; -export const CustomInput = styled.input` +export const CustomInput = styled.input<{ + customClearButton?: boolean; +}>` ${typography.sizeCSS.normal} width: 100%; - padding: 4px 8px 4px 8px; + padding: 4px 10px; border: 1px solid ${palette.solid.lightgrey3}; - color: ${palette.highlight.grey9}; + color: ${palette.solid.darkgrey}; background: ${palette.solid.lightgrey2}; border-radius: 2px; &::placeholder { color: ${palette.highlight.grey5}; } + + ${({ customClearButton }) => + customClearButton && + `&::-webkit-search-cancel-button { + -webkit-appearance: none; + }`} +`; + +export const CustomClearButton = styled.button` + border: 0; + padding: 0; + background: transparent; + color: ${palette.highlight.grey8}; + cursor: pointer; `; export const NoResultsFoundWrapper = styled.div` diff --git a/common/components/Dropdown/Dropdown.tsx b/common/components/Dropdown/Dropdown.tsx index b28598e44..7b05eb0cc 100644 --- a/common/components/Dropdown/Dropdown.tsx +++ b/common/components/Dropdown/Dropdown.tsx @@ -15,9 +15,11 @@ // along with this program. If not, see . // ============================================================================= +import { Icon, IconSVG } from "@recidiviz/design-system"; import React, { useEffect, useRef, useState } from "react"; import dropdownCaret from "../../assets/dropdown-caret.svg"; +import { palette } from "../GlobalStyles"; import * as Styled from "./Dropdown.styled"; import { DropdownMenuAlignment, @@ -40,6 +42,7 @@ type DropdownProps = { fullHeight?: boolean; highlightIcon?: React.ReactNode; typeaheadSearch?: { placeholder: string }; + customClearSearchButton?: string; }; /** @@ -71,6 +74,7 @@ export function Dropdown({ fullHeight, highlightIcon, typeaheadSearch, + customClearSearchButton, }: DropdownProps) { const [filteredOptions, setFilteredOptions] = useState(); const [inputValue, setInputValue] = useState(""); @@ -133,8 +137,17 @@ export function Dropdown({ <> {typeaheadSearch && ( + {customClearSearchButton && ( + + )} + {customClearSearchButton && ( + { + setInputValue(""); + updateFilteredOptions(""); + }} + > + {customClearSearchButton} + + )} )} diff --git a/publisher/src/components/DataViz/MetricsDataChart.styled.tsx b/publisher/src/components/DataViz/MetricsDataChart.styled.tsx index 4dfb90b75..c8212d9bf 100644 --- a/publisher/src/components/DataViz/MetricsDataChart.styled.tsx +++ b/publisher/src/components/DataViz/MetricsDataChart.styled.tsx @@ -56,6 +56,16 @@ export const MetricsViewPanel = styled.div<{ } `; +export const DisclaimerContainer = styled.div` + display: flex; + flex-direction: column; + justify-content: flex-end; + padding-top: 15px; + padding-bottom: 37px; + width: ${INNER_PANEL_LEFT_CONTAINER_MAX_WIDTH}px; + min-height: 200px; +`; + export const PanelContainerLeft = styled.div` width: 25%; min-width: calc(314px + 24px + 95px); @@ -64,7 +74,11 @@ export const PanelContainerLeft = styled.div` display: flex; flex-direction: column; justify-content: space-between; - padding: 46px 0 0 24px; + padding: 46px 24px 0; + + & > ${DisclaimerContainer} { + margin-top: auto; + } @media only screen and (max-width: ${MIN_DESKTOP_WIDTH}px) { display: none; @@ -135,16 +149,6 @@ export const MetricItem = styled.div<{ selected?: boolean }>` } `; -export const DisclaimerContainer = styled.div` - display: flex; - flex-direction: column; - justify-content: flex-end; - padding-top: 15px; - padding-bottom: 37px; - width: ${INNER_PANEL_LEFT_CONTAINER_MAX_WIDTH}px; - min-height: 200px; -`; - export const DisclaimerTitle = styled.div` ${typography.sizeCSS.small} `; diff --git a/publisher/src/components/DataViz/MetricsDataChart.tsx b/publisher/src/components/DataViz/MetricsDataChart.tsx index 4df7639f5..721ea0f25 100644 --- a/publisher/src/components/DataViz/MetricsDataChart.tsx +++ b/publisher/src/components/DataViz/MetricsDataChart.tsx @@ -54,6 +54,7 @@ import { ReactComponent as SwitchToDataTableIcon } from "../assets/switch-to-dat import { AppGuideKeys, GuideKeys } from "../HelpCenter/types"; import { createURLToGuide } from "../HelpCenter/utils"; import { Loading } from "../Loading"; +import { ChildAgenciesDropdown } from "../MetricsConfiguration/ChildAgenciesDropdown"; import { DisclaimerBanner } from "../primitives"; import { useSettingsSearchParams } from "../Settings"; import ConnectedDatapointsView from "./ConnectedDatapointsView"; @@ -250,6 +251,7 @@ export const MetricsDataChart: React.FC = observer(() => { {/* List Of Metrics */} +
{ {/* Data Visualization */} + {formatSystemName(currentSystem)} diff --git a/publisher/src/components/Menu/Menu.tsx b/publisher/src/components/Menu/Menu.tsx index 74d8436f9..23a98c3dc 100644 --- a/publisher/src/components/Menu/Menu.tsx +++ b/publisher/src/components/Menu/Menu.tsx @@ -198,9 +198,14 @@ const Menu: React.FC = () => { useEffect(() => { const superagencyId = userStore.isAgencySuperagency(agencyId) ? agencyId - : undefined; - agencyStore.loadChildAgencies(superagencyId); - }, [agencyId, agencyStore, userStore]); + : currentAgency?.super_agency_id; + const userHasAccessToSuperagency = userStore.userAgencies?.find( + (agency) => agency.id === Number(superagencyId) + ); + + if (superagencyId && userHasAccessToSuperagency) + agencyStore.getChildAgencies(String(superagencyId)); + }, [agencyId, currentAgency, agencyStore, userStore]); return ( diff --git a/publisher/src/components/MetricsConfiguration/ChildAgenciesDropdown.styled.tsx b/publisher/src/components/MetricsConfiguration/ChildAgenciesDropdown.styled.tsx new file mode 100644 index 000000000..4185e1fa3 --- /dev/null +++ b/publisher/src/components/MetricsConfiguration/ChildAgenciesDropdown.styled.tsx @@ -0,0 +1,55 @@ +// Recidiviz - a data platform for criminal justice reform +// Copyright (C) 2023 Recidiviz, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// ============================================================================= + +import { + CustomDropdown, + CustomDropdownMenu, + CustomInput, + CustomInputWrapper, +} from "@justice-counts/common/components/Dropdown"; +import { + MIN_DESKTOP_WIDTH, + palette, +} from "@justice-counts/common/components/GlobalStyles"; +import styled from "styled-components/macro"; + +export const DropdownWrapper = styled.div` + padding-bottom: 10px; + & ${CustomDropdown} { + width: 100%; + max-width: 420px; + border: 1px solid ${palette.highlight.grey2}; + padding: 0 12px; + + @media only screen and (max-width: ${MIN_DESKTOP_WIDTH}px) { + max-width: unset; + } + } + & ${CustomDropdownMenu} { + box-shadow: none; + border: 1px solid ${palette.highlight.grey2}; + } + & ${CustomInputWrapper} { + padding: 12px 16px; + border-bottom: 1px solid ${palette.highlight.grey2}; + } + & ${CustomInput} { + border: 0; + outline: 0; + background: transparent; + } +`; diff --git a/publisher/src/components/MetricsConfiguration/ChildAgenciesDropdown.tsx b/publisher/src/components/MetricsConfiguration/ChildAgenciesDropdown.tsx new file mode 100644 index 000000000..80880ddec --- /dev/null +++ b/publisher/src/components/MetricsConfiguration/ChildAgenciesDropdown.tsx @@ -0,0 +1,93 @@ +// Recidiviz - a data platform for criminal justice reform +// Copyright (C) 2023 Recidiviz, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// ============================================================================= + +import { + Dropdown, + DropdownOption, +} from "@justice-counts/common/components/Dropdown"; +import { observer } from "mobx-react-lite"; +import React from "react"; +import { useNavigate, useParams } from "react-router-dom"; + +import { useStore } from "../../stores"; +import * as Styled from "./ChildAgenciesDropdown.styled"; + +export const ChildAgenciesDropdown: React.FC<{ + view: string; +}> = observer(({ view }) => { + const navigate = useNavigate(); + const { agencyId } = useParams() as { agencyId: string }; + const { userStore, agencyStore } = useStore(); + + const { superagencyChildAgencies } = agencyStore; + const currentAgency = userStore.getAgency(agencyId); + const isSuperagency = userStore.isAgencySuperagency(agencyId); + const superagencyId = isSuperagency + ? agencyId + : currentAgency?.super_agency_id; + + const isChildAgency = superagencyChildAgencies?.some( + (childAgency) => childAgency.id === currentAgency?.id + ); + + const currentSuperagency = userStore.userAgencies?.find( + (agency) => agency.id === superagencyId + ); + + const superagencyDropdownOptions: DropdownOption[] = + currentSuperagency && isChildAgency + ? [ + { + key: currentSuperagency.id, + label: `${currentSuperagency.name} (Superagency)`, + onClick: () => navigate(`/agency/${currentSuperagency.id}/${view}`), + }, + ] + : []; + + const childAgenciesDropdownOptions: DropdownOption[] = + superagencyChildAgencies + ? superagencyChildAgencies + .map((agency) => ({ + key: agency.id, + label: agency.name, + onClick: () => navigate(`/agency/${agency.id}/${view}`), + highlight: agency.id === currentAgency?.id, + })) + .sort((a, b) => a.label.localeCompare(b.label)) + : []; + + return ( + childAgenciesDropdownOptions.length > 0 && + (isSuperagency || isChildAgency) && ( + + + + ) + ); +}); diff --git a/publisher/src/components/MetricsConfiguration/MetricsOverview.styled.tsx b/publisher/src/components/MetricsConfiguration/MetricsOverview.styled.tsx index 868fe067c..ad2e5edca 100644 --- a/publisher/src/components/MetricsConfiguration/MetricsOverview.styled.tsx +++ b/publisher/src/components/MetricsConfiguration/MetricsOverview.styled.tsx @@ -56,7 +56,7 @@ export const OverviewHeader = styled.div` export const OverviewDescription = styled.div` ${typography.body}; max-width: 470px; - margin-bottom: 32px; + margin-bottom: 24px; a, a:visited { diff --git a/publisher/src/components/MetricsConfiguration/MetricsOverview.tsx b/publisher/src/components/MetricsConfiguration/MetricsOverview.tsx index ee4b44693..0df8afe93 100644 --- a/publisher/src/components/MetricsConfiguration/MetricsOverview.tsx +++ b/publisher/src/components/MetricsConfiguration/MetricsOverview.tsx @@ -33,6 +33,7 @@ import { AppGuideKeys, GuideKeys } from "../HelpCenter/types"; import { createURLToGuide } from "../HelpCenter/utils"; import { DisclaimerBanner } from "../primitives"; import { useSettingsSearchParams } from "../Settings"; +import { ChildAgenciesDropdown } from "./ChildAgenciesDropdown"; import * as Styled from "./MetricsOverview.styled"; export const MetricsOverview = observer(() => { @@ -129,6 +130,8 @@ export const MetricsOverview = observer(() => { + + {/* System Selection */} {showSystems && ( diff --git a/publisher/src/stores/AgencyStore.ts b/publisher/src/stores/AgencyStore.ts index 7b72edc1a..71820ba35 100644 --- a/publisher/src/stores/AgencyStore.ts +++ b/publisher/src/stores/AgencyStore.ts @@ -155,6 +155,14 @@ class AgencyStore { })) as Response; if (response.status !== 200) { + runInAction(() => { + this.childAgencies = []; + }); + showToast({ + message: `There was an issue getting a list of child agencies.`, + color: "red", + timeout: 4000, + }); throw new Error("There was an issue getting a list of child agencies."); } const responseJson = (await response.json()) as ChildAgenciesRecord; @@ -276,14 +284,6 @@ class AgencyStore { return { settings: newSettings }; }; - loadChildAgencies(superagencyId: string | undefined) { - if (superagencyId) { - this.getChildAgencies(superagencyId); - } else { - this.childAgencies = []; - } - } - updateAgencySystems = ( systems: AgencySystem[] ): { systems: AgencySystem[] } => { From 2a48c57ad87f56a8266b92d113fc23072c3edc15 Mon Sep 17 00:00:00 2001 From: Michelle Orden <82831800+morden35@users.noreply.github.com> Date: Wed, 1 May 2024 13:36:52 -0500 Subject: [PATCH 23/28] [Justice Counts] Trim whitespace from agency name (#1319) * trim whitespace from agency name * replace strings * lint fix * add new helper * regex fix --------- Co-authored-by: Michelle Orden --- publisher/src/components/AdminPanel/AgencyProvisioning.tsx | 4 ++++ publisher/src/stores/AdminPanelStore.ts | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/publisher/src/components/AdminPanel/AgencyProvisioning.tsx b/publisher/src/components/AdminPanel/AgencyProvisioning.tsx index 66c2e0194..3914e9ebf 100644 --- a/publisher/src/components/AdminPanel/AgencyProvisioning.tsx +++ b/publisher/src/components/AdminPanel/AgencyProvisioning.tsx @@ -93,6 +93,7 @@ export const AgencyProvisioning: React.FC = observer( updateChildAgencyIDs, updateTeamMembers, saveAgencyProvisioningUpdates, + saveAgencyName, copySuperagencyMetricSettingsToChildAgencies, } = adminPanelStore; const scrollableContainerRef = useRef(null); @@ -249,6 +250,9 @@ export const AgencyProvisioning: React.FC = observer( const saveUpdates = async () => { setIsSaveInProgress(true); + // Update final agency name + saveAgencyName(agencyProvisioningUpdates.name); + /** Update final list of systems, child agencies, and team members */ updateSystems(Array.from(selectedSystems)); updateChildAgencyIDs(Array.from(selectedChildAgencyIDs)); diff --git a/publisher/src/stores/AdminPanelStore.ts b/publisher/src/stores/AdminPanelStore.ts index 48a7fe5fa..5e10d7620 100644 --- a/publisher/src/stores/AdminPanelStore.ts +++ b/publisher/src/stores/AdminPanelStore.ts @@ -354,6 +354,10 @@ class AdminPanelStore { this.agencyProvisioningUpdates.name = name; } + saveAgencyName(name: string) { + this.agencyProvisioningUpdates.name = name.trim().replaceAll(/\s+/gi, " "); + } + updateStateCode(stateCode: StateCodeKey | null) { const lowercaseStateCode = stateCode?.toLocaleLowerCase() as StateCodeKey; this.agencyProvisioningUpdates.state_code = lowercaseStateCode; From 9fba3606098cdda243c606f68b2b31cbd42afc94 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 May 2024 04:01:50 +0000 Subject: [PATCH 24/28] Bump ejs from 3.1.8 to 3.1.10 (#1325) Bumps [ejs](https://github.com/mde/ejs) from 3.1.8 to 3.1.10. - [Release notes](https://github.com/mde/ejs/releases) - [Commits](https://github.com/mde/ejs/compare/v3.1.8...v3.1.10) --- updated-dependencies: - dependency-name: ejs dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 46 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/yarn.lock b/yarn.lock index eace9d5de..2d28845c2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8800,17 +8800,10 @@ ee-first@1.1.1: resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== -ejs@^3.1.6: - version "3.1.8" - resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.8.tgz#758d32910c78047585c7ef1f92f9ee041c1c190b" - integrity sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ== - dependencies: - jake "^10.8.5" - -ejs@^3.1.8: - version "3.1.9" - resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.9.tgz#03c9e8777fe12686a9effcef22303ca3d8eeb361" - integrity sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ== +ejs@^3.1.6, ejs@^3.1.8: + version "3.1.10" + resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.10.tgz#69ab8358b14e896f80cc39e62087b88500c3ac3b" + integrity sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA== dependencies: jake "^10.8.5" @@ -15613,7 +15606,16 @@ string-natural-compare@^3.0.1: resolved "https://registry.yarnpkg.com/string-natural-compare/-/string-natural-compare-3.0.1.tgz#7a42d58474454963759e8e8b7ae63d71c1e7fdf4" integrity sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw== -"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -15733,7 +15735,14 @@ stringify-object@^3.3.0: is-obj "^1.0.1" is-regexp "^1.0.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -17217,7 +17226,16 @@ workbox-window@6.5.4: "@types/trusted-types" "^2.0.2" workbox-core "6.5.4" -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== From 29ceed74ecaeb1946635ef1965b02ccc470a7aaa Mon Sep 17 00:00:00 2001 From: Mahmoud O <59492998+mxosman@users.noreply.github.com> Date: Thu, 2 May 2024 11:35:10 -0500 Subject: [PATCH 25/28] [Publisher] Set Up Metrics: Add metric and dimension descriptions in definition modal (#1324) * Add metric and dimension descriptions in definition modal * Clean up * Spacing --------- Co-authored-by: Mahmoud --- .../MetricsConfiguration/DefinitionModalForm.tsx | 16 +++++++++++++--- .../MetricsConfiguration/ModalForm.styled.tsx | 6 ++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/publisher/src/components/MetricsConfiguration/DefinitionModalForm.tsx b/publisher/src/components/MetricsConfiguration/DefinitionModalForm.tsx index 900996935..5bdf3c7ed 100644 --- a/publisher/src/components/MetricsConfiguration/DefinitionModalForm.tsx +++ b/publisher/src/components/MetricsConfiguration/DefinitionModalForm.tsx @@ -330,14 +330,18 @@ function DefinitionModalForm({ activeDimensionKey ]) || undefined; + const displayLabel = isMetricDefinitionSettings + ? metrics[systemMetricKey]?.label + : currentDimension?.label; + const displayDescription = isMetricDefinitionSettings + ? metrics[systemMetricKey]?.description + : currentDimension?.description; return ( - {isMetricDefinitionSettings - ? metrics[systemMetricKey]?.label - : currentDimension?.label} + {displayLabel} @@ -362,6 +366,12 @@ function DefinitionModalForm({ )} + + + {displayLabel} description +

{displayDescription}

+
+ {currentSettings && ( {Object.entries(currentSettings).map( diff --git a/publisher/src/components/MetricsConfiguration/ModalForm.styled.tsx b/publisher/src/components/MetricsConfiguration/ModalForm.styled.tsx index 4902b6bf7..5d3a63716 100644 --- a/publisher/src/components/MetricsConfiguration/ModalForm.styled.tsx +++ b/publisher/src/components/MetricsConfiguration/ModalForm.styled.tsx @@ -88,6 +88,12 @@ export const Title = styled.div` text-transform: capitalize; `; +export const Subtitle = styled.div` + ${typography.bodyEmphasized}; + text-transform: capitalize; + margin-bottom: 4px; +`; + export const Description = styled.div` ${typography.body}; line-height: 24px; From 834107801a3e1d1fc9953cdba8f661d7e8863f5d Mon Sep 17 00:00:00 2001 From: Mahmoud O <59492998+mxosman@users.noreply.github.com> Date: Thu, 2 May 2024 17:16:29 -0500 Subject: [PATCH 26/28] [Publisher][Bug] Set Up Metrics Overview Page: Fix grouping of action required, available, and unavailable for supervision agencies with subpopulations (#1320) * Intial work on fix for supervision subsystems in metric overview * Refactor using a reducer - update types * Adjust type * Fix the frequency displayed in the overview page * Add comments * Add more comments * Clean up * Refactor getMetricFrequencies to get a list of frequencies from disaggregated supervision subsystems * Comment fix --------- Co-authored-by: Mahmoud Co-authored-by: Mahmoud --- common/utils/helperUtils.ts | 4 +- .../MetricsConfiguration/MetricsOverview.tsx | 198 ++++++++++++++---- .../components/MetricsConfiguration/types.ts | 1 + publisher/src/stores/MetricConfigStore.tsx | 14 +- 4 files changed, 166 insertions(+), 51 deletions(-) diff --git a/common/utils/helperUtils.ts b/common/utils/helperUtils.ts index 067e6b0b9..e66db3bad 100644 --- a/common/utils/helperUtils.ts +++ b/common/utils/helperUtils.ts @@ -23,8 +23,8 @@ export const slugify = (str: string): string => str?.replace(/\s/g, "-")?.toLowerCase(); export const frequencyString = (frequency?: string) => { - if (frequency === "ANNUAL") { - return "ANNUALLY"; + if (frequency?.includes("ANNUAL")) { + return frequency.replaceAll("ANNUAL", "ANNUALLY"); } return frequency; }; diff --git a/publisher/src/components/MetricsConfiguration/MetricsOverview.tsx b/publisher/src/components/MetricsConfiguration/MetricsOverview.tsx index 0df8afe93..56cd4d96d 100644 --- a/publisher/src/components/MetricsConfiguration/MetricsOverview.tsx +++ b/publisher/src/components/MetricsConfiguration/MetricsOverview.tsx @@ -20,7 +20,10 @@ import { AgencySystem, SupervisionSubsystems, } from "@justice-counts/common/types"; -import { frequencyString } from "@justice-counts/common/utils/helperUtils"; +import { + frequencyString, + removeSnakeCase, +} from "@justice-counts/common/utils/helperUtils"; import { observer } from "mobx-react-lite"; import React from "react"; import { useParams } from "react-router-dom"; @@ -32,17 +35,22 @@ import { ReactComponent as RightArrowIcon } from "../assets/bold-right-arrow-ico import { AppGuideKeys, GuideKeys } from "../HelpCenter/types"; import { createURLToGuide } from "../HelpCenter/utils"; import { DisclaimerBanner } from "../primitives"; -import { useSettingsSearchParams } from "../Settings"; +import { + replaceSystemMetricKeyWithNewSystem, + useSettingsSearchParams, +} from "../Settings"; import { ChildAgenciesDropdown } from "./ChildAgenciesDropdown"; import * as Styled from "./MetricsOverview.styled"; +import { MetricInfo } from "./types"; export const MetricsOverview = observer(() => { const [settingsSearchParams, setSettingsSearchParams] = useSettingsSearchParams(); + const { agencyId } = useParams() as { agencyId: string }; const { userStore, metricConfigStore } = useStore(); - const { getMetricsBySystem } = metricConfigStore; + const { metrics, getMetricsBySystem } = metricConfigStore; const { system: systemSearchParam } = settingsSearchParams; @@ -58,6 +66,13 @@ export const MetricsOverview = observer(() => { const currentAgency = userStore.getAgency(agencyId); const currentSystem = systemSearchParam || currentAgency?.systems[0]; + const agencySupervisionSubsystems = currentAgency?.systems.filter((system) => + SupervisionSubsystems.includes(system) + ); + + const hasSupervisionSubsystems = + agencySupervisionSubsystems && agencySupervisionSubsystems.length > 0; + const isSuperagency = userStore.isAgencySuperagency(agencyId); const isSuperagencySystem = currentSystem === "SUPERAGENCY"; @@ -68,20 +83,126 @@ export const MetricsOverview = observer(() => { const showSystems = currentAgency?.systems && currentAgency?.systems?.length > 1; - const actionRequiredMetrics = getMetricsBySystem(currentSystem)?.filter( - ({ metric }) => metric.enabled === null - ); + const metricsByCurrentSystem = getMetricsBySystem(currentSystem); + + // Returns a given metric frequency(ies) accounting for supervision agencies disaggregated by subsystems + const getMetricFrequency = (metric: MetricInfo) => { + // Default frequencies (custom or default) for agencies without supervision subsystems + if ( + !(hasSupervisionSubsystems && metric.disaggregatedBySupervisionSubsystems) + ) { + return [metric.customFrequency || metric.defaultFrequency]; + } + + /** + * For supervision agencies w/ subsystems and disaggregated metrics, we find all + * enabled subsystems and return a list of their frequency and subsystem name. + */ + const supervisionSubsystemMetricFrequencies = + agencySupervisionSubsystems?.reduce((acc, subsystem) => { + const replacedSystemMetricKey = replaceSystemMetricKeyWithNewSystem( + `SUPERVISION-${metric.key}`, + subsystem + ); + if (metrics[replacedSystemMetricKey].enabled) { + const frequency = + metrics[replacedSystemMetricKey].customFrequency || + metrics[replacedSystemMetricKey].defaultFrequency; + acc.push( + `${frequency} (${removeSnakeCase(subsystem).toLocaleLowerCase()})` + ); + } + return acc; + }, [] as string[]); + + return supervisionSubsystemMetricFrequencies; + }; + + /** + * Returns a boolean that indicates whether or not a supervision agency has a + * subsystem metric with an enabled status matching the `status` argument provided + */ + const hasSupervisionSubsystemWithEnabledStatus = ( + status: boolean | null, + metricKey: string | undefined + ) => { + if (!metricKey) return undefined; + return !!agencySupervisionSubsystems?.find((subsystem) => { + const replacedSystemMetricKey = replaceSystemMetricKeyWithNewSystem( + `SUPERVISION-${metricKey}`, + subsystem + ); + return metrics[replacedSystemMetricKey].enabled === status; + }); + }; + + const { actionRequiredMetrics, availableMetrics, unavailableMetrics } = + metricsByCurrentSystem?.reduce( + (acc, metric) => { + // Default grouping for agencies without supervision subsystems + if ( + !( + hasSupervisionSubsystems && + metric.disaggregatedBySupervisionSubsystems + ) + ) { + if (metric.enabled) { + acc.availableMetrics.push(metric); + return acc; + } + if (metric.enabled === false) { + acc.unavailableMetrics.push(metric); + return acc; + } + if (metric.enabled === null) { + acc.actionRequiredMetrics.push(metric); + return acc; + } + } + + /** + * For supervision agencies w/ subsystems and disaggregated metrics, we need to do + * an extra check of all of the subsystems' metric enabled status. + * + * `actionRequiredMetrics` will include the metrics if at least one subsystem has the metric as `null` (untouched) + * - currently, we will never reach this case as disaggregating a metric by default enables the subsystem metrics + * `availableMetrics` will include the metric if at least one subsystem has the metric enabled + * `unavailableMetrics` will include the metric if ALL of the subsystems have the metric disabled + * + */ + const hasSubsystemsWithEnabledMetrics = + hasSupervisionSubsystemWithEnabledStatus(true, metric.key); + const hasSubsystemsWithDisabledMetrics = + hasSupervisionSubsystemWithEnabledStatus(false, metric.key); + const hasSubsystemsWithUntoucheddMetrics = + hasSupervisionSubsystemWithEnabledStatus(null, metric.key); + + if (hasSubsystemsWithEnabledMetrics) { + acc.availableMetrics.push(metric); + return acc; + } + if (hasSubsystemsWithDisabledMetrics) { + acc.unavailableMetrics.push(metric); + return acc; + } + if (hasSubsystemsWithUntoucheddMetrics) { + acc.actionRequiredMetrics.push(metric); + return acc; + } + return acc; + }, + { + actionRequiredMetrics: [], + availableMetrics: [], + unavailableMetrics: [], + } as { [key: string]: MetricInfo[] } + ) ?? {}; + const hasActionRequiredMetrics = actionRequiredMetrics && actionRequiredMetrics.length > 0; - const availableMetrics = getMetricsBySystem(currentSystem)?.filter( - ({ metric }) => metric.enabled - ); const hasAvailableMetrics = availableMetrics && availableMetrics.length > 0; - const unavailableMetrics = getMetricsBySystem(currentSystem)?.filter( - ({ metric }) => metric.enabled === false - ); const hasUnavailableMetrics = unavailableMetrics && unavailableMetrics.length > 0; @@ -145,13 +266,13 @@ export const MetricsOverview = observer(() => { Action required - {actionRequiredMetrics?.map(({ key, metric }) => ( + {actionRequiredMetrics?.map((metric) => ( setSettingsSearchParams({ system: currentSystem, - metric: key, + metric: metric.key, }) } > @@ -168,25 +289,28 @@ export const MetricsOverview = observer(() => { Available - {availableMetrics?.map(({ key, metric }) => ( - - setSettingsSearchParams({ - system: currentSystem, - metric: key, - }) - } - > - - {metric.label} - - {frequencyString(metric.customFrequency)?.toLowerCase()} - - - - - ))} + {availableMetrics?.map((metric) => { + const frequency = getMetricFrequency(metric); + return ( + + setSettingsSearchParams({ + system: currentSystem, + metric: metric.key, + }) + } + > + + {metric.label} + + {frequencyString(frequency.join(", "))?.toLowerCase()} + + + + + ); + })} )} {hasUnavailableMetrics && ( @@ -194,13 +318,13 @@ export const MetricsOverview = observer(() => { Unavailable Metrics - {unavailableMetrics?.map(({ key, metric }) => ( + {unavailableMetrics?.map((metric) => ( setSettingsSearchParams({ system: currentSystem, - metric: key, + metric: metric.key, }) } > diff --git a/publisher/src/components/MetricsConfiguration/types.ts b/publisher/src/components/MetricsConfiguration/types.ts index 6491c2a80..ad78febbb 100644 --- a/publisher/src/components/MetricsConfiguration/types.ts +++ b/publisher/src/components/MetricsConfiguration/types.ts @@ -48,6 +48,7 @@ export type MetricSettings = { }; export type MetricInfo = { + key?: string; enabled?: boolean | null; label?: string; description?: Metric["description"]; diff --git a/publisher/src/stores/MetricConfigStore.tsx b/publisher/src/stores/MetricConfigStore.tsx index 0454ee108..195780b79 100644 --- a/publisher/src/stores/MetricConfigStore.tsx +++ b/publisher/src/stores/MetricConfigStore.tsx @@ -25,7 +25,6 @@ import { MetricContext, MetricDisaggregationDimensions, MetricDisaggregations, - ReportFrequency, } from "@justice-counts/common/types"; import { makeAutoObservable, runInAction } from "mobx"; @@ -177,21 +176,12 @@ class MetricConfigStore { MetricConfigStore.splitSystemMetricKey(systemMetricKey); if (system.toLowerCase() === systemName.toLowerCase()) { - filteredMetrics.push({ key: metricKey, metric }); + filteredMetrics.push({ key: metricKey, ...metric }); } return filteredMetrics; }, - [] as { - key: string; - metric: { - enabled?: boolean | null; - label?: string; - description?: Metric["description"]; - defaultFrequency?: ReportFrequency; - customFrequency?: Metric["custom_frequency"]; - }; - }[] + [] as MetricInfo[] ); return metrics; From 8980d24d112f6a40e045d73995796067d1fb1adb Mon Sep 17 00:00:00 2001 From: Ilya Date: Fri, 3 May 2024 17:51:31 +0300 Subject: [PATCH 27/28] [Publisher][Admin Panel] Add functionality to choose metrics/child agencies during metric copy (#1282) * Add functionality to choose metrics/child agencies during metric copy * [Publisher][Admin Panel] Add functionality to search metrics/child agencies by sectors (#1283) * Add functionality to search metrics/child agencies by sectors * Add sector to metric name * Fix default metrics key propagation * Fixes * Fixes * Fix naming * Block saving if there are no child agencies or metrics selected to copy * FIx disabling button behavior * Small disable save button logic refactor * Small re-refactor of save disabled - I gave bad advice --------- Co-authored-by: Mahmoud --- .../AdminPanel/AdminPanel.styles.tsx | 5 +- .../AdminPanel/AgencyProvisioning.tsx | 285 +++++++++++++++--- publisher/src/components/AdminPanel/types.ts | 14 + .../src/components/assets/close-icon.svg | 3 + publisher/src/stores/AdminPanelStore.ts | 41 ++- 5 files changed, 296 insertions(+), 52 deletions(-) create mode 100644 publisher/src/components/assets/close-icon.svg diff --git a/publisher/src/components/AdminPanel/AdminPanel.styles.tsx b/publisher/src/components/AdminPanel/AdminPanel.styles.tsx index 58867c29d..15c999d95 100644 --- a/publisher/src/components/AdminPanel/AdminPanel.styles.tsx +++ b/publisher/src/components/AdminPanel/AdminPanel.styles.tsx @@ -826,9 +826,8 @@ export const GraphicLines = styled.div<{ type?: SaveConfirmationType }>` export const WarningMessage = styled.div` ${typography.sizeCSS.small} - max-width: 350px; + max-width: 750px; color: ${palette.solid.red}; - margin-top: 5px; display: flex; flex-direction: column; align-items: center; @@ -836,7 +835,7 @@ export const WarningMessage = styled.div` border: 1px solid ${palette.solid.red}; border-radius: 4px; padding: 16px; - margin-left: 12px; + margin: 5px auto 16px; p { text-align: center; diff --git a/publisher/src/components/AdminPanel/AgencyProvisioning.tsx b/publisher/src/components/AdminPanel/AgencyProvisioning.tsx index 3914e9ebf..50583db3f 100644 --- a/publisher/src/components/AdminPanel/AgencyProvisioning.tsx +++ b/publisher/src/components/AdminPanel/AgencyProvisioning.tsx @@ -77,10 +77,12 @@ export const AgencyProvisioning: React.FC = observer( users, agencies, agenciesByID, + metrics, systems, agencyProvisioningUpdates, searchableCounties, searchableSystems, + searchableMetrics, csgAndRecidivizUsers, csgAndRecidivizDefaultRole, updateAgencyName, @@ -124,11 +126,20 @@ export const AgencyProvisioning: React.FC = observer( ? new Set(agencyProvisioningUpdates.child_agency_ids) : new Set() ); + const [selectedChildAgencyIDsToCopy, setSelectedChildAgencyIDsToCopy] = + useState>( + agencyProvisioningUpdates.child_agency_ids + ? new Set(agencyProvisioningUpdates.child_agency_ids) + : new Set() + ); const [selectedSystems, setSelectedSystems] = useState>( agencyProvisioningUpdates.systems ? new Set(agencyProvisioningUpdates.systems) : new Set() ); + const [selectedMetricsKeys, setSelectedMetricsKeys] = useState>( + new Set() + ); const [selectedTeamMembersToAdd, setSelectedTeamMembersToAdd] = useState< Set >(new Set()); @@ -174,9 +185,14 @@ export const AgencyProvisioning: React.FC = observer( ); /** A list of superagencies and child agencies to select from */ - const childAgencies = availableAgencies.filter( - (agency) => !agency.is_superagency - ); + const childAgencies = availableAgencies + .filter((agency) => !agency.is_superagency) + .map((agency) => ({ + ...agency, + sectors: agency.systems.map((system) => + removeSnakeCase(system.toLocaleLowerCase()) + ), + })); const superagencies = availableAgencies.filter( (agency) => agency.is_superagency ); @@ -288,7 +304,8 @@ export const AgencyProvisioning: React.FC = observer( String(agencyProvisioningUpdates.agency_id), agencyProvisioningUpdates.name, userStore.email, - ["ALL"] + Array.from(selectedMetricsKeys), + Array.from(selectedChildAgencyIDsToCopy).map((id) => String(id)) ); } @@ -474,26 +491,38 @@ export const AgencyProvisioning: React.FC = observer( Object.keys(teamMemberRoleUpdates).length > 0 || selectedTeamMembersToAdd.size > 0 || selectedTeamMembersToDelete.size > 0; + /** + * An update has been made when there are child agencies or metrics selected to copy + */ + const hasChildAgenciesCopyUpdates = selectedChildAgencyIDsToCopy.size > 0; + const hasMetricsCopyUpdates = selectedMetricsKeys.size > 0; /** * Saving is disabled if saving is in progress OR an existing agency has made no updates to either the name, state, * county, systems, dashboard enabled checkbox, superagency checkbox and child agencies, child agency's superagency * selection, and team member additions/deletions/role updates, or a newly created agency has no input for both name and state. */ - const isSaveDisabled = - !isCopySuperagencyMetricSettingsSelected && // Allows user to save if all they do is select that they want to copy superagency metric settings - (isSaveInProgress || + const hasCopySuperagencyMetricSettingsUpdates = + hasChildAgenciesCopyUpdates && hasMetricsCopyUpdates; + const hasAgencyInfoUpdates = + hasNameUpdate || + hasStateUpdate || + hasCountyUpdates || + hasSystemUpdates || + hasDashboardEnabledStatusUpdate || + hasIsSuperagencyUpdate || + hasChildAgencyUpdates || + hasSuperagencyUpdate || + hasTeamMemberOrRoleUpdates; + const hasRequiredCreateAgencyFields = + hasNameUpdate && hasStateUpdate && hasSystems; + + const isSaveDisabled = isCopySuperagencyMetricSettingsSelected + ? !hasCopySuperagencyMetricSettingsUpdates + : isSaveInProgress || !hasSystems || (selectedAgency - ? !hasNameUpdate && - !hasStateUpdate && - !hasCountyUpdates && - !hasSystemUpdates && - !hasDashboardEnabledStatusUpdate && - !hasIsSuperagencyUpdate && - !hasChildAgencyUpdates && - !hasSuperagencyUpdate && - !hasTeamMemberOrRoleUpdates - : !(hasNameUpdate && hasStateUpdate && hasSystems))); + ? !hasAgencyInfoUpdates + : !hasRequiredCreateAgencyFields); /** Automatically adds CSG and Recidiviz users to a newly created agency with the proper roles */ useEffect(() => { @@ -520,12 +549,19 @@ export const AgencyProvisioning: React.FC = observer( }; }); } + + if (isCopySuperagencyMetricSettingsSelected && selectedIDToEdit) { + adminPanelStore.fetchAgencyMetrics(String(selectedIDToEdit)); + } }, [ selectedAgency, adminPanelStore, api, csgAndRecidivizUsers, csgAndRecidivizDefaultRole, + secondaryCreatedId, + selectedIDToEdit, + isCopySuperagencyMetricSettingsSelected, ]); /** Here we are making the auto-adding if user was created via the secondary modal */ @@ -546,6 +582,18 @@ export const AgencyProvisioning: React.FC = observer( } }, [users, secondaryCreatedId, csgAndRecidivizDefaultRole]); + /** Here we are making the auto-selecting all metrics by default */ + useEffect(() => { + setSelectedMetricsKeys( + new Set(searchableMetrics.map((metric) => String(metric.id))) + ); + }, [searchableMetrics]); + + const selectedChildAgencies = childAgencies.filter((agency) => + selectedChildAgencyIDs.has(Number(agency.id)) + ); + const hasChildAgencyMetrics = metrics.length > 0; + return ( {showSaveConfirmation.show ? ( @@ -909,37 +957,178 @@ export const AgencyProvisioning: React.FC = observer( {hasSystems && hasChildAgencies && ( - - - setIsCopySuperagencyMetricSettingsSelected( - (prev) => !prev - ) - } - checked={isCopySuperagencyMetricSettingsSelected} - /> - - {isCopySuperagencyMetricSettingsSelected && ( - - -

- WARNING! This action cannot be undone. This - will OVERWRITE all metric settings in all - child agencies. After clicking{" "} - Save, the copying process - will begin and you will receive an email - confirmation once the metrics settings have - been copied over. -

-
- )} -
+ <> + + { + setIsCopySuperagencyMetricSettingsSelected( + (prev) => !prev + ); + }} + checked={ + isCopySuperagencyMetricSettingsSelected + } + /> + + + {isCopySuperagencyMetricSettingsSelected && + hasChildAgencyMetrics && ( + <> + + +

+ WARNING! This action cannot be undone. + This will OVERWRITE metric settings in + child agencies. After clicking{" "} + Save, the copying process + will begin and you will receive an email + confirmation once the metrics settings + have been copied over. +

+
+ + {showSelectionBox === + SelectionInputBoxTypes.COPY_CHILD_AGENCIES && ( + +agency.id + ) + ) + )} + updateSelections={({ id }) => { + setSelectedChildAgencyIDsToCopy( + (prev) => + toggleAddRemoveSetItem(prev, +id) + ); + }} + searchByKeys={["name", "sectors"]} + metadata={{ + listBoxLabel: + "Select child agencies to copy", + searchBoxLabel: "Search agencies", + }} + isActiveBox={ + showSelectionBox === + SelectionInputBoxTypes.COPY_CHILD_AGENCIES + } + /> + )} + { + setShowSelectionBox( + SelectionInputBoxTypes.COPY_CHILD_AGENCIES + ); + }} + fitContentHeight + hoverable + > + {selectedChildAgencyIDsToCopy.size === + 0 ? ( + + No child agencies selected to copy + + ) : ( + Array.from( + selectedChildAgencyIDsToCopy + ).map((agencyID) => ( + + {agenciesByID[agencyID]?.[0].name} + + )) + )} + + + Child agencies to copy + + + + + {showSelectionBox === + SelectionInputBoxTypes.COPY_AGENCY_METRICS && ( + + String(metric.id) + ) + ) + )} + updateSelections={({ id }) => { + setSelectedMetricsKeys((prev) => + toggleAddRemoveSetItem( + prev, + String(id) + ) + ); + }} + searchByKeys={["name", "sectors"]} + metadata={{ + listBoxLabel: + "Select metrics to copy", + searchBoxLabel: "Search metrics", + }} + isActiveBox={ + showSelectionBox === + SelectionInputBoxTypes.COPY_AGENCY_METRICS + } + /> + )} + { + setShowSelectionBox( + SelectionInputBoxTypes.COPY_AGENCY_METRICS + ); + }} + fitContentHeight + hoverable + > + {selectedMetricsKeys.size === 0 ? ( + + No metrics selected to copy + + ) : ( + Array.from(selectedMetricsKeys).map( + (metricKey) => ( + + { + searchableMetrics.find( + (metric) => + metric.id === metricKey + )?.name + } + + ) + ) + )} + + + Metrics to copy + + + + )} + )} )} diff --git a/publisher/src/components/AdminPanel/types.ts b/publisher/src/components/AdminPanel/types.ts index 892209a33..5dc9f0fbd 100644 --- a/publisher/src/components/AdminPanel/types.ts +++ b/publisher/src/components/AdminPanel/types.ts @@ -58,6 +58,8 @@ export enum SelectionInputBoxTypes { SYSTEMS = "SYSTEMS", SUPERAGENCY = "SUPERAGENCY", CHILD_AGENCIES = "CHILD AGENCIES", + COPY_CHILD_AGENCIES = "COPY CHILD AGENCIES", + COPY_AGENCY_METRICS = "COPY AGENCY METRICS", } export type SelectionInputBoxType = `${SelectionInputBoxTypes}`; @@ -86,6 +88,12 @@ export type Agency = { }[]; }; +export type AgencyMetric = { + key: string; + name: string; + sector: string; +}; + export type AgencyWithTeamByID = Omit & { team: Record; }; @@ -95,6 +103,11 @@ export type AgencyResponse = { systems: AgencySystem[]; }; +export type AgencyMetricResponse = { + agency: Agency; + metrics: AgencyMetric[]; +}; + export const AgencyProvisioningSettings = { AGENCY_INFORMATION: "Agency Information", TEAM_MEMBERS_ROLES: "Team Members & Roles", @@ -179,6 +192,7 @@ export type InteractiveSearchListUpdateSelections = ( export type SearchableListItem = { id: string | number; name: string; + sectors?: string | string[]; action?: InteractiveSearchListAction; email?: string; role?: AgencyTeamMemberRole; diff --git a/publisher/src/components/assets/close-icon.svg b/publisher/src/components/assets/close-icon.svg new file mode 100644 index 000000000..01deb4d57 --- /dev/null +++ b/publisher/src/components/assets/close-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/publisher/src/stores/AdminPanelStore.ts b/publisher/src/stores/AdminPanelStore.ts index 5e10d7620..28245aed4 100644 --- a/publisher/src/stores/AdminPanelStore.ts +++ b/publisher/src/stores/AdminPanelStore.ts @@ -17,6 +17,7 @@ import { AgencySystem, + AgencySystems, AgencyTeamMember, AgencyTeamMemberRole, } from "@justice-counts/common/types"; @@ -25,6 +26,8 @@ import { makeAutoObservable, runInAction } from "mobx"; import { Agency, + AgencyMetric, + AgencyMetricResponse, AgencyProvisioningUpdates, AgencyResponse, AgencyTeamUpdates, @@ -76,6 +79,8 @@ class AdminPanelStore { systems: AgencySystem[]; + metrics: AgencyMetric[]; + userProvisioningUpdates: UserProvisioningUpdates; agencyProvisioningUpdates: AgencyProvisioningUpdates; @@ -91,6 +96,7 @@ class AdminPanelStore { this.usersByID = {}; this.agenciesByID = {}; this.systems = []; + this.metrics = []; this.userProvisioningUpdates = initialEmptyUserProvisioningUpdates; this.agencyProvisioningUpdates = initialEmptyAgencyProvisioningUpdates; } @@ -136,6 +142,17 @@ class AdminPanelStore { })); } + get searchableMetrics(): SearchableListItem[] { + return this.metrics + .filter((metric) => metric.sector !== AgencySystems.SUPERAGENCY) + .map((metric) => ({ + ...metric, + id: metric.key, + sectors: metric.sector, + name: `${metric.name}: ${metric.sector.toLocaleLowerCase()}`, + })); + } + /** Returns a list of searchable counties based on the currently selected `state_code` in `agencyProvisioningUpdates` */ get searchableCounties(): SearchableListItem[] { if (!this.agencyProvisioningUpdates.state_code) return []; @@ -224,6 +241,26 @@ class AdminPanelStore { } } + async fetchAgencyMetrics(agencyID: string) { + try { + const response = (await this.api.request({ + path: `/admin/agency/${agencyID}`, + method: "GET", + })) as Response; + const data = (await response.json()) as AgencyMetricResponse; + + if (response.status !== 200) { + throw new Error("There was an issue fetching agency metrics."); + } + + runInAction(() => { + this.metrics = data.metrics; + }); + } catch (error) { + if (error instanceof Error) return new Error(error.message); + } + } + async fetchUsersAndAgencies() { await Promise.all([this.fetchUsers(), this.fetchAgencies()]); runInAction(() => { @@ -235,7 +272,8 @@ class AdminPanelStore { superagencyID: string, agencyName: string, userEmail: string, - metricDefinitionKeySubset: string[] // A list of metric definition keys for future use to update a subset of metrics + metricDefinitionKeySubset: string[], // A list of metric definition keys for future use to update a subset of metrics + childAgencyIdSubset: string[] // A list of child agencies ids for future use to update a subset of metrics ) { try { const response = (await this.api.request({ @@ -245,6 +283,7 @@ class AdminPanelStore { agency_name: agencyName, user_email: userEmail, metric_definition_key_subset: metricDefinitionKeySubset, + child_agency_id_subset: childAgencyIdSubset, }, })) as Response; From cc30319510c96ea4cb3721246fdb830a18e6f30e Mon Sep 17 00:00:00 2001 From: Michelle Orden <82831800+morden35@users.noreply.github.com> Date: Tue, 14 May 2024 09:38:26 -0500 Subject: [PATCH 28/28] [Justice Counts] Replace curly with straight apostrophe in agency name (#1331) * replace curly with straight apostrophe * lint fix --------- Co-authored-by: Michelle Orden --- publisher/src/stores/AdminPanelStore.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/publisher/src/stores/AdminPanelStore.ts b/publisher/src/stores/AdminPanelStore.ts index 28245aed4..1337e9223 100644 --- a/publisher/src/stores/AdminPanelStore.ts +++ b/publisher/src/stores/AdminPanelStore.ts @@ -394,7 +394,10 @@ class AdminPanelStore { } saveAgencyName(name: string) { - this.agencyProvisioningUpdates.name = name.trim().replaceAll(/\s+/gi, " "); + this.agencyProvisioningUpdates.name = name + .trim() + .replaceAll(/\s+/gi, " ") + .replaceAll("’", "'"); } updateStateCode(stateCode: StateCodeKey | null) {