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

[No QA] 1-6 Card Flow UI #44741

Merged
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
4216e95
feat: add selection list to assignee step
koko57 Jun 26, 2024
93de31b
feat: UI for card name step
koko57 Jun 26, 2024
c27e5ef
feat: UI for limit step
koko57 Jun 26, 2024
c3ea4e3
feat: limit type step UI
koko57 Jun 26, 2024
a38ad57
feat: card type step UI
koko57 Jun 26, 2024
a6ef082
feat: confirmation step UI, card type step improvements
koko57 Jun 27, 2024
966e1e9
feat: apply the proper routing for expensify card pages
koko57 Jun 27, 2024
0f6898c
Merge branch 'main' into feature/44309-issue-new-card-flow-steps
koko57 Jul 1, 2024
899a78a
feat: [wip] assignee list
koko57 Jul 1, 2024
169a0d3
feat: implement search
koko57 Jul 1, 2024
98dc915
fix: resolve conflicts
koko57 Jul 2, 2024
ece0ee0
fix: remove duplicates
koko57 Jul 2, 2024
7d74daa
feat: connect all the steps
koko57 Jul 2, 2024
f97a457
feat: flow improvements
koko57 Jul 2, 2024
992d2fb
fix: resolve conflicts
koko57 Jul 3, 2024
79f6d02
fix: minor improvements
koko57 Jul 3, 2024
7e621e1
fix: show saved name and limit as default values when editing
koko57 Jul 3, 2024
6623cc7
fix: minor fix
koko57 Jul 3, 2024
de38131
fix: wrap confirmation step items in scrollview
koko57 Jul 3, 2024
1b5302e
fix: add autofocus, fix confirmation step button styles
koko57 Jul 3, 2024
804ed05
fix: remove unnecessary line
koko57 Jul 3, 2024
0946e49
fix: fix styles for limit step form
koko57 Jul 4, 2024
bbbb91e
fix: enable going to the next step when offline
koko57 Jul 4, 2024
e0094ab
fix: resolve conflicts
koko57 Jul 4, 2024
6f12b11
fix: change icon
koko57 Jul 4, 2024
f60d52e
fix: remove permission
koko57 Jul 4, 2024
aa72999
fix: filter deleted employees, apply requested changes
koko57 Jul 4, 2024
b303307
fix: autofocus, code improvements
koko57 Jul 4, 2024
8e95e15
refactor: move isDeletedPolicyEmployee to PolicyUtils
koko57 Jul 4, 2024
768641f
fix: resolve conflicts
koko57 Jul 4, 2024
b58b364
feat: code improvements
koko57 Jul 4, 2024
8d81d1d
feat: add validation to limit step
koko57 Jul 4, 2024
1023fd6
fix: lint
koko57 Jul 4, 2024
255d899
Merge branch 'main' into feature/44309-issue-new-card-flow-steps
koko57 Jul 7, 2024
947e3c0
Merge branch 'main' into feature/44309-issue-new-card-flow-steps
koko57 Jul 8, 2024
bb7b3dc
feat: implement editing flow
koko57 Jul 8, 2024
4e3d445
fix: apply requested changes
koko57 Jul 8, 2024
5fe7e81
fix: minor fix
koko57 Jul 8, 2024
8624227
fix: minor fix
koko57 Jul 8, 2024
2c3a8c1
fix: minor fix
koko57 Jul 8, 2024
c514207
fix: resolve conflicts
koko57 Jul 9, 2024
b689e39
fix: add a whitespace
koko57 Jul 9, 2024
12b2fa7
fix: remove unnecessary line
koko57 Jul 9, 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
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2070,6 +2070,10 @@ const CONST = {
CARD_NAME: 'CardName',
CONFIRMATION: 'Confirmation',
},
CARD_TYPE: {
PHYSICAL: 'physical',
VIRTUAL: 'virtual',
},
},
AVATAR_ROW_SIZE: {
DEFAULT: 4,
Expand Down
4 changes: 2 additions & 2 deletions src/ONYXKEYS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -548,8 +548,8 @@ const ONYXKEYS = {
NEW_CHAT_NAME_FORM_DRAFT: 'newChatNameFormDraft',
SUBSCRIPTION_SIZE_FORM: 'subscriptionSizeForm',
SUBSCRIPTION_SIZE_FORM_DRAFT: 'subscriptionSizeFormDraft',
ISSUE_NEW_EXPENSIFY_CARD_FORM: 'issueNewExpensifyCardForm',
ISSUE_NEW_EXPENSIFY_CARD_FORM_DRAFT: 'issueNewExpensifyCardFormDraft',
ISSUE_NEW_EXPENSIFY_CARD_FORM: 'issueNewExpensifyCard',
ISSUE_NEW_EXPENSIFY_CARD_FORM_DRAFT: 'issueNewExpensifyCardDraft',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NAB: ISSUE_NEW_EXPENSIFY_CARD_FORM_DRAFT isn't used in anywhere

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's how it's implemented for the other forms - TS would complain if I removed it 😅

SAGE_INTACCT_CREDENTIALS_FORM: 'sageIntacctCredentialsForm',
SAGE_INTACCT_CREDENTIALS_FORM_DRAFT: 'sageIntacctCredentialsFormDraft',
},
Expand Down
11 changes: 4 additions & 7 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -787,13 +787,10 @@ const ROUTES = {
route: 'settings/workspaces/:policyID/expensify-card',
getRoute: (policyID: string) => `settings/workspaces/${policyID}/expensify-card` as const,
},
// TODO: uncomment after development is done
// WORKSPACE_EXPENSIFY_CARD_ISSUE_NEW: {
// route: 'settings/workspaces/:policyID/expensify-card/issues-new',
// getRoute: (policyID: string) => `settings/workspaces/${policyID}/expensify-card/issue-new` as const,
// },
// TODO: remove after development is done - this one is for testing purposes
WORKSPACE_EXPENSIFY_CARD_ISSUE_NEW: 'settings/workspaces/expensify-card/issue-new',
WORKSPACE_EXPENSIFY_CARD_ISSUE_NEW: {
route: 'settings/workspaces/:policyID/expensify-card/issue-new',
getRoute: (policyID: string) => `settings/workspaces/${policyID}/expensify-card/issue-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/components/FocusTrap/WIDE_LAYOUT_INACTIVE_SCREENS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const WIDE_LAYOUT_INACTIVE_SCREENS: string[] = [
SCREENS.WORKSPACE.REPORT_FIELDS,
SCREENS.WORKSPACE.EXPENSIFY_CARD,
SCREENS.WORKSPACE.DISTANCE_RATES,
SCREENS.WORKSPACE.EXPENSIFY_CARD,
koko57 marked this conversation as resolved.
Show resolved Hide resolved
SCREENS.SEARCH.CENTRAL_PANE,
SCREENS.SETTINGS.TROUBLESHOOT,
SCREENS.SETTINGS.SAVE_THE_WORLD,
Expand Down
2 changes: 2 additions & 0 deletions src/components/Icon/Illustrations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ import ThumbsUpStars from '@assets/images/simple-illustrations/simple-illustrati
import TrackShoe from '@assets/images/simple-illustrations/simple-illustration__track-shoe.svg';
import TrashCan from '@assets/images/simple-illustrations/simple-illustration__trashcan.svg';
import TreasureChest from '@assets/images/simple-illustrations/simple-illustration__treasurechest.svg';
import VirtualCard from '@assets/images/simple-illustrations/simple-illustration__virtualcard.svg';
import WalletAlt from '@assets/images/simple-illustrations/simple-illustration__wallet-alt.svg';
import Workflows from '@assets/images/simple-illustrations/simple-illustration__workflows.svg';
import ExpensifyApprovedLogoLight from '@assets/images/subscription-details__approvedlogo--light.svg';
Expand Down Expand Up @@ -194,4 +195,5 @@ export {
CheckmarkCircle,
CreditCardEyes,
LockClosedOrange,
VirtualCard,
};
4 changes: 2 additions & 2 deletions src/libs/CurrencyUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,11 +96,11 @@ function convertToFrontendAmountAsInteger(amountAsInt: number): number {
*
* @note we do not support any currencies with more than two decimal places.
*/
function convertToFrontendAmountAsString(amountAsInt: number | null | undefined): string {
function convertToFrontendAmountAsString(amountAsInt: number | null | undefined, withDecimals = true): string {
if (amountAsInt === null || amountAsInt === undefined) {
return '';
}
return convertToFrontendAmountAsInteger(amountAsInt).toFixed(2);
return convertToFrontendAmountAsInteger(amountAsInt).toFixed(withDecimals ? 2 : 0);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are implementing a PR to make this change. In that PR, the decimal relies on the currency and I think it will make more sense

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so we will be able to get an amount without decimals then?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure what you mean.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this PR, we will display the decimal relies on the current currency. Then do we need to apply your change here any more?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, because we don't want to display any decimals here - it's for the limit step. I guess that after the changes from this PR you mentioned we will have 2 decimals for USD and for other currencies using 2 decimals.

}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ const FULL_SCREEN_TO_RHP_MAPPING: Partial<Record<FullScreenName, string[]>> = {
SCREENS.WORKSPACE.DISTANCE_RATE_DETAILS,
],
[SCREENS.WORKSPACE.REPORT_FIELDS]: [],
[SCREENS.WORKSPACE.EXPENSIFY_CARD]: [],
[SCREENS.WORKSPACE.EXPENSIFY_CARD]: [SCREENS.WORKSPACE.EXPENSIFY_CARD_ISSUE_NEW],
};

export default FULL_SCREEN_TO_RHP_MAPPING;
2 changes: 1 addition & 1 deletion src/libs/Navigation/linkingConfig/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,7 @@ const config: LinkingOptions<RootStackParamList>['config'] = {
path: ROUTES.WORKSPACE_PROFILE_SHARE.route,
},
[SCREENS.WORKSPACE.EXPENSIFY_CARD_ISSUE_NEW]: {
path: ROUTES.WORKSPACE_EXPENSIFY_CARD_ISSUE_NEW,
path: ROUTES.WORKSPACE_EXPENSIFY_CARD_ISSUE_NEW.route,
},
[SCREENS.WORKSPACE.RATE_AND_UNIT]: {
path: ROUTES.WORKSPACE_RATE_AND_UNIT.route,
Expand Down
7 changes: 6 additions & 1 deletion src/libs/Navigation/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,9 @@ type SettingsNavigatorParamList = {
policyID: string;
taxID: string;
};
[SCREENS.WORKSPACE.EXPENSIFY_CARD_ISSUE_NEW]: {
policyID: string;
};
} & ReimbursementAccountNavigatorParamList;

type NewChatNavigatorParamList = {
Expand Down Expand Up @@ -908,7 +911,6 @@ type FullScreenNavigatorParamList = {
[SCREENS.WORKSPACE.DISTANCE_RATES]: {
policyID: string;
};

[SCREENS.WORKSPACE.ACCOUNTING.ROOT]: {
policyID: string;
};
Expand All @@ -921,6 +923,9 @@ type FullScreenNavigatorParamList = {
[SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_ONLINE_INVOICE_ACCOUNT_SELECTOR]: {
policyID: string;
};
[SCREENS.WORKSPACE.EXPENSIFY_CARD]: {
policyID: string;
};
};

type OnboardingModalNavigatorParamList = {
Expand Down
24 changes: 22 additions & 2 deletions src/libs/actions/Card.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type {ActivatePhysicalExpensifyCardParams, ReportVirtualExpensifyCardFrau
import {SIDE_EFFECT_REQUEST_COMMANDS, WRITE_COMMANDS} from '@libs/API/types';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {ExpensifyCardDetails, IssueNewCardStep} from '@src/types/onyx/Card';
import type {ExpensifyCardDetails, IssueNewCardData, IssueNewCardStep} from '@src/types/onyx/Card';

type ReplacementReason = 'damaged' | 'stolen';

Expand Down Expand Up @@ -189,5 +189,25 @@ function setIssueNewCardStep(step: IssueNewCardStep | null) {
Onyx.merge(ONYXKEYS.ISSUE_NEW_EXPENSIFY_CARD, {currentStep: step});
}

export {requestReplacementExpensifyCard, activatePhysicalExpensifyCard, clearCardListErrors, reportVirtualExpensifyCardFraud, revealVirtualCardDetails, setIssueNewCardStep};
function setIssueNewCardDataAndGoToStep(data: Partial<IssueNewCardData>, step: IssueNewCardStep) {
koko57 marked this conversation as resolved.
Show resolved Hide resolved
Onyx.merge(ONYXKEYS.ISSUE_NEW_EXPENSIFY_CARD, {data, currentStep: step});
}

function clearIssueNewCardFlow() {
Onyx.set(ONYXKEYS.ISSUE_NEW_EXPENSIFY_CARD, {
currentStep: null,
data: {},
});
}

export {
requestReplacementExpensifyCard,
activatePhysicalExpensifyCard,
clearCardListErrors,
reportVirtualExpensifyCardFraud,
revealVirtualCardDetails,
setIssueNewCardStep,
setIssueNewCardDataAndGoToStep,
clearIssueNewCardFlow,
};
export type {ReplacementReason};
92 changes: 76 additions & 16 deletions src/pages/workspace/card/issueNew/AssigneeStep.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,91 @@
import React from 'react';
import React, {useMemo, useState} from 'react';
import {View} from 'react-native';
import FormProvider from '@components/Form/FormProvider';
import type {OnyxEntry} from 'react-native-onyx';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import * as Expensicons from '@components/Icon/Expensicons';
import InteractiveStepSubHeader from '@components/InteractiveStepSubHeader';
import ScreenWrapper from '@components/ScreenWrapper';
import SelectionList from '@components/SelectionList';
import type {ListItem} from '@components/SelectionList/types';
import UserListItem from '@components/SelectionList/UserListItem';
import Text from '@components/Text';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import {formatPhoneNumber} from '@libs/LocalePhoneNumber';
import * as OptionsListUtils from '@libs/OptionsListUtils';
import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils';
import Navigation from '@navigation/Navigation';
import * as Card from '@userActions/Card';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type * as OnyxTypes from '@src/types/onyx';

function AssigneeStep() {
type AssigneeStepProps = {
// The policy that the card will be issued under
policy: OnyxEntry<OnyxTypes.Policy>;
};

function AssigneeStep({policy}: AssigneeStepProps) {
const {translate} = useLocalize();
const styles = useThemeStyles();
const [searchTerm, setSearchTerm] = useState('');

const submit = () => {
// TODO: the logic will be created in https://github.com/Expensify/App/issues/44309
Card.setIssueNewCardStep(CONST.EXPENSIFY_CARD.STEP.CARD_TYPE);
const submit = (assignee: ListItem) => {
Card.setIssueNewCardDataAndGoToStep({assigneeEmail: assignee?.login ?? ''}, CONST.EXPENSIFY_CARD.STEP.CARD_TYPE);
};

const handleBackButtonPress = () => {
Navigation.goBack();
};

const shouldShowSearchInput = policy?.employeeList && Object.keys(policy.employeeList).length > 8;
const textInputLabel = shouldShowSearchInput ? translate('workspace.card.issueNewCard.findMember') : undefined;
koko57 marked this conversation as resolved.
Show resolved Hide resolved

const membersDetails = useMemo(() => {
if (!policy?.employeeList) {
mountiny marked this conversation as resolved.
Show resolved Hide resolved
return [];
}

return Object.keys(policy.employeeList)
.map(PersonalDetailsUtils.getPersonalDetailByEmail)
.map((detail) => ({
keyForList: detail?.login,
text: detail?.displayName,
alternateText: detail?.login,
login: detail?.login,
accountID: detail?.accountID,
icons: [
{
source: detail?.avatar ?? Expensicons.FallbackAvatar,
name: formatPhoneNumber(detail?.login ?? ''),
type: CONST.ICON_TYPE_AVATAR,
id: detail?.accountID,
},
],
}));
}, [policy?.employeeList]);

const sections = useMemo(() => {
if (!searchTerm) {
return [
{
data: membersDetails,
shouldShow: true,
},
];
}

const searchValue = OptionsListUtils.getSearchValueForPhoneOrEmail(searchTerm).toLowerCase();
const filteredOptions = membersDetails.filter((option) => !!option.text?.toLowerCase().includes(searchValue) || !!option.alternateText?.toLowerCase().includes(searchValue));

mountiny marked this conversation as resolved.
Show resolved Hide resolved
return [
{
title: undefined,
data: filteredOptions,
shouldShow: true,
},
];
}, [membersDetails, searchTerm]);

return (
<ScreenWrapper
testID={AssigneeStep.displayName}
Expand All @@ -43,15 +104,14 @@ function AssigneeStep() {
/>
</View>
<Text style={[styles.textHeadlineLineHeightXXL, styles.ph5, styles.mv3]}>{translate('workspace.card.issueNewCard.whoNeedsCard')}</Text>
<FormProvider
formID={ONYXKEYS.FORMS.ISSUE_NEW_EXPENSIFY_CARD_FORM}
submitButtonText={translate('common.next')}
onSubmit={submit}
style={[styles.mh5, styles.flexGrow1]}
>
{/* TODO: the content will be created in https://github.com/Expensify/App/issues/44309 */}
<View />
</FormProvider>
<SelectionList
textInputLabel={textInputLabel}
textInputValue={searchTerm}
onChangeText={setSearchTerm}
sections={sections}
ListItem={UserListItem}
onSelectRow={submit}
/>
</ScreenWrapper>
);
}
Expand Down
46 changes: 37 additions & 9 deletions src/pages/workspace/card/issueNew/CardNameStep.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,45 @@
import React from 'react';
import React, {useCallback} from 'react';
import {View} from 'react-native';
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 HeaderWithBackButton from '@components/HeaderWithBackButton';
import InteractiveStepSubHeader from '@components/InteractiveStepSubHeader';
import ScreenWrapper from '@components/ScreenWrapper';
import Text from '@components/Text';
import TextInput from '@components/TextInput';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import * as ValidationUtils from '@libs/ValidationUtils';
import * as Card from '@userActions/Card';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import INPUT_IDS from '@src/types/form/IssueNewExpensifyCardForm';

function CardNameStep() {
const {translate} = useLocalize();
const styles = useThemeStyles();
const [issueNewCard] = useOnyx(ONYXKEYS.ISSUE_NEW_EXPENSIFY_CARD);

const submit = () => {
// TODO: the logic will be created in https://github.com/Expensify/App/issues/44309
Card.setIssueNewCardStep(CONST.EXPENSIFY_CARD.STEP.CONFIRMATION);
};
const validate = useCallback(
(values: FormOnyxValues<typeof ONYXKEYS.FORMS.ISSUE_NEW_EXPENSIFY_CARD_FORM>): FormInputErrors<typeof ONYXKEYS.FORMS.ISSUE_NEW_EXPENSIFY_CARD_FORM> => {
const errors = ValidationUtils.getFieldRequiredErrors(values, [INPUT_IDS.CARD_TITLE]);
if (!values.cardTitle) {
errors.cardTitle = translate('common.error.fieldRequired');
}
return errors;
},
[translate],
);

const submit = useCallback((values: FormOnyxValues<typeof ONYXKEYS.FORMS.ISSUE_NEW_EXPENSIFY_CARD_FORM>) => {
Card.setIssueNewCardDataAndGoToStep({cardTitle: values.cardTitle}, CONST.EXPENSIFY_CARD.STEP.CONFIRMATION);
}, []);

const handleBackButtonPress = () => {
const handleBackButtonPress = useCallback(() => {
Card.setIssueNewCardStep(CONST.EXPENSIFY_CARD.STEP.LIMIT);
};
}, []);

return (
<ScreenWrapper
Expand All @@ -44,12 +61,23 @@ function CardNameStep() {
<Text style={[styles.textHeadlineLineHeightXXL, styles.ph5, styles.mv3]}>{translate('workspace.card.issueNewCard.giveItName')}</Text>
<FormProvider
formID={ONYXKEYS.FORMS.ISSUE_NEW_EXPENSIFY_CARD_FORM}
// TODO: change the submitButtonText to 'common.confirm' when editing and navigate to ConfirmationStep
submitButtonText={translate('common.next')}
onSubmit={submit}
validate={validate}
style={[styles.mh5, styles.flexGrow1]}
>
{/* TODO: the content will be created in https://github.com/Expensify/App/issues/44309 */}
<View />
<InputWrapper
InputComponent={TextInput}
inputID={INPUT_IDS.CARD_TITLE}
label={translate('workspace.card.issueNewCard.cardName')}
hint={translate('workspace.card.issueNewCard.giveItNameInstruction')}
aria-label={translate('workspace.card.issueNewCard.cardName')}
role={CONST.ROLE.PRESENTATION}
// TODO: default value for card name
defaultValue={issueNewCard?.data?.cardTitle}
containerStyles={[styles.mb6]}
/>
</FormProvider>
</ScreenWrapper>
);
Expand Down
Loading
Loading