diff --git a/src/components/Accordion/index.tsx b/src/components/Accordion/index.tsx
index 9715f3902c03..d70b8db835dd 100644
--- a/src/components/Accordion/index.tsx
+++ b/src/components/Accordion/index.tsx
@@ -24,6 +24,7 @@ type AccordionProps = {
function Accordion({isExpanded, children, duration = 300, isToggleTriggered, style}: AccordionProps) {
const height = useSharedValue(0);
+ const isAnimating = useSharedValue(false);
const derivedHeight = useDerivedValue(() => {
if (!isToggleTriggered.get()) {
@@ -41,10 +42,20 @@ function Accordion({isExpanded, children, duration = 300, isToggleTriggered, sty
return isExpanded.get() ? 1 : 0;
}
- return withTiming(isExpanded.get() ? 1 : 0, {
- duration,
- easing: Easing.inOut(Easing.quad),
- });
+ isAnimating.set(true);
+ return withTiming(
+ isExpanded.get() ? 1 : 0,
+ {
+ duration,
+ easing: Easing.inOut(Easing.quad),
+ },
+ (finished) => {
+ if (!finished || !isExpanded.get()) {
+ return;
+ }
+ isAnimating.set(false);
+ },
+ );
});
const animatedStyle = useAnimatedStyle(() => {
@@ -52,12 +63,16 @@ function Accordion({isExpanded, children, duration = 300, isToggleTriggered, sty
return {
height: 0,
opacity: 0,
+ display: 'none',
};
}
return {
height: !isToggleTriggered.get() ? undefined : derivedHeight.get(),
+ maxHeight: !isToggleTriggered.get() ? undefined : derivedHeight.get(),
opacity: derivedOpacity.get(),
+ overflow: isAnimating.get() ? 'hidden' : 'visible',
+ display: isExpanded.get() ? 'inline' : 'none',
};
});
diff --git a/src/hooks/useAccordionAnimation.ts b/src/hooks/useAccordionAnimation.ts
new file mode 100644
index 000000000000..d3995493ff8e
--- /dev/null
+++ b/src/hooks/useAccordionAnimation.ts
@@ -0,0 +1,26 @@
+import {useEffect} from 'react';
+import {useSharedValue} from 'react-native-reanimated';
+
+/**
+ * @returns two values: isExpanded, which manages the expansion of the accordion component,
+ * and shouldAnimateAccordionSection, which determines whether we should animate
+ * the expanding and collapsing of the accordion based on changes in isExpanded.
+ */
+function useAccordionAnimation(isExpanded: boolean) {
+ const isAccordionExpanded = useSharedValue(isExpanded);
+ const shouldAnimateAccordionSection = useSharedValue(false);
+ const hasMounted = useSharedValue(false);
+
+ useEffect(() => {
+ isAccordionExpanded.set(isExpanded);
+ if (hasMounted.get()) {
+ shouldAnimateAccordionSection.set(true);
+ } else {
+ hasMounted.set(true);
+ }
+ }, [hasMounted, isAccordionExpanded, isExpanded, shouldAnimateAccordionSection]);
+
+ return {isAccordionExpanded, shouldAnimateAccordionSection};
+}
+
+export default useAccordionAnimation;
diff --git a/src/pages/workspace/accounting/intacct/advanced/SageIntacctAdvancedPage.tsx b/src/pages/workspace/accounting/intacct/advanced/SageIntacctAdvancedPage.tsx
index 2d2480835f6e..fa410a0e376b 100644
--- a/src/pages/workspace/accounting/intacct/advanced/SageIntacctAdvancedPage.tsx
+++ b/src/pages/workspace/accounting/intacct/advanced/SageIntacctAdvancedPage.tsx
@@ -1,10 +1,12 @@
import React, {useMemo} from 'react';
+import Accordion from '@components/Accordion';
import ConnectionLayout from '@components/ConnectionLayout';
import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription';
import OfflineWithFeedback from '@components/OfflineWithFeedback';
+import useAccordionAnimation from '@hooks/useAccordionAnimation';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
-import * as ErrorUtils from '@libs/ErrorUtils';
+import {getLatestErrorField} from '@libs/ErrorUtils';
import {areSettingsInErrorFields, getCurrentSageIntacctEntityName, settingsPendingAction} from '@libs/PolicyUtils';
import Navigation from '@navigation/Navigation';
import type {WithPolicyProps} from '@pages/workspace/withPolicy';
@@ -17,7 +19,7 @@ import {
updateSageIntacctSyncReimbursedReports,
updateSageIntacctSyncReimbursementAccountID,
} from '@userActions/connections/SageIntacct';
-import * as Policy from '@userActions/Policy/Policy';
+import {clearSageIntacctErrorField} from '@userActions/Policy/Policy';
import CONST from '@src/CONST';
import ROUTES from '@src/ROUTES';
import type {SageIntacctDataElement} from '@src/types/onyx/Policy';
@@ -34,6 +36,8 @@ function SageIntacctAdvancedPage({policy}: WithPolicyProps) {
const {importEmployees, autoSync, sync, pendingFields, errorFields} = policy?.connections?.intacct?.config ?? {};
const {data, config} = policy?.connections?.intacct ?? {};
+ const {isAccordionExpanded, shouldAnimateAccordionSection} = useAccordionAnimation(!!sync?.syncReimbursedReports);
+
const toggleSections = useMemo(
() => [
{
@@ -42,8 +46,8 @@ function SageIntacctAdvancedPage({policy}: WithPolicyProps) {
isActive: !!autoSync?.enabled,
onToggle: (enabled: boolean) => updateSageIntacctAutoSync(policyID, enabled),
subscribedSettings: [CONST.SAGE_INTACCT_CONFIG.AUTO_SYNC_ENABLED],
- error: ErrorUtils.getLatestErrorField(config, CONST.SAGE_INTACCT_CONFIG.AUTO_SYNC_ENABLED),
- onCloseError: () => Policy.clearSageIntacctErrorField(policyID, CONST.SAGE_INTACCT_CONFIG.AUTO_SYNC_ENABLED),
+ error: getLatestErrorField(config, CONST.SAGE_INTACCT_CONFIG.AUTO_SYNC_ENABLED),
+ onCloseError: () => clearSageIntacctErrorField(policyID, CONST.SAGE_INTACCT_CONFIG.AUTO_SYNC_ENABLED),
},
{
label: translate('workspace.sageIntacct.inviteEmployees'),
@@ -54,12 +58,10 @@ function SageIntacctAdvancedPage({policy}: WithPolicyProps) {
updateSageIntacctApprovalMode(policyID, enabled);
},
subscribedSettings: [CONST.SAGE_INTACCT_CONFIG.IMPORT_EMPLOYEES, CONST.SAGE_INTACCT_CONFIG.APPROVAL_MODE],
- error:
- ErrorUtils.getLatestErrorField(config ?? {}, CONST.SAGE_INTACCT_CONFIG.IMPORT_EMPLOYEES) ??
- ErrorUtils.getLatestErrorField(config ?? {}, CONST.SAGE_INTACCT_CONFIG.APPROVAL_MODE),
+ error: getLatestErrorField(config ?? {}, CONST.SAGE_INTACCT_CONFIG.IMPORT_EMPLOYEES) ?? getLatestErrorField(config ?? {}, CONST.SAGE_INTACCT_CONFIG.APPROVAL_MODE),
onCloseError: () => {
- Policy.clearSageIntacctErrorField(policyID, CONST.SAGE_INTACCT_CONFIG.IMPORT_EMPLOYEES);
- Policy.clearSageIntacctErrorField(policyID, CONST.SAGE_INTACCT_CONFIG.APPROVAL_MODE);
+ clearSageIntacctErrorField(policyID, CONST.SAGE_INTACCT_CONFIG.IMPORT_EMPLOYEES);
+ clearSageIntacctErrorField(policyID, CONST.SAGE_INTACCT_CONFIG.APPROVAL_MODE);
},
},
{
@@ -75,9 +77,9 @@ function SageIntacctAdvancedPage({policy}: WithPolicyProps) {
}
},
subscribedSettings: [CONST.SAGE_INTACCT_CONFIG.SYNC_REIMBURSED_REPORTS],
- error: ErrorUtils.getLatestErrorField(config ?? {}, CONST.SAGE_INTACCT_CONFIG.SYNC_REIMBURSED_REPORTS),
+ error: getLatestErrorField(config ?? {}, CONST.SAGE_INTACCT_CONFIG.SYNC_REIMBURSED_REPORTS),
onCloseError: () => {
- Policy.clearSageIntacctErrorField(policyID, CONST.SAGE_INTACCT_CONFIG.SYNC_REIMBURSED_REPORTS);
+ clearSageIntacctErrorField(policyID, CONST.SAGE_INTACCT_CONFIG.SYNC_REIMBURSED_REPORTS);
},
},
],
@@ -113,20 +115,23 @@ function SageIntacctAdvancedPage({policy}: WithPolicyProps) {
/>
))}
- {!!sync?.syncReimbursedReports && (
+
Navigation.navigate(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_PAYMENT_ACCOUNT.getRoute(policyID))}
brickRoadIndicator={areSettingsInErrorFields([CONST.SAGE_INTACCT_CONFIG.REIMBURSEMENT_ACCOUNT_ID], errorFields) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined}
/>
- )}
+
);
}
diff --git a/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableExpensesPage.tsx b/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableExpensesPage.tsx
index 91256a3c0f4e..e45fedc51fc1 100644
--- a/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableExpensesPage.tsx
+++ b/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableExpensesPage.tsx
@@ -1,26 +1,24 @@
import React from 'react';
+import Accordion from '@components/Accordion';
import ConnectionLayout from '@components/ConnectionLayout';
import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription';
import OfflineWithFeedback from '@components/OfflineWithFeedback';
+import useAccordionAnimation from '@hooks/useAccordionAnimation';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
-import * as ErrorUtils from '@libs/ErrorUtils';
+import {getLatestErrorField} from '@libs/ErrorUtils';
import Navigation from '@libs/Navigation/Navigation';
import {areSettingsInErrorFields, getSageIntacctNonReimbursableActiveDefaultVendor, settingsPendingAction} from '@libs/PolicyUtils';
-import type {MenuItem, ToggleItem} from '@pages/workspace/accounting/intacct/types';
+import type {ExtendedMenuItemWithSubscribedSettings, MenuItemToRender} from '@pages/workspace/accounting/intacct/types';
import type {WithPolicyConnectionsProps} from '@pages/workspace/withPolicyConnections';
import withPolicyConnections from '@pages/workspace/withPolicyConnections';
import ToggleSettingOptionRow from '@pages/workspace/workflows/ToggleSettingsOptionRow';
import {updateSageIntacctDefaultVendor} from '@userActions/connections/SageIntacct';
-import * as Policy from '@userActions/Policy/Policy';
+import {clearSageIntacctErrorField} from '@userActions/Policy/Policy';
import CONST from '@src/CONST';
import ROUTES from '@src/ROUTES';
import {getDefaultVendorName} from './utils';
-type MenuItemWithSubscribedSettings = Pick