diff --git a/src/CONST.ts b/src/CONST.ts index 6cbd983b92253..f2d8f764f2648 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -6421,6 +6421,14 @@ const CONST = { description: 'workspace.upgrade.perDiem.description' as const, icon: 'PerDiem', }, + travel: { + id: 'travel' as const, + alias: 'travel', + name: 'Travel', + title: 'workspace.upgrade.travel.title' as const, + description: 'workspace.upgrade.travel.description' as const, + icon: 'Luggage', + }, }; }, REPORT_FIELD_TYPES: { diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 87664b718974d..3ad97d710d1f2 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -1618,6 +1618,7 @@ const ROUTES = { route: 'travel/terms/:domain/accept', getRoute: (domain: string, backTo?: string) => getUrlWithBackToParam(`travel/terms/${domain}/accept`, backTo), }, + TRAVEL_UPGRADE: 'travel/upgrade', TRACK_TRAINING_MODAL: 'track-training', TRAVEL_TRIP_SUMMARY: { route: 'r/:reportID/trip/:transactionID', diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 4ee20f34cf166..712f549d7595a 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -30,6 +30,7 @@ const SCREENS = { TCS: 'Travel_TCS', TRIP_SUMMARY: 'Travel_TripSummary', TRIP_DETAILS: 'Travel_TripDetails', + UPGRADE: 'Travel_Upgrade', DOMAIN_SELECTOR: 'Travel_DomainSelector', DOMAIN_PERMISSION_INFO: 'Travel_DomainPermissionInfo', PUBLIC_DOMAIN_ERROR: 'Travel_PublicDomainError', diff --git a/src/components/BookTravelButton.tsx b/src/components/BookTravelButton.tsx index 1953acde8ad47..edffb83158081 100644 --- a/src/components/BookTravelButton.tsx +++ b/src/components/BookTravelButton.tsx @@ -9,7 +9,7 @@ import {openTravelDotLink} from '@libs/actions/Link'; import {cleanupTravelProvisioningSession} from '@libs/actions/Travel'; import Log from '@libs/Log'; import Navigation from '@libs/Navigation/Navigation'; -import {getAdminsPrivateEmailDomains} from '@libs/PolicyUtils'; +import {getAdminsPrivateEmailDomains, isPaidGroupPolicy} from '@libs/PolicyUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; @@ -52,6 +52,11 @@ function BookTravelButton({text}: BookTravelButtonProps) { return; } + if (!isPaidGroupPolicy(policy)) { + Navigation.navigate(ROUTES.TRAVEL_UPGRADE); + return; + } + // Spotnana requires an address anytime an entity is created for a policy if (isEmptyObject(policy?.address)) { Navigation.navigate(ROUTES.WORKSPACE_PROFILE_ADDRESS.getRoute(policy?.id, Navigation.getActiveRoute())); diff --git a/src/components/WorkspaceConfirmationForm.tsx b/src/components/WorkspaceConfirmationForm.tsx new file mode 100644 index 0000000000000..b74cc279abeeb --- /dev/null +++ b/src/components/WorkspaceConfirmationForm.tsx @@ -0,0 +1,208 @@ +import React, {useCallback, useMemo, useState} from 'react'; +import {View} from 'react-native'; +import {useOnyx} from 'react-native-onyx'; +import useAutoFocusInput from '@hooks/useAutoFocusInput'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import {generateDefaultWorkspaceName, generatePolicyID} from '@libs/actions/Policy/Policy'; +import type {CustomRNImageManipulatorResult} from '@libs/cropOrRotateImage/types'; +import {addErrorMessage} from '@libs/ErrorUtils'; +import Navigation from '@libs/Navigation/Navigation'; +import {getDefaultWorkspaceAvatar} from '@libs/ReportUtils'; +import {isRequiredFulfilled} from '@libs/ValidationUtils'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; +import INPUT_IDS from '@src/types/form/WorkspaceConfirmationForm'; +import Avatar from './Avatar'; +import AvatarWithImagePicker from './AvatarWithImagePicker'; +import CurrencyPicker from './CurrencyPicker'; +import FormProvider from './Form/FormProvider'; +import InputWrapper from './Form/InputWrapper'; +import type {FormInputErrors, FormOnyxValues} from './Form/types'; +import HeaderWithBackButton from './HeaderWithBackButton'; +import * as Expensicons from './Icon/Expensicons'; +import ScrollView from './ScrollView'; +import Text from './Text'; +import TextInput from './TextInput'; + +function getFirstAlphaNumericCharacter(str = '') { + return str + .normalize('NFD') + .replace(/[^0-9a-z]/gi, '') + .toUpperCase()[0]; +} + +type WorkspaceConfirmationSubmitFunctionParams = { + name: string; + currency: string; + avatarFile: File | CustomRNImageManipulatorResult | undefined; + policyID: string; +}; + +type WorkspaceConfirmationFormProps = { + /** The email of the workspace owner + * @summary Approved Accountants and Guides can enter a flow where they make a workspace for other users, + * and those are passed as a search parameter when using transition links + */ + policyOwnerEmail?: string; + + /** Submit function */ + onSubmit: (params: WorkspaceConfirmationSubmitFunctionParams) => void; + + /** go back function */ + onBackButtonPress?: () => void; +}; + +function WorkspaceConfirmationForm({onSubmit, policyOwnerEmail = '', onBackButtonPress = () => Navigation.goBack()}: WorkspaceConfirmationFormProps) { + const styles = useThemeStyles(); + const {translate} = useLocalize(); + const {inputCallbackRef} = useAutoFocusInput(); + + const validate = useCallback( + (values: FormOnyxValues) => { + const errors: FormInputErrors = {}; + const name = values.name.trim(); + + if (!isRequiredFulfilled(name)) { + errors.name = translate('workspace.editor.nameIsRequiredError'); + } else if ([...name].length > CONST.TITLE_CHARACTER_LIMIT) { + // Uses the spread syntax to count the number of Unicode code points instead of the number of UTF-16 + // code units. + addErrorMessage(errors, 'name', translate('common.error.characterLimitExceedCounter', {length: [...name].length, limit: CONST.TITLE_CHARACTER_LIMIT})); + } + + if (!isRequiredFulfilled(values[INPUT_IDS.CURRENCY])) { + errors[INPUT_IDS.CURRENCY] = translate('common.error.fieldRequired'); + } + + return errors; + }, + [translate], + ); + + const policyID = useMemo(() => generatePolicyID(), []); + const [session] = useOnyx(ONYXKEYS.SESSION); + + const [allPersonalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST); + + const defaultWorkspaceName = generateDefaultWorkspaceName(policyOwnerEmail); + const [workspaceNameFirstCharacter, setWorkspaceNameFirstCharacter] = useState(defaultWorkspaceName ?? ''); + + const userCurrency = allPersonalDetails?.[session?.accountID ?? CONST.DEFAULT_NUMBER_ID]?.localCurrencyCode ?? CONST.CURRENCY.USD; + + const [workspaceAvatar, setWorkspaceAvatar] = useState<{avatarUri: string | null; avatarFileName?: string | null; avatarFileType?: string | null}>({ + avatarUri: null, + avatarFileName: null, + avatarFileType: null, + }); + const [avatarFile, setAvatarFile] = useState(); + + const stashedLocalAvatarImage = workspaceAvatar?.avatarUri ?? undefined; + + const DefaultAvatar = useCallback( + () => ( + + ), + [workspaceAvatar?.avatarUri, workspaceNameFirstCharacter, styles.alignSelfCenter, styles.avatarXLarge, policyID], + ); + + return ( + <> + + + + {translate('workspace.emptyWorkspace.subtitle')} + + { + setAvatarFile(image); + setWorkspaceAvatar({avatarUri: image.uri ?? '', avatarFileName: image.name ?? '', avatarFileType: image.type}); + }} + onImageRemoved={() => { + setAvatarFile(undefined); + setWorkspaceAvatar({avatarUri: null, avatarFileName: null, avatarFileType: null}); + }} + size={CONST.AVATAR_SIZE.XLARGE} + avatarStyle={[styles.avatarXLarge, styles.alignSelfCenter]} + shouldDisableViewPhoto + editIcon={Expensicons.Camera} + editIconStyle={styles.smallEditIconAccount} + type={CONST.ICON_TYPE_WORKSPACE} + style={[styles.w100, styles.alignItemsCenter, styles.mv4, styles.mb6, styles.alignSelfCenter, styles.ph5]} + DefaultAvatar={DefaultAvatar} + editorMaskImage={Expensicons.ImageCropSquareMask} + /> + + onSubmit({ + name: val[INPUT_IDS.NAME], + currency: val[INPUT_IDS.CURRENCY], + avatarFile, + policyID, + }) + } + enabledWhenOffline + > + + { + if (getFirstAlphaNumericCharacter(str) === getFirstAlphaNumericCharacter(workspaceNameFirstCharacter)) { + return; + } + setWorkspaceNameFirstCharacter(str); + }} + ref={inputCallbackRef} + /> + + + + + + + + + ); +} + +WorkspaceConfirmationForm.displayName = 'WorkspaceConfirmationForm'; + +export default WorkspaceConfirmationForm; + +export type {WorkspaceConfirmationSubmitFunctionParams}; diff --git a/src/languages/en.ts b/src/languages/en.ts index 6551eb5e8e9c5..d8d9885fea7a3 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -3942,7 +3942,7 @@ const translations = { }, emptyWorkspace: { title: 'Create a workspace', - subtitle: 'Create a workspace to track receipts, reimburse expenses, send invoices, and more — all at the speed of chat.', + subtitle: 'Create a workspace to track receipts, reimburse expenses, manage travel, send invoices, and more — all at the speed of chat.', createAWorkspaceCTA: 'Get Started', features: { trackAndCollect: 'Track and collect receipts', @@ -4518,6 +4518,11 @@ const translations = { 'Per diem is a great way to keep your daily costs compliant and predictable whenever your employees travel. Enjoy features like custom rates, default categories, and more granular details like destinations and subrates.', onlyAvailableOnPlan: 'Per diem are only available on the Control plan, starting at ', }, + travel: { + title: 'Travel', + description: 'Expensify Travel is a new corporate travel booking and management platform that allows members to book accommodations, flights, transportation, and more.', + onlyAvailableOnPlan: 'Travel is available on the Collect plan, starting at ', + }, pricing: { perActiveMember: 'per active member per month.', }, @@ -4531,6 +4536,7 @@ const translations = { headline: `You've upgraded your workspace!`, successMessage: ({policyName}: ReportPolicyNameParams) => `You've successfully upgraded ${policyName} to the Control plan!`, categorizeMessage: `You've successfully upgraded to a workspace on the Collect plan. Now you can categorize your expenses!`, + travelMessage: `You've successfully upgraded to a workspace on the Collect plan. Now you can start booking and managing travel!`, viewSubscription: 'View your subscription', moreDetails: 'for more details.', gotIt: 'Got it, thanks', diff --git a/src/languages/es.ts b/src/languages/es.ts index f2db2c5b49b8f..14ce570e749eb 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -3986,7 +3986,7 @@ const translations = { }, emptyWorkspace: { title: 'Crea un espacio de trabajo', - subtitle: 'Crea un espacio de trabajo para organizar recibos, reembolsar gastos, enviar facturas y mucho más, todo a la velocidad del chat.', + subtitle: 'Crea un espacio de trabajo para organizar recibos, reembolsar gastos, gestionar viajes, enviar facturas y mucho más, todo a la velocidad del chat.', createAWorkspaceCTA: 'Comenzar', features: { trackAndCollect: 'Organiza recibos', @@ -4584,6 +4584,12 @@ const translations = { 'Las dietas per diem (ej.: $100 por día para comidas) son una excelente forma de mantener los gastos diarios predecibles y ajustados a las políticas de la empresa, especialmente si tus empleados viajan por negocios. Disfruta de funciones como tasas personalizadas, categorías por defecto y detalles más específicos como destinos y subtasas.', onlyAvailableOnPlan: 'Las dietas per diem solo están disponibles en el plan Control, a partir de ', }, + travel: { + title: 'Viajes', + description: + 'Expensify Travel es una nueva plataforma corporativa de reserva y gestión de viajes que permite a los miembros reservar alojamientos, vuelos, transporte y mucho más.', + onlyAvailableOnPlan: 'Los viajes están disponibles en el plan Recopilar, a partir de ', + }, note: { upgradeWorkspace: 'Mejore su espacio de trabajo para acceder a esta función, o', learnMore: 'más información', @@ -4596,6 +4602,7 @@ const translations = { completed: { headline: 'Has mejorado tu espacio de trabajo.', categorizeMessage: `Has actualizado con éxito a un espacio de trabajo en el plan Recopilar. ¡Ahora puedes categorizar tus gastos!`, + travelMessage: 'Has mejorado con éxito a un espacio de trabajo en el plan Recopilar. ¡Ahora puedes comenzar a reservar y gestionar viajes!', successMessage: ({policyName}: ReportPolicyNameParams) => `Has actualizado con éxito ${policyName} al plan Controlar.`, viewSubscription: 'Ver su suscripción', moreDetails: 'para obtener más información.', diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index e3b5524f8bba1..5959e5d6c9fe0 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -112,6 +112,7 @@ const MoneyRequestModalStackNavigator = createModalStackNavigator({ [SCREENS.TRAVEL.MY_TRIPS]: () => require('../../../../pages/Travel/MyTripsPage').default, [SCREENS.TRAVEL.TCS]: () => require('../../../../pages/Travel/TravelTerms').default, + [SCREENS.TRAVEL.UPGRADE]: () => require('../../../../pages/Travel/TravelUpgrade').default, [SCREENS.TRAVEL.TRIP_SUMMARY]: () => require('../../../../pages/Travel/TripSummaryPage').default, [SCREENS.TRAVEL.TRIP_DETAILS]: () => require('../../../../pages/Travel/TripDetailsPage').default, [SCREENS.TRAVEL.DOMAIN_SELECTOR]: () => require('../../../../pages/Travel/DomainSelectorPage').default, diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index 1838b85fcee0d..c756b1c396ba6 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -1420,6 +1420,7 @@ const config: LinkingOptions['config'] = { [SCREENS.RIGHT_MODAL.TRAVEL]: { screens: { [SCREENS.TRAVEL.MY_TRIPS]: ROUTES.TRAVEL_MY_TRIPS, + [SCREENS.TRAVEL.UPGRADE]: ROUTES.TRAVEL_UPGRADE, [SCREENS.TRAVEL.TCS]: ROUTES.TRAVEL_TCS.route, [SCREENS.TRAVEL.TRIP_SUMMARY]: ROUTES.TRAVEL_TRIP_SUMMARY.route, [SCREENS.TRAVEL.TRIP_DETAILS]: { diff --git a/src/libs/actions/Travel.ts b/src/libs/actions/Travel.ts index ff613bb2d48da..b3a892c99ab60 100644 --- a/src/libs/actions/Travel.ts +++ b/src/libs/actions/Travel.ts @@ -58,5 +58,4 @@ function cleanupTravelProvisioningSession() { Onyx.merge(ONYXKEYS.TRAVEL_PROVISIONING, null); } -// eslint-disable-next-line import/prefer-default-export export {acceptSpotnanaTerms, cleanupTravelProvisioningSession}; diff --git a/src/pages/Travel/TravelUpgrade.tsx b/src/pages/Travel/TravelUpgrade.tsx new file mode 100644 index 0000000000000..78b8f8d7e4880 --- /dev/null +++ b/src/pages/Travel/TravelUpgrade.tsx @@ -0,0 +1,88 @@ +import React, {useState} from 'react'; +import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import Modal from '@components/Modal'; +import ScreenWrapper from '@components/ScreenWrapper'; +import type {WorkspaceConfirmationSubmitFunctionParams} from '@components/WorkspaceConfirmationForm'; +import WorkspaceConfirmationForm from '@components/WorkspaceConfirmationForm'; +import useLocalize from '@hooks/useLocalize'; +import useNetwork from '@hooks/useNetwork'; +import useThemeStyles from '@hooks/useThemeStyles'; +import Navigation from '@libs/Navigation/Navigation'; +import UpgradeConfirmation from '@pages/workspace/upgrade/UpgradeConfirmation'; +import UpgradeIntro from '@pages/workspace/upgrade/UpgradeIntro'; +import CONST from '@src/CONST'; +import {createDraftWorkspace, createWorkspace} from '@src/libs/actions/Policy/Policy'; + +function TravelUpgrade() { + const styles = useThemeStyles(); + const feature = CONST.UPGRADE_FEATURE_INTRO_MAPPING.travel; + const {translate} = useLocalize(); + const {isOffline} = useNetwork(); + + const [isUpgraded, setIsUpgraded] = useState(false); + const [shouldShowConfirmation, setShouldShowConfirmation] = useState(false); + + const onSubmit = (params: WorkspaceConfirmationSubmitFunctionParams) => { + createDraftWorkspace('', false, params.name, params.policyID, params.currency, params.avatarFile as File); + setShouldShowConfirmation(false); + setIsUpgraded(true); + createWorkspace('', false, params.name, params.policyID, undefined, params.currency, params.avatarFile as File); + }; + + const onClose = () => { + setShouldShowConfirmation(false); + }; + + return ( + + Navigation.goBack()} + /> + + + + + + {isUpgraded ? ( + Navigation.goBack()} + policyName="" + isTravelUpgrade + /> + ) : ( + setShouldShowConfirmation(true)} + buttonDisabled={isOffline} + loading={false} + isCategorizing + /> + )} + + ); +} + +TravelUpgrade.displayName = 'TravelUpgrade'; + +export default TravelUpgrade; diff --git a/src/pages/workspace/WorkspaceConfirmationPage.tsx b/src/pages/workspace/WorkspaceConfirmationPage.tsx index d5acbe8630a1b..ae3356ddb215d 100644 --- a/src/pages/workspace/WorkspaceConfirmationPage.tsx +++ b/src/pages/workspace/WorkspaceConfirmationPage.tsx @@ -1,192 +1,33 @@ -import React, {useCallback, useMemo, useState} from 'react'; -import {View} from 'react-native'; -import {useOnyx} from 'react-native-onyx'; -import Avatar from '@components/Avatar'; -import AvatarWithImagePicker from '@components/AvatarWithImagePicker'; -import CurrencyPicker from '@components/CurrencyPicker'; -import FormProvider from '@components/Form/FormProvider'; -import InputWrapper from '@components/Form/InputWrapper'; -import type {FormInputErrors, FormOnyxValues} from '@components/Form/types'; -import HeaderWithBackButton from '@components/HeaderWithBackButton'; -import * as Expensicons from '@components/Icon/Expensicons'; +import React from 'react'; import ScreenWrapper from '@components/ScreenWrapper'; -import ScrollView from '@components/ScrollView'; -import Text from '@components/Text'; -import TextInput from '@components/TextInput'; -import useAutoFocusInput from '@hooks/useAutoFocusInput'; -import useLocalize from '@hooks/useLocalize'; -import useThemeStyles from '@hooks/useThemeStyles'; +import WorkspaceConfirmationForm from '@components/WorkspaceConfirmationForm'; +import type {WorkspaceConfirmationSubmitFunctionParams} from '@components/WorkspaceConfirmationForm'; import {createWorkspaceWithPolicyDraftAndNavigateToIt} from '@libs/actions/App'; -import {generateDefaultWorkspaceName, generatePolicyID} from '@libs/actions/Policy/Policy'; -import type {CustomRNImageManipulatorResult} from '@libs/cropOrRotateImage/types'; -import {addErrorMessage} from '@libs/ErrorUtils'; import getCurrentUrl from '@libs/Navigation/currentUrl'; -import Navigation from '@libs/Navigation/Navigation'; -import {getDefaultWorkspaceAvatar} from '@libs/ReportUtils'; -import {isRequiredFulfilled} from '@libs/ValidationUtils'; -import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; -import INPUT_IDS from '@src/types/form/WorkspaceConfirmationForm'; -import withPolicy from './withPolicy'; - -function getFirstAlphaNumericCharacter(str = '') { - return str - .normalize('NFD') - .replace(/[^0-9a-z]/gi, '') - .toUpperCase()[0]; -} function WorkspaceConfirmationPage() { - const styles = useThemeStyles(); - const {translate} = useLocalize(); - const {inputCallbackRef} = useAutoFocusInput(); - - const validate = useCallback( - (values: FormOnyxValues) => { - const errors: FormInputErrors = {}; - const name = values.name.trim(); - - if (!isRequiredFulfilled(name)) { - errors.name = translate('workspace.editor.nameIsRequiredError'); - } else if ([...name].length > CONST.TITLE_CHARACTER_LIMIT) { - // Uses the spread syntax to count the number of Unicode code points instead of the number of UTF-16 - // code units. - addErrorMessage(errors, 'name', translate('common.error.characterLimitExceedCounter', {length: [...name].length, limit: CONST.TITLE_CHARACTER_LIMIT})); - } - - if (!isRequiredFulfilled(values[INPUT_IDS.CURRENCY])) { - errors[INPUT_IDS.CURRENCY] = translate('common.error.fieldRequired'); - } - - return errors; - }, - [translate], - ); - + const onSubmit = (params: WorkspaceConfirmationSubmitFunctionParams) => { + createWorkspaceWithPolicyDraftAndNavigateToIt('', params.name, false, false, '', params.policyID, params.currency, params.avatarFile as File); + }; const currentUrl = getCurrentUrl(); - const policyID = useMemo(() => generatePolicyID(), []); - const [session] = useOnyx(ONYXKEYS.SESSION); // Approved Accountants and Guides can enter a flow where they make a workspace for other users, // and those are passed as a search parameter when using transition links const policyOwnerEmail = currentUrl ? new URL(currentUrl).searchParams.get('ownerEmail') ?? '' : ''; - const [allPersonalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST); - - const defaultWorkspaceName = generateDefaultWorkspaceName(policyOwnerEmail); - const [workspaceNameFirstCharacter, setWorkspaceNameFirstCharacter] = useState(defaultWorkspaceName ?? ''); - - const userCurrency = allPersonalDetails?.[session?.accountID ?? CONST.DEFAULT_NUMBER_ID]?.localCurrencyCode ?? CONST.CURRENCY.USD; - - const [workspaceAvatar, setWorkspaceAvatar] = useState<{avatarUri: string | null; avatarFileName?: string | null; avatarFileType?: string | null}>({ - avatarUri: null, - avatarFileName: null, - avatarFileType: null, - }); - const [avatarFile, setAvatarFile] = useState(); - - const stashedLocalAvatarImage = workspaceAvatar?.avatarUri ?? undefined; - - const DefaultAvatar = useCallback( - () => ( - - ), - [workspaceAvatar?.avatarUri, workspaceNameFirstCharacter, styles.alignSelfCenter, styles.avatarXLarge, policyID], - ); - return ( - Navigation.goBack()} + - - - {translate('workspace.emptyWorkspace.subtitle')} - - { - setAvatarFile(image); - setWorkspaceAvatar({avatarUri: image.uri ?? '', avatarFileName: image.name ?? '', avatarFileType: image.type}); - }} - onImageRemoved={() => { - setAvatarFile(undefined); - setWorkspaceAvatar({avatarUri: null, avatarFileName: null, avatarFileType: null}); - }} - size={CONST.AVATAR_SIZE.XLARGE} - avatarStyle={[styles.avatarXLarge, styles.alignSelfCenter]} - shouldDisableViewPhoto - editIcon={Expensicons.Camera} - editIconStyle={styles.smallEditIconAccount} - type={CONST.ICON_TYPE_WORKSPACE} - style={[styles.w100, styles.alignItemsCenter, styles.mv4, styles.mb6, styles.alignSelfCenter, styles.ph5]} - DefaultAvatar={DefaultAvatar} - editorMaskImage={Expensicons.ImageCropSquareMask} - /> - { - createWorkspaceWithPolicyDraftAndNavigateToIt('', val[INPUT_IDS.NAME], false, false, '', policyID, val[INPUT_IDS.CURRENCY], avatarFile as File); - }} - enabledWhenOffline - > - - { - if (getFirstAlphaNumericCharacter(str) === getFirstAlphaNumericCharacter(workspaceNameFirstCharacter)) { - return; - } - setWorkspaceNameFirstCharacter(str); - }} - ref={inputCallbackRef} - /> - - - - - - - ); } WorkspaceConfirmationPage.displayName = 'WorkspaceConfirmationPage'; -export default withPolicy(WorkspaceConfirmationPage); +export default WorkspaceConfirmationPage; diff --git a/src/pages/workspace/upgrade/UpgradeConfirmation.tsx b/src/pages/workspace/upgrade/UpgradeConfirmation.tsx index 9824b2919d659..8e93a62a73c71 100644 --- a/src/pages/workspace/upgrade/UpgradeConfirmation.tsx +++ b/src/pages/workspace/upgrade/UpgradeConfirmation.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, {useMemo} from 'react'; import ConfirmationPage from '@components/ConfirmationPage'; import TextLink from '@components/TextLink'; import useLocalize from '@hooks/useLocalize'; @@ -10,31 +10,40 @@ type Props = { policyName: string; onConfirmUpgrade: () => void; isCategorizing?: boolean; + isTravelUpgrade?: boolean; }; -function UpgradeConfirmation({policyName, onConfirmUpgrade, isCategorizing}: Props) { +function UpgradeConfirmation({policyName, onConfirmUpgrade, isCategorizing, isTravelUpgrade}: Props) { const {translate} = useLocalize(); const styles = useThemeStyles(); + const description = useMemo(() => { + if (isCategorizing) { + return translate('workspace.upgrade.completed.categorizeMessage'); + } + + if (isTravelUpgrade) { + return translate('workspace.upgrade.completed.travelMessage'); + } + + return ( + <> + {translate('workspace.upgrade.completed.successMessage', {policyName})}{' '} + Navigation.navigate(ROUTES.SETTINGS_SUBSCRIPTION)} + > + {translate('workspace.upgrade.completed.viewSubscription')} + {' '} + {translate('workspace.upgrade.completed.moreDetails')} + + ); + }, [isCategorizing, isTravelUpgrade, policyName, styles.link, translate]); + return ( - {translate('workspace.upgrade.completed.successMessage', {policyName})}{' '} - Navigation.navigate(ROUTES.SETTINGS_SUBSCRIPTION)} - > - {translate('workspace.upgrade.completed.viewSubscription')} - {' '} - {translate('workspace.upgrade.completed.moreDetails')} - - ) - } + description={description} shouldShowButton onButtonPress={onConfirmUpgrade} buttonText={translate('workspace.upgrade.completed.gotIt')}