Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Expensify Card promoting banner to Company cards page #56561

Merged
merged 11 commits into from
Feb 12, 2025
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions src/components/Icon/Illustrations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ import ConciergeBubble from '@assets/images/simple-illustrations/simple-illustra
import ConciergeNew from '@assets/images/simple-illustrations/simple-illustration__concierge.svg';
import CreditCardsNew from '@assets/images/simple-illustrations/simple-illustration__credit-cards.svg';
import CreditCardEyes from '@assets/images/simple-illustrations/simple-illustration__creditcardeyes.svg';
import CreditCardsNewGreen from '@assets/images/simple-illustrations/simple-illustration__creditcards--green.svg';
import EmailAddress from '@assets/images/simple-illustrations/simple-illustration__email-address.svg';
import EmptyState from '@assets/images/simple-illustrations/simple-illustration__empty-state.svg';
import EnvelopeReceipt from '@assets/images/simple-illustrations/simple-illustration__envelopereceipt.svg';
Expand Down Expand Up @@ -198,6 +199,7 @@ export {
MoneyReceipts,
PinkBill,
CreditCardsNew,
CreditCardsNewGreen,
InvoiceBlue,
LaptopwithSecondScreenandHourglass,
LockOpen,
Expand Down
3 changes: 3 additions & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3804,6 +3804,9 @@ const translations = {
noAccountsFound: 'No accounts found',
defaultCard: 'Default card',
noAccountsFoundDescription: ({connection}: ConnectionParams) => `Please add the account in ${connection} and sync the connection again.`,
expensifyCardBannerTitle: 'Get the Expensify Card',
expensifyCardBannerSubtitle: 'Enjoy cash back on every US purchase, up to 50% off your Expensify bill, unlimited virtual cards, and so much more.',
expensifyCardBannerLearnMoreButton: 'Learn more',
},
workflows: {
title: 'Workflows',
Expand Down
4 changes: 4 additions & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3850,6 +3850,10 @@ const translations = {
noAccountsFound: 'No se han encontrado cuentas',
defaultCard: 'Tarjeta predeterminada',
noAccountsFoundDescription: ({connection}: ConnectionParams) => `Añade la cuenta en ${connection} y sincroniza la conexión de nuevo.`,
expensifyCardBannerTitle: 'Obtén la Tarjeta Expensify',
expensifyCardBannerSubtitle:
'Disfruta de una devolución en cada compra en Estados Unidos, hasta un 50% de descuento en tu factura de Expensify, tarjetas virtuales ilimitadas y mucho más.',
expensifyCardBannerLearnMoreButton: 'Más información',
},
workflows: {
title: 'Flujos de trabajo',
Expand Down
7 changes: 7 additions & 0 deletions src/libs/PolicyUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1091,6 +1091,12 @@ function goBackWhenEnableFeature(policyID: string) {
}, CONST.WORKSPACE_ENABLE_FEATURE_REDIRECT_DELAY);
}

function navigateToExpensifyCardPage(policyID: string) {
Navigation.setNavigationActionToMicrotaskQueue(() => {
Navigation.navigate(ROUTES.WORKSPACE_EXPENSIFY_CARD.getRoute(policyID));
});
}

function getConnectedIntegration(policy: Policy | undefined, accountingIntegrations?: ConnectionName[]) {
return (accountingIntegrations ?? Object.values(CONST.POLICY.CONNECTIONS.NAME)).find((integration) => !!policy?.connections?.[integration]);
}
Expand Down Expand Up @@ -1422,6 +1428,7 @@ export {
sortWorkspacesBySelected,
removePendingFieldsFromCustomUnit,
goBackWhenEnableFeature,
navigateToExpensifyCardPage,
getIntegrationLastSuccessfulDate,
getCurrentConnectionName,
getCustomersOrJobsLabelNetSuite,
Expand Down
9 changes: 7 additions & 2 deletions src/libs/actions/Policy/Policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ import * as NumberUtils from '@libs/NumberUtils';
import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils';
import * as PhoneNumber from '@libs/PhoneNumber';
import * as PolicyUtils from '@libs/PolicyUtils';
import {goBackWhenEnableFeature} from '@libs/PolicyUtils';
import {goBackWhenEnableFeature, navigateToExpensifyCardPage} from '@libs/PolicyUtils';
import * as ReportUtils from '@libs/ReportUtils';
import type {PolicySelector} from '@pages/home/sidebar/FloatingActionButtonAndPopover';
import * as PaymentMethods from '@userActions/PaymentMethods';
Expand Down Expand Up @@ -2851,7 +2851,7 @@ function savePreferredExportMethod(policyID: string, exportMethod: ReportExportT
Onyx.merge(`${ONYXKEYS.LAST_EXPORT_METHOD}`, {[policyID]: exportMethod});
}

function enableExpensifyCard(policyID: string, enabled: boolean) {
function enableExpensifyCard(policyID: string, enabled: boolean, shouldNavigateToExpensifyCardPage = false) {
const authToken = NetworkStore.getAuthToken();
if (!authToken) {
return;
Expand Down Expand Up @@ -2898,6 +2898,11 @@ function enableExpensifyCard(policyID: string, enabled: boolean) {

API.write(WRITE_COMMANDS.ENABLE_POLICY_EXPENSIFY_CARDS, parameters, onyxData);

if (enabled && shouldNavigateToExpensifyCardPage) {
navigateToExpensifyCardPage(policyID);
return;
}

if (enabled && getIsNarrowLayout()) {
goBackWhenEnableFeature(policyID);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import React, {useCallback, useMemo} from 'react';
import {View} from 'react-native';
import type {OnyxEntry} from 'react-native-onyx';
import Button from '@components/Button';
import {CreditCardsNewGreen} from '@components/Icon/Illustrations';
import useLocalize from '@hooks/useLocalize';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useStyleUtils from '@hooks/useStyleUtils';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import {enableExpensifyCard} from '@libs/actions/Policy/Policy';
import {navigateToExpensifyCardPage} from '@libs/PolicyUtils';
import BillingBanner from '@pages/settings/Subscription/CardSection/BillingBanner/BillingBanner';
import type {Policy} from '@src/types/onyx';

type WorkspaceCompanyCardExpensifyCardPromotionBannerProps = {
policy: OnyxEntry<Policy>;
};

function WorkspaceCompanyCardExpensifyCardPromotionBanner({policy}: WorkspaceCompanyCardExpensifyCardPromotionBannerProps) {
const theme = useTheme();
const styles = useThemeStyles();
const {translate} = useLocalize();
const StyleUtils = useStyleUtils();
const {shouldUseNarrowLayout} = useResponsiveLayout();
const policyID = policy?.id;
const areExpensifyCardsEnabled = policy?.areExpensifyCardsEnabled;

const handleLearnMore = useCallback(() => {
if (!policyID) {
return;
}

if (areExpensifyCardsEnabled) {
navigateToExpensifyCardPage(policyID);
return;
}

enableExpensifyCard(policyID, true, true);
}, [policyID, areExpensifyCardsEnabled]);

const rightComponent = useMemo(() => {
const smallScreenStyle = shouldUseNarrowLayout ? [styles.flex0, styles.flexBasis100, styles.justifyContentCenter] : [];
return (
<View style={[styles.flexRow, styles.gap2, smallScreenStyle]}>
<Button
success
onPress={handleLearnMore}
style={shouldUseNarrowLayout && styles.flex1}
text={translate('workspace.moreFeatures.companyCards.expensifyCardBannerLearnMoreButton')}
/>
</View>
);
}, [styles, shouldUseNarrowLayout, translate, handleLearnMore]);

return (
<View style={[styles.ph4, styles.mb4]}>
<BillingBanner
icon={CreditCardsNewGreen}
title={translate('workspace.moreFeatures.companyCards.expensifyCardBannerTitle')}
titleStyle={StyleUtils.getTextColorStyle(theme.text)}
subtitle={translate('workspace.moreFeatures.companyCards.expensifyCardBannerSubtitle')}
subtitleStyle={[styles.mt1, styles.textLabel]}
style={[styles.borderRadiusComponentLarge]}
rightComponent={rightComponent}
/>
</View>
);
}

export default WorkspaceCompanyCardExpensifyCardPromotionBanner;
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ import Navigation from '@libs/Navigation/Navigation';
import withPolicyAndFullscreenLoading from '@pages/workspace/withPolicyAndFullscreenLoading';
import type {WithPolicyAndFullscreenLoadingProps} from '@pages/workspace/withPolicyAndFullscreenLoading';
import colors from '@styles/theme/colors';
import * as CompanyCards from '@userActions/CompanyCards';
import {clearAddNewCardFlow} from '@userActions/CompanyCards';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import WorkspaceCompanyCardExpensifyCardPromotionBanner from './WorkspaceCompanyCardExpensifyCardPromotionBanner';

const companyCardFeatures: FeatureListItem[] = [
{
Expand All @@ -40,16 +41,20 @@ function WorkspaceCompanyCardPageEmptyState({policy}: WithPolicyAndFullscreenLoa
const [isNoDelegateAccessMenuVisible, setIsNoDelegateAccessMenuVisible] = useState(false);

const handleCtaPress = useCallback(() => {
if (!policy?.id) {
return;
}
if (isActingAsDelegate) {
setIsNoDelegateAccessMenuVisible(true);
return;
}
CompanyCards.clearAddNewCardFlow();
Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW.getRoute(policy?.id ?? '-1'));
clearAddNewCardFlow();
Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW.getRoute(policy.id));
}, [policy, isActingAsDelegate]);

return (
<View style={[styles.mt3, shouldUseNarrowLayout ? styles.workspaceSectionMobile : styles.workspaceSection]}>
<WorkspaceCompanyCardExpensifyCardPromotionBanner policy={policy} />
<FeatureList
menuItems={companyCardFeatures}
title={translate('workspace.moreFeatures.companyCards.feed.title')}
Expand Down
Loading