From 7a0ba43f35f2ce9072ce9f0e2fda065c46cd9e78 Mon Sep 17 00:00:00 2001 From: war-in Date: Tue, 2 Jul 2024 18:34:15 +0200 Subject: [PATCH 01/18] update SelectionScreen and add Offline with feedback to components --- src/components/SelectionScreen.tsx | 50 ++++++++++---- src/libs/actions/Policy/Policy.ts | 5 ++ .../intacct/export/SageIntacctDatePage.tsx | 5 ++ .../export/SageIntacctDefaultVendorPage.tsx | 10 ++- .../SageIntacctPreferredExporterPage.tsx | 5 ++ .../SageIntacctReimbursableExpensesPage.tsx | 69 +++++++++++-------- 6 files changed, 103 insertions(+), 41 deletions(-) diff --git a/src/components/SelectionScreen.tsx b/src/components/SelectionScreen.tsx index af1cdfd171ea..bfde03a5b84d 100644 --- a/src/components/SelectionScreen.tsx +++ b/src/components/SelectionScreen.tsx @@ -1,11 +1,16 @@ import {isEmpty} from 'lodash'; import React from 'react'; +import {StyleProp, View, ViewStyle} from 'react-native'; +import OfflineWithFeedback from '@components/OfflineWithFeedback'; import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; import * as PolicyUtils from '@libs/PolicyUtils'; import type {AccessVariant} from '@pages/workspace/AccessOrNotFoundWrapper'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import type {TranslationPaths} from '@src/languages/types'; +import type * as OnyxCommon from '@src/types/onyx/OnyxCommon'; import type {ConnectionName, PolicyFeatureName} from '@src/types/onyx/Policy'; +import {ReceiptErrors} from '@src/types/onyx/Transaction'; import HeaderWithBackButton from './HeaderWithBackButton'; import ScreenWrapper from './ScreenWrapper'; import SelectionList from './SelectionList'; @@ -63,6 +68,15 @@ type SelectionScreenProps = { /** Name of the current connection */ connectionName: ConnectionName; + + /** The errors to display */ + errors?: OnyxCommon.Errors | ReceiptErrors | null; + + /** Additional style object for the error row */ + errorRowStyles?: StyleProp; + + /** A function to run when the X button next to the error is clicked */ + onClose?: () => void; }; function SelectionScreen({ @@ -81,8 +95,12 @@ function SelectionScreen({ featureName, shouldBeBlocked, connectionName, + errors, + errorRowStyles, + onClose, }: SelectionScreenProps) { const {translate} = useLocalize(); + const styles = useThemeStyles(); const policy = PolicyUtils.getPolicy(policyID); const isConnectionEmpty = isEmpty(policy?.connections?.[connectionName]); @@ -95,24 +113,32 @@ function SelectionScreen({ shouldBeBlocked={isConnectionEmpty || shouldBeBlocked} > - + + + ); diff --git a/src/libs/actions/Policy/Policy.ts b/src/libs/actions/Policy/Policy.ts index 1bb53fbfa002..80a90c4e7867 100644 --- a/src/libs/actions/Policy/Policy.ts +++ b/src/libs/actions/Policy/Policy.ts @@ -527,6 +527,10 @@ function clearXeroErrorField(policyID: string, fieldName: string) { Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {connections: {xero: {config: {errorFields: {[fieldName]: null}}}}}); } +function clearSageIntacctExportErrorField(policyID: string, fieldName: string) { + Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {connections: {intacct: {config: {export: {errorFields: {[fieldName]: null}}}}}}); +} + function clearNetSuiteErrorField(policyID: string, fieldName: string) { Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {connections: {netsuite: {options: {config: {errorFields: {[fieldName]: null}}}}}}); } @@ -3031,6 +3035,7 @@ export { generateCustomUnitID, clearQBOErrorField, clearXeroErrorField, + clearSageIntacctExportErrorField, clearNetSuiteErrorField, clearWorkspaceReimbursementErrors, setWorkspaceCurrencyDefault, diff --git a/src/pages/workspace/accounting/intacct/export/SageIntacctDatePage.tsx b/src/pages/workspace/accounting/intacct/export/SageIntacctDatePage.tsx index 51d08423e530..d4e504b1dbc6 100644 --- a/src/pages/workspace/accounting/intacct/export/SageIntacctDatePage.tsx +++ b/src/pages/workspace/accounting/intacct/export/SageIntacctDatePage.tsx @@ -8,10 +8,12 @@ import type {SelectorType} from '@components/SelectionScreen'; import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; +import * as ErrorUtils from '@libs/ErrorUtils'; import Navigation from '@navigation/Navigation'; import type {WithPolicyProps} from '@pages/workspace/withPolicy'; import withPolicyConnections from '@pages/workspace/withPolicyConnections'; import {updateSageIntacctExportDate} from '@userActions/connections/SageIntacct'; +import * as Policy from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; @@ -65,6 +67,9 @@ function SageIntacctDatePage({policy}: WithPolicyProps) { featureName={CONST.POLICY.MORE_FEATURES.ARE_CONNECTIONS_ENABLED} onBackButtonPress={() => Navigation.goBack(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_EXPORT.getRoute(policyID))} connectionName={CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT} + errors={ErrorUtils.getLatestErrorField(exportConfig ?? {}, CONST.SAGE_INTACCT_CONFIG.EXPORT_DATE)} + errorRowStyles={[styles.ph5, styles.mv2]} + onClose={() => Policy.clearSageIntacctExportErrorField(policyID, CONST.SAGE_INTACCT_CONFIG.EXPORT_DATE)} /> ); } diff --git a/src/pages/workspace/accounting/intacct/export/SageIntacctDefaultVendorPage.tsx b/src/pages/workspace/accounting/intacct/export/SageIntacctDefaultVendorPage.tsx index d32c8472f944..51b62cb78fe9 100644 --- a/src/pages/workspace/accounting/intacct/export/SageIntacctDefaultVendorPage.tsx +++ b/src/pages/workspace/accounting/intacct/export/SageIntacctDefaultVendorPage.tsx @@ -10,11 +10,13 @@ import SelectionScreen from '@components/SelectionScreen'; import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; +import * as ErrorUtils from '@libs/ErrorUtils'; import Navigation from '@libs/Navigation/Navigation'; import {getSageIntacctNonReimbursableActiveDefaultVendor, getSageIntacctVendors} from '@libs/PolicyUtils'; import type {SettingsNavigatorParamList} from '@navigation/types'; import variables from '@styles/variables'; import {updateSageIntacctDefaultVendor} from '@userActions/connections/SageIntacct'; +import * as Policy from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; @@ -29,20 +31,21 @@ function SageIntacctDefaultVendorPage({route}: SageIntacctDefaultVendorPageProps const policyID = route.params.policyID ?? '-1'; const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`); + const {export: exportConfig} = policy?.connections?.intacct?.config ?? {}; const isReimbursable = route.params.reimbursable === CONST.SAGE_INTACCT_CONFIG.REIMBURSABLE; let defaultVendor; let settingName: keyof Connections['intacct']['config']['export']; if (!isReimbursable) { - const {nonReimbursable} = policy?.connections?.intacct?.config.export ?? {}; + const {nonReimbursable} = exportConfig ?? {}; defaultVendor = getSageIntacctNonReimbursableActiveDefaultVendor(policy); settingName = nonReimbursable === CONST.SAGE_INTACCT_NON_REIMBURSABLE_EXPENSE_TYPE.CREDIT_CARD_CHARGE ? CONST.SAGE_INTACCT_CONFIG.NON_REIMBURSABLE_CREDIT_CARD_VENDOR : CONST.SAGE_INTACCT_CONFIG.NON_REIMBURSABLE_VENDOR; } else { - const {reimbursableExpenseReportDefaultVendor} = policy?.connections?.intacct?.config.export ?? {}; + const {reimbursableExpenseReportDefaultVendor} = exportConfig ?? {}; defaultVendor = reimbursableExpenseReportDefaultVendor; settingName = CONST.SAGE_INTACCT_CONFIG.REIMBURSABLE_VENDOR; } @@ -103,6 +106,9 @@ function SageIntacctDefaultVendorPage({route}: SageIntacctDefaultVendorPageProps listEmptyContent={listEmptyContent} accessVariants={[CONST.POLICY.ACCESS_VARIANTS.ADMIN, CONST.POLICY.ACCESS_VARIANTS.PAID]} connectionName={CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT} + errors={ErrorUtils.getLatestErrorField(exportConfig ?? {}, settingName)} + errorRowStyles={[styles.ph5, styles.mv2]} + onClose={() => Policy.clearSageIntacctExportErrorField(policyID, settingName)} /> ); } diff --git a/src/pages/workspace/accounting/intacct/export/SageIntacctPreferredExporterPage.tsx b/src/pages/workspace/accounting/intacct/export/SageIntacctPreferredExporterPage.tsx index abbb87982a5e..6a7596f08c38 100644 --- a/src/pages/workspace/accounting/intacct/export/SageIntacctPreferredExporterPage.tsx +++ b/src/pages/workspace/accounting/intacct/export/SageIntacctPreferredExporterPage.tsx @@ -8,11 +8,13 @@ import Text from '@components/Text'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; +import * as ErrorUtils from '@libs/ErrorUtils'; import {getAdminEmployees, isExpensifyTeam} from '@libs/PolicyUtils'; import Navigation from '@navigation/Navigation'; import type {WithPolicyProps} from '@pages/workspace/withPolicy'; import withPolicyConnections from '@pages/workspace/withPolicyConnections'; import {updateSageIntacctExporter} from '@userActions/connections/SageIntacct'; +import * as Policy from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; @@ -95,6 +97,9 @@ function SageIntacctPreferredExporterPage({policy}: WithPolicyProps) { onBackButtonPress={() => Navigation.goBack(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_EXPORT.getRoute(policyID))} title="workspace.sageIntacct.preferredExporter" connectionName={CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT} + errors={ErrorUtils.getLatestErrorField(exportConfiguration ?? {}, CONST.SAGE_INTACCT_CONFIG.EXPORTER)} + errorRowStyles={[styles.ph5, styles.mv2]} + onClose={() => Policy.clearSageIntacctExportErrorField(policyID, CONST.SAGE_INTACCT_CONFIG.EXPORTER)} /> ); } diff --git a/src/pages/workspace/accounting/intacct/export/SageIntacctReimbursableExpensesPage.tsx b/src/pages/workspace/accounting/intacct/export/SageIntacctReimbursableExpensesPage.tsx index 1bdac2b3eb62..a707e9d7b74f 100644 --- a/src/pages/workspace/accounting/intacct/export/SageIntacctReimbursableExpensesPage.tsx +++ b/src/pages/workspace/accounting/intacct/export/SageIntacctReimbursableExpensesPage.tsx @@ -10,11 +10,13 @@ import type {ListItem} from '@components/SelectionList/types'; import type {SelectorType} from '@components/SelectionScreen'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; +import * as ErrorUtils from '@libs/ErrorUtils'; import Navigation from '@navigation/Navigation'; import type {WithPolicyProps} from '@pages/workspace/withPolicy'; import withPolicyConnections from '@pages/workspace/withPolicyConnections'; import ToggleSettingOptionRow from '@pages/workspace/workflows/ToggleSettingsOptionRow'; import {updateSageIntacctDefaultVendor, updateSageIntacctReimbursableExpensesExportDestination} from '@userActions/connections/SageIntacct'; +import * as Policy from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; import type {SageIntacctDataElementWithValue} from '@src/types/onyx/Policy'; @@ -89,34 +91,47 @@ function SageIntacctReimbursableExpensesPage({policy}: WithPolicyProps) { displayName={SageIntacctReimbursableExpensesPage.displayName} policyID={policyID} connectionName={CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT} + contentContainerStyle={[styles.containerWithSpaceBetween, styles.pointerEventsBoxNone]} + shouldUseScrollView={false} + shouldIncludeSafeAreaPaddingBottom={true} > - selectReimbursableDestination(selection as MenuListItem)} - sections={[{data}]} - ListItem={RadioListItem} - showScrollIndicator - shouldShowTooltips={false} - listFooterContent={ - reimbursable === CONST.SAGE_INTACCT_REIMBURSABLE_EXPENSE_TYPE.EXPENSE_REPORT ? ( - - { - const vendor = enabled ? policy?.connections?.intacct?.data?.vendors?.[0].id ?? null : null; - updateSageIntacctDefaultVendor(policyID, CONST.SAGE_INTACCT_CONFIG.REIMBURSABLE_VENDOR, vendor); - }} - wrapperStyle={[styles.ph5, styles.pv3]} - pendingAction={config?.export?.pendingFields?.reimbursableExpenseReportDefaultVendor} - /> - {!!reimbursableExpenseReportDefaultVendor && defaultVendor} - - ) : undefined - } - /> + Policy.clearSageIntacctExportErrorField(policyID, CONST.SAGE_INTACCT_CONFIG.REIMBURSABLE)} + style={[styles.containerWithSpaceBetween, styles.pointerEventsBoxNone]} + contentContainerStyle={[styles.containerWithSpaceBetween, styles.pointerEventsBoxNone]} + > + selectReimbursableDestination(selection as MenuListItem)} + sections={[{data}]} + ListItem={RadioListItem} + showScrollIndicator + shouldShowTooltips={false} + listFooterContent={ + reimbursable === CONST.SAGE_INTACCT_REIMBURSABLE_EXPENSE_TYPE.EXPENSE_REPORT ? ( + + { + const vendor = enabled ? policy?.connections?.intacct?.data?.vendors?.[0].id ?? null : null; + updateSageIntacctDefaultVendor(policyID, CONST.SAGE_INTACCT_CONFIG.REIMBURSABLE_VENDOR, vendor); + }} + wrapperStyle={[styles.ph5, styles.pv3]} + pendingAction={config?.export?.pendingFields?.reimbursableExpenseReportDefaultVendor} + errors={ErrorUtils.getLatestErrorField(config?.export ?? {}, CONST.SAGE_INTACCT_CONFIG.REIMBURSABLE_VENDOR)} + onCloseError={() => Policy.clearSageIntacctExportErrorField(policyID, CONST.SAGE_INTACCT_CONFIG.REIMBURSABLE_VENDOR)} + /> + {!!reimbursableExpenseReportDefaultVendor && defaultVendor} + + ) : undefined + } + /> + ); } From 90883614d9fe949a6ccc21724b1a4b0cab6d6d90 Mon Sep 17 00:00:00 2001 From: war-in Date: Tue, 2 Jul 2024 18:42:59 +0200 Subject: [PATCH 02/18] add offline with feedback to remaining pages --- ...ctNonReimbursableCreditCardAccountPage.tsx | 5 ++ ...SageIntacctNonReimbursableExpensesPage.tsx | 75 +++++++++++-------- 2 files changed, 49 insertions(+), 31 deletions(-) diff --git a/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableCreditCardAccountPage.tsx b/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableCreditCardAccountPage.tsx index 1da1627189d5..5c6e708cfd54 100644 --- a/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableCreditCardAccountPage.tsx +++ b/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableCreditCardAccountPage.tsx @@ -8,12 +8,14 @@ import SelectionScreen from '@components/SelectionScreen'; import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; +import * as ErrorUtils from '@libs/ErrorUtils'; import Navigation from '@libs/Navigation/Navigation'; import {getSageIntacctCreditCards} from '@libs/PolicyUtils'; import type {WithPolicyConnectionsProps} from '@pages/workspace/withPolicyConnections'; import withPolicyConnections from '@pages/workspace/withPolicyConnections'; import variables from '@styles/variables'; import {updateSageIntacctNonreimbursableExpensesExportAccount} from '@userActions/connections/SageIntacct'; +import * as Policy from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; @@ -75,6 +77,9 @@ function SageIntacctNonReimbursableCreditCardAccountPage({policy}: WithPolicyCon listEmptyContent={listEmptyContent} accessVariants={[CONST.POLICY.ACCESS_VARIANTS.ADMIN, CONST.POLICY.ACCESS_VARIANTS.PAID]} connectionName={CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT} + errors={ErrorUtils.getLatestErrorField(exportConfig ?? {}, CONST.SAGE_INTACCT_CONFIG.NON_REIMBURSABLE_ACCOUNT)} + errorRowStyles={[styles.ph5, styles.mt2]} + onClose={() => Policy.clearSageIntacctExportErrorField(policyID, CONST.SAGE_INTACCT_CONFIG.NON_REIMBURSABLE_ACCOUNT)} /> ); } diff --git a/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableExpensesPage.tsx b/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableExpensesPage.tsx index ff1ca7a36634..76e6316023e9 100644 --- a/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableExpensesPage.tsx +++ b/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableExpensesPage.tsx @@ -10,12 +10,14 @@ import type {ListItem} from '@components/SelectionList/types'; import type {SelectorType} from '@components/SelectionScreen'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; +import * as ErrorUtils from '@libs/ErrorUtils'; import {getSageIntacctNonReimbursableActiveDefaultVendor} from '@libs/PolicyUtils'; import Navigation from '@navigation/Navigation'; import type {WithPolicyProps} from '@pages/workspace/withPolicy'; import withPolicyConnections from '@pages/workspace/withPolicyConnections'; import ToggleSettingOptionRow from '@pages/workspace/workflows/ToggleSettingsOptionRow'; import {updateSageIntacctDefaultVendor, updateSageIntacctNonreimbursableExpensesExportDestination} from '@userActions/connections/SageIntacct'; +import * as Policy from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; import type {SageIntacctDataElementWithValue} from '@src/types/onyx/Policy'; @@ -115,38 +117,49 @@ function SageIntacctNonReimbursableExpensesPage({policy}: WithPolicyProps) { displayName={SageIntacctNonReimbursableExpensesPage.displayName} policyID={policyID} connectionName={CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT} + contentContainerStyle={[styles.containerWithSpaceBetween, styles.pointerEventsBoxNone]} + shouldUseScrollView={false} + shouldIncludeSafeAreaPaddingBottom={true} > - selectNonReimbursableExpense(selection as MenuListItem)} - sections={[{data}]} - ListItem={RadioListItem} - showScrollIndicator - shouldShowTooltips={false} - listFooterContent={ - - {config?.export.nonReimbursable === CONST.SAGE_INTACCT_NON_REIMBURSABLE_EXPENSE_TYPE.VENDOR_BILL && defaultVendor} - {config?.export.nonReimbursable === CONST.SAGE_INTACCT_NON_REIMBURSABLE_EXPENSE_TYPE.CREDIT_CARD_CHARGE && ( - - {creditCardAccount} - { - const vendor = enabled ? policy?.connections?.intacct?.data?.vendors?.[0].id ?? null : null; - updateSageIntacctDefaultVendor(policyID, CONST.SAGE_INTACCT_CONFIG.NON_REIMBURSABLE_CREDIT_CARD_VENDOR, vendor); - }} - wrapperStyle={[styles.ph5, styles.pv3]} - pendingAction={config?.export?.pendingFields?.nonReimbursableCreditCardChargeDefaultVendor} - /> - {!!config?.export.nonReimbursableCreditCardChargeDefaultVendor && defaultVendor} - - )} - - } - /> + Policy.clearSageIntacctExportErrorField(policyID, CONST.SAGE_INTACCT_CONFIG.NON_REIMBURSABLE)} + style={[styles.containerWithSpaceBetween, styles.pointerEventsBoxNone]} + contentContainerStyle={[styles.containerWithSpaceBetween, styles.pointerEventsBoxNone]} + > + selectNonReimbursableExpense(selection as MenuListItem)} + sections={[{data}]} + ListItem={RadioListItem} + showScrollIndicator + shouldShowTooltips={false} + listFooterContent={ + + {config?.export.nonReimbursable === CONST.SAGE_INTACCT_NON_REIMBURSABLE_EXPENSE_TYPE.VENDOR_BILL && defaultVendor} + {config?.export.nonReimbursable === CONST.SAGE_INTACCT_NON_REIMBURSABLE_EXPENSE_TYPE.CREDIT_CARD_CHARGE && ( + + {creditCardAccount} + { + const vendor = enabled ? policy?.connections?.intacct?.data?.vendors?.[0].id ?? null : null; + updateSageIntacctDefaultVendor(policyID, CONST.SAGE_INTACCT_CONFIG.NON_REIMBURSABLE_CREDIT_CARD_VENDOR, vendor); + }} + wrapperStyle={[styles.ph5, styles.pv3]} + pendingAction={config?.export?.pendingFields?.nonReimbursableCreditCardChargeDefaultVendor} + /> + {!!config?.export.nonReimbursableCreditCardChargeDefaultVendor && defaultVendor} + + )} + + } + /> + ); } From 91c62d95fd0a478f5e38084301cb3cae6f422b3a Mon Sep 17 00:00:00 2001 From: war-in Date: Wed, 3 Jul 2024 16:10:40 +0200 Subject: [PATCH 03/18] show errors in correct places --- src/components/SelectionScreen.tsx | 50 +++++++++-------- ...SageIntacctNonReimbursableExpensesPage.tsx | 56 +++++++++---------- .../SageIntacctReimbursableExpensesPage.tsx | 52 +++++++++-------- 3 files changed, 79 insertions(+), 79 deletions(-) diff --git a/src/components/SelectionScreen.tsx b/src/components/SelectionScreen.tsx index bfde03a5b84d..0a0128dc1c12 100644 --- a/src/components/SelectionScreen.tsx +++ b/src/components/SelectionScreen.tsx @@ -1,7 +1,7 @@ import {isEmpty} from 'lodash'; import React from 'react'; -import {StyleProp, View, ViewStyle} from 'react-native'; -import OfflineWithFeedback from '@components/OfflineWithFeedback'; +import type {StyleProp, ViewStyle} from 'react-native'; +import {View} from 'react-native'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import * as PolicyUtils from '@libs/PolicyUtils'; @@ -10,8 +10,9 @@ import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import type {TranslationPaths} from '@src/languages/types'; import type * as OnyxCommon from '@src/types/onyx/OnyxCommon'; import type {ConnectionName, PolicyFeatureName} from '@src/types/onyx/Policy'; -import {ReceiptErrors} from '@src/types/onyx/Transaction'; +import type {ReceiptErrors} from '@src/types/onyx/Transaction'; import HeaderWithBackButton from './HeaderWithBackButton'; +import OfflineWithFeedback from './OfflineWithFeedback'; import ScreenWrapper from './ScreenWrapper'; import SelectionList from './SelectionList'; import type RadioListItem from './SelectionList/RadioListItem'; @@ -113,32 +114,35 @@ function SelectionScreen({ shouldBeBlocked={isConnectionEmpty || shouldBeBlocked} > - - - + + + + + ); diff --git a/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableExpensesPage.tsx b/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableExpensesPage.tsx index 76e6316023e9..f97e9b156bcc 100644 --- a/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableExpensesPage.tsx +++ b/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableExpensesPage.tsx @@ -117,16 +117,15 @@ function SageIntacctNonReimbursableExpensesPage({policy}: WithPolicyProps) { displayName={SageIntacctNonReimbursableExpensesPage.displayName} policyID={policyID} connectionName={CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT} - contentContainerStyle={[styles.containerWithSpaceBetween, styles.pointerEventsBoxNone]} shouldUseScrollView={false} - shouldIncludeSafeAreaPaddingBottom={true} + shouldIncludeSafeAreaPaddingBottom > Policy.clearSageIntacctExportErrorField(policyID, CONST.SAGE_INTACCT_CONFIG.NON_REIMBURSABLE)} - style={[styles.containerWithSpaceBetween, styles.pointerEventsBoxNone]} - contentContainerStyle={[styles.containerWithSpaceBetween, styles.pointerEventsBoxNone]} + style={[styles.flexGrow1, styles.flexShrink1]} + contentContainerStyle={[styles.flexGrow1, styles.flexShrink1]} > selectNonReimbursableExpense(selection as MenuListItem)} @@ -134,32 +133,31 @@ function SageIntacctNonReimbursableExpensesPage({policy}: WithPolicyProps) { ListItem={RadioListItem} showScrollIndicator shouldShowTooltips={false} - listFooterContent={ - - {config?.export.nonReimbursable === CONST.SAGE_INTACCT_NON_REIMBURSABLE_EXPENSE_TYPE.VENDOR_BILL && defaultVendor} - {config?.export.nonReimbursable === CONST.SAGE_INTACCT_NON_REIMBURSABLE_EXPENSE_TYPE.CREDIT_CARD_CHARGE && ( - - {creditCardAccount} - { - const vendor = enabled ? policy?.connections?.intacct?.data?.vendors?.[0].id ?? null : null; - updateSageIntacctDefaultVendor(policyID, CONST.SAGE_INTACCT_CONFIG.NON_REIMBURSABLE_CREDIT_CARD_VENDOR, vendor); - }} - wrapperStyle={[styles.ph5, styles.pv3]} - pendingAction={config?.export?.pendingFields?.nonReimbursableCreditCardChargeDefaultVendor} - /> - {!!config?.export.nonReimbursableCreditCardChargeDefaultVendor && defaultVendor} - - )} - - } + containerStyle={[styles.flexReset, styles.flexGrow1, styles.flexShrink1]} /> + + {config?.export.nonReimbursable === CONST.SAGE_INTACCT_NON_REIMBURSABLE_EXPENSE_TYPE.VENDOR_BILL && defaultVendor} + {config?.export.nonReimbursable === CONST.SAGE_INTACCT_NON_REIMBURSABLE_EXPENSE_TYPE.CREDIT_CARD_CHARGE && ( + + {creditCardAccount} + { + const vendor = enabled ? policy?.connections?.intacct?.data?.vendors?.[0].id ?? null : null; + updateSageIntacctDefaultVendor(policyID, CONST.SAGE_INTACCT_CONFIG.NON_REIMBURSABLE_CREDIT_CARD_VENDOR, vendor); + }} + wrapperStyle={[styles.ph5, styles.pv3]} + pendingAction={config?.export?.pendingFields?.nonReimbursableCreditCardChargeDefaultVendor} + /> + {!!config?.export.nonReimbursableCreditCardChargeDefaultVendor && defaultVendor} + + )} + ); } diff --git a/src/pages/workspace/accounting/intacct/export/SageIntacctReimbursableExpensesPage.tsx b/src/pages/workspace/accounting/intacct/export/SageIntacctReimbursableExpensesPage.tsx index a707e9d7b74f..fd6ea4c00996 100644 --- a/src/pages/workspace/accounting/intacct/export/SageIntacctReimbursableExpensesPage.tsx +++ b/src/pages/workspace/accounting/intacct/export/SageIntacctReimbursableExpensesPage.tsx @@ -91,16 +91,15 @@ function SageIntacctReimbursableExpensesPage({policy}: WithPolicyProps) { displayName={SageIntacctReimbursableExpensesPage.displayName} policyID={policyID} connectionName={CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT} - contentContainerStyle={[styles.containerWithSpaceBetween, styles.pointerEventsBoxNone]} shouldUseScrollView={false} - shouldIncludeSafeAreaPaddingBottom={true} + shouldIncludeSafeAreaPaddingBottom > Policy.clearSageIntacctExportErrorField(policyID, CONST.SAGE_INTACCT_CONFIG.REIMBURSABLE)} - style={[styles.containerWithSpaceBetween, styles.pointerEventsBoxNone]} - contentContainerStyle={[styles.containerWithSpaceBetween, styles.pointerEventsBoxNone]} + style={[styles.flexGrow1, styles.flexShrink1]} + contentContainerStyle={[styles.flexGrow1, styles.flexShrink1]} > selectReimbursableDestination(selection as MenuListItem)} @@ -108,30 +107,29 @@ function SageIntacctReimbursableExpensesPage({policy}: WithPolicyProps) { ListItem={RadioListItem} showScrollIndicator shouldShowTooltips={false} - listFooterContent={ - reimbursable === CONST.SAGE_INTACCT_REIMBURSABLE_EXPENSE_TYPE.EXPENSE_REPORT ? ( - - { - const vendor = enabled ? policy?.connections?.intacct?.data?.vendors?.[0].id ?? null : null; - updateSageIntacctDefaultVendor(policyID, CONST.SAGE_INTACCT_CONFIG.REIMBURSABLE_VENDOR, vendor); - }} - wrapperStyle={[styles.ph5, styles.pv3]} - pendingAction={config?.export?.pendingFields?.reimbursableExpenseReportDefaultVendor} - errors={ErrorUtils.getLatestErrorField(config?.export ?? {}, CONST.SAGE_INTACCT_CONFIG.REIMBURSABLE_VENDOR)} - onCloseError={() => Policy.clearSageIntacctExportErrorField(policyID, CONST.SAGE_INTACCT_CONFIG.REIMBURSABLE_VENDOR)} - /> - {!!reimbursableExpenseReportDefaultVendor && defaultVendor} - - ) : undefined - } + containerStyle={[styles.flexReset, styles.flexGrow1, styles.flexShrink1]} /> + {reimbursable === CONST.SAGE_INTACCT_REIMBURSABLE_EXPENSE_TYPE.EXPENSE_REPORT && ( + + { + const vendor = enabled ? policy?.connections?.intacct?.data?.vendors?.[0].id ?? null : null; + updateSageIntacctDefaultVendor(policyID, CONST.SAGE_INTACCT_CONFIG.REIMBURSABLE_VENDOR, vendor); + }} + wrapperStyle={[styles.ph5, styles.pv3]} + pendingAction={config?.export?.pendingFields?.reimbursableExpenseReportDefaultVendor} + errors={ErrorUtils.getLatestErrorField(config?.export ?? {}, CONST.SAGE_INTACCT_CONFIG.REIMBURSABLE_VENDOR)} + onCloseError={() => Policy.clearSageIntacctExportErrorField(policyID, CONST.SAGE_INTACCT_CONFIG.REIMBURSABLE_VENDOR)} + /> + {!!reimbursableExpenseReportDefaultVendor && defaultVendor} + + )} ); } From 95da618e7e9e8e93351b93a292ec87dcdd1b3da2 Mon Sep 17 00:00:00 2001 From: war-in Date: Thu, 4 Jul 2024 09:01:13 +0200 Subject: [PATCH 04/18] style selection screen correctly --- src/components/SelectionScreen.tsx | 42 ++++++++++++++---------------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/src/components/SelectionScreen.tsx b/src/components/SelectionScreen.tsx index 0a0128dc1c12..953a26ce28d9 100644 --- a/src/components/SelectionScreen.tsx +++ b/src/components/SelectionScreen.tsx @@ -1,7 +1,6 @@ import {isEmpty} from 'lodash'; import React from 'react'; import type {StyleProp, ViewStyle} from 'react-native'; -import {View} from 'react-native'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import * as PolicyUtils from '@libs/PolicyUtils'; @@ -121,28 +120,25 @@ function SelectionScreen({ title={translate(title)} onBackButtonPress={onBackButtonPress} /> - - - - - + + + ); From c4208932909f06ba5403015a80a10f468df56cad Mon Sep 17 00:00:00 2001 From: war-in Date: Thu, 4 Jul 2024 10:42:30 +0200 Subject: [PATCH 05/18] move error and pending fields up in onyx data --- src/libs/actions/Policy/Policy.ts | 6 +- src/libs/actions/connections/SageIntacct.ts | 38 ++++----- .../intacct/export/SageIntacctDatePage.tsx | 5 +- .../export/SageIntacctDefaultVendorPage.tsx | 5 +- .../intacct/export/SageIntacctExportPage.tsx | 19 ++--- ...ctNonReimbursableCreditCardAccountPage.tsx | 8 +- ...SageIntacctNonReimbursableExpensesPage.tsx | 20 ++--- .../SageIntacctPreferredExporterPage.tsx | 5 +- .../SageIntacctReimbursableExpensesPage.tsx | 14 ++-- src/types/onyx/Policy.ts | 81 ++++++++++--------- 10 files changed, 102 insertions(+), 99 deletions(-) diff --git a/src/libs/actions/Policy/Policy.ts b/src/libs/actions/Policy/Policy.ts index 80a90c4e7867..44cea98599f3 100644 --- a/src/libs/actions/Policy/Policy.ts +++ b/src/libs/actions/Policy/Policy.ts @@ -527,8 +527,8 @@ function clearXeroErrorField(policyID: string, fieldName: string) { Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {connections: {xero: {config: {errorFields: {[fieldName]: null}}}}}); } -function clearSageIntacctExportErrorField(policyID: string, fieldName: string) { - Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {connections: {intacct: {config: {export: {errorFields: {[fieldName]: null}}}}}}); +function clearSageIntacctErrorField(policyID: string, fieldName: string) { + Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {connections: {intacct: {config: {errorFields: {[fieldName]: null}}}}}); } function clearNetSuiteErrorField(policyID: string, fieldName: string) { @@ -3035,7 +3035,7 @@ export { generateCustomUnitID, clearQBOErrorField, clearXeroErrorField, - clearSageIntacctExportErrorField, + clearSageIntacctErrorField, clearNetSuiteErrorField, clearWorkspaceReimbursementErrors, setWorkspaceCurrencyDefault, diff --git a/src/libs/actions/connections/SageIntacct.ts b/src/libs/actions/connections/SageIntacct.ts index 5d56d8484fa3..7c7ff98a864a 100644 --- a/src/libs/actions/connections/SageIntacct.ts +++ b/src/libs/actions/connections/SageIntacct.ts @@ -42,12 +42,12 @@ function prepareOnyxData(policyID: string, settingName: keyof Connections['intac config: { export: { [settingName]: settingValue, - pendingFields: { - [settingName]: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, - }, - errorFields: { - [settingName]: null, - }, + }, + pendingFields: { + [settingName]: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + }, + errorFields: { + [settingName]: null, }, }, }, @@ -64,14 +64,11 @@ function prepareOnyxData(policyID: string, settingName: keyof Connections['intac connections: { intacct: { config: { - export: { - [settingName]: settingValue, - pendingFields: { - [settingName]: null, - }, - errorFields: { - [settingName]: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage'), - }, + pendingFields: { + [settingName]: null, + }, + errorFields: { + [settingName]: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage'), }, }, }, @@ -88,14 +85,11 @@ function prepareOnyxData(policyID: string, settingName: keyof Connections['intac connections: { intacct: { config: { - export: { - [settingName]: settingValue, - pendingFields: { - [settingName]: null, - }, - errorFields: { - [settingName]: null, - }, + pendingFields: { + [settingName]: null, + }, + errorFields: { + [settingName]: null, }, }, }, diff --git a/src/pages/workspace/accounting/intacct/export/SageIntacctDatePage.tsx b/src/pages/workspace/accounting/intacct/export/SageIntacctDatePage.tsx index d4e504b1dbc6..4f364f0e77f6 100644 --- a/src/pages/workspace/accounting/intacct/export/SageIntacctDatePage.tsx +++ b/src/pages/workspace/accounting/intacct/export/SageIntacctDatePage.tsx @@ -25,6 +25,7 @@ function SageIntacctDatePage({policy}: WithPolicyProps) { const {translate} = useLocalize(); const policyID = policy?.id ?? '-1'; const styles = useThemeStyles(); + const {config} = policy?.connections?.intacct ?? {}; const {export: exportConfig} = policy?.connections?.intacct?.config ?? {}; const data: MenuListItem[] = Object.values(CONST.SAGE_INTACCT_EXPORT_DATE).map((dateType) => ({ value: dateType, @@ -67,9 +68,9 @@ function SageIntacctDatePage({policy}: WithPolicyProps) { featureName={CONST.POLICY.MORE_FEATURES.ARE_CONNECTIONS_ENABLED} onBackButtonPress={() => Navigation.goBack(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_EXPORT.getRoute(policyID))} connectionName={CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT} - errors={ErrorUtils.getLatestErrorField(exportConfig ?? {}, CONST.SAGE_INTACCT_CONFIG.EXPORT_DATE)} + errors={ErrorUtils.getLatestErrorField(config ?? {}, CONST.SAGE_INTACCT_CONFIG.EXPORT_DATE)} errorRowStyles={[styles.ph5, styles.mv2]} - onClose={() => Policy.clearSageIntacctExportErrorField(policyID, CONST.SAGE_INTACCT_CONFIG.EXPORT_DATE)} + onClose={() => Policy.clearSageIntacctErrorField(policyID, CONST.SAGE_INTACCT_CONFIG.EXPORT_DATE)} /> ); } diff --git a/src/pages/workspace/accounting/intacct/export/SageIntacctDefaultVendorPage.tsx b/src/pages/workspace/accounting/intacct/export/SageIntacctDefaultVendorPage.tsx index 51b62cb78fe9..abf3a1cc391f 100644 --- a/src/pages/workspace/accounting/intacct/export/SageIntacctDefaultVendorPage.tsx +++ b/src/pages/workspace/accounting/intacct/export/SageIntacctDefaultVendorPage.tsx @@ -31,6 +31,7 @@ function SageIntacctDefaultVendorPage({route}: SageIntacctDefaultVendorPageProps const policyID = route.params.policyID ?? '-1'; const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`); + const {config} = policy?.connections?.intacct ?? {}; const {export: exportConfig} = policy?.connections?.intacct?.config ?? {}; const isReimbursable = route.params.reimbursable === CONST.SAGE_INTACCT_CONFIG.REIMBURSABLE; @@ -106,9 +107,9 @@ function SageIntacctDefaultVendorPage({route}: SageIntacctDefaultVendorPageProps listEmptyContent={listEmptyContent} accessVariants={[CONST.POLICY.ACCESS_VARIANTS.ADMIN, CONST.POLICY.ACCESS_VARIANTS.PAID]} connectionName={CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT} - errors={ErrorUtils.getLatestErrorField(exportConfig ?? {}, settingName)} + errors={ErrorUtils.getLatestErrorField(config, settingName)} errorRowStyles={[styles.ph5, styles.mv2]} - onClose={() => Policy.clearSageIntacctExportErrorField(policyID, settingName)} + onClose={() => Policy.clearSageIntacctErrorField(policyID, settingName)} /> ); } diff --git a/src/pages/workspace/accounting/intacct/export/SageIntacctExportPage.tsx b/src/pages/workspace/accounting/intacct/export/SageIntacctExportPage.tsx index b6aceb56b6ce..0fadd841deb9 100644 --- a/src/pages/workspace/accounting/intacct/export/SageIntacctExportPage.tsx +++ b/src/pages/workspace/accounting/intacct/export/SageIntacctExportPage.tsx @@ -15,6 +15,7 @@ function SageIntacctExportPage({policy}: WithPolicyProps) { const styles = useThemeStyles(); const policyID = policy?.id ?? '-1'; + const {config} = policy?.connections?.intacct ?? {}; const {export: exportConfig, credentials} = policy?.connections?.intacct?.config ?? {}; const sections = useMemo( @@ -23,15 +24,15 @@ function SageIntacctExportPage({policy}: WithPolicyProps) { description: translate('workspace.sageIntacct.preferredExporter'), action: () => Navigation.navigate(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_PREFERRED_EXPORTER.getRoute(policyID)), title: exportConfig?.exporter ?? translate('workspace.sageIntacct.notConfigured'), - hasError: !!exportConfig?.errorFields?.exporter, - pendingAction: exportConfig?.pendingFields?.exporter, + hasError: !!config?.errorFields?.exporter, + pendingAction: config?.pendingFields?.exporter, }, { description: translate('workspace.sageIntacct.exportDate.label'), action: () => Navigation.navigate(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_EXPORT_DATE.getRoute(policyID)), title: exportConfig?.exportDate ? translate(`workspace.sageIntacct.exportDate.values.${exportConfig.exportDate}.label`) : translate(`workspace.sageIntacct.notConfigured`), - hasError: !!exportConfig?.errorFields?.exportDate, - pendingAction: exportConfig?.pendingFields?.exportDate, + hasError: !!config?.errorFields?.exportDate, + pendingAction: config?.pendingFields?.exportDate, }, { description: translate('workspace.sageIntacct.reimbursableExpenses.label'), @@ -39,8 +40,8 @@ function SageIntacctExportPage({policy}: WithPolicyProps) { title: exportConfig?.reimbursable ? translate(`workspace.sageIntacct.reimbursableExpenses.values.${exportConfig.reimbursable}`) : translate('workspace.sageIntacct.notConfigured'), - hasError: !!exportConfig?.errorFields?.reimbursable, - pendingAction: exportConfig?.pendingFields?.reimbursable, + hasError: !!config?.errorFields?.reimbursable, + pendingAction: config?.pendingFields?.reimbursable, }, { description: translate('workspace.sageIntacct.nonReimbursableExpenses.label'), @@ -48,11 +49,11 @@ function SageIntacctExportPage({policy}: WithPolicyProps) { title: exportConfig?.nonReimbursable ? translate(`workspace.sageIntacct.nonReimbursableExpenses.values.${exportConfig.nonReimbursable}`) : translate('workspace.sageIntacct.notConfigured'), - hasError: !!exportConfig?.errorFields?.nonReimbursable, - pendingAction: exportConfig?.pendingFields?.nonReimbursable, + hasError: !!config?.errorFields?.nonReimbursable, + pendingAction: config?.pendingFields?.nonReimbursable, }, ], - [exportConfig, policyID, translate], + [config, exportConfig, policyID, translate], ); return ( diff --git a/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableCreditCardAccountPage.tsx b/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableCreditCardAccountPage.tsx index 5c6e708cfd54..e19c17a276a1 100644 --- a/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableCreditCardAccountPage.tsx +++ b/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableCreditCardAccountPage.tsx @@ -24,7 +24,7 @@ function SageIntacctNonReimbursableCreditCardAccountPage({policy}: WithPolicyCon const {translate} = useLocalize(); const policyID = policy?.id ?? '-1'; - + const {config} = policy?.connections?.intacct ?? {}; const {export: exportConfig} = policy?.connections?.intacct?.config ?? {}; const creditCardSelectorOptions = useMemo(() => getSageIntacctCreditCards(policy, exportConfig?.nonReimbursableAccount), [exportConfig?.nonReimbursableAccount, policy]); @@ -77,9 +77,9 @@ function SageIntacctNonReimbursableCreditCardAccountPage({policy}: WithPolicyCon listEmptyContent={listEmptyContent} accessVariants={[CONST.POLICY.ACCESS_VARIANTS.ADMIN, CONST.POLICY.ACCESS_VARIANTS.PAID]} connectionName={CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT} - errors={ErrorUtils.getLatestErrorField(exportConfig ?? {}, CONST.SAGE_INTACCT_CONFIG.NON_REIMBURSABLE_ACCOUNT)} - errorRowStyles={[styles.ph5, styles.mt2]} - onClose={() => Policy.clearSageIntacctExportErrorField(policyID, CONST.SAGE_INTACCT_CONFIG.NON_REIMBURSABLE_ACCOUNT)} + errors={ErrorUtils.getLatestErrorField(config ?? {}, CONST.SAGE_INTACCT_CONFIG.NON_REIMBURSABLE_ACCOUNT)} + errorRowStyles={[styles.ph5, styles.mv2]} + onClose={() => Policy.clearSageIntacctErrorField(policyID, CONST.SAGE_INTACCT_CONFIG.NON_REIMBURSABLE_ACCOUNT)} /> ); } diff --git a/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableExpensesPage.tsx b/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableExpensesPage.tsx index f97e9b156bcc..9ed8d2579620 100644 --- a/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableExpensesPage.tsx +++ b/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableExpensesPage.tsx @@ -60,12 +60,12 @@ function SageIntacctNonReimbursableExpensesPage({policy}: WithPolicyProps) { title: activeDefaultVendor ? getDefaultVendorName(activeDefaultVendor, intacctData?.vendors ?? []) : translate('workspace.sageIntacct.notConfigured'), hasError: config?.export.nonReimbursable === CONST.SAGE_INTACCT_NON_REIMBURSABLE_EXPENSE_TYPE.VENDOR_BILL - ? !!config?.export?.errorFields?.nonReimbursableVendor - : !!config?.export?.errorFields?.nonReimbursableCreditCardChargeDefaultVendor, + ? !!config?.errorFields?.nonReimbursableVendor + : !!config?.errorFields?.nonReimbursableCreditCardChargeDefaultVendor, pendingAction: config?.export.nonReimbursable === CONST.SAGE_INTACCT_NON_REIMBURSABLE_EXPENSE_TYPE.VENDOR_BILL - ? config?.export?.pendingFields?.nonReimbursableVendor - : config?.export?.pendingFields?.nonReimbursableCreditCardChargeDefaultVendor, + ? config?.pendingFields?.nonReimbursableVendor + : config?.pendingFields?.nonReimbursableCreditCardChargeDefaultVendor, }; const defaultVendor = ( @@ -87,8 +87,8 @@ function SageIntacctNonReimbursableExpensesPage({policy}: WithPolicyProps) { description: translate('workspace.sageIntacct.creditCardAccount'), action: () => Navigation.navigate(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_NON_REIMBURSABLE_CREDIT_CARD_ACCOUNT.getRoute(policyID)), title: config?.export.nonReimbursableAccount ? config.export.nonReimbursableAccount : translate('workspace.sageIntacct.notConfigured'), - hasError: !!config?.export?.errorFields?.nonReimbursableAccount, - pendingAction: config?.export?.pendingFields?.nonReimbursableAccount, + hasError: !!config?.errorFields?.nonReimbursableAccount, + pendingAction: config?.pendingFields?.nonReimbursableAccount, }; const creditCardAccount = ( @@ -121,9 +121,9 @@ function SageIntacctNonReimbursableExpensesPage({policy}: WithPolicyProps) { shouldIncludeSafeAreaPaddingBottom > Policy.clearSageIntacctExportErrorField(policyID, CONST.SAGE_INTACCT_CONFIG.NON_REIMBURSABLE)} + onClose={() => Policy.clearSageIntacctErrorField(policyID, CONST.SAGE_INTACCT_CONFIG.NON_REIMBURSABLE)} style={[styles.flexGrow1, styles.flexShrink1]} contentContainerStyle={[styles.flexGrow1, styles.flexShrink1]} > @@ -152,7 +152,9 @@ function SageIntacctNonReimbursableExpensesPage({policy}: WithPolicyProps) { updateSageIntacctDefaultVendor(policyID, CONST.SAGE_INTACCT_CONFIG.NON_REIMBURSABLE_CREDIT_CARD_VENDOR, vendor); }} wrapperStyle={[styles.ph5, styles.pv3]} - pendingAction={config?.export?.pendingFields?.nonReimbursableCreditCardChargeDefaultVendor} + pendingAction={config?.pendingFields?.nonReimbursableCreditCardChargeDefaultVendor} + errors={ErrorUtils.getLatestErrorField(config, CONST.SAGE_INTACCT_CONFIG.NON_REIMBURSABLE_CREDIT_CARD_VENDOR)} + onCloseError={() => Policy.clearSageIntacctErrorField(policyID, CONST.SAGE_INTACCT_CONFIG.NON_REIMBURSABLE_CREDIT_CARD_VENDOR)} /> {!!config?.export.nonReimbursableCreditCardChargeDefaultVendor && defaultVendor} diff --git a/src/pages/workspace/accounting/intacct/export/SageIntacctPreferredExporterPage.tsx b/src/pages/workspace/accounting/intacct/export/SageIntacctPreferredExporterPage.tsx index 6a7596f08c38..5223a101add2 100644 --- a/src/pages/workspace/accounting/intacct/export/SageIntacctPreferredExporterPage.tsx +++ b/src/pages/workspace/accounting/intacct/export/SageIntacctPreferredExporterPage.tsx @@ -26,6 +26,7 @@ function SageIntacctPreferredExporterPage({policy}: WithPolicyProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); const policyOwner = policy?.owner ?? ''; + const {config} = policy?.connections?.intacct ?? {}; const {export: exportConfiguration} = policy?.connections?.intacct?.config ?? {}; const exporters = getAdminEmployees(policy); const {login: currentUserLogin} = useCurrentUserPersonalDetails(); @@ -97,9 +98,9 @@ function SageIntacctPreferredExporterPage({policy}: WithPolicyProps) { onBackButtonPress={() => Navigation.goBack(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_EXPORT.getRoute(policyID))} title="workspace.sageIntacct.preferredExporter" connectionName={CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT} - errors={ErrorUtils.getLatestErrorField(exportConfiguration ?? {}, CONST.SAGE_INTACCT_CONFIG.EXPORTER)} + errors={ErrorUtils.getLatestErrorField(config ?? {}, CONST.SAGE_INTACCT_CONFIG.EXPORTER)} errorRowStyles={[styles.ph5, styles.mv2]} - onClose={() => Policy.clearSageIntacctExportErrorField(policyID, CONST.SAGE_INTACCT_CONFIG.EXPORTER)} + onClose={() => Policy.clearSageIntacctErrorField(policyID, CONST.SAGE_INTACCT_CONFIG.EXPORTER)} /> ); } diff --git a/src/pages/workspace/accounting/intacct/export/SageIntacctReimbursableExpensesPage.tsx b/src/pages/workspace/accounting/intacct/export/SageIntacctReimbursableExpensesPage.tsx index fd6ea4c00996..750d809117f4 100644 --- a/src/pages/workspace/accounting/intacct/export/SageIntacctReimbursableExpensesPage.tsx +++ b/src/pages/workspace/accounting/intacct/export/SageIntacctReimbursableExpensesPage.tsx @@ -61,8 +61,8 @@ function SageIntacctReimbursableExpensesPage({policy}: WithPolicyProps) { title: reimbursableExpenseReportDefaultVendor ? getDefaultVendorName(reimbursableExpenseReportDefaultVendor, intacctData?.vendors ?? []) : translate('workspace.sageIntacct.notConfigured'), - hasError: !!config?.export?.errorFields?.reimbursableExpenseReportDefaultVendor, - pendingAction: config?.export?.pendingFields?.reimbursableExpenseReportDefaultVendor, + hasError: !!config?.errorFields?.reimbursableExpenseReportDefaultVendor, + pendingAction: config?.pendingFields?.reimbursableExpenseReportDefaultVendor, }; const defaultVendor = ( @@ -95,9 +95,9 @@ function SageIntacctReimbursableExpensesPage({policy}: WithPolicyProps) { shouldIncludeSafeAreaPaddingBottom > Policy.clearSageIntacctExportErrorField(policyID, CONST.SAGE_INTACCT_CONFIG.REIMBURSABLE)} + onClose={() => Policy.clearSageIntacctErrorField(policyID, CONST.SAGE_INTACCT_CONFIG.REIMBURSABLE)} style={[styles.flexGrow1, styles.flexShrink1]} contentContainerStyle={[styles.flexGrow1, styles.flexShrink1]} > @@ -123,9 +123,9 @@ function SageIntacctReimbursableExpensesPage({policy}: WithPolicyProps) { updateSageIntacctDefaultVendor(policyID, CONST.SAGE_INTACCT_CONFIG.REIMBURSABLE_VENDOR, vendor); }} wrapperStyle={[styles.ph5, styles.pv3]} - pendingAction={config?.export?.pendingFields?.reimbursableExpenseReportDefaultVendor} - errors={ErrorUtils.getLatestErrorField(config?.export ?? {}, CONST.SAGE_INTACCT_CONFIG.REIMBURSABLE_VENDOR)} - onCloseError={() => Policy.clearSageIntacctExportErrorField(policyID, CONST.SAGE_INTACCT_CONFIG.REIMBURSABLE_VENDOR)} + pendingAction={config?.pendingFields?.reimbursableExpenseReportDefaultVendor} + errors={ErrorUtils.getLatestErrorField(config, CONST.SAGE_INTACCT_CONFIG.REIMBURSABLE_VENDOR)} + onCloseError={() => Policy.clearSageIntacctErrorField(policyID, CONST.SAGE_INTACCT_CONFIG.REIMBURSABLE_VENDOR)} /> {!!reimbursableExpenseReportDefaultVendor && defaultVendor} diff --git a/src/types/onyx/Policy.ts b/src/types/onyx/Policy.ts index 1a83f744d89b..6a0185bb065a 100644 --- a/src/types/onyx/Policy.ts +++ b/src/types/onyx/Policy.ts @@ -975,58 +975,61 @@ type SageIntacctConnectionData = { vendors: SageIntacctDataElementWithValue[]; }; -/** - * Connection config for Sage Intacct - */ -type SageIntacctConnectiosConfig = OnyxCommon.OnyxValueWithOfflineFeedback<{ - /** Sage Intacct credentials */ - credentials: { - /** Sage Intacct companyID */ - companyID: string; +/** Sage Intacct export configs */ +type SageIntacctExportConfig = { + /** Export date type */ + exportDate: ValueOf; - /** Sage Intacct password */ - password: string; + /** The e-mail of the exporter */ + exporter: string; - /** Sage Intacct userID */ - userID: string; - }; + /** Defines how non-reimbursable expenses are exported */ + nonReimbursable: ValueOf; - /** Sage Intacct export configs */ - export: OnyxCommon.OnyxValueWithOfflineFeedback<{ - /** Export date type */ - exportDate: ValueOf; + /** Account that receives the non-reimbursable expenses */ + nonReimbursableAccount: string; - /** The e-mail of the exporter */ - exporter: string; + /** Default vendor used for credit card transactions of non-reimbursable bill */ + nonReimbursableCreditCardChargeDefaultVendor: string | null; - /** Defines how non-reimbursable expenses are exported */ - nonReimbursable: ValueOf; + /** Default vendor of non-reimbursable bill */ + nonReimbursableVendor: string | null; - /** Account that receives the non-reimbursable expenses */ - nonReimbursableAccount: string; + /** Defines how reimbursable expenses are exported */ + reimbursable: ValueOf; - /** Default vendor used for credit card transactions of non-reimbursable bill */ - nonReimbursableCreditCardChargeDefaultVendor: string | null; + /** Default vendor of reimbursable bill */ + reimbursableExpenseReportDefaultVendor: string | null; +}; - /** Default vendor of non-reimbursable bill */ - nonReimbursableVendor: string | null; +/** + * Connection config for Sage Intacct + */ +type SageIntacctConnectiosConfig = OnyxCommon.OnyxValueWithOfflineFeedback< + { + /** Sage Intacct credentials */ + credentials: { + /** Sage Intacct companyID */ + companyID: string; - /** Defines how reimbursable expenses are exported */ - reimbursable: ValueOf; + /** Sage Intacct password */ + password: string; - /** Default vendor of reimbursable bill */ - reimbursableExpenseReportDefaultVendor: string | null; + /** Sage Intacct userID */ + userID: string; + }; - /** Collection of mapping field errors, which will be triggered when update action fails */ - errorFields?: OnyxCommon.ErrorFields; - }>; + /** Sage Intacct export configs */ + export: SageIntacctExportConfig; - /** Collection of Sage Intacct config errors */ - errors?: OnyxCommon.Errors; + /** Collection of Sage Intacct config errors */ + errors?: OnyxCommon.Errors; - /** Collection of form field errors */ - errorFields?: OnyxCommon.ErrorFields; -}>; + /** Collection of form field errors */ + errorFields?: OnyxCommon.ErrorFields; + }, + keyof SageIntacctExportConfig +>; /** State of integration connection */ type Connection = { From 42f683c8009fd503f272daab7bc5d19249aa8387 Mon Sep 17 00:00:00 2001 From: war-in Date: Thu, 4 Jul 2024 10:57:15 +0200 Subject: [PATCH 06/18] handle offline state --- src/components/SelectionScreen.tsx | 7 ++++++- .../accounting/intacct/export/SageIntacctDatePage.tsx | 1 + .../intacct/export/SageIntacctDefaultVendorPage.tsx | 1 + .../SageIntacctNonReimbursableCreditCardAccountPage.tsx | 1 + .../export/SageIntacctNonReimbursableExpensesPage.tsx | 1 + .../intacct/export/SageIntacctPreferredExporterPage.tsx | 1 + .../intacct/export/SageIntacctReimbursableExpensesPage.tsx | 3 ++- 7 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/components/SelectionScreen.tsx b/src/components/SelectionScreen.tsx index 953a26ce28d9..bcc80a5970b1 100644 --- a/src/components/SelectionScreen.tsx +++ b/src/components/SelectionScreen.tsx @@ -69,6 +69,9 @@ type SelectionScreenProps = { /** Name of the current connection */ connectionName: ConnectionName; + /** The type of action that's pending */ + pendingAction?: OnyxCommon.PendingAction | null; + /** The errors to display */ errors?: OnyxCommon.Errors | ReceiptErrors | null; @@ -95,6 +98,7 @@ function SelectionScreen({ featureName, shouldBeBlocked, connectionName, + pendingAction, errors, errorRowStyles, onClose, @@ -120,7 +124,9 @@ function SelectionScreen({ title={translate(title)} onBackButtonPress={onBackButtonPress} /> + {headerContent} Navigation.goBack(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_EXPORT.getRoute(policyID))} connectionName={CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT} + pendingAction={config?.pendingFields?.exportDate} errors={ErrorUtils.getLatestErrorField(config ?? {}, CONST.SAGE_INTACCT_CONFIG.EXPORT_DATE)} errorRowStyles={[styles.ph5, styles.mv2]} onClose={() => Policy.clearSageIntacctErrorField(policyID, CONST.SAGE_INTACCT_CONFIG.EXPORT_DATE)} diff --git a/src/pages/workspace/accounting/intacct/export/SageIntacctDefaultVendorPage.tsx b/src/pages/workspace/accounting/intacct/export/SageIntacctDefaultVendorPage.tsx index abf3a1cc391f..97590d970aa2 100644 --- a/src/pages/workspace/accounting/intacct/export/SageIntacctDefaultVendorPage.tsx +++ b/src/pages/workspace/accounting/intacct/export/SageIntacctDefaultVendorPage.tsx @@ -107,6 +107,7 @@ function SageIntacctDefaultVendorPage({route}: SageIntacctDefaultVendorPageProps listEmptyContent={listEmptyContent} accessVariants={[CONST.POLICY.ACCESS_VARIANTS.ADMIN, CONST.POLICY.ACCESS_VARIANTS.PAID]} connectionName={CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT} + pendingAction={config?.pendingFields?.[settingName]} errors={ErrorUtils.getLatestErrorField(config, settingName)} errorRowStyles={[styles.ph5, styles.mv2]} onClose={() => Policy.clearSageIntacctErrorField(policyID, settingName)} diff --git a/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableCreditCardAccountPage.tsx b/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableCreditCardAccountPage.tsx index e19c17a276a1..70758a88726c 100644 --- a/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableCreditCardAccountPage.tsx +++ b/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableCreditCardAccountPage.tsx @@ -77,6 +77,7 @@ function SageIntacctNonReimbursableCreditCardAccountPage({policy}: WithPolicyCon listEmptyContent={listEmptyContent} accessVariants={[CONST.POLICY.ACCESS_VARIANTS.ADMIN, CONST.POLICY.ACCESS_VARIANTS.PAID]} connectionName={CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT} + pendingAction={config?.pendingFields?.nonReimbursableAccount} errors={ErrorUtils.getLatestErrorField(config ?? {}, CONST.SAGE_INTACCT_CONFIG.NON_REIMBURSABLE_ACCOUNT)} errorRowStyles={[styles.ph5, styles.mv2]} onClose={() => Policy.clearSageIntacctErrorField(policyID, CONST.SAGE_INTACCT_CONFIG.NON_REIMBURSABLE_ACCOUNT)} diff --git a/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableExpensesPage.tsx b/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableExpensesPage.tsx index 9ed8d2579620..7a0c595baa7b 100644 --- a/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableExpensesPage.tsx +++ b/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableExpensesPage.tsx @@ -121,6 +121,7 @@ function SageIntacctNonReimbursableExpensesPage({policy}: WithPolicyProps) { shouldIncludeSafeAreaPaddingBottom > Policy.clearSageIntacctErrorField(policyID, CONST.SAGE_INTACCT_CONFIG.NON_REIMBURSABLE)} diff --git a/src/pages/workspace/accounting/intacct/export/SageIntacctPreferredExporterPage.tsx b/src/pages/workspace/accounting/intacct/export/SageIntacctPreferredExporterPage.tsx index 5223a101add2..c7a954cfc722 100644 --- a/src/pages/workspace/accounting/intacct/export/SageIntacctPreferredExporterPage.tsx +++ b/src/pages/workspace/accounting/intacct/export/SageIntacctPreferredExporterPage.tsx @@ -98,6 +98,7 @@ function SageIntacctPreferredExporterPage({policy}: WithPolicyProps) { onBackButtonPress={() => Navigation.goBack(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_EXPORT.getRoute(policyID))} title="workspace.sageIntacct.preferredExporter" connectionName={CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT} + pendingAction={config?.pendingFields?.exporter} errors={ErrorUtils.getLatestErrorField(config ?? {}, CONST.SAGE_INTACCT_CONFIG.EXPORTER)} errorRowStyles={[styles.ph5, styles.mv2]} onClose={() => Policy.clearSageIntacctErrorField(policyID, CONST.SAGE_INTACCT_CONFIG.EXPORTER)} diff --git a/src/pages/workspace/accounting/intacct/export/SageIntacctReimbursableExpensesPage.tsx b/src/pages/workspace/accounting/intacct/export/SageIntacctReimbursableExpensesPage.tsx index 750d809117f4..16064e1e94c0 100644 --- a/src/pages/workspace/accounting/intacct/export/SageIntacctReimbursableExpensesPage.tsx +++ b/src/pages/workspace/accounting/intacct/export/SageIntacctReimbursableExpensesPage.tsx @@ -95,6 +95,7 @@ function SageIntacctReimbursableExpensesPage({policy}: WithPolicyProps) { shouldIncludeSafeAreaPaddingBottom > Policy.clearSageIntacctErrorField(policyID, CONST.SAGE_INTACCT_CONFIG.REIMBURSABLE)} @@ -122,9 +123,9 @@ function SageIntacctReimbursableExpensesPage({policy}: WithPolicyProps) { const vendor = enabled ? policy?.connections?.intacct?.data?.vendors?.[0].id ?? null : null; updateSageIntacctDefaultVendor(policyID, CONST.SAGE_INTACCT_CONFIG.REIMBURSABLE_VENDOR, vendor); }} - wrapperStyle={[styles.ph5, styles.pv3]} pendingAction={config?.pendingFields?.reimbursableExpenseReportDefaultVendor} errors={ErrorUtils.getLatestErrorField(config, CONST.SAGE_INTACCT_CONFIG.REIMBURSABLE_VENDOR)} + wrapperStyle={[styles.ph5, styles.pv3]} onCloseError={() => Policy.clearSageIntacctErrorField(policyID, CONST.SAGE_INTACCT_CONFIG.REIMBURSABLE_VENDOR)} /> {!!reimbursableExpenseReportDefaultVendor && defaultVendor} From 80cc0582c4a609b6fbf69bec996dffce1929769e Mon Sep 17 00:00:00 2001 From: war-in Date: Thu, 4 Jul 2024 14:50:18 +0200 Subject: [PATCH 07/18] track errors in (non)reimbursable in main page --- .../intacct/export/SageIntacctExportPage.tsx | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/pages/workspace/accounting/intacct/export/SageIntacctExportPage.tsx b/src/pages/workspace/accounting/intacct/export/SageIntacctExportPage.tsx index 0fadd841deb9..0a1b67fc0b0b 100644 --- a/src/pages/workspace/accounting/intacct/export/SageIntacctExportPage.tsx +++ b/src/pages/workspace/accounting/intacct/export/SageIntacctExportPage.tsx @@ -40,8 +40,8 @@ function SageIntacctExportPage({policy}: WithPolicyProps) { title: exportConfig?.reimbursable ? translate(`workspace.sageIntacct.reimbursableExpenses.values.${exportConfig.reimbursable}`) : translate('workspace.sageIntacct.notConfigured'), - hasError: !!config?.errorFields?.reimbursable, - pendingAction: config?.pendingFields?.reimbursable, + hasError: !!config?.errorFields?.reimbursable || !!config?.errorFields?.reimbursableExpenseReportDefaultVendor, + pendingAction: config?.pendingFields?.reimbursable || config?.pendingFields?.reimbursableExpenseReportDefaultVendor, }, { description: translate('workspace.sageIntacct.nonReimbursableExpenses.label'), @@ -49,8 +49,18 @@ function SageIntacctExportPage({policy}: WithPolicyProps) { title: exportConfig?.nonReimbursable ? translate(`workspace.sageIntacct.nonReimbursableExpenses.values.${exportConfig.nonReimbursable}`) : translate('workspace.sageIntacct.notConfigured'), - hasError: !!config?.errorFields?.nonReimbursable, - pendingAction: config?.pendingFields?.nonReimbursable, + hasError: + !!config?.errorFields?.nonReimbursable || + !!config?.errorFields?.nonReimbursableAccount || + config?.export.nonReimbursable === CONST.SAGE_INTACCT_NON_REIMBURSABLE_EXPENSE_TYPE.VENDOR_BILL + ? !!config?.errorFields?.nonReimbursableVendor + : !!config?.errorFields?.nonReimbursableCreditCardChargeDefaultVendor, + pendingAction: + config?.pendingFields?.nonReimbursable || + config?.errorFields?.nonReimbursableAccount || + config?.export.nonReimbursable === CONST.SAGE_INTACCT_NON_REIMBURSABLE_EXPENSE_TYPE.VENDOR_BILL + ? config?.pendingFields?.nonReimbursableVendor + : config?.pendingFields?.nonReimbursableCreditCardChargeDefaultVendor, }, ], [config, exportConfig, policyID, translate], From c836521f3d73e44014ac58c50a013030eeb21b41 Mon Sep 17 00:00:00 2001 From: war-in Date: Thu, 4 Jul 2024 15:48:09 +0200 Subject: [PATCH 08/18] address comments --- .../intacct/export/SageIntacctExportPage.tsx | 10 +++++----- .../export/SageIntacctNonReimbursableExpensesPage.tsx | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/pages/workspace/accounting/intacct/export/SageIntacctExportPage.tsx b/src/pages/workspace/accounting/intacct/export/SageIntacctExportPage.tsx index 0a1b67fc0b0b..5f024f5d68e1 100644 --- a/src/pages/workspace/accounting/intacct/export/SageIntacctExportPage.tsx +++ b/src/pages/workspace/accounting/intacct/export/SageIntacctExportPage.tsx @@ -41,7 +41,7 @@ function SageIntacctExportPage({policy}: WithPolicyProps) { ? translate(`workspace.sageIntacct.reimbursableExpenses.values.${exportConfig.reimbursable}`) : translate('workspace.sageIntacct.notConfigured'), hasError: !!config?.errorFields?.reimbursable || !!config?.errorFields?.reimbursableExpenseReportDefaultVendor, - pendingAction: config?.pendingFields?.reimbursable || config?.pendingFields?.reimbursableExpenseReportDefaultVendor, + pendingAction: config?.pendingFields?.reimbursable ?? config?.pendingFields?.reimbursableExpenseReportDefaultVendor, }, { description: translate('workspace.sageIntacct.nonReimbursableExpenses.label'), @@ -50,14 +50,14 @@ function SageIntacctExportPage({policy}: WithPolicyProps) { ? translate(`workspace.sageIntacct.nonReimbursableExpenses.values.${exportConfig.nonReimbursable}`) : translate('workspace.sageIntacct.notConfigured'), hasError: - !!config?.errorFields?.nonReimbursable || - !!config?.errorFields?.nonReimbursableAccount || + !!config?.errorFields?.nonReimbursable ?? + !!config?.errorFields?.nonReimbursableAccount ?? config?.export.nonReimbursable === CONST.SAGE_INTACCT_NON_REIMBURSABLE_EXPENSE_TYPE.VENDOR_BILL ? !!config?.errorFields?.nonReimbursableVendor : !!config?.errorFields?.nonReimbursableCreditCardChargeDefaultVendor, pendingAction: - config?.pendingFields?.nonReimbursable || - config?.errorFields?.nonReimbursableAccount || + config?.pendingFields?.nonReimbursable ?? + config?.errorFields?.nonReimbursableAccount ?? config?.export.nonReimbursable === CONST.SAGE_INTACCT_NON_REIMBURSABLE_EXPENSE_TYPE.VENDOR_BILL ? config?.pendingFields?.nonReimbursableVendor : config?.pendingFields?.nonReimbursableCreditCardChargeDefaultVendor, diff --git a/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableExpensesPage.tsx b/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableExpensesPage.tsx index 9e3ecfcf7fc1..53da3fcd2537 100644 --- a/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableExpensesPage.tsx +++ b/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableExpensesPage.tsx @@ -84,11 +84,11 @@ function SageIntacctNonReimbursableExpensesPage({policy}: WithPolicyProps) { ); }, [ - config?.export?.errorFields?.nonReimbursableCreditCardChargeDefaultVendor, - config?.export?.errorFields?.nonReimbursableVendor, + config?.errorFields?.nonReimbursableCreditCardChargeDefaultVendor, + config?.errorFields?.nonReimbursableVendor, config?.export.nonReimbursable, - config?.export?.pendingFields?.nonReimbursableCreditCardChargeDefaultVendor, - config?.export?.pendingFields?.nonReimbursableVendor, + config?.pendingFields?.nonReimbursableCreditCardChargeDefaultVendor, + config?.pendingFields?.nonReimbursableVendor, intacctData?.vendors, policy, policyID, @@ -118,7 +118,7 @@ function SageIntacctNonReimbursableExpensesPage({policy}: WithPolicyProps) { /> ); - }, [config?.export?.errorFields?.nonReimbursableAccount, config?.export.nonReimbursableAccount, config?.export?.pendingFields?.nonReimbursableAccount, policyID, translate]); + }, [config?.errorFields?.nonReimbursableAccount, config?.export.nonReimbursableAccount, config?.pendingFields?.nonReimbursableAccount, policyID, translate]); return ( Date: Thu, 4 Jul 2024 15:50:23 +0200 Subject: [PATCH 09/18] use already defined type --- src/libs/actions/connections/SageIntacct.ts | 6 +++--- src/types/onyx/Policy.ts | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/libs/actions/connections/SageIntacct.ts b/src/libs/actions/connections/SageIntacct.ts index fc0a3a704a39..ca8155dcabe8 100644 --- a/src/libs/actions/connections/SageIntacct.ts +++ b/src/libs/actions/connections/SageIntacct.ts @@ -17,7 +17,7 @@ import {WRITE_COMMANDS} from '@libs/API/types'; import * as ErrorUtils from '@libs/ErrorUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {Connections} from '@src/types/onyx/Policy'; +import type {SageIntacctExportConfig} from '@src/types/onyx/Policy'; type SageIntacctCredentials = {companyID: string; userID: string; password: string}; @@ -31,7 +31,7 @@ function connectToSageIntacct(policyID: string, credentials: SageIntacctCredenti API.write(WRITE_COMMANDS.CONNECT_POLICY_TO_SAGE_INTACCT, parameters, {}); } -function prepareOnyxDataForExportUpdate(policyID: string, settingName: keyof Connections['intacct']['config']['export'], settingValue: string | null) { +function prepareOnyxDataForExportUpdate(policyID: string, settingName: keyof SageIntacctExportConfig, settingValue: string | null) { const optimisticData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, @@ -181,7 +181,7 @@ function updateSageIntacctNonreimbursableExpensesExportVendor(policyID: string, API.write(WRITE_COMMANDS.UPDATE_SAGE_INTACCT_NON_REIMBURSABLE_EXPENSES_EXPORT_VENDOR, parameters, {optimisticData, failureData, successData}); } -function updateSageIntacctDefaultVendor(policyID: string, settingName: keyof Connections['intacct']['config']['export'], vendor: string | null) { +function updateSageIntacctDefaultVendor(policyID: string, settingName: keyof SageIntacctExportConfig, vendor: string | null) { if (settingName === CONST.SAGE_INTACCT_CONFIG.REIMBURSABLE_VENDOR) { updateSageIntacctReimbursableExpensesReportExportDefaultVendor(policyID, vendor); } else if (settingName === CONST.SAGE_INTACCT_CONFIG.NON_REIMBURSABLE_CREDIT_CARD_VENDOR) { diff --git a/src/types/onyx/Policy.ts b/src/types/onyx/Policy.ts index 6a0185bb065a..ba3b6302e681 100644 --- a/src/types/onyx/Policy.ts +++ b/src/types/onyx/Policy.ts @@ -1403,4 +1403,5 @@ export type { ConnectionLastSync, NetSuiteSubsidiary, SageIntacctDataElementWithValue, + SageIntacctExportConfig, }; From a6d65882642832d6cb6751a13c69d821139d8068 Mon Sep 17 00:00:00 2001 From: war-in Date: Tue, 9 Jul 2024 12:32:11 +0200 Subject: [PATCH 10/18] include safe area padding bottom on errors --- src/components/SelectionScreen.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/SelectionScreen.tsx b/src/components/SelectionScreen.tsx index bcc80a5970b1..8e1b0a88c875 100644 --- a/src/components/SelectionScreen.tsx +++ b/src/components/SelectionScreen.tsx @@ -10,6 +10,7 @@ import type {TranslationPaths} from '@src/languages/types'; import type * as OnyxCommon from '@src/types/onyx/OnyxCommon'; import type {ConnectionName, PolicyFeatureName} from '@src/types/onyx/Policy'; import type {ReceiptErrors} from '@src/types/onyx/Transaction'; +import {isEmptyObject} from '@src/types/utils/EmptyObject'; import HeaderWithBackButton from './HeaderWithBackButton'; import OfflineWithFeedback from './OfflineWithFeedback'; import ScreenWrapper from './ScreenWrapper'; @@ -117,7 +118,7 @@ function SelectionScreen({ shouldBeBlocked={isConnectionEmpty || shouldBeBlocked} > Date: Tue, 9 Jul 2024 15:19:29 +0200 Subject: [PATCH 11/18] polish endpoints usages --- ...reimbursableExpensesExportAccountParams.ts | 2 +- src/libs/PolicyUtils.ts | 7 +-- src/libs/actions/connections/SageIntacct.ts | 2 +- ...SageIntacctNonReimbursableExpensesPage.tsx | 10 +-- .../SageIntacctReimbursableExpensesPage.tsx | 63 +++++++++++-------- src/types/onyx/Policy.ts | 6 +- 6 files changed, 50 insertions(+), 40 deletions(-) diff --git a/src/libs/API/parameters/UpdateSageIntacctNonreimbursableExpensesExportAccountParams.ts b/src/libs/API/parameters/UpdateSageIntacctNonreimbursableExpensesExportAccountParams.ts index 316308cbf67c..2a260ad267b3 100644 --- a/src/libs/API/parameters/UpdateSageIntacctNonreimbursableExpensesExportAccountParams.ts +++ b/src/libs/API/parameters/UpdateSageIntacctNonreimbursableExpensesExportAccountParams.ts @@ -1,6 +1,6 @@ type UpdateSageIntacctNonreimbursableExpensesExportAccountParams = { policyID: string; - bankAccountID: string; + creditCardAccountID: string; }; export default UpdateSageIntacctNonreimbursableExpensesExportAccountParams; diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index d5570eb7c478..9f8f2dc9690f 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -617,19 +617,18 @@ function getIntegrationLastSuccessfulDate(connection?: Connections[keyof Connect return (connection as ConnectionWithLastSyncData)?.lastSync?.successfulDate; } -function getSageIntacctVendors(policy?: Policy, selectedVendorId?: string | null): SelectorType[] { +function getSageIntacctVendors(policy?: Policy, selectedVendorId?: string): SelectorType[] { const vendors = policy?.connections?.intacct?.data?.vendors ?? []; - const isMatchFound = vendors?.some(({id}) => id === selectedVendorId); return vendors.map(({id, value}) => ({ value: id, text: value, keyForList: id, - isSelected: isMatchFound && selectedVendorId === id, + isSelected: selectedVendorId === id, })); } -function getSageIntacctNonReimbursableActiveDefaultVendor(policy?: Policy): string | null | undefined { +function getSageIntacctNonReimbursableActiveDefaultVendor(policy?: Policy): string | undefined { const { nonReimbursableCreditCardChargeDefaultVendor: creditCardDefaultVendor, nonReimbursableVendor: expenseReportDefaultVendor, diff --git a/src/libs/actions/connections/SageIntacct.ts b/src/libs/actions/connections/SageIntacct.ts index ca8155dcabe8..2f47e0df9dd6 100644 --- a/src/libs/actions/connections/SageIntacct.ts +++ b/src/libs/actions/connections/SageIntacct.ts @@ -165,7 +165,7 @@ function updateSageIntacctNonreimbursableExpensesExportAccount(policyID: string, const {optimisticData, failureData, successData} = prepareOnyxDataForExportUpdate(policyID, CONST.SAGE_INTACCT_CONFIG.NON_REIMBURSABLE_ACCOUNT, nonReimbursableAccount); const parameters: UpdateSageIntacctNonreimbursableExpensesExportAccountParams = { policyID, - bankAccountID: nonReimbursableAccount, + creditCardAccountID: nonReimbursableAccount, }; API.write(WRITE_COMMANDS.UPDATE_SAGE_INTACCT_NON_REIMBURSABLE_EXPENSES_EXPORT_ACCOUNT, parameters, {optimisticData, failureData, successData}); diff --git a/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableExpensesPage.tsx b/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableExpensesPage.tsx index 46fbd824273f..086c68621b2c 100644 --- a/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableExpensesPage.tsx +++ b/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableExpensesPage.tsx @@ -26,8 +26,8 @@ type MenuListItem = ListItem & { value: ValueOf; }; -function getDefaultVendorName(defaultVendor: string, vendors: SageIntacctDataElementWithValue[]): string { - return vendors.find((vendor) => vendor.id === defaultVendor)?.value ?? defaultVendor; +function getDefaultVendorName(defaultVendor?: string, vendors?: SageIntacctDataElementWithValue[]): string | undefined { + return (vendors ?? []).find((vendor) => vendor.id === defaultVendor)?.value ?? defaultVendor; } function SageIntacctNonReimbursableExpensesPage({policy}: WithPolicyProps) { @@ -55,10 +55,12 @@ function SageIntacctNonReimbursableExpensesPage({policy}: WithPolicyProps) { const defaultVendor = useMemo(() => { const activeDefaultVendor = getSageIntacctNonReimbursableActiveDefaultVendor(policy); + const defaultVendorName = getDefaultVendorName(activeDefaultVendor, intacctData?.vendors); + const defaultVendorSection = { description: translate('workspace.sageIntacct.defaultVendor'), action: () => Navigation.navigate(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_DEFAULT_VENDOR.getRoute(policyID, CONST.SAGE_INTACCT_CONFIG.NON_REIMBURSABLE.toLowerCase())), - title: activeDefaultVendor ? getDefaultVendorName(activeDefaultVendor, intacctData?.vendors ?? []) : translate('workspace.sageIntacct.notConfigured'), + title: defaultVendorName && defaultVendorName !== '' ? defaultVendorName : translate('workspace.sageIntacct.notConfigured'), hasError: config?.export.nonReimbursable === CONST.SAGE_INTACCT_NON_REIMBURSABLE_EXPENSE_TYPE.VENDOR_BILL ? !!config?.errorFields?.nonReimbursableVendor @@ -163,7 +165,7 @@ function SageIntacctNonReimbursableExpensesPage({policy}: WithPolicyProps) { switchAccessibilityLabel={translate('workspace.sageIntacct.defaultVendor')} isActive={!!config?.export.nonReimbursableCreditCardChargeDefaultVendor} onToggle={(enabled) => { - const vendor = enabled ? policy?.connections?.intacct?.data?.vendors?.[0].id ?? null : null; + const vendor = enabled ? policy?.connections?.intacct?.data?.vendors?.[0].id ?? '' : ''; updateSageIntacctDefaultVendor(policyID, CONST.SAGE_INTACCT_CONFIG.NON_REIMBURSABLE_CREDIT_CARD_VENDOR, vendor); }} wrapperStyle={[styles.ph5, styles.pv3]} diff --git a/src/pages/workspace/accounting/intacct/export/SageIntacctReimbursableExpensesPage.tsx b/src/pages/workspace/accounting/intacct/export/SageIntacctReimbursableExpensesPage.tsx index 9fc5a13911d3..c83023391de4 100644 --- a/src/pages/workspace/accounting/intacct/export/SageIntacctReimbursableExpensesPage.tsx +++ b/src/pages/workspace/accounting/intacct/export/SageIntacctReimbursableExpensesPage.tsx @@ -1,4 +1,4 @@ -import React, {useCallback} from 'react'; +import React, {useCallback, useMemo} from 'react'; import {View} from 'react-native'; import type {ValueOf} from 'type-fest'; import ConnectionLayout from '@components/ConnectionLayout'; @@ -25,8 +25,8 @@ type MenuListItem = ListItem & { value: ValueOf; }; -function getDefaultVendorName(defaultVendor: string, vendors: SageIntacctDataElementWithValue[]): string { - return vendors.find((vendor) => vendor.id === defaultVendor)?.value ?? defaultVendor; +function getDefaultVendorName(defaultVendor?: string, vendors?: SageIntacctDataElementWithValue[]): string | undefined { + return (vendors ?? []).find((vendor) => vendor.id === defaultVendor)?.value ?? defaultVendor; } function SageIntacctReimbursableExpensesPage({policy}: WithPolicyProps) { @@ -55,30 +55,39 @@ function SageIntacctReimbursableExpensesPage({policy}: WithPolicyProps) { [reimbursable, policyID], ); - const defaultVendorSection = { - description: translate('workspace.sageIntacct.defaultVendor'), - action: () => Navigation.navigate(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_DEFAULT_VENDOR.getRoute(policyID, CONST.SAGE_INTACCT_CONFIG.REIMBURSABLE)), - title: reimbursableExpenseReportDefaultVendor - ? getDefaultVendorName(reimbursableExpenseReportDefaultVendor, intacctData?.vendors ?? []) - : translate('workspace.sageIntacct.notConfigured'), - hasError: !!config?.errorFields?.reimbursableExpenseReportDefaultVendor, - pendingAction: config?.pendingFields?.reimbursableExpenseReportDefaultVendor, - }; + const defaultVendor = useMemo(() => { + const defaultVendorName = getDefaultVendorName(reimbursableExpenseReportDefaultVendor, intacctData?.vendors); - const defaultVendor = ( - - - - ); + const defaultVendorSection = { + description: translate('workspace.sageIntacct.defaultVendor'), + action: () => Navigation.navigate(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_DEFAULT_VENDOR.getRoute(policyID, CONST.SAGE_INTACCT_CONFIG.REIMBURSABLE)), + title: defaultVendorName && defaultVendorName !== '' ? defaultVendorName : translate('workspace.sageIntacct.notConfigured'), + hasError: !!config?.errorFields?.reimbursableExpenseReportDefaultVendor, + pendingAction: config?.pendingFields?.reimbursableExpenseReportDefaultVendor, + }; + + return ( + + + + ); + }, [ + config?.errorFields?.reimbursableExpenseReportDefaultVendor, + config?.pendingFields?.reimbursableExpenseReportDefaultVendor, + intacctData?.vendors, + policyID, + reimbursableExpenseReportDefaultVendor, + translate, + ]); return ( { - const vendor = enabled ? policy?.connections?.intacct?.data?.vendors?.[0].id ?? null : null; + const vendor = enabled ? policy?.connections?.intacct?.data?.vendors?.[0].id ?? '' : ''; updateSageIntacctDefaultVendor(policyID, CONST.SAGE_INTACCT_CONFIG.REIMBURSABLE_VENDOR, vendor); }} pendingAction={config?.pendingFields?.reimbursableExpenseReportDefaultVendor} diff --git a/src/types/onyx/Policy.ts b/src/types/onyx/Policy.ts index 47809a47571e..c77467198af3 100644 --- a/src/types/onyx/Policy.ts +++ b/src/types/onyx/Policy.ts @@ -999,16 +999,16 @@ type SageIntacctExportConfig = { nonReimbursableAccount: string; /** Default vendor used for credit card transactions of non-reimbursable bill */ - nonReimbursableCreditCardChargeDefaultVendor: string | null; + nonReimbursableCreditCardChargeDefaultVendor: string; /** Default vendor of non-reimbursable bill */ - nonReimbursableVendor: string | null; + nonReimbursableVendor: string; /** Defines how reimbursable expenses are exported */ reimbursable: ValueOf; /** Default vendor of reimbursable bill */ - reimbursableExpenseReportDefaultVendor: string | null; + reimbursableExpenseReportDefaultVendor: string; }; /** From 97f476aca920a99ad875af8949422780812b0712 Mon Sep 17 00:00:00 2001 From: war-in Date: Wed, 10 Jul 2024 10:29:53 +0200 Subject: [PATCH 12/18] fix Policy.ts after merge --- src/libs/actions/Policy/Policy.ts | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/libs/actions/Policy/Policy.ts b/src/libs/actions/Policy/Policy.ts index 8b0835c9ea17..985c4adf2bff 100644 --- a/src/libs/actions/Policy/Policy.ts +++ b/src/libs/actions/Policy/Policy.ts @@ -526,22 +526,18 @@ function clearXeroErrorField(policyID: string, fieldName: string) { Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {connections: {xero: {config: {errorFields: {[fieldName]: null}}}}}); } -function clearSageIntacctErrorField(policyID: string, fieldName: string) { - Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {connections: {intacct: {config: {errorFields: {[fieldName]: null}}}}}); -} - function clearNetSuiteErrorField(policyID: string, fieldName: string) { Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {connections: {netsuite: {options: {config: {errorFields: {[fieldName]: null}}}}}}); } -function clearNetSuiteAutoSyncErrorField(policyID: string) { - Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {connections: {netsuite: {config: {errorFields: {autoSync: null}}}}}); -} - function clearSageIntacctErrorField(policyID: string, fieldName: string) { Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {connections: {intacct: {config: {errorFields: {[fieldName]: null}}}}}); } +function clearNetSuiteAutoSyncErrorField(policyID: string) { + Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {connections: {netsuite: {config: {errorFields: {autoSync: null}}}}}); +} + function setWorkspaceReimbursement(policyID: string, reimbursementChoice: ValueOf, reimburserEmail: string) { const policy = getPolicy(policyID); @@ -3149,7 +3145,6 @@ export { openPolicyExpensifyCardsPage, requestExpensifyCardLimitIncrease, getPoliciesConnectedToSageIntacct, - clearSageIntacctErrorField, }; export type {NewCustomUnit}; From 43fc0f8e8db2877a676f23c7b66a9a4e4b1c6506 Mon Sep 17 00:00:00 2001 From: war-in Date: Wed, 10 Jul 2024 14:53:31 +0200 Subject: [PATCH 13/18] make error message sticky --- .../SelectionList/BaseSelectionList.tsx | 3 ++- src/components/SelectionList/types.ts | 3 +++ src/components/SelectionScreen.tsx | 26 ++++++++++++++----- 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/components/SelectionList/BaseSelectionList.tsx b/src/components/SelectionList/BaseSelectionList.tsx index 8b6ba790e6b0..bed174203240 100644 --- a/src/components/SelectionList/BaseSelectionList.tsx +++ b/src/components/SelectionList/BaseSelectionList.tsx @@ -67,6 +67,7 @@ function BaseSelectionList( showConfirmButton = false, shouldPreventDefaultFocusOnSelectRow = false, containerStyle, + sectionListStyle, disableKeyboardShortcuts = false, children, shouldStopPropagation = false, @@ -725,7 +726,7 @@ function BaseSelectionList( viewabilityConfig={{viewAreaCoveragePercentThreshold: 95}} testID="selection-list" onLayout={onSectionListLayout} - style={(!maxToRenderPerBatch || (shouldHideListOnInitialRender && isInitialSectionListRender)) && styles.opacity0} + style={[(!maxToRenderPerBatch || (shouldHideListOnInitialRender && isInitialSectionListRender)) && styles.opacity0, sectionListStyle]} ListHeaderComponent={listHeaderContent} ListFooterComponent={listFooterContent ?? ShowMoreButtonInstance} ListEmptyComponent={listEmptyContent} diff --git a/src/components/SelectionList/types.ts b/src/components/SelectionList/types.ts index d40a6f0fa225..a58b350f316f 100644 --- a/src/components/SelectionList/types.ts +++ b/src/components/SelectionList/types.ts @@ -407,6 +407,9 @@ type BaseSelectionListProps = Partial & { /** Styles to apply to SelectionList container */ containerStyle?: StyleProp; + /** Styles to apply to SectionList component */ + sectionListStyle?: StyleProp; + /** Whether focus event should be delayed */ shouldDelayFocus?: boolean; diff --git a/src/components/SelectionScreen.tsx b/src/components/SelectionScreen.tsx index 8e1b0a88c875..6098b75df533 100644 --- a/src/components/SelectionScreen.tsx +++ b/src/components/SelectionScreen.tsx @@ -1,4 +1,4 @@ -import {isEmpty} from 'lodash'; +import {isEmpty, mapValues} from 'lodash'; import React from 'react'; import type {StyleProp, ViewStyle} from 'react-native'; import useLocalize from '@hooks/useLocalize'; @@ -9,9 +9,10 @@ import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import type {TranslationPaths} from '@src/languages/types'; import type * as OnyxCommon from '@src/types/onyx/OnyxCommon'; import type {ConnectionName, PolicyFeatureName} from '@src/types/onyx/Policy'; -import type {ReceiptErrors} from '@src/types/onyx/Transaction'; +import type {ReceiptError, ReceiptErrors} from '@src/types/onyx/Transaction'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import HeaderWithBackButton from './HeaderWithBackButton'; +import MessagesRow from './MessagesRow'; import OfflineWithFeedback from './OfflineWithFeedback'; import ScreenWrapper from './ScreenWrapper'; import SelectionList from './SelectionList'; @@ -110,6 +111,11 @@ function SelectionScreen({ const policy = PolicyUtils.getPolicy(policyID); const isConnectionEmpty = isEmpty(policy?.connections?.[connectionName]); + const errorEntries = Object.entries(errors ?? {}); + const filteredErrorEntries = errorEntries.filter((errorEntry): errorEntry is [string, string | ReceiptError] => errorEntry[1] !== null); + const errorMessages = mapValues(Object.fromEntries(filteredErrorEntries), (error) => error); + const hasErrorMessages = !isEmptyObject(errorMessages); + return ( @@ -143,7 +146,18 @@ function SelectionScreen({ initiallyFocusedOptionKey={initiallyFocusedOptionKey} listEmptyContent={listEmptyContent} listFooterContent={listFooterContent} - /> + sectionListStyle={[styles.flexGrow0]} + > + {hasErrorMessages && ( + + )} + From b70782ac2522839e42775a04d2796ab05a5867c9 Mon Sep 17 00:00:00 2001 From: war-in Date: Wed, 10 Jul 2024 15:28:48 +0200 Subject: [PATCH 14/18] fix margin in non-reimbursable config --- .../intacct/export/SageIntacctNonReimbursableExpensesPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableExpensesPage.tsx b/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableExpensesPage.tsx index 086c68621b2c..60817f5afb98 100644 --- a/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableExpensesPage.tsx +++ b/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableExpensesPage.tsx @@ -153,7 +153,7 @@ function SageIntacctNonReimbursableExpensesPage({policy}: WithPolicyProps) { containerStyle={[styles.flexReset, styles.flexGrow1, styles.flexShrink1]} /> - + {config?.export.nonReimbursable === CONST.SAGE_INTACCT_NON_REIMBURSABLE_EXPENSE_TYPE.VENDOR_BILL && defaultVendor} {config?.export.nonReimbursable === CONST.SAGE_INTACCT_NON_REIMBURSABLE_EXPENSE_TYPE.CREDIT_CARD_CHARGE && ( From 5f9dc9493c325b3f340c3ecce8172ec827f4644e Mon Sep 17 00:00:00 2001 From: war-in Date: Wed, 10 Jul 2024 15:30:30 +0200 Subject: [PATCH 15/18] fix prettier --- src/libs/actions/connections/SageIntacct.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/connections/SageIntacct.ts b/src/libs/actions/connections/SageIntacct.ts index d10d1a40d1f7..261b7826426e 100644 --- a/src/libs/actions/connections/SageIntacct.ts +++ b/src/libs/actions/connections/SageIntacct.ts @@ -11,11 +11,11 @@ import type { Connections, SageIntacctConnectionsConfig, SageIntacctDimension, + SageIntacctExportConfig, SageIntacctMappingName, SageIntacctMappingType, SageIntacctMappingValue, SageIntacctOfflineStateKeys, - SageIntacctExportConfig, } from '@src/types/onyx/Policy'; type SageIntacctCredentials = {companyID: string; userID: string; password: string}; From 0bd28cc2f0c79e766f0d61084fbf08ac2ee29be1 Mon Sep 17 00:00:00 2001 From: war-in Date: Thu, 11 Jul 2024 11:53:11 +0200 Subject: [PATCH 16/18] dry the code --- src/components/ErrorMessageRow.tsx | 43 ++++++++++++++++++++++++++ src/components/OfflineWithFeedback.tsx | 22 +++++-------- src/components/SelectionScreen.tsx | 25 +++++---------- 3 files changed, 58 insertions(+), 32 deletions(-) create mode 100644 src/components/ErrorMessageRow.tsx diff --git a/src/components/ErrorMessageRow.tsx b/src/components/ErrorMessageRow.tsx new file mode 100644 index 000000000000..2e6e41449274 --- /dev/null +++ b/src/components/ErrorMessageRow.tsx @@ -0,0 +1,43 @@ +import {mapValues} from 'lodash'; +import React from 'react'; +import type {StyleProp, ViewStyle} from 'react-native'; +import type * as OnyxCommon from '@src/types/onyx/OnyxCommon'; +import type {ReceiptError, ReceiptErrors} from '@src/types/onyx/Transaction'; +import {isEmptyObject} from '@src/types/utils/EmptyObject'; +import MessagesRow from './MessagesRow'; + +type ErrorMessageRowProps = { + /** The errors to display */ + errors?: OnyxCommon.Errors | ReceiptErrors | null; + + /** Additional style object for the error row */ + errorRowStyles?: StyleProp; + + /** A function to run when the X button next to the error is clicked */ + onClose?: () => void; + + /** Whether we can dismiss the error message */ + canDismissError?: boolean; +}; + +function ErrorMessageRow({errors, errorRowStyles, onClose, canDismissError = true}: ErrorMessageRowProps) { + // Some errors have a null message. This is used to apply opacity only and to avoid showing redundant messages. + const errorEntries = Object.entries(errors ?? {}); + const filteredErrorEntries = errorEntries.filter((errorEntry): errorEntry is [string, string | ReceiptError] => errorEntry[1] !== null); + const errorMessages = mapValues(Object.fromEntries(filteredErrorEntries), (error) => error); + const hasErrorMessages = !isEmptyObject(errorMessages); + + return hasErrorMessages ? ( + + ) : null; +} + +ErrorMessageRow.displayName = 'ErrorMessageRow'; + +export default ErrorMessageRow; diff --git a/src/components/OfflineWithFeedback.tsx b/src/components/OfflineWithFeedback.tsx index ac9eda4043e8..1f4b56d87f17 100644 --- a/src/components/OfflineWithFeedback.tsx +++ b/src/components/OfflineWithFeedback.tsx @@ -1,4 +1,3 @@ -import {mapValues} from 'lodash'; import React, {useCallback} from 'react'; import type {StyleProp, ViewStyle} from 'react-native'; import {View} from 'react-native'; @@ -10,11 +9,11 @@ import shouldRenderOffscreen from '@libs/shouldRenderOffscreen'; import type {AllStyles} from '@styles/utils/types'; import CONST from '@src/CONST'; import type * as OnyxCommon from '@src/types/onyx/OnyxCommon'; -import type {ReceiptError, ReceiptErrors} from '@src/types/onyx/Transaction'; +import type {ReceiptErrors} from '@src/types/onyx/Transaction'; import type ChildrenProps from '@src/types/utils/ChildrenProps'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import CustomStylesForChildrenProvider from './CustomStylesForChildrenProvider'; -import MessagesRow from './MessagesRow'; +import ErrorMessageRow from './ErrorMessageRow'; /** * This component should be used when we are using the offline pattern B (offline with feedback). @@ -83,12 +82,6 @@ function OfflineWithFeedback({ const hasErrors = !isEmptyObject(errors ?? {}); - // Some errors have a null message. This is used to apply opacity only and to avoid showing redundant messages. - const errorEntries = Object.entries(errors ?? {}); - const filteredErrorEntries = errorEntries.filter((errorEntry): errorEntry is [string, string | ReceiptError] => errorEntry[1] !== null); - const errorMessages = mapValues(Object.fromEntries(filteredErrorEntries), (error) => error); - - const hasErrorMessages = !isEmptyObject(errorMessages); const isOfflinePendingAction = !!isOffline && !!pendingAction; const isUpdateOrDeleteError = hasErrors && (pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE || pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE); const isAddError = hasErrors && pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD; @@ -139,13 +132,12 @@ function OfflineWithFeedback({ {children} )} - {shouldShowErrorMessages && hasErrorMessages && ( - )} diff --git a/src/components/SelectionScreen.tsx b/src/components/SelectionScreen.tsx index 6098b75df533..0e3f0d84e10e 100644 --- a/src/components/SelectionScreen.tsx +++ b/src/components/SelectionScreen.tsx @@ -1,4 +1,4 @@ -import {isEmpty, mapValues} from 'lodash'; +import {isEmpty} from 'lodash'; import React from 'react'; import type {StyleProp, ViewStyle} from 'react-native'; import useLocalize from '@hooks/useLocalize'; @@ -9,10 +9,10 @@ import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import type {TranslationPaths} from '@src/languages/types'; import type * as OnyxCommon from '@src/types/onyx/OnyxCommon'; import type {ConnectionName, PolicyFeatureName} from '@src/types/onyx/Policy'; -import type {ReceiptError, ReceiptErrors} from '@src/types/onyx/Transaction'; +import type {ReceiptErrors} from '@src/types/onyx/Transaction'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; +import ErrorMessageRow from './ErrorMessageRow'; import HeaderWithBackButton from './HeaderWithBackButton'; -import MessagesRow from './MessagesRow'; import OfflineWithFeedback from './OfflineWithFeedback'; import ScreenWrapper from './ScreenWrapper'; import SelectionList from './SelectionList'; @@ -111,11 +111,6 @@ function SelectionScreen({ const policy = PolicyUtils.getPolicy(policyID); const isConnectionEmpty = isEmpty(policy?.connections?.[connectionName]); - const errorEntries = Object.entries(errors ?? {}); - const filteredErrorEntries = errorEntries.filter((errorEntry): errorEntry is [string, string | ReceiptError] => errorEntry[1] !== null); - const errorMessages = mapValues(Object.fromEntries(filteredErrorEntries), (error) => error); - const hasErrorMessages = !isEmptyObject(errorMessages); - return ( - {hasErrorMessages && ( - - )} + From 161f0ecefda477c97d8ba9d381bca832f2d36dcb Mon Sep 17 00:00:00 2001 From: war-in Date: Thu, 11 Jul 2024 12:53:23 +0200 Subject: [PATCH 17/18] unify spacing --- .../accounting/intacct/advanced/SageIntacctAdvancedPage.tsx | 2 +- .../intacct/advanced/SageIntacctPaymentAccountPage.tsx | 2 +- .../accounting/intacct/export/SageIntacctDatePage.tsx | 2 +- .../intacct/export/SageIntacctDefaultVendorPage.tsx | 2 +- .../SageIntacctNonReimbursableCreditCardAccountPage.tsx | 2 +- .../intacct/export/SageIntacctNonReimbursableExpensesPage.tsx | 4 ++-- .../intacct/export/SageIntacctPreferredExporterPage.tsx | 2 +- .../intacct/export/SageIntacctReimbursableExpensesPage.tsx | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/pages/workspace/accounting/intacct/advanced/SageIntacctAdvancedPage.tsx b/src/pages/workspace/accounting/intacct/advanced/SageIntacctAdvancedPage.tsx index b098000f47f2..620a76941269 100644 --- a/src/pages/workspace/accounting/intacct/advanced/SageIntacctAdvancedPage.tsx +++ b/src/pages/workspace/accounting/intacct/advanced/SageIntacctAdvancedPage.tsx @@ -119,7 +119,7 @@ function SageIntacctAdvancedPage({policy}: WithPolicyProps) { switchAccessibilityLabel={section.label} isActive={section.isActive} onToggle={section.onToggle} - wrapperStyle={[styles.ph5, styles.pv5]} + wrapperStyle={[styles.ph5, styles.pb3]} pendingAction={section.pendingAction} errors={section.error} onCloseError={section.onCloseError} diff --git a/src/pages/workspace/accounting/intacct/advanced/SageIntacctPaymentAccountPage.tsx b/src/pages/workspace/accounting/intacct/advanced/SageIntacctPaymentAccountPage.tsx index dadd1dc0af2c..3a9bd67b1e48 100644 --- a/src/pages/workspace/accounting/intacct/advanced/SageIntacctPaymentAccountPage.tsx +++ b/src/pages/workspace/accounting/intacct/advanced/SageIntacctPaymentAccountPage.tsx @@ -65,7 +65,7 @@ function SageIntacctPaymentAccountPage({policy}: WithPolicyConnectionsProps) { connectionName={CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT} pendingAction={config?.pendingFields?.reimbursementAccountID} errors={ErrorUtils.getLatestErrorField(config ?? {}, CONST.SAGE_INTACCT_CONFIG.REIMBURSEMENT_ACCOUNT_ID)} - errorRowStyles={[styles.ph5, styles.mv2]} + errorRowStyles={[styles.ph5, styles.pv3]} onClose={() => Policy.clearSageIntacctErrorField(policyID, CONST.SAGE_INTACCT_CONFIG.REIMBURSEMENT_ACCOUNT_ID)} /> ); diff --git a/src/pages/workspace/accounting/intacct/export/SageIntacctDatePage.tsx b/src/pages/workspace/accounting/intacct/export/SageIntacctDatePage.tsx index d337d17f7e1a..a0761a4e334b 100644 --- a/src/pages/workspace/accounting/intacct/export/SageIntacctDatePage.tsx +++ b/src/pages/workspace/accounting/intacct/export/SageIntacctDatePage.tsx @@ -70,7 +70,7 @@ function SageIntacctDatePage({policy}: WithPolicyProps) { connectionName={CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT} pendingAction={config?.pendingFields?.exportDate} errors={ErrorUtils.getLatestErrorField(config ?? {}, CONST.SAGE_INTACCT_CONFIG.EXPORT_DATE)} - errorRowStyles={[styles.ph5, styles.mv2]} + errorRowStyles={[styles.ph5, styles.pv3]} onClose={() => Policy.clearSageIntacctErrorField(policyID, CONST.SAGE_INTACCT_CONFIG.EXPORT_DATE)} /> ); diff --git a/src/pages/workspace/accounting/intacct/export/SageIntacctDefaultVendorPage.tsx b/src/pages/workspace/accounting/intacct/export/SageIntacctDefaultVendorPage.tsx index dd9b9a3167e1..43f2e289409c 100644 --- a/src/pages/workspace/accounting/intacct/export/SageIntacctDefaultVendorPage.tsx +++ b/src/pages/workspace/accounting/intacct/export/SageIntacctDefaultVendorPage.tsx @@ -109,7 +109,7 @@ function SageIntacctDefaultVendorPage({route}: SageIntacctDefaultVendorPageProps connectionName={CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT} pendingAction={config?.pendingFields?.[settingName]} errors={ErrorUtils.getLatestErrorField(config, settingName)} - errorRowStyles={[styles.ph5, styles.mv2]} + errorRowStyles={[styles.ph5, styles.pv3]} onClose={() => Policy.clearSageIntacctErrorField(policyID, settingName)} /> ); diff --git a/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableCreditCardAccountPage.tsx b/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableCreditCardAccountPage.tsx index 8628910aa3ce..1b191cc53627 100644 --- a/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableCreditCardAccountPage.tsx +++ b/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableCreditCardAccountPage.tsx @@ -67,7 +67,7 @@ function SageIntacctNonReimbursableCreditCardAccountPage({policy}: WithPolicyCon connectionName={CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT} pendingAction={config?.pendingFields?.nonReimbursableAccount} errors={ErrorUtils.getLatestErrorField(config ?? {}, CONST.SAGE_INTACCT_CONFIG.NON_REIMBURSABLE_ACCOUNT)} - errorRowStyles={[styles.ph5, styles.mv2]} + errorRowStyles={[styles.ph5, styles.pv3]} onClose={() => Policy.clearSageIntacctErrorField(policyID, CONST.SAGE_INTACCT_CONFIG.NON_REIMBURSABLE_ACCOUNT)} /> ); diff --git a/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableExpensesPage.tsx b/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableExpensesPage.tsx index 60817f5afb98..d0c43f7b1ae5 100644 --- a/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableExpensesPage.tsx +++ b/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableExpensesPage.tsx @@ -139,7 +139,7 @@ function SageIntacctNonReimbursableExpensesPage({policy}: WithPolicyProps) { Policy.clearSageIntacctErrorField(policyID, CONST.SAGE_INTACCT_CONFIG.NON_REIMBURSABLE)} style={[styles.flexGrow1, styles.flexShrink1]} contentContainerStyle={[styles.flexGrow1, styles.flexShrink1]} @@ -153,7 +153,7 @@ function SageIntacctNonReimbursableExpensesPage({policy}: WithPolicyProps) { containerStyle={[styles.flexReset, styles.flexGrow1, styles.flexShrink1]} /> - + {config?.export.nonReimbursable === CONST.SAGE_INTACCT_NON_REIMBURSABLE_EXPENSE_TYPE.VENDOR_BILL && defaultVendor} {config?.export.nonReimbursable === CONST.SAGE_INTACCT_NON_REIMBURSABLE_EXPENSE_TYPE.CREDIT_CARD_CHARGE && ( diff --git a/src/pages/workspace/accounting/intacct/export/SageIntacctPreferredExporterPage.tsx b/src/pages/workspace/accounting/intacct/export/SageIntacctPreferredExporterPage.tsx index c7a954cfc722..5d248aec382d 100644 --- a/src/pages/workspace/accounting/intacct/export/SageIntacctPreferredExporterPage.tsx +++ b/src/pages/workspace/accounting/intacct/export/SageIntacctPreferredExporterPage.tsx @@ -100,7 +100,7 @@ function SageIntacctPreferredExporterPage({policy}: WithPolicyProps) { connectionName={CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT} pendingAction={config?.pendingFields?.exporter} errors={ErrorUtils.getLatestErrorField(config ?? {}, CONST.SAGE_INTACCT_CONFIG.EXPORTER)} - errorRowStyles={[styles.ph5, styles.mv2]} + errorRowStyles={[styles.ph5, styles.pv3]} onClose={() => Policy.clearSageIntacctErrorField(policyID, CONST.SAGE_INTACCT_CONFIG.EXPORTER)} /> ); diff --git a/src/pages/workspace/accounting/intacct/export/SageIntacctReimbursableExpensesPage.tsx b/src/pages/workspace/accounting/intacct/export/SageIntacctReimbursableExpensesPage.tsx index f9fca3f7944c..8da3e6815fab 100644 --- a/src/pages/workspace/accounting/intacct/export/SageIntacctReimbursableExpensesPage.tsx +++ b/src/pages/workspace/accounting/intacct/export/SageIntacctReimbursableExpensesPage.tsx @@ -105,7 +105,7 @@ function SageIntacctReimbursableExpensesPage({policy}: WithPolicyProps) { Policy.clearSageIntacctErrorField(policyID, CONST.SAGE_INTACCT_CONFIG.REIMBURSABLE)} style={[styles.flexGrow1, styles.flexShrink1]} contentContainerStyle={[styles.flexGrow1, styles.flexShrink1]} From 4285951aaf6fa8975052a87963d0c8c1dcda43da Mon Sep 17 00:00:00 2001 From: war-in Date: Thu, 11 Jul 2024 17:42:45 +0200 Subject: [PATCH 18/18] fix selection list padding bottom in (non)reimbursable pages --- .../intacct/export/SageIntacctNonReimbursableExpensesPage.tsx | 2 +- .../intacct/export/SageIntacctReimbursableExpensesPage.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableExpensesPage.tsx b/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableExpensesPage.tsx index d0c43f7b1ae5..c87d33857513 100644 --- a/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableExpensesPage.tsx +++ b/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableExpensesPage.tsx @@ -150,7 +150,7 @@ function SageIntacctNonReimbursableExpensesPage({policy}: WithPolicyProps) { ListItem={RadioListItem} showScrollIndicator shouldShowTooltips={false} - containerStyle={[styles.flexReset, styles.flexGrow1, styles.flexShrink1]} + containerStyle={[styles.flexReset, styles.flexGrow1, styles.flexShrink1, styles.pb0]} /> diff --git a/src/pages/workspace/accounting/intacct/export/SageIntacctReimbursableExpensesPage.tsx b/src/pages/workspace/accounting/intacct/export/SageIntacctReimbursableExpensesPage.tsx index 8da3e6815fab..f03094393667 100644 --- a/src/pages/workspace/accounting/intacct/export/SageIntacctReimbursableExpensesPage.tsx +++ b/src/pages/workspace/accounting/intacct/export/SageIntacctReimbursableExpensesPage.tsx @@ -116,7 +116,7 @@ function SageIntacctReimbursableExpensesPage({policy}: WithPolicyProps) { ListItem={RadioListItem} showScrollIndicator shouldShowTooltips={false} - containerStyle={[styles.flexReset, styles.flexGrow1, styles.flexShrink1]} + containerStyle={[styles.flexReset, styles.flexGrow1, styles.flexShrink1, styles.pb0]} /> {reimbursable === CONST.SAGE_INTACCT_REIMBURSABLE_EXPENSE_TYPE.EXPENSE_REPORT && (