diff --git a/assets/images/bill.svg b/assets/images/bill.svg
new file mode 100644
index 00000000000..b27e6776b0f
--- /dev/null
+++ b/assets/images/bill.svg
@@ -0,0 +1,11 @@
+
+
+
diff --git a/assets/images/briefcase.svg b/assets/images/briefcase.svg
new file mode 100644
index 00000000000..f73854bbb36
--- /dev/null
+++ b/assets/images/briefcase.svg
@@ -0,0 +1,8 @@
+
+
+
diff --git a/assets/images/circle-hourglass.svg b/assets/images/circle-hourglass.svg
new file mode 100644
index 00000000000..1bba8b47410
--- /dev/null
+++ b/assets/images/circle-hourglass.svg
@@ -0,0 +1,10 @@
+
+
+
diff --git a/assets/images/concierge.svg b/assets/images/concierge.svg
new file mode 100644
index 00000000000..4b22f626a0e
--- /dev/null
+++ b/assets/images/concierge.svg
@@ -0,0 +1,8 @@
+
+
+
diff --git a/assets/images/confetti-pop.gif b/assets/images/confetti-pop.gif
new file mode 100644
index 00000000000..a4137b6e9dd
Binary files /dev/null and b/assets/images/confetti-pop.gif differ
diff --git a/assets/images/invoice.svg b/assets/images/invoice.svg
new file mode 100644
index 00000000000..91391b0f9bc
--- /dev/null
+++ b/assets/images/invoice.svg
@@ -0,0 +1,11 @@
+
+
+
diff --git a/assets/images/luggage.svg b/assets/images/luggage.svg
new file mode 100644
index 00000000000..b7f8dc7fe93
--- /dev/null
+++ b/assets/images/luggage.svg
@@ -0,0 +1,16 @@
+
+
+
diff --git a/assets/images/paycheck.svg b/assets/images/paycheck.svg
index 228faf7cee7..f81a5a9a86a 100644
--- a/assets/images/paycheck.svg
+++ b/assets/images/paycheck.svg
@@ -1,20 +1,17 @@
-
+
diff --git a/assets/images/product-illustrations/bank-arrow--pink.svg b/assets/images/product-illustrations/bank-arrow--pink.svg
new file mode 100644
index 00000000000..c561bfd2790
--- /dev/null
+++ b/assets/images/product-illustrations/bank-arrow--pink.svg
@@ -0,0 +1,43 @@
+
+
+
diff --git a/assets/images/product-illustrations/bank-mouse--green.svg b/assets/images/product-illustrations/bank-mouse--green.svg
new file mode 100644
index 00000000000..99dfd1718c1
--- /dev/null
+++ b/assets/images/product-illustrations/bank-mouse--green.svg
@@ -0,0 +1,50 @@
+
+
+
diff --git a/assets/images/product-illustrations/bank-user--green.svg b/assets/images/product-illustrations/bank-user--green.svg
new file mode 100644
index 00000000000..676d05c3bc0
--- /dev/null
+++ b/assets/images/product-illustrations/bank-user--green.svg
@@ -0,0 +1,48 @@
+
+
+
diff --git a/assets/images/product-illustrations/concierge--blue.svg b/assets/images/product-illustrations/concierge--blue.svg
new file mode 100644
index 00000000000..d1d3fede1f6
--- /dev/null
+++ b/assets/images/product-illustrations/concierge--blue.svg
@@ -0,0 +1,20 @@
+
+
+
diff --git a/assets/images/product-illustrations/credit-cards--blue.svg b/assets/images/product-illustrations/credit-cards--blue.svg
new file mode 100644
index 00000000000..008dbd20be3
--- /dev/null
+++ b/assets/images/product-illustrations/credit-cards--blue.svg
@@ -0,0 +1,31 @@
+
+
+
diff --git a/assets/images/product-illustrations/invoice--orange.svg b/assets/images/product-illustrations/invoice--orange.svg
new file mode 100644
index 00000000000..aebd5066066
--- /dev/null
+++ b/assets/images/product-illustrations/invoice--orange.svg
@@ -0,0 +1,25 @@
+
+
+
diff --git a/assets/images/product-illustrations/jewel-box--blue.svg b/assets/images/product-illustrations/jewel-box--blue.svg
new file mode 100644
index 00000000000..b9d6a084bcb
--- /dev/null
+++ b/assets/images/product-illustrations/jewel-box--blue.svg
@@ -0,0 +1,45 @@
+
+
+
diff --git a/assets/images/product-illustrations/jewel-box--green.svg b/assets/images/product-illustrations/jewel-box--green.svg
new file mode 100644
index 00000000000..ba1cade3dcc
--- /dev/null
+++ b/assets/images/product-illustrations/jewel-box--green.svg
@@ -0,0 +1,45 @@
+
+
+
diff --git a/assets/images/product-illustrations/jewel-box--pink.svg b/assets/images/product-illustrations/jewel-box--pink.svg
new file mode 100644
index 00000000000..dd58151c913
--- /dev/null
+++ b/assets/images/product-illustrations/jewel-box--pink.svg
@@ -0,0 +1,45 @@
+
+
+
diff --git a/assets/images/product-illustrations/jewel-box--yellow.svg b/assets/images/product-illustrations/jewel-box--yellow.svg
new file mode 100644
index 00000000000..858d5b66688
--- /dev/null
+++ b/assets/images/product-illustrations/jewel-box--yellow.svg
@@ -0,0 +1,45 @@
+
+
+
diff --git a/assets/images/product-illustrations/money-envelope--blue.svg b/assets/images/product-illustrations/money-envelope--blue.svg
new file mode 100644
index 00000000000..199489af882
--- /dev/null
+++ b/assets/images/product-illustrations/money-envelope--blue.svg
@@ -0,0 +1,30 @@
+
+
+
diff --git a/assets/images/product-illustrations/money-mouse--pink.svg b/assets/images/product-illustrations/money-mouse--pink.svg
new file mode 100644
index 00000000000..72c21fc4675
--- /dev/null
+++ b/assets/images/product-illustrations/money-mouse--pink.svg
@@ -0,0 +1,30 @@
+
+
+
diff --git a/assets/images/product-illustrations/receipt--yellow.svg b/assets/images/product-illustrations/receipt--yellow.svg
new file mode 100644
index 00000000000..f40f3e0a5aa
--- /dev/null
+++ b/assets/images/product-illustrations/receipt--yellow.svg
@@ -0,0 +1,20 @@
+
+
+
diff --git a/assets/images/product-illustrations/rocket--orange.svg b/assets/images/product-illustrations/rocket--orange.svg
new file mode 100644
index 00000000000..a3bb9a67fb7
--- /dev/null
+++ b/assets/images/product-illustrations/rocket--orange.svg
@@ -0,0 +1,87 @@
+
+
+
diff --git a/assets/images/product-illustrations/tada--yellow.svg b/assets/images/product-illustrations/tada--yellow.svg
new file mode 100644
index 00000000000..037baef7def
--- /dev/null
+++ b/assets/images/product-illustrations/tada--yellow.svg
@@ -0,0 +1,56 @@
+
+
+
diff --git a/assets/images/receipt-search.svg b/assets/images/receipt-search.svg
new file mode 100644
index 00000000000..6272cfea321
--- /dev/null
+++ b/assets/images/receipt-search.svg
@@ -0,0 +1,15 @@
+
+
+
diff --git a/assets/images/users.svg b/assets/images/users.svg
index e74a77d7f4c..948998d2c35 100644
--- a/assets/images/users.svg
+++ b/assets/images/users.svg
@@ -13,12 +13,13 @@
-
-
-
-
-
+
+
+
+
diff --git a/src/ONYXKEYS.js b/src/ONYXKEYS.js
index ac19970dc63..4317a9ac6be 100755
--- a/src/ONYXKEYS.js
+++ b/src/ONYXKEYS.js
@@ -134,4 +134,7 @@ export default {
// Notifies all tabs that they should sign out and clear storage.
SHOULD_SIGN_OUT: 'shouldSignOut',
+
+ // Set when we are loading payment methods
+ IS_LOADING_PAYMENT_METHODS: 'isLoadingPaymentMethods',
};
diff --git a/src/ROUTES.js b/src/ROUTES.js
index 860b1f597ef..71206888801 100644
--- a/src/ROUTES.js
+++ b/src/ROUTES.js
@@ -77,18 +77,29 @@ export default {
LOGIN_WITH_VALIDATE_CODE_WORKSPACE_CARD: 'v/:accountID/:validateCode/workspace/:policyID/card',
LOGIN_WITH_VALIDATE_CODE_2FA_WORKSPACE_CARD: 'v/:accountID/:validateCode/2fa/workspace/:policyID/card',
ENABLE_PAYMENTS: 'enable-payments',
- WORKSPACE: 'workspace',
- WORKSPACE_CARD: ':policyID/card',
- WORKSPACE_PEOPLE: ':policyID/people',
WORKSPACE_NEW: 'workspace/new',
- getWorkspaceCardRoute: policyID => `workspace/${policyID}/card`,
- getWorkspacePeopleRoute: policyID => `workspace/${policyID}/people`,
- getWorkspaceInviteRoute: policyID => `workspace/${policyID}/invite`,
+ WORKSPACE_INITIAL: 'workspace/:policyID',
WORKSPACE_INVITE: 'workspace/:policyID/invite',
+ WORKSPACE_SETTINGS: 'workspace/:policyID/settings',
+ WORKSPACE_CARD: 'workspace/:policyID/card',
+ WORKSPACE_REIMBURSE: 'workspace/:policyID/reimburse',
+ WORKSPACE_BILLS: 'workspace/:policyID/bills',
+ WORKSPACE_INVOICES: 'workspace/:policyID/invoices',
+ WORKSPACE_TRAVEL: 'workspace/:policyID/travel',
+ WORKSPACE_MEMBERS: 'workspace/:policyID/members',
+ WORKSPACE_BANK_ACCOUNT: 'workspace/:policyID/bank-account',
+ getWorkspaceInitialRoute: policyID => `workspace/${policyID}`,
+ getWorkspaceInviteRoute: policyID => `workspace/${policyID}/invite`,
+ getWorkspaceSettingsRoute: policyID => `workspace/${policyID}/settings`,
+ getWorkspaceCardRoute: policyID => `workspace/${policyID}/card`,
+ getWorkspaceReimburseRoute: policyID => `workspace/${policyID}/reimburse`,
+ getWorkspaceBillsRoute: policyID => `workspace/${policyID}/bills`,
+ getWorkspaceInvoicesRoute: policyID => `workspace/${policyID}/invoices`,
+ getWorkspaceTravelRoute: policyID => `workspace/${policyID}/travel`,
+ getWorkspaceMembersRoute: policyID => `workspace/${policyID}/members`,
+ getWorkspaceBankAccountRoute: policyID => `workspace/${policyID}/bank-account`,
getRequestCallRoute: taskID => `request-call/${taskID}`,
REQUEST_CALL: 'request-call/:taskID',
- getWorkspaceEditorRoute: policyID => `workspace/${policyID}/edit`,
- WORKSPACE_EDITOR: 'workspace/:policyID/edit',
/**
* @param {String} route
diff --git a/src/components/AddPlaidBankAccount.js b/src/components/AddPlaidBankAccount.js
index 180bf9ea39b..b88e2d58b09 100644
--- a/src/components/AddPlaidBankAccount.js
+++ b/src/components/AddPlaidBankAccount.js
@@ -138,8 +138,11 @@ class AddPlaidBankAccount extends React.Component {
}
const account = this.getAccounts()[this.state.selectedIndex];
+ const bankName = lodashGet(this.props.plaidBankAccounts, 'bankName');
this.props.onSubmit({
- account, plaidLinkToken: this.props.plaidLinkToken,
+ bankName,
+ account,
+ plaidLinkToken: this.props.plaidLinkToken,
});
}
diff --git a/src/components/AddressSearch.js b/src/components/AddressSearch.js
index 3769cd2c0cf..536e144f090 100644
--- a/src/components/AddressSearch.js
+++ b/src/components/AddressSearch.js
@@ -108,7 +108,7 @@ class AddressSearch extends React.Component {
styles.overflowAuto,
],
description: [styles.googleSearchText],
- separator: [styles.googleSearchSeperator],
+ separator: [styles.googleSearchSeparator],
}}
/>
);
diff --git a/src/components/Avatar.js b/src/components/Avatar.js
index aa56b6262d2..727ea7bbc1d 100644
--- a/src/components/Avatar.js
+++ b/src/components/Avatar.js
@@ -3,6 +3,7 @@ import {Image, View} from 'react-native';
import PropTypes from 'prop-types';
import styles from '../styles/styles';
import RoomAvatar from './RoomAvatar';
+import stylePropTypes from '../styles/stylePropTypes';
const propTypes = {
/** Url source for the avatar */
@@ -12,7 +13,7 @@ const propTypes = {
imageStyles: PropTypes.arrayOf(PropTypes.object),
/** Extra styles to pass to View wrapper */
- containerStyles: PropTypes.oneOfType([PropTypes.object, PropTypes.arrayOf(PropTypes.object)]),
+ containerStyles: stylePropTypes,
/** Set the size of Avatar */
size: PropTypes.oneOf(['default', 'small']),
diff --git a/src/components/AvatarWithImagePicker.js b/src/components/AvatarWithImagePicker.js
index b677a9ab5f2..f0f703bc395 100644
--- a/src/components/AvatarWithImagePicker.js
+++ b/src/components/AvatarWithImagePicker.js
@@ -17,13 +17,14 @@ import withLocalize, {withLocalizePropTypes} from './withLocalize';
import variables from '../styles/variables';
import CONST from '../CONST';
import SpinningIndicatorAnimation from '../styles/animation/SpinningIndicatorAnimation';
+import stylePropTypes from '../styles/stylePropTypes';
const propTypes = {
/** Avatar URL to display */
avatarURL: PropTypes.string,
/** Additional style props */
- style: PropTypes.oneOfType([PropTypes.object, PropTypes.arrayOf(PropTypes.object)]),
+ style: stylePropTypes,
/** Executed once an image has been selected */
onImageSelected: PropTypes.func,
diff --git a/src/components/CopyTextToClipboard.js b/src/components/CopyTextToClipboard.js
new file mode 100644
index 00000000000..ef721eaac69
--- /dev/null
+++ b/src/components/CopyTextToClipboard.js
@@ -0,0 +1,65 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import Text from './Text';
+import {Checkmark, Clipboard as ClipboardIcon} from './Icon/Expensicons';
+import Clipboard from '../libs/Clipboard';
+import Icon from './Icon';
+import styles from '../styles/styles';
+
+const propTypes = {
+ /** The text to display and copy to the clipboard */
+ text: PropTypes.string.isRequired,
+
+ /** Styles to apply to the text */
+ textStyles: PropTypes.arrayOf(PropTypes.object),
+};
+
+const defaultProps = {
+ textStyles: [],
+};
+
+class CopyTextToClipboard extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.copyToClipboard = this.copyToClipboard.bind(this);
+
+ this.state = {
+ showCheckmark: false,
+ };
+ }
+
+ componentWillUnmount() {
+ // Clear the interval when the component unmounts so that if the user navigates
+ // away quickly, then setState() won't try to update a component that's been unmounted
+ clearInterval(this.showCheckmarkInterval);
+ }
+
+ copyToClipboard() {
+ Clipboard.setString(this.props.text);
+ this.setState({showCheckmark: true}, () => {
+ this.showCheckmarkInterval = setTimeout(() => {
+ this.setState({showCheckmark: false});
+ }, 2000);
+ });
+ }
+
+ render() {
+ return (
+
+ {this.props.text}
+ {this.state.showCheckmark
+ ?
+ : }
+
+ );
+ }
+}
+
+CopyTextToClipboard.propTypes = propTypes;
+CopyTextToClipboard.defaultProps = defaultProps;
+
+export default CopyTextToClipboard;
diff --git a/src/components/Icon/Expensicons.js b/src/components/Icon/Expensicons.js
index 8555edc7395..ac2cd32c0c8 100644
--- a/src/components/Icon/Expensicons.js
+++ b/src/components/Icon/Expensicons.js
@@ -3,14 +3,18 @@ import Apple from '../../../assets/images/apple.svg';
import ArrowRight from '../../../assets/images/arrow-right.svg';
import BackArrow from '../../../assets/images/back-left.svg';
import Bank from '../../../assets/images/bank.svg';
+import Bill from '../../../assets/images/bill.svg';
+import Briefcase from '../../../assets/images/briefcase.svg';
import Bug from '../../../assets/images/bug.svg';
import Building from '../../../assets/images/building.svg';
import Camera from '../../../assets/images/camera.svg';
import Cash from '../../../assets/images/cash.svg';
import ChatBubble from '../../../assets/images/chatbubble.svg';
import Checkmark from '../../../assets/images/checkmark.svg';
+import CircleHourglass from '../../../assets/images/circle-hourglass.svg';
import Clipboard from '../../../assets/images/clipboard.svg';
import Close from '../../../assets/images/close.svg';
+import Concierge from '../../../assets/images/concierge.svg';
import CreditCard from '../../../assets/images/creditcard.svg';
import DownArrow from '../../../assets/images/down.svg';
import Download from '../../../assets/images/download.svg';
@@ -21,9 +25,11 @@ import ExpensifyCard from '../../../assets/images/expensifycard.svg';
import Gallery from '../../../assets/images/gallery.svg';
import Gear from '../../../assets/images/gear.svg';
import Info from '../../../assets/images/info.svg';
+import Invoice from '../../../assets/images/invoice.svg';
import Link from '../../../assets/images/link.svg';
import LinkCopy from '../../../assets/images/link-copy.svg';
import Lock from '../../../assets/images/lock.svg';
+import Luggage from '../../../assets/images/luggage.svg';
import MagnifyingGlass from '../../../assets/images/magnifyingglass.svg';
import Mail from '../../../assets/images/mail.svg';
import MoneyBag from '../../../assets/images/money-bag.svg';
@@ -43,6 +49,7 @@ import Plus from '../../../assets/images/plus.svg';
import Printer from '../../../assets/images/printer.svg';
import Profile from '../../../assets/images/profile.svg';
import Receipt from '../../../assets/images/receipt.svg';
+import ReceiptSearch from '../../../assets/images/receipt-search.svg';
import Send from '../../../assets/images/send.svg';
import SignOut from '../../../assets/images/sign-out.svg';
import Sync from '../../../assets/images/sync.svg';
@@ -60,14 +67,18 @@ export {
ArrowRight,
BackArrow,
Bank,
+ Bill,
+ Briefcase,
Building,
Bug,
Camera,
Cash,
ChatBubble,
Checkmark,
+ CircleHourglass,
Clipboard,
Close,
+ Concierge,
CreditCard,
DownArrow,
Download,
@@ -78,9 +89,11 @@ export {
Gallery,
Gear,
Info,
+ Invoice,
Link,
LinkCopy,
Lock,
+ Luggage,
MagnifyingGlass,
Mail,
MoneyBag,
@@ -100,6 +113,7 @@ export {
Printer,
Profile,
Receipt,
+ ReceiptSearch,
Send,
SignOut,
Sync,
diff --git a/src/components/Icon/Illustrations.js b/src/components/Icon/Illustrations.js
new file mode 100644
index 00000000000..da565effa4a
--- /dev/null
+++ b/src/components/Icon/Illustrations.js
@@ -0,0 +1,33 @@
+import BankArrowPink from '../../../assets/images/product-illustrations/bank-arrow--pink.svg';
+import BankMouseGreen from '../../../assets/images/product-illustrations/bank-mouse--green.svg';
+import BankUserGreen from '../../../assets/images/product-illustrations/bank-user--green.svg';
+import ConciergeBlue from '../../../assets/images/product-illustrations/concierge--blue.svg';
+import CreditCardsBlue from '../../../assets/images/product-illustrations/credit-cards--blue.svg';
+import InvoiceOrange from '../../../assets/images/product-illustrations/invoice--orange.svg';
+import JewelBoxBlue from '../../../assets/images/product-illustrations/jewel-box--blue.svg';
+import JewelBoxGreen from '../../../assets/images/product-illustrations/jewel-box--green.svg';
+import JewelBoxPink from '../../../assets/images/product-illustrations/jewel-box--pink.svg';
+import JewelBoxYellow from '../../../assets/images/product-illustrations/jewel-box--yellow.svg';
+import MoneyEnvelopeBlue from '../../../assets/images/product-illustrations/money-envelope--blue.svg';
+import MoneyMousePink from '../../../assets/images/product-illustrations/money-mouse--pink.svg';
+import ReceiptYellow from '../../../assets/images/product-illustrations/receipt--yellow.svg';
+import RocketOrange from '../../../assets/images/product-illustrations/rocket--orange.svg';
+import TadaYellow from '../../../assets/images/product-illustrations/tada--yellow.svg';
+
+export {
+ BankArrowPink,
+ BankMouseGreen,
+ BankUserGreen,
+ ConciergeBlue,
+ CreditCardsBlue,
+ InvoiceOrange,
+ JewelBoxBlue,
+ JewelBoxGreen,
+ JewelBoxPink,
+ JewelBoxYellow,
+ MoneyEnvelopeBlue,
+ MoneyMousePink,
+ ReceiptYellow,
+ RocketOrange,
+ TadaYellow,
+};
diff --git a/src/components/MenuItem.js b/src/components/MenuItem.js
index 03b3f4847e4..4b22a620481 100644
--- a/src/components/MenuItem.js
+++ b/src/components/MenuItem.js
@@ -2,7 +2,6 @@ import React from 'react';
import {
View, Pressable,
} from 'react-native';
-import PropTypes from 'prop-types';
import Text from './Text';
import styles, {getButtonBackgroundColorStyle, getIconFillColor} from '../styles/styles';
import Icon from './Icon';
@@ -11,59 +10,10 @@ import getButtonState from '../libs/getButtonState';
import Avatar from './Avatar';
import Badge from './Badge';
import CONST from '../CONST';
+import menuItemPropTypes from './menuItemPropTypes';
const propTypes = {
- /** Text to be shown as badge near the right end. */
- badgeText: PropTypes.string,
-
- /** Any additional styles to apply */
- // eslint-disable-next-line react/forbid-prop-types
- wrapperStyle: PropTypes.object,
-
- /** Function to fire when component is pressed */
- onPress: PropTypes.func.isRequired,
-
- /** Icon to display on the left side of component */
- icon: PropTypes.oneOfType([PropTypes.elementType, PropTypes.string]),
-
- /** Icon Height */
- iconWidth: PropTypes.number,
-
- /** Icon Height */
- iconHeight: PropTypes.number,
-
- /** Text to display for the item */
- title: PropTypes.string.isRequired,
-
- /** Boolean whether to display the right icon */
- shouldShowRightIcon: PropTypes.bool,
-
- /** A boolean flag that gives the icon a green fill if true */
- success: PropTypes.bool,
-
- /** Overrides the icon for shouldShowRightIcon */
- iconRight: PropTypes.elementType,
-
- /** A description text to show under the title */
- description: PropTypes.string,
-
- /** Any additional styles to pass to the icon container. */
- iconStyles: PropTypes.arrayOf(PropTypes.object),
-
- /** The fill color to pass into the icon. */
- iconFill: PropTypes.string,
-
- /** Whether item is focused or active */
- focused: PropTypes.bool,
-
- /** Should we disable this menu item? */
- disabled: PropTypes.bool,
-
- /** A right-aligned subtitle for this menu option */
- subtitle: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
-
- /** Flag to choose between avatar image or an icon */
- iconType: PropTypes.oneOf([CONST.ICON_TYPE_AVATAR, CONST.ICON_TYPE_ICON]),
+ ...menuItemPropTypes,
};
const defaultProps = {
diff --git a/src/components/MenuItemList.js b/src/components/MenuItemList.js
new file mode 100644
index 00000000000..3ffa6aa9806
--- /dev/null
+++ b/src/components/MenuItemList.js
@@ -0,0 +1,31 @@
+import React from 'react';
+import _ from 'underscore';
+import PropTypes from 'prop-types';
+import MenuItem from './MenuItem';
+import menuItemPropTypes from './menuItemPropTypes';
+
+const propTypes = {
+ /** An array of props that are pass to individual MenuItem components */
+ menuItems: PropTypes.arrayOf(PropTypes.shape(menuItemPropTypes)),
+};
+const defaultProps = {
+ menuItems: [],
+};
+
+const MenuItemList = ({menuItems}) => (
+ <>
+ {_.map(menuItems, menuItemProps => (
+
+ ))}
+ >
+);
+
+MenuItemList.displayName = 'MenuItemList';
+MenuItemList.propTypes = propTypes;
+MenuItemList.defaultProps = defaultProps;
+
+export default MenuItemList;
diff --git a/src/components/PDFView/index.js b/src/components/PDFView/index.js
index e2b051bf5cf..6beb9c10c00 100644
--- a/src/components/PDFView/index.js
+++ b/src/components/PDFView/index.js
@@ -54,7 +54,7 @@ class PDFView extends PureComponent {
style={[styles.PDFView, this.props.style]}
>
}
+ loading={}
file={this.props.sourceURL}
options={{
cMapUrl: 'cmaps/',
diff --git a/src/components/PDFView/index.native.js b/src/components/PDFView/index.native.js
index 57554a53e8d..962bf4e5224 100644
--- a/src/components/PDFView/index.native.js
+++ b/src/components/PDFView/index.native.js
@@ -32,7 +32,7 @@ const defaultProps = {
const PDFView = props => (
}
+ activityIndicator={}
source={{uri: props.sourceURL}}
style={[
styles.imageModalPDF,
diff --git a/src/components/TextLink.js b/src/components/TextLink.js
index 4403c55715c..e8947f21434 100644
--- a/src/components/TextLink.js
+++ b/src/components/TextLink.js
@@ -4,6 +4,7 @@ import PropTypes from 'prop-types';
import {Pressable, Linking} from 'react-native';
import Text from './Text';
import styles from '../styles/styles';
+import stylePropTypes from '../styles/stylePropTypes';
const propTypes = {
/** Link to open in new tab */
@@ -17,7 +18,7 @@ const propTypes = {
]).isRequired,
/** Additional style props */
- style: PropTypes.oneOfType([PropTypes.object, PropTypes.arrayOf(PropTypes.object)]),
+ style: stylePropTypes,
/** Overwrites the default link behavior with a custom callback */
onPress: PropTypes.func,
diff --git a/src/components/UnorderedList.js b/src/components/UnorderedList.js
new file mode 100644
index 00000000000..9c2ee04fc8f
--- /dev/null
+++ b/src/components/UnorderedList.js
@@ -0,0 +1,34 @@
+import React from 'react';
+import _ from 'underscore';
+import {View} from 'react-native';
+import PropTypes from 'prop-types';
+import Text from './Text';
+import styles from '../styles/styles';
+
+const propTypes = {
+ /** An array of strings to display as an unordered list */
+ items: PropTypes.arrayOf(PropTypes.string),
+};
+const defaultProps = {
+ items: [],
+};
+
+const UnorderedList = ({items}) => (
+ <>
+ {_.map(items, itemText => (
+
+ {'\u2022'}
+ {itemText}
+
+ ))}
+ >
+);
+
+UnorderedList.displayName = 'UnorderedList';
+UnorderedList.propTypes = propTypes;
+UnorderedList.defaultProps = defaultProps;
+
+export default UnorderedList;
diff --git a/src/components/bankAccountPropTypes.js b/src/components/bankAccountPropTypes.js
new file mode 100644
index 00000000000..3331a617cbc
--- /dev/null
+++ b/src/components/bankAccountPropTypes.js
@@ -0,0 +1,15 @@
+import PropTypes from 'prop-types';
+
+export default PropTypes.shape({
+ /** The name of the institution (bank of america, etc */
+ addressName: PropTypes.string,
+
+ /** The masked bank account number */
+ accountNumber: PropTypes.string,
+
+ /** The bankAccountID in the bankAccounts db */
+ bankAccountID: PropTypes.number,
+
+ /** The bank account type */
+ type: PropTypes.string,
+});
diff --git a/src/components/menuItemPropTypes.js b/src/components/menuItemPropTypes.js
new file mode 100644
index 00000000000..5802dc9976b
--- /dev/null
+++ b/src/components/menuItemPropTypes.js
@@ -0,0 +1,58 @@
+import PropTypes from 'prop-types';
+import CONST from '../CONST';
+
+const propTypes = {
+ /** Text to be shown as badge near the right end. */
+ badgeText: PropTypes.string,
+
+ /** Any additional styles to apply */
+ // eslint-disable-next-line react/forbid-prop-types
+ wrapperStyle: PropTypes.object,
+
+ /** Function to fire when component is pressed */
+ onPress: PropTypes.func.isRequired,
+
+ /** Icon to display on the left side of component */
+ icon: PropTypes.oneOfType([PropTypes.elementType, PropTypes.string]),
+
+ /** Icon Width */
+ iconWidth: PropTypes.number,
+
+ /** Icon Height */
+ iconHeight: PropTypes.number,
+
+ /** Text to display for the item */
+ title: PropTypes.string.isRequired,
+
+ /** Boolean whether to display the right icon */
+ shouldShowRightIcon: PropTypes.bool,
+
+ /** A boolean flag that gives the icon a green fill if true */
+ success: PropTypes.bool,
+
+ /** Overrides the icon for shouldShowRightIcon */
+ iconRight: PropTypes.elementType,
+
+ /** A description text to show under the title */
+ description: PropTypes.string,
+
+ /** Any additional styles to pass to the icon container. */
+ iconStyles: PropTypes.arrayOf(PropTypes.object),
+
+ /** The fill color to pass into the icon. */
+ iconFill: PropTypes.string,
+
+ /** Whether item is focused or active */
+ focused: PropTypes.bool,
+
+ /** Should we disable this menu item? */
+ disabled: PropTypes.bool,
+
+ /** A right-aligned subtitle for this menu option */
+ subtitle: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
+
+ /** Flag to choose between avatar image or an icon */
+ iconType: PropTypes.oneOf([CONST.ICON_TYPE_AVATAR, CONST.ICON_TYPE_ICON]),
+};
+
+export default propTypes;
diff --git a/src/languages/en.js b/src/languages/en.js
index 7f7ecf8c521..3cfaf952b2b 100755
--- a/src/languages/en.js
+++ b/src/languages/en.js
@@ -45,7 +45,7 @@ export default {
saveAndContinue: 'Save & continue',
settings: 'Settings',
termsOfService: 'Terms of service',
- people: 'People',
+ members: 'Members',
invite: 'Invite',
here: 'here',
dob: 'Date of birth',
@@ -411,10 +411,10 @@ export default {
routingNumber: 'Routing number',
addBankAccount: 'Add bank account',
chooseAnAccount: 'Choose an account',
- logIntoYourBank: 'Log into your bank',
+ connectOnlineWithPlaid: 'Connect online with Plaid',
connectManually: 'Connect manually',
yourDataIsSecure: 'Your data is secure',
- toGetStarted: 'To get started with the Expensify Card, you first need to add a bank account.',
+ toGetStarted: 'Add a bank account and issue corporate cards, reimburse expenses, collect invoice payments, and pay bills, all from one place.',
plaidBodyCopy: 'Give your employees an easier way to pay - and get paid back - for company expenses.',
checkHelpLine: 'Your routing number and account number can be found on a check for the account.',
validateAccountError: 'In order to finish setting up your bank account, you must validate your account. Please check your email to validate your account, and return here to finish up!',
@@ -592,7 +592,7 @@ export default {
reviewingInfo: 'Thanks! We\'re reviewing your information, and will be in touch shortly. Please check your chat with Concierge ',
forNextSteps: ' for next steps to finish setting up your bank account.',
letsChatCTA: 'Yes, let\'s chat!',
- letsChatText: 'Thanks for providing your information! We have a couple more things to work out, but it\'ll be easier over chat. Ready to get started?',
+ letsChatText: 'Thanks for doing that! We have a couple more things to work out, but it’ll be easier over chat. Ready to chat?',
letsChatTitle: 'Let\'s chat!',
},
beneficialOwnersStep: {
@@ -621,9 +621,18 @@ export default {
},
workspace: {
common: {
- card: 'Expensify Card',
+ card: 'Issue corporate cards',
workspace: 'Workspace',
edit: 'Edit workspace',
+ settings: 'General settings',
+ reimburse: 'Reimburse receipts',
+ bills: 'Pay bills',
+ invoices: 'Send invoices',
+ travel: 'Book travel',
+ members: 'Manage members',
+ bankAccount: 'Connect bank account',
+ issueAndManageCards: 'Issue and manage cards',
+ reconcileCards: 'Reconcile cards',
},
new: {
newWorkspace: 'New workspace',
@@ -637,14 +646,55 @@ export default {
selectAll: 'Select all',
},
card: {
- addEmail: 'Add email',
- tagline: 'The smartest corporate card in the room.',
- publicCopy: 'In order to use the Expensify Card you must use your company\'s private domain. Go ahead and add your private email address as a secondary login.',
- privateCopy: 'Just swipe your Expensify Card and your expenses are done, it\'s that simple!',
- getStarted: 'Get started',
- finishSetup: 'Finish setup',
- manageCards: 'Manage cards',
- cardReadyTagline: 'Your Expensify Cards are ready to go!',
+ header: 'Unlock free Expensify Cards',
+ headerWithEcard: 'Cards are ready!',
+ noVBACopy: 'Connect a bank account to issue unlimited Expensify Cards for your workspace members and access all of these incredible benefits:',
+ VBANoECardCopy: 'Issue unlimited Expensify Cards for your workspace members, as well as all of these incredible benefits:',
+ conciergeCanHelp: 'Concierge can help you add a work email address to enable the Expensify Card.',
+ VBAWithECardCopy: 'Enjoy all these incredible benefits:',
+ benefit1: 'Up to 2% cash back',
+ benefit2: 'Digital and physical cards',
+ benefit3: 'No personal liability',
+ benefit4: 'Customizable limits',
+ chatWithConcierge: 'Chat with Concierge',
+ },
+ reimburse: {
+ captureReceipts: 'Capture receipts',
+ fastReimbursementsHappyMembers: 'Fast reimbursements = happy members!',
+ viewAllReceipts: 'View all receipts',
+ reimburseReceipts: 'Reimburse receipts',
+ unlockNextDayReimbursements: 'Unlock next day reimbursements',
+ captureNoVBACopyBeforeEmail: 'Ask your workspace members to forward receipts to ',
+ captureNoVBACopyAfterEmail: ' and download the Expensify App to track cash expenses on the go.',
+ unlockNoVBACopy: 'Connect a bank account to reimburse your workspace members online.',
+ fastReimbursementsVBACopy: 'You\'re all set to reimburse receipts from your bank account!',
+ },
+ bills: {
+ manageYourBills: 'Manage your bills',
+ askYourVendorsBeforeEmail: 'Ask your vendors to forward their invoices to ',
+ askYourVendorsAfterEmail: ' and we\'ll scan them for you to pay',
+ viewAllBills: 'View all bills',
+ unlockOnlineBillPayment: 'Unlock online bill payment',
+ unlockNoVBACopy: 'Connect your bank account to pay bills online for free!',
+ hassleFreeBills: 'Hassle-free bills!',
+ VBACopy: 'You\'re all set to make payments from your bank account!',
+ },
+ invoices: {
+ invoiceClientsAndCustomers: 'Invoice clients and customers',
+ invoiceFirstSectionCopy: 'Send beautiful, professional invoices directly to your clients and customers right from within the Expensify app.',
+ viewAllInvoices: 'View all invoices',
+ unlockOnlineInvoicesCollection: 'Unlock online invoices collection',
+ unlockNoVBACopy: 'Connect your bank account to accept online payments for invoices - by ACH or credit card - to be deposited straight into your account.',
+ moneyBackInAFlash: 'Money back, in a flash!',
+ unlockVBACopy: 'You\'re all set to accept payments by ACH or credit card!',
+ viewUnpaidInvoices: 'View unpaid invoices',
+ },
+ travel: {
+ unlockConciergeBookingTravel: 'Unlock Concierge travel booking',
+ noVBACopy: 'Connect your bank account to let workspace members book their flights, hotels, and cars by starting a chat with Concierge.',
+ packYourBags: 'Pack your bags!',
+ VBACopy: 'Members with the Expensify card can chat with Concierge to book travel!',
+ bookTravelWithConcierge: 'Book travel with Concierge',
},
invite: {
invitePeople: 'Invite new members',
@@ -661,12 +711,24 @@ export default {
editor: {
nameInputLabel: 'Name',
nameInputHelpText: 'This is the name you will see on your workspace.',
+ nameIsRequiredError: 'You need to define a name for your workspace',
+ currencyInputLabel: 'Default Currency',
+ currencyInputHelpText: 'All expenses on this workspace will be converted to this currency.',
save: 'Save',
genericFailureMessage: 'An error occurred updating the workspace, please try again.',
avatarUploadFailureMessage: 'An error occurred uploading the avatar, please try again.',
},
- error: {
- growlMessageInvalidPolicy: 'Invalid workspace!',
+ bankAccount: {
+ continueWithSetup: 'Continue with setup',
+ youreAlmostDone: 'You\'re almost done setting up your bank account, which will let you issue corporate cards, reimburse expenses, collect invoices, and pay bills all from the same bank account.',
+ streamlinePayments: 'Streamline payments',
+ oneMoreThing: 'One more thing!',
+ allSet: 'All set!',
+ accountDescriptionNoCards: 'This bank account will be used to reimburse expenses, collect invoices, and pay bills all from the same account.\n\nConcierge can help you add a work email address to enable the Expensify Card.',
+ accountDescriptionWithCards: 'This bank account will be used to issue corporate cards, reimburse expenses, collect invoices, and pay bills all from the same account.',
+ chatWithConcierge: 'Chat with Concierge',
+ letsFinishInChat: 'Let\'s finish in chat!',
+ almostDone: 'Almost done!',
},
},
requestCallPage: {
diff --git a/src/languages/es.js b/src/languages/es.js
index fa9b594af0e..27f5c81cd3a 100644
--- a/src/languages/es.js
+++ b/src/languages/es.js
@@ -45,7 +45,7 @@ export default {
saveAndContinue: 'Guardar y continuar',
settings: 'Configuración',
termsOfService: 'Términos de servicio',
- people: 'Personas',
+ members: 'Miembros',
invite: 'Invitación',
here: 'aquÃ',
dob: 'Fecha de Nacimiento',
@@ -411,10 +411,10 @@ export default {
routingNumber: 'Número de ruta',
addBankAccount: 'Agregar cuenta bancaria',
chooseAnAccount: 'Elige una cuenta',
- logIntoYourBank: 'Inicia sesión en su banco',
+ connectOnlineWithPlaid: 'Conéctate a Plaid online',
connectManually: 'Conectar manualmente',
yourDataIsSecure: 'Tus datos están seguros',
- toGetStarted: 'Para comenzar con la tarjeta Expensify, primero debe agregar una cuenta bancaria.',
+ toGetStarted: 'Añade una cuenta bancaria y emite tarjetas corporativas, reembolsa gastos y cobra y paga facturas, todo desde un mismo sitio.',
plaidBodyCopy: 'Ofrezca a sus empleados una forma más sencilla de pagar - y recuperar - los gastos de la empresa.',
checkHelpLine: 'Su número de ruta y número de cuenta se pueden encontrar en un cheque de la cuenta bancaria.',
validateAccountError: 'Para terminar de configurar tu cuenta bancaria, debes validar tu cuenta de Expensify. Por favor revisa tu correo electrónico para validar tu cuenta y regresa aquà para continuar.',
@@ -594,7 +594,7 @@ export default {
reviewingInfo: '¡Gracias! Estamos revisando tu información y nos comunicaremos contigo en breve. Consulte su chat con Concierge ',
forNextSteps: ' para conocer los próximos pasos para terminar de configurar su cuenta bancaria.',
letsChatCTA: '¡SÃ, vamos a chatear!',
- letsChatText: '¡Gracias por darnos tu información! Aún nos quedan algunas cosas por revisar, pero será mas fácil hacerlo por chat. ¿Estás listo para comenzar?',
+ letsChatText: '¡Gracias por hacer eso! TodavÃa tenemos que solucionar un par de cosas, pero será más fácil por chat. ¿Listo para charlar?',
letsChatTitle: '¡Vamos a chatear!',
},
beneficialOwnersStep: {
@@ -623,9 +623,18 @@ export default {
},
workspace: {
common: {
- card: 'Tarjeta Expensify',
+ card: 'Emitir tarjetas corporativas',
workspace: 'Espacio de trabajo',
edit: 'Editar espacio de trabajo',
+ settings: 'Configuración general',
+ reimburse: 'Reembolsar recibos',
+ bills: 'Pagar facturas',
+ invoices: 'Enviar facturas',
+ travel: 'Reservar viaje',
+ members: 'Gestionar miembros',
+ bankAccount: 'Conectar cuenta bancaria',
+ issueAndManageCards: 'Emitir y gestionar tarjetas',
+ reconcileCards: 'Reconciliar tarjetas',
},
new: {
newWorkspace: 'Nuevo espacio de trabajo',
@@ -639,14 +648,55 @@ export default {
selectAll: 'Seleccionar todo',
},
card: {
- addEmail: 'Agregar correo electrónico',
- tagline: 'La tarjeta corporativa más inteligente de la habitación.',
- publicCopy: 'Para utilizar la Tarjeta Expensify debe utilizar el dominio privado de su empresa. Continúe y agregue su dirección de correo electrónico privada como inicio de sesión secundario.',
- privateCopy: 'Simplemente deslice su Tarjeta Expensify y sus gastos estarán listos, ¡es asà de simple!',
- getStarted: 'Empezar',
- finishSetup: 'Finalizar configuración',
- manageCards: 'Administrar tarjetas',
- cardReadyTagline: 'Tus tarjetas Expensify están listas para usar!',
+ header: 'Desbloquea Tarjetas Expensify gratis',
+ headerWithEcard: '¡Tus tarjetas están listas!',
+ noVBACopy: 'Conecta una cuenta bancaria para emitir Tarjetas Expensify ilimitadas para los miembros de tu espacio de trabajo y acceder a todas estas increÃbles ventajas:',
+ VBANoECardCopy: 'Emite Tarjetas Expensify ilimitadas para los miembros de tu espacio de trabajo y accede a todas estas increÃbles ventajas:',
+ conciergeCanHelp: 'Concierge te puede ayudar a añadir un correo electrónico de trabajo para activar la Tarjeta Expensify.',
+ VBAWithECardCopy: 'Disfruta de todas estas increÃbles ventajas:',
+ benefit1: 'Hasta un 2% de devolución en tus gastos',
+ benefit2: 'Tarjetas digitales y fÃsicas',
+ benefit3: 'Sin responsabilidad personal',
+ benefit4: 'LÃmites personalizables',
+ chatWithConcierge: 'Chatea con Concierge',
+ },
+ reimburse: {
+ captureReceipts: 'Captura recibos',
+ fastReimbursementsHappyMembers: '¡Reembolsos rápidos = miembros felices!',
+ viewAllReceipts: 'Ver todos los recibos',
+ reimburseReceipts: 'Reembolsar recibos',
+ unlockNextDayReimbursements: 'Desbloquea reembolsos diarios',
+ captureNoVBACopyBeforeEmail: 'Pide a los miembros de tu espacio de trabajo que envÃen recibos a ',
+ captureNoVBACopyAfterEmail: ' y descarga la App de Expensify para controlar tus gastos en efectivo sobre la marcha.',
+ unlockNoVBACopy: 'Conecta una cuenta bancaria para reembolsar online a los miembros de tu espacio de trabajo.',
+ fastReimbursementsVBACopy: '¡Todo listo para reembolsar recibos desde tu cuenta bancaria!',
+ },
+ bills: {
+ manageYourBills: 'Gestiona tus facturas',
+ askYourVendorsBeforeEmail: 'Pide a tus proveedores que envÃen sus facturas a ',
+ askYourVendorsAfterEmail: ' y las escanearemos para que las pagues',
+ viewAllBills: 'Ver facturas recibidas',
+ unlockOnlineBillPayment: 'Desbloquea el pago de facturas online',
+ unlockNoVBACopy: '¡Conecta tu cuenta bancaria para pagar tus facturas online de manera gratuita!',
+ hassleFreeBills: '¡Facturas sin complicaciones!',
+ VBACopy: '¡Todo listo para realizar pagos desde tu cuenta bancaria!',
+ },
+ invoices: {
+ invoiceClientsAndCustomers: 'Emite facturas a tus clientes',
+ invoiceFirstSectionCopy: 'EnvÃa facturas detalladas y profesionales directamente a tus clientes desde la app de Expensify.',
+ viewAllInvoices: 'Ver facturas emitidas',
+ unlockOnlineInvoicesCollection: 'Desbloquea el cobro de facturas online',
+ unlockNoVBACopy: 'Conecta tu cuenta bancaria para recibir pagos online de facturas - por transferencia o con tarjeta - directamente en tu cuenta.',
+ moneyBackInAFlash: '¡Tu dinero de vuelta en un momento!',
+ unlockVBACopy: '¡Todo listo para recibir pagos por transferencia o con tarjeta!',
+ viewUnpaidInvoices: 'Ver facturas emitidas pendientes',
+ },
+ travel: {
+ unlockConciergeBookingTravel: 'Desbloquea la reserva de viajes con Concierge',
+ noVBACopy: 'Conecta tu cuenta bancaria para permitir a los miembros de tu espacio de trabajo reservar sus vuelos, hoteles y coches empezando una conversación con Concierge.',
+ packYourBags: '¡Haz las maletas!',
+ VBACopy: '¡Miembros con la tarjeta Expensify pueden hablar con Concierge para reservar viajes!',
+ bookTravelWithConcierge: 'Reserva viajes con Concierge',
},
invite: {
invitePeople: 'Invitar nuevos miembros',
@@ -663,12 +713,24 @@ export default {
editor: {
nameInputLabel: 'Nombre',
nameInputHelpText: 'Este es el nombre que verás en tu espacio de trabajo.',
+ nameIsRequiredError: 'Debes definir un nombre para tu espacio de trabajo.',
+ currencyInputLabel: 'Moneda por defecto',
+ currencyInputHelpText: 'Todas los gastos en este epecio de trabajo serán convertidos a esta moneda.',
save: 'Guardar',
genericFailureMessage: 'Se produjo un error al guardar el espacio de trabajo. Por favor, inténtalo de nuevo.',
avatarUploadFailureMessage: 'No se pudo subir el avatar. Por favor, inténtalo de nuevo.',
},
- error: {
- growlMessageInvalidPolicy: '¡Espacio de trabajo no válido!',
+ bankAccount: {
+ continueWithSetup: 'Continuar con la configuración',
+ youreAlmostDone: 'Casi has acabado de configurar tu cuenta bancaria, que te permitirá emitir tarjetas corporativas, reembolsar gastos y cobrar pagar facturas, todo desde la misma cuenta bancaria.',
+ streamlinePayments: 'Optimiza pagos',
+ oneMoreThing: '¡Una cosa más!',
+ allSet: '¡Todo listo!',
+ accountDescriptionNoCards: 'Esta cuenta bancaria se utilizará para reembolsar gastos y cobrar y pagar facturas, todo desde la misma cuenta. Concierge puede ayudarte a añadir tu correo de trabajo para activar la Tarjeta Expensify.',
+ accountDescriptionWithCards: 'Esta cuenta bancaria se utilizará para emitir tarjetas corporativas, reembolsar gastos y cobrar y pagar facturas, todo desde la misma cuenta.',
+ chatWithConcierge: 'Chat con Concierge',
+ letsFinishInChat: '¡Acabemos en el chat!',
+ almostDone: '¡Casi listo!',
},
},
requestCallPage: {
diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.js b/src/libs/Navigation/AppNavigator/AuthScreens.js
index 72f26868fa7..10f22a0ca3c 100644
--- a/src/libs/Navigation/AppNavigator/AuthScreens.js
+++ b/src/libs/Navigation/AppNavigator/AuthScreens.js
@@ -6,7 +6,7 @@ import Str from 'expensify-common/lib/str';
import moment from 'moment';
import _ from 'underscore';
import lodashGet from 'lodash/get';
-import styles, {getNavigationModalCardStyle} from '../../../styles/styles';
+import {getNavigationModalCardStyle} from '../../../styles/styles';
import withWindowDimensions, {windowDimensionsPropTypes} from '../../../components/withWindowDimensions';
import CONST from '../../../CONST';
import compose from '../../compose';
@@ -55,18 +55,13 @@ import {
SettingsModalStackNavigator,
EnablePaymentsStackNavigator,
AddPersonalBankAccountModalStackNavigator,
- ReimbursementAccountModalStackNavigator,
WorkspaceInviteModalStackNavigator,
RequestCallModalStackNavigator,
ReportDetailsModalStackNavigator,
- WorkspaceEditorNavigator,
} from './ModalStackNavigators';
import SCREENS from '../../../SCREENS';
import Timers from '../../Timers';
import LogInWithShortLivedTokenPage from '../../../pages/LogInWithShortLivedTokenPage';
-import WorkspaceSettingsDrawerNavigator from './WorkspaceSettingsDrawerNavigator';
-import spacing from '../../../styles/utilities/spacing';
-import CardOverlay from '../../../components/CardOverlay';
import defaultScreenOptions from './defaultScreenOptions';
import * as API from '../../API';
import {setLocale} from '../../actions/App';
@@ -250,17 +245,6 @@ class AuthScreens extends React.Component {
// when displaying a modal. This allows us to dismiss by clicking outside on web / large screens.
isModal: true,
};
- const fullscreenModalScreenOptions = {
- ...commonModalScreenOptions,
- cardStyle: {
- ...styles.fullscreenCard,
- padding: this.props.isSmallScreenWidth ? spacing.p0.padding : spacing.p5.padding,
- },
- cardStyleInterpolator: props => modalCardStyleInterpolator(this.props.isSmallScreenWidth, true, props),
- cardOverlayEnabled: !this.props.isSmallScreenWidth,
- isFullScreenModal: true,
- cardOverlay: CardOverlay,
- };
return (
-
-
);
}
diff --git a/src/libs/Navigation/AppNavigator/MainDrawerNavigator.js b/src/libs/Navigation/AppNavigator/MainDrawerNavigator.js
index fafe54572ab..fac1a19acae 100644
--- a/src/libs/Navigation/AppNavigator/MainDrawerNavigator.js
+++ b/src/libs/Navigation/AppNavigator/MainDrawerNavigator.js
@@ -50,7 +50,7 @@ const MainDrawerNavigator = (props) => {
// Wait until reports are fetched and there is a reportID in initialParams
if (!initialParams.reportID) {
- return ;
+ return ;
}
// After the app initializes and reports are available the home navigation is mounted
diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js
index d64960f6feb..10fd8b38d11 100644
--- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js
+++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js
@@ -28,7 +28,16 @@ import WorkspaceInvitePage from '../../../pages/workspace/WorkspaceInvitePage';
import ReimbursementAccountPage from '../../../pages/ReimbursementAccount/ReimbursementAccountPage';
import RequestCallPage from '../../../pages/RequestCallPage';
import ReportDetailsPage from '../../../pages/ReportDetailsPage';
-import WorkspaceEditorPage from '../../../pages/workspace/WorkspaceEditorPage';
+import WorkspaceSettingsPage from '../../../pages/workspace/WorkspaceSettingsPage';
+import WorkspaceInitialPage from '../../../pages/workspace/WorkspaceInitialPage';
+import WorkspaceCardPage from '../../../pages/workspace/card/WorkspaceCardPage';
+import WorkspaceReimbursePage from '../../../pages/workspace/reimburse/WorkspaceReimbursePage';
+import WorkspaceInvoicesPage from '../../../pages/workspace/invoices/WorkspaceInvoicesPage';
+import WorkspaceBillsPage from '../../../pages/workspace/bills/WorkspaceBillsPage';
+import WorkspaceTravelPage from '../../../pages/workspace/travel/WorkspaceTravelPage';
+import WorkspaceMembersPage from '../../../pages/workspace/WorkspaceMembersPage';
+import WorkspaceBankAccountPage from '../../../pages/workspace/WorkspaceBankAccountPage';
+import CONST from '../../../CONST';
const defaultSubRouteOptions = {
cardStyle: styles.navigationScreenCardStyle,
@@ -55,6 +64,7 @@ function createModalStackNavigator(screens) {
key={screen.name}
name={screen.name}
component={screen.Component}
+ initialParams={screen.initialParams}
/>
))}
@@ -170,6 +180,47 @@ const SettingsModalStackNavigator = createModalStackNavigator([
Component: SettingsAddDebitCardPage,
name: 'Settings_Add_Debit_Card',
},
+ {
+ Component: WorkspaceInitialPage,
+ name: 'Workspace_Initial',
+ },
+ {
+ Component: WorkspaceSettingsPage,
+ name: 'Workspace_Settings',
+ },
+ {
+ Component: WorkspaceCardPage,
+ name: 'Workspace_Card',
+ },
+ {
+ Component: WorkspaceReimbursePage,
+ name: 'Workspace_Reimburse',
+ },
+ {
+ Component: WorkspaceBillsPage,
+ name: 'Workspace_Bills',
+ },
+ {
+ Component: WorkspaceInvoicesPage,
+ name: 'Workspace_Invoices',
+ },
+ {
+ Component: WorkspaceTravelPage,
+ name: 'Workspace_Travel',
+ },
+ {
+ Component: WorkspaceMembersPage,
+ name: 'Workspace_Members',
+ },
+ {
+ Component: WorkspaceBankAccountPage,
+ name: 'Workspace_BankAccount',
+ },
+ {
+ Component: ReimbursementAccountPage,
+ name: 'ReimbursementAccount',
+ initialParams: {stepToOpen: CONST.BANK_ACCOUNT.STEP.BANK_ACCOUNT},
+ },
]);
const EnablePaymentsStackNavigator = createModalStackNavigator([{
@@ -197,11 +248,6 @@ const RequestCallModalStackNavigator = createModalStackNavigator([{
name: 'RequestCall_Root',
}]);
-const WorkspaceEditorNavigator = createModalStackNavigator([{
- Component: WorkspaceEditorPage,
- name: 'WorkspaceEditor_Root',
-}]);
-
export {
IOUBillStackNavigator,
IOURequestModalStackNavigator,
@@ -219,5 +265,4 @@ export {
ReimbursementAccountModalStackNavigator,
WorkspaceInviteModalStackNavigator,
RequestCallModalStackNavigator,
- WorkspaceEditorNavigator,
};
diff --git a/src/libs/Navigation/AppNavigator/WorkspaceSettingsDrawerNavigator.js b/src/libs/Navigation/AppNavigator/WorkspaceSettingsDrawerNavigator.js
deleted file mode 100644
index 589dd6bda3c..00000000000
--- a/src/libs/Navigation/AppNavigator/WorkspaceSettingsDrawerNavigator.js
+++ /dev/null
@@ -1,30 +0,0 @@
-import React from 'react';
-
-// Screens
-import BaseDrawerNavigator from './BaseDrawerNavigator';
-import WorkspaceCardPage from '../../../pages/workspace/WorkspaceCardPage';
-import WorkspacePeoplePage from '../../../pages/workspace/WorkspacePeoplePage';
-import WorkspaceSidebar from '../../../pages/workspace/WorkspaceSidebar';
-
-const WorkspaceSettingsDrawerNavigator = () => (
- }
- screens={[
- {
- name: 'WorkspaceCard',
- component: WorkspaceCardPage,
- initialParams: {},
- },
- {
- name: 'WorkspacePeople',
- component: WorkspacePeoplePage,
- initialParams: {},
- },
- ]}
- />
-);
-
-WorkspaceSettingsDrawerNavigator.displayName = 'WorkspaceSettingsDrawerNavigator';
-
-export default WorkspaceSettingsDrawerNavigator;
diff --git a/src/libs/Navigation/NavigationRoot.js b/src/libs/Navigation/NavigationRoot.js
index ad516ebf0f5..fb8134d55f7 100644
--- a/src/libs/Navigation/NavigationRoot.js
+++ b/src/libs/Navigation/NavigationRoot.js
@@ -43,7 +43,7 @@ class NavigationRoot extends Component {
render() {
return (
}
+ fallback={}
onStateChange={this.parseAndStoreRoute}
ref={navigationRef}
linking={linkingConfig}
diff --git a/src/libs/Navigation/linkingConfig.js b/src/libs/Navigation/linkingConfig.js
index 7f246559de0..a9a4fd1ceb9 100644
--- a/src/libs/Navigation/linkingConfig.js
+++ b/src/libs/Navigation/linkingConfig.js
@@ -75,6 +75,38 @@ export default {
Settings_Add_Secondary_Login: {
path: ROUTES.SETTINGS_ADD_LOGIN,
},
+ Workspace_Initial: {
+ path: ROUTES.WORKSPACE_INITIAL,
+ },
+ Workspace_Settings: {
+ path: ROUTES.WORKSPACE_SETTINGS,
+ },
+ Workspace_Card: {
+ path: ROUTES.WORKSPACE_CARD,
+ },
+ Workspace_Reimburse: {
+ path: ROUTES.WORKSPACE_REIMBURSE,
+ },
+ Workspace_Bills: {
+ path: ROUTES.WORKSPACE_BILLS,
+ },
+ Workspace_Invoices: {
+ path: ROUTES.WORKSPACE_INVOICES,
+ },
+ Workspace_Travel: {
+ path: ROUTES.WORKSPACE_TRAVEL,
+ },
+ Workspace_Members: {
+ path: ROUTES.WORKSPACE_MEMBERS,
+ },
+ Workspace_BankAccount: {
+ path: ROUTES.WORKSPACE_BANK_ACCOUNT,
+ exact: true,
+ },
+ ReimbursementAccount: {
+ path: ROUTES.BANK_ACCOUNT,
+ exact: true,
+ },
},
},
Report_Details: {
@@ -141,31 +173,11 @@ export default {
EnablePayments_Root: ROUTES.ENABLE_PAYMENTS,
},
},
- ReimbursementAccount: {
- screens: {
- ReimbursementAccount_Root: ROUTES.BANK_ACCOUNT,
- },
- },
WorkspaceInvite: {
screens: {
WorkspaceInvite_Root: ROUTES.WORKSPACE_INVITE,
},
},
-
- WorkspaceSettings: {
- path: ROUTES.WORKSPACE,
- screens: {
- WorkspaceCard: ROUTES.WORKSPACE_CARD,
- WorkspacePeople: ROUTES.WORKSPACE_PEOPLE,
- },
- },
-
- WorkspaceEditor: {
- screens: {
- WorkspaceEditor_Root: ROUTES.WORKSPACE_EDITOR,
- },
- },
-
RequestCall: {
screens: {
RequestCall_Root: ROUTES.REQUEST_CALL,
diff --git a/src/libs/actions/BankAccounts.js b/src/libs/actions/BankAccounts.js
index 83ae72680dd..d23a1f91168 100644
--- a/src/libs/actions/BankAccounts.js
+++ b/src/libs/actions/BankAccounts.js
@@ -64,7 +64,7 @@ function goToWithdrawalAccountSetupStep(stepID, achData) {
if (!newACHData.useOnfido && stepID === CONST.BANK_ACCOUNT.STEP.REQUESTOR) {
delete newACHData.questions;
delete newACHData.answers;
- if (lodashHas(achData, CONST.BANK_ACCOUNT.VERIFICATIONS.EXTERNAL_API_RESPONSES)) {
+ if (lodashHas(newACHData, CONST.BANK_ACCOUNT.VERIFICATIONS.EXTERNAL_API_RESPONSES)) {
delete newACHData.verifications.externalApiResponses.requestorIdentityID;
delete newACHData.verifications.externalApiResponses.requestorIdentityKBA;
}
@@ -115,6 +115,7 @@ function getPlaidBankAccounts(publicToken, bank) {
...account,
accountNumber: Str.maskPAN(account.accountNumber),
})),
+ bankName,
});
});
}
@@ -336,6 +337,9 @@ function fetchUserWallet() {
* @param {String} [stepToOpen]
*/
function fetchFreePlanVerifiedBankAccount(stepToOpen) {
+ // Remember which account BankAccountStep subStep the user had before so we can set it later
+ const subStep = lodashGet(reimbursementAccountInSetup, 'subStep', '');
+
// We are using set here since we will rely on data from the server (not local data) to populate the VBA flow
// and determine which step to navigate to.
Onyx.set(ONYXKEYS.REIMBURSEMENT_ACCOUNT, {loading: true, error: ''});
@@ -379,6 +383,11 @@ function fetchFreePlanVerifiedBankAccount(stepToOpen) {
// If the user is already setting up a bank account we will continue the flow for them
let currentStep = reimbursementAccountInSetup.currentStep;
const achData = bankAccount ? bankAccount.toACHData() : {};
+ if (!stepToOpen && achData.currentStep) {
+ // eslint-disable-next-line no-use-before-define
+ currentStep = getNextStepToComplete(achData);
+ }
+
achData.useOnfido = true;
achData.policyID = reimbursementAccountWorkspaceID || '';
achData.isInSetup = !bankAccount || bankAccount.isInSetup();
@@ -386,9 +395,13 @@ function fetchFreePlanVerifiedBankAccount(stepToOpen) {
achData.domainLimit = 0;
// If the bank account has already been created in the db and is not yet open
- // let's show the manual form with the previously added values
- achData.subStep = bankAccount && bankAccount.isInSetup()
- && CONST.BANK_ACCOUNT.SETUP_TYPE.MANUAL;
+ // let's show the manual form with the previously added values. Otherwise, we will
+ // make the subStep the previous value.
+ if (bankAccount && bankAccount.isInSetup()) {
+ achData.subStep = CONST.BANK_ACCOUNT.SETUP_TYPE.MANUAL;
+ } else {
+ achData.subStep = subStep;
+ }
// If we're not in setup, it means we already have a withdrawal account
// and we're upgrading it to a business bank account. So let the user
@@ -504,16 +517,29 @@ function getIndexByStepID(stepID) {
/**
* Get next step ID
+ * @param {String} [stepID]
* @return {String}
*/
-function getNextStepID() {
+function getNextStepID(stepID) {
const nextStepIndex = Math.min(
- getIndexByStepID(reimbursementAccountInSetup.currentStep) + 1,
+ getIndexByStepID(stepID || reimbursementAccountInSetup.currentStep) + 1,
WITHDRAWAL_ACCOUNT_STEPS.length - 1,
);
return lodashGet(WITHDRAWAL_ACCOUNT_STEPS, [nextStepIndex, 'id'], CONST.BANK_ACCOUNT.STEP.BANK_ACCOUNT);
}
+/**
+ * @param {Object} achData
+ * @returns {String}
+ */
+function getNextStepToComplete(achData) {
+ if (achData.currentStep === CONST.BANK_ACCOUNT.STEP.REQUESTOR && !achData.isOnfidoSetupComplete) {
+ return CONST.BANK_ACCOUNT.STEP.REQUESTOR;
+ }
+
+ return getNextStepID(achData.currentStep);
+}
+
/**
* @private
* @param {Number} bankAccountID
@@ -651,7 +677,7 @@ function setupWithdrawalAccount(data) {
API.BankAccount_SetupWithdrawal(newACHData)
.then((response) => {
- Onyx.merge(ONYXKEYS.REIMBURSEMENT_ACCOUNT, {loading: false, achData: {...newACHData}});
+ Onyx.merge(ONYXKEYS.REIMBURSEMENT_ACCOUNT, {achData: {...newACHData}});
const currentStep = newACHData.currentStep;
let achData = lodashGet(response, 'achData', {});
let error = lodashGet(achData, CONST.BANK_ACCOUNT.VERIFICATIONS.ERROR_MESSAGE);
@@ -720,6 +746,7 @@ function setupWithdrawalAccount(data) {
|| achData.state === BankAccount.STATE.VERIFYING;
goToWithdrawalAccountSetupStep(CONST.BANK_ACCOUNT.STEP.VALIDATION, achData);
+ Onyx.merge(ONYXKEYS.REIMBURSEMENT_ACCOUNT, {loading: false});
});
return;
}
@@ -763,6 +790,7 @@ function setupWithdrawalAccount(data) {
showBankAccountFormValidationError(error);
showBankAccountErrorModal(error);
}
+ Onyx.merge(ONYXKEYS.REIMBURSEMENT_ACCOUNT, {loading: false});
})
.catch((response) => {
Onyx.merge(ONYXKEYS.REIMBURSEMENT_ACCOUNT, {loading: false, achData: {...newACHData}});
diff --git a/src/libs/actions/PaymentMethods.js b/src/libs/actions/PaymentMethods.js
index 3deddf4fde1..d9467327244 100644
--- a/src/libs/actions/PaymentMethods.js
+++ b/src/libs/actions/PaymentMethods.js
@@ -15,6 +15,7 @@ import {maskCardNumber} from '../cardUtils';
* @returns {Promise}
*/
function getPaymentMethods() {
+ Onyx.set(ONYXKEYS.IS_LOADING_PAYMENT_METHODS, true);
return API.Get({
returnValueList: 'bankAccountList, fundList, userWallet, nameValuePairs',
name: 'paypalMeAddress',
@@ -24,6 +25,7 @@ function getPaymentMethods() {
})
.then((response) => {
Onyx.multiSet({
+ [ONYXKEYS.IS_LOADING_PAYMENT_METHODS]: false,
[ONYXKEYS.USER_WALLET]: lodashGet(response, 'userWallet', {}),
[ONYXKEYS.BANK_ACCOUNT_LIST]: lodashGet(response, 'bankAccountList', []),
[ONYXKEYS.CARD_LIST]: lodashGet(response, 'fundList', []),
diff --git a/src/libs/actions/Policy.js b/src/libs/actions/Policy.js
index 289436cfee3..64319477989 100644
--- a/src/libs/actions/Policy.js
+++ b/src/libs/actions/Policy.js
@@ -46,6 +46,7 @@ function getSimplifiedEmployeeList(employeeList) {
* @param {String} fullPolicy.name
* @param {String} fullPolicy.role
* @param {String} fullPolicy.type
+ * @param {String} fullPolicy.value.outputCurrency
* @param {Object} fullPolicy.value.employeeList
* @param {String} [fullPolicy.value.avatarURL]
* @returns {Object}
@@ -56,6 +57,7 @@ function getSimplifiedPolicyObject(fullPolicy) {
name: fullPolicy.name,
role: fullPolicy.role,
type: fullPolicy.type,
+ outputCurrency: lodashGet(fullPolicy, 'value.outputCurrency', ''),
employeeList: getSimplifiedEmployeeList(lodashGet(fullPolicy, 'value.employeeList')),
avatarURL: lodashGet(fullPolicy, 'value.avatarURL', ''),
};
@@ -118,6 +120,7 @@ function create(name = '', shouldAutomaticallyReroute = true) {
type: response.policy.type,
name: response.policy.name,
role: CONST.POLICY.ROLE.ADMIN,
+ outputCurrency: response.policy.outputCurrency,
});
}).then(() => {
const policyID = lodashGet(res, 'policyID');
@@ -305,13 +308,14 @@ function update(policyID, values) {
// Show the user feedback
const errorMessage = translateLocal('workspace.editor.genericFailureMessage');
Growl.error(errorMessage, 5000);
+ Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {isPolicyUpdating: false});
return;
}
const updatedValues = {...values, ...{isPolicyUpdating: false}};
Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, updatedValues);
- Navigation.dismissModal();
}).catch(() => {
+ Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {isPolicyUpdating: false});
const errorMessage = translateLocal('workspace.editor.genericFailureMessage');
Growl.error(errorMessage, 5000);
});
diff --git a/src/pages/DetailsPage.js b/src/pages/DetailsPage.js
index ce36dad38b3..8921bc707a3 100755
--- a/src/pages/DetailsPage.js
+++ b/src/pages/DetailsPage.js
@@ -77,7 +77,7 @@ const DetailsPage = ({
Navigation.goBack()}
onCloseButtonPress={() => Navigation.dismissModal()}
/>
setBankAccountSubStep(null)}
- shouldShowBackButton={Boolean(subStep)}
+ onBackButtonPress={() => {
+ // If we have a subStep then we will remove otherwise we will go back
+ if (subStep) {
+ setBankAccountSubStep(null);
+ return;
+ }
+ Navigation.goBack();
+ }}
+ shouldShowBackButton
/>
{!subStep && (
<>
+
{this.props.translate('bankAccount.toGetStarted')}