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

[Simplified Collect][Taxes] Creating new taxes #37967

Merged
Merged
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
b7cdc1b
add initial new tax page
kosmydel Mar 7, 2024
b351a28
add API command
kosmydel Mar 7, 2024
0f5eaad
add AmountSelectorModal
kosmydel Feb 27, 2024
3c3fc37
add TextPicker
kosmydel Feb 27, 2024
fd5029c
fix new pickers
kosmydel Mar 7, 2024
9e492c2
use pendingActions and errors in tax rates
kosmydel Mar 8, 2024
da6573f
draft
kosmydel Mar 8, 2024
4538c2a
Merge branch 'wave8/WorkspaceTaxesPage' into wave8/CreatingNewTaxes
kosmydel Mar 8, 2024
01a70b9
fix validation
kosmydel Mar 8, 2024
05f3a09
add saving drafts
kosmydel Mar 8, 2024
c0ead40
fix displaying tax rate in the list
kosmydel Mar 8, 2024
34cef2a
change type names
kosmydel Mar 11, 2024
d9574c2
Merge branch 'wave8/WorkspaceTaxesPage' into wave8/CreatingNewTaxes
kosmydel Mar 11, 2024
71d90f4
add spanish translation drafts
kosmydel Mar 11, 2024
4be8838
fix translation
kosmydel Mar 11, 2024
ae25dc5
Merge branch 'wave8/WorkspaceTaxesPage' into wave8/CreatingNewTaxes
kosmydel Mar 12, 2024
b6b6fd4
add auto focus
kosmydel Mar 12, 2024
6903cb5
add brickRoadIndicator
kosmydel Mar 12, 2024
d461d15
fix parameters
kosmydel Mar 12, 2024
4728b24
Merge branch 'wave8/WorkspaceTaxesPage' into wave8/CreatingNewTaxes
kosmydel Mar 13, 2024
7ba715e
fix rhp mapping
kosmydel Mar 13, 2024
c8ce6bb
clear drafts
kosmydel Mar 13, 2024
31d41b9
cleanup
kosmydel Mar 13, 2024
3cf7374
lint
kosmydel Mar 13, 2024
2747599
Merge branch 'main' into wave8/CreatingNewTaxes
kosmydel Mar 14, 2024
5ebdcb1
extract validation
kosmydel Mar 14, 2024
46da5b0
fix types
kosmydel Mar 14, 2024
5d6fb5e
adress review
kosmydel Mar 15, 2024
438c973
Fix
kosmydel Mar 15, 2024
e94dde9
Merge branch 'main' into wave8/CreatingNewTaxes
kosmydel Mar 15, 2024
3e62eb3
prevent creating duplicate id
kosmydel Mar 15, 2024
efd2a9b
cleanup
kosmydel Mar 15, 2024
58e682b
polish
kosmydel Mar 15, 2024
a0a1049
address review
kosmydel Mar 15, 2024
145c10b
Merge branch 'main' into wave8/CreatingNewTaxes
kosmydel Mar 18, 2024
bda7696
address review
kosmydel Mar 18, 2024
9671e6c
fix types
kosmydel Mar 18, 2024
082eeed
rename WorkspaceCreateTaxPage
kosmydel Mar 18, 2024
dc900fc
rename: getNextTaxCode
kosmydel Mar 18, 2024
ca68505
rename
kosmydel Mar 18, 2024
186a79b
rename TAX_CREATE screen
kosmydel Mar 18, 2024
79977f2
Update src/SCREENS.ts
kosmydel Mar 18, 2024
4b764ea
address review
kosmydel Mar 18, 2024
dd79adb
fix comments
kosmydel Mar 18, 2024
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/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4031,6 +4031,10 @@ const CONST = {
SESSION_STORAGE_KEYS: {
INITIAL_URL: 'INITIAL_URL',
},

TAX_RATES: {
NAME_MAX_LENGTH: 50,
kosmydel marked this conversation as resolved.
Show resolved Hide resolved
},
} 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 @@ -409,6 +409,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',
luacmartins marked this conversation as resolved.
Show resolved Hide resolved
},
} as const;

Expand Down Expand Up @@ -456,6 +458,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;
};

type OnyxFormDraftValuesMapping = {
Expand Down
8 changes: 6 additions & 2 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -601,9 +601,13 @@ 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_TAXES_NEW: {
mountiny marked this conversation as resolved.
Show resolved Hide resolved
route: 'settings/workspaces/:policyID/taxes/new',
getRoute: (policyID: string) => `settings/workspaces/${policyID}/taxes/new` as const,
},
WORKSPACE_DISTANCE_RATES: {
route: 'workspace/:policyID/distance-rates',
getRoute: (policyID: string) => `workspace/${policyID}/distance-rates` as const,
route: 'settings/workspaces/:policyID/distance-rates',
kosmydel marked this conversation as resolved.
Show resolved Hide resolved
getRoute: (policyID: string) => `settings/workspaces/${policyID}/distance-rates` as const,
},
// Referral program promotion
REFERRAL_DETAILS_MODAL: {
Expand Down
1 change: 1 addition & 0 deletions src/SCREENS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ const SCREENS = {
TAGS_SETTINGS: 'Tags_Settings',
TAGS_EDIT: 'Tags_Edit',
TAXES: 'Workspace_Taxes',
TAXES_NEW: 'Workspace_Taxes_New',
TAG_CREATE: 'Tag_Create',
CURRENCY: 'Workspace_Profile_Currency',
WORKFLOWS: 'Workspace_Workflows',
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};
63 changes: 63 additions & 0 deletions src/components/AmountPicker/AmountSelectorModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
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}
>
<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);
51 changes: 51 additions & 0 deletions src/components/AmountPicker/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import type {AmountFormProps} from '@components/AmountForm';
import type {MenuItemBaseProps} from '@components/MenuItem';
import type {ListItem} from '@components/SelectionList/types';
import type {MaybePhraseKey} from '@libs/Localize';

type AmountPickerListItem = ListItem & {
kosmydel marked this conversation as resolved.
Show resolved Hide resolved
value?: string;
};

type AmountPickerItem = {
kosmydel marked this conversation as resolved.
Show resolved Hide resolved
label?: string;
value?: string;
description?: string;
};

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 toolip text */
kosmydel marked this conversation as resolved.
Show resolved Hide resolved
shouldShowTooltips?: boolean;
} & Pick<MenuItemBaseProps, 'rightLabel' | 'description'> &
AmountFormProps;

export type {AmountPickerItem, AmountSelectorModalProps, AmountPickerProps, AmountPickerListItem};
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
2 changes: 2 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 Down Expand Up @@ -43,6 +44,7 @@ type ValidInputs =
| typeof ValuePicker
| typeof DatePicker
| typeof RadioButtons
| typeof AmountPicker
kosmydel marked this conversation as resolved.
Show resolved Hide resolved
| typeof AddPlaidBankAccount;

type ValueTypeKey = 'string' | 'boolean' | 'date' | 'country';
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 Down Expand Up @@ -58,22 +61,26 @@ function BaseTextInputWithCurrencySymbol(
}}
onKeyPress={onKeyPress}
style={styles.pr1}
// eslint-disable-next-line react/jsx-props-no-spreading
{...rest}
/>
);

if (isCurrencySymbolLTR) {
return (
<>
{currencySymbolButton}
{!hideCurrencySymbol && currencySymbolButton}
kosmydel marked this conversation as resolved.
Show resolved Hide resolved
{amountTextInput}
{extraSymbol}
</>
);
}

return (
<>
{amountTextInput}
{currencySymbolButton}
{!hideCurrencySymbol && currencySymbolButton}
kosmydel marked this conversation as resolved.
Show resolved Hide resolved
{extraSymbol}
</>
);
}
Expand Down
9 changes: 8 additions & 1 deletion src/components/TextInputWithCurrencySymbol/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type {NativeSyntheticEvent, TextInputSelectionChangeEventData} from 'react-native';
import type {TextSelection} from '@components/Composer/types';
import type {BaseTextInputProps} from '@components/TextInput/BaseTextInput/types';

type TextInputWithCurrencySymbolProps = {
/** Formatted amount in local currency */
Expand Down Expand Up @@ -28,6 +29,12 @@ type TextInputWithCurrencySymbolProps = {

/** Whether the currency symbol is pressable */
isCurrencyPressable: boolean;
};

/** Whether to hide the currency symbol */
hideCurrencySymbol?: boolean;

/** Extra symbol to display next to the currency symbol */
kosmydel marked this conversation as resolved.
Show resolved Hide resolved
extraSymbol?: React.ReactNode;
} & Pick<BaseTextInputProps, 'autoFocus'>;

export default TextInputWithCurrencySymbolProps;
Loading
Loading