diff --git a/Mobile-Expensify b/Mobile-Expensify index 42ad9da8bfde..fdf508981aff 160000 --- a/Mobile-Expensify +++ b/Mobile-Expensify @@ -1 +1 @@ -Subproject commit 42ad9da8bfde582b57b2e66dc30e22757e06deea +Subproject commit fdf508981aff5189c5c17c67493a3d1c4535c1a6 diff --git a/android/app/build.gradle b/android/app/build.gradle index c7a7c6045cb9..a5b9d35d6bf0 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -111,8 +111,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1009040500 - versionName "9.4.5-0" + versionCode 1009040600 + versionName "9.4.6-0" // Supported language variants must be declared here to avoid from being removed during the compilation. // This also helps us to not include unnecessary language variants in the APK. resConfigs "en", "es" diff --git a/assets/images/receipt-search.svg b/assets/images/receipt-search.svg new file mode 100644 index 000000000000..bbf36cf9b1df --- /dev/null +++ b/assets/images/receipt-search.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/config/eslint/eslint.config.mjs b/config/eslint/eslint.config.mjs index f41e62edc95c..86e05b589d15 100644 --- a/config/eslint/eslint.config.mjs +++ b/config/eslint/eslint.config.mjs @@ -157,11 +157,6 @@ const restrictedImportPaths = [ ]; const restrictedImportPatterns = [ - { - group: ['**/TransitionTracker', './TransitionTracker', '../TransitionTracker'], - message: - "TransitionTracker is an internal primitive. Please use higher-level APIs (Navigation with 'afterTransition'/'waitForTransition', KeyboardUtils, useConfirmModal). See contributingGuides/INTERACTION_MANAGER.md.", - }, { group: ['**/assets/animations/**/*.json'], message: "Do not import animations directly. Please use the '@components/LottieAnimations' import instead.", @@ -188,6 +183,26 @@ const restrictedReportNameImportPatterns = [ }, ]; +// `isPaidGroupPolicy` is BILLING/paid-only (Collect/Control). Existing usages are grandfathered via +// eslint-seatbelt; this only flags NEW imports so they make a conscious choice: for workspace feature +// gating (violations, report fields, workspace chat, report creation, expense-workspace usability) use +// `isGroupPolicy` / `isReportInGroupPolicy` instead, otherwise free group plans like Submit (submit2026) +// are wrongly excluded and access bugs return. +const restrictedPaidGroupPolicyImportPatterns = [ + { + group: ['**/PolicyUtils', '**/libs/PolicyUtils'], + importNames: ['isPaidGroupPolicy'], + message: + 'isPaidGroupPolicy is billing/paid-only (Collect/Control). For workspace feature gating use isGroupPolicy so free group plans like Submit are not excluded. If this is genuinely a billing/paid-only check, keep it and disable this line with a reason.', + }, + { + group: ['**/ReportUtils', '**/libs/ReportUtils'], + importNames: ['isPaidGroupPolicy', 'isPaidGroupPolicyExpenseReport'], + message: + 'isPaidGroupPolicy / isPaidGroupPolicyExpenseReport are billing/paid-only. For feature gating use isReportInGroupPolicy / isGroupPolicyExpenseReport so Submit workspaces are not excluded. If this is genuinely billing/paid-only, keep it and disable this line with a reason.', + }, +]; + const config = defineConfig([ expensifyConfig, typescriptEslint.configs.recommendedTypeChecked, @@ -255,8 +270,6 @@ const config = defineConfig([ files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx', '**/*.mjs', '**/*.cjs'], rules: { - '@lwc/lwc/no-async-await': 'off', - // TypeScript specific rules '@typescript-eslint/prefer-enum-initializers': 'error', '@typescript-eslint/no-var-requires': 'off', @@ -336,7 +349,6 @@ const config = defineConfig([ 'es/no-optional-chaining': 'off', '@typescript-eslint/no-deprecated': ['error', {allow: ['translateFn']}], 'arrow-body-style': 'off', - 'no-continue': 'off', 'no-empty': ['error', {allowEmptyCatch: true}], // Import specific rules @@ -715,7 +727,7 @@ const config = defineConfig([ 'error', { paths: restrictedImportPaths, - patterns: [...restrictedImportPatterns, ...restrictedReportNameImportPatterns], + patterns: [...restrictedImportPatterns, ...restrictedReportNameImportPatterns, ...restrictedPaidGroupPolicyImportPatterns], }, ], }, diff --git a/config/eslint/eslint.seatbelt.tsv b/config/eslint/eslint.seatbelt.tsv index 57557c64e0ce..7efae31cde1c 100644 --- a/config/eslint/eslint.seatbelt.tsv +++ b/config/eslint/eslint.seatbelt.tsv @@ -68,7 +68,7 @@ "../../src/Expensify.tsx" "react-hooks/set-state-in-effect" 1 "../../src/GlobalModals.tsx" "no-restricted-syntax" 2 "../../src/ONYXKEYS.ts" "no-restricted-syntax" 2 -"../../src/ROUTES.ts" "@typescript-eslint/no-deprecated/getUrlWithBackToParam" 132 +"../../src/ROUTES.ts" "@typescript-eslint/no-deprecated/getUrlWithBackToParam" 139 "../../src/ROUTES.ts" "@typescript-eslint/no-unsafe-type-assertion" 3 "../../src/TIMEZONES.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/components/AccountingConnectionConfirmationModal.tsx" "@typescript-eslint/no-deprecated/ConfirmModal" 1 @@ -109,6 +109,7 @@ "../../src/components/AvatarSelector.tsx" "no-restricted-syntax" 2 "../../src/components/AvatarWithImagePicker.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/components/AvatarWithImagePicker.tsx" "react-hooks/set-state-in-effect" 2 +"../../src/components/BookTravelButton.tsx" "no-restricted-imports" 1 "../../src/components/Button/index.tsx" "@typescript-eslint/no-unsafe-type-assertion" 2 "../../src/components/Button/validateSubmitShortcut/index.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/components/ButtonComposed/Button.tsx" "@typescript-eslint/no-unsafe-type-assertion" 2 @@ -249,7 +250,7 @@ "../../src/components/MapView/MapViewImpl.web.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/components/MapView/MapViewImpl.web.tsx" "react-hooks/set-state-in-effect" 1 "../../src/components/MapView/ToggleDistanceUnitButton/index.android.tsx" "no-restricted-syntax" 1 -"../../src/components/MenuItem.tsx" "@typescript-eslint/no-unsafe-type-assertion" 4 +"../../src/components/MenuItem.tsx" "@typescript-eslint/no-unsafe-type-assertion" 3 "../../src/components/Modal/BaseModal.tsx" "react-hooks/set-state-in-effect" 1 "../../src/components/Modal/Global/ConfirmModalWrapper.tsx" "@typescript-eslint/no-deprecated/ConfirmModal" 1 "../../src/components/Modal/Global/ModalContext.tsx" "@typescript-eslint/no-unsafe-type-assertion" 2 @@ -276,10 +277,9 @@ "../../src/components/MoneyRequestConfirmationListFooter/sections/DistanceMapSection.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/components/MoneyRequestConfirmationListFooter/sections/ReceiptSection.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 -"../../src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx" "no-restricted-imports" 1 "../../src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx" "react-hooks/refs" 3 "../../src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx" "react-hooks/set-state-in-effect" 3 -"../../src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx" "@typescript-eslint/no-unsafe-type-assertion" 3 +"../../src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx" "@typescript-eslint/no-unsafe-type-assertion" 2 "../../src/components/MoneyRequestReportView/MoneyRequestReportTransactionsNavigation.tsx" "@typescript-eslint/no-unsafe-type-assertion" 2 "../../src/components/MoneyRequestReportView/MoneyRequestViewReportFields.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/components/MultiGestureCanvas/index.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 @@ -296,8 +296,8 @@ "../../src/components/MultifactorAuthentication/config/scenarios/ChangePIN.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/components/MultifactorAuthentication/config/scenarios/DefaultUserInterface.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/components/Navigation/NavigationTabBar/SearchTabButton.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 -"../../src/components/Navigation/NavigationTabBar/index.tsx" "@typescript-eslint/no-unsafe-type-assertion" 3 "../../src/components/Navigation/QuickCreationActionsBar/index.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 +"../../src/components/Navigation/QuickCreationActionsBar/index.tsx" "no-restricted-imports" 1 "../../src/components/Navigation/TopLevelNavigationTabBar/index.tsx" "react-hooks/set-state-in-effect" 1 "../../src/components/NavigationDeferredMount.tsx" "no-restricted-imports" 1 "../../src/components/NumberWithSymbolForm.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 @@ -377,6 +377,8 @@ "../../src/components/Search/SearchList/ListItem/ChatListItem.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/components/Search/SearchList/ListItem/ExpenseReportListItem.tsx" "@typescript-eslint/no-unsafe-type-assertion" 5 "../../src/components/Search/SearchList/ListItem/ExpenseReportListItemRow/ExpenseReportListItemRowWide.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 +"../../src/components/Search/SearchList/ListItem/GroupChildrenContent.tsx" "@typescript-eslint/no-unsafe-type-assertion" 4 +"../../src/components/Search/SearchList/ListItem/GroupHeader.tsx" "@typescript-eslint/no-unsafe-type-assertion" 3 "../../src/components/Search/SearchList/ListItem/MemberListItemHeader.tsx" "@typescript-eslint/no-unsafe-type-assertion" 2 "../../src/components/Search/SearchList/ListItem/ReportListItemHeader.tsx" "@typescript-eslint/no-unsafe-type-assertion" 4 "../../src/components/Search/SearchList/ListItem/TaskListItem.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 @@ -408,7 +410,6 @@ "../../src/components/Search/hooks/useOptimisticSearchTracking.ts" "@typescript-eslint/no-unsafe-type-assertion" 4 "../../src/components/Search/hooks/useStableOptimisticSortedData.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/components/Search/index.tsx" "@typescript-eslint/no-unsafe-type-assertion" 14 -"../../src/components/Search/index.tsx" "no-restricted-imports" 1 "../../src/components/Search/index.tsx" "react-hooks/preserve-manual-memoization" 2 "../../src/components/Search/index.tsx" "react-hooks/refs" 5 "../../src/components/Search/index.tsx" "react-hooks/set-state-in-effect" 1 @@ -423,6 +424,7 @@ "../../src/components/SelectionList/utils/getListboxRole/index.web.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/components/SettlementButton/AnimatedSettlementButton.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/components/SettlementButton/index.tsx" "@typescript-eslint/no-unsafe-type-assertion" 11 +"../../src/components/SettlementButton/index.tsx" "no-restricted-imports" 1 "../../src/components/SidePanel/SidePanelOverlay.tsx" "no-restricted-syntax" 1 "../../src/components/SidePanel/SidePanelReport/index.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/components/SignInButtons/IconButton.tsx" "no-restricted-syntax" 1 @@ -446,11 +448,10 @@ "../../src/components/Table/TableContext.tsx" "@typescript-eslint/no-unsafe-type-assertion" 3 "../../src/components/Table/TableFilterButtons/buildFilterItems.tsx" "@typescript-eslint/no-unsafe-type-assertion" 2 "../../src/components/Table/middlewares/filtering.ts" "@typescript-eslint/no-unsafe-type-assertion" 3 -"../../src/components/Tables/WorkspaceCompanyCardsTable/WorkspaceCompanyCardsTableRow.tsx" "@typescript-eslint/no-unsafe-type-assertion" 3 +"../../src/components/Tables/WorkspaceCompanyCardsTable/WorkspaceCompanyCardsTableRow.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/components/TagPicker/index.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/components/TaxPicker.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/components/TestDrive/Modal/AdminTestDriveModal.tsx" "no-restricted-imports" 1 -"../../src/components/TestDrive/Modal/EmployeeTestDriveModal.tsx" "@typescript-eslint/no-deprecated/InteractionManager.runAfterInteractions" 1 "../../src/components/TestDrive/TestDriveDemo.tsx" "no-restricted-imports" 1 "../../src/components/TextInput/BaseTextInput/implementation/index.native.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/components/TextInput/BaseTextInput/implementation/index.native.tsx" "react-hooks/refs" 2 @@ -503,6 +504,7 @@ "../../src/hooks/useAdvancedSearchFilters.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/hooks/useAnimatedHighlightStyle/index.ts" "react-hooks/set-state-in-effect" 2 "../../src/hooks/useAssignCard.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 +"../../src/hooks/useAutoCreateTrackWorkspace.ts" "no-restricted-imports" 1 "../../src/hooks/useAutoFocusInput.ts" "@typescript-eslint/no-deprecated/InteractionManager.runAfterInteractions" 1 "../../src/hooks/useAutoUpdateTimezone.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/hooks/useAutocompleteSuggestions.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 @@ -514,7 +516,9 @@ "../../src/hooks/useCachedImageSource.ts" "react-hooks/set-state-in-effect" 1 "../../src/hooks/useCancellationType.ts" "react-hooks/refs" 2 "../../src/hooks/useCancellationType.ts" "react-hooks/set-state-in-effect" 1 +"../../src/hooks/useCardFeedsForActivePolicies.ts" "no-restricted-imports" 1 "../../src/hooks/useCardFeedsForDisplay.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 +"../../src/hooks/useCardFeedsForDisplay.ts" "no-restricted-imports" 1 "../../src/hooks/useCreateReport.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/hooks/useDebounce.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/hooks/useDebounceNonReactive.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 @@ -526,18 +530,18 @@ "../../src/hooks/useDomainGroupFilter.ts" "react-hooks/set-state-in-effect" 1 "../../src/hooks/useDragAndDrop/types.ts" "@typescript-eslint/no-deprecated/React.MutableRefObject" 1 "../../src/hooks/useDynamicBackPath.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 -"../../src/hooks/useExpenseActions.ts" "@typescript-eslint/no-deprecated/InteractionManager.runAfterInteractions" 3 +"../../src/hooks/useExpenseActions.ts" "@typescript-eslint/no-deprecated/InteractionManager.runAfterInteractions" 1 "../../src/hooks/useExportActions.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/hooks/useExportedToFilterOptions.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/hooks/useFilesValidation.tsx" "@typescript-eslint/no-deprecated/ConfirmModal" 1 "../../src/hooks/useFilterFormValues.tsx" "@typescript-eslint/no-unsafe-type-assertion" 3 "../../src/hooks/useGeographicalStateAndCountryFromRoute.ts" "@typescript-eslint/no-unsafe-type-assertion" 3 -"../../src/hooks/useHRSyncResultsModal.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/hooks/useHtmlPaste/index.ts" "@typescript-eslint/no-unsafe-type-assertion" 2 "../../src/hooks/useImportSpreadsheetConfirmModal.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/hooks/useInitial.ts" "react-hooks/refs" 4 "../../src/hooks/useIsBlockedToAddFeed.ts" "react-hooks/set-state-in-effect" 1 "../../src/hooks/useIsOwnWorkspaceChatRef.ts" "react-hooks/refs" 2 +"../../src/hooks/useIsPaidPolicyAdmin.ts" "no-restricted-imports" 1 "../../src/hooks/useKeyboardShortcut.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/hooks/useLazyAsset.ts" "@typescript-eslint/no-unsafe-type-assertion" 5 "../../src/hooks/useLazyAsset.ts" "react-hooks/refs" 2 @@ -647,6 +651,7 @@ "../../src/libs/Middleware/Pagination.ts" "@typescript-eslint/no-unsafe-type-assertion" 5 "../../src/libs/Middleware/SaveResponseInOnyx.ts" "no-restricted-syntax" 1 "../../src/libs/ModifiedExpenseMessage.ts" "@typescript-eslint/no-unsafe-type-assertion" 6 +"../../src/libs/MoneyRequestReportUtils.ts" "no-restricted-imports" 1 "../../src/libs/Navigation/AppNavigator/AuthScreens.tsx" "no-restricted-syntax" 1 "../../src/libs/Navigation/AppNavigator/AuthScreensInitHandler.tsx" "no-restricted-syntax" 6 "../../src/libs/Navigation/AppNavigator/AuthScreensInitHandler.tsx" "react-hooks/refs" 1 @@ -657,6 +662,7 @@ "../../src/libs/Navigation/AppNavigator/KeyboardShortcutsHandler/ShortcutsOverviewHandler.tsx" "no-restricted-syntax" 1 "../../src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx" "@typescript-eslint/no-unsafe-type-assertion" 5 "../../src/libs/Navigation/AppNavigator/Navigators/Overlay/BaseOverlay.tsx" "no-restricted-syntax" 2 +"../../src/libs/Navigation/AppNavigator/Navigators/ReportsSplitNavigator.tsx" "no-restricted-imports" 1 "../../src/libs/Navigation/AppNavigator/Navigators/ReportsSplitNavigator.tsx" "no-restricted-syntax" 1 "../../src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.tsx" "no-restricted-imports" 1 @@ -665,9 +671,8 @@ "../../src/libs/Navigation/AppNavigator/Navigators/SettingsSplitNavigator.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/libs/Navigation/AppNavigator/Navigators/TabNavigator.native.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/libs/Navigation/AppNavigator/Navigators/TestToolsModalNavigator.tsx" "no-restricted-syntax" 1 -"../../src/libs/Navigation/AppNavigator/Navigators/WorkspaceNavigator.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 -"../../src/libs/Navigation/AppNavigator/Navigators/WorkspaceSplitNavigator.tsx" "@typescript-eslint/no-unsafe-type-assertion" 2 -"../../src/libs/Navigation/AppNavigator/createRootStackNavigator/GetStateForActionHandlers.ts" "@typescript-eslint/no-unsafe-type-assertion" 17 +"../../src/libs/Navigation/AppNavigator/Navigators/WorkspaceSplitNavigator.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 +"../../src/libs/Navigation/AppNavigator/createRootStackNavigator/GetStateForActionHandlers.ts" "@typescript-eslint/no-unsafe-type-assertion" 15 "../../src/libs/Navigation/AppNavigator/createRootStackNavigator/index.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/libs/Navigation/AppNavigator/createRootStackNavigator/useCustomRootStackNavigatorState/restoreTabNavigatorRoutes.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/libs/Navigation/AppNavigator/createSplitNavigator/SplitRouter.ts" "@typescript-eslint/no-unsafe-type-assertion" 4 @@ -688,7 +693,6 @@ "../../src/libs/Navigation/guards/OnboardingGuard.ts" "@typescript-eslint/no-unsafe-type-assertion" 2 "../../src/libs/Navigation/helpers/createNormalizedConfigs.ts" "@typescript-eslint/no-deprecated/escape" 1 "../../src/libs/Navigation/helpers/createNormalizedConfigs.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 -"../../src/libs/Navigation/helpers/dismissModalAndOpenReportInInboxTab.ts" "@typescript-eslint/no-deprecated/InteractionManager.runAfterInteractions" 1 "../../src/libs/Navigation/helpers/dynamicRoutesUtils/createDynamicRoute.ts" "@typescript-eslint/no-unsafe-type-assertion" 2 "../../src/libs/Navigation/helpers/dynamicRoutesUtils/getDynamicRouteAdaptedState.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/libs/Navigation/helpers/dynamicRoutesUtils/getDynamicRouteQueryParams.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 @@ -725,6 +729,7 @@ "../../src/libs/NextStepUtils.ts" "@typescript-eslint/no-deprecated/buildNextStepNew" 2 "../../src/libs/Notification/LocalNotification/BrowserNotifications.ts" "@typescript-eslint/no-deprecated/translateLocal" 1 "../../src/libs/Notification/LocalNotification/BrowserNotifications.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 +"../../src/libs/Notification/LocalNotification/BrowserNotifications.ts" "no-restricted-imports" 1 "../../src/libs/Notification/LocalNotification/BrowserNotifications.ts" "no-restricted-syntax" 2 "../../src/libs/Notification/PushNotification/parsePushNotificationPayload.ts" "@typescript-eslint/no-unsafe-type-assertion" 5 "../../src/libs/Notification/PushNotification/shouldShowPushNotification.ts" "no-restricted-syntax" 2 @@ -758,10 +763,11 @@ "../../src/libs/ReportActionItemEventHandler/index.android.ts" "@typescript-eslint/no-deprecated/InteractionManager.runAfterInteractions" 1 "../../src/libs/ReportActionsUtils.ts" "@typescript-eslint/no-deprecated/reportAction.sequenceNumber" 1 "../../src/libs/ReportActionsUtils.ts" "@typescript-eslint/no-deprecated/reportAction?.originalMessage" 2 -"../../src/libs/ReportActionsUtils.ts" "@typescript-eslint/no-unsafe-type-assertion" 110 +"../../src/libs/ReportActionsUtils.ts" "@typescript-eslint/no-unsafe-type-assertion" 111 "../../src/libs/ReportActionsUtils.ts" "rulesdir/no-onyx-connect" 3 "../../src/libs/ReportNameUtils.ts" "@typescript-eslint/no-deprecated/translateLocal" 12 "../../src/libs/ReportNameUtils.ts" "@typescript-eslint/no-unsafe-type-assertion" 2 +"../../src/libs/ReportPrimaryActionUtils.ts" "no-restricted-imports" 1 "../../src/libs/ReportTitleUtils.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/libs/ReportUtils.ts" "@typescript-eslint/no-deprecated/getPolicy" 28 "../../src/libs/ReportUtils.ts" "@typescript-eslint/no-deprecated/translateLocal" 34 @@ -770,6 +776,7 @@ "../../src/libs/SearchAutocompleteUtils.ts" "@typescript-eslint/no-unsafe-type-assertion" 4 "../../src/libs/SearchQueryUtils.ts" "@typescript-eslint/no-unsafe-type-assertion" 73 "../../src/libs/SearchUIUtils.ts" "@typescript-eslint/no-unsafe-type-assertion" 69 +"../../src/libs/SearchUIUtils.ts" "no-restricted-imports" 1 "../../src/libs/SelectionScraper/index.ts" "@typescript-eslint/no-unsafe-type-assertion" 4 "../../src/libs/SidebarUtils.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/libs/Sound/index.native.ts" "@typescript-eslint/no-unsafe-type-assertion" 4 @@ -782,6 +789,7 @@ "../../src/libs/TransactionPreviewUtils.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/libs/TransactionUtils/index.ts" "@typescript-eslint/no-deprecated/translateLocal" 5 "../../src/libs/TransactionUtils/index.ts" "@typescript-eslint/no-unsafe-type-assertion" 8 +"../../src/libs/UnreadIndicatorUpdater/index.ts" "no-restricted-imports" 1 "../../src/libs/UnreadIndicatorUpdater/index.ts" "no-restricted-syntax" 1 "../../src/libs/Url.ts" "@typescript-eslint/no-unsafe-type-assertion" 3 "../../src/libs/UserAvatarUtils.ts" "@typescript-eslint/no-unsafe-type-assertion" 3 @@ -800,6 +808,7 @@ "../../src/libs/actions/Chronos.ts" "no-restricted-syntax" 1 "../../src/libs/actions/ClearReportActionErrors.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/libs/actions/CompanyCards.ts" "@typescript-eslint/no-unsafe-type-assertion" 4 +"../../src/libs/actions/CompanyCards.ts" "no-restricted-imports" 1 "../../src/libs/actions/CompanyCards.ts" "no-restricted-syntax" 5 "../../src/libs/actions/Delegate.ts" "no-restricted-syntax" 4 "../../src/libs/actions/Domain.ts" "@typescript-eslint/no-unsafe-type-assertion" 3 @@ -823,6 +832,7 @@ "../../src/libs/actions/IOU/MoneyRequestBuilder.ts" "@typescript-eslint/no-unsafe-type-assertion" 2 "../../src/libs/actions/IOU/PayMoneyRequest.ts" "@typescript-eslint/no-deprecated/buildNextStepNew" 3 "../../src/libs/actions/IOU/PayMoneyRequest.ts" "@typescript-eslint/no-unsafe-type-assertion" 2 +"../../src/libs/actions/IOU/PayMoneyRequest.ts" "no-restricted-imports" 1 "../../src/libs/actions/IOU/PayMoneyRequest.ts" "no-restricted-syntax" 1 "../../src/libs/actions/IOU/PerDiem.ts" "@typescript-eslint/no-deprecated/buildNextStepNew" 1 "../../src/libs/actions/IOU/PerDiem.ts" "@typescript-eslint/no-unsafe-type-assertion" 3 @@ -834,6 +844,7 @@ "../../src/libs/actions/IOU/RejectMoneyRequest.ts" "no-restricted-syntax" 1 "../../src/libs/actions/IOU/ReportWorkflow.ts" "@typescript-eslint/no-deprecated/buildNextStepNew" 7 "../../src/libs/actions/IOU/ReportWorkflow.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 +"../../src/libs/actions/IOU/ReportWorkflow.ts" "no-restricted-imports" 1 "../../src/libs/actions/IOU/ReportWorkflow.ts" "no-restricted-syntax" 1 "../../src/libs/actions/IOU/SearchUpdate.ts" "@typescript-eslint/no-unsafe-type-assertion" 2 "../../src/libs/actions/IOU/SendInvoice.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 @@ -864,13 +875,13 @@ "../../src/libs/actions/ImportTransactions.ts" "no-restricted-syntax" 1 "../../src/libs/actions/InputFocus/index.web.ts" "no-restricted-imports" 1 "../../src/libs/actions/InputFocus/index.web.ts" "no-restricted-syntax" 1 -"../../src/libs/actions/Link.ts" "@typescript-eslint/no-deprecated/InteractionManager.runAfterInteractions" 1 "../../src/libs/actions/Link.ts" "@typescript-eslint/no-unsafe-type-assertion" 4 "../../src/libs/actions/Link.ts" "no-restricted-syntax" 3 "../../src/libs/actions/MapboxToken.ts" "no-restricted-syntax" 2 "../../src/libs/actions/MergeAccounts.ts" "no-restricted-syntax" 1 "../../src/libs/actions/MergeTransaction.ts" "@typescript-eslint/no-deprecated/getPolicyTagsData" 1 "../../src/libs/actions/MergeTransaction.ts" "@typescript-eslint/no-unsafe-type-assertion" 2 +"../../src/libs/actions/MergeTransaction.ts" "no-restricted-imports" 1 "../../src/libs/actions/MergeTransaction.ts" "no-restricted-syntax" 1 "../../src/libs/actions/OdometerTransactionUtils.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/libs/actions/Onboarding.ts" "no-restricted-syntax" 1 @@ -889,6 +900,7 @@ "../../src/libs/actions/Policy/Category.ts" "no-restricted-syntax" 5 "../../src/libs/actions/Policy/DistanceRate.ts" "no-restricted-syntax" 2 "../../src/libs/actions/Policy/Member.ts" "@typescript-eslint/no-unsafe-type-assertion" 2 +"../../src/libs/actions/Policy/Member.ts" "no-restricted-imports" 1 "../../src/libs/actions/Policy/Member.ts" "no-restricted-syntax" 8 "../../src/libs/actions/Policy/Member.ts" "rulesdir/no-onyx-connect" 1 "../../src/libs/actions/Policy/PerDiem.ts" "no-restricted-syntax" 1 @@ -896,11 +908,14 @@ "../../src/libs/actions/Policy/Policy.ts" "@typescript-eslint/no-deprecated/buildNextStepNew" 1 "../../src/libs/actions/Policy/Policy.ts" "@typescript-eslint/no-deprecated/translateLocal" 1 "../../src/libs/actions/Policy/Policy.ts" "@typescript-eslint/no-unsafe-type-assertion" 8 +"../../src/libs/actions/Policy/Policy.ts" "no-restricted-imports" 2 "../../src/libs/actions/Policy/Policy.ts" "no-restricted-syntax" 10 "../../src/libs/actions/Policy/Policy.ts" "rulesdir/no-onyx-connect" 2 +"../../src/libs/actions/Policy/ReportField.ts" "no-restricted-imports" 1 "../../src/libs/actions/Policy/ReportField.ts" "no-restricted-syntax" 4 "../../src/libs/actions/Policy/Rules.ts" "no-restricted-syntax" 3 "../../src/libs/actions/Policy/Tag.ts" "@typescript-eslint/no-unsafe-type-assertion" 2 +"../../src/libs/actions/Policy/Tag.ts" "no-restricted-imports" 1 "../../src/libs/actions/Policy/Tag.ts" "no-restricted-syntax" 4 "../../src/libs/actions/Policy/Travel.ts" "no-restricted-syntax" 2 "../../src/libs/actions/PolicyConnections.ts" "no-restricted-syntax" 1 @@ -908,7 +923,6 @@ "../../src/libs/actions/ReimbursementAccount/resetNonUSDBankAccount.ts" "no-restricted-syntax" 1 "../../src/libs/actions/ReimbursementAccount/resetUSDBankAccount.ts" "no-restricted-syntax" 1 "../../src/libs/actions/Report/MarkAllMessageAsRead.tsx" "no-restricted-syntax" 1 -"../../src/libs/actions/Report/index.ts" "@typescript-eslint/no-deprecated/InteractionManager.runAfterInteractions" 1 "../../src/libs/actions/Report/index.ts" "@typescript-eslint/no-deprecated/buildNextStepNew" 3 "../../src/libs/actions/Report/index.ts" "@typescript-eslint/no-deprecated/reportAction.originalMessage" 1 "../../src/libs/actions/Report/index.ts" "@typescript-eslint/no-unsafe-type-assertion" 20 @@ -921,7 +935,6 @@ "../../src/libs/actions/Search.ts" "@typescript-eslint/no-unsafe-type-assertion" 19 "../../src/libs/actions/Search.ts" "no-restricted-syntax" 1 "../../src/libs/actions/Session/index.ts" "@typescript-eslint/no-unsafe-type-assertion" 3 -"../../src/libs/actions/Session/index.ts" "no-restricted-imports" 1 "../../src/libs/actions/Session/index.ts" "no-restricted-syntax" 9 "../../src/libs/actions/Session/index.ts" "rulesdir/no-onyx-connect" 2 "../../src/libs/actions/SplitExpenses.ts" "@typescript-eslint/no-unsafe-type-assertion" 2 @@ -931,6 +944,7 @@ "../../src/libs/actions/Subscription.ts" "no-restricted-syntax" 1 "../../src/libs/actions/Tab.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/libs/actions/Task.ts" "@typescript-eslint/no-unsafe-type-assertion" 3 +"../../src/libs/actions/Task.ts" "no-restricted-imports" 1 "../../src/libs/actions/Task.ts" "no-restricted-syntax" 7 "../../src/libs/actions/TaxRate.ts" "no-restricted-syntax" 1 "../../src/libs/actions/TeachersUnite.ts" "no-restricted-syntax" 1 @@ -943,6 +957,7 @@ "../../src/libs/actions/TravelInvoicing.ts" "no-restricted-syntax" 3 "../../src/libs/actions/UnreportedExpenses.tsx" "no-restricted-syntax" 1 "../../src/libs/actions/User.ts" "@typescript-eslint/no-unsafe-type-assertion" 5 +"../../src/libs/actions/User.ts" "no-restricted-imports" 1 "../../src/libs/actions/User.ts" "no-restricted-syntax" 8 "../../src/libs/actions/VacationDelegate.ts" "no-restricted-syntax" 2 "../../src/libs/actions/Wallet.ts" "@typescript-eslint/no-unsafe-type-assertion" 4 @@ -960,8 +975,10 @@ "../../src/libs/actions/connections/QuickbooksOnline.ts" "no-restricted-syntax" 2 "../../src/libs/actions/connections/SageIntacct.ts" "no-restricted-syntax" 2 "../../src/libs/actions/connections/Xero.ts" "no-restricted-syntax" 2 +"../../src/libs/actions/connections/index.ts" "no-restricted-imports" 1 "../../src/libs/actions/connections/index.ts" "no-restricted-syntax" 3 "../../src/libs/actions/getCompanyCardBankConnection/index.tsx" "@typescript-eslint/no-unsafe-type-assertion" 4 +"../../src/libs/actions/getCompanyCardBankConnection/index.tsx" "no-restricted-imports" 1 "../../src/libs/actions/getCompanyCardBankConnection/index.tsx" "no-restricted-syntax" 2 "../../src/libs/actions/replaceOptimisticReportWithActualReport.ts" "@typescript-eslint/no-deprecated/InteractionManager.runAfterInteractions" 1 "../../src/libs/actions/replaceOptimisticReportWithActualReport.ts" "@typescript-eslint/no-unsafe-type-assertion" 2 @@ -1032,10 +1049,14 @@ "../../src/pages/MissingPersonalDetails/subPages/Address.tsx" "@typescript-eslint/no-unsafe-type-assertion" 5 "../../src/pages/MissingPersonalDetails/subPages/Address.tsx" "react-hooks/refs" 4 "../../src/pages/MultifactorAuthentication/RevokePage.tsx" "@typescript-eslint/no-deprecated/ConfirmModal" 1 +"../../src/pages/OnboardingAccounting/BaseOnboardingAccounting.tsx" "no-restricted-imports" 1 "../../src/pages/OnboardingInterestedFeatures/BaseOnboardingInterestedFeatures.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 +"../../src/pages/OnboardingInterestedFeatures/BaseOnboardingInterestedFeatures.tsx" "no-restricted-imports" 1 "../../src/pages/OnboardingInterestedFeatures/BaseOnboardingInterestedFeatures.tsx" "react-hooks/preserve-manual-memoization" 2 "../../src/pages/OnboardingPrivateDomain/BaseOnboardingPrivateDomain.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 +"../../src/pages/OnboardingWorkspaceConfirmation/BaseOnboardingWorkspaceConfirmation.tsx" "no-restricted-imports" 1 "../../src/pages/OnboardingWorkspaceCurrency/BaseOnboardingWorkspaceCurrency.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 +"../../src/pages/OnboardingWorkspaceOptional/BaseOnboardingWorkspaceOptional.tsx" "no-restricted-imports" 1 "../../src/pages/ProfilePage.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/pages/ReimbursementAccount/AddressFormFields.tsx" "@typescript-eslint/no-unsafe-type-assertion" 2 "../../src/pages/ReimbursementAccount/EnterSignerInfo/index.tsx" "@typescript-eslint/no-deprecated/useSubStep" 1 @@ -1069,6 +1090,7 @@ "../../src/pages/ReimbursementAccount/USD/ConnectBankAccount/components/BankAccountValidationForm.tsx" "@typescript-eslint/no-unsafe-type-assertion" 2 "../../src/pages/ReimbursementAccount/USD/USDVerifiedBankAccountFlowPage.tsx" "@typescript-eslint/no-unsafe-type-assertion" 3 "../../src/pages/ReimbursementAccount/utils/getSubStepValues.ts" "@typescript-eslint/no-unsafe-type-assertion" 4 +"../../src/pages/ReportDescriptionPage.tsx" "no-restricted-imports" 1 "../../src/pages/ReportDescriptionPage.tsx" "no-restricted-syntax" 1 "../../src/pages/ReportParticipantDetailsPage.tsx" "@typescript-eslint/no-deprecated/ConfirmModal" 1 "../../src/pages/ReportParticipantDetailsPage.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 @@ -1095,6 +1117,7 @@ "../../src/pages/ShareCodePage.tsx" "@typescript-eslint/no-unsafe-type-assertion" 2 "../../src/pages/TeachersUnite/SaveTheWorldPage.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/pages/TransactionDuplicate/Confirmation.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 +"../../src/pages/TransactionDuplicate/Confirmation.tsx" "no-restricted-imports" 1 "../../src/pages/TransactionDuplicate/Confirmation.tsx" "react-hooks/refs" 12 "../../src/pages/TransactionDuplicate/DuplicateTransactionItem.tsx" "@typescript-eslint/no-unsafe-type-assertion" 2 "../../src/pages/TransactionMerge/ConfirmationPage.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 @@ -1102,6 +1125,7 @@ "../../src/pages/TransactionMerge/MergeTransactionItem.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/pages/TransactionMerge/MergeTransactionsListContent.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/pages/Travel/DynamicTravelTerms.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 +"../../src/pages/Travel/DynamicTravelUpgrade.tsx" "no-restricted-imports" 1 "../../src/pages/Travel/TravelUpgrade.tsx" "react-hooks/set-state-in-effect" 1 "../../src/pages/Travel/WorkspaceConfirmationForTravelPage.tsx" "@typescript-eslint/no-unsafe-type-assertion" 2 "../../src/pages/UnreportedExpenseListItem.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 @@ -1110,9 +1134,11 @@ "../../src/pages/domain/Saml/SamlLoginSectionContent.tsx" "@typescript-eslint/no-deprecated/ConfirmModal" 1 "../../src/pages/home/ForYouSection/EmptyState.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/pages/home/GettingStartedSection/hooks/useGettingStartedItems.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 +"../../src/pages/home/GettingStartedSection/hooks/useGettingStartedItems.ts" "no-restricted-imports" 1 "../../src/pages/home/SpendOverTimeSection/useSpendOverTimeData.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/pages/home/TimeSensitiveSection/index.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/pages/home/TimeSensitiveSection/items/FixCompanyCardConnection.tsx" "@typescript-eslint/no-unsafe-type-assertion" 2 +"../../src/pages/home/YourSpendSection/useYourSpendData.ts" "no-restricted-imports" 1 "../../src/pages/inbox/DeleteTransactionNavigateBackHandler.tsx" "no-restricted-imports" 1 "../../src/pages/inbox/ReactionListWrapper.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/pages/inbox/ReportFetchHandler.tsx" "@typescript-eslint/no-deprecated/InteractionManager.runAfterInteractions" 2 @@ -1149,7 +1175,6 @@ "../../src/pages/inbox/report/ReportActionItemMessageEdit.tsx" "react-hooks/refs" 5 "../../src/pages/inbox/report/ReportActionItemMessageEdit.tsx" "react-hooks/set-state-in-effect" 1 "../../src/pages/inbox/report/ReportActionsList.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 -"../../src/pages/inbox/report/ReportActionsList.tsx" "no-restricted-imports" 1 "../../src/pages/inbox/report/ReportActionsList.tsx" "react-hooks/refs" 2 "../../src/pages/inbox/report/TripSummary.tsx" "rulesdir/no-default-id-values" 1 "../../src/pages/inbox/report/UserTypingEventListener.tsx" "no-restricted-imports" 1 @@ -1166,24 +1191,23 @@ "../../src/pages/inbox/sidebar/FABPopoverContent/menuItems/InvoiceMenuItem.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/pages/inbox/sidebar/FABPopoverContent/menuItems/NewWorkspaceMenuItem.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/pages/inbox/sidebar/FABPopoverContent/menuItems/QuickActionMenuItem.tsx" "@typescript-eslint/no-unsafe-type-assertion" 3 +"../../src/pages/inbox/sidebar/FABPopoverContent/menuItems/TravelMenuItem.tsx" "no-restricted-imports" 1 "../../src/pages/inbox/sidebar/FABPopoverContent/useScanActions.ts" "@typescript-eslint/no-unsafe-type-assertion" 2 "../../src/pages/inbox/sidebar/SidebarLinksData.tsx" "react-hooks/refs" 1 -"../../src/pages/inbox/usePusherDraftPacing.ts" "@typescript-eslint/no-unsafe-type-assertion" 2 "../../src/pages/iou/MoneyRequestAmountForm.tsx" "react-hooks/set-state-in-effect" 3 "../../src/pages/iou/SplitExpensePage.tsx" "@typescript-eslint/no-deprecated/InteractionManager.runAfterInteractions" 1 "../../src/pages/iou/SplitExpensePage.tsx" "@typescript-eslint/no-unsafe-type-assertion" 2 "../../src/pages/iou/SplitExpensePage.tsx" "react-hooks/set-state-in-effect" 3 "../../src/pages/iou/SplitList/index.tsx" "@typescript-eslint/no-unsafe-type-assertion" 2 "../../src/pages/iou/request/MoneyRequestAttendeeSelector.tsx" "@typescript-eslint/no-unsafe-type-assertion" 2 +"../../src/pages/iou/request/MoneyRequestAttendeeSelector.tsx" "no-restricted-imports" 1 "../../src/pages/iou/request/ParticipantSearchResults.tsx" "@typescript-eslint/no-unsafe-type-assertion" 4 "../../src/pages/iou/request/step/DistanceManualTabContent.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 -"../../src/pages/iou/request/step/DynamicIOURequestStepCategoryPage.tsx" "@typescript-eslint/no-deprecated/InteractionManager.runAfterInteractions" 1 -"../../src/pages/iou/request/step/DynamicIOURequestStepUpgradePage.tsx" "@typescript-eslint/no-deprecated/ConfirmModal" 1 -"../../src/pages/iou/request/step/DynamicIOURequestStepUpgradePage.tsx" "@typescript-eslint/no-unsafe-type-assertion" 2 "../../src/pages/iou/request/step/IOURequestEditReport.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/pages/iou/request/step/IOURequestEditReportCommon.tsx" "@typescript-eslint/no-deprecated/ConfirmModal" 1 "../../src/pages/iou/request/step/IOURequestEditReportCommon.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/pages/iou/request/step/IOURequestStepAmount.tsx" "react-hooks/set-state-in-effect" 1 +"../../src/pages/iou/request/step/IOURequestStepCategory.tsx" "@typescript-eslint/no-deprecated/InteractionManager.runAfterInteractions" 1 "../../src/pages/iou/request/step/IOURequestStepConfirmation.tsx" "@typescript-eslint/no-unsafe-type-assertion" 3 "../../src/pages/iou/request/step/IOURequestStepConfirmation.tsx" "react-hooks/preserve-manual-memoization" 1 "../../src/pages/iou/request/step/IOURequestStepDestination.tsx" "@typescript-eslint/no-deprecated/InteractionManager.runAfterInteractions" 1 @@ -1209,6 +1233,8 @@ "../../src/pages/iou/request/step/IOURequestStepSubrate.tsx" "@typescript-eslint/no-deprecated/InteractionManager.runAfterInteractions" 1 "../../src/pages/iou/request/step/IOURequestStepSubrate.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/pages/iou/request/step/IOURequestStepSubrate.tsx" "react-hooks/set-state-in-effect" 1 +"../../src/pages/iou/request/step/IOURequestStepUpgrade.tsx" "@typescript-eslint/no-deprecated/ConfirmModal" 1 +"../../src/pages/iou/request/step/IOURequestStepUpgrade.tsx" "@typescript-eslint/no-unsafe-type-assertion" 2 "../../src/pages/iou/request/step/IOURequestStepWaypoint.tsx" "@typescript-eslint/no-unsafe-type-assertion" 2 "../../src/pages/iou/request/step/StepScreenWrapper.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/pages/iou/request/step/confirmation/submitDismissStrategies.ts" "no-restricted-imports" 1 @@ -1286,6 +1312,7 @@ "../../src/pages/settings/Wallet/UnshareBankAccount/UnshareBankAccount.tsx" "react-hooks/set-state-in-effect" 1 "../../src/pages/settings/Wallet/UpdatePersonalBankAccountPage.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/pages/settings/Wallet/WalletPage/index.tsx" "@typescript-eslint/no-unsafe-type-assertion" 2 +"../../src/pages/settings/Wallet/WalletPage/index.tsx" "no-restricted-imports" 1 "../../src/pages/settings/Wallet/WalletPage/index.tsx" "react-hooks/set-state-in-effect" 1 "../../src/pages/signin/LoginForm/BaseLoginForm.tsx" "@typescript-eslint/no-deprecated/InteractionManager.runAfterInteractions" 1 "../../src/pages/signin/SAMLSignInPage/index.native.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 @@ -1298,7 +1325,6 @@ "../../src/pages/signin/SignInPageLayout/FooterRow/index.native.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx" "react-hooks/set-state-in-effect" 4 "../../src/pages/tasks/NewTaskPage.tsx" "@typescript-eslint/no-deprecated/InteractionManager.runAfterInteractions" 1 -"../../src/pages/tasks/TaskAssigneeSelectorModal.tsx" "@typescript-eslint/no-deprecated/InteractionManager.runAfterInteractions" 2 "../../src/pages/tasks/TaskShareDestinationSelectorModal.tsx" "@typescript-eslint/no-unsafe-type-assertion" 2 "../../src/pages/wallet/WalletStatementPage.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/pages/workspace/DynamicWorkspaceOverviewPlanTypePage.tsx" "@typescript-eslint/no-unsafe-type-assertion" 2 @@ -1306,6 +1332,7 @@ "../../src/pages/workspace/WorkspaceConfirmationPage.tsx" "@typescript-eslint/no-unsafe-type-assertion" 2 "../../src/pages/workspace/WorkspaceInitialPage.tsx" "@typescript-eslint/no-unsafe-type-assertion" 2 "../../src/pages/workspace/WorkspaceMembersPage.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 +"../../src/pages/workspace/WorkspaceMembersPage.tsx" "no-restricted-imports" 1 "../../src/pages/workspace/WorkspaceMembersPage.tsx" "react-hooks/exhaustive-deps" 1 "../../src/pages/workspace/WorkspaceMembersPage.tsx" "react-hooks/refs" 1 "../../src/pages/workspace/WorkspaceNewRoomPage.tsx" "@typescript-eslint/no-deprecated/InteractionManager.runAfterInteractions" 1 @@ -1366,12 +1393,16 @@ "../../src/pages/workspace/accounting/qbd/export/DynamicQuickbooksDesktopOutOfPocketExpenseEntitySelectPage.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/pages/workspace/accounting/qbd/import/QuickbooksDesktopChartOfAccountsPage.tsx" "no-restricted-syntax" 2 "../../src/pages/workspace/accounting/qbd/import/QuickbooksDesktopChartOfAccountsPage.tsx" "rulesdir/no-default-id-values" 1 +"../../src/pages/workspace/accounting/qbd/import/QuickbooksDesktopClassesPage.tsx" "no-restricted-imports" 1 "../../src/pages/workspace/accounting/qbd/import/QuickbooksDesktopClassesPage.tsx" "no-restricted-syntax" 3 "../../src/pages/workspace/accounting/qbd/import/QuickbooksDesktopClassesPage.tsx" "rulesdir/no-default-id-values" 1 +"../../src/pages/workspace/accounting/qbd/import/QuickbooksDesktopCustomersPage.tsx" "no-restricted-imports" 1 "../../src/pages/workspace/accounting/qbd/import/QuickbooksDesktopCustomersPage.tsx" "no-restricted-syntax" 3 "../../src/pages/workspace/accounting/qbd/import/QuickbooksDesktopCustomersPage.tsx" "rulesdir/no-default-id-values" 1 +"../../src/pages/workspace/accounting/qbd/import/QuickbooksDesktopImportPage.tsx" "no-restricted-imports" 1 "../../src/pages/workspace/accounting/qbd/import/QuickbooksDesktopImportPage.tsx" "no-restricted-syntax" 1 "../../src/pages/workspace/accounting/qbd/import/QuickbooksDesktopImportPage.tsx" "rulesdir/no-default-id-values" 1 +"../../src/pages/workspace/accounting/qbd/import/QuickbooksDesktopItemsPage.tsx" "no-restricted-imports" 1 "../../src/pages/workspace/accounting/qbd/import/QuickbooksDesktopItemsPage.tsx" "no-restricted-syntax" 3 "../../src/pages/workspace/accounting/qbd/import/QuickbooksDesktopItemsPage.tsx" "rulesdir/no-default-id-values" 1 "../../src/pages/workspace/accounting/qbo/advanced/DynamicQuickbooksOnlineAccountingMethodPage.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 @@ -1382,15 +1413,18 @@ "../../src/pages/workspace/accounting/qbo/import/QuickbooksClassesPage.tsx" "rulesdir/no-default-id-values" 1 "../../src/pages/workspace/accounting/qbo/import/QuickbooksCustomersPage.tsx" "no-restricted-syntax" 2 "../../src/pages/workspace/accounting/qbo/import/QuickbooksCustomersPage.tsx" "rulesdir/no-default-id-values" 1 +"../../src/pages/workspace/accounting/qbo/import/QuickbooksImportPage.tsx" "no-restricted-imports" 1 "../../src/pages/workspace/accounting/qbo/import/QuickbooksImportPage.tsx" "no-restricted-syntax" 2 "../../src/pages/workspace/accounting/qbo/import/QuickbooksImportPage.tsx" "rulesdir/no-default-id-values" 1 "../../src/pages/workspace/accounting/qbo/import/QuickbooksTaxesPage.tsx" "no-restricted-syntax" 2 "../../src/pages/workspace/accounting/qbo/import/QuickbooksTaxesPage.tsx" "rulesdir/no-default-id-values" 1 "../../src/pages/workspace/accounting/reconciliation/CardReconciliationPage.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/pages/workspace/accounting/utils.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 +"../../src/pages/workspace/accounting/xero/XeroImportPage.tsx" "no-restricted-imports" 1 "../../src/pages/workspace/accounting/xero/XeroImportPage.tsx" "no-restricted-syntax" 1 "../../src/pages/workspace/accounting/xero/XeroImportPage.tsx" "rulesdir/no-default-id-values" 1 "../../src/pages/workspace/accounting/xero/XeroMapTrackingCategoryConfigurationPage.tsx" "@typescript-eslint/no-unsafe-type-assertion" 2 +"../../src/pages/workspace/accounting/xero/XeroTaxesConfigurationPage.tsx" "no-restricted-imports" 1 "../../src/pages/workspace/accounting/xero/XeroTaxesConfigurationPage.tsx" "no-restricted-syntax" 4 "../../src/pages/workspace/accounting/xero/XeroTaxesConfigurationPage.tsx" "rulesdir/no-default-id-values" 1 "../../src/pages/workspace/accounting/xero/XeroTrackingCategoryConfigurationPage.tsx" "@typescript-eslint/no-unsafe-type-assertion" 2 @@ -1399,10 +1433,11 @@ "../../src/pages/workspace/accounting/xero/export/DynamicXeroPurchaseBillStatusSelectorPage.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/pages/workspace/accounting/xero/import/XeroChartOfAccountsPage.tsx" "no-restricted-syntax" 2 "../../src/pages/workspace/accounting/xero/import/XeroChartOfAccountsPage.tsx" "rulesdir/no-default-id-values" 1 +"../../src/pages/workspace/accounting/xero/import/XeroCustomerConfigurationPage.tsx" "no-restricted-imports" 1 "../../src/pages/workspace/accounting/xero/import/XeroCustomerConfigurationPage.tsx" "no-restricted-syntax" 3 "../../src/pages/workspace/accounting/xero/import/XeroCustomerConfigurationPage.tsx" "rulesdir/no-default-id-values" 1 "../../src/pages/workspace/categories/CategorySettingsPage.tsx" "react-hooks/preserve-manual-memoization" 1 -"../../src/pages/workspace/categories/WorkspaceCategoriesPage.tsx" "react-hooks/set-state-in-effect" 2 +"../../src/pages/workspace/categories/WorkspaceCategoriesPage.tsx" "react-hooks/set-state-in-effect" 1 "../../src/pages/workspace/companyCards/BankConnection/index.tsx" "react-hooks/set-state-in-effect" 1 "../../src/pages/workspace/companyCards/WorkspaceCompanyCardAddWorkEmailPage.tsx" "@typescript-eslint/no-unsafe-type-assertion" 2 "../../src/pages/workspace/companyCards/WorkspaceCompanyCardDetailsPage.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 @@ -1420,7 +1455,6 @@ "../../src/pages/workspace/copyPolicySettings/CopyPolicySettingsConfirmPage.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/pages/workspace/copyPolicySettings/CopyPolicySettingsSelectFeaturesPage.tsx" "@typescript-eslint/no-unsafe-type-assertion" 2 "../../src/pages/workspace/distanceRates/PolicyDistanceRatesPage.tsx" "react-hooks/preserve-manual-memoization" 1 -"../../src/pages/workspace/downgrade/WorkspaceDowngradePage.tsx" "@typescript-eslint/no-deprecated/InteractionManager.runAfterInteractions" 1 "../../src/pages/workspace/duplicate/WorkspaceDuplicateSelectFeaturesForm.tsx" "react-hooks/preserve-manual-memoization" 2 "../../src/pages/workspace/duplicate/WorkspaceDuplicateSelectFeaturesForm.tsx" "react-hooks/set-state-in-effect" 1 "../../src/pages/workspace/expensifyCard/DynamicExpensifyCardLimitTypePage.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 @@ -1434,10 +1468,10 @@ "../../src/pages/workspace/hr/HRProviderCard.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/pages/workspace/hr/utils.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/pages/workspace/invoices/WorkspaceInvoiceVBASection.tsx" "@typescript-eslint/no-unsafe-type-assertion" 2 +"../../src/pages/workspace/members/ImportMembersPage.tsx" "no-restricted-imports" 1 "../../src/pages/workspace/members/ImportMembersPage.tsx" "no-restricted-syntax" 1 "../../src/pages/workspace/members/ImportMembersPage.tsx" "rulesdir/no-default-id-values" 1 "../../src/pages/workspace/members/ImportedMembersConfirmationPage.tsx" "react-hooks/refs" 7 -"../../src/pages/workspace/members/ImportedMembersPage.tsx" "@typescript-eslint/no-deprecated/InteractionManager.runAfterInteractions" 1 "../../src/pages/workspace/members/WorkspaceInviteMessageComponent.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/pages/workspace/members/WorkspaceInviteMessageComponent.tsx" "react-hooks/set-state-in-effect" 1 "../../src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx" "@typescript-eslint/no-unsafe-type-assertion" 4 @@ -1456,6 +1490,7 @@ "../../src/pages/workspace/travel/WorkspaceTravelInvoicingSection.tsx" "@typescript-eslint/no-deprecated/ConfirmModal" 3 "../../src/pages/workspace/travel/WorkspaceTravelInvoicingSettlementAccountPage.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/pages/workspace/upgrade/UpgradeIntro.tsx" "@typescript-eslint/no-unsafe-type-assertion" 2 +"../../src/pages/workspace/upgrade/WorkspaceUpgradePage.tsx" "no-restricted-imports" 1 "../../src/pages/workspace/withPolicy.tsx" "@typescript-eslint/no-unsafe-type-assertion" 3 "../../src/pages/workspace/withPolicyAndFullscreenLoading.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/pages/workspace/workflows/WorkspaceAutoReportingFrequencyPage.tsx" "@typescript-eslint/no-unsafe-type-assertion" 2 @@ -1467,6 +1502,7 @@ "../../src/selectors/AdvancedSearchFiltersForm.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/selectors/Domain.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/selectors/Policy.ts" "@typescript-eslint/no-unsafe-type-assertion" 2 +"../../src/selectors/Policy.ts" "no-restricted-imports" 1 "../../src/setup/addUtilsToWindow.ts" "@typescript-eslint/no-unsafe-type-assertion" 20 "../../src/setup/moduleInitPolyfill.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/setup/telemetry/index.web.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 @@ -1551,19 +1587,29 @@ "../../tests/actions/BankAccountsTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 2 "../../tests/actions/CopyPolicySettingsTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 18 "../../tests/actions/DomainTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 20 +"../../tests/actions/EnforceActionExportRestrictions.ts" "no-restricted-imports" 1 "../../tests/actions/IOU/BuildOnyxDataForMoneyRequestTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 17 +"../../tests/actions/IOU/CreateDraftTransactionTest.ts" "no-restricted-imports" 1 "../../tests/actions/IOU/GetMoneyRequestInformationTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 +"../../tests/actions/IOU/MoneyRequestBuilderTest.ts" "no-restricted-imports" 1 +"../../tests/actions/IOU/MoneyRequestSettersTest.ts" "no-restricted-imports" 1 "../../tests/actions/IOU/PerDiemTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 6 "../../tests/actions/IOU/RequestMoneyTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 5 +"../../tests/actions/IOU/RequestMoneyTest.ts" "no-restricted-imports" 1 "../../tests/actions/IOU/SearchUpdateTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 4 +"../../tests/actions/IOU/SearchUpdateTest.ts" "no-restricted-imports" 1 "../../tests/actions/IOU/SplitReportTotalsTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 10 +"../../tests/actions/IOU/SplitReportTotalsTest.ts" "no-restricted-imports" 1 "../../tests/actions/IOUTest/BulkEditTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 25 "../../tests/actions/IOUTest/DeleteMoneyRequestTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 2 "../../tests/actions/IOUTest/DuplicateTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 23 "../../tests/actions/IOUTest/GetUpdateMoneyRequestParamsTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 3 "../../tests/actions/IOUTest/HoldTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 4 +"../../tests/actions/IOUTest/HoldTest.ts" "no-restricted-imports" 1 "../../tests/actions/IOUTest/PayMoneyRequestTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 6 +"../../tests/actions/IOUTest/PayMoneyRequestTest.ts" "no-restricted-imports" 1 "../../tests/actions/IOUTest/ReceiptTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 9 +"../../tests/actions/IOUTest/ReceiptTest.ts" "no-restricted-imports" 1 "../../tests/actions/IOUTest/RejectMoneyRequestTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../tests/actions/IOUTest/ReportWorkflowTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 64 "../../tests/actions/IOUTest/SendInvoiceTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 7 @@ -1571,27 +1617,37 @@ "../../tests/actions/IOUTest/SplitSelfDMTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 2 "../../tests/actions/IOUTest/SplitTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 21 "../../tests/actions/IOUTest/TrackExpenseTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 10 +"../../tests/actions/IOUTest/TrackExpenseTest.ts" "no-restricted-imports" 1 "../../tests/actions/IOUTest/UpdateMoneyRequestTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 4 -"../../tests/actions/IOUTest/UpdateMoneyRequestVendorTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 2 +"../../tests/actions/IOUTest/UpdateMoneyRequestTest.ts" "no-restricted-imports" 1 +"../../tests/actions/IOUTest/UpdateMoneyRequestVendorTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 3 "../../tests/actions/MergeTransactionTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 10 +"../../tests/actions/OdometerTransactionUtilsTest.ts" "no-restricted-imports" 1 "../../tests/actions/OnyxUpdateManagerTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 4 "../../tests/actions/PersonalDetailsTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 10 "../../tests/actions/PolicyCategoryTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../tests/actions/PolicyMemberTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 9 "../../tests/actions/PolicyProfileTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 +"../../tests/actions/PolicyProfileTest.ts" "no-restricted-imports" 1 "../../tests/actions/PolicyTagTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../tests/actions/PolicyTaxTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../tests/actions/PolicyTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 86 +"../../tests/actions/PolicyTest.ts" "no-restricted-imports" 1 "../../tests/actions/QueuedOnyxUpdatesTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../tests/actions/ReimbursementAccountTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 5 "../../tests/actions/ReplaceOptimisticReportWithActualReportTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 3 "../../tests/actions/ReportFieldTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 +"../../tests/actions/ReportFieldTest.ts" "no-restricted-imports" 1 "../../tests/actions/ReportPreviewActionUtilsTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 28 +"../../tests/actions/ReportPreviewActionUtilsTest.ts" "no-restricted-imports" 2 "../../tests/actions/ReportTest.ts" "@typescript-eslint/no-deprecated/buildNextStepNew" 1 "../../tests/actions/ReportTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 80 +"../../tests/actions/ReportTest.ts" "no-restricted-imports" 1 "../../tests/actions/SessionTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 12 "../../tests/actions/SubscriptionTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 2 "../../tests/actions/TaskTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 44 +"../../tests/actions/TaskTest.ts" "no-restricted-imports" 1 +"../../tests/actions/TransactionTest.ts" "no-restricted-imports" 1 "../../tests/actions/UserTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 45 "../../tests/actions/WorkflowTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 13 "../../tests/actions/connections/NetSuite.ts" "@typescript-eslint/no-unsafe-type-assertion" 6 @@ -1625,7 +1681,7 @@ "../../tests/perf-test/ReportUtils.perf-test.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../tests/perf-test/SelectionList.perf-test.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../tests/perf-test/useFilterFormValues.perf-test.tsx" "@typescript-eslint/no-unsafe-type-assertion" 15 -"../../tests/ui/AgentsPromoBannersTest.tsx" "@typescript-eslint/no-unsafe-type-assertion" 4 +"../../tests/ui/AgentsPromoBannersTest.tsx" "@typescript-eslint/no-unsafe-type-assertion" 3 "../../tests/ui/AssignCardFeed.tsx" "@typescript-eslint/no-unsafe-type-assertion" 3 "../../tests/ui/AuthScreensInitHandlerTest.tsx" "@typescript-eslint/no-unsafe-type-assertion" 7 "../../tests/ui/AvatarSelector.test.tsx" "@typescript-eslint/no-unsafe-type-assertion" 7 @@ -1655,6 +1711,7 @@ "../../tests/ui/MoneyReportViewTest.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../tests/ui/MoneyRequestReportActionsListRejectModalTest.tsx" "@typescript-eslint/no-unsafe-type-assertion" 2 "../../tests/ui/MoneyRequestReportFooter.tsx" "@typescript-eslint/no-unsafe-type-assertion" 5 +"../../tests/ui/MoneyRequestReportPreview.test.tsx" "no-restricted-imports" 1 "../../tests/ui/MoneyRequestViewTest.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../tests/ui/MonthListItemHeaderTest.tsx" "@typescript-eslint/no-unsafe-type-assertion" 2 "../../tests/ui/MultifactorAuthenticationRevokePageTest.tsx" "@typescript-eslint/no-unsafe-type-assertion" 8 @@ -1694,6 +1751,7 @@ "../../tests/ui/WorkspaceInviteMessageApproverPageTest.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../tests/ui/WorkspaceMembersTest.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../tests/ui/WorkspaceMoreFeaturesPageTest.tsx" "@typescript-eslint/no-unsafe-type-assertion" 2 +"../../tests/ui/WorkspaceMoreFeaturesPageTest.tsx" "no-restricted-imports" 1 "../../tests/ui/WorkspaceOnboarding.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../tests/ui/WorkspaceTagsTest.tsx" "@typescript-eslint/no-unsafe-type-assertion" 2 "../../tests/ui/WorkspaceUpgradeTest.tsx" "@typescript-eslint/no-unsafe-type-assertion" 2 @@ -1712,9 +1770,9 @@ "../../tests/ui/components/SettlementButtonTest.tsx" "@typescript-eslint/no-unsafe-type-assertion" 3 "../../tests/ui/components/TimePickerTest.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../tests/ui/hooks/useHtmlPasteTest/index.tsx" "@typescript-eslint/no-unsafe-type-assertion" 10 -"../../tests/unit/APITest.ts" "@typescript-eslint/no-unsafe-type-assertion" 74 +"../../tests/unit/APITest.ts" "@typescript-eslint/no-unsafe-type-assertion" 61 "../../tests/unit/AddExistingExpenseSearchTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 6 -"../../tests/unit/AgentActionTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 61 +"../../tests/unit/AgentActionTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 53 "../../tests/unit/AgentZeroStatusContextTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 5 "../../tests/unit/AvatarButtonWithIconTest.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../tests/unit/AvatarUtilsTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 2 @@ -1727,7 +1785,7 @@ "../../tests/unit/CIGitLogicTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 5 "../../tests/unit/CLIVariadicTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../tests/unit/CardFeedErrorsDerivedValueTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 -"../../tests/unit/CardFeedUtilsTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 16 +"../../tests/unit/CardFeedUtilsTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../tests/unit/CardUtilsTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 152 "../../tests/unit/CardsSectionUtilsTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../tests/unit/CategoryUtilsTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 @@ -1758,14 +1816,17 @@ "../../tests/unit/FlashListTest.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../tests/unit/FocusTrapForModalTest.tsx" "@typescript-eslint/no-unsafe-type-assertion" 3 "../../tests/unit/FormulaTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 78 +"../../tests/unit/FormulaTest.ts" "no-restricted-imports" 1 "../../tests/unit/FraudProtectionTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 2 "../../tests/unit/GitTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 5 "../../tests/unit/GithubUtilsTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 3 "../../tests/unit/HomePage/YourSpendSection/YourSpendSectionTest.tsx" "@typescript-eslint/no-unsafe-type-assertion" 3 "../../tests/unit/HomePage/YourSpendSection/useYourSpendDataTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 6 +"../../tests/unit/HomePage/YourSpendSection/useYourSpendDataTest.ts" "no-restricted-imports" 1 "../../tests/unit/HrUtilsTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 61 "../../tests/unit/HybridAppActionsTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 2 "../../tests/unit/IOUUtilsTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 11 +"../../tests/unit/IOUUtilsTest.ts" "no-restricted-imports" 1 "../../tests/unit/ImageSVGCachePolicyTest.tsx" "@typescript-eslint/no-unsafe-type-assertion" 3 "../../tests/unit/ImportTransactions.test.ts" "@typescript-eslint/no-unsafe-type-assertion" 17 "../../tests/unit/LHNAvatarPipelineTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 2 @@ -1776,7 +1837,8 @@ "../../tests/unit/MentionUserRendererTest.tsx" "@typescript-eslint/no-unsafe-type-assertion" 8 "../../tests/unit/MentionUtilsTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 4 "../../tests/unit/MiddlewareTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 20 -"../../tests/unit/ModifiedExpenseMessageTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 5 +"../../tests/unit/ModifiedExpenseMessageTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 6 +"../../tests/unit/ModifiedExpenseMessageTest.ts" "no-restricted-imports" 1 "../../tests/unit/MoneyRequestReportTransactionListActiveTransactionIDsTest.tsx" "@typescript-eslint/no-unsafe-type-assertion" 5 "../../tests/unit/MoneyRequestReportUtilsTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 8 "../../tests/unit/MoneyRequestUtilsTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 3 @@ -1808,6 +1870,7 @@ "../../tests/unit/PopoverMenuV2Test.tsx" "@typescript-eslint/no-unsafe-type-assertion" 4 "../../tests/unit/PressResponderTest.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../tests/unit/QuickActionUtilsTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 7 +"../../tests/unit/QuickActionUtilsTest.ts" "no-restricted-imports" 2 "../../tests/unit/ReceiptAlternativeMethodsTest.tsx" "@typescript-eslint/no-unsafe-type-assertion" 4 "../../tests/unit/RefreshCardFeedConnectionPageTest.tsx" "@typescript-eslint/no-unsafe-type-assertion" 4 "../../tests/unit/ReimbursementAccountUtilsTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 2 @@ -1816,13 +1879,17 @@ "../../tests/unit/ReportActionsListPaddingViewTest.tsx" "@typescript-eslint/no-unsafe-type-assertion" 4 "../../tests/unit/ReportActionsUtilsTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 35 "../../tests/unit/ReportLayoutUtilsTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 -"../../tests/unit/ReportNameUtilsTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 22 +"../../tests/unit/ReportNameUtilsTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 23 "../../tests/unit/ReportPrimaryActionUtilsTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 168 -"../../tests/unit/ReportSecondaryActionUtilsTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 450 +"../../tests/unit/ReportPrimaryActionUtilsTest.ts" "no-restricted-imports" 1 +"../../tests/unit/ReportSecondaryActionUtilsTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 466 +"../../tests/unit/ReportSecondaryActionUtilsTest.ts" "no-restricted-imports" 2 "../../tests/unit/ReportSubmitUtilsTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../tests/unit/ReportTitleUtilsTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 3 +"../../tests/unit/ReportTitleUtilsTest.ts" "no-restricted-imports" 1 "../../tests/unit/ReportUtilsGetIconsTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 -"../../tests/unit/ReportUtilsTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 98 +"../../tests/unit/ReportUtilsTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 105 +"../../tests/unit/ReportUtilsTest.ts" "no-restricted-imports" 1 "../../tests/unit/ReportWelcomeTextTest.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../tests/unit/RequestConflictUtilsTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 11 "../../tests/unit/RequestTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 @@ -1831,7 +1898,7 @@ "../../tests/unit/Search/SearchListRenderCountTest.tsx" "@typescript-eslint/no-unsafe-type-assertion" 2 "../../tests/unit/Search/SearchQueryUtilsTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 9 "../../tests/unit/Search/SearchSingleSelectionPickerTest.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 -"../../tests/unit/Search/SearchUIUtilsTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 133 +"../../tests/unit/Search/SearchUIUtilsTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 136 "../../tests/unit/Search/buildCardFilterDataTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 22 "../../tests/unit/Search/buildSubstitutionsMapTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 3 "../../tests/unit/Search/getSpendOverTimeStateTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 2 @@ -1846,7 +1913,8 @@ "../../tests/unit/SelectFeaturesEmptyTagGroupTest.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../tests/unit/SettlementButtonUtilsTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 4 "../../tests/unit/SidebarTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 2 -"../../tests/unit/SidebarUtilsTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 31 +"../../tests/unit/SidebarUtilsTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 35 +"../../tests/unit/SidebarUtilsTest.ts" "no-restricted-imports" 1 "../../tests/unit/SplitExpenseAutoAdjustmentTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../tests/unit/SubscriptionUtilsTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 2 "../../tests/unit/SuggestedFollowupTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 @@ -1869,6 +1937,7 @@ "../../tests/unit/ViolationUtilsTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 20 "../../tests/unit/WhisperContentMentionContextTest.tsx" "@typescript-eslint/no-unsafe-type-assertion" 3 "../../tests/unit/WorkflowUtilsTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 8 +"../../tests/unit/WorkspaceReportFieldUtilsTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 2 "../../tests/unit/WorkspacesSettingsUtilsTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 15 "../../tests/unit/awaitStagingDeploysTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 3 "../../tests/unit/canEditFieldOfMoneyRequestTest.ts" "@typescript-eslint/no-deprecated/randomReportAction.originalMessage" 3 @@ -1920,7 +1989,7 @@ "../../tests/unit/hooks/useDraftMessageVideoAttributeCache.test.ts" "@typescript-eslint/no-unsafe-type-assertion" 2 "../../tests/unit/hooks/useEditComposerToggle.test.ts" "@typescript-eslint/no-unsafe-type-assertion" 2 "../../tests/unit/hooks/useEditMessage.test.ts" "@typescript-eslint/no-unsafe-type-assertion" 2 -"../../tests/unit/hooks/useExpenseSubmission.test.ts" "@typescript-eslint/no-unsafe-type-assertion" 3 +"../../tests/unit/hooks/useExpenseSubmission.test.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../tests/unit/hooks/useExpensifyCardFeedsForFeedSelector.test.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../tests/unit/hooks/useExportedToFilterOptions.test.ts" "@typescript-eslint/no-unsafe-type-assertion" 5 "../../tests/unit/hooks/useFilterFeedDataTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 2 @@ -1930,7 +1999,6 @@ "../../tests/unit/hooks/useFreeTrial.test.ts" "@typescript-eslint/no-unsafe-type-assertion" 8 "../../tests/unit/hooks/useGettingStartedItems.test.ts" "@typescript-eslint/no-unsafe-type-assertion" 11 "../../tests/unit/hooks/useHasAnyAdminExpensifyCardFeed.test.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 -"../../tests/unit/hooks/useHoldRejectActionsTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../tests/unit/hooks/useIsAllowedToIssueCompanyCard.test.ts" "@typescript-eslint/no-unsafe-type-assertion" 8 "../../tests/unit/hooks/useIsBlockedToAddFeed.test.ts" "@typescript-eslint/no-unsafe-type-assertion" 11 "../../tests/unit/hooks/useIsSidebarRouteActive.test.ts" "@typescript-eslint/no-unsafe-type-assertion" 2 @@ -1978,10 +2046,12 @@ "../../tests/unit/markPullRequestsAsDeployedTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 2 "../../tests/unit/navigateAfterExpenseCreateTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 8 "../../tests/unit/navigateAfterOnboardingTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 +"../../tests/unit/navigateAfterOnboardingTest.ts" "no-restricted-imports" 1 "../../tests/unit/navigateToWorkspacesPageTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 14 +"../../tests/unit/navigateToWorkspacesPageTest.ts" "no-restricted-imports" 1 "../../tests/unit/pages/home/GettingStartedSection/GettingStartedSectionTest.tsx" "@typescript-eslint/no-unsafe-type-assertion" 11 -"../../tests/unit/pages/inbox/ConciergeDraftContext.test.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../tests/unit/pages/inbox/report/ReportActionEditMessageContext.test.tsx" "@typescript-eslint/no-unsafe-type-assertion" 2 +"../../tests/unit/pages/inbox/report/ReportActionEditMessageContext.test.tsx" "no-restricted-imports" 1 "../../tests/unit/pages/settings/AddAgentAvatarPageTest.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../tests/unit/pages/settings/AddAgentPageTest.tsx" "@typescript-eslint/no-unsafe-type-assertion" 11 "../../tests/unit/pages/settings/AgentsListRowTest.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 @@ -2017,6 +2087,7 @@ "../../tests/unit/useGroupDraftRestoreTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 14 "../../tests/unit/useIndicatorStatusTest.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../tests/unit/useLastWorkspaceNumberTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 4 +"../../tests/unit/useMarkAsReadTest.ts" "no-restricted-imports" 1 "../../tests/unit/usePersonalDetailSearchSelectorTest.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../tests/unit/usePolicyIndicatorChecksTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 11 "../../tests/unit/usePopoverPositionTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 diff --git a/contributingGuides/CONTRIBUTING.md b/contributingGuides/CONTRIBUTING.md index 449f5ff7a125..8e0cd9a65e59 100644 --- a/contributingGuides/CONTRIBUTING.md +++ b/contributingGuides/CONTRIBUTING.md @@ -4,10 +4,13 @@ Welcome! Thanks for checking out the New Expensify app and taking the time to co ## Getting Started If you would like to become an Expensify contributor, the first step is to read this document in its **entirety**. The second step is to review the README guidelines [here](https://github.com/Expensify/App/blob/main/README.md) to understand our coding philosophy and for a general overview of the code repository (i.e. how to run the app locally, testing, storage, our app philosophy, etc). Please read both documents before asking questions, as it may be covered within the documentation. +### MelvinBot issues (read this) +Many App jobs use [MelvinBot](https://github.com/Expensify/App/blob/main/contributingGuides/HOW_TO_WORK_WITH_MELVINBOT.md) (Melvin), our AI agent that posts the first proposal on an issue. **Do not open a pull request until a proposal is accepted.** Contributors, C+, and internal engineers each have specific responsibilities on these jobs. See **[How to Work with MelvinBot](./HOW_TO_WORK_WITH_MELVINBOT.md)** before working on any issue where Melvin has posted a proposal. + ### Test Accounts You can create as many accounts as needed in order to test your changes directly from [the app](https://new.expensify.com/). An initial account can be created when logging in for the first time, and additional accounts can be created by opening the "New Chat" or "Group Chat" pages via the Global Create menu, inputting a valid email or phone number, and tapping the user's avatar. Do not use Expensify employee or customer accounts for testing. -**Notes**: +**Notes**: 1. When creating test accounts: - Include a `+` (plus sign) in the email address (e.g., matt+1@gmail.com). This marks the account and their associated workspaces as test accounts in Expensify, ensuring Expensify Guides are not assigned to help with account setup. @@ -21,7 +24,7 @@ You can generate multiple test accounts by using a `+` postfix, for example if y #### High Traffic Accounts -All internal engineers, contributors, and C+ members are required to test with a “high traffic” account against the staging or production web servers. Use these Google forms to manage your high-traffic accounts. You'll need to authenticate via Google first. +All internal engineers, contributors, and C+ members are required to test with a “high traffic” account against the staging or production web servers. Use these Google forms to manage your high-traffic accounts. You'll need to authenticate via Google first. 1. [Make an account high-traffic](https://docs.google.com/forms/d/e/1FAIpQLScpiS0Mo-HA5xHPsvDow79yTsMBgF0wjuqc0K37lTK5fheB8Q/viewform) 2. [Remove a high-traffic account](https://docs.google.com/forms/d/e/1FAIpQLSd9_FDav83pnhhtu1KGAKIpf2yttQ_0Bvq1b9nuFM1-wbL11Q/viewform) @@ -40,7 +43,7 @@ We have a shared Slack channel called #expensify-open-source — this channel is That said, we have a small issue with adding users at the moment and we’re working with Slack to try and get this resolved. If you would like to join, [fill out this form](https://forms.gle/Q7hnhUJPnQCK7Fe56) with your email and Upwork profile link. Once resolved, we’ll add you. -Note: Do not send direct messages to the Expensify team in Slack or Expensify Chat, they will not be able to respond. +Note: Do not send direct messages to the Expensify team in Slack or Expensify Chat, they will not be able to respond. Note: if you are hired for an Upwork job and have any job-specific questions, please ask in the GitHub issue or pull request. This will ensure that the person addressing your question has as much context as possible. @@ -48,9 +51,9 @@ Note: if you are hired for an Upwork job and have any job-specific questions, pl If you've found a vulnerability, please email security@expensify.com with the subject `Vulnerability Report` instead of creating an issue. ## Payment for Contributions -We hire and pay external contributors via [Upwork.com](https://www.upwork.com). If you'd like to be paid for contributing, please create an Upwork account, apply for an available job in [GitHub](https://github.com/Expensify/App/issues?q=is%3Aopen+is%3Aissue+label%3A%22Help+Wanted%22), and finally apply for the job in Upwork once your proposal gets selected in GitHub. Please make sure your Upwork profile is **fully verified** before applying, otherwise you run the risk of not being paid. If you think your compensation should be increased for a specific job, you can request a reevaluation by commenting in the Github issue where the Upwork job was posted. +We hire and pay external contributors via [Upwork.com](https://www.upwork.com). If you'd like to be paid for contributing, please create an Upwork account, apply for an available job in [GitHub](https://github.com/Expensify/App/issues?q=is%3Aopen+is%3Aissue+label%3A%22Help+Wanted%22), and finally apply for the job in Upwork once your proposal gets selected in GitHub. Please make sure your Upwork profile is **fully verified** before applying, otherwise you run the risk of not being paid. If you think your compensation should be increased for a specific job, you can request a reevaluation by commenting in the Github issue where the Upwork job was posted. -Please add your Upwork profile link in your GitHub Bio to help ensure prompt payment. If you're using Slack or Expensify for discussions, please add your Upwork profile link **and** your GitHub username in your Slack Title and Expensify Status. +Please add your Upwork profile link in your GitHub Bio to help ensure prompt payment. If you're using Slack or Expensify for discussions, please add your Upwork profile link **and** your GitHub username in your Slack Title and Expensify Status. Payment for your contributions will be made no less than 7 days after the pull request is deployed to production to allow for [regression](https://github.com/Expensify/App/blob/main/contributingGuides/CONTRIBUTING.md#regressions) testing. If you have not received payment after 8 days of the PR being deployed to production, and there are no [regressions](https://github.com/Expensify/App/blob/main/contributingGuides/CONTRIBUTING.md#regressions), please add a comment to the issue mentioning the team member assigned to pay (Look for the melvin-bot "Triggered auto assignment to... (`Awaiting Payment`)" to see who this is). @@ -58,13 +61,13 @@ New contributors are limited to working on one job at a time, **do not submit pr Please be aware that compensation for any support in solving an issue is provided **entirely at Expensify’s discretion**. Personal time or resources applied towards investigating a proposal **will not guarantee compensation**. Compensation is only guaranteed to those who **[propose a solution and get hired for that job](https://github.com/Expensify/App/blob/main/contributingGuides/CONTRIBUTING.md#propose-a-solution-for-the-job)**. We understand there may be cases where a selected proposal may take inspiration from a previous proposal. Unfortunately, it’s not possible for us to evaluate every individual case and we have no process that can efficiently do so. Issues with higher rewards come with higher risk factors so try to keep things civil and make the best proposal you can. Once again, **any information provided may not necessarily lead to you getting hired for that issue or compensated in any way.** -**Important:** Payment amounts are variable, dependent on if there are any [regressions](https://github.com/Expensify/App/blob/main/contributingGuides/CONTRIBUTING.md#regressions). Your PR will be reviewed by a [Contributor+ (C+)](https://github.com/Expensify/App/blob/main/contributingGuides/HOW_TO_BECOME_A_CONTRIBUTOR_PLUS.md) team member and an internal engineer. All tests must pass and all code must pass lint checks before a merge. +**Important:** Payment amounts are variable, dependent on if there are any [regressions](https://github.com/Expensify/App/blob/main/contributingGuides/CONTRIBUTING.md#regressions). Your PR will be reviewed by a [Contributor+ (C+)](https://github.com/Expensify/App/blob/main/contributingGuides/HOW_TO_BECOME_A_CONTRIBUTOR_PLUS.md) team member and an internal engineer. All tests must pass and all code must pass lint checks before a merge. Contributors are eligible to be paid via Expensify 18 months after they were first assigned to a job. More details at [PAYMENT_VIA_EXPENSIFY.md](https://github.com/Expensify/App/blob/main/contributingGuides/PAYMENT_VIA_EXPENSIFY.md) ### Regressions -If a PR causes a regression at any point within the regression period (starting when the code is merged and ending 168 hours (that's 7 days) after being deployed to production): +If a PR causes a regression at any point within the regression period (starting when the code is merged and ending 168 hours (that's 7 days) after being deployed to production): - payments will be issued 7 days after all regressions are fixed (ie: deployed to production) - a 50% penalty will be applied to the Contributor and [Contributor+](https://github.com/Expensify/App/blob/main/contributingGuides/HOW_TO_BECOME_A_CONTRIBUTOR_PLUS.md) for each regression on an issue @@ -88,7 +91,7 @@ In short, a good problem statement makes no mention whatsoever of the desired so **Bad:** > **Problem:** We don't have a car -> +> > **Solution:** Buy a car. @@ -152,8 +155,9 @@ This helps future investigators understand the history and current status of err 3. If you cannot reproduce the problem, pause on this step and add a comment to the issue explaining where you are stuck or that you don't think the issue can be reproduced. ### Propose a solution for the job -4. Proposals must only be posted after the `Help Wanted` label is added. Any proposals submitted beforehand will be ignored and not reviewed. Do not post proposals in Slack. -5. Contributors should **not** submit proposals on issues when they have assigned issues or PRs that are awaiting an action from them. If so, they will be in violation of Rule #1 (Get Shit Done) in our [Code of Conduct](https://github.com/Expensify/App/blob/main/CODE_OF_CONDUCT.md) and will receive a warning. Multiple warnings can lead to removal from the program. +4. Proposals must only be posted after the `Help Wanted` label is added. Any proposals submitted beforehand will be ignored and not reviewed. Do not post proposals in Slack. MelvinBot is an exception: it may post a proposal before `Help Wanted` is applied. + - On issues where [MelvinBot](./HOW_TO_WORK_WITH_MELVINBOT.md) has posted a proposal, review Melvin's proposal using the same standards as contributor proposals. Melvin's proposal is not guaranteed to be the earliest, but it should always be reviewed first because we are spending the money to run it. Follow first-come-first-serve review order for all other proposals after `Help Wanted`. **Do not open a PR** until a proposal is accepted. See [How to Work with MelvinBot](./HOW_TO_WORK_WITH_MELVINBOT.md) for more details. +5. Contributors should **not** submit proposals on issues when they have assigned issues or PRs that are awaiting an action from them. If so, they will be in violation of Rule #1 (Get Shit Done) in our [Code of Conduct](https://github.com/Expensify/App/blob/main/CODE_OF_CONDUCT.md) and will receive a warning. Multiple warnings can lead to removal from the program. 6. After you reproduce the issue, complete the [proposal template here](./PROPOSAL_TEMPLATE.md) and post it as a comment in the corresponding GitHub issue (linked in the Upwork job). - Note: Before submitting a proposal on an issue, be sure to read any other existing proposals. ALL NEW PROPOSALS MUST BE DIFFERENT FROM EXISTING PROPOSALS. The *difference* should be important, meaningful or considerable. 7. Refrain from leaving additional comments until someone from the Contributor-Plus team and / or someone from Expensify provides feedback on your proposal (do not create a pull request yet). @@ -197,18 +201,18 @@ This helps future investigators understand the history and current status of err ### Completing the final checklist 20. Once your PR has been deployed to production, a checklist will automatically be commented in the GH issue. You're required to complete the steps that have your name mentioned before payment will be issued. -21. The items requiring your completion consist of: +21. The items requiring your completion consist of: 1. Proposing steps to take for a regression test to ensure the bug doesn't occur again (For information on how to successfully complete this, head [here](https://github.com/Expensify/App/blob/main/contributingGuides/REGRESSION_TEST_BEST_PRACTICES.md)). 2. Identifying and noting the offending PR that caused the bug (if any). 3. Commenting on the offending PR to note the bug it caused and why (if applicable). - 4. Starting a conversation on if any additional steps should be taken to prevent further bugs similar to the one fixed from occurring again. -22. Once the above items have been successfully completed, then payments will begin to be issued. + 4. Starting a conversation on if any additional steps should be taken to prevent further bugs similar to the one fixed from occurring again. +22. Once the above items have been successfully completed, then payments will begin to be issued. ### Timeline expectations and asking for help along the way - If you have made a change to your pull request and are ready for another review, leave a comment that says "Updated" on the pull request itself. - Please keep the conversation in GitHub, and do not ping individual reviewers in Slack or Upwork to get their attention. - Pull Request reviews can sometimes take a few days. If your pull request has not been addressed after four days, please let us know via the #expensify-open-source Slack channel. -- On occasion, our engineers will need to focus on a feature release and choose to place a hold on the review of your PR. +- On occasion, our engineers will need to focus on a feature release and choose to place a hold on the review of your PR. ### Important note about JavaScript Style - Read our official [JavaScript and React style guide](https://github.com/Expensify/App/blob/main/contributingGuides/STYLE.md). Please refer to our Style Guide before asking for a review. @@ -219,9 +223,9 @@ This helps future investigators understand the history and current status of err ### For external agencies that Expensify partners with Follow all the above above steps and processes. When you find a job you'd like to work on: - Post “I’m from [agency], I’d like to work on this job” - - If no proposals have been submitted by other contributors, BugZero (BZ) team member or an internal engineer will assign the issue to you. - - If there are existing proposals, BZ will put the issue on hold. [Contributor+](https://github.com/Expensify/App/blob/main/contributingGuides/HOW_TO_BECOME_A_CONTRIBUTOR_PLUS.md) will review the existing proposals. If a contributor’s proposal is accepted then the contributor will be assigned to the issue. If not the issue will be assigned to the agency-employee. -- Once assigned follow the steps [here](https://github.com/Expensify/App/blob/main/contributingGuides/CONTRIBUTING.md#propose-a-solution-for-the-job) to submit your proposal + - If no proposals have been submitted by other contributors, BugZero (BZ) team member or an internal engineer will assign the issue to you. + - If there are existing proposals, BZ will put the issue on hold. [Contributor+](https://github.com/Expensify/App/blob/main/contributingGuides/HOW_TO_BECOME_A_CONTRIBUTOR_PLUS.md) will review the existing proposals. If a contributor’s proposal is accepted then the contributor will be assigned to the issue. If not the issue will be assigned to the agency-employee. +- Once assigned follow the steps [here](https://github.com/Expensify/App/blob/main/contributingGuides/CONTRIBUTING.md#propose-a-solution-for-the-job) to submit your proposal ## Guide on Acronyms used within Expensify Communication During communication with Expensify, you will come across a variety of acronyms used by our team. While acronyms can be useful, they cease to be the moment they are not known to the receiver. As such, we wanted to create a list here of our most commonly used acronyms and what they're referring to. Lastly, please never hesitate to ask in Slack or the GH issue if there are any that are not understood/known! @@ -231,8 +235,8 @@ During communication with Expensify, you will come across a variety of acronyms - **LHN:** Left Hand Navigation (Primary navigation modal in Expensify Chat, docked on the left-hand side) - **OP:** Original Post (Most commonly the post in E/App GH issues that reports the bug) - **FAB** Floating Action Button (the + Button that is used to launch flows like 'New Chat', 'Request Money') -- **GBR:** Green Brick Road (UX Design Principle that utilizes green indicators on action items to encourage the user down the optimal path for a given process or task) -- **RBR:** Red Brick Road (UX Design Principle that utilizes red indicators on action items to encourage the user down the optimal path for handling and discovering errors) +- **GBR:** Green Brick Road (UX Design Principle that utilizes green indicators on action items to encourage the user down the optimal path for a given process or task) +- **RBR:** Red Brick Road (UX Design Principle that utilizes red indicators on action items to encourage the user down the optimal path for handling and discovering errors) - **VBA:** Verified Bank Account (Bank account that has been verified as real and belonging to the correct business/individual) - **NAB:** Not a Blocker (An issue that doesn't block progress, but would be nice to not have) - **IOU:** I owe you (used to describe payment requests between users) @@ -241,3 +245,4 @@ During communication with Expensify, you will come across a variety of acronyms - **QA:** Quality Assurance - **GH:** GitHub - **LGTM:*** Looks good to me +- **MelvinBot:** Expensify's AI agent for App GitHub issues (posts proposals, implements accepted solutions). See [How to Work with MelvinBot](./HOW_TO_WORK_WITH_MELVINBOT.md). diff --git a/contributingGuides/HOW_TO_BECOME_A_CONTRIBUTOR_PLUS.md b/contributingGuides/HOW_TO_BECOME_A_CONTRIBUTOR_PLUS.md index 9993755274e5..9922324c1ea8 100644 --- a/contributingGuides/HOW_TO_BECOME_A_CONTRIBUTOR_PLUS.md +++ b/contributingGuides/HOW_TO_BECOME_A_CONTRIBUTOR_PLUS.md @@ -2,6 +2,7 @@ C+ are contributors who are experienced at working with Expensify and have gained the confidence of the internal Expensify team. Accordingly, they are allocated additional opportunities and responsibilities: - They review proposed solutions in external GitHub issues, validate them, and make recommendations to an internal Contributor Manager Engineer (CME). - Once a proposal has been accepted by a CME, the C+ will be the first person to review the pull request associated with that proposal. + - On [MelvinBot issues](./HOW_TO_WORK_WITH_MELVINBOT.md), the C+ reviews Melvin's proposal, recommends it to the CME, asks Melvin to implement after CME approval, and owns the PR as the human author before CME PR review. ## Why would someone want to be a C+ - C+ are compensated the same price as the contributor for reviewing proposals and the associated PR. (ie. if a job is listed at $250, that’s how much the C+ will make if they review both the proposals and PR). If regressions are found that should have* been caught after the PR has been approved, C+ payment is reduced by 50% for each regression found. diff --git a/contributingGuides/HOW_TO_WORK_WITH_MELVINBOT.md b/contributingGuides/HOW_TO_WORK_WITH_MELVINBOT.md new file mode 100644 index 000000000000..da3b0101a5d9 --- /dev/null +++ b/contributingGuides/HOW_TO_WORK_WITH_MELVINBOT.md @@ -0,0 +1,105 @@ +# Working with MelvinBot on App Issues + +MelvinBot (Melvin) is Expensify's AI agent for App GitHub issues. On many open-source jobs, Melvin posts the first proposal automatically. This guide explains how contributors, Contributor+ (C+), and internal engineers work together on those issues. + +Clearly documenting the process helps us move efficiently by preventing all of us from wasting time on incorrect solutions. Internal engineering approval is required on Melvinbot proposals because we usually have the most context to know whether a solution aligns with our best practices. + +For general contributor workflow (proposals, payment, PR standards), see [CONTRIBUTING.md](./CONTRIBUTING.md). For C+ responsibilities, see [HOW_TO_BECOME_A_CONTRIBUTOR_PLUS.md](./HOW_TO_BECOME_A_CONTRIBUTOR_PLUS.md). + +## Roles + +| Role | Responsibility on Melvin issues | +|------|----------------------------------| +| **MelvinBot** | Posts the first proposal on the issue. Implements the accepted solution when asked. Opens a draft PR with MelvinBot as the GitHub author. | +| **Contributor** | May submit their own proposal if they have a meaningfully different approach. Must not open a PR until a proposal is accepted and they are hired on the job. | +| **Contributor+ (C+)** | Reviews proposals (Melvin's and any contributor proposals) using the same standards as any other job. Recommends acceptance to the CME. After the CME approves, asks Melvin to implement, then owns the PR as the human author before sending it for final review. | +| **Contributor Manager Engineer (CME)** | Reviews and approves proposals (same as any other App job). After implementation, reviews and merges the PR. | + +Only Expensify employees, C+ members, and backend contributors can trigger Melvin with `@MelvinBot` comments on GitHub. + +## Workflow overview + +``` +Issue opened → Melvin posts proposal → C+ reviews proposal(s) + → C+ recommends acceptance (🎀👀🎀) → CME approves proposal + → C+ asks Melvin to implement (only after CME approval) + → Not accepted: C+ or CME explains why +→ Melvin opens draft PR → C+ tweaks, tests, posts PR body/checklist in a PR comment, asks Melvin to apply it, self-reviews (including Reviewer checklist) +→ C+ submits PR for review as human author → CME reviews and merges (PR body and/or C+ PR comment) +``` + +## Phase 1: Proposal review + +Follow the **same proposal review process** as any other App job, but remember to review the MelvinBot proposal first, and then review in first-come-first-serve review order for proposals posted after the `Help Wanted` is applied (see [CONTRIBUTING.md](./CONTRIBUTING.md#propose-a-solution-for-the-job)). + +1. Use the [proposal template](./PROPOSAL_TEMPLATE.md) criteria: clear root cause, concrete solution, no code diffs. +2. C+ reviews Melvin's proposal with the same rigor as a contributor proposal. Do not approve proposals that lack a satisfying root-cause explanation. +3. Contributors may still post proposals if they have a **meaningfully different** approach (see [CONTRIBUTING.md](./CONTRIBUTING.md#propose-a-solution-for-the-job)). +4. **No one opens a pull request** until a proposal is accepted by the CME (same as any other App job; see [CONTRIBUTING.md](./CONTRIBUTING.md#propose-a-solution-for-the-job)). +5. If Melvin's proposal is not acceptable, the C+ can review proposals from other contributors, or iterate with Melvin by tagging `@MelvinBot` on the issue, explaining the needed changes, and asking it to update its proposal or post a new one until it is satisfactory. + +### C+ reviews and recommends + +The C+ reviews Melvin's proposal with the same rigor as a contributor proposal. Follow the [proposal template](./PROPOSAL_TEMPLATE.md) review instructions. + +When the C+ is satisfied with Melvin's proposal, they recommend it to the CME by posting `🎀👀🎀` on the issue. That triggers CME assignment for the job. + +### After the CME approves the proposal + +Once the assigned CME has approved Melvin's proposal (the same acceptance step as for any contributor proposal), the C+ comments on the issue asking Melvin to implement. Include a link to the specific proposal comment so Melvin implements the latest version (Melvin may post a new proposal when asked to update). For example: + +``` +@MelvinBot please implement your proposal at https://github.com/Expensify/App/issues/12345#issuecomment-6789012345 +``` + +Adjust the wording if needed, but the comment must mention `@MelvinBot`, link to the proposal comment, and clearly request implementation. + +## Phase 2: C+ owns the pull request + +Melvin opens a draft PR linked to the issue. MelvinBot remains the GitHub author but the C+ is the human author. Most C+ members will **not be able to edit the PR description directly** (GitHub only allows the author or users with write access to edit a pull request body). + +Before requesting final review, the assigned C+ must: + +1. **Manually tweak the PR** if Melvin's implementation needs corrections. Comment on the pull request, tag `@MelvinBot`, and explain the needed changes so Melvin can update the code. +2. **Test the change** on all required platforms (see [CONTRIBUTING.md](./CONTRIBUTING.md#make-sure-you-can-test-on-all-platforms)). +3. **Complete every step** in the PR Author Checklist (see [Updating the PR description and checklist](#updating-the-pr-description-and-checklist) below). Screenshots and videos may live only in the Reviewer Checklist section of your PR comment; you do not need to duplicate them in the Author Checklist. +4. **Self-review the code** against [PR Review Guidelines](./PR_REVIEW_GUIDELINES.md), [PR Authoring & Reviewing Best Practices](./PR_AUTHOR_REVIEWER_BEST_PRACTICES.md), and complete the [Reviewer Checklist](./REVIEWER_CHECKLIST.md) as part of that self-review. + +The C+ is accountable for the PR quality, the same as any contributor who authored a PR. + +### Updating the PR description and checklist + +C+ members who cannot edit the PR body directly should use this workaround: + +1. On the **pull request**, post a comment with the exact content you want in the PR description. Put the full body inside a `
` block so the PR thread stays readable. Include the complete [PR template](https://github.com/Expensify/App/blob/main/.github/PULL_REQUEST_TEMPLATE.md) when possible: Explanation of Change, Fixed Issues, Tests, Offline tests, QA Steps, a fully checked PR Author Checklist, and Screenshots/Videos sections as needed. +2. In the same comment, ask Melvin to copy that content into the pull request body, for example: + +``` +@MelvinBot please set the PR body to the content in the details section above +``` + +To update only the checklist, etc, post the section in a `
` block and ask Melvin to set just that portion of the PR body. + +3. Confirm Melvin updated the PR description before marking the pull request ready for review. + +The C+ PR comment is the **source of truth** for what was tested and checked. CMEs may review that comment directly when the PR body is incomplete, out of date, or hard to verify. + +## Phase 3: CME review and merge + +After the C+ submits the PR for review: + +1. A **CME** (internal engineer) is assigned to review and merge. +2. The CME follows the normal internal review process. Review the **PR body** when Melvin has applied the C+ content, or failing that, review the C+ **PR comment** (with the `
` block) directly for testing steps and checklist completion. +3. Payment and regression checklists follow the standard rules in [CONTRIBUTING.md](./CONTRIBUTING.md). + +### No additional contributor or C+ PR review + +We intentionally do **not** add a second round of contributor or C+ PR review on top of the CME review. Prior discussions did not surface a problem that extra review would solve, and an additional review step would add cost and delay without clear benefit. The C+ self-review before submission, combined with CME review, is the intended quality gate. + +## Related documentation + +- [CONTRIBUTING.md](./CONTRIBUTING.md) — full contributor workflow +- [PROPOSAL_TEMPLATE.md](./PROPOSAL_TEMPLATE.md) — proposal format and C+ review instructions +- [Reviewer Checklist](./REVIEWER_CHECKLIST.md) — C+ completes this during self-review before submitting for CME review +- [AI Etiquette](./AI_ETIQUETTE.md) — accountability for AI-assisted work +- [AI Reviewer philosophy](./philosophies/AI-REVIEWER.md) — automated PR feedback vs human review diff --git a/contributingGuides/INTERACTION_MANAGER.md b/contributingGuides/INTERACTION_MANAGER.md index 08008b0b42ad..5d152fc04532 100644 --- a/contributingGuides/INTERACTION_MANAGER.md +++ b/contributingGuides/INTERACTION_MANAGER.md @@ -29,7 +29,37 @@ On top of TransitionTracker, existing APIs gain transition-aware callbacks: This makes the code self-descriptive: instead of a generic `runAfterInteractions`, each call site says exactly what it's waiting for and why. -> **Note:** `TransitionTracker.runAfterTransitions` is an internal primitive. Application code should use the higher-level APIs (`Navigation`, `useConfirmModal`, etc.) rather than importing TransitionTracker directly. +### What TransitionTracker actually tracks + +TransitionTracker registers transitions **only** in the following cases: + +1. **Navigation transitions** — screen push/pop/replace animations (via `transitionStart`/`transitionEnd` events in `ScreenLayout`) +2. **Modal transitions** — modal open/close animations (via `ReanimatedModal`) +3. **Keyboard transitions** — keyboard dismiss animations (via `KeyboardUtils.dismiss`) + +### When to use `runAfterTransitions` directly + +Prefer higher-level APIs (`Navigation` with `afterTransition`/`waitForTransition`, `KeyboardUtils`, `useConfirmModal`) whenever possible. Use `TransitionTracker.runAfterTransitions` directly **only** when: + +- There is no specific navigation/keyboard/modal action you can attach a callback to, or +- There are too many concurrent actions and it's impossible to bind the callback to a particular one. + +### When NOT to use `runAfterTransitions` + +If you are not waiting for any of the tracked events (navigation, modal, keyboard), adding `runAfterTransitions` is meaningless - TransitionTracker will have no active transitions and the callback will fire synchronously in the same tick. For example, scroll events, layout animations, or Lottie playback are **not** tracked, so wrapping post-scroll logic in `runAfterTransitions` provides no benefit. + +### How `runAfterTransitions` works + +1. If there are ongoing transitions at the time of the call, the callback is enqueued and waits until all current transitions complete. +2. If there are no ongoing transitions, the callback executes in the same tick. + +### The same-tick race condition + +Sometimes a transition starts in the same tick as the `runAfterTransitions` call. Because TransitionTracker hasn't registered the transition yet, the callback fires immediately - before the transition even begins. + +To fix this, pass the `waitForUpcomingTransition` flag along with the callback. This tells TransitionTracker to wait for a transition that hasn't started yet, ensuring the callback truly runs after the transition completes. + +> **Important:** `waitForUpcomingTransition` waits up to 1 second for a transition to begin. If no transition starts within that window, the callback fires anyway — effectively acting as a 1-second timeout. When using this flag, always make sure that TransitionTracker actually picks up the transition (i.e. the transition is registered within the 1-second window). Otherwise you're not waiting for a real transition, just adding an arbitrary delay. ## How The migration is split into 9 issues. Current status of the migration can be found in the parent Github issue [here](https://github.com/Expensify/App/issues/71913). diff --git a/cspell.json b/cspell.json index 8f866b8f6afb..80ef1951f2c2 100644 --- a/cspell.json +++ b/cspell.json @@ -203,6 +203,8 @@ "Marqeta", "McAfee", "Michelina", + "Melvin", + "MelvinBot", "Menlo", "Microtransaction", "Miniwarehouses", diff --git a/docs/articles/expensify-classic/connections/VAT-IT.md b/docs/articles/expensify-classic/connections/VAT-IT.md index 29465eaeb874..f55b6e7d6e9f 100644 --- a/docs/articles/expensify-classic/connections/VAT-IT.md +++ b/docs/articles/expensify-classic/connections/VAT-IT.md @@ -27,13 +27,13 @@ Businesses can reclaim VAT on various expenses, including: # How VAT Refunds Work -VAT refund regulations vary by country and expense type, making the process complex. Global VaTax simplifies this by handling: +VAT refund regulations vary by country and expense type, making the process complex. VAT IT simplifies this by handling: - VAT analysis and calculation - Expense classification based on VAT eligibility - Report preparation and submission to tax authorities -By syncing Expensify with Global VaTax, businesses can automate VAT reporting and reclaim eligible expenses efficiently. +By syncing Expensify with VAT IT, businesses can automate VAT reporting and reclaim eligible expenses efficiently. --- @@ -52,6 +52,6 @@ Once VAT IT prepares your VAT reclaim documents in the required languages, they # Tracking Your VAT Reclaim -After submission, you can monitor the progress of your VAT reclaim via the **Submission Analysis Report** in Global VaTax. +After submission, you can monitor the progress of your VAT reclaim via the **Submission Analysis Report** in VAT IT. By integrating Expensify with VAT IT, businesses can maximize VAT recovery while reducing administrative burden. diff --git a/docs/articles/new-expensify/connect-credit-cards/Assign-Company-Cards.md b/docs/articles/new-expensify/connect-credit-cards/Assign-Company-Cards.md index 5ccfe82e069b..2ce67abacb34 100644 --- a/docs/articles/new-expensify/connect-credit-cards/Assign-Company-Cards.md +++ b/docs/articles/new-expensify/connect-credit-cards/Assign-Company-Cards.md @@ -28,15 +28,19 @@ If you haven’t set up a feed yet, learn how to set one up: 3. Select the card connection name. 4. Click **Assign card** on the card you want to assign. 5. Enter the assignee's name, email address, or phone number. -6. Review the details and click **Assign card** to finalize the assignment. +6. If needed, update the **Transaction start date** to choose the date from which Expensify starts importing transactions for the card. +7. Click **Assign card** to finalize the assignment. --- ## What happens after you assign company cards - Posted transactions from the assigned card import automatically into the member's account as expenses. + - Expensify imports transactions from the **Transaction start date** onward. - The imported company card expenses are added to reports and submitted according to Workspace rules. +--- + # FAQ ## Can I assign a company card to someone who is not a member of the Workspace? @@ -50,3 +54,7 @@ Yes. A member can have multiple company cards assigned. Each card’s transactio ## Can members unassign their own company cards? No. Only Workspace Admins and their Copilots can assign or reassign company cards. + +## Can I change the transaction start date on an assigned card? + +Yes. You can update the **Transaction start date** at any time from the assigned card's details. The **Transaction start date** determines the date from which Expensify imports transactions for the card. diff --git a/docs/articles/new-expensify/connections/quickbooks-online/Configure-Quickbooks-Online.md b/docs/articles/new-expensify/connections/quickbooks-online/Configure-Quickbooks-Online.md index b7d71247a78b..1c55091863b7 100644 --- a/docs/articles/new-expensify/connections/quickbooks-online/Configure-Quickbooks-Online.md +++ b/docs/articles/new-expensify/connections/quickbooks-online/Configure-Quickbooks-Online.md @@ -75,8 +75,8 @@ To manage automation and other connection preferences: - **Sync reimbursed reports**: - If marked as paid in QuickBooks Online, the report will show as reimbursed in Expensify - If reimbursed via ACH in Expensify, the status will sync to paid in QuickBooks -- **QuickBooks bill payment account**: Select where paid expense reports are stored -- **QuickBooks invoice collections account**: Select where paid invoices are stored +- **QuickBooks bill payment account**: Select where payments for expense reports are recorded +- **QuickBooks invoice collections account**: Select where payments for invoices are recorded --- diff --git a/docs/articles/new-expensify/domains/Domain-Migration.md b/docs/articles/new-expensify/domains/Domain-Migration.md index 04f7247bc12d..7d06e6adbdcc 100644 --- a/docs/articles/new-expensify/domains/Domain-Migration.md +++ b/docs/articles/new-expensify/domains/Domain-Migration.md @@ -21,7 +21,7 @@ This is the recommended approach when possible. Concierge can typically perform an automatic Domain Migration if: -- The old domain or new domain is verified +- The old domain is verified - Email addresses match a 1:1 format, such as `name@olddomain.com` to `name@newdomain.com` - Members have not already created separate accounts under the new email address @@ -65,7 +65,8 @@ Yes. If done correctly, members keep their full expense history, reports, and ap ## Do I need to verify the new domain before migrating? -Yes. You must claim and verify the new domain before transferring domain control or updating member login emails. +- If you are migrating your domain automatically with assistance from Concierge, you will not need to verify the new domain. It is preferred that you do not add or verify the new domain in Expensify. +- If you are migrating your domain manually, you will need to verify the new domain. ## What happens to the Expensify Card during Domain Migration? diff --git a/docs/articles/new-expensify/reports-and-expenses/Getting-Started-with-the-Spend-Page.md b/docs/articles/new-expensify/reports-and-expenses/Getting-Started-with-the-Spend-Page.md index 819b9d95edf9..dd39d268f9c1 100644 --- a/docs/articles/new-expensify/reports-and-expenses/Getting-Started-with-the-Spend-Page.md +++ b/docs/articles/new-expensify/reports-and-expenses/Getting-Started-with-the-Spend-Page.md @@ -104,9 +104,9 @@ Filters help you narrow down expenses or reports so you can find exactly what yo You can filter your **expenses** and **reports** using dropdowns, search bars, and chips. -- **Expenses:** can be filtered by `Date`, `Merchant`, `Category`, `Tag`, `Amount`, `Reimbursable`, `Billable`, `Status` (e.g. Unreported, Deleted) +- **Expenses:** can be filtered by `Date`, `Merchant`, `Category`, `Tag`, `Amount`, `Reimbursable`, `Billable`, `Status` (e.g. Unreported, Deleted), and `Exported to`. -- **Reports**: can be filtered by `Submitter`, `Workspace`, `Report Status` (e.g. Draft, Outstanding, Approved, Paid), `Date` +- **Reports**: can be filtered by `Submitter`, `Workspace`, `Report Status` (e.g. Draft, Outstanding, Approved, Paid), `Date`, and `Exported to`. To view other available filters, click into the **search box** on the **Spend** page while viewing **Reports** or **Expenses** under **Explore**. diff --git a/docs/articles/new-expensify/reports-and-expenses/How-to-Export-Expenses.md b/docs/articles/new-expensify/reports-and-expenses/How-to-Export-Expenses.md index e17e44e77517..b69b1547a41f 100644 --- a/docs/articles/new-expensify/reports-and-expenses/How-to-Export-Expenses.md +++ b/docs/articles/new-expensify/reports-and-expenses/How-to-Export-Expenses.md @@ -38,16 +38,12 @@ You'll receive the exported CSV file in a message from Concierge. ## What export templates can I choose from? -Expensify offers pre-built export templates, or you can build your own custom export template. All available templates will appear in the menu when you export an expense. +Expensify offers pre-built export templates, or you can build your own custom export template. - **Basic export** - Essential fields including date, amount, merchant, category, and receipt URL. - **All Data - expense level** - One row per expense with all available data fields. - - **custom templates** - Any custom template created by you or your Workspace Admin, if available. - - **Custom templates** - Any custom template created by you or your Workspace Admin, if available. -**Note** Currently, it's not possible to build custom export templates on New Expensify, they can only be created on Expensify Classic. However, once built they will be available on New Expensify when exporting expenses. [Learn how to build a custom export template in Expensify Classic](/articles/expensify-classic/spending-insights/Export-Expenses-And-Reports#create-a-custom-export-template). - ## Where do I find the exported file? For the Basic Export template, the file downloads directly to your device. For all other templates, Concierge sends the file to you in a direct message. Open your Concierge chat in the **Inbox** to find it. @@ -58,8 +54,6 @@ If your Workspace Admin has created custom export templates, you can select one ## Why don't I see certain export templates? -Report-level templates (such as All Data - Report Level Export) only appear when exporting from **Spend > Reports** page with full reports selected. When exporting individual expenses from the Expenses page, only expense-level templates are available. - If all selected expenses are deleted, only the **Basic export** template is available. Other export templates require a report, and deleted expenses are not associated with any report. ## The data looks wrong in Excel. How can I fix it? diff --git a/docs/articles/new-expensify/reports-and-expenses/Use-Search-Operators-to-Filter-and-Analyze.md b/docs/articles/new-expensify/reports-and-expenses/Use-Search-Operators-to-Filter-and-Analyze.md index 13f38e6c5ed3..f06cbb0880b4 100644 --- a/docs/articles/new-expensify/reports-and-expenses/Use-Search-Operators-to-Filter-and-Analyze.md +++ b/docs/articles/new-expensify/reports-and-expenses/Use-Search-Operators-to-Filter-and-Analyze.md @@ -77,6 +77,7 @@ You can use the following operators to filter reports: - `report-id:` – unique report reference - `status:` – draft, outstanding, approved, paid, done - `submitted:` / `approved:` / `paid:` / `exported:` – supports absolute or relative dates, and comparisons for date ranges (e.g., `submitted>=2024-01-01 submitted<=2024-01-31`) +- `exported-to:` – filter by where reports or expenses were exported, such as a connected accounting integration. - `title:` – report title - `total:` – total amount with relative comparisons - `withdrawn:` – ACH withdrawal date @@ -84,7 +85,7 @@ You can use the following operators to filter reports: - `action:` – blocking report action, e.g. `action:approve` **Example query:** -`status:paid exported<=2026-01-01` +`status:paid exported<=2026-01-01 exported-to:xero` --- diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index f21fb37f5600..785a864e6500 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -23,7 +23,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 9.4.5 + 9.4.6 CFBundleSignature ???? CFBundleURLTypes @@ -44,7 +44,7 @@ CFBundleVersion - 9.4.5.0 + 9.4.6.0 FullStory OrgId diff --git a/ios/NotificationServiceExtension/Info.plist b/ios/NotificationServiceExtension/Info.plist index f0541fc79c1c..e721b9d49276 100644 --- a/ios/NotificationServiceExtension/Info.plist +++ b/ios/NotificationServiceExtension/Info.plist @@ -11,9 +11,9 @@ CFBundleName $(PRODUCT_NAME) CFBundleShortVersionString - 9.4.5 + 9.4.6 CFBundleVersion - 9.4.5.0 + 9.4.6.0 NSExtension NSExtensionPointIdentifier diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 1d385f71f0f0..480cdc510747 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -4088,7 +4088,7 @@ PODS: - ReactCommon/turbomodule/core - SocketRocket - Yoga - - RNSVG (15.12.1): + - RNSVG (15.15.5): - boost - DoubleConversion - fast_float @@ -4114,10 +4114,10 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - RNSVG/common (= 15.12.1) + - RNSVG/common (= 15.15.5) - SocketRocket - Yoga - - RNSVG/common (15.12.1): + - RNSVG/common (15.15.5): - boost - DoubleConversion - fast_float @@ -4808,7 +4808,7 @@ SPEC CHECKSUMS: GTMAppAuth: f69bd07d68cd3b766125f7e072c45d7340dea0de GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6 GzipSwift: 893f3e48e597a1a4f62fafcb6514220fcf8287fa - hermes-engine: c57209f580d9916632aeb0aa3851b1b90b47859f + hermes-engine: a0c087309de5ae9b2297a2cd8acfa9c53de099ee libavif: 84bbb62fb232c3018d6f1bab79beea87e35de7b7 libdav1d: 23581a4d8ec811ff171ed5e2e05cd27bad64c39f libwebp: 02b23773aedb6ff1fd38cec7a77b81414c6842a8 @@ -4938,7 +4938,7 @@ SPEC CHECKSUMS: RNScreens: 2b6107925ee4e14a9b2eb0dfb52fe25223aa64d7 RNSentry: f73f4da92e4c20841ab16e1fa22fc289bc2f9f4e RNShare: 1c1fde2c4134b9cf220ffebbd6df9c414036d382 - RNSVG: 74eb75bd44d62ba9969941e80d8f9832971c681f + RNSVG: 14e21dacbded6cfa51becd6076c5921aa52a6992 RNWorklets: 165079e6aa8caad7a05156ec2161fd4c52f9b745 SDWebImage: 16309af6d214ba3f77a7c6f6fdda888cb313a50a SDWebImageAVIFCoder: afe194a084e851f70228e4be35ef651df0fc5c57 diff --git a/ios/ShareViewController/Info.plist b/ios/ShareViewController/Info.plist index 44d8056abf00..bef8c163e2df 100644 --- a/ios/ShareViewController/Info.plist +++ b/ios/ShareViewController/Info.plist @@ -11,9 +11,9 @@ CFBundleName $(PRODUCT_NAME) CFBundleShortVersionString - 9.4.5 + 9.4.6 CFBundleVersion - 9.4.5.0 + 9.4.6.0 NSExtension NSExtensionAttributes diff --git a/knip.json b/knip.json index 2df7e9791c2d..1f6121995c89 100644 --- a/knip.json +++ b/knip.json @@ -28,7 +28,7 @@ ".storybook/**/*.{js,ts,tsx}", ".github/actions/javascript/**/*.ts" ], - "ignore": [".github/actions/**/index.js", "tests/perf-test/**", "web/snippets/gib.js"], + "ignore": [".github/actions/**/index.js", "tests/perf-test/**", "web/snippets/gib.js", "src/libs/actions/connections/FinancialForce.ts"], "eslint": { "config": ["config/eslint/eslint.config.mjs", "eslint.changed.config.mjs"] }, diff --git a/package-lock.json b/package-lock.json index 620ab9bee353..c66c94d2fc9b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "9.4.5-0", + "version": "9.4.6-0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "9.4.5-0", + "version": "9.4.6-0", "hasInstallScript": true, "license": "MIT", "workspaces": [ @@ -120,7 +120,7 @@ "react-native-localize": "^3.5.4", "react-native-nitro-modules": "0.35.0", "react-native-nitro-sqlite": "9.6.0", - "react-native-onyx": "3.0.80", + "react-native-onyx": "3.0.83", "react-native-pager-view": "8.0.0", "react-native-pdf": "7.0.2", "react-native-permissions": "^5.4.0", @@ -132,7 +132,7 @@ "react-native-safe-area-context": "5.6.2", "react-native-screens": "4.25.0", "react-native-share": "11.0.2", - "react-native-svg": "15.12.1", + "react-native-svg": "15.15.5", "react-native-tab-view": "^4.3.0", "react-native-url-polyfill": "^2.0.0", "react-native-view-shot": "5.1.0", @@ -243,7 +243,7 @@ "dotenv": "^16.0.3", "eslint": "^9.36.0", "eslint-config-airbnb-typescript": "^18.0.0", - "eslint-config-expensify": "3.0.2", + "eslint-config-expensify": "3.0.3", "eslint-config-prettier": "^9.1.0", "eslint-plugin-file-progress": "3.0.1", "eslint-plugin-jest": "^29.0.1", @@ -23200,14 +23200,13 @@ } }, "node_modules/eslint-config-expensify": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/eslint-config-expensify/-/eslint-config-expensify-3.0.2.tgz", - "integrity": "sha512-ulH1Gl3Uu2fh0E918Rt2R76nMidY1BuxezBBtT20+v2K8O/bFs5v83oX1cwvg3Kz97anhf64aqpA2h2KIG8p9w==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/eslint-config-expensify/-/eslint-config-expensify-3.0.3.tgz", + "integrity": "sha512-xPsrTYCE/6AthZ5rIyXCh5HjNdUXNrDnfpz3AoUmbepTAa6XS51ulZjuyoyGnAfy092RLuKi2dXgOZLmkHN1CA==", "dev": true, "license": "ISC", "dependencies": { "@babel/eslint-parser": "^7.28.4", - "@lwc/eslint-plugin-lwc": "^3.2.0", "@typescript-eslint/parser": "^8.44.1", "@typescript-eslint/utils": "^8.44.1", "confusing-browser-globals": "^1.0.11", @@ -35673,9 +35672,9 @@ } }, "node_modules/react-native-onyx": { - "version": "3.0.80", - "resolved": "https://registry.npmjs.org/react-native-onyx/-/react-native-onyx-3.0.80.tgz", - "integrity": "sha512-rXcHgLi0V7LkkPsim7P+ULQKHCWzWqER+Hx4fDmt+omwtVD3n0LpuV63Fq91/g8cEl6PT4okmYseMVDUUSQPLA==", + "version": "3.0.83", + "resolved": "https://registry.npmjs.org/react-native-onyx/-/react-native-onyx-3.0.83.tgz", + "integrity": "sha512-/OIwvQeXkVaKNsdHWbS1T/16o0iWOlX5KOMF6USKq3ePRVfhBzEOczNgtchAZ5MDStqnZMLNbOsTLwFjLJu56w==", "license": "MIT", "dependencies": { "ascii-table": "0.0.9", @@ -35942,14 +35941,13 @@ } }, "node_modules/react-native-svg": { - "version": "15.12.1", - "resolved": "https://registry.npmjs.org/react-native-svg/-/react-native-svg-15.12.1.tgz", - "integrity": "sha512-vCuZJDf8a5aNC2dlMovEv4Z0jjEUET53lm/iILFnFewa15b4atjVxU6Wirm6O9y6dEsdjDZVD7Q3QM4T1wlI8g==", + "version": "15.15.5", + "resolved": "https://registry.npmjs.org/react-native-svg/-/react-native-svg-15.15.5.tgz", + "integrity": "sha512-L4go5jA+GWutdJ/JucuN20cjAbMg1HmMtAP+wZ+3JLCf6Jd0bhXQHxciRP/AQm/FlrIEZwkMcHNZP+FXAiic0w==", "license": "MIT", "dependencies": { "css-select": "^5.1.0", - "css-tree": "^1.1.3", - "warn-once": "0.1.1" + "css-tree": "^1.1.3" }, "peerDependencies": { "react": "*", diff --git a/package.json b/package.json index a6238f2779c4..f6370118ed66 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "9.4.5-0", + "version": "9.4.6-0", "author": "Expensify, Inc.", "homepage": "https://new.expensify.com", "description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.", @@ -193,7 +193,7 @@ "react-native-localize": "^3.5.4", "react-native-nitro-modules": "0.35.0", "react-native-nitro-sqlite": "9.6.0", - "react-native-onyx": "3.0.80", + "react-native-onyx": "3.0.83", "react-native-pager-view": "8.0.0", "react-native-pdf": "7.0.2", "react-native-permissions": "^5.4.0", @@ -205,7 +205,7 @@ "react-native-safe-area-context": "5.6.2", "react-native-screens": "4.25.0", "react-native-share": "11.0.2", - "react-native-svg": "15.12.1", + "react-native-svg": "15.15.5", "react-native-tab-view": "^4.3.0", "react-native-url-polyfill": "^2.0.0", "react-native-view-shot": "5.1.0", @@ -316,7 +316,7 @@ "dotenv": "^16.0.3", "eslint": "^9.36.0", "eslint-config-airbnb-typescript": "^18.0.0", - "eslint-config-expensify": "3.0.2", + "eslint-config-expensify": "3.0.3", "eslint-config-prettier": "^9.1.0", "eslint-plugin-file-progress": "3.0.1", "eslint-plugin-jest": "^29.0.1", diff --git a/patches/react-native-draggable-flatlist/details.md b/patches/react-native-draggable-flatlist/details.md index 8afd4dc971c9..0f72087f6309 100644 --- a/patches/react-native-draggable-flatlist/details.md +++ b/patches/react-native-draggable-flatlist/details.md @@ -14,3 +14,11 @@ - Upstream PR/issue: https://github.com/computerjazz/react-native-draggable-flatlist/pull/544 - E/App issue: 🛑 - PR Introducing Patch: [#55066](https://github.com/Expensify/App/pull/55066) + + +### [react-native-draggable-flatlist+4.0.3+003+fix-ios-autoscroll-feedback.patch](react-native-draggable-flatlist+4.0.3+003+fix-ios-autoscroll-feedback.patch) + +- Reason: On iOS, `scrollToOffset({animated: true})` does not emit intermediate `onScroll` events, so the `scrollOffset` shared value never updates mid-scroll. This blocks the autoscroll feedback loop (`hasScrolledToTarget` stays false), preventing waypoint reordering beyond the visible viewport. The fix manually advances `scrollOffset` after each `scrollToOffset` call to keep the loop alive. +- Upstream PR/issue: https://github.com/computerjazz/react-native-draggable-flatlist/issues/509 +- E/App issue: [#87362](https://github.com/Expensify/App/issues/87362) +- PR Introducing Patch: [#90617](https://github.com/Expensify/App/pull/90617) diff --git a/patches/react-native-draggable-flatlist/react-native-draggable-flatlist+4.0.3+003+fix-ios-autoscroll-feedback.patch b/patches/react-native-draggable-flatlist/react-native-draggable-flatlist+4.0.3+003+fix-ios-autoscroll-feedback.patch new file mode 100644 index 000000000000..4e331c5b7383 --- /dev/null +++ b/patches/react-native-draggable-flatlist/react-native-draggable-flatlist+4.0.3+003+fix-ios-autoscroll-feedback.patch @@ -0,0 +1,13 @@ +diff --git a/node_modules/react-native-draggable-flatlist/src/hooks/useAutoScroll.tsx b/node_modules/react-native-draggable-flatlist/src/hooks/useAutoScroll.tsx +index 4e30bca..b1a2f3e 100644 +--- a/node_modules/react-native-draggable-flatlist/src/hooks/useAutoScroll.tsx ++++ b/node_modules/react-native-draggable-flatlist/src/hooks/useAutoScroll.tsx +@@ -89,6 +89,8 @@ export function useAutoScroll() { + function scrollToInternal(offset: number) { + if (flatlistRef && "current" in flatlistRef) { + flatlistRef.current?.scrollToOffset({ offset, animated: true }); ++ // Manually advance scrollOffset so the autoscroll feedback loop is not blocked on iOS. ++ scrollOffset.value = offset; + } + } + diff --git a/patches/react-native/details.md b/patches/react-native/details.md index 4d5f5e2dc67c..58b04ce0b32e 100644 --- a/patches/react-native/details.md +++ b/patches/react-native/details.md @@ -284,3 +284,11 @@ - Upstream PR/issue: https://github.com/facebook/react-native/issues/53128 - E/App issue: https://github.com/Expensify/App/issues/91292 - PR introducing patch: https://github.com/Expensify/App/pull/91736 + + +### [react-native+0.83.1+038+fix-nil-BlobModule-crash-APP-8BM.patch](react-native+0.83.1+038+fix-nil-BlobModule-crash-APP-8BM.patch) + +- Reason: Fixes a fatal iOS crash (APP-8BM) in HybridApp where `RCTNetworking`'s default URL-request-handler provider builds its handler list using an Objective-C array literal (`@[...]`) with `[moduleRegistry moduleForName:"BlobModule"]` at index 3. During OldDot↔NewDot bridge transitions, the `__weak _turboModuleRegistry` in `RCTModuleRegistry` is zeroed by ARC at the start of `TurboModuleManager` dealloc — before `[RCTNetworking invalidate]` clears the handler cache — leaving a window where a concurrent in-flight network request calls `prioritizedHandlers`, finds the cache empty, and tries to rebuild it with a nil `BlobModule`. Since `@[…]` compiles to `+[NSArray arrayWithObjects:count:]` which raises `NSInvalidArgumentException` on any nil element, the crash is fatal. The fix replaces the literal with an `NSMutableArray` built from the three always-non-nil handlers (`RCTHTTPRequestHandler`, `RCTDataRequestHandler`, `RCTFileRequestHandler`) and conditionally appends `BlobModule` only when the registry lookup is non-nil, turning a guaranteed crash into a graceful "no blob handler for this window". +- Upstream PR/issue: 🛑 +- E/App issue: https://github.com/Expensify/App/issues/92413 +- PR introducing patch: https://github.com/Expensify/App/pull/92918 \ No newline at end of file diff --git a/patches/react-native/react-native+0.83.1+038+fix-nil-BlobModule-crash-APP-8BM.patch b/patches/react-native/react-native+0.83.1+038+fix-nil-BlobModule-crash-APP-8BM.patch new file mode 100644 index 000000000000..d0a50ce8c581 --- /dev/null +++ b/patches/react-native/react-native+0.83.1+038+fix-nil-BlobModule-crash-APP-8BM.patch @@ -0,0 +1,24 @@ +diff --git a/node_modules/react-native/Libraries/AppDelegate/RCTAppSetupUtils.mm b/node_modules/react-native/Libraries/AppDelegate/RCTAppSetupUtils.mm +index fabfff3..cefa0fa 100644 +--- a/node_modules/react-native/Libraries/AppDelegate/RCTAppSetupUtils.mm ++++ b/node_modules/react-native/Libraries/AppDelegate/RCTAppSetupUtils.mm +@@ -103,11 +103,16 @@ void RCTAppSetupPrepareApp(UIApplication *application, BOOL turboModuleEnabled) + initWithHandlersProvider:^NSArray> *(RCTModuleRegistry *moduleRegistry) { + NSArray *URLRequestHandlerModules = + extractModuleConformingToProtocol(moduleRegistry, @protocol(RCTURLRequestHandler)); +- return [@[ ++ NSMutableArray> *defaultHandlers = [NSMutableArray arrayWithObjects: + [RCTHTTPRequestHandler new], + [RCTDataRequestHandler new], + [RCTFileRequestHandler new], +- [moduleRegistry moduleForName:"BlobModule"], +- ] arrayByAddingObjectsFromArray:URLRequestHandlerModules]; ++ nil ++ ]; ++ id blobModule = [moduleRegistry moduleForName:"BlobModule"]; ++ if (blobModule != nil) { ++ [defaultHandlers addObject:blobModule]; ++ } ++ return [defaultHandlers arrayByAddingObjectsFromArray:URLRequestHandlerModules]; + }]; + } diff --git a/rock.config.mjs b/rock.config.mjs index 3f441f1ac5a3..fd354e8f6832 100644 --- a/rock.config.mjs +++ b/rock.config.mjs @@ -22,7 +22,7 @@ export default { android: platformAndroid({sourceDir: isHybrid ? './Mobile-Expensify/Android' : './android'}), }, fingerprint: { - extraSources: ['android/gradle.properties', 'ios/Podfile', 'scripts/compute-patches-hash.sh'], + extraSources: ['android/gradle.properties', 'ios/Podfile', 'scripts/compute-patches-hash.sh', 'patches', ...(isHybrid ? ['Mobile-Expensify/patches'] : [])], env: ['USE_WEB_PROXY', 'PUSHER_DEV_SUFFIX', 'SECURE_NGROK_URL', 'NGROK_URL', 'USE_NGROK', 'FORCE_NATIVE_BUILD'], ignorePaths: ['Mobile-Expensify/Android/assets/app/shared/bundle.js'], }, diff --git a/src/CONST/index.ts b/src/CONST/index.ts index 93e47db3b810..23dcba5a2b1f 100644 --- a/src/CONST/index.ts +++ b/src/CONST/index.ts @@ -259,6 +259,7 @@ const CONST = { COMPOSER_FOCUS_DELAY: 150, MAX_TRANSITION_DURATION_MS: 1000, MAX_TRANSITION_START_WAIT_MS: 1000, + EXPENSE_REPORT_DELETE_DELAY_MS: 300, ANIMATION_DIRECTION: { IN: 'in', OUT: 'out', @@ -369,6 +370,8 @@ const CONST = { API_TRANSACTION_TAG_MAX_LENGTH: 255, + TRANSACTION_TAG_AND_CATEGORY_PICKER_MAX_TITLE_LINES: 5, + AUTO_AUTH_STATE: { NOT_STARTED: 'not-started', SIGNING_IN: 'signing-in', @@ -1645,6 +1648,7 @@ const CONST = { MARK_REIMBURSED_FROM_INTEGRATION: 'ACTIONMARKEDREIMBURSEDFROMINTEGRATION', // OldDot Action MERGED_WITH_CASH_TRANSACTION: 'MERGEDWITHCASHTRANSACTION', MODIFIED_EXPENSE: 'MODIFIEDEXPENSE', + CONCIERGE_AUTO_MATCH_VENDOR: 'CONCIERGEAUTOMATCHVENDOR', MOVED: 'MOVED', MOVED_TRANSACTION: 'MOVEDTRANSACTION', UNREPORTED_TRANSACTION: 'UNREPORTEDTRANSACTION', @@ -1742,6 +1746,7 @@ const CONST = { UPDATE_AUTO_REPORTING_FREQUENCY: 'POLICYCHANGELOG_UPDATE_AUTOREPORTING_FREQUENCY', UPDATE_BUDGET: 'POLICYCHANGELOG_UPDATE_BUDGET', UPDATE_CATEGORY: 'POLICYCHANGELOG_UPDATE_CATEGORY', + UPDATE_CATEGORY_TAX_RATE: 'POLICYCHANGELOG_UPDATE_CATEGORY_TAX_RATE', UPDATE_CATEGORIES: 'POLICYCHANGELOG_UPDATE_CATEGORIES', UPDATE_CURRENCY: 'POLICYCHANGELOG_UPDATE_CURRENCY', UPDATE_CUSTOM_UNIT: 'POLICYCHANGELOG_UPDATE_CUSTOM_UNIT', @@ -1962,6 +1967,7 @@ const CONST = { MESSAGE_KEY: { WAITING_TO_ADD_TRANSACTIONS: 'waitingToAddTransactions', WAITING_TO_SUBMIT: 'waitingToSubmit', + WAITING_TO_MARK_AS_DONE: 'waitingToMarkAsDone', NO_FURTHER_ACTION: 'noFurtherAction', WAITING_FOR_SUBMITTER_ACCOUNT: 'waitingForSubmitterAccount', WAITING_FOR_AUTOMATIC_SUBMIT: 'waitingForAutomaticSubmit', @@ -2328,6 +2334,11 @@ const CONST = { GSD: 'gsd', DEFAULT: 'default', }, + INBOX_TAB: { + ALL: 'all', + TODO: 'todo', + UNREAD: 'unread', + }, THEME: { DEFAULT: 'system', FALLBACK: 'dark', @@ -2995,6 +3006,7 @@ const CONST = { REPORT_EXPORT_STATUS: 'reportExportStatus', TAX_NON_BILLABLE: 'taxNonBillable', EXPORT_FOREIGN_CURRENCY: 'exportForeignCurrency', + COMPANY: 'company', }, CERTINIA_EXPORT_STATUS: { @@ -3089,6 +3101,7 @@ const CONST = { MANAGER: 'manager', CUSTOM: 'custom', }, + COOKIE_CLEAR_DELAY_MS: 500, SYNC_STATUS: { SYNCING: 'SYNCING', DONE: 'DONE', @@ -3683,6 +3696,8 @@ const CONST = { IOU: { MAX_RECENT_REPORTS_TO_SHOW: 5, + MAX_RECENT_ATTENDEES: 40, + // This will guranatee that the quantity input will not exceed 9,007,199,254,740,991 (Number.MAX_SAFE_INTEGER). QUANTITY_MAX_LENGTH: 12, // This is the transactionID used when going through the create expense flow so that it mimics a real transaction (like the edit flow) @@ -3967,6 +3982,7 @@ const CONST = { MAKE_MEMBER: 'makeMember', MAKE_ADMIN: 'makeAdmin', MAKE_AUDITOR: 'makeAuditor', + MAKE_CARD_ADMIN: 'makeCardAdmin', }, BULK_ACTION_TYPES: { DELETE: 'delete', @@ -3981,7 +3997,6 @@ const CONST = { ARE_DISTANCE_RATES_ENABLED: 'areDistanceRatesEnabled', ARE_WORKFLOWS_ENABLED: 'areWorkflowsEnabled', ARE_REPORT_FIELDS_ENABLED: 'areReportFieldsEnabled', - ARE_INVOICE_FIELDS_ENABLED: 'areInvoiceFieldsEnabled', ARE_CONNECTIONS_ENABLED: 'areConnectionsEnabled', ARE_RECEIPT_PARTNERS_ENABLED: 'receiptPartners', ARE_COMPANY_CARDS_ENABLED: 'areCompanyCardsEnabled', @@ -4035,7 +4050,6 @@ const CONST = { AUTOREPORTING_FREQUENCY: 'autoReportingFrequency', AUTOREPORTING_OFFSET: 'autoReportingOffset', GENERAL_SETTINGS: 'generalSettings', - ADD_AGENT: 'addAgent', }, EXPENSE_REPORT_RULES: { PREVENT_SELF_APPROVAL: 'preventSelfApproval', @@ -4282,6 +4296,12 @@ const CONST = { FAKE_P2P_ID: '_FAKE_P2P_ID_', MILES_TO_KILOMETERS: 1.609344, KILOMETERS_TO_MILES: 0.621371, + RATE_STATUS: { + ACTIVE: 'active', + FUTURE: 'future', + EXPIRED: 'expired', + INACTIVE: 'inactive', + }, RATE_FIELD: { START_DATE: 'startDate', END_DATE: 'endDate', @@ -7248,14 +7268,6 @@ const CONST = { description: 'workspace.upgrade.reportFields.description' as const, icon: 'Pencil', }, - invoiceFields: { - id: 'invoiceFields' as const, - alias: 'invoice-fields', - name: 'Invoice Fields', - title: 'workspace.upgrade.invoiceFields.title' as const, - description: 'workspace.upgrade.invoiceFields.description' as const, - icon: 'Pencil', - }, policyPreventMemberChangingTitle: { id: 'policyPreventMemberChangingTitle' as const, alias: 'policy-prevent-member-changing-title', @@ -7429,12 +7441,12 @@ const CONST = { description: 'workspace.upgrade.distanceRates.description' as const, icon: 'CarIce', }, - auditor: { - id: 'auditor' as const, - alias: 'auditor', - name: 'Auditor', - title: 'workspace.upgrade.auditor.title' as const, - description: 'workspace.upgrade.auditor.description' as const, + controlPolicyRoles: { + id: 'controlPolicyRoles' as const, + alias: 'control-policy-roles', + name: 'Control policy roles', + title: 'workspace.upgrade.controlPolicyRoles.title' as const, + description: 'workspace.upgrade.controlPolicyRoles.description' as const, icon: 'BlueShield', }, reports: { @@ -7517,10 +7529,6 @@ const CONST = { LIST: 'dropdown', FORMULA: 'formula', }, - REPORT_FIELD_TARGETS: { - EXPENSE: 'expense', - INVOICE: 'invoice', - }, NAVIGATION_ACTIONS: { RESET: 'RESET', @@ -7753,7 +7761,6 @@ const CONST = { HAS_FILTER_NEGATION: 'hasFilterNegation', }, CHANGE_POLICY_TRAINING_MODAL: 'changePolicyModal', - AGENTS_WORKFLOWS_BANNER: 'agentsWorkflowsBanner', AGENTS_RULES_BANNER: 'agentsRulesBanner', SMART_BANNER_HEIGHT: 152, @@ -7900,9 +7907,6 @@ const CONST = { HIGH_CONTRAST_MODE_SWITCHER: { TOGGLE: 'HighContrastModeSwitcher-Toggle', }, - AGENTS_WORKFLOWS_BANNER: { - DISMISS: 'AgentsWorkflowsBanner-Dismiss', - }, AGENTS_RULES_BANNER: { CTA: 'AgentsRulesBanner-CTA', DISMISS: 'AgentsRulesBanner-Dismiss', @@ -8464,6 +8468,7 @@ const CONST = { BULK_ACTIONS_DROPDOWN: 'WorkspaceTaxes-BulkActionsDropdown', }, DISTANCE_RATES: { + ROW: 'WorkspaceDistanceRates-Row', ADD_BUTTON: 'WorkspaceDistanceRates-AddButton', MORE_DROPDOWN: 'WorkspaceDistanceRates-MoreDropdown', BULK_ACTIONS_DROPDOWN: 'WorkspaceDistanceRates-BulkActionsDropdown', @@ -8684,6 +8689,8 @@ const CONST = { DELEGATE_ITEM: 'SettingsSecurity-DelegateItem', DELEGATE_CHANGE_ACCESS: 'SettingsSecurity-DelegateChangeAccess', DELEGATE_REMOVE: 'SettingsSecurity-DelegateRemove', + DELEGATOR_ITEM: 'SettingsSecurity-DelegatorItem', + DELEGATOR_REMOVE: 'SettingsSecurity-DelegatorRemove', }, SETTINGS_WALLET: { ADD_BANK_ACCOUNT: 'SettingsWallet-AddBankAccount', diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index f22984a038b1..4e43a9a5e2c4 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -172,6 +172,9 @@ const ONYXKEYS = { /** Contains the user preference for the LHN priority mode */ NVP_PRIORITY_MODE: 'nvp_priorityMode', + /** Contains the user preference for the active inbox tab filter */ + NVP_INBOX_TAB: 'nvp_inboxTab', + /** Contains the users's block expiration (if they have one) */ NVP_BLOCKED_FROM_CONCIERGE: 'nvp_private_blockedFromConcierge', @@ -599,25 +602,6 @@ const ONYXKEYS = { /** Stores the information about currently edited advanced approval workflow */ APPROVAL_WORKFLOW: 'approvalWorkflow', - /** - * Workflow saves the user committed while a freshly-created agent was still pending. Keyed - * by `${policyID}:${firstApproverEmail}`. WorkspaceWorkflowsPage overlays these entries on - * top of the regular workflows so the new agent shows up faded in the approver card, and - * a watcher fires the actual `updateApprovalWorkflow` once the agent gets its real email - * and clears the entry. This lets the admin "Save" before CREATE_AGENT resolves without - * the modal blocking on a `This field is required` validation error. - */ - DEFERRED_AGENT_WORKFLOW_SAVES: 'deferredAgentWorkflowSaves', - - /** - * Maps optimistic agent account IDs (randomly generated) to the real, server-assigned IDs returned - * by CREATE_AGENT. The server echoes this mapping in the response's `onyxData` (and queues - * it on the owner's account channel) so the WorkspaceWorkflowsPage and Edit Approvers - * reconciliation can swap the pending approver to the real agent without falling back to - * matching by prompt — which is ambiguous when multiple agents share the same prompt text. - */ - OPTIMISTIC_AGENT_ACCOUNT_ID_MAPPING: 'optimisticAgentAccountIDMapping', - /** Stores the user search value for persistence across the screens */ ROOM_MEMBERS_USER_SEARCH_PHRASE: 'roomMembersUserSearchPhrase', @@ -1214,6 +1198,7 @@ const ONYXKEYS = { TODOS: 'todos', RAM_ONLY_SORTED_REPORT_ACTIONS: 'sortedReportActions', OPEN_AND_SUBMITTED_REPORTS_BY_POLICY_ID: 'openAndSubmittedReportsByPolicyID', + FLAGGED_EXPENSES: 'flaggedExpenses', }, /** Stores HybridApp specific state required to interoperate with OldDot */ @@ -1496,6 +1481,7 @@ type OnyxValuesMapping = { [ONYXKEYS.BETA_CONFIGURATION]: OnyxTypes.BetaConfiguration; [ONYXKEYS.NVP_MUTED_PLATFORMS]: Partial>; [ONYXKEYS.NVP_PRIORITY_MODE]: ValueOf; + [ONYXKEYS.NVP_INBOX_TAB]: ValueOf; [ONYXKEYS.NVP_BLOCKED_FROM_CONCIERGE]: OnyxTypes.BlockedFromConcierge; [ONYXKEYS.QUEUE_FLUSHED_DATA]: AnyOnyxUpdate[]; [ONYXKEYS.TRANSACTIONS_PENDING_3DS_REVIEW]: OnyxTypes.TransactionsPending3DSReview; @@ -1639,8 +1625,6 @@ type OnyxValuesMapping = { [ONYXKEYS.NVP_PRIVATE_CANCELLATION_DETAILS]: OnyxTypes.CancellationDetails[]; [ONYXKEYS.ROOM_MEMBERS_USER_SEARCH_PHRASE]: string; [ONYXKEYS.APPROVAL_WORKFLOW]: OnyxTypes.ApprovalWorkflowOnyx; - [ONYXKEYS.DEFERRED_AGENT_WORKFLOW_SAVES]: Record; - [ONYXKEYS.OPTIMISTIC_AGENT_ACCOUNT_ID_MAPPING]: Record; [ONYXKEYS.IMPORTED_SPREADSHEET]: OnyxTypes.ImportedSpreadsheet; [ONYXKEYS.IMPORTED_SPREADSHEET_MEMBER_DATA]: OnyxTypes.ImportedSpreadsheetMemberData[]; [ONYXKEYS.IMPORTED_SPREADSHEET_MEMBER_ROLE]: ValueOf; @@ -1707,6 +1691,7 @@ type OnyxDerivedValuesMapping = { [ONYXKEYS.DERIVED.TODOS]: OnyxTypes.TodosDerivedValue; [ONYXKEYS.DERIVED.RAM_ONLY_SORTED_REPORT_ACTIONS]: OnyxTypes.SortedReportActionsDerivedValue; [ONYXKEYS.DERIVED.OPEN_AND_SUBMITTED_REPORTS_BY_POLICY_ID]: OnyxTypes.OpenAndSubmittedReportsByPolicyIDDerivedValue; + [ONYXKEYS.DERIVED.FLAGGED_EXPENSES]: OnyxTypes.FlaggedExpensesDerivedValue; }; type OnyxValues = OnyxValuesMapping & OnyxCollectionValuesMapping & OnyxFormValuesMapping & OnyxFormDraftValuesMapping & OnyxDerivedValuesMapping; diff --git a/src/ROUTES.ts b/src/ROUTES.ts index f8e65e8cc72b..03d6054d2a70 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -180,126 +180,6 @@ const DYNAMIC_ROUTES = { entryScreens: ['*'], getRoute: (accountID: number) => `avatar/${accountID}` as const, }, - MONEY_REQUEST_STEP_TAX_RATE: { - path: 'money-request/tax-rate/:action/:iouType/:transactionID/:reportID?', - entryScreens: [ - SCREENS.MONEY_REQUEST.STEP_CONFIRMATION, - SCREENS.REPORT, - SCREENS.RIGHT_MODAL.EXPENSE_REPORT, - SCREENS.RIGHT_MODAL.SEARCH_REPORT, - SCREENS.RIGHT_MODAL.SEARCH_REPORT_ACTIONS, - SCREENS.RIGHT_MODAL.SEARCH_MONEY_REQUEST_REPORT, - ], - getRoute: (action: IOUAction, iouType: IOUType, transactionID: string | undefined, reportID?: string) => { - if (!transactionID || !reportID) { - Log.warn('Invalid transactionID or reportID is used to build the MONEY_REQUEST_STEP_TAX_RATE dynamic route'); - } - - return `money-request/tax-rate/${action as string}/${iouType as string}/${transactionID}${reportID ? `/${reportID}` : ''}` as const; - }, - }, - MONEY_REQUEST_STEP_TAX_AMOUNT: { - path: 'money-request/tax-amount/:action/:iouType/:transactionID/:reportID?', - entryScreens: [ - SCREENS.MONEY_REQUEST.STEP_CONFIRMATION, - SCREENS.REPORT, - SCREENS.RIGHT_MODAL.EXPENSE_REPORT, - SCREENS.RIGHT_MODAL.SEARCH_REPORT, - SCREENS.RIGHT_MODAL.SEARCH_REPORT_ACTIONS, - SCREENS.RIGHT_MODAL.SEARCH_MONEY_REQUEST_REPORT, - ], - getRoute: (action: IOUAction, iouType: IOUType, transactionID: string | undefined, reportID?: string) => { - if (!transactionID || !reportID) { - Log.warn('Invalid transactionID or reportID is used to build the MONEY_REQUEST_STEP_TAX_AMOUNT dynamic route'); - } - - return `money-request/tax-amount/${action as string}/${iouType as string}/${transactionID}${reportID ? `/${reportID}` : ''}` as const; - }, - }, - MONEY_REQUEST_STEP_CATEGORY: { - path: 'money-request/category/:action/:iouType/:transactionID/:reportID/:reportActionID?', - entryScreens: [ - SCREENS.MONEY_REQUEST.STEP_CONFIRMATION, - SCREENS.MONEY_REQUEST.SPLIT_EXPENSE_EDIT, - SCREENS.REPORT, - SCREENS.RIGHT_MODAL.EXPENSE_REPORT, - SCREENS.RIGHT_MODAL.SEARCH_REPORT, - SCREENS.RIGHT_MODAL.SEARCH_REPORT_ACTIONS, - SCREENS.RIGHT_MODAL.SEARCH_MONEY_REQUEST_REPORT, - ], - getRoute: (action: IOUAction, iouType: IOUType, transactionID: string | undefined, reportID: string | undefined, reportActionID?: string) => { - if (!transactionID || !reportID) { - Log.warn('Invalid transactionID or reportID is used to build the MONEY_REQUEST_STEP_CATEGORY dynamic route'); - } - - return `money-request/category/${action as string}/${iouType as string}/${transactionID}/${reportID}${reportActionID ? `/${reportActionID}` : ''}` as const; - }, - }, - MONEY_REQUEST_ATTENDEE: { - path: 'money-request/attendees/:action/:iouType/:transactionID/:reportID', - entryScreens: [ - SCREENS.MONEY_REQUEST.STEP_CONFIRMATION, - SCREENS.REPORT, - SCREENS.RIGHT_MODAL.EXPENSE_REPORT, - SCREENS.RIGHT_MODAL.SEARCH_REPORT, - SCREENS.RIGHT_MODAL.SEARCH_REPORT_ACTIONS, - SCREENS.RIGHT_MODAL.SEARCH_MONEY_REQUEST_REPORT, - ], - getRoute: (action: IOUAction, iouType: IOUType, transactionID: string | undefined, reportID: string | undefined) => { - if (!transactionID || !reportID) { - Log.warn('Invalid transactionID or reportID is used to build the MONEY_REQUEST_ATTENDEE dynamic route'); - } - - return `money-request/attendees/${action as string}/${iouType as string}/${transactionID}/${reportID}` as const; - }, - }, - MONEY_REQUEST_ACCOUNTANT: { - path: 'money-request/accountant/:action/:iouType/:transactionID/:reportID', - entryScreens: [ - SCREENS.MONEY_REQUEST.STEP_CONFIRMATION, - SCREENS.REPORT, - SCREENS.REPORT_DETAILS.DYNAMIC_ROOT, - SCREENS.RIGHT_MODAL.EXPENSE_REPORT, - SCREENS.RIGHT_MODAL.SEARCH_REPORT, - SCREENS.RIGHT_MODAL.SEARCH_REPORT_ACTIONS, - SCREENS.RIGHT_MODAL.SEARCH_MONEY_REQUEST_REPORT, - ], - getRoute: (action: IOUAction, iouType: IOUType, transactionID: string | undefined, reportID: string | undefined) => { - if (!transactionID || !reportID) { - Log.warn('Invalid transactionID or reportID is used to build the MONEY_REQUEST_ACCOUNTANT dynamic route'); - } - - return `money-request/accountant/${action as string}/${iouType as string}/${transactionID}/${reportID}` as const; - }, - }, - MONEY_REQUEST_UPGRADE: { - path: 'money-request/upgrade/:action/:iouType/:transactionID/:reportID/:upgradePath?', - entryScreens: [ - SCREENS.MONEY_REQUEST.STEP_CONFIRMATION, - SCREENS.MONEY_REQUEST.SPLIT_EXPENSE_EDIT, - SCREENS.HOME, - SCREENS.INBOX, - SCREENS.REPORT, - SCREENS.RIGHT_MODAL.EXPENSE_REPORT, - SCREENS.RIGHT_MODAL.SEARCH_REPORT, - SCREENS.RIGHT_MODAL.SEARCH_REPORT_ACTIONS, - SCREENS.RIGHT_MODAL.SEARCH_MONEY_REQUEST_REPORT, - SCREENS.SEARCH.ROOT, - SCREENS.SEARCH.TRANSACTIONS_CHANGE_REPORT_SEARCH_RHP, - ], - getRoute: (params: {action: IOUAction; iouType: IOUType; transactionID: string; reportID: string; upgradePath?: string; shouldSubmitExpense?: boolean}) => { - const {action, iouType, transactionID, reportID, upgradePath, shouldSubmitExpense} = params; - const upgradePathParam = upgradePath ? `/${upgradePath}` : ''; - const basePath = `money-request/upgrade/${action as string}/${iouType as string}/${transactionID}/${reportID}${upgradePathParam}` as const; - - if (shouldSubmitExpense) { - return `${basePath}?shouldSubmitExpense=true` as const; - } - - return basePath; - }, - queryParams: ['shouldSubmitExpense'], - }, NEW_REPORT_WORKSPACE_SELECTION: { path: 'new-report-workspace-selection', entryScreens: ['*'], @@ -342,6 +222,14 @@ const DYNAMIC_ROUTES = { path: 'certinia/advanced', entryScreens: [SCREENS.WORKSPACE.ACCOUNTING.ROOT], }, + POLICY_ACCOUNTING_CERTINIA_REPORT_EXPORT_STATUS: { + path: 'certinia-report-status/select', + entryScreens: [SCREENS.WORKSPACE.ACCOUNTING.CERTINIA_EXPORT], + }, + POLICY_ACCOUNTING_CERTINIA_COMPANY_SELECTOR: { + path: 'certinia/company', + entryScreens: [SCREENS.WORKSPACE.ACCOUNTING.ROOT], + }, POLICY_ACCOUNTING_NETSUITE_EXPORT_EXPENSES_VENDOR_SELECT: { path: 'vendor/select', entryScreens: [SCREENS.WORKSPACE.ACCOUNTING.DYNAMIC_NETSUITE_EXPORT_EXPENSES], @@ -1427,14 +1315,11 @@ const ROUTES = { SETTINGS_AGENTS: 'settings/agents', SETTINGS_AGENTS_ADD: { route: 'settings/agents/new', - getRoute: ({policyID, workflowApproverEmail}: {policyID?: string; workflowApproverEmail?: string} = {}) => { + getRoute: ({policyID}: {policyID?: string} = {}) => { const params = new URLSearchParams(); if (policyID) { params.set('policyID', policyID); } - if (workflowApproverEmail) { - params.set('workflowApproverEmail', workflowApproverEmail); - } const query = params.toString(); return `settings/agents/new${query ? `?${query}` : ''}` as const; }, @@ -1749,6 +1634,26 @@ const ROUTES = { return getUrlWithBackToParam(`${action as string}/${iouType as string}/amount/${transactionID}/${reportID}/${reportActionID ? `${reportActionID}/` : ''}${pageIndex}`, backTo); }, }, + MONEY_REQUEST_STEP_TAX_RATE: { + route: ':action/:iouType/taxRate/:transactionID/:reportID?', + getRoute: (action: IOUAction, iouType: IOUType, transactionID: string | undefined, reportID: string | undefined, backTo = '') => { + if (!transactionID || !reportID) { + Log.warn('Invalid transactionID or reportID is used to build the MONEY_REQUEST_STEP_TAX_RATE route'); + } + + return getUrlWithBackToParam(`${action as string}/${iouType as string}/taxRate/${transactionID}/${reportID}`, backTo); + }, + }, + MONEY_REQUEST_STEP_TAX_AMOUNT: { + route: ':action/:iouType/taxAmount/:transactionID/:reportID?', + getRoute: (action: IOUAction, iouType: IOUType, transactionID: string | undefined, reportID: string | undefined, backTo = '') => { + if (!transactionID || !reportID) { + Log.warn('Invalid transactionID or reportID is used to build the MONEY_REQUEST_STEP_TAX_AMOUNT route'); + } + + return getUrlWithBackToParam(`${action as string}/${iouType as string}/taxAmount/${transactionID}/${reportID}`, backTo); + }, + }, MONEY_REQUEST_STEP_CATEGORY_CREATE: { route: ':action/:iouType/category/new/:transactionID/:reportID/:reportActionID?', getRoute: (action: IOUAction, iouType: IOUType, transactionID: string | undefined, reportID: string | undefined, reportActionID?: string, backTo = '') => { @@ -1759,6 +1664,60 @@ const ROUTES = { return getUrlWithBackToParam(`${action as string}/${iouType as string}/category/new/${transactionID}/${reportID}${reportActionID ? `/${reportActionID}` : ''}`, backTo); }, }, + MONEY_REQUEST_STEP_CATEGORY: { + route: ':action/:iouType/category/:transactionID/:reportID/:reportActionID?', + getRoute: (action: IOUAction, iouType: IOUType, transactionID: string | undefined, reportID: string | undefined, backTo = '', reportActionID?: string) => { + if (!transactionID || !reportID) { + Log.warn('Invalid transactionID or reportID is used to build the MONEY_REQUEST_STEP_CATEGORY route'); + } + + return getUrlWithBackToParam(`${action as string}/${iouType as string}/category/${transactionID}/${reportID}${reportActionID ? `/${reportActionID}` : ''}`, backTo); + }, + }, + MONEY_REQUEST_ATTENDEE: { + route: ':action/:iouType/attendees/:transactionID/:reportID', + getRoute: (action: IOUAction, iouType: IOUType, transactionID: string | undefined, reportID: string | undefined, backTo = '') => { + if (!transactionID || !reportID) { + Log.warn('Invalid transactionID or reportID is used to build the MONEY_REQUEST_ATTENDEE route'); + } + + return getUrlWithBackToParam(`${action as string}/${iouType as string}/attendees/${transactionID}/${reportID}`, backTo); + }, + }, + MONEY_REQUEST_ACCOUNTANT: { + route: ':action/:iouType/accountant/:transactionID/:reportID', + getRoute: (action: IOUAction, iouType: IOUType, transactionID: string | undefined, reportID: string | undefined, backTo = '') => { + if (!transactionID || !reportID) { + Log.warn('Invalid transactionID or reportID is used to build the MONEY_REQUEST_ACCOUNTANT route'); + } + + return getUrlWithBackToParam(`${action as string}/${iouType as string}/accountant/${transactionID}/${reportID}`, backTo); + }, + }, + MONEY_REQUEST_UPGRADE: { + route: ':action/:iouType/upgrade/:transactionID/:reportID/:upgradePath?', + getRoute: (params: {action: IOUAction; iouType: IOUType; transactionID: string; reportID: string; backTo?: string; shouldSubmitExpense?: boolean; upgradePath?: string}) => { + const {action, iouType, transactionID, reportID, backTo = '', shouldSubmitExpense = false, upgradePath} = params; + const upgradePathParam = upgradePath ? `/${upgradePath}` : ''; + const baseURL = `${action as string}/${iouType as string}/upgrade/${transactionID}/${reportID}${upgradePathParam}` as const; + + if (shouldSubmitExpense) { + return getUrlWithBackToParam(`${baseURL}?shouldSubmitExpense=${shouldSubmitExpense}` as const, backTo); + } + + return getUrlWithBackToParam(baseURL, backTo); + }, + }, + MONEY_REQUEST_STEP_VENDOR: { + route: ':action/:iouType/vendor/:transactionID/:reportID/:reportActionID?', + getRoute: (action: IOUAction, iouType: IOUType, transactionID: string | undefined, reportID: string | undefined, reportActionID?: string) => { + if (!transactionID || !reportID) { + Log.warn('Invalid transactionID or reportID is used to build the MONEY_REQUEST_STEP_VENDOR route'); + } + + return `${action as string}/${iouType as string}/vendor/${transactionID}/${reportID}${reportActionID ? `/${reportActionID}` : ''}` as const; + }, + }, MONEY_REQUEST_STEP_DESTINATION: { route: ':action/:iouType/destination/:transactionID/:reportID', getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID: string, backTo = '') => @@ -2353,11 +2312,7 @@ const ROUTES = { }, WORKSPACE_WORKFLOWS_APPROVALS_EDIT: { route: 'workspaces/:policyID/workflows/approvals/:firstApproverEmail/edit', - getRoute: (policyID: string, firstApproverEmail: string, seedApproverEmail?: string, seedApproverAccountID?: number) => - getUrlWithParams(`workspaces/${policyID}/workflows/approvals/${encodeURIComponent(firstApproverEmail)}/edit`, { - seedApproverEmail, - seedApproverAccountID: seedApproverAccountID !== undefined ? String(seedApproverAccountID) : undefined, - }), + getRoute: (policyID: string, firstApproverEmail: string) => `workspaces/${policyID}/workflows/approvals/${encodeURIComponent(firstApproverEmail)}/edit` as const, }, WORKSPACE_WORKFLOWS_APPROVALS_EXPENSES_FROM: { route: 'workspaces/:policyID/workflows/approvals/expenses-from', @@ -2380,11 +2335,6 @@ const ROUTES = { route: 'workspaces/:policyID/workflows/approvals/over-limit-approver', getRoute: (policyID: string, approverIndex: number) => `workspaces/${policyID}/workflows/approvals/over-limit-approver?approverIndex=${approverIndex}` as const, }, - WORKSPACE_WORKFLOWS_ADD_AGENT: { - route: 'workspaces/:policyID/workflows/add-agent', - getRoute: ({policyID, workflowApproverEmail}: {policyID: string; workflowApproverEmail?: string}) => - getUrlWithParams(`workspaces/${policyID}/workflows/add-agent`, {workflowApproverEmail}), - }, WORKSPACE_WORKFLOWS_PAYER: { route: 'workspaces/:policyID/workflows/payer', getRoute: (policyId: string) => `workspaces/${policyId}/workflows/payer` as const, @@ -2428,35 +2378,6 @@ const ROUTES = { route: 'workspaces/:policyID/invoices/company-website', getRoute: (policyID: string) => `workspaces/${policyID}/invoices/company-website` as const, }, - WORKSPACE_INVOICE_FIELDS_CREATE: { - route: 'workspaces/:policyID/invoices/newInvoiceField', - getRoute: (policyID: string) => `workspaces/${policyID}/invoices/newInvoiceField` as const, - }, - WORKSPACE_INVOICE_FIELDS_SETTINGS: { - route: 'workspaces/:policyID/invoices/:reportFieldID/edit', - getRoute: (policyID: string, reportFieldID: string) => `workspaces/${policyID}/invoices/${encodeURIComponent(reportFieldID)}/edit` as const, - }, - WORKSPACE_INVOICE_FIELDS_LIST_VALUES: { - route: 'workspaces/:policyID/invoices/listValues/:reportFieldID?', - getRoute: (policyID: string, reportFieldID?: string) => `workspaces/${policyID}/invoices/listValues/${reportFieldID ? encodeURIComponent(reportFieldID) : ''}` as const, - }, - WORKSPACE_INVOICE_FIELDS_ADD_VALUE: { - route: 'workspaces/:policyID/invoices/addValue/:reportFieldID?', - getRoute: (policyID: string, reportFieldID?: string) => `workspaces/${policyID}/invoices/addValue/${reportFieldID ? encodeURIComponent(reportFieldID) : ''}` as const, - }, - WORKSPACE_INVOICE_FIELDS_VALUE_SETTINGS: { - route: 'workspaces/:policyID/invoices/:valueIndex/:reportFieldID?', - getRoute: (policyID: string, valueIndex: number, reportFieldID?: string) => - `workspaces/${policyID}/invoices/${valueIndex}/${reportFieldID ? encodeURIComponent(reportFieldID) : ''}` as const, - }, - WORKSPACE_INVOICE_FIELDS_EDIT_VALUE: { - route: 'workspaces/:policyID/invoices/newInvoiceField/:valueIndex/edit', - getRoute: (policyID: string, valueIndex: number) => `workspaces/${policyID}/invoices/newInvoiceField/${valueIndex}/edit` as const, - }, - WORKSPACE_INVOICE_FIELDS_EDIT_INITIAL_VALUE: { - route: 'workspaces/:policyID/invoices/:reportFieldID/edit/initialValue', - getRoute: (policyID: string, reportFieldID: string) => `workspaces/${policyID}/invoices/${encodeURIComponent(reportFieldID)}/edit/initialValue` as const, - }, WORKSPACE_MEMBERS: { route: 'workspaces/:policyID/members', getRoute: (policyID: string | undefined) => { @@ -4019,6 +3940,33 @@ const ROUTES = { return `workspaces/${policyID}/accounting/certinia/advanced` as const; }, }, + POLICY_ACCOUNTING_CERTINIA_TAGS_MAPPING: { + route: 'workspaces/:policyID/accounting/certinia/import/tags-mapping', + getRoute: (policyID: string | undefined) => { + if (!policyID) { + Log.warn('Invalid policyID is used to build the POLICY_ACCOUNTING_CERTINIA_TAGS_MAPPING route'); + } + return `workspaces/${policyID}/accounting/certinia/import/tags-mapping` as const; + }, + }, + POLICY_ACCOUNTING_CERTINIA_REPORT_EXPORT_STATUS: { + route: 'workspaces/:policyID/accounting/certinia/export/report-status', + getRoute: (policyID: string | undefined) => { + if (!policyID) { + Log.warn('Invalid policyID is used to build the POLICY_ACCOUNTING_CERTINIA_REPORT_EXPORT_STATUS route'); + } + return `workspaces/${policyID}/accounting/certinia/export/report-status` as const; + }, + }, + POLICY_ACCOUNTING_CERTINIA_COMPANY_SELECTOR: { + route: 'workspaces/:policyID/accounting/certinia/company', + getRoute: (policyID: string | undefined) => { + if (!policyID) { + Log.warn('Invalid policyID is used to build the POLICY_ACCOUNTING_CERTINIA_COMPANY_SELECTOR route'); + } + return `workspaces/${policyID}/accounting/certinia/company` as const; + }, + }, ADD_EXISTING_EXPENSE: { route: 'search/r/:reportID/add-existing-expense/:backToReport?', getRoute: (reportID: string | undefined, backToReport?: string) => `search/r/${reportID}/add-existing-expense/${backToReport ?? ''}` as const, diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 6066e17d3967..cc9c10d594ad 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -373,9 +373,9 @@ const SCREENS = { STEP_CONFIRMATION: 'Money_Request_Step_Confirmation', STEP_CONFIRMATION_VERIFY_ACCOUNT: 'Money_Request_Step_Confirmation_Verify_Account', START: 'Money_Request_Start', - DYNAMIC_STEP_UPGRADE: 'Dynamic_Money_Request_Step_Upgrade', + STEP_UPGRADE: 'Money_Request_Step_Upgrade', STEP_AMOUNT: 'Money_Request_Step_Amount', - DYNAMIC_STEP_CATEGORY: 'Dynamic_Money_Request_Step_Category', + STEP_CATEGORY: 'Money_Request_Step_Category', STEP_CATEGORY_CREATE: 'Money_Request_Step_Category_Create', STEP_DATE: 'Money_Request_Step_Date', STEP_DESCRIPTION: 'Money_Request_Step_Description', @@ -386,8 +386,9 @@ const SCREENS = { STEP_SCAN: 'Money_Request_Step_Scan', STEP_TAG: 'Money_Request_Step_Tag', STEP_WAYPOINT: 'Money_Request_Step_Waypoint', - DYNAMIC_STEP_TAX_AMOUNT: 'Dynamic_Money_Request_Step_Tax_Amount', - DYNAMIC_STEP_TAX_RATE: 'Dynamic_Money_Request_Step_Tax_Rate', + STEP_TAX_AMOUNT: 'Money_Request_Step_Tax_Amount', + STEP_TAX_RATE: 'Money_Request_Step_Tax_Rate', + STEP_VENDOR: 'Money_Request_Step_Vendor', RECEIPT_VIEW: 'Money_Request_Receipt_View', STEP_SEND_FROM: 'Money_Request_Step_Send_From', STEP_COMPANY_INFO: 'Money_Request_Step_Company_Info', @@ -396,8 +397,8 @@ const SCREENS = { EDIT_WAYPOINT: 'Money_Request_Edit_Waypoint', RECEIPT: 'Money_Request_Receipt', STATE_SELECTOR: 'Money_Request_State_Selector', - DYNAMIC_STEP_ATTENDEES: 'Dynamic_Money_Request_Attendee', - DYNAMIC_STEP_ACCOUNTANT: 'Dynamic_Money_Request_Step_Accountant', + STEP_ATTENDEES: 'Money_Request_Attendee', + STEP_ACCOUNTANT: 'Money_Request_Accountant', STEP_DESTINATION: 'Money_Request_Destination', STEP_TIME: 'Money_Request_Time', STEP_SUBRATE: 'Money_Request_SubRate', @@ -701,6 +702,9 @@ const SCREENS = { CERTINIA_EXPORT_DATE: 'Policy_Accounting_Certinia_Export_Date', CERTINIA_DEFAULT_VENDOR: 'Policy_Accounting_Certinia_Default_Vendor', CERTINIA_ADVANCED: 'Policy_Accounting_Certinia_Advanced', + CERTINIA_TAGS_MAPPING: 'Policy_Accounting_Certinia_Tags_Mapping', + CERTINIA_REPORT_EXPORT_STATUS: 'Policy_Accounting_Certinia_Report_Export_Status', + CERTINIA_COMPANY_SELECTOR: 'Policy_Accounting_Certinia_Company_Selector', CARD_RECONCILIATION: 'Policy_Accounting_Card_Reconciliation', CARD_RECONCILIATION_SAGE_INTACCT_AUTO_SYNC: 'Policy_Accounting_Card_Reconciliation_Sage_Intacct_Auto_Sync', DYNAMIC_RECONCILIATION_ACCOUNT_SETTINGS: 'Dynamic_Policy_Accounting_Reconciliation_Account_Settings', @@ -766,13 +770,6 @@ const SCREENS = { INVOICES_VERIFY_ACCOUNT: 'Workspace_Invoices_Verify_Account', INVOICES_COMPANY_NAME: 'Workspace_Invoices_Company_Name', INVOICES_COMPANY_WEBSITE: 'Workspace_Invoices_Company_Website', - INVOICE_FIELDS_CREATE: 'Workspace_InvoiceFields_Create', - INVOICE_FIELDS_SETTINGS: 'Workspace_InvoiceFields_Settings', - INVOICE_FIELDS_LIST_VALUES: 'Workspace_InvoiceFields_ListValues', - INVOICE_FIELDS_ADD_VALUE: 'Workspace_InvoiceFields_AddValue', - INVOICE_FIELDS_VALUE_SETTINGS: 'Workspace_InvoiceFields_ValueSettings', - INVOICE_FIELDS_EDIT_VALUE: 'Workspace_InvoiceFields_EditValue', - INVOICE_FIELDS_EDIT_INITIAL_VALUE: 'Workspace_InvoiceFields_EditInitialValue', MEMBERS: 'Workspace_Members', ROOMS: 'Workspace_Rooms', ROOM_CREATE: 'Workspace_Room_Create', @@ -829,7 +826,6 @@ const SCREENS = { WORKFLOWS_APPROVALS_APPROVER_CHANGE: 'Workspace_Workflows_Approvals_Approver_Change', WORKFLOWS_APPROVALS_APPROVAL_LIMIT: 'Workspace_Workflows_Approvals_Approval_Limit', WORKFLOWS_APPROVALS_OVER_LIMIT_APPROVER: 'Workspace_Workflows_Approvals_Over_Limit_Approver', - WORKFLOWS_ADD_AGENT: 'Workspace_Workflows_Add_Agent', WORKFLOWS_AUTO_REPORTING_FREQUENCY: 'Workspace_Workflows_Auto_Reporting_Frequency', WORKFLOWS_AUTO_REPORTING_MONTHLY_OFFSET: 'Workspace_Workflows_Auto_Reporting_Monthly_Offset', DESCRIPTION: 'Workspace_Overview_Description', diff --git a/src/components/AnimatedSubmitButton/index.tsx b/src/components/AnimatedSubmitButton/index.tsx index 945d5333fc9b..6e3e010aae1e 100644 --- a/src/components/AnimatedSubmitButton/index.tsx +++ b/src/components/AnimatedSubmitButton/index.tsx @@ -28,9 +28,12 @@ type AnimatedSubmitButtonProps = WithSentryLabel & { // Whether the button should be disabled isDisabled?: boolean; + + /** Whether to show "Mark as done" copy instead of "Submit" copy for track-intent users */ + isMarkAsDone?: boolean; }; -function AnimatedSubmitButton({success, text, onPress, isSubmittingAnimationRunning, onAnimationFinish, isDisabled, sentryLabel}: AnimatedSubmitButtonProps) { +function AnimatedSubmitButton({success, text, onPress, isSubmittingAnimationRunning, onAnimationFinish, isDisabled, sentryLabel, isMarkAsDone}: AnimatedSubmitButtonProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); const isAnimationRunning = isSubmittingAnimationRunning; @@ -120,7 +123,7 @@ function AnimatedSubmitButton({success, text, onPress, isSubmittingAnimationRunn >