From 6081d7b14fb69213515cd29291d8195fe21f4f67 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Tue, 21 Jan 2025 18:54:06 +0300 Subject: [PATCH 1/6] applied showing not found page for pending delete policy --- src/libs/PolicyUtils.ts | 14 ++++++++------ src/pages/ErrorPage/NotFoundPage.tsx | 8 ++++++-- .../ReimbursementAccountPage.tsx | 4 ++-- src/pages/workspace/AccessOrNotFoundWrapper.tsx | 4 ++-- src/pages/workspace/WorkspacePageWithSections.tsx | 7 +++---- 5 files changed, 21 insertions(+), 16 deletions(-) diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index 4b35152c7677..7ece85aa3419 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -211,17 +211,17 @@ function getPolicyRole(policy: OnyxInputOrEntry | SearchPolicy, currentU /** * Check if the policy can be displayed - * If offline, always show the policy pending deletion. - * If online, show the policy pending deletion only if there is an error. + * If shouldShowPendingDeletePolicy is true, show the policy pending deletion. + * If shouldShowPendingDeletePolicy is false, show the policy pending deletion only if there is an error. * Note: Using a local ONYXKEYS.NETWORK subscription will cause a delay in * updating the screen. Passing the offline status from the component. */ -function shouldShowPolicy(policy: OnyxEntry, isOffline: boolean, currentUserLogin: string | undefined): boolean { +function shouldShowPolicy(policy: OnyxEntry, shouldShowPendingDeletePolicy: boolean, currentUserLogin: string | undefined): boolean { return ( !!policy?.isJoinRequestPending || (!!policy && policy?.type !== CONST.POLICY.TYPE.PERSONAL && - (isOffline || policy?.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE || Object.keys(policy.errors ?? {}).length > 0) && + (shouldShowPendingDeletePolicy || policy?.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE || Object.keys(policy.errors ?? {}).length > 0) && !!getPolicyRole(policy, currentUserLogin)) ); } @@ -496,7 +496,7 @@ function getPolicyEmployeeListByIdWithoutCurrentUser(policies: OnyxCollection): boolean { - return !isEmptyObject(policy) && (Object.keys(policy).length !== 1 || isEmptyObject(policy.errors)) && !!policy?.id; + return ( + !isEmptyObject(policy) && (Object.keys(policy).length !== 1 || isEmptyObject(policy.errors)) && !!policy?.id && policy?.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE + ); } function areAllGroupPoliciesExpenseChatDisabled(policies = allPolicies) { diff --git a/src/pages/ErrorPage/NotFoundPage.tsx b/src/pages/ErrorPage/NotFoundPage.tsx index 26b6aae5bc0b..51149e0ac775 100644 --- a/src/pages/ErrorPage/NotFoundPage.tsx +++ b/src/pages/ErrorPage/NotFoundPage.tsx @@ -10,10 +10,11 @@ import ONYXKEYS from '@src/ONYXKEYS'; type NotFoundPageProps = { onBackButtonPress?: () => void; isReportRelatedPage?: boolean; + shouldShowOfflineIndicator?: boolean; } & FullPageNotFoundViewProps; // eslint-disable-next-line rulesdir/no-negated-variables -function NotFoundPage({onBackButtonPress = () => Navigation.goBack(), isReportRelatedPage, ...fullPageNotFoundViewProps}: NotFoundPageProps) { +function NotFoundPage({onBackButtonPress = () => Navigation.goBack(), isReportRelatedPage, shouldShowOfflineIndicator, ...fullPageNotFoundViewProps}: NotFoundPageProps) { // We need to use isSmallScreenWidth instead of shouldUseNarrowLayout to go back to the not found page on large screens and to the home page on small screen // eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth const {isSmallScreenWidth} = useResponsiveLayout(); @@ -21,7 +22,10 @@ function NotFoundPage({onBackButtonPress = () => Navigation.goBack(), isReportRe const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${topmostReportId}`); return ( - + { diff --git a/src/pages/ReimbursementAccount/ReimbursementAccountPage.tsx b/src/pages/ReimbursementAccount/ReimbursementAccountPage.tsx index 25ce1c6be39b..30b9666b84b7 100644 --- a/src/pages/ReimbursementAccount/ReimbursementAccountPage.tsx +++ b/src/pages/ReimbursementAccount/ReimbursementAccountPage.tsx @@ -438,14 +438,14 @@ function ReimbursementAccountPage({route, policy, isLoadingPolicy}: Reimbursemen return ; } - if (!isLoading && (isEmptyObject(policy) || !PolicyUtils.isPolicyAdmin(policy))) { + if ((!isLoading && (isEmptyObject(policy) || !PolicyUtils.isPolicyAdmin(policy))) || PolicyUtils.isPendingDeletePolicy(policy)) { return ( ); diff --git a/src/pages/workspace/AccessOrNotFoundWrapper.tsx b/src/pages/workspace/AccessOrNotFoundWrapper.tsx index b9da0147b525..2ef48654dbaa 100644 --- a/src/pages/workspace/AccessOrNotFoundWrapper.tsx +++ b/src/pages/workspace/AccessOrNotFoundWrapper.tsx @@ -97,6 +97,7 @@ function PageNotFoundFallback({policyID, fullPageNotFoundViewProps, isFeatureEna return ( { if (isPolicyNotAccessible) { Navigation.dismissModal(); @@ -128,7 +129,6 @@ function AccessOrNotFoundWrapper({ const {login = ''} = useCurrentUserPersonalDetails(); const isPolicyIDInRoute = !!policyID?.length; const isMoneyRequest = !!iouType && IOUUtils.isValidMoneyRequestType(iouType); - const isFromGlobalCreate = isEmptyObject(report?.reportID); const pendingField = featureName ? policy?.pendingFields?.[featureName] : undefined; useEffect(() => { @@ -154,7 +154,7 @@ function AccessOrNotFoundWrapper({ }, true); const isPolicyNotAccessible = !PolicyUtils.isPolicyAccessible(policy); - const shouldShowNotFoundPage = (!isMoneyRequest && !isFromGlobalCreate && isPolicyNotAccessible) || !isPageAccessible || !isPolicyFeatureEnabled || shouldBeBlocked; + const shouldShowNotFoundPage = (!isMoneyRequest && isPolicyNotAccessible) || !isPageAccessible || !isPolicyFeatureEnabled || shouldBeBlocked; // We only update the feature state if it isn't pending. // This is because the feature state changes several times during the creation of a workspace, while we are waiting for a response from the backend. diff --git a/src/pages/workspace/WorkspacePageWithSections.tsx b/src/pages/workspace/WorkspacePageWithSections.tsx index 26175c9793d9..a4630b0bf421 100644 --- a/src/pages/workspace/WorkspacePageWithSections.tsx +++ b/src/pages/workspace/WorkspacePageWithSections.tsx @@ -10,7 +10,6 @@ import HeaderWithBackButton from '@components/HeaderWithBackButton'; import type HeaderWithBackButtonProps from '@components/HeaderWithBackButton/types'; import ScreenWrapper from '@components/ScreenWrapper'; import ScrollViewWithContext from '@components/ScrollViewWithContext'; -import useNetwork from '@hooks/useNetwork'; import usePrevious from '@hooks/usePrevious'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -124,7 +123,6 @@ function WorkspacePageWithSections({ }: WorkspacePageWithSectionsProps) { const styles = useThemeStyles(); const policyID = route.params?.policyID ?? '-1'; - const {isOffline} = useNetwork({onReconnect: () => fetchData(policyID, shouldSkipVBBACall)}); const [user] = useOnyx(ONYXKEYS.USER); const [reimbursementAccount = CONST.REIMBURSEMENT_ACCOUNT.DEFAULT_DATA] = useOnyx(ONYXKEYS.REIMBURSEMENT_ACCOUNT); @@ -151,8 +149,8 @@ function WorkspacePageWithSections({ }, [policyID, shouldSkipVBBACall]), ); - const shouldShowPolicy = useMemo(() => PolicyUtils.shouldShowPolicy(policy, isOffline, currentUserLogin), [policy, isOffline, currentUserLogin]); - const prevShouldShowPolicy = useMemo(() => PolicyUtils.shouldShowPolicy(prevPolicy, isOffline, currentUserLogin), [prevPolicy, isOffline, currentUserLogin]); + const shouldShowPolicy = useMemo(() => PolicyUtils.shouldShowPolicy(policy, false, currentUserLogin), [policy, currentUserLogin]); + const prevShouldShowPolicy = useMemo(() => PolicyUtils.shouldShowPolicy(prevPolicy, false, currentUserLogin), [prevPolicy, currentUserLogin]); const shouldShow = useMemo(() => { // If the policy object doesn't exist or contains only error data, we shouldn't display it. if (((isEmptyObject(policy) || (Object.keys(policy).length === 1 && !isEmptyObject(policy.errors))) && isEmptyObject(policyDraft)) || shouldShowNotFoundPage) { @@ -170,6 +168,7 @@ function WorkspacePageWithSections({ shouldEnablePickerAvoiding={false} shouldEnableMaxHeight testID={testID ?? WorkspacePageWithSections.displayName} + shouldShowOfflineIndicator={!shouldShow} shouldShowOfflineIndicatorInWideScreen={shouldShowOfflineIndicatorInWideScreen && !shouldShow} > Date: Wed, 22 Jan 2025 00:42:27 +0300 Subject: [PATCH 2/6] implement for WorkspaceInitialPage --- src/pages/workspace/WorkspaceInitialPage.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/workspace/WorkspaceInitialPage.tsx b/src/pages/workspace/WorkspaceInitialPage.tsx index 2a9b77551c0f..aae3b727057e 100644 --- a/src/pages/workspace/WorkspaceInitialPage.tsx +++ b/src/pages/workspace/WorkspaceInitialPage.tsx @@ -340,8 +340,8 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, route}: Workspac const prevProtectedMenuItems = usePrevious(protectedCollectPolicyMenuItems); const enabledItem = protectedCollectPolicyMenuItems.find((curItem) => !prevProtectedMenuItems.some((prevItem) => curItem.routeName === prevItem.routeName)); - const shouldShowPolicy = useMemo(() => PolicyUtils.shouldShowPolicy(policy, isOffline, currentUserLogin), [policy, isOffline, currentUserLogin]); - const prevShouldShowPolicy = useMemo(() => PolicyUtils.shouldShowPolicy(prevPolicy, isOffline, currentUserLogin), [prevPolicy, isOffline, currentUserLogin]); + const shouldShowPolicy = useMemo(() => PolicyUtils.shouldShowPolicy(policy, false, currentUserLogin), [policy, currentUserLogin]); + const prevShouldShowPolicy = useMemo(() => PolicyUtils.shouldShowPolicy(prevPolicy, false, currentUserLogin), [prevPolicy, currentUserLogin]); // We check shouldShowPolicy and prevShouldShowPolicy to prevent the NotFound view from showing right after we delete the workspace // eslint-disable-next-line rulesdir/no-negated-variables const shouldShowNotFoundPage = isEmptyObject(policy) || (!shouldShowPolicy && !prevShouldShowPolicy); From be823ab44b4854afd40bc07ee08b40775dab8ac7 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Wed, 22 Jan 2025 01:01:02 +0300 Subject: [PATCH 3/6] minor fix --- src/libs/PolicyUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index 7ece85aa3419..23d290a64c6d 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -496,7 +496,7 @@ function getPolicyEmployeeListByIdWithoutCurrentUser(policies: OnyxCollection Date: Thu, 23 Jan 2025 21:55:21 +0300 Subject: [PATCH 4/6] fix lint --- .../ReimbursementAccountPage.tsx | 56 +++++++++------- .../workspace/AccessOrNotFoundWrapper.tsx | 54 +++++++-------- src/pages/workspace/WorkspaceInitialPage.tsx | 67 +++++++++++-------- 3 files changed, 94 insertions(+), 83 deletions(-) diff --git a/src/pages/ReimbursementAccount/ReimbursementAccountPage.tsx b/src/pages/ReimbursementAccount/ReimbursementAccountPage.tsx index 30b9666b84b7..31091e231c17 100644 --- a/src/pages/ReimbursementAccount/ReimbursementAccountPage.tsx +++ b/src/pages/ReimbursementAccount/ReimbursementAccountPage.tsx @@ -17,17 +17,23 @@ import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import usePrevious from '@hooks/usePrevious'; import useThemeStyles from '@hooks/useThemeStyles'; +import {clearOnfidoToken, openReimbursementAccountPage, setPlaidEvent, setReimbursementAccountLoading} from '@libs/actions/BankAccounts'; +import { + clearReimbursementAccountDraft, + goToWithdrawalAccountSetupStep, + hideBankAccountErrors, + setBankAccountSubStep, + updateReimbursementAccountDraft, +} from '@libs/actions/ReimbursementAccount'; import getPlaidOAuthReceivedRedirectURI from '@libs/getPlaidOAuthReceivedRedirectURI'; import BankAccount from '@libs/models/BankAccount'; import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackRouteProp, PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {ReimbursementAccountNavigatorParamList} from '@libs/Navigation/types'; -import * as PolicyUtils from '@libs/PolicyUtils'; +import {goBackFromInvalidPolicy, isPendingDeletePolicy, isPolicyAdmin} from '@libs/PolicyUtils'; import shouldReopenOnfido from '@libs/shouldReopenOnfido'; import type {WithPolicyOnyxProps} from '@pages/workspace/withPolicy'; import withPolicy from '@pages/workspace/withPolicy'; -import * as BankAccounts from '@userActions/BankAccounts'; -import * as ReimbursementAccount from '@userActions/ReimbursementAccount'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; @@ -267,7 +273,7 @@ function ReimbursementAccountPage({route, policy, isLoadingPolicy}: Reimbursemen const localCurrentStep = isPreviousPolicy ? achData?.currentStep ?? '' : ''; if (policyIDParam) { - BankAccounts.openReimbursementAccountPage(stepToOpen, subStep, localCurrentStep, policyIDParam); + openReimbursementAccountPage(stepToOpen, subStep, localCurrentStep, policyIDParam); } } @@ -278,14 +284,14 @@ function ReimbursementAccountPage({route, policy, isLoadingPolicy}: Reimbursemen return; } - BankAccounts.setReimbursementAccountLoading(true); - ReimbursementAccount.clearReimbursementAccountDraft(); + setReimbursementAccountLoading(true); + clearReimbursementAccountDraft(); // If the step to open is empty, we want to clear the sub step, so the connect option view is shown to the user const isStepToOpenEmpty = getStepToOpenFromRouteParams(route) === ''; if (isStepToOpenEmpty) { - BankAccounts.setBankAccountSubStep(null); - BankAccounts.setPlaidEvent(null); + setBankAccountSubStep(null); + setPlaidEvent(null); } fetchData(); // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps @@ -321,7 +327,7 @@ function ReimbursementAccountPage({route, policy, isLoadingPolicy}: Reimbursemen if (currentStepRouteParam === currentStep) { // If the user is connecting online with plaid, reset any bank account errors so we don't persist old data from a potential previous connection if (currentStep === CONST.BANK_ACCOUNT.STEP.BANK_ACCOUNT && achData?.subStep === CONST.BANK_ACCOUNT.SETUP_TYPE.PLAID) { - BankAccounts.hideBankAccountErrors(); + hideBankAccountErrors(); } // The route is showing the correct step, no need to update the route param or clear errors. @@ -331,14 +337,14 @@ function ReimbursementAccountPage({route, policy, isLoadingPolicy}: Reimbursemen // Update the data that is returned from back-end to draft value const draftStep = reimbursementAccount?.draftStep; if (draftStep) { - BankAccounts.updateReimbursementAccountDraft(getBankAccountFields(getFieldsForStep(draftStep))); + updateReimbursementAccountDraft(getBankAccountFields(getFieldsForStep(draftStep))); } if (currentStepRouteParam !== '') { // When we click "Connect bank account", we load the page without the current step param, if there // was an error when we tried to disconnect or start over, we want the user to be able to see the error, // so we don't clear it. We only want to clear the errors if we are moving between steps. - BankAccounts.hideBankAccountErrors(); + hideBankAccountErrors(); } Navigation.setParams({stepToOpen: getRouteForCurrentStep(currentStep)}); @@ -348,7 +354,7 @@ function ReimbursementAccountPage({route, policy, isLoadingPolicy}: Reimbursemen ); const setManualStep = () => { - BankAccounts.setBankAccountSubStep(CONST.BANK_ACCOUNT.SETUP_TYPE.MANUAL).then(() => { + setBankAccountSubStep(CONST.BANK_ACCOUNT.SETUP_TYPE.MANUAL).then(() => { setShouldShowContinueSetupButton(false); }); }; @@ -363,37 +369,37 @@ function ReimbursementAccountPage({route, policy, isLoadingPolicy}: Reimbursemen setShouldShowContinueSetupButton(true); } if (subStep) { - BankAccounts.setBankAccountSubStep(null); - BankAccounts.setPlaidEvent(null); + setBankAccountSubStep(null); + setPlaidEvent(null); } else { Navigation.goBack(); } break; case CONST.BANK_ACCOUNT.STEP.COMPANY: - BankAccounts.clearOnfidoToken(); - BankAccounts.goToWithdrawalAccountSetupStep(CONST.BANK_ACCOUNT.STEP.REQUESTOR); + clearOnfidoToken(); + goToWithdrawalAccountSetupStep(CONST.BANK_ACCOUNT.STEP.REQUESTOR); break; case CONST.BANK_ACCOUNT.STEP.REQUESTOR: if (shouldShowOnfido) { - BankAccounts.clearOnfidoToken(); + clearOnfidoToken(); } else { - BankAccounts.goToWithdrawalAccountSetupStep(CONST.BANK_ACCOUNT.STEP.BANK_ACCOUNT); + goToWithdrawalAccountSetupStep(CONST.BANK_ACCOUNT.STEP.BANK_ACCOUNT); } break; case CONST.BANK_ACCOUNT.STEP.BENEFICIAL_OWNERS: - BankAccounts.goToWithdrawalAccountSetupStep(CONST.BANK_ACCOUNT.STEP.COMPANY); + goToWithdrawalAccountSetupStep(CONST.BANK_ACCOUNT.STEP.COMPANY); break; case CONST.BANK_ACCOUNT.STEP.ACH_CONTRACT: - BankAccounts.goToWithdrawalAccountSetupStep(CONST.BANK_ACCOUNT.STEP.BENEFICIAL_OWNERS); + goToWithdrawalAccountSetupStep(CONST.BANK_ACCOUNT.STEP.BENEFICIAL_OWNERS); break; case CONST.BANK_ACCOUNT.STEP.VALIDATION: if ([BankAccount.STATE.VERIFYING, BankAccount.STATE.SETUP].some((value) => value === achData?.state)) { - BankAccounts.goToWithdrawalAccountSetupStep(CONST.BANK_ACCOUNT.STEP.ACH_CONTRACT); + goToWithdrawalAccountSetupStep(CONST.BANK_ACCOUNT.STEP.ACH_CONTRACT); } else if (!isOffline && achData?.state === BankAccount.STATE.PENDING) { setShouldShowContinueSetupButton(true); } else { @@ -438,14 +444,14 @@ function ReimbursementAccountPage({route, policy, isLoadingPolicy}: Reimbursemen return ; } - if ((!isLoading && (isEmptyObject(policy) || !PolicyUtils.isPolicyAdmin(policy))) || PolicyUtils.isPendingDeletePolicy(policy)) { + if ((!isLoading && (isEmptyObject(policy) || !isPolicyAdmin(policy))) || isPendingDeletePolicy(policy)) { return ( ); diff --git a/src/pages/workspace/AccessOrNotFoundWrapper.tsx b/src/pages/workspace/AccessOrNotFoundWrapper.tsx index 2ef48654dbaa..db33902c2d88 100644 --- a/src/pages/workspace/AccessOrNotFoundWrapper.tsx +++ b/src/pages/workspace/AccessOrNotFoundWrapper.tsx @@ -7,50 +7,42 @@ import FullscreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useNetwork from '@hooks/useNetwork'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; -import * as IOUUtils from '@libs/IOUUtils'; +import {openWorkspace} from '@libs/actions/Policy/Policy'; +import {isValidMoneyRequestType} from '@libs/IOUUtils'; import Navigation from '@libs/Navigation/Navigation'; -import * as PolicyUtils from '@libs/PolicyUtils'; -import * as ReportUtils from '@libs/ReportUtils'; +import {canSendInvoice, isControlPolicy, isPaidGroupPolicy, isPolicyAccessible, isPolicyAdmin, isPolicyFeatureEnabled as isPolicyFeatureEnabledUtil} from '@libs/PolicyUtils'; +import {canCreateRequest} from '@libs/ReportUtils'; import NotFoundPage from '@pages/ErrorPage/NotFoundPage'; -import * as Policy from '@userActions/Policy/Policy'; import type {IOUType} from '@src/CONST'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import SCREENS from '@src/SCREENS'; -import type * as OnyxTypes from '@src/types/onyx'; +import type {Report} from '@src/types/onyx'; import type {PolicyFeatureName} from '@src/types/onyx/Policy'; +import type Policy from '@src/types/onyx/Policy'; import callOrReturn from '@src/types/utils/callOrReturn'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; const ACCESS_VARIANTS = { - [CONST.POLICY.ACCESS_VARIANTS.PAID]: (policy: OnyxEntry) => PolicyUtils.isPaidGroupPolicy(policy), - [CONST.POLICY.ACCESS_VARIANTS.CONTROL]: (policy: OnyxEntry) => PolicyUtils.isControlPolicy(policy), - [CONST.POLICY.ACCESS_VARIANTS.ADMIN]: (policy: OnyxEntry, login: string) => PolicyUtils.isPolicyAdmin(policy, login), - [CONST.IOU.ACCESS_VARIANTS.CREATE]: ( - policy: OnyxEntry, - login: string, - report: OnyxEntry, - allPolicies: NonNullable> | null, - iouType?: IOUType, - ) => + [CONST.POLICY.ACCESS_VARIANTS.PAID]: (policy: OnyxEntry) => isPaidGroupPolicy(policy), + [CONST.POLICY.ACCESS_VARIANTS.CONTROL]: (policy: OnyxEntry) => isControlPolicy(policy), + [CONST.POLICY.ACCESS_VARIANTS.ADMIN]: (policy: OnyxEntry, login: string) => isPolicyAdmin(policy, login), + [CONST.IOU.ACCESS_VARIANTS.CREATE]: (policy: OnyxEntry, login: string, report: OnyxEntry, allPolicies: NonNullable> | null, iouType?: IOUType) => !!iouType && - IOUUtils.isValidMoneyRequestType(iouType) && + isValidMoneyRequestType(iouType) && // Allow the user to submit the expense if we are submitting the expense in global menu or the report can create the expense - (isEmptyObject(report?.reportID) || ReportUtils.canCreateRequest(report, policy, iouType)) && - (iouType !== CONST.IOU.TYPE.INVOICE || PolicyUtils.canSendInvoice(allPolicies, login)), -} as const satisfies Record< - string, - (policy: OnyxTypes.Policy, login: string, report: OnyxTypes.Report, allPolicies: NonNullable> | null, iouType?: IOUType) => boolean ->; + (isEmptyObject(report?.reportID) || canCreateRequest(report, policy, iouType)) && + (iouType !== CONST.IOU.TYPE.INVOICE || canSendInvoice(allPolicies, login)), +} as const satisfies Record> | null, iouType?: IOUType) => boolean>; type AccessVariant = keyof typeof ACCESS_VARIANTS; type AccessOrNotFoundWrapperChildrenProps = { /** The report that holds the transaction */ - report: OnyxEntry; + report: OnyxEntry; /** The report currently being looked at */ - policy: OnyxEntry; + policy: OnyxEntry; /** Indicated whether the report data is loading */ isLoadingReportData: OnyxEntry; @@ -82,7 +74,7 @@ type AccessOrNotFoundWrapperProps = { iouType?: IOUType; /** The list of all policies */ - allPolicies?: OnyxCollection; + allPolicies?: OnyxCollection; } & Pick; type PageNotFoundFallbackProps = Pick & { @@ -128,7 +120,8 @@ function AccessOrNotFoundWrapper({ const [isLoadingReportData] = useOnyx(ONYXKEYS.IS_LOADING_REPORT_DATA, {initialValue: true}); const {login = ''} = useCurrentUserPersonalDetails(); const isPolicyIDInRoute = !!policyID?.length; - const isMoneyRequest = !!iouType && IOUUtils.isValidMoneyRequestType(iouType); + const isMoneyRequest = !!iouType && isValidMoneyRequestType(iouType); + const isFromGlobalCreate = !!reportID && isEmptyObject(report?.reportID); const pendingField = featureName ? policy?.pendingFields?.[featureName] : undefined; useEffect(() => { @@ -137,13 +130,13 @@ function AccessOrNotFoundWrapper({ return; } - Policy.openWorkspace(policyID, []); + openWorkspace(policyID, []); // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps }, [isPolicyIDInRoute, policyID]); const shouldShowFullScreenLoadingIndicator = !isMoneyRequest && isLoadingReportData !== false && (!Object.entries(policy ?? {}).length || !policy?.id); - const isFeatureEnabled = featureName ? PolicyUtils.isPolicyFeatureEnabled(policy, featureName) : true; + const isFeatureEnabled = featureName ? isPolicyFeatureEnabledUtil(policy, featureName) : true; const [isPolicyFeatureEnabled, setIsPolicyFeatureEnabled] = useState(isFeatureEnabled); const {isOffline} = useNetwork(); @@ -153,9 +146,8 @@ function AccessOrNotFoundWrapper({ return acc && accessFunction(policy, login, report, allPolicies ?? null, iouType); }, true); - const isPolicyNotAccessible = !PolicyUtils.isPolicyAccessible(policy); - const shouldShowNotFoundPage = (!isMoneyRequest && isPolicyNotAccessible) || !isPageAccessible || !isPolicyFeatureEnabled || shouldBeBlocked; - + const isPolicyNotAccessible = !isPolicyAccessible(policy); + const shouldShowNotFoundPage = (!isMoneyRequest && !isFromGlobalCreate && isPolicyNotAccessible) || !isPageAccessible || !isPolicyFeatureEnabled || shouldBeBlocked; // We only update the feature state if it isn't pending. // This is because the feature state changes several times during the creation of a workspace, while we are waiting for a response from the backend. // Without this, we can have unexpectedly have 'Not Found' be shown. diff --git a/src/pages/workspace/WorkspaceInitialPage.tsx b/src/pages/workspace/WorkspaceInitialPage.tsx index aae3b727057e..601fc23c0a7d 100644 --- a/src/pages/workspace/WorkspaceInitialPage.tsx +++ b/src/pages/workspace/WorkspaceInitialPage.tsx @@ -20,18 +20,31 @@ import usePrevious from '@hooks/usePrevious'; import useSingleExecution from '@hooks/useSingleExecution'; import useThemeStyles from '@hooks/useThemeStyles'; import useWaitForNavigation from '@hooks/useWaitForNavigation'; +import {confirmReadyToOpenApp} from '@libs/actions/App'; import {isConnectionInProgress} from '@libs/actions/connections'; -import * as CardUtils from '@libs/CardUtils'; -import * as CurrencyUtils from '@libs/CurrencyUtils'; +import {clearErrors, openPolicyInitialPage, removeWorkspace, updateGeneralSettings} from '@libs/actions/Policy/Policy'; +import {navigateToBankAccountRoute} from '@libs/actions/ReimbursementAccount'; +import {getCompanyFeeds} from '@libs/CardUtils'; +import {convertToDisplayString} from '@libs/CurrencyUtils'; import getTopmostRouteName from '@libs/Navigation/getTopmostRouteName'; import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; -import * as PolicyUtils from '@libs/PolicyUtils'; +import { + getWorkspaceAccountID, + goBackFromInvalidPolicy, + hasPolicyCategoriesError, + hasPolicyFeedsError as hasPolicyFeedsErrorUtil, + isPaidGroupPolicy as isPaidGroupPolicyUtil, + isPendingDeletePolicy, + isPolicyAdmin, + isPolicyFeatureEnabled, + shouldShowEmployeeListError, + shouldShowPolicy as shouldShowPolicyUtil, + shouldShowSyncError, + shouldShowTaxRateError, +} from '@libs/PolicyUtils'; import {getDefaultWorkspaceAvatar, getIcons, getPolicyExpenseChat, getReportName, getReportOfflinePendingActionAndErrors} from '@libs/ReportUtils'; import type {FullScreenNavigatorParamList} from '@navigation/types'; -import * as App from '@userActions/App'; -import * as Policy from '@userActions/Policy/Policy'; -import * as ReimbursementAccount from '@userActions/ReimbursementAccount'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -76,17 +89,17 @@ type PolicyFeatureStates = Record; function dismissError(policyID: string, pendingAction: PendingAction | undefined) { if (!policyID || pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD) { - PolicyUtils.goBackFromInvalidPolicy(); - Policy.removeWorkspace(policyID); + goBackFromInvalidPolicy(); + removeWorkspace(policyID); } else { - Policy.clearErrors(policyID); + clearErrors(policyID); } } function WorkspaceInitialPage({policyDraft, policy: policyProp, route}: WorkspaceInitialPageProps) { const styles = useThemeStyles(); const policy = policyDraft?.id ? policyDraft : policyProp; - const workspaceAccountID = PolicyUtils.getWorkspaceAccountID(policy?.id); + const workspaceAccountID = getWorkspaceAccountID(policy?.id); const [isCurrencyModalOpen, setIsCurrencyModalOpen] = useState(false); const hasPolicyCreationError = !!(policy?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD && !isEmptyObject(policy.errors)); const [cardFeeds] = useOnyx(`${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID}`); @@ -97,7 +110,7 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, route}: Workspac const [policyCategories] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${route.params?.policyID}`); const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST); const {login, accountID} = useCurrentUserPersonalDetails(); - const hasSyncError = PolicyUtils.shouldShowSyncError(policy, isConnectionInProgress(connectionSyncProgress, policy)); + const hasSyncError = shouldShowSyncError(policy, isConnectionInProgress(connectionSyncProgress, policy)); const waitForNavigate = useWaitForNavigation(); const {singleExecution, isExecuting} = useSingleExecution(); const activeRoute = useNavigationState(getTopmostRouteName); @@ -131,7 +144,7 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, route}: Workspac if (policyDraft?.id) { return; } - Policy.openPolicyInitialPage(route.params.policyID); + openPolicyInitialPage(route.params.policyID); }, [policyDraft?.id, route.params.policyID]); useNetwork({onReconnect: fetchPolicyData}); @@ -153,20 +166,20 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, route}: Workspac /** Call update workspace currency and hide the modal */ const confirmCurrencyChangeAndHideModal = useCallback(() => { - Policy.updateGeneralSettings(policyID, policyName, CONST.CURRENCY.USD); + updateGeneralSettings(policyID, policyName, CONST.CURRENCY.USD); setIsCurrencyModalOpen(false); - ReimbursementAccount.navigateToBankAccountRoute(policyID); + navigateToBankAccountRoute(policyID); }, [policyID, policyName]); - const hasMembersError = PolicyUtils.shouldShowEmployeeListError(policy); - const hasPolicyCategoryError = PolicyUtils.hasPolicyCategoriesError(policyCategories); + const hasMembersError = shouldShowEmployeeListError(policy); + const hasPolicyCategoryError = hasPolicyCategoriesError(policyCategories); const hasGeneralSettingsError = !isEmptyObject(policy?.errorFields?.name ?? {}) || !isEmptyObject(policy?.errorFields?.avatarURL ?? {}) || !isEmptyObject(policy?.errorFields?.ouputCurrency ?? {}) || !isEmptyObject(policy?.errorFields?.address ?? {}); - const shouldShowProtectedItems = PolicyUtils.isPolicyAdmin(policy, login); - const isPaidGroupPolicy = PolicyUtils.isPaidGroupPolicy(policy); + const shouldShowProtectedItems = isPolicyAdmin(policy, login); + const isPaidGroupPolicy = isPaidGroupPolicyUtil(policy); const [featureStates, setFeatureStates] = useState(policyFeatureStates); const protectedCollectPolicyMenuItems: WorkspaceMenuItem[] = []; @@ -177,7 +190,7 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, route}: Workspac setFeatureStates((currentFeatureStates) => { const newFeatureStates = {} as PolicyFeatureStates; (Object.keys(policy?.pendingFields ?? {}) as PolicyFeatureName[]).forEach((key) => { - const isFeatureEnabled = PolicyUtils.isPolicyFeatureEnabled(policy, key); + const isFeatureEnabled = isPolicyFeatureEnabled(policy, key); newFeatureStates[key] = prevPendingFields?.[key] !== policy?.pendingFields?.[key] || isOffline || !policy?.pendingFields?.[key] ? isFeatureEnabled : currentFeatureStates[key]; }); @@ -189,7 +202,7 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, route}: Workspac }, [policy, isOffline, policyFeatureStates, prevPendingFields]); useEffect(() => { - App.confirmReadyToOpenApp(); + confirmReadyToOpenApp(); }, []); if (featureStates?.[CONST.POLICY.MORE_FEATURES.ARE_DISTANCE_RATES_ENABLED]) { @@ -212,7 +225,7 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, route}: Workspac } if (featureStates?.[CONST.POLICY.MORE_FEATURES.ARE_COMPANY_CARDS_ENABLED]) { - const hasPolicyFeedsError = PolicyUtils.hasPolicyFeedsError(CardUtils.getCompanyFeeds(cardFeeds)); + const hasPolicyFeedsError = hasPolicyFeedsErrorUtil(getCompanyFeeds(cardFeeds)); protectedCollectPolicyMenuItems.push({ translationKey: 'workspace.common.companyCards', @@ -259,7 +272,7 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, route}: Workspac icon: Expensicons.InvoiceGeneric, action: singleExecution(waitForNavigate(() => Navigation.navigate(ROUTES.WORKSPACE_INVOICES.getRoute(policyID)))), routeName: SCREENS.WORKSPACE.INVOICES, - badgeText: CurrencyUtils.convertToDisplayString(policy?.invoice?.bankAccount?.stripeConnectAccountBalance ?? 0, currencyCode), + badgeText: convertToDisplayString(policy?.invoice?.bankAccount?.stripeConnectAccountBalance ?? 0, currencyCode), }); } @@ -288,7 +301,7 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, route}: Workspac icon: Expensicons.Coins, action: singleExecution(waitForNavigate(() => Navigation.navigate(ROUTES.WORKSPACE_TAXES.getRoute(policyID)))), routeName: SCREENS.WORKSPACE.TAXES, - brickRoadIndicator: PolicyUtils.shouldShowTaxRateError(policy) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined, + brickRoadIndicator: shouldShowTaxRateError(policy) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined, }); } @@ -340,17 +353,17 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, route}: Workspac const prevProtectedMenuItems = usePrevious(protectedCollectPolicyMenuItems); const enabledItem = protectedCollectPolicyMenuItems.find((curItem) => !prevProtectedMenuItems.some((prevItem) => curItem.routeName === prevItem.routeName)); - const shouldShowPolicy = useMemo(() => PolicyUtils.shouldShowPolicy(policy, false, currentUserLogin), [policy, currentUserLogin]); - const prevShouldShowPolicy = useMemo(() => PolicyUtils.shouldShowPolicy(prevPolicy, false, currentUserLogin), [prevPolicy, currentUserLogin]); + const shouldShowPolicy = useMemo(() => shouldShowPolicyUtil(policy, false, currentUserLogin), [policy, currentUserLogin]); + const prevShouldShowPolicy = useMemo(() => shouldShowPolicyUtil(prevPolicy, false, currentUserLogin), [prevPolicy, currentUserLogin]); // We check shouldShowPolicy and prevShouldShowPolicy to prevent the NotFound view from showing right after we delete the workspace // eslint-disable-next-line rulesdir/no-negated-variables const shouldShowNotFoundPage = isEmptyObject(policy) || (!shouldShowPolicy && !prevShouldShowPolicy); useEffect(() => { - if (isEmptyObject(prevPolicy) || PolicyUtils.isPendingDeletePolicy(prevPolicy) || !PolicyUtils.isPendingDeletePolicy(policy)) { + if (isEmptyObject(prevPolicy) || isPendingDeletePolicy(prevPolicy) || !isPendingDeletePolicy(policy)) { return; } - PolicyUtils.goBackFromInvalidPolicy(); + goBackFromInvalidPolicy(); }, [policy, prevPolicy]); // We are checking if the user can access the route. From cb1af904c1d2e56646c4309f666434613b60b2a5 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Thu, 23 Jan 2025 21:59:45 +0300 Subject: [PATCH 5/6] fix lint --- src/pages/workspace/WorkspacePageWithSections.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/pages/workspace/WorkspacePageWithSections.tsx b/src/pages/workspace/WorkspacePageWithSections.tsx index a4630b0bf421..6cf1d49ebd14 100644 --- a/src/pages/workspace/WorkspacePageWithSections.tsx +++ b/src/pages/workspace/WorkspacePageWithSections.tsx @@ -13,10 +13,10 @@ import ScrollViewWithContext from '@components/ScrollViewWithContext'; import usePrevious from '@hooks/usePrevious'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useThemeStyles from '@hooks/useThemeStyles'; +import {openWorkspaceView} from '@libs/actions/BankAccounts'; import BankAccount from '@libs/models/BankAccount'; import Navigation from '@libs/Navigation/Navigation'; -import * as PolicyUtils from '@libs/PolicyUtils'; -import * as BankAccounts from '@userActions/BankAccounts'; +import {isPolicyAdmin, shouldShowPolicy as shouldShowPolicyUtil} from '@libs/PolicyUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Route} from '@src/ROUTES'; @@ -91,7 +91,7 @@ function fetchData(policyID: string, skipVBBACal?: boolean) { return; } - BankAccounts.openWorkspaceView(policyID); + openWorkspaceView(policyID); } function WorkspacePageWithSections({ @@ -149,8 +149,8 @@ function WorkspacePageWithSections({ }, [policyID, shouldSkipVBBACall]), ); - const shouldShowPolicy = useMemo(() => PolicyUtils.shouldShowPolicy(policy, false, currentUserLogin), [policy, currentUserLogin]); - const prevShouldShowPolicy = useMemo(() => PolicyUtils.shouldShowPolicy(prevPolicy, false, currentUserLogin), [prevPolicy, currentUserLogin]); + const shouldShowPolicy = useMemo(() => shouldShowPolicyUtil(policy, false, currentUserLogin), [policy, currentUserLogin]); + const prevShouldShowPolicy = useMemo(() => shouldShowPolicyUtil(prevPolicy, false, currentUserLogin), [prevPolicy, currentUserLogin]); const shouldShow = useMemo(() => { // If the policy object doesn't exist or contains only error data, we shouldn't display it. if (((isEmptyObject(policy) || (Object.keys(policy).length === 1 && !isEmptyObject(policy.errors))) && isEmptyObject(policyDraft)) || shouldShowNotFoundPage) { @@ -158,7 +158,7 @@ function WorkspacePageWithSections({ } // We check shouldShowPolicy and prevShouldShowPolicy to prevent the NotFound view from showing right after we delete the workspace - return (!isEmptyObject(policy) && !PolicyUtils.isPolicyAdmin(policy) && !shouldShowNonAdmin) || (!shouldShowPolicy && !prevShouldShowPolicy); + return (!isEmptyObject(policy) && !isPolicyAdmin(policy) && !shouldShowNonAdmin) || (!shouldShowPolicy && !prevShouldShowPolicy); // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps }, [policy, shouldShowNonAdmin, shouldShowPolicy, prevShouldShowPolicy]); From d3b0ccbbd6a9ae315da483e94b358ecdcd169cc1 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Thu, 23 Jan 2025 22:05:41 +0300 Subject: [PATCH 6/6] fix lint --- src/pages/workspace/WorkspacePageWithSections.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/workspace/WorkspacePageWithSections.tsx b/src/pages/workspace/WorkspacePageWithSections.tsx index 6cf1d49ebd14..8867e32fa8ef 100644 --- a/src/pages/workspace/WorkspacePageWithSections.tsx +++ b/src/pages/workspace/WorkspacePageWithSections.tsx @@ -122,7 +122,7 @@ function WorkspacePageWithSections({ threeDotsAnchorPosition, }: WorkspacePageWithSectionsProps) { const styles = useThemeStyles(); - const policyID = route.params?.policyID ?? '-1'; + const policyID = route.params?.policyID ?? `${CONST.DEFAULT_NUMBER_ID}`; const [user] = useOnyx(ONYXKEYS.USER); const [reimbursementAccount = CONST.REIMBURSEMENT_ACCOUNT.DEFAULT_DATA] = useOnyx(ONYXKEYS.REIMBURSEMENT_ACCOUNT); @@ -130,7 +130,7 @@ function WorkspacePageWithSections({ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing const isLoading = (reimbursementAccount?.isLoading || isPageLoading) ?? true; - const achState = reimbursementAccount?.achData?.state ?? '-1'; + const achState = reimbursementAccount?.achData?.state; const isUsingECard = user?.isUsingExpensifyCard ?? false; const hasVBA = achState === BankAccount.STATE.OPEN; const content = typeof children === 'function' ? children(hasVBA, policyID, isUsingECard) : children;