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

Improve workspace settings on Main Pane #10

Merged
merged 5 commits into from
Dec 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
1 change: 1 addition & 0 deletions src/SCREENS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ const SCREENS = {
INVITE: 'Workspace_Invite',
INVITE_MESSAGE: 'Workspace_Invite_Message',
CURRENCY: 'Workspace_Overview_Currency',
NAME: 'Workspace_Name',
},

EDIT_REQUEST: {
Expand Down
2 changes: 2 additions & 0 deletions src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,8 @@ const SettingsModalStackNavigator = createModalStackNavigator<SettingsNavigatorP
[SCREENS.WORKSPACE.RATE_AND_UNIT]: () => 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,
Expand Down
1 change: 1 addition & 0 deletions src/libs/Navigation/linkingConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,7 @@ const linkingConfig: LinkingOptions<RootStackParamList> = {
[SCREENS.KEYBOARD_SHORTCUTS]: {
path: ROUTES.KEYBOARD_SHORTCUTS,
},
[SCREENS.WORKSPACE.NAME]: ROUTES.WORKSPACE_NAME.route,
},
},
[SCREENS.RIGHT_MODAL.PRIVATE_NOTES]: {
Expand Down
7 changes: 4 additions & 3 deletions src/pages/workspace/WorkspaceMembersPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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();

Expand Down Expand Up @@ -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 = () => {
Expand Down Expand Up @@ -427,13 +428,13 @@ function WorkspaceMembersPage(props) {
>
<HeaderWithBackButton
title={props.translate('workspace.common.members')}
subtitle={policyName}
onBackButtonPress={() => {
setSearchValue('');
Navigation.goBack(ROUTES.WORKSPACE_INITIAL.getRoute(policyID));
}}
shouldShowGetAssistanceButton
shouldShowBackButton={isSmallScreenWidth}
guidesCallTaskID={CONST.GUIDES_CALL_TASK_IDS.WORKSPACE_MEMBERS}
shouldShowBorderBottom
/>
<ConfirmModal
danger
Expand Down
113 changes: 113 additions & 0 deletions src/pages/workspace/WorkspaceNamePage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import React, {useCallback} from 'react';
import {Keyboard, View} from 'react-native';
import {withOnyx} from 'react-native-onyx';
import FormProvider from '@components/Form/FormProvider';
import InputWrapper from '@components/Form/InputWrapper';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import {withNetwork} from '@components/OnyxProvider';
import ScreenWrapper from '@components/ScreenWrapper';
import TextInput from '@components/TextInput';
import {withCurrentUserPersonalDetailsDefaultProps, withCurrentUserPersonalDetailsPropTypes} from '@components/withCurrentUserPersonalDetails';
import {withLocalizePropTypes} from '@components/withLocalize';
import withWindowDimensions from '@components/withWindowDimensions';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import Navigation from '@libs/Navigation/Navigation';
import * as ValidationUtils from '@libs/ValidationUtils';
import * as Policy from '@userActions/Policy';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import withPolicy from './withPolicy';

const propTypes = {
...withLocalizePropTypes,
...withCurrentUserPersonalDetailsPropTypes,
};

const defaultProps = {
...withCurrentUserPersonalDetailsDefaultProps,
isLoadingApp: true,
};

function WorkspaceNamePage({policy}) {
const styles = useThemeStyles();
const {translate} = useLocalize();

const submit = useCallback(
(values) => {
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 (
<ScreenWrapper
includeSafeAreaPaddingBottom={false}
shouldEnableMaxHeight
testID={WorkspaceNamePage.displayName}
>
<HeaderWithBackButton
title={translate('workspace.editor.nameInputLabel')}
onBackButtonPress={() => Navigation.goBack(ROUTES.WORKSPACE_OVERVIEW.getRoute(policy.id))}
/>
<FormProvider
formID={ONYXKEYS.FORMS.WORKSPACE_SETTINGS_FORM}
submitButtonText={translate('workspace.editor.save')}
style={[styles.flexGrow1, styles.ph5]}
scrollContextEnabled
validate={validate}
onSubmit={submit}
enabledWhenOffline
>
<View style={styles.mb4}>
<InputWrapper
InputComponent={TextInput}
role={CONST.ROLE.PRESENTATION}
inputID="name"
label={translate('workspace.editor.nameInputLabel')}
accessibilityLabel={translate('workspace.editor.nameInputLabel')}
defaultValue={policy.name}
maxLength={CONST.WORKSPACE_NAME_CHARACTER_LIMIT}
spellCheck={false}
/>
</View>
</FormProvider>
</ScreenWrapper>
);
}

WorkspaceNamePage.propTypes = propTypes;
WorkspaceNamePage.defaultProps = defaultProps;
WorkspaceNamePage.displayName = 'WorkspaceNamePage';

export default compose(
withPolicy,
withWindowDimensions,
withOnyx({
currencyList: {key: ONYXKEYS.CURRENCY_LIST},
}),
withNetwork(),
)(WorkspaceNamePage);
68 changes: 13 additions & 55 deletions src/pages/workspace/WorkspaceOverviewPage.js
Original file line number Diff line number Diff line change
@@ -1,27 +1,23 @@
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';
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';
Expand Down Expand Up @@ -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', '');

Expand All @@ -101,15 +70,7 @@ function WorkspaceOverviewPage({policy, currencyList, windowWidth, route}) {
guidesCallTaskID={CONST.GUIDES_CALL_TASK_IDS.WORKSPACE_OVERVIEW}
>
{(hasVBA) => (
<FormProvider
formID={ONYXKEYS.FORMS.WORKSPACE_SETTINGS_FORM}
submitButtonText={translate('workspace.editor.save')}
style={[styles.flexGrow1, styles.ph5]}
scrollContextEnabled
validate={validate}
onSubmit={submit}
enabledWhenOffline
>
<>
<AvatarWithImagePicker
source={lodashGet(policy, 'avatar')}
size={CONST.AVATAR_SIZE.LARGE}
Expand All @@ -127,7 +88,7 @@ function WorkspaceOverviewPage({policy, currencyList, windowWidth, route}) {
)}
type={CONST.ICON_TYPE_WORKSPACE}
fallbackIcon={Expensicons.FallbackWorkspaceAvatar}
style={[styles.mb3]}
style={[styles.mb3, styles.mt5]}
anchorPosition={styles.createMenuPositionProfile(windowWidth)}
anchorAlignment={{horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.LEFT, vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.TOP}}
isUsingDefaultAvatar={!lodashGet(policy, 'avatar', null)}
Expand All @@ -142,18 +103,15 @@ function WorkspaceOverviewPage({policy, currencyList, windowWidth, route}) {
originalFileName={policy.originalFileName}
/>
<OfflineWithFeedback pendingAction={lodashGet(policy, 'pendingFields.generalSettings')}>
<InputWrapper
InputComponent={TextInput}
role={CONST.ROLE.PRESENTATION}
inputID="name"
label={translate('workspace.editor.nameInputLabel')}
accessibilityLabel={translate('workspace.editor.nameInputLabel')}
defaultValue={policy.name}
maxLength={CONST.WORKSPACE_NAME_CHARACTER_LIMIT}
containerStyles={[styles.mt4]}
spellCheck={false}
<MenuItemWithTopDescription
title={policy.name}
description={translate('workspace.editor.nameInputLabel')}
shouldShowRightIcon
disabled={hasVBA}
onPress={onPressName}
/>
<View style={[styles.mt4, styles.mhn5]}>

<View>
<MenuItemWithTopDescription
title={formattedCurrency}
description={translate('workspace.editor.currencyInputLabel')}
Expand All @@ -166,7 +124,7 @@ function WorkspaceOverviewPage({policy, currencyList, windowWidth, route}) {
</Text>
</View>
</OfflineWithFeedback>
</FormProvider>
</>
)}
</WorkspacePageWithSections>
);
Expand Down
7 changes: 4 additions & 3 deletions src/pages/workspace/WorkspacePageWithSections.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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);
Expand All @@ -112,10 +113,10 @@ function WorkspacePageWithSections({backButtonRoute, children, footer, guidesCal
>
<HeaderWithBackButton
title={headerText}
subtitle={policyName}
shouldShowGetAssistanceButton
guidesCallTaskID={guidesCallTaskID}
shouldShowBackButton={isSmallScreenWidth}
onBackButtonPress={() => Navigation.goBack(backButtonRoute || ROUTES.WORKSPACE_INITIAL.getRoute(policyID))}
shouldShowBorderBottom
/>
{shouldUseScrollView ? (
<ScrollViewWithContext
Expand Down
7 changes: 5 additions & 2 deletions src/pages/workspace/bills/WorkspaceBillsPage.js
Original file line number Diff line number Diff line change
@@ -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 WorkspaceBillsNoVBAView from './WorkspaceBillsNoVBAView';
Expand All @@ -20,6 +22,7 @@ const propTypes = {
};

function WorkspaceBillsPage(props) {
const styles = useThemeStyles();
return (
<WorkspacePageWithSections
shouldUseScrollView
Expand All @@ -28,10 +31,10 @@ function WorkspaceBillsPage(props) {
guidesCallTaskID={CONST.GUIDES_CALL_TASK_IDS.WORKSPACE_BILLS}
>
{(hasVBA, policyID) => (
<>
<View style={[styles.workspaceSection, styles.mt6]}>
{!hasVBA && <WorkspaceBillsNoVBAView policyID={policyID} />}
{hasVBA && <WorkspaceBillsVBAView policyID={policyID} />}
</>
</View>
)}
</WorkspacePageWithSections>
);
Expand Down
Loading
Loading