Skip to content

Commit

Permalink
Merge pull request #51328 from MrMuzyk/feat/step-1-ui
Browse files Browse the repository at this point in the history
feat: Step 1 country (without API calls)
  • Loading branch information
madmax330 authored Oct 28, 2024
2 parents 5103b4b + 1256fb4 commit 7445ef2
Show file tree
Hide file tree
Showing 11 changed files with 209 additions and 14 deletions.
57 changes: 57 additions & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3325,6 +3325,63 @@ const CONST = {
ZW: 'Zimbabwe',
},

ALL_EUROPEAN_COUNTRIES: {
AL: 'Albania',
AD: 'Andorra',
AT: 'Austria',
BY: 'Belarus',
BE: 'Belgium',
BA: 'Bosnia & Herzegovina',
BG: 'Bulgaria',
HR: 'Croatia',
CY: 'Cyprus',
CZ: 'Czech Republic',
DK: 'Denmark',
EE: 'Estonia',
FO: 'Faroe Islands',
FI: 'Finland',
FR: 'France',
GE: 'Georgia',
DE: 'Germany',
GI: 'Gibraltar',
GR: 'Greece',
GL: 'Greenland',
HU: 'Hungary',
IS: 'Iceland',
IE: 'Ireland',
IM: 'Isle of Man',
IT: 'Italy',
JE: 'Jersey',
XK: 'Kosovo',
LV: 'Latvia',
LI: 'Liechtenstein',
LT: 'Lithuania',
LU: 'Luxembourg',
MT: 'Malta',
MD: 'Moldova',
MC: 'Monaco',
ME: 'Montenegro',
NL: 'Netherlands',
MK: 'North Macedonia',
NO: 'Norway',
PL: 'Poland',
PT: 'Portugal',
RO: 'Romania',
RU: 'Russia',
SM: 'San Marino',
RS: 'Serbia',
SK: 'Slovakia',
SI: 'Slovenia',
ES: 'Spain',
SJ: 'Svalbard & Jan Mayen',
SE: 'Sweden',
CH: 'Switzerland',
TR: 'Turkey',
UA: 'Ukraine',
GB: 'United Kingdom',
VA: 'Vatican City',
},

// Sources: https://github.com/Expensify/App/issues/14958#issuecomment-1442138427
// https://github.com/Expensify/App/issues/14958#issuecomment-1456026810
COUNTRY_ZIP_REGEX_DATA: {
Expand Down
4 changes: 3 additions & 1 deletion src/components/Form/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import type DatePicker from '@components/DatePicker';
import type EmojiPickerButtonDropdown from '@components/EmojiPicker/EmojiPickerButtonDropdown';
import type PercentageForm from '@components/PercentageForm';
import type Picker from '@components/Picker';
import type PushRowWithModal from '@components/PushRowWithModal';
import type RadioButtons from '@components/RadioButtons';
import type RoomNameInput from '@components/RoomNameInput';
import type SingleChoiceQuestion from '@components/SingleChoiceQuestion';
Expand Down Expand Up @@ -63,7 +64,8 @@ type ValidInputs =
| typeof NetSuiteMenuWithTopDescriptionForm
| typeof CountryPicker
| typeof StatePicker
| typeof ConstantSelector;
| typeof ConstantSelector
| typeof PushRowWithModal;

type ValueTypeKey = 'string' | 'boolean' | 'date' | 'country' | 'reportFields' | 'disabledListValues';
type ValueTypeMap = {
Expand Down
7 changes: 6 additions & 1 deletion src/components/PushRowWithModal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ type PushRowWithModalProps = {
selectedOption: string;

/** Function to call when the user selects an option */
onOptionChange: (value: string) => void;
onOptionChange: (option: string) => void;

/** Additional styles to apply to container */
wrapperStyles?: StyleProp<ViewStyle>;
Expand All @@ -31,6 +31,9 @@ type PushRowWithModalProps = {

/** Text to display on error message */
errorText?: string;

/** Function called whenever option changes */
onInputChange?: (value: string) => void;
};

function PushRowWithModal({
Expand All @@ -43,6 +46,7 @@ function PushRowWithModal({
searchInputTitle,
shouldAllowChange = true,
errorText,
onInputChange = () => {},
}: PushRowWithModalProps) {
const [isModalVisible, setIsModalVisible] = useState(false);

Expand All @@ -56,6 +60,7 @@ function PushRowWithModal({

const handleOptionChange = (value: string) => {
onOptionChange(value);
onInputChange(value);
};

return (
Expand Down
4 changes: 4 additions & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2245,6 +2245,10 @@ const translations = {
countryStep: {
confirmBusinessBank: 'Confirm business bank account currency and country',
confirmCurrency: 'Confirm currency and country',
yourBusiness: 'Your business bank account currency must match your workspace currency.',
youCanChange: 'You can change your workspace currency in your',
findCountry: 'Find country',
selectCountry: 'Select country',
},
signerInfoStep: {
signerInfo: 'Signer info',
Expand Down
4 changes: 4 additions & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2268,6 +2268,10 @@ const translations = {
countryStep: {
confirmBusinessBank: 'Confirmar moneda y país de la cuenta bancaria comercial',
confirmCurrency: 'Confirmar moneda y país',
yourBusiness: 'La moneda de su cuenta bancaria comercial debe coincidir con la moneda de su espacio de trabajo.',
youCanChange: 'Puede cambiar la moneda de su espacio de trabajo en su',
findCountry: 'Encontrar país',
selectCountry: 'Seleccione su país',
},
signerInfoStep: {
signerInfo: 'Información del firmante',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,48 +1,114 @@
import React, {useState} from 'react';
import React, {useCallback, useEffect, useState} from 'react';
import {useOnyx} from 'react-native-onyx';
import FormProvider from '@components/Form/FormProvider';
import InputWrapper from '@components/Form/InputWrapper';
import type {FormInputErrors, FormOnyxValues} from '@components/Form/types';
import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription';
import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback';
import PushRowWithModal from '@components/PushRowWithModal';
import SafeAreaConsumer from '@components/SafeAreaConsumer';
import ScrollView from '@components/ScrollView';
import Text from '@components/Text';
import useLocalize from '@hooks/useLocalize';
import type {SubStepProps} from '@hooks/useSubStep/types';
import useThemeStyles from '@hooks/useThemeStyles';
import Navigation from '@libs/Navigation/Navigation';
import * as ValidationUtils from '@libs/ValidationUtils';
import mapCurrencyToCountry from '@pages/ReimbursementAccount/utils/mapCurrencyToCountry';
import * as FormActions from '@userActions/FormActions';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import INPUT_IDS from '@src/types/form/ReimbursementAccountForm';

const {COUNTRY} = INPUT_IDS.ADDITIONAL_DATA;

function Confirmation({onNext}: SubStepProps) {
const {translate} = useLocalize();
const styles = useThemeStyles();
const [reimbursementAccount] = useOnyx(ONYXKEYS.REIMBURSEMENT_ACCOUNT);
const [reimbursementAccountDraft] = useOnyx(ONYXKEYS.FORMS.REIMBURSEMENT_ACCOUNT_FORM_DRAFT);

const policyID = reimbursementAccount?.achData?.policyID ?? '-1';
const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`);
const currency = policy?.outputCurrency ?? '';

const shouldAllowChange = currency === CONST.CURRENCY.EUR;
const currencyMappedToCountry = mapCurrencyToCountry(currency);

const [selectedCountry, setSelectedCountry] = useState<string>('');
const countryDefaultValue = reimbursementAccount?.achData?.additionalData?.[COUNTRY] ?? reimbursementAccountDraft?.[COUNTRY] ?? '';
const [selectedCountry, setSelectedCountry] = useState<string>(countryDefaultValue);

const disableSubmit = !(currency in CONST.CURRENCY);

const handleSettingsPress = () => {
Navigation.navigate(ROUTES.WORKSPACE_PROFILE.getRoute(policyID));
};

const handleSelectingCountry = (country: string) => {
FormActions.setDraftValues(ONYXKEYS.FORMS.REIMBURSEMENT_ACCOUNT_FORM, {[COUNTRY]: country});
setSelectedCountry(country);
};

const validate = useCallback((values: FormOnyxValues<typeof ONYXKEYS.FORMS.REIMBURSEMENT_ACCOUNT_FORM>): FormInputErrors<typeof ONYXKEYS.FORMS.REIMBURSEMENT_ACCOUNT_FORM> => {
return ValidationUtils.getFieldRequiredErrors(values, [COUNTRY]);
}, []);

useEffect(() => {
if (currency === CONST.CURRENCY.EUR) {
return;
}

FormActions.setDraftValues(ONYXKEYS.FORMS.REIMBURSEMENT_ACCOUNT_FORM, {[COUNTRY]: currencyMappedToCountry});
setSelectedCountry(currencyMappedToCountry);
}, [currency, currencyMappedToCountry]);

return (
<SafeAreaConsumer>
{({safeAreaPaddingBottomStyle}) => (
<ScrollView
style={styles.pt0}
contentContainerStyle={[styles.flexGrow1, safeAreaPaddingBottomStyle]}
>
<Text style={[styles.textHeadlineLineHeightXXL, styles.ph5, styles.mb3]}>{translate('countryStep.confirmBusinessBank')}</Text>
<MenuItemWithTopDescription
description={translate('common.currency')}
title={currency}
interactive={false}
/>
<Text style={[styles.ph5, styles.mb3, styles.mutedTextLabel]}>
{`${translate('countryStep.yourBusiness')} ${translate('countryStep.youCanChange')}`}
<PressableWithoutFeedback
accessibilityRole="button"
accessibilityLabel={translate('common.settings')}
accessible
onPress={handleSettingsPress}
style={styles.ml1}
>
<Text style={[styles.label, styles.textBlue]}>{translate('common.settings').toLowerCase()}</Text>
</PressableWithoutFeedback>
.
</Text>
<FormProvider
formID={ONYXKEYS.FORMS.REIMBURSEMENT_ACCOUNT_FORM}
submitButtonText={translate('common.confirm')}
validate={validate}
onSubmit={onNext}
style={[styles.flexGrow1]}
submitButtonStyles={[styles.mh5, styles.pb0, styles.mbn1]}
isSubmitDisabled={disableSubmit}
>
<Text style={[styles.textHeadlineLineHeightXXL, styles.ph5, styles.mb3]}>{translate('countryStep.confirmBusinessBank')}</Text>
{/* This is only to showcase usage of PushRowWithModal component. The actual implementation will come in next issue - https://github.com/Expensify/App/issues/50897 */}
<PushRowWithModal
optionsList={CONST.ALL_COUNTRIES}
<InputWrapper
InputComponent={PushRowWithModal}
optionsList={shouldAllowChange ? CONST.ALL_EUROPEAN_COUNTRIES : CONST.ALL_COUNTRIES}
selectedOption={selectedCountry}
onOptionChange={handleSelectingCountry}
description={translate('common.country')}
modalHeaderTitle="Select country"
searchInputTitle="Find country"
modalHeaderTitle={translate('countryStep.selectCountry')}
searchInputTitle={translate('countryStep.findCountry')}
shouldAllowChange={shouldAllowChange}
value={selectedCountry}
inputID={COUNTRY}
/>
</FormProvider>
</ScrollView>
Expand Down
7 changes: 4 additions & 3 deletions src/pages/ReimbursementAccount/ReimbursementAccountPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ function ReimbursementAccountPage({route, policy}: ReimbursementAccountPageProps
const [onfidoToken = ''] = useOnyx(ONYXKEYS.ONFIDO_TOKEN);
const [isLoadingApp = false] = useOnyx(ONYXKEYS.IS_LOADING_APP);
const [account] = useOnyx(ONYXKEYS.ACCOUNT);
const [isDebugModeEnabled] = useOnyx(ONYXKEYS.USER, {selector: (user) => !!user?.isDebugModeEnabled});

const policyName = policy?.name ?? '';
const policyIDParam = route.params?.policyID ?? '-1';
Expand Down Expand Up @@ -178,7 +179,7 @@ function ReimbursementAccountPage({route, policy}: ReimbursementAccountPageProps
const [hasACHDataBeenLoaded, setHasACHDataBeenLoaded] = useState(reimbursementAccount !== CONST.REIMBURSEMENT_ACCOUNT.DEFAULT_DATA && isPreviousPolicy);
const [shouldShowContinueSetupButton, setShouldShowContinueSetupButton] = useState(getShouldShowContinueSetupButtonInitialValue());

function getBankAccountFields<T extends InputID>(fieldNames: T[]): Pick<ACHDataReimbursementAccount, T> {
function getBankAccountFields(fieldNames: InputID[]): Partial<ACHDataReimbursementAccount> {
return {
...lodashPick(reimbursementAccount?.achData, ...fieldNames),
};
Expand Down Expand Up @@ -439,8 +440,8 @@ function ReimbursementAccountPage({route, policy}: ReimbursementAccountPageProps
const policyCurrency = policy?.outputCurrency ?? '';
// TODO once nonUSD flow is complete update the flag below to reflect all supported currencies, this will be updated in - https://github.com/Expensify/App/issues/50912
const hasUnsupportedCurrency = policyCurrency !== CONST.CURRENCY.USD;
// TODO remove isDevelopment flag once nonUSD flow is complete, this will be updated in - https://github.com/Expensify/App/issues/50912
const hasForeignCurrency = SUPPORTED_FOREIGN_CURRENCIES.includes(policyCurrency) && isDevelopment;
// TODO remove isDevelopment and isDebugModeEnabled flags once nonUSD flow is complete, this will be updated in - https://github.com/Expensify/App/issues/50912
const hasForeignCurrency = SUPPORTED_FOREIGN_CURRENCIES.includes(policyCurrency) && (isDevelopment || isDebugModeEnabled);

if (userHasPhonePrimaryEmail) {
errorText = translate('bankAccount.hasPhoneLoginError');
Expand Down
18 changes: 18 additions & 0 deletions src/pages/ReimbursementAccount/utils/mapCurrencyToCountry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import CONST from '@src/CONST';

function mapCurrencyToCountry(currency: string): string {
switch (currency) {
case CONST.CURRENCY.USD:
return CONST.COUNTRY.US;
case CONST.CURRENCY.AUD:
return CONST.COUNTRY.AU;
case CONST.CURRENCY.CAD:
return CONST.COUNTRY.CA;
case CONST.CURRENCY.GBP:
return CONST.COUNTRY.GB;
default:
return '';
}
}

export default mapCurrencyToCountry;
7 changes: 7 additions & 0 deletions src/pages/workspace/WorkspaceProfileCurrencyPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,26 @@ import ScreenWrapper from '@components/ScreenWrapper';
import useLocalize from '@hooks/useLocalize';
import Navigation from '@libs/Navigation/Navigation';
import * as PolicyUtils from '@libs/PolicyUtils';
import mapCurrencyToCountry from '@pages/ReimbursementAccount/utils/mapCurrencyToCountry';
import * as FormActions from '@userActions/FormActions';
import * as Policy from '@userActions/Policy/Policy';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import INPUT_IDS from '@src/types/form/ReimbursementAccountForm';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
import AccessOrNotFoundWrapper from './AccessOrNotFoundWrapper';
import type {WithPolicyAndFullscreenLoadingProps} from './withPolicyAndFullscreenLoading';
import withPolicyAndFullscreenLoading from './withPolicyAndFullscreenLoading';

type WorkspaceProfileCurrencyPageProps = WithPolicyAndFullscreenLoadingProps;

const {COUNTRY} = INPUT_IDS.ADDITIONAL_DATA;

function WorkspaceProfileCurrencyPage({policy}: WorkspaceProfileCurrencyPageProps) {
const {translate} = useLocalize();

const onSelectCurrency = (item: CurrencyListItem) => {
FormActions.setDraftValues(ONYXKEYS.FORMS.REIMBURSEMENT_ACCOUNT_FORM, {[COUNTRY]: mapCurrencyToCountry(item.currencyCode)});
Policy.updateGeneralSettings(policy?.id ?? '-1', policy?.name ?? '', item.currencyCode);
Navigation.setNavigationActionToMicrotaskQueue(Navigation.goBack);
};
Expand Down
22 changes: 21 additions & 1 deletion src/types/form/ReimbursementAccountForm.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type {Country} from '@src/CONST';
import type DeepValueOf from '@src/types/utils/DeepValueOf';
import type Form from './Form';

Expand Down Expand Up @@ -50,6 +51,9 @@ const INPUT_IDS = {
AMOUNT1: 'amount1',
AMOUNT2: 'amount2',
AMOUNT3: 'amount3',
ADDITIONAL_DATA: {
COUNTRY: 'country',
},
} as const;

type InputID = DeepValueOf<typeof INPUT_IDS>;
Expand Down Expand Up @@ -121,8 +125,23 @@ type ReimbursementAccountProps = {
[INPUT_IDS.AMOUNT3]: string;
};

/** Additional props for non-USD reimbursement account */
type NonUSDReimbursementAccountAdditionalProps = {
/** Country of the reimbursement account */
[INPUT_IDS.ADDITIONAL_DATA.COUNTRY]: Country | '';
};

type ReimbursementAccountForm = ReimbursementAccountFormExtraProps &
Form<InputID, BeneficialOwnersStepBaseProps & BankAccountStepProps & CompanyStepProps & RequestorStepProps & ACHContractStepProps & ReimbursementAccountProps>;
Form<
InputID,
BeneficialOwnersStepBaseProps &
BankAccountStepProps &
CompanyStepProps &
RequestorStepProps &
ACHContractStepProps &
ReimbursementAccountProps &
NonUSDReimbursementAccountAdditionalProps
>;

export type {
ReimbursementAccountForm,
Expand All @@ -133,6 +152,7 @@ export type {
BeneficialOwnersStepProps,
ACHContractStepProps,
ReimbursementAccountProps,
NonUSDReimbursementAccountAdditionalProps,
InputID,
};
export default INPUT_IDS;
Loading

0 comments on commit 7445ef2

Please sign in to comment.