From 27b37bf2499122db9c0b0757363aa794643f2d07 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Tue, 30 Dec 2025 22:59:13 +0700 Subject: [PATCH 1/5] feat: Add promo offer to Xero on Accounting page --- .../treasure-chest-green-with-sparkle.svg | 247 ++++++++++++++++++ src/CONST/index.ts | 4 + src/ROUTES.ts | 4 + src/SCREENS.ts | 1 + .../Icon/chunks/expensify-icons.chunk.ts | 2 + src/components/MenuItem.tsx | 6 + src/languages/de.ts | 14 + src/languages/en.ts | 14 + src/languages/es.ts | 15 ++ src/languages/fr.ts | 15 ++ src/languages/it.ts | 14 + src/languages/ja.ts | 15 ++ src/languages/nl.ts | 14 + src/languages/pl.ts | 14 + src/languages/pt-BR.ts | 14 + src/languages/zh-hans.ts | 14 + .../ModalStackNavigators/index.tsx | 1 + .../RELATIONS/WORKSPACE_TO_RHP.ts | 1 + src/libs/Navigation/linkingConfig/config.ts | 2 +- src/libs/Navigation/types.ts | 4 + .../workspace/accounting/ClaimOfferPage.tsx | 157 +++++++++++ .../accounting/PolicyAccountingPage.tsx | 16 +- .../WorkspaceReceiptPartnersPage.tsx | 9 + 23 files changed, 594 insertions(+), 3 deletions(-) create mode 100644 assets/images/treasure-chest-green-with-sparkle.svg create mode 100644 src/pages/workspace/accounting/ClaimOfferPage.tsx diff --git a/assets/images/treasure-chest-green-with-sparkle.svg b/assets/images/treasure-chest-green-with-sparkle.svg new file mode 100644 index 000000000000..ca2344babadd --- /dev/null +++ b/assets/images/treasure-chest-green-with-sparkle.svg @@ -0,0 +1,247 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/CONST/index.ts b/src/CONST/index.ts index e1b5247c7136..6fda5ba47a7d 100755 --- a/src/CONST/index.ts +++ b/src/CONST/index.ts @@ -32,6 +32,8 @@ const USE_EXPENSIFY_URL = 'https://use.expensify.com'; const EXPENSIFY_MOBILE_URL = 'https://expensify.com/mobile'; const EXPENSIFY_URL = 'https://www.expensify.com'; const UBER_CONNECT_URL = 'https://business-integrations.uber.com/connect'; +const XERO_PARTNER_LINK = 'https://xero5440.partnerlinks.io/uzfjy4uegog2-v0pj1v'; +const UBER_TERMS_LINK = 'https://www.uber.com/us/en/business/sign-up/terms/expense-partners/'; const PLATFORM_OS_MACOS = 'Mac OS'; const PLATFORM_IOS = 'iOS'; const ANDROID_PACKAGE_NAME = 'org.me.mobiexpensifyg'; @@ -445,6 +447,8 @@ const CONST = { NEW_EXPENSIFY_URL: ACTIVE_EXPENSIFY_URL, UBER_CONNECT_URL, + XERO_PARTNER_LINK, + UBER_TERMS_LINK, APP_DOWNLOAD_LINKS: { ANDROID: `https://play.google.com/store/apps/details?id=${ANDROID_PACKAGE_NAME}`, IOS: 'https://apps.apple.com/us/app/expensify-travel-expense/id471713959', diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 3074432aa0d2..27db3f97e7b1 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -2951,6 +2951,10 @@ const ROUTES = { return `workspaces/${policyID}/accounting/xero/advanced` as const; }, }, + POLICY_ACCOUNTING_CLAIM_OFFER: { + route: 'workspaces/:policyID/accounting/claim-offer/:integration', + getRoute: (policyID: string, integration: string) => `workspaces/${policyID}/accounting/claim-offer/${integration}` as const, + }, POLICY_ACCOUNTING_XERO_AUTO_SYNC: { route: 'workspaces/:policyID/accounting/xero/advanced/autosync', getRoute: (policyID: string | undefined, backTo?: string) => { diff --git a/src/SCREENS.ts b/src/SCREENS.ts index ed79d21a5412..c0dc143c2294 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -495,6 +495,7 @@ const SCREENS = { QUICKBOOKS_DESKTOP_CUSTOMERS_DISPLAYED_AS: 'Policy_Accounting_Quickbooks_Desktop_Import_Customers_Displayed_As', QUICKBOOKS_DESKTOP_ITEMS: 'Policy_Accounting_Quickbooks_Desktop_Import_Items', XERO_IMPORT: 'Policy_Accounting_Xero_Import', + CLAIM_OFFER: 'Policy_Accounting_Claim_Offer', XERO_ORGANIZATION: 'Policy_Accounting_Xero_Customers', XERO_CHART_OF_ACCOUNTS: 'Policy_Accounting_Xero_Import_Chart_Of_Accounts', XERO_CUSTOMER: 'Policy_Accounting_Xero_Import_Customer', diff --git a/src/components/Icon/chunks/expensify-icons.chunk.ts b/src/components/Icon/chunks/expensify-icons.chunk.ts index 10cd17ba424d..8cf46183968c 100644 --- a/src/components/Icon/chunks/expensify-icons.chunk.ts +++ b/src/components/Icon/chunks/expensify-icons.chunk.ts @@ -214,6 +214,7 @@ import ThumbsUp from '@assets/images/thumbs-up.svg'; import Train from '@assets/images/train.svg'; import Transfer from '@assets/images/transfer.svg'; import Trashcan from '@assets/images/trashcan.svg'; +import TreasureChestGreenWithSparkle from '@assets/images/treasure-chest-green-with-sparkle.svg'; import TreasureChest from '@assets/images/treasure-chest.svg'; import Unlock from '@assets/images/unlock.svg'; import UploadAlt from '@assets/images/upload-alt.svg'; @@ -467,6 +468,7 @@ const Expensicons = { XeroExport, ArrowCircleClockwise, LuggageWithLines, + TreasureChestGreenWithSparkle, }; // Create the ExpensifyIcons object from the imported Expensicons diff --git a/src/components/MenuItem.tsx b/src/components/MenuItem.tsx index d8837d1ad898..d5b1a0cc9bf9 100644 --- a/src/components/MenuItem.tsx +++ b/src/components/MenuItem.tsx @@ -86,6 +86,9 @@ type MenuItemBaseProps = ForwardedFSClassProps & /** Whether the badge should be shown as success */ badgeSuccess?: boolean; + /** Callback to fire when the badge is pressed */ + onBadgePress?: (event?: GestureResponderEvent | KeyboardEvent) => void; + /** Used to apply offline styles to child text components */ style?: StyleProp; @@ -415,6 +418,7 @@ function MenuItem({ badgeText, badgeIcon, badgeSuccess, + onBadgePress, style, wrapperStyle, titleWrapperStyle, @@ -924,6 +928,8 @@ function MenuItem({ icon={badgeIcon} badgeStyles={badgeStyle} success={badgeSuccess} + onPress={onBadgePress} + pressable={!!onBadgePress} /> )} {/* Since subtitle can be of type number, we should allow 0 to be shown */} diff --git a/src/languages/de.ts b/src/languages/de.ts index 38639ee12c4b..457b2245adef 100644 --- a/src/languages/de.ts +++ b/src/languages/de.ts @@ -5604,6 +5604,20 @@ _Für ausführlichere Anweisungen [besuchen Sie unsere Hilfeseite](${CONST.NETSU connectPrompt: ({connectionName}: ConnectionNameParams) => `Sind Sie sicher, dass Sie ${CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[connectionName] ?? 'diese Buchhaltungsintegration'} verbinden möchten? Dadurch werden alle bestehenden Buchhaltungsverbindungen entfernt.`, enterCredentials: 'Geben Sie Ihre Anmeldedaten ein', + claimOffer: { + badgeText: 'Angebot verfügbar!', + xero: { + headline: '6 Monate kostenlos mit Xero!', + description: 'Neu bei Xero? Expensify-Kunden erhalten 6 Monate kostenlos. Fordern Sie Ihr Angebot unten an.', + connectButton: 'Mit Xero verbinden', + }, + uber: { + headerTitle: 'Uber for Business', + headline: '5% Rabatt auf Uber-Fahrten', + description: `Aktivieren Sie Uber for Business über Expensify und sparen Sie 5% bei allen Geschäftsreisen bis Juni. Bedingungen gelten.`, + connectButton: 'Mit Uber for Business verbinden', + }, + }, connections: { syncStageName: ({stage}: SyncStageNameConnectionsParams) => { switch (stage) { diff --git a/src/languages/en.ts b/src/languages/en.ts index 47bd22a5c711..95bec5125e60 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -5485,6 +5485,20 @@ const translations = { CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[connectionName] ?? 'this accounting integration' }? This will remove any existing accounting connections.`, enterCredentials: 'Enter your credentials', + claimOffer: { + badgeText: 'Offer available!', + xero: { + headline: 'Get Xero free for 6 months!', + description: 'New to Xero? Expensify customers get 6 months free. Claim your offer below.', + connectButton: 'Connect to Xero', + }, + uber: { + headerTitle: 'Uber for Business', + headline: 'Get 5% off Uber rides', + description: `Activate Uber for Business through Expensify and save 5% on all business rides through June. Terms apply.`, + connectButton: 'Connect to Uber for Business', + }, + }, connections: { syncStageName: ({stage}: SyncStageNameConnectionsParams) => { switch (stage) { diff --git a/src/languages/es.ts b/src/languages/es.ts index 1523187bbc45..c8fed9b2e414 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -5157,6 +5157,21 @@ ${amount} para ${merchant} - ${date}`, CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[connectionName] ?? 'esta integración contable' }? Esto eliminará cualquier conexión contable existente.`, enterCredentials: 'Ingresa tus credenciales', + claimOffer: { + badgeText: '¡Oferta disponible!', + xero: { + headline: '¡Obtén Xero gratis por 6 meses!', + description: + '¿Nuevo en Xero? Los clientes de Expensify obtienen 6 meses gratis. Reclama tu oferta a continuación.', + connectButton: 'Conectar con Xero', + }, + uber: { + headerTitle: 'Uber for Business', + headline: 'Obtén 5% de descuento en viajes de Uber', + description: `Activa Uber for Business a través de Expensify y ahorra 5% en todos los viajes de negocios hasta junio. Aplican términos.`, + connectButton: 'Conectar con Uber for Business', + }, + }, connections: { syncStageName: ({stage}) => { switch (stage) { diff --git a/src/languages/fr.ts b/src/languages/fr.ts index 5fe5aaa831b3..a97a9619ad2b 100644 --- a/src/languages/fr.ts +++ b/src/languages/fr.ts @@ -5611,6 +5611,21 @@ _Pour des instructions plus détaillées, [visitez notre site d’aide](${CONST. connectPrompt: ({connectionName}: ConnectionNameParams) => `Voulez-vous vraiment connecter ${CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[connectionName] ?? 'cette intégration comptable'} ? Cela supprimera toutes les connexions comptables existantes.`, enterCredentials: 'Saisissez vos identifiants', + claimOffer: { + badgeText: 'Offre disponible !', + xero: { + headline: 'Obtenez Xero gratuitement pendant 6 mois !', + description: + 'Nouveau sur Xero ? Les clients Expensify bénéficient de 6 mois gratuits. Réclamez votre offre ci-dessous.', + connectButton: 'Se connecter à Xero', + }, + uber: { + headerTitle: 'Uber for Business', + headline: 'Obtenez 5% de réduction sur les trajets Uber', + description: `Activez Uber for Business via Expensify et économisez 5% sur tous les trajets professionnels jusqu\'en juin. Conditions applicables.`, + connectButton: 'Se connecter à Uber for Business', + }, + }, connections: { syncStageName: ({stage}: SyncStageNameConnectionsParams) => { switch (stage) { diff --git a/src/languages/it.ts b/src/languages/it.ts index 50fe5beb0e63..3eb64a2ead33 100644 --- a/src/languages/it.ts +++ b/src/languages/it.ts @@ -5587,6 +5587,20 @@ _Per istruzioni più dettagliate, [visita il nostro sito di assistenza](${CONST. connectPrompt: ({connectionName}: ConnectionNameParams) => `Sei sicuro di voler collegare ${CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[connectionName] ?? 'questa integrazione contabile'}? Questo rimuoverà tutte le connessioni contabili esistenti.`, enterCredentials: 'Inserisci le tue credenziali', + claimOffer: { + badgeText: 'Offerta disponibile!', + xero: { + headline: 'Ottieni Xero gratis per 6 mesi!', + description: 'Nuovo su Xero? I clienti Expensify ottengono 6 mesi gratuiti. Richiedi la tua offerta qui sotto.', + connectButton: 'Connetti a Xero', + }, + uber: { + headerTitle: 'Uber for Business', + headline: 'Ottieni il 5% di sconto sui viaggi Uber', + description: `Attiva Uber for Business tramite Expensify e risparmia il 5% su tutti i viaggi di lavoro fino a giugno. Si applicano i termini.`, + connectButton: 'Connetti a Uber for Business', + }, + }, connections: { syncStageName: ({stage}: SyncStageNameConnectionsParams) => { switch (stage) { diff --git a/src/languages/ja.ts b/src/languages/ja.ts index 8b13494ec0bb..f38af9473c9a 100644 --- a/src/languages/ja.ts +++ b/src/languages/ja.ts @@ -5552,6 +5552,21 @@ _より詳しい手順については、[ヘルプサイトをご覧ください connectPrompt: ({connectionName}: ConnectionNameParams) => `${CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[connectionName] ?? 'この会計統合'} を接続してもよろしいですか?これにより、既存の会計連携はすべて削除されます。`, enterCredentials: '認証情報を入力してください', + claimOffer: { + badgeText: 'オファー利用可能!', + xero: { + headline: 'Xero を6か月間無料で利用!', + description: + 'Xero を初めてご利用ですか?Expensify のお客様は6か月間無料でご利用いただけます。以下のオファーを獲得してください。', + connectButton: 'Xero に接続', + }, + uber: { + headerTitle: 'Uber for Business', + headline: 'Uber の乗車で5%割引', + description: `Expensify を通じて Uber for Business を有効化すると、6月までのすべてのビジネス乗車で5%割引になります。条件が適用されます。`, + connectButton: 'Uber for Business に接続', + }, + }, connections: { syncStageName: ({stage}: SyncStageNameConnectionsParams) => { switch (stage) { diff --git a/src/languages/nl.ts b/src/languages/nl.ts index d461fa20aa09..f9eeff047d93 100644 --- a/src/languages/nl.ts +++ b/src/languages/nl.ts @@ -5578,6 +5578,20 @@ _Voor gedetailleerdere instructies, [bezoek onze helpsite](${CONST.NETSUITE_IMPO connectPrompt: ({connectionName}: ConnectionNameParams) => `Weet je zeker dat je ${CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[connectionName] ?? 'deze boekhoudkundige integratie'} wilt verbinden? Hierdoor worden alle bestaande boekhoudkoppelingen verwijderd.`, enterCredentials: 'Voer je inloggegevens in', + claimOffer: { + badgeText: 'Aanbieding beschikbaar!', + xero: { + headline: 'Krijg 6 maanden gratis Xero!', + description: 'Nieuw bij Xero? Expensify-klanten krijgen 6 maanden gratis. Verzilver je aanbieding hieronder.', + connectButton: 'Verbinden met Xero', + }, + uber: { + headerTitle: 'Uber for Business', + headline: 'Krijg 5% korting op Uber-ritten', + description: `Activeer Uber for Business via Expensify en bespaar 5% op alle zakelijke ritten tot en met juni. Voorwaarden zijn van toepassing.`, + connectButton: 'Verbinden met Uber for Business', + }, + }, connections: { syncStageName: ({stage}: SyncStageNameConnectionsParams) => { switch (stage) { diff --git a/src/languages/pl.ts b/src/languages/pl.ts index 859934be424f..dc454e2ab791 100644 --- a/src/languages/pl.ts +++ b/src/languages/pl.ts @@ -5569,6 +5569,20 @@ _Aby uzyskać bardziej szczegółowe instrukcje, [odwiedź naszą stronę pomocy connectPrompt: ({connectionName}: ConnectionNameParams) => `Czy na pewno chcesz połączyć ${CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[connectionName] ?? 'to połączenie z systemem księgowym'}? Spowoduje to usunięcie wszystkich istniejących połączeń księgowych.`, enterCredentials: 'Wprowadź swoje dane logowania', + claimOffer: { + badgeText: 'Oferta dostępna!', + xero: { + headline: 'Otrzymaj Xero za darmo na 6 miesięcy!', + description: 'Nowy w Xero? Klienci Expensify otrzymują 6 miesięcy za darmo. Odbierz swoją ofertę poniżej.', + connectButton: 'Połącz z Xero', + }, + uber: { + headerTitle: 'Uber for Business', + headline: 'Otrzymaj 5% zniżki na przejazdy Uber', + description: `Aktywuj Uber for Business przez Expensify i zaoszczędź 5% na wszystkich przejazdach służbowych do czerwca. Obowiązują warunki.`, + connectButton: 'Połącz z Uber for Business', + }, + }, connections: { syncStageName: ({stage}: SyncStageNameConnectionsParams) => { switch (stage) { diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts index 4f67d07d2e6b..88adb24a91fc 100644 --- a/src/languages/pt-BR.ts +++ b/src/languages/pt-BR.ts @@ -5570,6 +5570,20 @@ _Para instruções mais detalhadas, [visite nosso site de ajuda](${CONST.NETSUIT connectPrompt: ({connectionName}: ConnectionNameParams) => `Tem certeza de que deseja conectar ${CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[connectionName] ?? 'esta integração contábil'}? Isso removerá quaisquer conexões contábeis existentes.`, enterCredentials: 'Insira suas credenciais', + claimOffer: { + badgeText: 'Oferta disponível!', + xero: { + headline: 'Obtenha Xero grátis por 6 meses!', + description: 'Novo no Xero? Clientes Expensify ganham 6 meses grátis. Resgate sua oferta abaixo.', + connectButton: 'Conectar ao Xero', + }, + uber: { + headerTitle: 'Uber for Business', + headline: 'Obtenha 5% de desconto em viagens do Uber', + description: `Ative o Uber for Business através do Expensify e economize 5% em todas as viagens de negócios até junho. Termos se aplicam.`, + connectButton: 'Conectar ao Uber for Business', + }, + }, connections: { syncStageName: ({stage}: SyncStageNameConnectionsParams) => { switch (stage) { diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts index 23c246d3d493..cc9420c5fb94 100644 --- a/src/languages/zh-hans.ts +++ b/src/languages/zh-hans.ts @@ -5471,6 +5471,20 @@ _如需更详细的说明,请[访问我们的帮助网站](${CONST.NETSUITE_IM connectPrompt: ({connectionName}: ConnectionNameParams) => `您确定要连接 ${CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[connectionName] ?? '此会计集成'} 吗?这将移除所有现有的会计连接。`, enterCredentials: '请输入您的凭证', + claimOffer: { + badgeText: '优惠可用!', + xero: { + headline: '免费使用 Xero 6 个月!', + description: '首次使用 Xero?Expensify 客户可免费使用 6 个月。请在下方领取您的优惠。', + connectButton: '连接到 Xero', + }, + uber: { + headerTitle: 'Uber for Business', + headline: 'Uber 乘车享受 5% 折扣', + description: `通过 Expensify 激活 Uber for Business,在 6 月之前的所有商务乘车均可享受 5% 折扣。适用条款。`, + connectButton: '连接到 Uber for Business', + }, + }, connections: { syncStageName: ({stage}: SyncStageNameConnectionsParams) => { switch (stage) { diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index 79a8aaf0f59c..26a19992f7d2 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -568,6 +568,7 @@ const SettingsModalStackNavigator = createModalStackNavigator('../../../../pages/workspace/accounting/qbo/advanced/QuickbooksInvoiceAccountSelectPage').default, [SCREENS.WORKSPACE.ACCOUNTING.XERO_IMPORT]: () => require('../../../../pages/workspace/accounting/xero/XeroImportPage').default, + [SCREENS.WORKSPACE.ACCOUNTING.CLAIM_OFFER]: () => require('../../../../pages/workspace/accounting/ClaimOfferPage').default, [SCREENS.WORKSPACE.ACCOUNTING.XERO_ORGANIZATION]: () => require('../../../../pages/workspace/accounting/xero/XeroOrganizationConfigurationPage').default, [SCREENS.WORKSPACE.ACCOUNTING.XERO_CHART_OF_ACCOUNTS]: () => require('../../../../pages/workspace/accounting/xero/import/XeroChartOfAccountsPage').default, [SCREENS.WORKSPACE.ACCOUNTING.XERO_CUSTOMER]: () => require('../../../../pages/workspace/accounting/xero/import/XeroCustomerConfigurationPage').default, diff --git a/src/libs/Navigation/linkingConfig/RELATIONS/WORKSPACE_TO_RHP.ts b/src/libs/Navigation/linkingConfig/RELATIONS/WORKSPACE_TO_RHP.ts index 5dee0b252dd9..386f9aaeaca0 100755 --- a/src/libs/Navigation/linkingConfig/RELATIONS/WORKSPACE_TO_RHP.ts +++ b/src/libs/Navigation/linkingConfig/RELATIONS/WORKSPACE_TO_RHP.ts @@ -99,6 +99,7 @@ const WORKSPACE_TO_RHP: Partial['config'] = { [SCREENS.WORKSPACE.ACCOUNTING.XERO_EXPORT_PURCHASE_BILL_DATE_SELECT]: {path: ROUTES.POLICY_ACCOUNTING_XERO_EXPORT_PURCHASE_BILL_DATE_SELECT.route}, [SCREENS.WORKSPACE.ACCOUNTING.XERO_EXPORT_BANK_ACCOUNT_SELECT]: {path: ROUTES.POLICY_ACCOUNTING_XERO_EXPORT_BANK_ACCOUNT_SELECT.route}, [SCREENS.WORKSPACE.ACCOUNTING.XERO_ADVANCED]: {path: ROUTES.POLICY_ACCOUNTING_XERO_ADVANCED.route}, + [SCREENS.WORKSPACE.ACCOUNTING.CLAIM_OFFER]: {path: ROUTES.POLICY_ACCOUNTING_CLAIM_OFFER.route}, [SCREENS.WORKSPACE.ACCOUNTING.XERO_AUTO_SYNC]: {path: ROUTES.POLICY_ACCOUNTING_XERO_AUTO_SYNC.route}, [SCREENS.WORKSPACE.ACCOUNTING.XERO_ACCOUNTING_METHOD]: {path: ROUTES.POLICY_ACCOUNTING_XERO_ACCOUNTING_METHOD.route}, [SCREENS.WORKSPACE.ACCOUNTING.XERO_BILL_STATUS_SELECTOR]: {path: ROUTES.POLICY_ACCOUNTING_XERO_BILL_STATUS_SELECTOR.route}, @@ -822,7 +823,6 @@ const config: LinkingOptions['config'] = { [SCREENS.WORKSPACE.RECEIPT_PARTNERS_CHANGE_BILLING_ACCOUNT]: { path: ROUTES.WORKSPACE_RECEIPT_PARTNERS_CHANGE_BILLING_ACCOUNT.route, }, - [SCREENS.WORKSPACE.RECEIPT_PARTNERS_INVITE_EDIT]: { path: ROUTES.WORKSPACE_RECEIPT_PARTNERS_INVITE_EDIT.route, screens: { diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 6599295ac29b..562fef532e63 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -810,6 +810,10 @@ type SettingsNavigatorParamList = { [SCREENS.WORKSPACE.ACCOUNTING.XERO_ADVANCED]: { policyID: string; }; + [SCREENS.WORKSPACE.ACCOUNTING.CLAIM_OFFER]: { + policyID: string; + integration: string; + }; [SCREENS.WORKSPACE.ACCOUNTING.XERO_BILL_STATUS_SELECTOR]: { policyID: string; // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md diff --git a/src/pages/workspace/accounting/ClaimOfferPage.tsx b/src/pages/workspace/accounting/ClaimOfferPage.tsx new file mode 100644 index 000000000000..8222730da906 --- /dev/null +++ b/src/pages/workspace/accounting/ClaimOfferPage.tsx @@ -0,0 +1,157 @@ +import React, {useCallback, useMemo} from 'react'; +import {View} from 'react-native'; +import Button from '@components/Button'; +import FixedFooter from '@components/FixedFooter'; +import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import Icon from '@components/Icon'; +import RenderHTML from '@components/RenderHTML'; +import ScreenWrapper from '@components/ScreenWrapper'; +import ScrollView from '@components/ScrollView'; +import Text from '@components/Text'; +import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; +import useLocalize from '@hooks/useLocalize'; +import useNetwork from '@hooks/useNetwork'; +import usePolicy from '@hooks/usePolicy'; +import useThemeStyles from '@hooks/useThemeStyles'; +import {openExternalLink} from '@libs/actions/Link'; +import Navigation from '@libs/Navigation/Navigation'; +import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; +import type {SettingsNavigatorParamList} from '@navigation/types'; +import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; +import CONST from '@src/CONST'; +import type SCREENS from '@src/SCREENS'; +import type {PolicyFeatureName} from '@src/types/onyx/Policy'; +import {useAccountingContext} from './AccountingContext'; + +type IntegrationType = typeof CONST.POLICY.CONNECTIONS.NAME.XERO | typeof CONST.POLICY.RECEIPT_PARTNERS.NAME.UBER; + +type IntegrationConfig = { + headerTitle: string; + headline: string; + descriptionHtml: string; + claimOfferLink?: string; + connectButtonText: string; + connectionName: string; + featureName: PolicyFeatureName; + onConnect: () => void; +}; + +type ClaimOfferPageProps = PlatformStackScreenProps; + +function ClaimOfferPage({route}: ClaimOfferPageProps) { + const {integration, policyID} = route.params; + const styles = useThemeStyles(); + const {translate} = useLocalize(); + const {isOffline} = useNetwork(); + const {startIntegrationFlow} = useAccountingContext(); + const expensifyIcons = useMemoizedLazyExpensifyIcons(['TreasureChestGreenWithSparkle'] as const); + const policy = usePolicy(policyID); + const integrations = policy?.receiptPartners; + + const integrationConfig: Record = useMemo( + () => ({ + xero: { + headerTitle: translate('workspace.accounting.xero'), + headline: translate('workspace.accounting.claimOffer.xero.headline'), + descriptionHtml: translate('workspace.accounting.claimOffer.xero.description'), + claimOfferLink: CONST.XERO_PARTNER_LINK, + connectButtonText: translate('workspace.accounting.claimOffer.xero.connectButton'), + connectionName: CONST.POLICY.CONNECTIONS.NAME.XERO, + featureName: CONST.POLICY.MORE_FEATURES.ARE_CONNECTIONS_ENABLED, + onConnect: () => startIntegrationFlow({name: CONST.POLICY.CONNECTIONS.NAME.XERO}), + }, + uber: { + headerTitle: translate('workspace.accounting.claimOffer.uber.headerTitle'), + headline: translate('workspace.accounting.claimOffer.uber.headline'), + descriptionHtml: translate('workspace.accounting.claimOffer.uber.description'), + connectButtonText: translate('workspace.accounting.claimOffer.uber.connectButton'), + connectionName: CONST.POLICY.RECEIPT_PARTNERS.NAME.UBER, + featureName: CONST.POLICY.MORE_FEATURES.ARE_RECEIPT_PARTNERS_ENABLED, + onConnect: () => { + openExternalLink(`${CONST.UBER_CONNECT_URL}?${integrations?.uber?.connectFormData}`); + }, + }, + }), + [startIntegrationFlow, integrations?.uber?.connectFormData, translate], + ); + + const config = integrationConfig[integration as IntegrationType]; + + const handleClaimOffer = useCallback(() => { + if (!config.claimOfferLink) { + return; + } + openExternalLink(config.claimOfferLink); + }, [config.claimOfferLink]); + + const handleConnect = useCallback(() => { + config.onConnect(); + }, [config]); + + const buttons = useMemo( + () => ( + + {!!config.claimOfferLink && ( +