{"results":{"result":{"added-files":{"code-health":10.0,"old-code-health":0.0,"files":[{"file":"src/lib/apollo/client.test.ts","loc":55,"code-health":10.0},{"file":"src/lib/apollo/ssrClient.test.ts","loc":52,"code-health":10.0}]},"external-review-url":"https://github.com/CruGlobal/mpdx-react/pull/2127","old-code-health":8.17532752544741,"modified-files":{"code-health":7.906314231820814,"old-code-health":8.17532752544741,"files":[{"file":"src/components/Reports/StaffExpenseReport/StaffExpenseReport.test.tsx","loc":650,"old-loc":610,"code-health":7.806251233515971,"old-code-health":8.35469004682882},{"file":"src/components/Reports/StaffExpenseReport/StaffExpenseReport.tsx","loc":582,"old-loc":576,"code-health":7.561198864706369,"old-code-health":7.586230529203857},{"file":"src/lib/apollo/client.ts","loc":85,"old-loc":84,"code-health":10.0,"old-code-health":10.0},{"file":"src/lib/apollo/ssrClient.ts","loc":42,"old-loc":42,"code-health":10.0,"old-code-health":10.0}]},"removed-files":{"code-health":0.0,"old-code-health":0.0,"files":[]},"external-review-id":"2127","analysis-time":"2026-10-07T21:17:58Z","negative-impact-count":3,"suppressions":{"number-of-types":0,"number-of-files-touched":0,"findings":[]},"affected-hotspots":2,"commits":["4dc00d07dc61c9aef8b9fb1053f5310de10faff0","91c6d2129647d747c02f9c0697fee1d16db688ea","5c2422f31026b150436d3261bcd95a54530d2d72"],"is-negative-review":true,"negative-findings":{"number-of-types":3,"number-of-files-touched":2,"findings":[{"why-it-occurs":"This module has 528 lines of code (comments stripped away). This puts the module at risk of evolving into a Brain Class. Brain Classes are problematic since changes become more complex over time, harder to test, and challenging to refactor. Act now to prevent future maintenance issues.","name":"Lines of Code in a Single File","file":"src/components/Reports/StaffExpenseReport/StaffExpenseReport.test.tsx","refactoring-examples":null,"change-level":"warning","is-hotspot?":true,"what-changed":"The lines of code increases from 494 to 528, improve code health by reducing it to 500","how-to-fix":"Look for opportunities to modularize the design. This is done by identifying groups of functions that represent different responsibilities and/or operate on different data. Once you have identified the different responsibilities, then use refactorings like [EXTRACT CLASS](https://refactoring.com/catalog/extractClass.html).","change-type":"degraded"},{"method":"TestComponent:React.FC<TestComponentProps>","why-it-occurs":"Overly long functions make the code harder to read. The recommended maximum function length for the React language is 200 lines of code. Severity: Brain Method - Complex Method - Long Method.","name":"Large Method","file":"src/components/Reports/StaffExpenseReport/StaffExpenseReport.test.tsx","refactoring-examples":null,"change-level":"warning","is-hotspot?":true,"line":122,"what-changed":"TestComponent:React.FC<TestComponentProps> increases from 207 to 218 lines of code, threshold = 200","how-to-fix":"We recommend to be careful here -- just splitting long functions don't necessarily make the code easier to read. Instead, look for natural chunks inside the functions that expresses a specific task or concern. Often, such concerns are indicated by a Code Comment followed by an if-statement. Use the [EXTRACT FUNCTION](https://refactoring.com/catalog/extractFunction.html) refactoring to encapsulate that concern.","change-type":"degraded"},{"method":"StaffExpenseReport:React.FC<StaffExpenseReportProps>","why-it-occurs":"A Complex Method has a high cyclomatic complexity. The recommended threshold for the React language is a cyclomatic complexity lower than 20.","name":"Complex Method","file":"src/components/Reports/StaffExpenseReport/StaffExpenseReport.tsx","refactoring-examples":[{"architectural-component-id":null,"author-name":"Daniel Bisgrove","training-data":{"loc-added":"3","loc-deleted":"1","delta-cc-mean":"0.0","delta-cc-total":"0","delta-penalties":"5.382","delta-n-functions":"0","current-file-score":"10.0"},"author-email":"daniel@bizz-websites.com","commit-full-message":"","commit-date":"2024-10-17T14:59:21Z","current-rev":"b4989f844","filename":"mpdx-react/src/components/Layouts/Primary/TopBar/Items/ProfileMenu/ProfileMenu.tsx","previous-rev":"5894a893f","commit-title":"fixup! Fix routing issue after switching account","language":"React","id":"584c52e9e432999ea473cfb396e1aa9c502822cd","model-score":0.75,"author-id":null,"project-id":53436,"delta-file-score":1.555564,"diff":"diff --git a/src/components/Layouts/Primary/TopBar/Items/ProfileMenu/ProfileMenu.tsx b/src/components/Layouts/Primary/TopBar/Items/ProfileMenu/ProfileMenu.tsx\nindex a1e149d28..1f91bda3c 100644\n--- a/src/components/Layouts/Primary/TopBar/Items/ProfileMenu/ProfileMenu.tsx\n+++ b/src/components/Layouts/Primary/TopBar/Items/ProfileMenu/ProfileMenu.tsx\n@@ -197,3 +197,5 @@ const ProfileMenu = (): ReactElement => {\n       router.pathname ===\n-      '/accountLists/[accountListId]/reports/financialAccounts/[[...financialAccount]]'\n+        '/accountLists/[accountListId]/reports/financialAccounts/[financialAccountId]/entries' ||\n+      router.pathname ===\n+        '/accountLists/[accountListId]/reports/financialAccounts/[financialAccountId]'\n     ) {\n","improvement-type":"Complex Method"},{"architectural-component-id":null,"author-name":"Bizz (Daniel Bisgrove)","training-data":{"loc-added":"27","loc-deleted":"2","delta-cc-mean":"0.0","delta-cc-total":"0","delta-penalties":"6.048","delta-n-functions":"0","current-file-score":"9.842730062691357"},"author-email":"56281168+dr-bizz@users.noreply.github.com","commit-full-message":"* Add more warning messages when dragging contact to different columns.","commit-date":"2024-09-12T14:53:23Z","current-rev":"556abc41a","filename":"mpdx-react/src/components/Tool/Appeal/Modals/PledgeModal/PledgeModal.tsx","previous-rev":"3cde16b8e","commit-title":"MPDX-8187 + MPDX-8188 Add more warning messages when dragging contact to different columns. (#1056)","language":"React","id":"106116e788da85247f60c309cc55586685450747","model-score":0.7,"author-id":null,"project-id":53436,"delta-file-score":1.5710658,"diff":"diff --git a/src/components/Tool/Appeal/Modals/PledgeModal/PledgeModal.tsx b/src/components/Tool/Appeal/Modals/PledgeModal/PledgeModal.tsx\nindex a7217c8d6..3f655858e 100644\n--- a/src/components/Tool/Appeal/Modals/PledgeModal/PledgeModal.tsx\n+++ b/src/components/Tool/Appeal/Modals/PledgeModal/PledgeModal.tsx\n@@ -35,2 +35,3 @@ import i18n from 'src/lib/i18n';\n import {\n+  AppealStatusEnum,\n   AppealsContext,\n@@ -39,2 +40,3 @@ import {\n import { AppealContactInfoFragment } from '../../AppealsContext/contacts.generated';\n+import handleReceivedSnackBarNotifications from '../../Shared/handleReceivedSnackBarNotifications/handleReceivedSnackBarNotifications';\n import {\n@@ -51,2 +53,3 @@ interface PledgeModalProps {\n   pledge?: AppealContactInfoFragment['pledges'][0];\n+  selectedAppealStatus?: AppealStatusEnum | null;\n }\n@@ -79,2 +82,3 @@ export const PledgeModal: React.FC<PledgeModalProps> = ({\n   handleClose,\n+  selectedAppealStatus,\n }) => {\n@@ -116,3 +120,13 @@ export const PledgeModal: React.FC<PledgeModalProps> = ({\n         refetchQueries: ['Contacts', 'Appeal'],\n-        onCompleted: () => {\n+        onCompleted: ({ createAccountListPledge }) => {\n+          const newStatus = createAccountListPledge?.pledge.status;\n+          if (selectedAppealStatus) {\n+            handleReceivedSnackBarNotifications({\n+              dbStatus: newStatus,\n+              selectedAppealStatus,\n+              t,\n+              enqueueSnackbar,\n+            });\n+          }\n+\n           enqueueSnackbar(t('Successfully added commitment to appeal'), {\n@@ -145,3 +159,14 @@ export const PledgeModal: React.FC<PledgeModalProps> = ({\n         refetchQueries: ['Contacts', 'Appeal'],\n-        onCompleted: () => {\n+        onCompleted: ({ updateAccountListPledge }) => {\n+          const newStatus = updateAccountListPledge?.pledge.status;\n+\n+          if (selectedAppealStatus) {\n+            handleReceivedSnackBarNotifications({\n+              dbStatus: newStatus,\n+              selectedAppealStatus,\n+              t,\n+              enqueueSnackbar,\n+            });\n+          }\n+\n           enqueueSnackbar(t('Successfully edited commitment'), {\n","improvement-type":"Complex Method"},{"architectural-component-id":null,"author-name":"cru-self-healing-agent[bot]","training-data":{"loc-added":"6","loc-deleted":"0","delta-cc-mean":"0.0","delta-cc-total":"0","delta-penalties":"4.392","delta-n-functions":"0","current-file-score":"9.6882083290695"},"author-email":"318792136+cru-self-healing-agent[bot]@users.noreply.github.com","commit-full-message":"The Headers step's \"Next\" button call to save() had no .catch, so a\nfailed save (save() does console.error(err); throw err;) left the\nuser with no feedback beyond the button re-enabling itself, prompting\nrepeated clicks that Datadog RUM logs as error_click frustration\nevents on /accountLists/?/tools/import/csv.\n\nSame bug already fixed for the Preview step's \"Import\" button\n(a4173fa19) and the Values step's \"Next\" button (PR #2062) — this\ncovers the one remaining step in the same three-step save pattern.\n\nMonitor: MPDX React frustration rate by page [prod] (312883915)","commit-date":"2026-09-20T19:29:30Z","current-rev":"ee6d47a8f","filename":"mpdx-react/src/components/Tool/Import/Csv/CsvHeaders.tsx","previous-rev":"df619f621","commit-title":"Show an error toast when CSV import save fails on the Headers step","language":"React","id":"6ecc31873216cf6ab3fc24929d1032a5a1590ff6","model-score":0.64,"author-id":null,"project-id":53436,"delta-file-score":1.5042071,"diff":"diff --git a/src/components/Tool/Import/Csv/CsvHeaders.tsx b/src/components/Tool/Import/Csv/CsvHeaders.tsx\nindex 3596c6bfd..799304767 100644\n--- a/src/components/Tool/Import/Csv/CsvHeaders.tsx\n+++ b/src/components/Tool/Import/Csv/CsvHeaders.tsx\n@@ -19,2 +19,3 @@ import {\n import { cloneDeep } from 'lodash/fp';\n+import { useSnackbar } from 'notistack';\n import { useTranslation } from 'react-i18next';\n@@ -22,2 +23,3 @@ import { useApiConstants } from 'src/components/Constants/UseApiConstants';\n import { Confirmation } from 'src/components/Shared/Modal/Confirmation/Confirmation';\n+import { getErrorMessage } from 'src/lib/error';\n import { getAppName } from 'src/lib/getAppName';\n@@ -96,2 +98,3 @@ const CsvHeaders: React.FC<CsvHeadersProps> = ({\n   const { t } = useTranslation();\n+  const { enqueueSnackbar } = useSnackbar();\n   const appName = getAppName();\n@@ -209,2 +212,5 @@ const CsvHeaders: React.FC<CsvHeadersProps> = ({\n       })\n+      .catch((err) => {\n+        enqueueSnackbar(getErrorMessage(err), { variant: 'error' });\n+      })\n       .finally(() => {\n","improvement-type":"Complex Method"},{"architectural-component-id":null,"author-name":"Will James","training-data":{"loc-added":"13","loc-deleted":"22","delta-cc-mean":"0.0","delta-cc-total":"0","delta-penalties":"4.734","delta-n-functions":"0","current-file-score":"10.0"},"author-email":"william.james@cru.org","commit-full-message":"","commit-date":"2025-07-14T17:18:44Z","current-rev":"0bda3723a","filename":"mpdx-react/src/components/Settings/integrations/Organization/ConnectOrganization.tsx","previous-rev":"ae4398dda","commit-title":"Updates components to use OrganizationAutocomplete and new location of DonorAccountAutocomplete.","language":"React","id":"69d809f40ed2a58a216204dcb06d76f74bac23db","model-score":0.54,"author-id":null,"project-id":53436,"delta-file-score":1.3835225,"diff":"diff --git a/src/components/Settings/integrations/Organization/ConnectOrganization.tsx b/src/components/Settings/integrations/Organization/ConnectOrganization.tsx\nindex 2f47f930b..d5713c142 100644\n--- a/src/components/Settings/integrations/Organization/ConnectOrganization.tsx\n+++ b/src/components/Settings/integrations/Organization/ConnectOrganization.tsx\n@@ -9,3 +9,2 @@ import { useApolloClient } from '@apollo/client';\n import {\n-  Autocomplete,\n   Box,\n@@ -23,2 +22,4 @@ import { useTranslation } from 'react-i18next';\n import { FieldWrapper } from 'src/components/Shared/Forms/FieldWrapper';\n+import { OrganizationAutocomplete } from 'src/components/common/Autocomplete/OrganizationAutocomplete/OrganizationAutocomplete';\n+import { Organization } from 'src/graphql/types.generated';\n import useGetAppSettings from 'src/hooks/useGetAppSettings';\n@@ -175,32 +176,22 @@ export const ConnectOrganization: React.FC<ConnectOrganizationProps> = ({\n           <ContentContainer>\n-            <Autocomplete\n+            <OrganizationAutocomplete\n               disabled={isSubmitting}\n               autoHighlight\n+              fullWidth\n               loading={loading}\n               value={selectedOrganization}\n+              organizations={\n+                organizations?.organizations?.filter(\n+                  (organization) => !organization?.disableNewUsers,\n+                ) ?? []\n+              }\n               onChange={(_, value) => {\n+                const org = value as Organization;\n                 setOrganizationType(\n-                  getOrganizationType(value?.apiClass, value?.oauth),\n+                  getOrganizationType(org?.apiClass, org?.oauth),\n                 );\n-                setSelectedOrg(value?.name ?? '');\n+                setSelectedOrg(org?.name ?? '');\n                 setFieldValue('selectedOrganization', value);\n               }}\n-              options={\n-                organizations?.organizations?.filter(\n-                  (organization) => !organization?.disableNewUsers,\n-                ) || []\n-              }\n-              getOptionLabel={(option) =>\n-                organizations?.organizations?.find(\n-                  ({ id }) => String(id) === String(option.id),\n-                )?.name ?? ''\n-              }\n-              fullWidth\n-              renderInput={(params) => (\n-                <TextField\n-                  {...params}\n-                  label={t('Organization')}\n-                  inputRef={focusOnOrganization}\n-                />\n-              )}\n+              textFieldFocusRef={focusOnOrganization}\n             />\n","improvement-type":"Complex Method"},{"architectural-component-id":null,"author-name":"Daniel Bisgrove","training-data":{"loc-added":"36","loc-deleted":"11","delta-cc-mean":"0.0","delta-cc-total":"0","delta-penalties":"3.114","delta-n-functions":"0","current-file-score":"10.0"},"author-email":"daniel@bizz-websites.com","commit-full-message":"","commit-date":"2024-09-09T14:15:22Z","current-rev":"c4ef64282","filename":"mpdx-react/src/components/Tool/Appeal/Flow/ContactFlowColumn/ContactFlowColumn.tsx","previous-rev":"d2f225d28","commit-title":"Adding select all and deselect all onto each column","language":"React","id":"71c7067d34e931c166b2b014f7cb1e52c43ff71e","model-score":0.48,"author-id":null,"project-id":53436,"delta-file-score":0.93582284,"diff":"diff --git a/src/components/Tool/Appeal/Flow/ContactFlowColumn/ContactFlowColumn.tsx b/src/components/Tool/Appeal/Flow/ContactFlowColumn/ContactFlowColumn.tsx\nindex 3c24bc0f8..14c0029d0 100644\n--- a/src/components/Tool/Appeal/Flow/ContactFlowColumn/ContactFlowColumn.tsx\n+++ b/src/components/Tool/Appeal/Flow/ContactFlowColumn/ContactFlowColumn.tsx\n@@ -1,2 +1,2 @@\n-import React, { useRef, useState } from 'react';\n+import React, { useMemo, useRef, useState } from 'react';\n import MoreVertIcon from '@mui/icons-material/MoreVert';\n@@ -30,2 +30,3 @@ import {\n } from 'src/components/Tool/Appeal/AppealsContext/AppealsContext';\n+import { useGetIdsForMassSelectionQuery } from 'src/hooks/GetIdsForMassSelection.generated';\n import { appealHeaderInfoHeight } from '../../AppealDetails/AppealHeaderInfo/AppealHeaderInfo';\n@@ -70,5 +71,9 @@ export const ContactFlowColumn: React.FC<Props> = ({\n }) => {\n-  const { appealId, sanitizedFilters, starredFilter } = React.useContext(\n-    AppealsContext,\n-  ) as AppealsType;\n+  const {\n+    appealId,\n+    sanitizedFilters,\n+    starredFilter,\n+    selectMultipleIds,\n+    deselectMultipleIds,\n+  } = React.useContext(AppealsContext) as AppealsType;\n   const { t } = useTranslation();\n@@ -79,2 +84,13 @@ export const ContactFlowColumn: React.FC<Props> = ({\n \n+  const contactsFilters = useMemo(\n+    () => ({\n+      ...sanitizedFilters,\n+      ...starredFilter,\n+      appeal: [appealId ?? ''],\n+      appealStatus,\n+      wildcardSearch: searchTerm as string,\n+    }),\n+    [sanitizedFilters, starredFilter, searchTerm, appealId],\n+  );\n+\n   const { data, loading, fetchMore } = useContactsQuery({\n@@ -82,9 +98,3 @@ export const ContactFlowColumn: React.FC<Props> = ({\n       accountListId: accountListId ?? '',\n-      contactsFilters: {\n-        ...sanitizedFilters,\n-        ...starredFilter,\n-        appeal: [appealId ?? ''],\n-        appealStatus,\n-        wildcardSearch: searchTerm as string,\n-      },\n+      contactsFilters,\n     },\n@@ -93,2 +103,17 @@ export const ContactFlowColumn: React.FC<Props> = ({\n \n+  const contactCount = data?.contacts.totalCount ?? 0;\n+  const { data: allContacts } = useGetIdsForMassSelectionQuery({\n+    variables: {\n+      accountListId,\n+      first: contactCount,\n+      contactsFilters,\n+    },\n+    skip: contactCount === 0,\n+  });\n+\n+  const allContactIds = useMemo(\n+    () => allContacts?.contacts.nodes.map((contact) => contact.id) ?? [],\n+    [allContacts],\n+  );\n+\n   const { data: excludedContacts } = useExcludedAppealContactsQuery({\n@@ -118,4 +143,4 @@ export const ContactFlowColumn: React.FC<Props> = ({\n   const handleSelectAll = () => {\n+    selectMultipleIds(allContactIds);\n     setAnchorEl(null);\n-    // TODO implement select all\n   };\n@@ -123,4 +148,4 @@ export const ContactFlowColumn: React.FC<Props> = ({\n   const handleDeselectAll = () => {\n+    deselectMultipleIds(allContactIds);\n     setAnchorEl(null);\n-    // TODO implement deselect all\n   };\n","improvement-type":"Complex Method"}],"change-level":"warning","is-hotspot?":true,"line":89,"what-changed":"StaffExpenseReport:React.FC<StaffExpenseReportProps> increases in cyclomatic complexity from 75 to 77, threshold = 20","how-to-fix":"There are many reasons for Complex Method. Sometimes, another design approach is beneficial such as a) modeling state using an explicit state machine rather than conditionals, or b) using table lookup rather than long chains of logic. In other scenarios, the function can be split using [EXTRACT FUNCTION](https://refactoring.com/catalog/extractFunction.html). Just make sure you extract natural and cohesive functions. Complex Methods can also be addressed by identifying complex conditional expressions and then using the [DECOMPOSE CONDITIONAL](https://refactoring.com/catalog/decomposeConditional.html) refactoring.","change-type":"degraded"}]},"positive-impact-count":0,"repo":"mpdx-react","code-health":8.059127585978503,"version":"3.0","authors":["Daniel Frett"],"directives":{"added":[],"removed":[]},"positive-findings":{"number-of-types":0,"number-of-files-touched":0,"findings":[]},"notices":{"number-of-types":0,"number-of-files-touched":0,"findings":[]},"external-review-provider":"GitHub"},"analysistime":"2026-10-07T21:17:57.000Z","project-name":"mpdx-react","repository":"https://github.com/CruGlobal/mpdx-react.git"}}