diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 31fe5b434570..873f6d0c0f16 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -460,6 +460,10 @@ const ROUTES = { route: 'workspace/:policyID/members', getRoute: (policyID: string) => `workspace/${policyID}/members` as const, }, + WORKSPACE_NAME: { + route: 'workspace/:policyID/name', + getRoute: (policyID: string) => `workspace/${policyID}/name` as const, + }, // Referral program promotion REFERRAL_DETAILS_MODAL: { route: 'referral/:contentType', diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 9ba6358b4ba4..7d0b66d441fc 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -201,6 +201,7 @@ const SCREENS = { INVITE: 'Workspace_Invite', INVITE_MESSAGE: 'Workspace_Invite_Message', CURRENCY: 'Workspace_Overview_Currency', + NAME: 'Workspace_Name', }, EDIT_REQUEST: { diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx index 63243b008d96..90bb88fc100a 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx @@ -240,6 +240,8 @@ const SettingsModalStackNavigator = createModalStackNavigator require('../../../pages/workspace/reimburse/WorkspaceRateAndUnitPage').default as React.ComponentType, [SCREENS.WORKSPACE.INVITE]: () => require('../../../pages/workspace/WorkspaceInvitePage').default as React.ComponentType, [SCREENS.WORKSPACE.INVITE_MESSAGE]: () => require('../../../pages/workspace/WorkspaceInviteMessagePage').default as React.ComponentType, + [SCREENS.WORKSPACE.NAME]: () => require('../../../pages/workspace/WorkspaceNamePage').default as React.ComponentType, + [SCREENS.WORKSPACE.CURRENCY]: () => require('../../../pages/workspace/WorkspaceOverviewCurrencyPage').default as React.ComponentType, [SCREENS.REIMBURSEMENT_ACCOUNT]: () => require('../../../pages/ReimbursementAccount/ReimbursementAccountPage').default as React.ComponentType, [SCREENS.GET_ASSISTANCE]: () => require('../../../pages/GetAssistancePage').default as React.ComponentType, [SCREENS.SETTINGS.TWO_FACTOR_AUTH]: () => require('../../../pages/settings/Security/TwoFactorAuth/TwoFactorAuthPage').default as React.ComponentType, diff --git a/src/libs/Navigation/linkingConfig.ts b/src/libs/Navigation/linkingConfig.ts index f6daf824ae5b..03f05394450c 100644 --- a/src/libs/Navigation/linkingConfig.ts +++ b/src/libs/Navigation/linkingConfig.ts @@ -288,6 +288,7 @@ const linkingConfig: LinkingOptions = { [SCREENS.KEYBOARD_SHORTCUTS]: { path: ROUTES.KEYBOARD_SHORTCUTS, }, + [SCREENS.WORKSPACE.NAME]: ROUTES.WORKSPACE_NAME.route, }, }, [SCREENS.RIGHT_MODAL.PRIVATE_NOTES]: { diff --git a/src/pages/workspace/WorkspaceMembersPage.js b/src/pages/workspace/WorkspaceMembersPage.js index 9834d4e9e1c0..1c70abf3fe65 100644 --- a/src/pages/workspace/WorkspaceMembersPage.js +++ b/src/pages/workspace/WorkspaceMembersPage.js @@ -20,6 +20,7 @@ import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; import withWindowDimensions, {windowDimensionsPropTypes} from '@components/withWindowDimensions'; import usePrevious from '@hooks/usePrevious'; import useThemeStyles from '@hooks/useThemeStyles'; +import useWindowDimensions from '@hooks/useWindowDimensions'; import * as Browser from '@libs/Browser'; import compose from '@libs/compose'; import Log from '@libs/Log'; @@ -86,6 +87,7 @@ function WorkspaceMembersPage(props) { const textInputRef = useRef(null); const isOfflineAndNoMemberDataAvailable = _.isEmpty(props.policyMembers) && props.network.isOffline; const prevPersonalDetails = usePrevious(props.personalDetails); + const {isSmallScreenWidth} = useWindowDimensions(); const isFocusedScreen = useIsFocused(); @@ -301,7 +303,6 @@ function WorkspaceMembersPage(props) { const policyOwner = lodashGet(props.policy, 'owner'); const currentUserLogin = lodashGet(props.currentUserPersonalDetails, 'login'); const policyID = lodashGet(props.route, 'params.policyID'); - const policyName = lodashGet(props.policy, 'name'); const invitedPrimaryToSecondaryLogins = _.invert(props.policy.primaryLoginsInvited); const getMemberOptions = () => { @@ -427,13 +428,13 @@ function WorkspaceMembersPage(props) { > { setSearchValue(''); Navigation.goBack(ROUTES.WORKSPACE_INITIAL.getRoute(policyID)); }} - shouldShowGetAssistanceButton + shouldShowBackButton={isSmallScreenWidth} guidesCallTaskID={CONST.GUIDES_CALL_TASK_IDS.WORKSPACE_MEMBERS} + shouldShowBorderBottom /> { + if (policy.isPolicyUpdating) { + return; + } + + Policy.updateGeneralSettings(policy.id, values.name.trim(), policy.outputCurrency); + Keyboard.dismiss(); + Navigation.goBack(ROUTES.WORKSPACE_OVERVIEW.getRoute(policy.id)); + }, + [policy.id, policy.isPolicyUpdating, policy.outputCurrency], + ); + + const validate = useCallback((values) => { + const errors = {}; + const name = values.name.trim(); + + if (!ValidationUtils.isRequiredFulfilled(name)) { + errors.name = 'workspace.editor.nameIsRequiredError'; + } else if ([...name].length > CONST.WORKSPACE_NAME_CHARACTER_LIMIT) { + // Uses the spread syntax to count the number of Unicode code points instead of the number of UTF-16 + // code units. + errors.name = 'workspace.editor.nameIsTooLongError'; + } + + return errors; + }, []); + + return ( + + Navigation.goBack(ROUTES.WORKSPACE_OVERVIEW.getRoute(policy.id))} + /> + + + + + + + ); +} + +WorkspaceNamePage.propTypes = propTypes; +WorkspaceNamePage.defaultProps = defaultProps; +WorkspaceNamePage.displayName = 'WorkspaceNamePage'; + +export default compose( + withPolicy, + withWindowDimensions, + withOnyx({ + currencyList: {key: ONYXKEYS.CURRENCY_LIST}, + }), + withNetwork(), +)(WorkspaceNamePage); diff --git a/src/pages/workspace/WorkspaceOverviewPage.js b/src/pages/workspace/WorkspaceOverviewPage.js index 148dec8f7637..bd6959da09fe 100644 --- a/src/pages/workspace/WorkspaceOverviewPage.js +++ b/src/pages/workspace/WorkspaceOverviewPage.js @@ -1,19 +1,16 @@ import lodashGet from 'lodash/get'; import PropTypes from 'prop-types'; import React, {useCallback} from 'react'; -import {Keyboard, View} from 'react-native'; +import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; import Avatar from '@components/Avatar'; import AvatarWithImagePicker from '@components/AvatarWithImagePicker'; -import FormProvider from '@components/Form/FormProvider'; -import InputWrapper from '@components/Form/InputWrapper'; import * as Expensicons from '@components/Icon/Expensicons'; import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; import {withNetwork} from '@components/OnyxProvider'; import Text from '@components/Text'; -import TextInput from '@components/TextInput'; import withWindowDimensions, {windowDimensionsPropTypes} from '@components/withWindowDimensions'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -21,7 +18,6 @@ import compose from '@libs/compose'; import Navigation from '@libs/Navigation/Navigation'; import * as ReportUtils from '@libs/ReportUtils'; import * as UserUtils from '@libs/UserUtils'; -import * as ValidationUtils from '@libs/ValidationUtils'; import * as Policy from '@userActions/Policy'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -62,35 +58,8 @@ function WorkspaceOverviewPage({policy, currencyList, windowWidth, route}) { const formattedCurrency = !_.isEmpty(policy) && !_.isEmpty(currencyList) ? `${policy.outputCurrency} - ${currencyList[policy.outputCurrency].symbol}` : ''; - const submit = useCallback( - (values) => { - if (policy.isPolicyUpdating) { - return; - } - - Policy.updateGeneralSettings(policy.id, values.name.trim(), policy.outputCurrency); - Keyboard.dismiss(); - Navigation.goBack(ROUTES.WORKSPACE_INITIAL.getRoute(policy.id)); - }, - [policy.id, policy.isPolicyUpdating, policy.outputCurrency], - ); - - const validate = useCallback((values) => { - const errors = {}; - const name = values.name.trim(); - - if (!ValidationUtils.isRequiredFulfilled(name)) { - errors.name = 'workspace.editor.nameIsRequiredError'; - } else if ([...name].length > CONST.WORKSPACE_NAME_CHARACTER_LIMIT) { - // Uses the spread syntax to count the number of Unicode code points instead of the number of UTF-16 - // code units. - errors.name = 'workspace.editor.nameIsTooLongError'; - } - - return errors; - }, []); - const onPressCurrency = useCallback(() => Navigation.navigate(ROUTES.WORKSPACE_OVERVIEW_CURRENCY.getRoute(policy.id)), [policy.id]); + const onPressName = useCallback(() => Navigation.navigate(ROUTES.WORKSPACE_NAME.getRoute(policy.id)), [policy.id]); const policyName = lodashGet(policy, 'name', ''); @@ -101,15 +70,7 @@ function WorkspaceOverviewPage({policy, currencyList, windowWidth, route}) { guidesCallTaskID={CONST.GUIDES_CALL_TASK_IDS.WORKSPACE_OVERVIEW} > {(hasVBA) => ( - + <> - - + + - + )} ); diff --git a/src/pages/workspace/WorkspacePageWithSections.js b/src/pages/workspace/WorkspacePageWithSections.js index 6b5c179a2e51..e91ff47362b2 100644 --- a/src/pages/workspace/WorkspacePageWithSections.js +++ b/src/pages/workspace/WorkspacePageWithSections.js @@ -10,6 +10,7 @@ import ScreenWrapper from '@components/ScreenWrapper'; import ScrollViewWithContext from '@components/ScrollViewWithContext'; import useNetwork from '@hooks/useNetwork'; import useThemeStyles from '@hooks/useThemeStyles'; +import useWindowDimensions from '@hooks/useWindowDimensions'; import compose from '@libs/compose'; import BankAccount from '@libs/models/BankAccount'; import Navigation from '@libs/Navigation/Navigation'; @@ -91,8 +92,8 @@ function WorkspacePageWithSections({backButtonRoute, children, footer, guidesCal const hasVBA = achState === BankAccount.STATE.OPEN; const isUsingECard = lodashGet(user, 'isUsingExpensifyCard', false); const policyID = lodashGet(route, 'params.policyID'); - const policyName = lodashGet(policy, 'name'); const content = children(hasVBA, policyID, isUsingECard); + const {isSmallScreenWidth} = useWindowDimensions(); useEffect(() => { fetchData(shouldSkipVBBACall); @@ -112,10 +113,10 @@ function WorkspacePageWithSections({backButtonRoute, children, footer, guidesCal > Navigation.goBack(backButtonRoute || ROUTES.WORKSPACE_INITIAL.getRoute(policyID))} + shouldShowBorderBottom /> {shouldUseScrollView ? ( {(hasVBA, policyID) => ( - <> + {!hasVBA && } {hasVBA && } - + )} ); diff --git a/src/pages/workspace/card/WorkspaceCardPage.js b/src/pages/workspace/card/WorkspaceCardPage.js index 55220b85ce63..2e2b6be80699 100644 --- a/src/pages/workspace/card/WorkspaceCardPage.js +++ b/src/pages/workspace/card/WorkspaceCardPage.js @@ -1,6 +1,8 @@ import PropTypes from 'prop-types'; import React from 'react'; +import {View} from 'react-native'; import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; import WorkspacePageWithSections from '@pages/workspace/WorkspacePageWithSections'; import CONST from '@src/CONST'; import WorkspaceCardNoVBAView from './WorkspaceCardNoVBAView'; @@ -21,6 +23,7 @@ const propTypes = { }; function WorkspaceCardPage(props) { + const styles = useThemeStyles(); return ( {(hasVBA, policyID, isUsingECard) => ( - <> + {!hasVBA && } {hasVBA && !isUsingECard && } {hasVBA && isUsingECard && } - + )} ); diff --git a/src/pages/workspace/invoices/WorkspaceInvoicesPage.js b/src/pages/workspace/invoices/WorkspaceInvoicesPage.js index 8bc0c4484100..c63ce185fd48 100644 --- a/src/pages/workspace/invoices/WorkspaceInvoicesPage.js +++ b/src/pages/workspace/invoices/WorkspaceInvoicesPage.js @@ -1,6 +1,8 @@ import PropTypes from 'prop-types'; import React from 'react'; +import {View} from 'react-native'; import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; import WorkspacePageWithSections from '@pages/workspace/WorkspacePageWithSections'; import CONST from '@src/CONST'; import WorkspaceInvoicesNoVBAView from './WorkspaceInvoicesNoVBAView'; @@ -20,6 +22,7 @@ const propTypes = { }; function WorkspaceInvoicesPage(props) { + const styles = useThemeStyles(); return ( {(hasVBA, policyID) => ( - <> + {!hasVBA && } {hasVBA && } - + )} ); diff --git a/src/pages/workspace/reimburse/WorkspaceReimburseView.js b/src/pages/workspace/reimburse/WorkspaceReimburseView.js index 23136064fc2b..678d03665b62 100644 --- a/src/pages/workspace/reimburse/WorkspaceReimburseView.js +++ b/src/pages/workspace/reimburse/WorkspaceReimburseView.js @@ -103,7 +103,7 @@ function WorkspaceReimburseView(props) { }, [props.policy.customUnits, getCurrentRatePerUnitLabel]); return ( - <> +
- + ); } diff --git a/src/pages/workspace/travel/WorkspaceTravelPage.js b/src/pages/workspace/travel/WorkspaceTravelPage.js index a88e180cc7b6..a386320a9c8f 100644 --- a/src/pages/workspace/travel/WorkspaceTravelPage.js +++ b/src/pages/workspace/travel/WorkspaceTravelPage.js @@ -1,6 +1,8 @@ import PropTypes from 'prop-types'; import React from 'react'; +import {View} from 'react-native'; import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; import WorkspacePageWithSections from '@pages/workspace/WorkspacePageWithSections'; import CONST from '@src/CONST'; import WorkspaceTravelNoVBAView from './WorkspaceTravelNoVBAView'; @@ -20,6 +22,7 @@ const propTypes = { }; function WorkspaceTravelPage(props) { + const styles = useThemeStyles(); return ( {(hasVBA, policyID) => ( - <> + {!hasVBA && } {hasVBA && } - + )} ); diff --git a/src/styles/index.ts b/src/styles/index.ts index 6cc51c5ad489..713e32eeab45 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -4139,6 +4139,11 @@ const styles = (theme: ThemeColors) => lineHeight: variables.lineHeightXLarge, }, + workspaceSection: { + maxWidth: variables.workspaceSectionMaxWidth, + alignSelf: 'center', + }, + aspectRatioLottie: (animation: DotLottieAnimation) => ({aspectRatio: animation.w / animation.h}), receiptDropHeaderGap: { diff --git a/src/styles/variables.ts b/src/styles/variables.ts index 4e4d70dd3a07..b7fb0e7fbad8 100644 --- a/src/styles/variables.ts +++ b/src/styles/variables.ts @@ -179,6 +179,7 @@ export default { reportActionItemImagesMoreCornerTriangleWidth: 40, bankCardWidth: 40, bankCardHeight: 26, + workspaceSectionMaxWidth: 560, // The height of the empty list is 14px (2px for borders and 12px for vertical padding) // This is calculated based on the values specified in the 'getGoogleListViewStyle' function of the 'StyleUtils' utility