Skip to content

Commit

Permalink
Merge pull request #52164 from Krishna2323/krishna2323/issue/51504
Browse files Browse the repository at this point in the history
feat: Provide education/confirmation before creating workspaces in New Workspace flows
  • Loading branch information
grgia authored Dec 9, 2024
2 parents 0ef4fb1 + 4a7359d commit 37105a1
Show file tree
Hide file tree
Showing 21 changed files with 393 additions and 25 deletions.
3 changes: 3 additions & 0 deletions src/ONYXKEYS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,8 @@ const ONYXKEYS = {
ADD_PAYMENT_CARD_FORM_DRAFT: 'addPaymentCardFormDraft',
WORKSPACE_SETTINGS_FORM: 'workspaceSettingsForm',
WORKSPACE_CATEGORY_FORM: 'workspaceCategoryForm',
WORKSPACE_CONFIRMATION_FORM: 'workspaceConfirmationForm',
WORKSPACE_CONFIRMATION_FORM_DRAFT: 'workspaceConfirmationFormDraft',
WORKSPACE_CATEGORY_FORM_DRAFT: 'workspaceCategoryFormDraft',
WORKSPACE_CATEGORY_DESCRIPTION_HINT_FORM: 'workspaceCategoryDescriptionHintForm',
WORKSPACE_CATEGORY_DESCRIPTION_HINT_FORM_DRAFT: 'workspaceCategoryDescriptionHintFormDraft',
Expand Down Expand Up @@ -733,6 +735,7 @@ type OnyxFormValuesMapping = {
[ONYXKEYS.FORMS.ADD_PAYMENT_CARD_FORM]: FormTypes.AddPaymentCardForm;
[ONYXKEYS.FORMS.WORKSPACE_SETTINGS_FORM]: FormTypes.WorkspaceSettingsForm;
[ONYXKEYS.FORMS.WORKSPACE_CATEGORY_FORM]: FormTypes.WorkspaceCategoryForm;
[ONYXKEYS.FORMS.WORKSPACE_CONFIRMATION_FORM]: FormTypes.WorkspaceConfirmationForm;
[ONYXKEYS.FORMS.WORKSPACE_TAG_FORM]: FormTypes.WorkspaceTagForm;
[ONYXKEYS.FORMS.WORKSPACE_TAX_CUSTOM_NAME]: FormTypes.WorkspaceTaxCustomName;
[ONYXKEYS.FORMS.WORKSPACE_COMPANY_CARD_FEED_NAME]: FormTypes.WorkspaceCompanyCardFeedName;
Expand Down
2 changes: 1 addition & 1 deletion src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1387,8 +1387,8 @@ const ROUTES = {
},
WELCOME_VIDEO_ROOT: 'onboarding/welcome-video',
EXPLANATION_MODAL_ROOT: 'onboarding/explanation',
WORKSPACE_CONFIRMATION: 'workspace/confirmation',
MIGRATED_USER_WELCOME_MODAL: 'onboarding/migrated-user-welcome',

TRANSACTION_RECEIPT: {
route: 'r/:reportID/transaction/:transactionID/receipt',
getRoute: (reportID: string, transactionID: string, readonly = false, isFromReviewDuplicates = false) =>
Expand Down
3 changes: 3 additions & 0 deletions src/SCREENS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ const SCREENS = {
DETAILS: 'Details',
PROFILE: 'Profile',
REPORT_DETAILS: 'Report_Details',
WORKSPACE_CONFIRMATION: 'Workspace_Confirmation',
REPORT_SETTINGS: 'Report_Settings',
REPORT_DESCRIPTION: 'Report_Description',
PARTICIPANTS: 'Participants',
Expand Down Expand Up @@ -317,6 +318,8 @@ const SCREENS = {
EXPORT: 'Report_Details_Export',
},

WORKSPACE_CONFIRMATION: {ROOT: 'Workspace_Confirmation_Root'},

WORKSPACE: {
ACCOUNTING: {
ROOT: 'Policy_Accounting',
Expand Down
87 changes: 87 additions & 0 deletions src/components/CurrencyPicker.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import React, {forwardRef, useState} from 'react';
import type {ForwardedRef} from 'react';
import {View} from 'react-native';
import useStyleUtils from '@hooks/useStyleUtils';
import useThemeStyles from '@hooks/useThemeStyles';
import variables from '@styles/variables';
import CONST from '@src/CONST';
import CurrencySelectionListWithOnyx from './CurrencySelectionList';
import HeaderWithBackButton from './HeaderWithBackButton';
import MenuItemWithTopDescription from './MenuItemWithTopDescription';
import Modal from './Modal';
import ScreenWrapper from './ScreenWrapper';
import type {ValuePickerItem, ValuePickerProps} from './ValuePicker/types';

type CurrencyPickerProps = {
selectedCurrency?: string;
};
function CurrencyPicker({selectedCurrency, label, errorText = '', value, onInputChange, furtherDetails}: ValuePickerProps & CurrencyPickerProps, forwardedRef: ForwardedRef<View>) {
const StyleUtils = useStyleUtils();
const styles = useThemeStyles();
const [isPickerVisible, setIsPickerVisible] = useState(false);

const showPickerModal = () => {
setIsPickerVisible(true);
};

const hidePickerModal = () => {
setIsPickerVisible(false);
};

const updateInput = (item: ValuePickerItem) => {
if (item.value !== selectedCurrency) {
onInputChange?.(item.value);
}
hidePickerModal();
};

const descStyle = !selectedCurrency || selectedCurrency.length === 0 ? StyleUtils.getFontSizeStyle(variables.fontSizeLabel) : null;

return (
<View>
<MenuItemWithTopDescription
ref={forwardedRef}
shouldShowRightIcon
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
title={value || ''}
descriptionTextStyle={descStyle}
description={label}
onPress={showPickerModal}
furtherDetails={furtherDetails}
brickRoadIndicator={errorText ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined}
errorText={errorText}
/>

<Modal
type={CONST.MODAL.MODAL_TYPE.RIGHT_DOCKED}
isVisible={isPickerVisible}
onClose={() => hidePickerModal}
onModalHide={hidePickerModal}
hideModalContentWhileAnimating
useNativeDriver
onBackdropPress={hidePickerModal}
>
<ScreenWrapper
style={styles.pb0}
includePaddingTop={false}
includeSafeAreaPaddingBottom={false}
testID={label ?? 'TEST'}
>
<HeaderWithBackButton
title={label}
onBackButtonPress={hidePickerModal}
/>
<CurrencySelectionListWithOnyx
onSelect={(item) => updateInput({value: item.currencyCode})}
searchInputLabel="Currency"
initiallySelectedCurrencyCode={selectedCurrency}
/>
</ScreenWrapper>
</Modal>
</View>
);
}

CurrencyPicker.displayName = 'CurrencyPicker';

export default forwardRef(CurrencyPicker);
3 changes: 2 additions & 1 deletion src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3716,7 +3716,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, send invoices, and more all at the speed of chat.',
createAWorkspaceCTA: 'Get Started',
features: {
trackAndCollect: 'Track and collect receipts',
Expand All @@ -3734,6 +3734,7 @@ const translations = {
new: {
newWorkspace: 'New workspace',
getTheExpensifyCardAndMore: 'Get the Expensify Card and more',
confirmWorkspace: 'Confirm Workspace',
},
people: {
genericFailureMessage: 'An error occurred removing a member from the workspace, please try again.',
Expand Down
1 change: 1 addition & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3779,6 +3779,7 @@ const translations = {
new: {
newWorkspace: 'Nuevo espacio de trabajo',
getTheExpensifyCardAndMore: 'Consigue la Tarjeta Expensify y más',
confirmWorkspace: 'Confirmar espacio de trabajo',
},
people: {
genericFailureMessage: 'Se ha producido un error al intentar eliminar a un miembro del espacio de trabajo. Por favor, inténtalo más tarde.',
Expand Down
2 changes: 2 additions & 0 deletions src/libs/API/parameters/CreateWorkspaceParams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ type CreateWorkspaceParams = {
customUnitID: string;
customUnitRateID: string;
engagementChoice?: string;
currency: string;
file?: File;
};

export default CreateWorkspaceParams;
7 changes: 7 additions & 0 deletions src/libs/CurrencyUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import Onyx from 'react-native-onyx';
import CONST from '@src/CONST';
import type {OnyxValues} from '@src/ONYXKEYS';
import ONYXKEYS from '@src/ONYXKEYS';
import type {Currency} from '@src/types/onyx';
import BaseLocaleListener from './Localize/LocaleListener/BaseLocaleListener';
import * as NumberFormatUtils from './NumberFormatUtils';

Expand Down Expand Up @@ -30,6 +31,11 @@ function getCurrencyDecimals(currency: string = CONST.CURRENCY.USD): number {
return decimals ?? 2;
}

function getCurrency(currency: string = CONST.CURRENCY.USD): Currency | null {
const currencyItem = currencyList?.[currency];
return currencyItem;
}

/**
* Returns the currency's minor unit quantity
* e.g. Cent in USD
Expand Down Expand Up @@ -216,5 +222,6 @@ export {
convertToDisplayStringWithoutCurrency,
isValidCurrencyCode,
convertToShortDisplayString,
getCurrency,
sanitizeCurrencyCode,
};
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import type {
TransactionDuplicateNavigatorParamList,
TravelNavigatorParamList,
WalletStatementNavigatorParamList,
WorkspaceConfirmationNavigatorParamList,
} from '@navigation/types';
import type {Screen} from '@src/SCREENS';
import SCREENS from '@src/SCREENS';
Expand Down Expand Up @@ -129,6 +130,10 @@ const ReportSettingsModalStackNavigator = createModalStackNavigator<ReportSettin
[SCREENS.REPORT_SETTINGS.VISIBILITY]: () => require<ReactComponentModule>('../../../../pages/settings/Report/VisibilityPage').default,
});

const WorkspaceConfirmationModalStackNavigator = createModalStackNavigator<WorkspaceConfirmationNavigatorParamList>({
[SCREENS.WORKSPACE_CONFIRMATION.ROOT]: () => require<ReactComponentModule>('../../../../pages/workspace/WorkspaceConfirmationPage').default,
});

const TaskModalStackNavigator = createModalStackNavigator<TaskDetailsNavigatorParamList>({
[SCREENS.TASK.TITLE]: () => require<ReactComponentModule>('../../../../pages/tasks/TaskTitlePage').default,
[SCREENS.TASK.ASSIGNEE]: () => require<ReactComponentModule>('../../../../pages/tasks/TaskAssigneeSelectorModal').default,
Expand Down Expand Up @@ -715,4 +720,5 @@ export {
SearchSavedSearchModalStackNavigator,
MissingPersonalDetailsModalStackNavigator,
DebugModalStackNavigator,
WorkspaceConfirmationModalStackNavigator,
};
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,10 @@ function RightModalNavigator({navigation, route}: RightModalNavigatorProps) {
name={SCREENS.RIGHT_MODAL.MONEY_REQUEST}
component={ModalStackNavigators.MoneyRequestModalStackNavigator}
/>
<Stack.Screen
name={SCREENS.RIGHT_MODAL.WORKSPACE_CONFIRMATION}
component={ModalStackNavigators.WorkspaceConfirmationModalStackNavigator}
/>
<Stack.Screen
name={SCREENS.RIGHT_MODAL.NEW_TASK}
component={ModalStackNavigators.NewTaskModalStackNavigator}
Expand Down
5 changes: 5 additions & 0 deletions src/libs/Navigation/linkingConfig/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1150,6 +1150,11 @@ const config: LinkingOptions<RootStackParamList>['config'] = {
},
},
},
[SCREENS.RIGHT_MODAL.WORKSPACE_CONFIRMATION]: {
screens: {
[SCREENS.WORKSPACE_CONFIRMATION.ROOT]: ROUTES.WORKSPACE_CONFIRMATION,
},
},
[SCREENS.RIGHT_MODAL.NEW_TASK]: {
screens: {
[SCREENS.NEW_TASK.ROOT]: ROUTES.NEW_TASK.route,
Expand Down
6 changes: 6 additions & 0 deletions src/libs/Navigation/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1206,6 +1206,10 @@ type MoneyRequestNavigatorParamList = {
};
};

type WorkspaceConfirmationNavigatorParamList = {
[SCREENS.WORKSPACE_CONFIRMATION.ROOT]: undefined;
};

type NewTaskNavigatorParamList = {
[SCREENS.NEW_TASK.ROOT]: {
backTo?: Routes;
Expand Down Expand Up @@ -1382,6 +1386,7 @@ type RightModalNavigatorParamList = {
[SCREENS.RIGHT_MODAL.PARTICIPANTS]: NavigatorScreenParams<ParticipantsNavigatorParamList>;
[SCREENS.RIGHT_MODAL.ROOM_MEMBERS]: NavigatorScreenParams<RoomMembersNavigatorParamList>;
[SCREENS.RIGHT_MODAL.MONEY_REQUEST]: NavigatorScreenParams<MoneyRequestNavigatorParamList>;
[SCREENS.RIGHT_MODAL.WORKSPACE_CONFIRMATION]: NavigatorScreenParams<WorkspaceConfirmationNavigatorParamList>;
[SCREENS.RIGHT_MODAL.NEW_TASK]: NavigatorScreenParams<NewTaskNavigatorParamList>;
[SCREENS.RIGHT_MODAL.TEACHERS_UNITE]: NavigatorScreenParams<TeachersUniteNavigatorParamList>;
[SCREENS.RIGHT_MODAL.TASK_DETAILS]: NavigatorScreenParams<TaskDetailsNavigatorParamList>;
Expand Down Expand Up @@ -1771,5 +1776,6 @@ export type {
RestrictedActionParamList,
MissingPersonalDetailsParamList,
DebugParamList,
WorkspaceConfirmationNavigatorParamList,
MigratedUserModalNavigatorParamList,
};
23 changes: 17 additions & 6 deletions src/libs/actions/App.ts
Original file line number Diff line number Diff line change
Expand Up @@ -367,18 +367,29 @@ function endSignOnTransition() {
* @param [transitionFromOldDot] Optional, if the user is transitioning from old dot
* @param [makeMeAdmin] Optional, leave the calling account as an admin on the policy
* @param [backTo] An optional return path. If provided, it will be URL-encoded and appended to the resulting URL.
* @param [policyID] Optional, Policy id.
* @param [file],file
*/
function createWorkspaceWithPolicyDraftAndNavigateToIt(policyOwnerEmail = '', policyName = '', transitionFromOldDot = false, makeMeAdmin = false, backTo = '') {
const policyID = Policy.generatePolicyID();
Policy.createDraftInitialWorkspace(policyOwnerEmail, policyName, policyID, makeMeAdmin);
function createWorkspaceWithPolicyDraftAndNavigateToIt(
policyOwnerEmail = '',
policyName = '',
transitionFromOldDot = false,
makeMeAdmin = false,
backTo = '',
policyID = '',
currency?: string,
file?: File,
) {
const genereatedPolicyID = Policy.generatePolicyID();
Policy.createDraftInitialWorkspace(policyOwnerEmail, policyName, policyID || genereatedPolicyID, makeMeAdmin, currency, file);

Navigation.isNavigationReady()
.then(() => {
if (transitionFromOldDot) {
// We must call goBack() to remove the /transition route from history
Navigation.goBack();
}
savePolicyDraftByNewWorkspace(policyID, policyName, policyOwnerEmail, makeMeAdmin);
savePolicyDraftByNewWorkspace(policyID, policyName, policyOwnerEmail, makeMeAdmin, currency, file);
Navigation.navigate(ROUTES.WORKSPACE_INITIAL.getRoute(policyID, backTo));
})
.then(endSignOnTransition);
Expand All @@ -392,8 +403,8 @@ function createWorkspaceWithPolicyDraftAndNavigateToIt(policyOwnerEmail = '', po
* @param [policyOwnerEmail] Optional, the email of the account to make the owner of the policy
* @param [makeMeAdmin] Optional, leave the calling account as an admin on the policy
*/
function savePolicyDraftByNewWorkspace(policyID?: string, policyName?: string, policyOwnerEmail = '', makeMeAdmin = false) {
Policy.createWorkspace(policyOwnerEmail, makeMeAdmin, policyName, policyID);
function savePolicyDraftByNewWorkspace(policyID?: string, policyName?: string, policyOwnerEmail = '', makeMeAdmin = false, currency = '', file?: File) {
Policy.createWorkspace(policyOwnerEmail, makeMeAdmin, policyName, policyID, '', currency, file);
}

/**
Expand Down
Loading

0 comments on commit 37105a1

Please sign in to comment.