diff --git a/assets/images/empty-state__attach-receipt.svg b/assets/images/empty-state__attach-receipt.svg new file mode 100644 index 000000000000..6b50afbdbf0b --- /dev/null +++ b/assets/images/empty-state__attach-receipt.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/src/components/Badge.js b/src/components/Badge.js deleted file mode 100644 index 49b330ae37b2..000000000000 --- a/src/components/Badge.js +++ /dev/null @@ -1,79 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import {View} from 'react-native'; -import styles from '@styles/styles'; -import * as StyleUtils from '@styles/StyleUtils'; -import CONST from '@src/CONST'; -import PressableWithoutFeedback from './Pressable/PressableWithoutFeedback'; -import Text from './Text'; - -const propTypes = { - /** Is Success type */ - success: PropTypes.bool, - - /** Is Error type */ - error: PropTypes.bool, - - /** Whether badge is clickable */ - pressable: PropTypes.bool, - - /** Text to display in the Badge */ - text: PropTypes.string.isRequired, - - /** Text to display in the Badge */ - environment: PropTypes.string, - - /** Styles for Badge */ - // eslint-disable-next-line react/forbid-prop-types - badgeStyles: PropTypes.arrayOf(PropTypes.object), - - /** Styles for Badge Text */ - // eslint-disable-next-line react/forbid-prop-types - textStyles: PropTypes.arrayOf(PropTypes.object), - - /** Callback to be called on onPress */ - onPress: PropTypes.func, -}; - -const defaultProps = { - success: false, - error: false, - pressable: false, - badgeStyles: [], - textStyles: [], - onPress: undefined, - environment: CONST.ENVIRONMENT.DEV, -}; - -function Badge(props) { - const textStyles = props.success || props.error ? styles.textWhite : undefined; - const Wrapper = props.pressable ? PressableWithoutFeedback : View; - const wrapperStyles = ({pressed}) => [ - styles.badge, - styles.ml2, - StyleUtils.getBadgeColorStyle(props.success, props.error, pressed, props.environment === CONST.ENVIRONMENT.ADHOC), - ...props.badgeStyles, - ]; - - return ( - - - {props.text} - - - ); -} - -Badge.displayName = 'Badge'; -Badge.propTypes = propTypes; -Badge.defaultProps = defaultProps; -export default Badge; diff --git a/src/components/Badge.tsx b/src/components/Badge.tsx new file mode 100644 index 000000000000..2ccd41575073 --- /dev/null +++ b/src/components/Badge.tsx @@ -0,0 +1,65 @@ +import React, {useCallback} from 'react'; +import {GestureResponderEvent, PressableStateCallbackType, StyleProp, TextStyle, View, ViewStyle} from 'react-native'; +import styles from '@styles/styles'; +import * as StyleUtils from '@styles/StyleUtils'; +import CONST from '@src/CONST'; +import PressableWithoutFeedback from './Pressable/PressableWithoutFeedback'; +import Text from './Text'; + +type BadgeProps = { + /** Is Success type */ + success?: boolean; + + /** Is Error type */ + error?: boolean; + + /** Whether badge is clickable */ + pressable?: boolean; + + /** Text to display in the Badge */ + text: string; + + /** Text to display in the Badge */ + environment?: string; + + /** Styles for Badge */ + badgeStyles?: StyleProp; + + /** Styles for Badge Text */ + textStyles?: StyleProp; + + /** Callback to be called on onPress */ + onPress: (event?: GestureResponderEvent | KeyboardEvent) => void; +}; + +function Badge({success = false, error = false, pressable = false, text, environment = CONST.ENVIRONMENT.DEV, badgeStyles, textStyles, onPress = () => {}}: BadgeProps) { + const textColorStyles = success || error ? styles.textWhite : undefined; + const Wrapper = pressable ? PressableWithoutFeedback : View; + + const wrapperStyles: (state: PressableStateCallbackType) => StyleProp = useCallback( + ({pressed}) => [styles.badge, styles.ml2, StyleUtils.getBadgeColorStyle(success, error, pressed, environment === CONST.ENVIRONMENT.ADHOC), badgeStyles], + [success, error, environment, badgeStyles], + ); + + return ( + + + {text} + + + ); +} + +Badge.displayName = 'Badge'; + +export default Badge; diff --git a/src/components/Icon/Expensicons.ts b/src/components/Icon/Expensicons.ts index a4aa6b13cb29..3d4f0edb1656 100644 --- a/src/components/Icon/Expensicons.ts +++ b/src/components/Icon/Expensicons.ts @@ -45,6 +45,7 @@ import Download from '@assets/images/download.svg'; import DragAndDrop from '@assets/images/drag-and-drop.svg'; import DragHandles from '@assets/images/drag-handles.svg'; import Emoji from '@assets/images/emoji.svg'; +import EmptyStateAttachReceipt from '@assets/images/empty-state__attach-receipt.svg'; import EmptyStateRoutePending from '@assets/images/emptystate__routepending.svg'; import EReceiptIcon from '@assets/images/eReceiptIcon.svg'; import Exclamation from '@assets/images/exclamation.svg'; @@ -176,6 +177,7 @@ export { EReceiptIcon, Emoji, EmptyStateRoutePending, + EmptyStateAttachReceipt, Exclamation, Exit, ExpensifyCard, diff --git a/src/components/ReceiptEmptyState.js b/src/components/ReceiptEmptyState.js new file mode 100644 index 000000000000..ef2f6a5b62e4 --- /dev/null +++ b/src/components/ReceiptEmptyState.js @@ -0,0 +1,44 @@ +import PropTypes from 'prop-types'; +import React from 'react'; +import styles from '@styles/styles'; +import variables from '@styles/variables'; +import Icon from './Icon'; +import * as Expensicons from './Icon/Expensicons'; +import PressableWithoutFeedback from './Pressable/PressableWithoutFeedback'; + +const propTypes = { + /** Whether or not there is an error */ + hasError: PropTypes.bool, + + /** Callback to be called on onPress */ + onPress: PropTypes.func, +}; + +const defaultProps = { + hasError: false, + onPress: () => {}, +}; + +// Returns an SVG icon indicating that the user should attach a receipt +function ReceiptEmptyState({hasError, onPress}) { + return ( + + + + ); +} + +ReceiptEmptyState.displayName = 'ReceiptEmptyState'; +ReceiptEmptyState.propTypes = propTypes; +ReceiptEmptyState.defaultProps = defaultProps; + +export default ReceiptEmptyState; diff --git a/src/components/ReportActionItem/MoneyRequestView.js b/src/components/ReportActionItem/MoneyRequestView.js index f1fc51bd2be8..5b9a1b2212c7 100644 --- a/src/components/ReportActionItem/MoneyRequestView.js +++ b/src/components/ReportActionItem/MoneyRequestView.js @@ -8,6 +8,7 @@ import categoryPropTypes from '@components/categoryPropTypes'; import * as Expensicons from '@components/Icon/Expensicons'; import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; +import ReceiptEmptyState from '@components/ReceiptEmptyState'; import SpacerView from '@components/SpacerView'; import Switch from '@components/Switch'; import tagPropTypes from '@components/tagPropTypes'; @@ -172,6 +173,12 @@ function MoneyRequestView({report, parentReport, policyCategories, shouldShowHor )} + {!hasReceipt && canEdit && !isSettled && Permissions.canUseViolations() && ( + Navigation.navigate(ROUTES.EDIT_REQUEST.getRoute(report.reportID, CONST.EDIT_REQUEST_FIELD.RECEIPT))} + /> + )} maxWidth: 400, }, + moneyRequestAttachReceipt: { + backgroundColor: theme.appBG, + borderColor: theme.textSupporting, + }, + mapViewContainer: { ...flex.flex1, minHeight: 300,