Skip to content

Commit

Permalink
Merge pull request #37967 from software-mansion-labs/wave8/CreatingNe…
Browse files Browse the repository at this point in the history
…wTaxes

[Simplified Collect][Taxes] Creating new taxes
  • Loading branch information
luacmartins authored Mar 18, 2024
2 parents e737a63 + dd79adb commit 623d084
Show file tree
Hide file tree
Showing 41 changed files with 746 additions and 26 deletions.
4 changes: 4 additions & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4065,6 +4065,10 @@ const CONST = {
SESSION_STORAGE_KEYS: {
INITIAL_URL: 'INITIAL_URL',
},

TAX_RATES: {
NAME_MAX_LENGTH: 50,
},
} as const;

type Country = keyof typeof CONST.ALL_COUNTRIES;
Expand Down
3 changes: 3 additions & 0 deletions src/ONYXKEYS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,8 @@ const ONYXKEYS = {
EXIT_SURVEY_RESPONSE_FORM_DRAFT: 'exitSurveyResponseFormDraft',
POLICY_TAG_NAME_FORM: 'policyTagNameForm',
POLICY_TAG_NAME_FORM_DRAFT: 'policyTagNameFormDraft',
WORKSPACE_NEW_TAX_FORM: 'workspaceNewTaxForm',
WORKSPACE_NEW_TAX_FORM_DRAFT: 'workspaceNewTaxFormDraft',
},
} as const;

Expand Down Expand Up @@ -458,6 +460,7 @@ type OnyxFormValuesMapping = {
[ONYXKEYS.FORMS.PERSONAL_BANK_ACCOUNT]: FormTypes.PersonalBankAccountForm;
[ONYXKEYS.FORMS.WORKSPACE_DESCRIPTION_FORM]: FormTypes.WorkspaceDescriptionForm;
[ONYXKEYS.FORMS.POLICY_TAG_NAME_FORM]: FormTypes.PolicyTagNameForm;
[ONYXKEYS.FORMS.WORKSPACE_NEW_TAX_FORM]: FormTypes.WorkspaceNewTaxForm;
[ONYXKEYS.FORMS.POLICY_CREATE_DISTANCE_RATE_FORM]: FormTypes.PolicyCreateDistanceRateForm;
};

Expand Down
4 changes: 4 additions & 0 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,10 @@ const ROUTES = {
route: 'settings/workspaces/:policyID/members/:accountID/role-selection',
getRoute: (policyID: string, accountID: number, backTo?: string) => getUrlWithBackToParam(`settings/workspaces/${policyID}/members/${accountID}/role-selection`, backTo),
},
WORKSPACE_TAX_CREATE: {
route: 'settings/workspaces/:policyID/taxes/new',
getRoute: (policyID: string) => `settings/workspaces/${policyID}/taxes/new` as const,
},
WORKSPACE_DISTANCE_RATES: {
route: 'settings/workspaces/:policyID/distance-rates',
getRoute: (policyID: string) => `settings/workspaces/${policyID}/distance-rates` as const,
Expand Down
1 change: 1 addition & 0 deletions src/SCREENS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ const SCREENS = {
TAGS_SETTINGS: 'Tags_Settings',
TAGS_EDIT: 'Tags_Edit',
TAXES: 'Workspace_Taxes',
TAX_CREATE: 'Workspace_Tax_Create',
TAG_CREATE: 'Tag_Create',
TAG_SETTINGS: 'Tag_Settings',
CURRENCY: 'Workspace_Profile_Currency',
Expand Down
11 changes: 8 additions & 3 deletions src/components/AmountForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ import * as MoneyRequestUtils from '@libs/MoneyRequestUtils';
import CONST from '@src/CONST';
import BigNumberPad from './BigNumberPad';
import FormHelpMessage from './FormHelpMessage';
import type {BaseTextInputRef} from './TextInput/BaseTextInput/types';
import type {BaseTextInputProps, BaseTextInputRef} from './TextInput/BaseTextInput/types';
import TextInputWithCurrencySymbol from './TextInputWithCurrencySymbol';
import type TextInputWithCurrencySymbolProps from './TextInputWithCurrencySymbol/types';

type AmountFormProps = {
/** Amount supplied by the FormProvider */
Expand All @@ -36,7 +37,8 @@ type AmountFormProps = {

/** Whether the currency symbol is pressable */
isCurrencyPressable?: boolean;
};
} & Pick<TextInputWithCurrencySymbolProps, 'hideCurrencySymbol' | 'extraSymbol'> &
Pick<BaseTextInputProps, 'autoFocus'>;

/**
* Returns the new selection object based on the updated amount's length
Expand All @@ -51,7 +53,7 @@ const NUM_PAD_CONTAINER_VIEW_ID = 'numPadContainerView';
const NUM_PAD_VIEW_ID = 'numPadView';

function AmountForm(
{value: amount, currency = CONST.CURRENCY.USD, extraDecimals = 0, errorText, onInputChange, onCurrencyButtonPress, isCurrencyPressable = true}: AmountFormProps,
{value: amount, currency = CONST.CURRENCY.USD, extraDecimals = 0, errorText, onInputChange, onCurrencyButtonPress, isCurrencyPressable = true, ...rest}: AmountFormProps,
forwardedRef: ForwardedRef<BaseTextInputRef>,
) {
const styles = useThemeStyles();
Expand Down Expand Up @@ -214,6 +216,8 @@ function AmountForm(
}}
onKeyPress={textInputKeyPress}
isCurrencyPressable={isCurrencyPressable}
// eslint-disable-next-line react/jsx-props-no-spreading
{...rest}
/>
{!!errorText && (
<FormHelpMessage
Expand Down Expand Up @@ -243,3 +247,4 @@ function AmountForm(
AmountForm.displayName = 'AmountForm';

export default forwardRef(AmountForm);
export type {AmountFormProps};
64 changes: 64 additions & 0 deletions src/components/AmountPicker/AmountSelectorModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import React, {useState} from 'react';
import {View} from 'react-native';
import AmountForm from '@components/AmountForm';
import Button from '@components/Button';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import Modal from '@components/Modal';
import ScreenWrapper from '@components/ScreenWrapper';
import ScrollView from '@components/ScrollView';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import CONST from '@src/CONST';
import type {AmountSelectorModalProps} from './types';

function AmountSelectorModal({value, description = '', onValueSelected, isVisible, onClose, ...rest}: AmountSelectorModalProps) {
const {translate} = useLocalize();
const styles = useThemeStyles();

const [currentValue, setValue] = useState(value);

return (
<Modal
type={CONST.MODAL.MODAL_TYPE.RIGHT_DOCKED}
isVisible={isVisible}
onClose={onClose}
onModalHide={onClose}
hideModalContentWhileAnimating
useNativeDriver
>
<ScreenWrapper
includePaddingTop={false}
includeSafeAreaPaddingBottom={false}
testID={AmountSelectorModal.displayName}
shouldEnableMaxHeight
>
<HeaderWithBackButton
title={description}
onBackButtonPress={onClose}
/>
<ScrollView contentContainerStyle={[styles.flex1, styles.mh5, styles.mb5]}>
<View style={styles.flex1}>
<AmountForm
// eslint-disable-next-line react/jsx-props-no-spreading
{...rest}
autoFocus
value={currentValue}
onInputChange={setValue}
/>
</View>
<Button
success
large
pressOnEnter
text={translate('common.save')}
onPress={() => onValueSelected?.(currentValue ?? '')}
/>
</ScrollView>
</ScreenWrapper>
</Modal>
);
}

AmountSelectorModal.displayName = 'AmountSelectorModal';

export default AmountSelectorModal;
65 changes: 65 additions & 0 deletions src/components/AmountPicker/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import React, {forwardRef, useState} from 'react';
import type {ForwardedRef} from 'react';
import {View} from 'react-native';
import FormHelpMessage from '@components/FormHelpMessage';
import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription';
import useStyleUtils from '@hooks/useStyleUtils';
import useThemeStyles from '@hooks/useThemeStyles';
import variables from '@styles/variables';
import callOrReturn from '@src/types/utils/callOrReturn';
import AmountSelectorModal from './AmountSelectorModal';
import type {AmountPickerProps} from './types';

function AmountPicker({value, description, title, errorText = '', onInputChange, furtherDetails, rightLabel, ...rest}: AmountPickerProps, forwardedRef: ForwardedRef<View>) {
const styles = useThemeStyles();
const StyleUtils = useStyleUtils();
const [isPickerVisible, setIsPickerVisible] = useState(false);

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

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

const updateInput = (updatedValue: string) => {
if (updatedValue !== value) {
onInputChange?.(updatedValue);
}
hidePickerModal();
};

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

return (
<View>
<MenuItemWithTopDescription
ref={forwardedRef}
shouldShowRightIcon
title={callOrReturn(title, value)}
descriptionTextStyle={descStyle}
description={description}
onPress={showPickerModal}
furtherDetails={furtherDetails}
rightLabel={rightLabel}
/>
<View style={styles.ml5}>
<FormHelpMessage message={errorText} />
</View>
<AmountSelectorModal
// eslint-disable-next-line react/jsx-props-no-spreading
{...rest}
value={value}
isVisible={isPickerVisible}
description={description}
onClose={hidePickerModal}
onValueSelected={updateInput}
/>
</View>
);
}

AmountPicker.displayName = 'AmountPicker';

export default forwardRef(AmountPicker);
40 changes: 40 additions & 0 deletions src/components/AmountPicker/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import type {AmountFormProps} from '@components/AmountForm';
import type {MenuItemBaseProps} from '@components/MenuItem';
import type {MaybePhraseKey} from '@libs/Localize';

type AmountSelectorModalProps = {
/** Whether the modal is visible */
isVisible: boolean;

/** Current value */
value?: string;

/** Function to call when the user selects a item */
onValueSelected?: (value: string) => void;

/** Function to call when the user closes the modal */
onClose: () => void;
} & Pick<MenuItemBaseProps, 'description'>;

type AmountPickerProps = {
/** Item to display */
value?: string;

/** A placeholder value to display */
title?: string | ((value?: string) => string);

/** Form Error description */
errorText?: MaybePhraseKey;

/** Callback to call when the input changes */
onInputChange?: (value: string | undefined) => void;

/** Text to display under the main menu item */
furtherDetails?: string;

/** Whether to show the tooltip text */
shouldShowTooltips?: boolean;
} & Pick<MenuItemBaseProps, 'rightLabel' | 'description'> &
AmountFormProps;

export type {AmountSelectorModalProps, AmountPickerProps};
8 changes: 5 additions & 3 deletions src/components/AmountTextInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import useThemeStyles from '@hooks/useThemeStyles';
import CONST from '@src/CONST';
import type {TextSelection} from './Composer/types';
import TextInput from './TextInput';
import type {BaseTextInputRef} from './TextInput/BaseTextInput/types';
import type {BaseTextInputProps, BaseTextInputRef} from './TextInput/BaseTextInput/types';

type AmountTextInputProps = {
/** Formatted amount in local currency */
Expand All @@ -31,10 +31,10 @@ type AmountTextInputProps = {

/** Function to call to handle key presses in the text input */
onKeyPress?: (event: NativeSyntheticEvent<KeyboardEvent>) => void;
};
} & Pick<BaseTextInputProps, 'autoFocus'>;

function AmountTextInput(
{formattedAmount, onChangeAmount, placeholder, selection, onSelectionChange, style, touchableInputWrapperStyle, onKeyPress}: AmountTextInputProps,
{formattedAmount, onChangeAmount, placeholder, selection, onSelectionChange, style, touchableInputWrapperStyle, onKeyPress, ...rest}: AmountTextInputProps,
ref: ForwardedRef<BaseTextInputRef>,
) {
const styles = useThemeStyles();
Expand All @@ -57,6 +57,8 @@ function AmountTextInput(
role={CONST.ROLE.PRESENTATION}
onKeyPress={onKeyPress as (event: NativeSyntheticEvent<TextInputKeyPressEventData>) => void}
touchableInputWrapperStyle={touchableInputWrapperStyle}
// eslint-disable-next-line react/jsx-props-no-spreading
{...rest}
/>
);
}
Expand Down
4 changes: 4 additions & 0 deletions src/components/Form/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type {ValueOf} from 'type-fest';
import type AddPlaidBankAccount from '@components/AddPlaidBankAccount';
import type AddressSearch from '@components/AddressSearch';
import type AmountForm from '@components/AmountForm';
import type AmountPicker from '@components/AmountPicker';
import type AmountTextInput from '@components/AmountTextInput';
import type CheckboxWithLabel from '@components/CheckboxWithLabel';
import type CountrySelector from '@components/CountrySelector';
Expand All @@ -14,6 +15,7 @@ import type RoomNameInput from '@components/RoomNameInput';
import type SingleChoiceQuestion from '@components/SingleChoiceQuestion';
import type StatePicker from '@components/StatePicker';
import type TextInput from '@components/TextInput';
import type TextPicker from '@components/TextPicker';
import type ValuePicker from '@components/ValuePicker';
import type {MaybePhraseKey} from '@libs/Localize';
import type BusinessTypePicker from '@pages/ReimbursementAccount/BusinessInfo/substeps/TypeBusiness/BusinessTypePicker';
Expand Down Expand Up @@ -43,6 +45,8 @@ type ValidInputs =
| typeof ValuePicker
| typeof DatePicker
| typeof RadioButtons
| typeof AmountPicker
| typeof TextPicker
| typeof AddPlaidBankAccount;

type ValueTypeKey = 'string' | 'boolean' | 'date' | 'country';
Expand Down
2 changes: 1 addition & 1 deletion src/components/MultipleAvatars.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ type MultipleAvatarsProps = {
/** Whether avatars are displayed within a reportAction */
isInReportAction?: boolean;

/** Whether to show the toolip text */
/** Whether to show the tooltip text */
shouldShowTooltip?: boolean;

/** Whether avatars are displayed with the highlighted background color instead of the app background color. This is primarily the case for IOU previews. */
Expand Down
2 changes: 1 addition & 1 deletion src/components/SelectionList/selectionListPropTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ const propTypes = {
/** Custom content to display in the footer */
footerContent: PropTypes.oneOfType([PropTypes.func, PropTypes.node]),

/** Whether to show the toolip text */
/** Whether to show the tooltip text */
shouldShowTooltips: PropTypes.bool,

/** Whether to use dynamic maxToRenderPerBatch depending on the visible number of elements */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ function BaseTextInputWithCurrencySymbol(
onSelectionChange = () => {},
onKeyPress = () => {},
isCurrencyPressable = true,
hideCurrencySymbol = false,
extraSymbol,
...rest
}: TextInputWithCurrencySymbolProps,
ref: React.ForwardedRef<BaseTextInputRef>,
) {
Expand All @@ -28,7 +31,7 @@ function BaseTextInputWithCurrencySymbol(
const isCurrencySymbolLTR = CurrencyUtils.isCurrencySymbolLTR(selectedCurrencyCode);
const styles = useThemeStyles();

const currencySymbolButton = (
const currencySymbolButton = !hideCurrencySymbol && (
<CurrencySymbolButton
currencySymbol={currencySymbol ?? ''}
onCurrencyButtonPress={onCurrencyButtonPress}
Expand Down Expand Up @@ -58,6 +61,8 @@ function BaseTextInputWithCurrencySymbol(
}}
onKeyPress={onKeyPress}
style={styles.pr1}
// eslint-disable-next-line react/jsx-props-no-spreading
{...rest}
/>
);

Expand All @@ -66,6 +71,7 @@ function BaseTextInputWithCurrencySymbol(
<>
{currencySymbolButton}
{amountTextInput}
{extraSymbol}
</>
);
}
Expand All @@ -74,6 +80,7 @@ function BaseTextInputWithCurrencySymbol(
<>
{amountTextInput}
{currencySymbolButton}
{extraSymbol}
</>
);
}
Expand Down
Loading

0 comments on commit 623d084

Please sign in to comment.