From e90e0085e5e300fd841c6cf4cfafd7d56b71f288 Mon Sep 17 00:00:00 2001 From: Dylan Date: Mon, 29 Jan 2024 16:04:33 +0700 Subject: [PATCH 001/167] remove NewDistanceRequestPage and EditRequestDistancePage --- src/ROUTES.ts | 10 +- .../MoneyRequestConfirmationList.js | 12 -- ...oraryForRefactorRequestConfirmationList.js | 10 +- .../ReportActionItem/MoneyRequestView.js | 12 +- .../AppNavigator/ModalStackNavigators.tsx | 1 - src/libs/Navigation/linkingConfig.ts | 1 - src/libs/Navigation/types.ts | 5 +- src/pages/EditRequestDistancePage.js | 122 ------------------ src/pages/EditRequestPage.js | 11 -- src/pages/iou/MoneyRequestSelectorPage.js | 12 -- src/pages/iou/NewDistanceRequestPage.js | 85 ------------ 11 files changed, 27 insertions(+), 254 deletions(-) delete mode 100644 src/pages/EditRequestDistancePage.js delete mode 100644 src/pages/iou/NewDistanceRequestPage.js diff --git a/src/ROUTES.ts b/src/ROUTES.ts index deabdc0ac853..b985f993367a 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -295,10 +295,6 @@ const ROUTES = { route: ':iouType/new/receipt/:reportID?', getRoute: (iouType: string, reportID = '') => `${iouType}/new/receipt/${reportID}` as const, }, - MONEY_REQUEST_DISTANCE: { - route: ':iouType/new/address/:reportID?', - getRoute: (iouType: string, reportID = '') => `${iouType}/new/address/${reportID}` as const, - }, MONEY_REQUEST_DISTANCE_TAB: { route: ':iouType/new/:reportID?/distance', getRoute: (iouType: string, reportID = '') => `${iouType}/new/${reportID}/distance` as const, @@ -350,9 +346,9 @@ const ROUTES = { getUrlWithBackToParam(`create/${iouType}/description/${transactionID}/${reportID}`, backTo), }, MONEY_REQUEST_STEP_DISTANCE: { - route: 'create/:iouType/distance/:transactionID/:reportID', - getRoute: (iouType: ValueOf, transactionID: string, reportID: string, backTo = '') => - getUrlWithBackToParam(`create/${iouType}/distance/${transactionID}/${reportID}`, backTo), + route: ':action/:iouType/distance/:transactionID/:reportID', + getRoute: (action: ValueOf, iouType: ValueOf, transactionID: string, reportID: string, backTo = '') => + getUrlWithBackToParam(`${action}/${iouType}/distance/${transactionID}/${reportID}`, backTo), }, MONEY_REQUEST_STEP_MERCHANT: { route: 'create/:iouType/merchant/:transactionID/:reportID', diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.js index d967d04ab94b..101e135d36e1 100755 --- a/src/components/MoneyRequestConfirmationList.js +++ b/src/components/MoneyRequestConfirmationList.js @@ -703,18 +703,6 @@ function MoneyRequestConfirmationList(props) { error={shouldDisplayFieldError && TransactionUtils.isCreatedMissing(transaction) ? translate('common.error.enterDate') : ''} /> )} - {props.isDistanceRequest && ( - Navigation.navigate(ROUTES.MONEY_REQUEST_DISTANCE.getRoute(props.iouType, props.reportID))} - disabled={didConfirm || !isTypeRequest} - interactive={!props.isReadOnly} - /> - )} {shouldShowMerchant && ( - Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_DISTANCE.getRoute(iouType, transaction.transactionID, reportID, Navigation.getActiveRouteWithoutParams())) + Navigation.navigate( + ROUTES.MONEY_REQUEST_STEP_DISTANCE.getRoute( + CONST.IOU.ACTION.CREATE, + CONST.IOU.TYPE.REQUEST, + transaction.transactionID, + reportID, + Navigation.getActiveRouteWithoutParams(), + ), + ) } disabled={didConfirm || !isTypeRequest} interactive={!isReadOnly} diff --git a/src/components/ReportActionItem/MoneyRequestView.js b/src/components/ReportActionItem/MoneyRequestView.js index 3121328138ee..6cc2119a9044 100644 --- a/src/components/ReportActionItem/MoneyRequestView.js +++ b/src/components/ReportActionItem/MoneyRequestView.js @@ -301,7 +301,17 @@ function MoneyRequestView({report, parentReport, parentReportActions, policyCate interactive={canEditDistance} shouldShowRightIcon={canEditDistance} titleStyle={styles.flex1} - onPress={() => Navigation.navigate(ROUTES.EDIT_REQUEST.getRoute(report.reportID, CONST.EDIT_REQUEST_FIELD.DISTANCE))} + onPress={() => + Navigation.navigate( + ROUTES.MONEY_REQUEST_STEP_DISTANCE.getRoute( + CONST.IOU.ACTION.EDIT, + CONST.IOU.TYPE.REQUEST, + transaction.transactionID, + report.reportID, + Navigation.getActiveRouteWithoutParams(), + ), + ) + } /> ) : ( diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx index 3a843e400409..9c2361916934 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx @@ -103,7 +103,6 @@ const MoneyRequestModalStackNavigator = createModalStackNavigator require('../../../pages/settings/Wallet/AddDebitCardPage').default as React.ComponentType, [SCREENS.IOU_SEND.ENABLE_PAYMENTS]: () => require('../../../pages/EnablePayments/EnablePaymentsPage').default as React.ComponentType, [SCREENS.MONEY_REQUEST.WAYPOINT]: () => require('../../../pages/iou/MoneyRequestWaypointPage').default as React.ComponentType, - [SCREENS.MONEY_REQUEST.DISTANCE]: () => require('../../../pages/iou/NewDistanceRequestPage').default as React.ComponentType, [SCREENS.MONEY_REQUEST.RECEIPT]: () => require('../../../pages/EditRequestReceiptPage').default as React.ComponentType, }); diff --git a/src/libs/Navigation/linkingConfig.ts b/src/libs/Navigation/linkingConfig.ts index d4e04d5402e2..715b14c4cb90 100644 --- a/src/libs/Navigation/linkingConfig.ts +++ b/src/libs/Navigation/linkingConfig.ts @@ -435,7 +435,6 @@ const linkingConfig: LinkingOptions = { [SCREENS.MONEY_REQUEST.TAG]: ROUTES.MONEY_REQUEST_TAG.route, [SCREENS.MONEY_REQUEST.MERCHANT]: ROUTES.MONEY_REQUEST_MERCHANT.route, [SCREENS.MONEY_REQUEST.RECEIPT]: ROUTES.MONEY_REQUEST_RECEIPT.route, - [SCREENS.MONEY_REQUEST.DISTANCE]: ROUTES.MONEY_REQUEST_DISTANCE.route, [SCREENS.IOU_SEND.ENABLE_PAYMENTS]: ROUTES.IOU_SEND_ENABLE_PAYMENTS, [SCREENS.IOU_SEND.ADD_BANK_ACCOUNT]: ROUTES.IOU_SEND_ADD_BANK_ACCOUNT, [SCREENS.IOU_SEND.ADD_DEBIT_CARD]: ROUTES.IOU_SEND_ADD_DEBIT_CARD, diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index b4a77f96cc74..ea2b48df4ed2 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -238,9 +238,12 @@ type MoneyRequestNavigatorParamList = { waypointIndex: string; threadReportID: number; }; - [SCREENS.MONEY_REQUEST.DISTANCE]: { + [SCREENS.MONEY_REQUEST.STEP_DISTANCE]: { + action: string; iouType: ValueOf; + transactionID: string; reportID: string; + backTo: string; }; [SCREENS.MONEY_REQUEST.RECEIPT]: { iouType: string; diff --git a/src/pages/EditRequestDistancePage.js b/src/pages/EditRequestDistancePage.js deleted file mode 100644 index f3ea76a3390a..000000000000 --- a/src/pages/EditRequestDistancePage.js +++ /dev/null @@ -1,122 +0,0 @@ -import lodashGet from 'lodash/get'; -import PropTypes from 'prop-types'; -import React, {useEffect, useRef} from 'react'; -import {withOnyx} from 'react-native-onyx'; -import _ from 'underscore'; -import DistanceRequest from '@components/DistanceRequest'; -import HeaderWithBackButton from '@components/HeaderWithBackButton'; -import ScreenWrapper from '@components/ScreenWrapper'; -import transactionPropTypes from '@components/transactionPropTypes'; -import useLocalize from '@hooks/useLocalize'; -import useNetwork from '@hooks/useNetwork'; -import usePrevious from '@hooks/usePrevious'; -import Navigation from '@libs/Navigation/Navigation'; -import * as IOU from '@userActions/IOU'; -import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; -import reportPropTypes from './reportPropTypes'; - -const propTypes = { - /** The transactionID we're currently editing */ - transactionID: PropTypes.string.isRequired, - - /** The report to with which the distance request is associated */ - report: reportPropTypes.isRequired, - - /** Passed from the navigator */ - route: PropTypes.shape({ - /** Parameters the route gets */ - params: PropTypes.shape({ - /** Type of IOU */ - iouType: PropTypes.oneOf(_.values(CONST.IOU.TYPE)), - - /** Id of the report on which the distance request is being created */ - reportID: PropTypes.string, - }), - }).isRequired, - - /* Onyx props */ - /** The original transaction that is being edited */ - transaction: transactionPropTypes, - - /** backup version of the original transaction */ - transactionBackup: transactionPropTypes, -}; - -const defaultProps = { - transaction: {}, - transactionBackup: {}, -}; - -function EditRequestDistancePage({report, route, transaction, transactionBackup}) { - const {isOffline} = useNetwork(); - const {translate} = useLocalize(); - const hasWaypointError = useRef(false); - const prevIsLoading = usePrevious(transaction.isLoading); - - useEffect(() => { - hasWaypointError.current = Boolean(lodashGet(transaction, 'errorFields.route') || lodashGet(transaction, 'errorFields.waypoints')); - - // When the loading goes from true to false, then we know the transaction has just been - // saved to the server. Check for errors. If there are no errors, then the modal can be closed. - if (prevIsLoading && !transaction.isLoading && !hasWaypointError.current) { - Navigation.dismissModal(report.reportID); - } - }, [transaction, prevIsLoading, report]); - - /** - * Save the changes to the original transaction object - * @param {Object} waypoints - */ - const saveTransaction = (waypoints) => { - // If nothing was changed, simply go to transaction thread - // We compare only addresses because numbers are rounded while backup - const oldWaypoints = lodashGet(transactionBackup, 'comment.waypoints', {}); - const oldAddresses = _.mapObject(oldWaypoints, (waypoint) => _.pick(waypoint, 'address')); - const addresses = _.mapObject(waypoints, (waypoint) => _.pick(waypoint, 'address')); - if (_.isEqual(oldAddresses, addresses)) { - Navigation.dismissModal(report.reportID); - return; - } - - IOU.updateMoneyRequestDistance(transaction.transactionID, report.reportID, waypoints); - - // If the client is offline, then the modal can be closed as well (because there are no errors or other feedback to show them - // until they come online again and sync with the server). - if (isOffline) { - Navigation.dismissModal(report.reportID); - } - }; - - return ( - - Navigation.goBack()} - /> - - - ); -} - -EditRequestDistancePage.propTypes = propTypes; -EditRequestDistancePage.defaultProps = defaultProps; -EditRequestDistancePage.displayName = 'EditRequestDistancePage'; -export default withOnyx({ - transaction: { - key: (props) => `${ONYXKEYS.COLLECTION.TRANSACTION}${props.transactionID}`, - }, - transactionBackup: { - key: (props) => `${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${props.transactionID}`, - }, -})(EditRequestDistancePage); diff --git a/src/pages/EditRequestPage.js b/src/pages/EditRequestPage.js index 3eb9d88f1120..9f1ba51806e5 100644 --- a/src/pages/EditRequestPage.js +++ b/src/pages/EditRequestPage.js @@ -23,7 +23,6 @@ import EditRequestAmountPage from './EditRequestAmountPage'; import EditRequestCategoryPage from './EditRequestCategoryPage'; import EditRequestCreatedPage from './EditRequestCreatedPage'; import EditRequestDescriptionPage from './EditRequestDescriptionPage'; -import EditRequestDistancePage from './EditRequestDistancePage'; import EditRequestMerchantPage from './EditRequestMerchantPage'; import EditRequestReceiptPage from './EditRequestReceiptPage'; import EditRequestTagPage from './EditRequestTagPage'; @@ -264,16 +263,6 @@ function EditRequestPage({report, route, policyCategories, policyTags, parentRep ); } - if (fieldToEdit === CONST.EDIT_REQUEST_FIELD.DISTANCE) { - return ( - - ); - } - return ( { const moneyRequestID = `${iouType}${reportID}`; @@ -133,13 +128,6 @@ function MoneyRequestSelectorPage(props) { initialParams={{reportID, iouType}} /> {() => } - {shouldDisplayDistanceRequest && ( - - )} ) : ( diff --git a/src/pages/iou/NewDistanceRequestPage.js b/src/pages/iou/NewDistanceRequestPage.js deleted file mode 100644 index 750ac5d0141e..000000000000 --- a/src/pages/iou/NewDistanceRequestPage.js +++ /dev/null @@ -1,85 +0,0 @@ -import lodashGet from 'lodash/get'; -import PropTypes from 'prop-types'; -import React, {useCallback, useEffect} from 'react'; -import {withOnyx} from 'react-native-onyx'; -import _ from 'underscore'; -import DistanceRequest from '@components/DistanceRequest'; -import Navigation from '@libs/Navigation/Navigation'; -import reportPropTypes from '@pages/reportPropTypes'; -import * as IOU from '@userActions/IOU'; -import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; -import {iouPropTypes} from './propTypes'; - -const propTypes = { - /** Holds data related to Money Request view state, rather than the underlying Money Request data. */ - iou: iouPropTypes, - - /** The report on which the request is initiated on */ - report: reportPropTypes, - - /** Passed from the navigator */ - route: PropTypes.shape({ - /** Parameters the route gets */ - params: PropTypes.shape({ - /** Type of IOU */ - iouType: PropTypes.oneOf(_.values(CONST.IOU.TYPE)), - /** Id of the report on which the distance request is being created */ - reportID: PropTypes.string, - }), - }), -}; - -const defaultProps = { - iou: {}, - report: {}, - route: { - params: { - iouType: '', - reportID: '', - }, - }, -}; - -// This component is responsible for getting the transactionID from the IOU key, or creating the transaction if it doesn't exist yet, and then passing the transactionID. -// You can't use Onyx props in the withOnyx mapping, so we need to set up and access the transactionID here, and then pass it down so that DistanceRequest can subscribe to the transaction. -function NewDistanceRequestPage({iou, report, route}) { - const iouType = lodashGet(route, 'params.iouType', 'request'); - const isEditingNewRequest = Navigation.getActiveRoute().includes('address'); - - useEffect(() => { - if (iou.transactionID) { - return; - } - IOU.setUpDistanceTransaction(); - }, [iou.transactionID]); - - const onSubmit = useCallback(() => { - if (isEditingNewRequest) { - Navigation.goBack(ROUTES.MONEY_REQUEST_CONFIRMATION.getRoute(iouType, report.reportID)); - return; - } - IOU.navigateToNextPage(iou, iouType, report); - }, [iou, iouType, isEditingNewRequest, report]); - - return ( - - ); -} - -NewDistanceRequestPage.displayName = 'NewDistanceRequestPage'; -NewDistanceRequestPage.propTypes = propTypes; -NewDistanceRequestPage.defaultProps = defaultProps; -export default withOnyx({ - iou: {key: ONYXKEYS.IOU}, - report: { - key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${lodashGet(route, 'params.reportID')}`, - }, -})(NewDistanceRequestPage); From 4feb09259b4501462807da3bb197aa705e3c1d4f Mon Sep 17 00:00:00 2001 From: ruben-rebelo Date: Tue, 23 Jan 2024 13:44:07 +0000 Subject: [PATCH 002/167] [TS Migration] Migrate WorkspaceCard to Typescript --- .../workspace/card/WorkspaceCardNoVBAView.js | 49 ------------------- .../workspace/card/WorkspaceCardNoVBAView.tsx | 40 +++++++++++++++ src/pages/workspace/card/WorkspaceCardPage.js | 47 ------------------ .../workspace/card/WorkspaceCardPage.tsx | 39 +++++++++++++++ ...iew.js => WorkspaceCardVBANoECardView.tsx} | 48 +++++++----------- ...w.js => WorkspaceCardVBAWithECardView.tsx} | 39 ++++++++------- 6 files changed, 117 insertions(+), 145 deletions(-) delete mode 100644 src/pages/workspace/card/WorkspaceCardNoVBAView.js create mode 100644 src/pages/workspace/card/WorkspaceCardNoVBAView.tsx delete mode 100644 src/pages/workspace/card/WorkspaceCardPage.js create mode 100644 src/pages/workspace/card/WorkspaceCardPage.tsx rename src/pages/workspace/card/{WorkspaceCardVBANoECardView.js => WorkspaceCardVBANoECardView.tsx} (53%) rename src/pages/workspace/card/{WorkspaceCardVBAWithECardView.js => WorkspaceCardVBAWithECardView.tsx} (67%) diff --git a/src/pages/workspace/card/WorkspaceCardNoVBAView.js b/src/pages/workspace/card/WorkspaceCardNoVBAView.js deleted file mode 100644 index 3233f8ea7e23..000000000000 --- a/src/pages/workspace/card/WorkspaceCardNoVBAView.js +++ /dev/null @@ -1,49 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import {View} from 'react-native'; -import ConnectBankAccountButton from '@components/ConnectBankAccountButton'; -import * as Illustrations from '@components/Icon/Illustrations'; -import Section from '@components/Section'; -import Text from '@components/Text'; -import UnorderedList from '@components/UnorderedList'; -import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; -import useThemeStyles from '@hooks/useThemeStyles'; - -const propTypes = { - /** The policy ID currently being configured */ - policyID: PropTypes.string.isRequired, - - ...withLocalizePropTypes, -}; - -function WorkspaceCardNoVBAView(props) { - const styles = useThemeStyles(); - return ( -
- - {props.translate('workspace.card.noVBACopy')} - - - - -
- ); -} - -WorkspaceCardNoVBAView.propTypes = propTypes; -WorkspaceCardNoVBAView.displayName = 'WorkspaceCardNoVBAView'; - -export default withLocalize(WorkspaceCardNoVBAView); diff --git a/src/pages/workspace/card/WorkspaceCardNoVBAView.tsx b/src/pages/workspace/card/WorkspaceCardNoVBAView.tsx new file mode 100644 index 000000000000..322d433a8e62 --- /dev/null +++ b/src/pages/workspace/card/WorkspaceCardNoVBAView.tsx @@ -0,0 +1,40 @@ +import React from 'react'; +import {View} from 'react-native'; +import ConnectBankAccountButton from '@components/ConnectBankAccountButton'; +import * as Illustrations from '@components/Icon/Illustrations'; +import Section from '@components/Section'; +import Text from '@components/Text'; +import UnorderedList from '@components/UnorderedList'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; + +type WorkspaceCardNoVBAViewProps = { + /** The policy ID currently being configured */ + policyID: string; +}; + +function WorkspaceCardNoVBAView({policyID}: WorkspaceCardNoVBAViewProps) { + const styles = useThemeStyles(); + const {translate} = useLocalize(); + + return ( +
+ + {translate('workspace.card.noVBACopy')} + + + + +
+ ); +} + +WorkspaceCardNoVBAView.displayName = 'WorkspaceCardNoVBAView'; + +export default WorkspaceCardNoVBAView; diff --git a/src/pages/workspace/card/WorkspaceCardPage.js b/src/pages/workspace/card/WorkspaceCardPage.js deleted file mode 100644 index 55220b85ce63..000000000000 --- a/src/pages/workspace/card/WorkspaceCardPage.js +++ /dev/null @@ -1,47 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; -import WorkspacePageWithSections from '@pages/workspace/WorkspacePageWithSections'; -import CONST from '@src/CONST'; -import WorkspaceCardNoVBAView from './WorkspaceCardNoVBAView'; -import WorkspaceCardVBANoECardView from './WorkspaceCardVBANoECardView'; -import WorkspaceCardVBAWithECardView from './WorkspaceCardVBAWithECardView'; - -const propTypes = { - /** The route object passed to this page from the navigator */ - route: PropTypes.shape({ - /** Each parameter passed via the URL */ - params: PropTypes.shape({ - /** The policyID that is being configured */ - policyID: PropTypes.string.isRequired, - }).isRequired, - }).isRequired, - - ...withLocalizePropTypes, -}; - -function WorkspaceCardPage(props) { - return ( - - {(hasVBA, policyID, isUsingECard) => ( - <> - {!hasVBA && } - - {hasVBA && !isUsingECard && } - - {hasVBA && isUsingECard && } - - )} - - ); -} - -WorkspaceCardPage.propTypes = propTypes; -WorkspaceCardPage.displayName = 'WorkspaceCardPage'; - -export default withLocalize(WorkspaceCardPage); diff --git a/src/pages/workspace/card/WorkspaceCardPage.tsx b/src/pages/workspace/card/WorkspaceCardPage.tsx new file mode 100644 index 000000000000..f6e368db84ea --- /dev/null +++ b/src/pages/workspace/card/WorkspaceCardPage.tsx @@ -0,0 +1,39 @@ +import type {StackScreenProps} from '@react-navigation/stack'; +import React from 'react'; +import useLocalize from '@hooks/useLocalize'; +import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; +import WorkspacePageWithSections from '@pages/workspace/WorkspacePageWithSections'; +import CONST from '@src/CONST'; +import type SCREENS from '@src/SCREENS'; +import WorkspaceCardNoVBAView from './WorkspaceCardNoVBAView'; +import WorkspaceCardVBANoECardView from './WorkspaceCardVBANoECardView'; +import WorkspaceCardVBAWithECardView from './WorkspaceCardVBAWithECardView'; + +type WorkspaceCardPageProps = StackScreenProps; + +function WorkspaceCardPage({route}: WorkspaceCardPageProps) { + const {translate} = useLocalize(); + + return ( + + {(hasVBA: boolean, policyID: string, isUsingECard: boolean) => ( + <> + {false && } + + {false && } + + {true && } + + )} + + ); +} + +WorkspaceCardPage.displayName = 'WorkspaceCardPage'; + +export default WorkspaceCardPage; diff --git a/src/pages/workspace/card/WorkspaceCardVBANoECardView.js b/src/pages/workspace/card/WorkspaceCardVBANoECardView.tsx similarity index 53% rename from src/pages/workspace/card/WorkspaceCardVBANoECardView.js rename to src/pages/workspace/card/WorkspaceCardVBANoECardView.tsx index 970cd9105368..3c9b773d6994 100644 --- a/src/pages/workspace/card/WorkspaceCardVBANoECardView.js +++ b/src/pages/workspace/card/WorkspaceCardVBANoECardView.tsx @@ -1,5 +1,6 @@ import React from 'react'; import {View} from 'react-native'; +import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; import Button from '@components/Button'; import * as Expensicons from '@components/Icon/Expensicons'; @@ -7,45 +8,37 @@ import * as Illustrations from '@components/Icon/Illustrations'; import Section from '@components/Section'; import Text from '@components/Text'; import UnorderedList from '@components/UnorderedList'; -import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; +import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import compose from '@libs/compose'; -import userPropTypes from '@pages/settings/userPropTypes'; import * as Link from '@userActions/Link'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import type {User} from '@src/types/onyx'; -const propTypes = { +type WorkspaceCardVBANoECardViewOnyxProps = { /** Information about the logged in user's account */ - user: userPropTypes, - - ...withLocalizePropTypes, + user: OnyxEntry; }; -const defaultProps = { - user: {}, -}; +type WorkspaceCardVBANoECardViewProps = WorkspaceCardVBANoECardViewOnyxProps; -function WorkspaceCardVBANoECardView(props) { +function WorkspaceCardVBANoECardView({user}: WorkspaceCardVBANoECardViewProps) { const styles = useThemeStyles(); + const {translate} = useLocalize(); + return ( <>
- {Boolean(props.user.isCheckingDomain) && {props.translate('workspace.card.checkingDomain')}} + {Boolean(user?.isCheckingDomain) && {translate('workspace.card.checkingDomain')}} ); } -WorkspaceCardVBANoECardView.propTypes = propTypes; -WorkspaceCardVBANoECardView.defaultProps = defaultProps; WorkspaceCardVBANoECardView.displayName = 'WorkspaceCardVBANoECardView'; -export default compose( - withLocalize, - withOnyx({ - user: { - key: ONYXKEYS.USER, - }, - }), -)(WorkspaceCardVBANoECardView); +export default withOnyx({ + user: { + key: ONYXKEYS.USER, + }, +})(WorkspaceCardVBANoECardView); diff --git a/src/pages/workspace/card/WorkspaceCardVBAWithECardView.js b/src/pages/workspace/card/WorkspaceCardVBAWithECardView.tsx similarity index 67% rename from src/pages/workspace/card/WorkspaceCardVBAWithECardView.js rename to src/pages/workspace/card/WorkspaceCardVBAWithECardView.tsx index 40ecd80b8e6e..a53a44fa52cf 100644 --- a/src/pages/workspace/card/WorkspaceCardVBAWithECardView.js +++ b/src/pages/workspace/card/WorkspaceCardVBAWithECardView.tsx @@ -2,28 +2,35 @@ import React from 'react'; import {View} from 'react-native'; import * as Expensicons from '@components/Icon/Expensicons'; import * as Illustrations from '@components/Icon/Illustrations'; +import type {MenuItemWithLink} from '@components/MenuItemList'; import Section from '@components/Section'; import Text from '@components/Text'; import UnorderedList from '@components/UnorderedList'; -import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; +import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import * as Link from '@userActions/Link'; -const propTypes = { - ...withLocalizePropTypes, +type MenuLinks = { + ISSUE_AND_MANAGE_CARDS: string; + RECONCILE_CARDS: string; + SETTLEMENT_FREQUENCY: string; }; -const MENU_LINKS = { +type MenuItems = MenuItemWithLink[]; + +const MENU_LINKS: MenuLinks = { ISSUE_AND_MANAGE_CARDS: 'domain_companycards', RECONCILE_CARDS: encodeURI('domain_companycards?param={"section":"cardReconciliation"}'), SETTLEMENT_FREQUENCY: encodeURI('domain_companycards?param={"section":"configureSettings"}'), }; -function WorkspaceCardVBAWithECardView(props) { +function WorkspaceCardVBAWithECardView() { const styles = useThemeStyles(); - const menuItems = [ + const {translate} = useLocalize(); + + const menuItems: MenuItems = [ { - title: props.translate('workspace.common.issueAndManageCards'), + title: translate('workspace.common.issueAndManageCards'), onPress: () => Link.openOldDotLink(MENU_LINKS.ISSUE_AND_MANAGE_CARDS), icon: Expensicons.ExpensifyCard, shouldShowRightIcon: true, @@ -32,7 +39,7 @@ function WorkspaceCardVBAWithECardView(props) { link: () => Link.buildOldDotURL(MENU_LINKS.ISSUE_AND_MANAGE_CARDS), }, { - title: props.translate('workspace.common.reconcileCards'), + title: translate('workspace.common.reconcileCards'), onPress: () => Link.openOldDotLink(MENU_LINKS.RECONCILE_CARDS), icon: Expensicons.ReceiptSearch, shouldShowRightIcon: true, @@ -41,7 +48,7 @@ function WorkspaceCardVBAWithECardView(props) { link: () => Link.buildOldDotURL(MENU_LINKS.RECONCILE_CARDS), }, { - title: props.translate('workspace.common.settlementFrequency'), + title: translate('workspace.common.settlementFrequency'), onPress: () => Link.openOldDotLink(MENU_LINKS.SETTLEMENT_FREQUENCY), icon: Expensicons.Gear, shouldShowRightIcon: true, @@ -53,29 +60,23 @@ function WorkspaceCardVBAWithECardView(props) { return (
- {props.translate('workspace.card.VBAWithECardCopy')} + {translate('workspace.card.VBAWithECardCopy')}
); } -WorkspaceCardVBAWithECardView.propTypes = propTypes; WorkspaceCardVBAWithECardView.displayName = 'WorkspaceCardVBAWithECardView'; -export default withLocalize(WorkspaceCardVBAWithECardView); +export default WorkspaceCardVBAWithECardView; From b9746297385965d1a1ba4150ab10e4110aef012d Mon Sep 17 00:00:00 2001 From: ruben-rebelo Date: Thu, 1 Feb 2024 10:15:16 +0000 Subject: [PATCH 003/167] [TS migration] Migrate WorkspaceReimburse page --- src/ONYXKEYS.ts | 4 +- src/components/Picker/BasePicker.tsx | 4 +- src/components/Picker/types.ts | 2 +- src/libs/DistanceRequestUtils.ts | 2 +- src/libs/PolicyUtils.ts | 4 +- src/libs/actions/Policy.ts | 17 +- .../reimburse/WorkspaceRateAndUnitPage.js | 173 ------------------ .../reimburse/WorkspaceRateAndUnitPage.tsx | 157 ++++++++++++++++ .../reimburse/WorkspaceReimbursePage.js | 42 ----- .../reimburse/WorkspaceReimbursePage.tsx | 33 ++++ ...ction.js => WorkspaceReimburseSection.tsx} | 60 +++--- ...urseView.js => WorkspaceReimburseView.tsx} | 116 ++++-------- src/types/onyx/Form.ts | 9 +- src/types/onyx/Policy.ts | 6 +- src/types/onyx/index.ts | 3 +- 15 files changed, 291 insertions(+), 341 deletions(-) delete mode 100644 src/pages/workspace/reimburse/WorkspaceRateAndUnitPage.js create mode 100644 src/pages/workspace/reimburse/WorkspaceRateAndUnitPage.tsx delete mode 100644 src/pages/workspace/reimburse/WorkspaceReimbursePage.js create mode 100644 src/pages/workspace/reimburse/WorkspaceReimbursePage.tsx rename src/pages/workspace/reimburse/{WorkspaceReimburseSection.js => WorkspaceReimburseSection.tsx} (57%) rename src/pages/workspace/reimburse/{WorkspaceReimburseView.js => WorkspaceReimburseView.tsx} (53%) diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 2867cb3905a2..0eaa79d7cdda 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -486,8 +486,8 @@ type OnyxValues = { [ONYXKEYS.FORMS.ADD_DEBIT_CARD_FORM_DRAFT]: OnyxTypes.AddDebitCardForm; [ONYXKEYS.FORMS.WORKSPACE_SETTINGS_FORM]: OnyxTypes.Form; [ONYXKEYS.FORMS.WORKSPACE_SETTINGS_FORM_DRAFT]: OnyxTypes.Form; - [ONYXKEYS.FORMS.WORKSPACE_RATE_AND_UNIT_FORM]: OnyxTypes.Form; - [ONYXKEYS.FORMS.WORKSPACE_RATE_AND_UNIT_FORM_DRAFT]: OnyxTypes.Form; + [ONYXKEYS.FORMS.WORKSPACE_RATE_AND_UNIT_FORM]: OnyxTypes.RateUnitForm; + [ONYXKEYS.FORMS.WORKSPACE_RATE_AND_UNIT_FORM_DRAFT]: OnyxTypes.RateUnitForm; [ONYXKEYS.FORMS.CLOSE_ACCOUNT_FORM]: OnyxTypes.Form; [ONYXKEYS.FORMS.CLOSE_ACCOUNT_FORM_DRAFT]: OnyxTypes.Form; [ONYXKEYS.FORMS.PROFILE_SETTINGS_FORM]: OnyxTypes.Form; diff --git a/src/components/Picker/BasePicker.tsx b/src/components/Picker/BasePicker.tsx index 1bee95532104..020a3cf72680 100644 --- a/src/components/Picker/BasePicker.tsx +++ b/src/components/Picker/BasePicker.tsx @@ -69,11 +69,11 @@ function BasePicker( */ const onValueChange = (inputValue: TPickerValue, index: number) => { if (inputID) { - onInputChange(inputValue); + onInputChange?.(inputValue); return; } - onInputChange(inputValue, index); + onInputChange?.(inputValue, index); }; const enableHighlight = () => { diff --git a/src/components/Picker/types.ts b/src/components/Picker/types.ts index edf39a59c9d8..6304b23e7a2c 100644 --- a/src/components/Picker/types.ts +++ b/src/components/Picker/types.ts @@ -73,7 +73,7 @@ type BasePickerProps = { shouldSaveDraft?: boolean; /** A callback method that is called when the value changes and it receives the selected value as an argument */ - onInputChange: (value: TPickerValue, index?: number) => void; + onInputChange?: (value: TPickerValue, index?: number) => void; /** Size of a picker component */ size?: PickerSize; diff --git a/src/libs/DistanceRequestUtils.ts b/src/libs/DistanceRequestUtils.ts index c92e9bfd3f67..fb4e92d7f147 100644 --- a/src/libs/DistanceRequestUtils.ts +++ b/src/libs/DistanceRequestUtils.ts @@ -100,7 +100,7 @@ function getDistanceMerchant( const distanceUnit = unit === CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES ? translate('common.miles') : translate('common.kilometers'); const singularDistanceUnit = unit === CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES ? translate('common.mile') : translate('common.kilometer'); const unitString = distanceInUnits === '1' ? singularDistanceUnit : distanceUnit; - const ratePerUnit = rate ? PolicyUtils.getUnitRateValue({rate}, toLocaleDigit) : translate('common.tbd'); + const ratePerUnit = rate ? PolicyUtils.getUnitRateValue(toLocaleDigit, {rate}) : translate('common.tbd'); // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing const currencySymbol = rate ? CurrencyUtils.getCurrencySymbol(currency) || `${currency} ` : ''; diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index b8ed62f93082..a812cab24402 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -4,11 +4,11 @@ import type {ValueOf} from 'type-fest'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {PersonalDetailsList, Policy, PolicyMembers, PolicyTag, PolicyTags} from '@src/types/onyx'; +import type {Rate} from '@src/types/onyx/Policy'; import type {EmptyObject} from '@src/types/utils/EmptyObject'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; type MemberEmailsToAccountIDs = Record; -type UnitRate = {rate: number}; /** * Filter out the active policies, which will exclude policies with pending deletion @@ -66,7 +66,7 @@ function getRateDisplayValue(value: number, toLocaleDigit: (arg: string) => stri return numValue.toString().replace('.', toLocaleDigit('.')).substring(0, value.toString().length); } -function getUnitRateValue(customUnitRate: UnitRate, toLocaleDigit: (arg: string) => string) { +function getUnitRateValue(toLocaleDigit: (arg: string) => string, customUnitRate?: Rate) { return getRateDisplayValue((customUnitRate?.rate ?? 0) / CONST.POLICY.CUSTOM_UNIT_RATE_BASE_OFFSET, toLocaleDigit); } diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index fbe92aeb378d..48fd8944f196 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -67,6 +67,13 @@ type OptimisticCustomUnits = { outputCurrency: string; }; +type NewCustomUnit = { + customUnitID: string; + name: string; + attributes: Attributes; + rates: Rate; +}; + type PoliciesRecord = Record>; const allPolicies: OnyxCollection = {}; @@ -932,7 +939,7 @@ function hideWorkspaceAlertMessage(policyID: string) { Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {alertMessage: ''}); } -function updateWorkspaceCustomUnitAndRate(policyID: string, currentCustomUnit: CustomUnit, newCustomUnit: CustomUnit, lastModified: number) { +function updateWorkspaceCustomUnitAndRate(policyID: string, currentCustomUnit: CustomUnit, newCustomUnit: NewCustomUnit, lastModified: number) { if (!currentCustomUnit.customUnitID || !newCustomUnit?.customUnitID || !newCustomUnit.rates?.customUnitRateID) { return; } @@ -946,7 +953,7 @@ function updateWorkspaceCustomUnitAndRate(policyID: string, currentCustomUnit: C [newCustomUnit.customUnitID]: { ...newCustomUnit, rates: { - [newCustomUnit.rates.customUnitRateID as string]: { + [newCustomUnit.rates.customUnitRateID]: { ...newCustomUnit.rates, errors: null, pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, @@ -969,7 +976,7 @@ function updateWorkspaceCustomUnitAndRate(policyID: string, currentCustomUnit: C pendingAction: null, errors: null, rates: { - [newCustomUnit.rates.customUnitRateID as string]: { + [newCustomUnit.rates.customUnitRateID]: { pendingAction: null, }, }, @@ -988,7 +995,7 @@ function updateWorkspaceCustomUnitAndRate(policyID: string, currentCustomUnit: C [currentCustomUnit.customUnitID]: { customUnitID: currentCustomUnit.customUnitID, rates: { - [newCustomUnit.rates.customUnitRateID as string]: { + [newCustomUnit.rates.customUnitRateID]: { ...currentCustomUnit.rates, errors: ErrorUtils.getMicroSecondOnyxError('workspace.reimburse.updateCustomUnitError'), }, @@ -2018,3 +2025,5 @@ export { createDraftInitialWorkspace, setWorkspaceInviteMessageDraft, }; + +export type {NewCustomUnit}; diff --git a/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage.js b/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage.js deleted file mode 100644 index 93ea7212e741..000000000000 --- a/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage.js +++ /dev/null @@ -1,173 +0,0 @@ -import lodashGet from 'lodash/get'; -import React, {useEffect} from 'react'; -import {Keyboard, View} from 'react-native'; -import {withOnyx} from 'react-native-onyx'; -import _ from 'underscore'; -import FormProvider from '@components/Form/FormProvider'; -import InputWrapper from '@components/Form/InputWrapper'; -import OfflineWithFeedback from '@components/OfflineWithFeedback'; -import {withNetwork} from '@components/OnyxProvider'; -import Picker from '@components/Picker'; -import TextInput from '@components/TextInput'; -import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; -import withThemeStyles, {withThemeStylesPropTypes} from '@components/withThemeStyles'; -import compose from '@libs/compose'; -import * as CurrencyUtils from '@libs/CurrencyUtils'; -import getPermittedDecimalSeparator from '@libs/getPermittedDecimalSeparator'; -import Navigation from '@libs/Navigation/Navigation'; -import * as NumberUtils from '@libs/NumberUtils'; -import * as PolicyUtils from '@libs/PolicyUtils'; -import withPolicy, {policyDefaultProps, policyPropTypes} from '@pages/workspace/withPolicy'; -import WorkspacePageWithSections from '@pages/workspace/WorkspacePageWithSections'; -import * as BankAccounts from '@userActions/BankAccounts'; -import * as Policy from '@userActions/Policy'; -import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; - -const propTypes = { - ...policyPropTypes, - ...withLocalizePropTypes, - ...withThemeStylesPropTypes, -}; - -const defaultProps = { - reimbursementAccount: {}, - ...policyDefaultProps, -}; - -function WorkspaceRateAndUnitPage(props) { - useEffect(() => { - if (lodashGet(props, 'policy.customUnits', []).length !== 0) { - return; - } - - BankAccounts.setReimbursementAccountLoading(true); - Policy.openWorkspaceReimburseView(props.policy.id); - }, [props]); - - const unitItems = [ - {label: props.translate('common.kilometers'), value: CONST.CUSTOM_UNITS.DISTANCE_UNIT_KILOMETERS}, - {label: props.translate('common.miles'), value: CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES}, - ]; - - const saveUnitAndRate = (unit, rate) => { - const distanceCustomUnit = _.find(lodashGet(props, 'policy.customUnits', {}), (customUnit) => customUnit.name === CONST.CUSTOM_UNITS.NAME_DISTANCE); - if (!distanceCustomUnit) { - return; - } - const currentCustomUnitRate = _.find(lodashGet(distanceCustomUnit, 'rates', {}), (r) => r.name === CONST.CUSTOM_UNITS.DEFAULT_RATE); - const unitID = lodashGet(distanceCustomUnit, 'customUnitID', ''); - const unitName = lodashGet(distanceCustomUnit, 'name', ''); - const rateNumValue = PolicyUtils.getNumericValue(rate, props.toLocaleDigit); - - const newCustomUnit = { - customUnitID: unitID, - name: unitName, - attributes: {unit}, - rates: { - ...currentCustomUnitRate, - rate: rateNumValue * CONST.POLICY.CUSTOM_UNIT_RATE_BASE_OFFSET, - }, - }; - Policy.updateWorkspaceCustomUnitAndRate(props.policy.id, distanceCustomUnit, newCustomUnit, props.policy.lastModified); - }; - - const submit = (values) => { - saveUnitAndRate(values.unit, values.rate); - Keyboard.dismiss(); - Navigation.goBack(ROUTES.WORKSPACE_REIMBURSE.getRoute(props.policy.id)); - }; - - const validate = (values) => { - const errors = {}; - const decimalSeparator = props.toLocaleDigit('.'); - const outputCurrency = lodashGet(props, 'policy.outputCurrency', CONST.CURRENCY.USD); - // Allow one more decimal place for accuracy - const rateValueRegex = RegExp(String.raw`^-?\d{0,8}([${getPermittedDecimalSeparator(decimalSeparator)}]\d{1,${CurrencyUtils.getCurrencyDecimals(outputCurrency) + 1}})?$`, 'i'); - if (!rateValueRegex.test(values.rate) || values.rate === '') { - errors.rate = 'workspace.reimburse.invalidRateError'; - } else if (NumberUtils.parseFloatAnyLocale(values.rate) <= 0) { - errors.rate = 'workspace.reimburse.lowRateError'; - } - return errors; - }; - - const distanceCustomUnit = _.find(lodashGet(props, 'policy.customUnits', {}), (unit) => unit.name === CONST.CUSTOM_UNITS.NAME_DISTANCE); - const distanceCustomRate = _.find(lodashGet(distanceCustomUnit, 'rates', {}), (rate) => rate.name === CONST.CUSTOM_UNITS.DEFAULT_RATE); - - return ( - - {() => ( - - - Policy.clearCustomUnitErrors(props.policy.id, lodashGet(distanceCustomUnit, 'customUnitID', ''), lodashGet(distanceCustomRate, 'customUnitRateID', '')) - } - > - - - - - - - - )} - - ); -} - -WorkspaceRateAndUnitPage.propTypes = propTypes; -WorkspaceRateAndUnitPage.defaultProps = defaultProps; -WorkspaceRateAndUnitPage.displayName = 'WorkspaceRateAndUnitPage'; - -export default compose( - withPolicy, - withLocalize, - withNetwork(), - withOnyx({ - reimbursementAccount: { - key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, - }, - }), - withThemeStyles, -)(WorkspaceRateAndUnitPage); diff --git a/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage.tsx b/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage.tsx new file mode 100644 index 000000000000..9c24b6bde023 --- /dev/null +++ b/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage.tsx @@ -0,0 +1,157 @@ +import type {StackScreenProps} from '@react-navigation/stack'; +import React, {useEffect} from 'react'; +import {Keyboard, View} from 'react-native'; +import FormProvider from '@components/Form/FormProvider'; +import InputWrapper from '@components/Form/InputWrapper'; +import type {OnyxFormValuesFields} from '@components/Form/types'; +import OfflineWithFeedback from '@components/OfflineWithFeedback'; +import Picker from '@components/Picker'; +import TextInput from '@components/TextInput'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import * as CurrencyUtils from '@libs/CurrencyUtils'; +import getPermittedDecimalSeparator from '@libs/getPermittedDecimalSeparator'; +import Navigation from '@libs/Navigation/Navigation'; +import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; +import * as NumberUtils from '@libs/NumberUtils'; +import * as PolicyUtils from '@libs/PolicyUtils'; +import withPolicy from '@pages/workspace/withPolicy'; +import type {WithPolicyProps} from '@pages/workspace/withPolicy'; +import WorkspacePageWithSections from '@pages/workspace/WorkspacePageWithSections'; +import * as BankAccounts from '@userActions/BankAccounts'; +import * as Policy from '@userActions/Policy'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; +import ROUTES from '@src/ROUTES'; +import type SCREENS from '@src/SCREENS'; +import type {Unit} from '@src/types/onyx/Policy'; + +type WorkspaceRateAndUnitPageProps = WithPolicyProps & StackScreenProps; + +type ValidationError = {rate?: string}; + +function WorkspaceRateAndUnitPage({policy, route}: WorkspaceRateAndUnitPageProps) { + const {translate, toLocaleDigit} = useLocalize(); + const styles = useThemeStyles(); + + useEffect(() => { + if ((policy?.customUnits ?? []).length !== 0) { + return; + } + + BankAccounts.setReimbursementAccountLoading(true); + Policy.openWorkspaceReimburseView(policy?.id ?? ''); + }, [policy?.customUnits, policy?.id]); + + const unitItems = [ + {label: translate('common.kilometers'), value: CONST.CUSTOM_UNITS.DISTANCE_UNIT_KILOMETERS}, + {label: translate('common.miles'), value: CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES}, + ]; + + const saveUnitAndRate = (unit: Unit, rate: number) => { + const distanceCustomUnit = Object.values(policy?.customUnits ?? {}).find((customUnit) => customUnit.name === CONST.CUSTOM_UNITS.NAME_DISTANCE); + if (!distanceCustomUnit) { + return; + } + const currentCustomUnitRate = Object.values(distanceCustomUnit?.rates ?? {}).find((r) => r.name === CONST.CUSTOM_UNITS.DEFAULT_RATE); + const unitID = distanceCustomUnit.customUnitID ?? ''; + const unitName = distanceCustomUnit.name ?? ''; + const rateNumValue = PolicyUtils.getNumericValue(rate, toLocaleDigit) as number; + + const newCustomUnit: Policy.NewCustomUnit = { + customUnitID: unitID, + name: unitName, + attributes: {unit}, + rates: { + ...currentCustomUnitRate, + rate: rateNumValue * CONST.POLICY.CUSTOM_UNIT_RATE_BASE_OFFSET, + }, + }; + + Policy.updateWorkspaceCustomUnitAndRate(policy?.id ?? '', distanceCustomUnit, newCustomUnit, parseInt(policy?.lastModified ?? '', 2)); + }; + + const submit = (values: OnyxFormValuesFields) => { + saveUnitAndRate(values.unit, values.rate); + Keyboard.dismiss(); + Navigation.goBack(ROUTES.WORKSPACE_REIMBURSE.getRoute(policy?.id ?? '')); + }; + + const validate = (values: OnyxFormValuesFields): ValidationError => { + const errors: ValidationError = {}; + const decimalSeparator = toLocaleDigit('.'); + const outputCurrency = policy?.outputCurrency ?? CONST.CURRENCY.USD; + // Allow one more decimal place for accuracy + const rateValueRegex = RegExp(String.raw`^-?\d{0,8}([${getPermittedDecimalSeparator(decimalSeparator)}]\d{1,${CurrencyUtils.getCurrencyDecimals(outputCurrency) + 1}})?$`, 'i'); + if (!rateValueRegex.test(values.rate.toString()) || values.rate.toString() === 'Nan') { + errors.rate = 'workspace.reimburse.invalidRateError'; + } else if (NumberUtils.parseFloatAnyLocale(values.rate.toString()) <= 0) { + errors.rate = 'workspace.reimburse.lowRateError'; + } + return errors; + }; + + const distanceCustomUnit = Object.values(policy?.customUnits ?? {}).find((unit) => unit.name === CONST.CUSTOM_UNITS.NAME_DISTANCE); + const distanceCustomRate = Object.values(distanceCustomUnit?.rates ?? {}).find((rate) => rate.name === CONST.CUSTOM_UNITS.DEFAULT_RATE); + + return ( + + {() => ( + + Policy.clearCustomUnitErrors(policy?.id ?? '', distanceCustomUnit?.customUnitID ?? '', distanceCustomRate?.customUnitRateID ?? '')} + > + + + + + + + + )} + + ); +} + +WorkspaceRateAndUnitPage.displayName = 'WorkspaceRateAndUnitPage'; + +export default withPolicy(WorkspaceRateAndUnitPage); diff --git a/src/pages/workspace/reimburse/WorkspaceReimbursePage.js b/src/pages/workspace/reimburse/WorkspaceReimbursePage.js deleted file mode 100644 index fa3849abc941..000000000000 --- a/src/pages/workspace/reimburse/WorkspaceReimbursePage.js +++ /dev/null @@ -1,42 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; -import compose from '@libs/compose'; -import withPolicy, {policyPropTypes} from '@pages/workspace/withPolicy'; -import WorkspacePageWithSections from '@pages/workspace/WorkspacePageWithSections'; -import CONST from '@src/CONST'; -import WorkspaceReimburseView from './WorkspaceReimburseView'; - -const propTypes = { - /** The route object passed to this page from the navigator */ - route: PropTypes.shape({ - /** Each parameter passed via the URL */ - params: PropTypes.shape({ - /** The policyID that is being configured */ - policyID: PropTypes.string.isRequired, - }).isRequired, - }).isRequired, - - ...policyPropTypes, - ...withLocalizePropTypes, -}; - -function WorkspaceReimbursePage(props) { - return ( - - {() => } - - ); -} - -WorkspaceReimbursePage.propTypes = propTypes; -WorkspaceReimbursePage.displayName = 'WorkspaceReimbursePage'; - -export default compose(withPolicy, withLocalize)(WorkspaceReimbursePage); diff --git a/src/pages/workspace/reimburse/WorkspaceReimbursePage.tsx b/src/pages/workspace/reimburse/WorkspaceReimbursePage.tsx new file mode 100644 index 000000000000..78bf58301db5 --- /dev/null +++ b/src/pages/workspace/reimburse/WorkspaceReimbursePage.tsx @@ -0,0 +1,33 @@ +import type {StackScreenProps} from '@react-navigation/stack'; +import React from 'react'; +import useLocalize from '@hooks/useLocalize'; +import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; +import type {WithPolicyProps} from '@pages/workspace/withPolicy'; +import withPolicy from '@pages/workspace/withPolicy'; +import WorkspacePageWithSections from '@pages/workspace/WorkspacePageWithSections'; +import CONST from '@src/CONST'; +import type SCREENS from '@src/SCREENS'; +import WorkspaceReimburseView from './WorkspaceReimburseView'; + +type WorkspaceReimbursePageProps = WithPolicyProps & StackScreenProps; + +function WorkspaceReimbursePage({route, policy}: WorkspaceReimbursePageProps) { + const {translate} = useLocalize(); + + return ( + + {() => } + + ); +} + +WorkspaceReimbursePage.displayName = 'WorkspaceReimbursePage'; + +export default withPolicy(WorkspaceReimbursePage); diff --git a/src/pages/workspace/reimburse/WorkspaceReimburseSection.js b/src/pages/workspace/reimburse/WorkspaceReimburseSection.tsx similarity index 57% rename from src/pages/workspace/reimburse/WorkspaceReimburseSection.js rename to src/pages/workspace/reimburse/WorkspaceReimburseSection.tsx index 00ef284c50ae..e4c99d79e324 100644 --- a/src/pages/workspace/reimburse/WorkspaceReimburseSection.js +++ b/src/pages/workspace/reimburse/WorkspaceReimburseSection.tsx @@ -1,45 +1,40 @@ -import lodashGet from 'lodash/get'; -import PropTypes from 'prop-types'; import React, {useEffect, useState} from 'react'; import {ActivityIndicator, View} from 'react-native'; +import type {OnyxEntry} from 'react-native-onyx'; import ConnectBankAccountButton from '@components/ConnectBankAccountButton'; import * as Expensicons from '@components/Icon/Expensicons'; import * as Illustrations from '@components/Icon/Illustrations'; -import networkPropTypes from '@components/networkPropTypes'; import Section from '@components/Section'; import Text from '@components/Text'; +import useLocalize from '@hooks/useLocalize'; +import useNetwork from '@hooks/useNetwork'; import usePrevious from '@hooks/usePrevious'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import BankAccount from '@libs/models/BankAccount'; -import * as ReimbursementAccountProps from '@pages/ReimbursementAccount/reimbursementAccountPropTypes'; import * as Link from '@userActions/Link'; +import type * as OnyxTypes from '@src/types/onyx'; -const propTypes = { +type WorkspaceReimburseSectionProps = { /** Policy values needed in the component */ - policy: PropTypes.shape({ - id: PropTypes.string, - }).isRequired, + policy: OnyxEntry; /** Bank account attached to free plan */ - reimbursementAccount: ReimbursementAccountProps.reimbursementAccountPropTypes.isRequired, - - /** Information about the network */ - network: networkPropTypes.isRequired, - - /** Returns translated string for given locale and phrase */ - translate: PropTypes.func.isRequired, + reimbursementAccount: OnyxEntry; }; -function WorkspaceReimburseSection(props) { +function WorkspaceReimburseSection({policy, reimbursementAccount}: WorkspaceReimburseSectionProps) { const theme = useTheme(); const styles = useThemeStyles(); + const {translate} = useLocalize(); const [shouldShowLoadingSpinner, setShouldShowLoadingSpinner] = useState(true); - const achState = lodashGet(props.reimbursementAccount, 'achData.state', ''); + const achState = reimbursementAccount?.achData?.state ?? ''; const hasVBA = achState === BankAccount.STATE.OPEN; - const reimburseReceiptsUrl = `reports?policyID=${props.policy.id}&from=all&type=expense&showStates=Archived&isAdvancedFilterMode=true`; - const isLoading = lodashGet(props.reimbursementAccount, 'isLoading', false); + const policyId = policy?.id ?? ''; + const reimburseReceiptsUrl = `reports?policyID=${policyId}&from=all&type=expense&showStates=Archived&isAdvancedFilterMode=true`; + const isLoading = reimbursementAccount?.isLoading ?? false; const prevIsLoading = usePrevious(isLoading); + const {isOffline} = useNetwork(); useEffect(() => { if (prevIsLoading === isLoading) { @@ -48,14 +43,14 @@ function WorkspaceReimburseSection(props) { setShouldShowLoadingSpinner(isLoading); }, [prevIsLoading, isLoading]); - if (props.network.isOffline) { + if (isOffline) { return (
- {`${props.translate('common.youAppearToBeOffline')} ${props.translate('common.thisFeatureRequiresInternet')}`} + {`${translate('common.youAppearToBeOffline')} ${translate('common.thisFeatureRequiresInternet')}`}
); @@ -76,35 +71,35 @@ function WorkspaceReimburseSection(props) { <> {hasVBA ? (
Link.openOldDotLink(reimburseReceiptsUrl), icon: Expensicons.Bank, shouldShowRightIcon: true, iconRight: Expensicons.NewWindow, - wrapperStyle: [styles.cardMenuItem], + wrapperStyle: styles.cardMenuItem, link: () => Link.buildOldDotURL(reimburseReceiptsUrl), }, ]} > - - {props.translate('workspace.reimburse.fastReimbursementsVBACopy')} + + {translate('workspace.reimburse.fastReimbursementsVBACopy')}
) : (
- - {props.translate('workspace.reimburse.unlockNoVBACopy')} + + {translate('workspace.reimburse.unlockNoVBACopy')}
)} @@ -112,7 +107,6 @@ function WorkspaceReimburseSection(props) { ); } -WorkspaceReimburseSection.propTypes = propTypes; WorkspaceReimburseSection.displayName = 'WorkspaceReimburseSection'; export default WorkspaceReimburseSection; diff --git a/src/pages/workspace/reimburse/WorkspaceReimburseView.js b/src/pages/workspace/reimburse/WorkspaceReimburseView.tsx similarity index 53% rename from src/pages/workspace/reimburse/WorkspaceReimburseView.js rename to src/pages/workspace/reimburse/WorkspaceReimburseView.tsx index 23136064fc2b..ea9ee9e8a421 100644 --- a/src/pages/workspace/reimburse/WorkspaceReimburseView.js +++ b/src/pages/workspace/reimburse/WorkspaceReimburseView.tsx @@ -1,85 +1,57 @@ -import lodashGet from 'lodash/get'; -import PropTypes from 'prop-types'; import React, {useCallback, useEffect, useState} from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; -import _ from 'underscore'; +import type {OnyxEntry} from 'react-native-onyx'; import CopyTextToClipboard from '@components/CopyTextToClipboard'; import * as Expensicons from '@components/Icon/Expensicons'; import * as Illustrations from '@components/Icon/Illustrations'; import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; -import networkPropTypes from '@components/networkPropTypes'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; -import {withNetwork} from '@components/OnyxProvider'; import Section from '@components/Section'; import Text from '@components/Text'; -import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; +import useLocalize from '@hooks/useLocalize'; +import useNetwork from '@hooks/useNetwork'; import useThemeStyles from '@hooks/useThemeStyles'; -import compose from '@libs/compose'; import Navigation from '@libs/Navigation/Navigation'; import * as PolicyUtils from '@libs/PolicyUtils'; -import * as ReimbursementAccountProps from '@pages/ReimbursementAccount/reimbursementAccountPropTypes'; import * as BankAccounts from '@userActions/BankAccounts'; import * as Link from '@userActions/Link'; import * as Policy from '@userActions/Policy'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import type * as OnyxTypes from '@src/types/onyx'; +import type {Unit} from '@src/types/onyx/Policy'; import WorkspaceReimburseSection from './WorkspaceReimburseSection'; -const propTypes = { - /** Policy values needed in the component */ - policy: PropTypes.shape({ - id: PropTypes.string, - customUnits: PropTypes.objectOf( - PropTypes.shape({ - customUnitID: PropTypes.string, - name: PropTypes.string, - attributes: PropTypes.shape({ - unit: PropTypes.string, - }), - rates: PropTypes.objectOf( - PropTypes.shape({ - customUnitRateID: PropTypes.string, - name: PropTypes.string, - rate: PropTypes.number, - }), - ), - }), - ), - outputCurrency: PropTypes.string, - lastModified: PropTypes.number, - }).isRequired, - +type WorkspaceReimburseViewOnyxProps = { /** From Onyx */ /** Bank account attached to free plan */ - reimbursementAccount: ReimbursementAccountProps.reimbursementAccountPropTypes, - - /** Information about the network */ - network: networkPropTypes.isRequired, - - ...withLocalizePropTypes, + reimbursementAccount: OnyxEntry; }; -const defaultProps = { - reimbursementAccount: ReimbursementAccountProps.reimbursementAccountDefaultProps, +type WorkspaceReimburseViewProps = WorkspaceReimburseViewOnyxProps & { + /** Policy values needed in the component */ + policy: OnyxEntry; }; -function WorkspaceReimburseView(props) { +function WorkspaceReimburseView({policy, reimbursementAccount}: WorkspaceReimburseViewProps) { const styles = useThemeStyles(); - const [currentRatePerUnit, setCurrentRatePerUnit] = useState(''); - const viewAllReceiptsUrl = `expenses?policyIDList=${props.policy.id}&billableReimbursable=reimbursable&submitterEmail=%2B%2B`; - const distanceCustomUnit = _.find(lodashGet(props.policy, 'customUnits', {}), (unit) => unit.name === CONST.CUSTOM_UNITS.NAME_DISTANCE); - const distanceCustomRate = _.find(lodashGet(distanceCustomUnit, 'rates', {}), (rate) => rate.name === CONST.CUSTOM_UNITS.DEFAULT_RATE); - const {translate, toLocaleDigit} = props; + const [currentRatePerUnit, setCurrentRatePerUnit] = useState(''); + const viewAllReceiptsUrl = `expenses?policyIDList=${policy?.id ?? ''}&billableReimbursable=reimbursable&submitterEmail=%2B%2B`; + const distanceCustomUnit = Object.values(policy?.customUnits ?? {}).find((unit) => unit.name === CONST.CUSTOM_UNITS.NAME_DISTANCE); + const distanceCustomRate = Object.values(distanceCustomUnit?.rates ?? {}).find((rate) => rate.name === CONST.CUSTOM_UNITS.DEFAULT_RATE); + const {translate, toLocaleDigit} = useLocalize(); + const {isOffline} = useNetwork(); - const getUnitLabel = useCallback((value) => translate(`common.${value}`), [translate]); + const getUnitLabel = useCallback((value: Unit): string => translate(`common.${value}`), [translate]); const getCurrentRatePerUnitLabel = useCallback(() => { - const customUnitRate = _.find(lodashGet(distanceCustomUnit, 'rates', '{}'), (rate) => rate && rate.name === CONST.CUSTOM_UNITS.DEFAULT_RATE); - const currentUnit = getUnitLabel(lodashGet(distanceCustomUnit, 'attributes.unit', CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES)); - const currentRate = PolicyUtils.getUnitRateValue(customUnitRate, toLocaleDigit); + const customUnitRate = Object.values(distanceCustomUnit?.rates ?? {}).find((rate) => rate && rate.name === CONST.CUSTOM_UNITS.DEFAULT_RATE); + const currentUnit = getUnitLabel(distanceCustomUnit?.attributes.unit ?? CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES); + const currentRate = PolicyUtils.getUnitRateValue(toLocaleDigit, customUnitRate); const perWord = translate('common.per'); + return `${currentRate} ${perWord} ${currentUnit}`; }, [translate, distanceCustomUnit, toLocaleDigit, getUnitLabel]); @@ -88,19 +60,19 @@ function WorkspaceReimburseView(props) { // openWorkspaceReimburseView uses API.read which will not make the request until all WRITE requests in the sequential queue have finished responding, so there would be a delay in // updating Onyx with the optimistic data. BankAccounts.setReimbursementAccountLoading(true); - Policy.openWorkspaceReimburseView(props.policy.id); - }, [props.policy.id]); + Policy.openWorkspaceReimburseView(policy?.id ?? ''); + }, [policy?.id]); useEffect(() => { - if (props.network.isOffline) { + if (isOffline) { return; } fetchData(); - }, [props.network.isOffline, fetchData]); + }, [isOffline, fetchData]); useEffect(() => { setCurrentRatePerUnit(getCurrentRatePerUnitLabel()); - }, [props.policy.customUnits, getCurrentRatePerUnitLabel]); + }, [policy?.customUnits, getCurrentRatePerUnitLabel]); return ( <> @@ -114,7 +86,7 @@ function WorkspaceReimburseView(props) { icon: Expensicons.Receipt, shouldShowRightIcon: true, iconRight: Expensicons.NewWindow, - wrapperStyle: [styles.cardMenuItem], + wrapperStyle: styles.cardMenuItem, link: () => Link.buildOldDotURL(viewAllReceiptsUrl), }, ]} @@ -124,7 +96,7 @@ function WorkspaceReimburseView(props) { {translate('workspace.reimburse.captureNoVBACopyBeforeEmail')} {translate('workspace.reimburse.captureNoVBACopyAfterEmail')} @@ -135,44 +107,36 @@ function WorkspaceReimburseView(props) { title={translate('workspace.reimburse.trackDistance')} icon={Illustrations.TrackShoe} > - + {translate('workspace.reimburse.trackDistanceCopy')} Navigation.navigate(ROUTES.WORKSPACE_RATE_AND_UNIT.getRoute(props.policy.id))} + onPress={() => Navigation.navigate(ROUTES.WORKSPACE_RATE_AND_UNIT.getRoute(policy?.id ?? ''))} wrapperStyle={[styles.mhn5, styles.wAuto]} - brickRoadIndicator={(lodashGet(distanceCustomUnit, 'errors') || lodashGet(distanceCustomRate, 'errors')) && CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR} + brickRoadIndicator={(distanceCustomUnit?.errors ?? distanceCustomRate?.errors) && CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR} /> ); } -WorkspaceReimburseView.defaultProps = defaultProps; -WorkspaceReimburseView.propTypes = propTypes; WorkspaceReimburseView.displayName = 'WorkspaceReimburseView'; -export default compose( - withLocalize, - withNetwork(), - withOnyx({ - reimbursementAccount: { - key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, - }, - }), -)(WorkspaceReimburseView); +export default withOnyx({ + reimbursementAccount: { + key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, + }, +})(WorkspaceReimburseView); diff --git a/src/types/onyx/Form.ts b/src/types/onyx/Form.ts index 9c6d52a1020d..5c0bb096b42f 100644 --- a/src/types/onyx/Form.ts +++ b/src/types/onyx/Form.ts @@ -1,7 +1,8 @@ import type * as OnyxCommon from './OnyxCommon'; import type PersonalBankAccount from './PersonalBankAccount'; +import type {Unit} from './Policy'; -type FormValueType = string | boolean | Date | OnyxCommon.Errors; +type FormValueType = string | boolean | Date | number | OnyxCommon.Errors; type BaseForm = { /** Controls the loading state of the form */ @@ -59,6 +60,11 @@ type PersonalBankAccountForm = Form; type ReportFieldEditForm = Form>; +type RateUnitForm = Form<{ + unit: Unit; + rate: number; +}>; + export default Form; export type { @@ -73,4 +79,5 @@ export type { IntroSchoolPrincipalForm, PersonalBankAccountForm, ReportFieldEditForm, + RateUnitForm, }; diff --git a/src/types/onyx/Policy.ts b/src/types/onyx/Policy.ts index fe50bbb497d2..ecc8b1797654 100644 --- a/src/types/onyx/Policy.ts +++ b/src/types/onyx/Policy.ts @@ -10,7 +10,7 @@ type Rate = { currency?: string; customUnitRateID?: string; errors?: OnyxCommon.Errors; - pendingAction?: string; + pendingAction?: OnyxCommon.PendingAction; }; type Attributes = { @@ -22,7 +22,7 @@ type CustomUnit = { customUnitID: string; attributes: Attributes; rates: Record; - pendingAction?: string; + pendingAction?: OnyxCommon.PendingAction; errors?: OnyxCommon.Errors; }; @@ -156,4 +156,4 @@ type Policy = { export default Policy; -export type {Unit, CustomUnit}; +export type {Unit, CustomUnit, Rate, Attributes}; diff --git a/src/types/onyx/index.ts b/src/types/onyx/index.ts index 64eec736b5bf..a2cc5a878b47 100644 --- a/src/types/onyx/index.ts +++ b/src/types/onyx/index.ts @@ -9,7 +9,7 @@ import type Credentials from './Credentials'; import type Currency from './Currency'; import type CustomStatusDraft from './CustomStatusDraft'; import type Download from './Download'; -import type {AddDebitCardForm, DateOfBirthForm, DisplayNameForm, IKnowATeacherForm, IntroSchoolPrincipalForm, NewRoomForm, PrivateNotesForm, ReportFieldEditForm} from './Form'; +import type {AddDebitCardForm, DateOfBirthForm, DisplayNameForm, IKnowATeacherForm, IntroSchoolPrincipalForm, NewRoomForm, PrivateNotesForm, RateUnitForm, ReportFieldEditForm} from './Form'; import type Form from './Form'; import type FrequentlyUsedEmoji from './FrequentlyUsedEmoji'; import type {FundList} from './Fund'; @@ -151,5 +151,6 @@ export type { IKnowATeacherForm, IntroSchoolPrincipalForm, PrivateNotesForm, + RateUnitForm, ReportFieldEditForm, }; From f51dfad8cf298537861d378a9336e4598115ce5a Mon Sep 17 00:00:00 2001 From: ruben-rebelo Date: Thu, 1 Feb 2024 10:15:28 +0000 Subject: [PATCH 004/167] Revert "[TS Migration] Migrate WorkspaceCard to Typescript" This reverts commit 4feb09259b4501462807da3bb197aa705e3c1d4f. --- .../workspace/card/WorkspaceCardNoVBAView.js | 49 +++++++++++++++++++ .../workspace/card/WorkspaceCardNoVBAView.tsx | 40 --------------- src/pages/workspace/card/WorkspaceCardPage.js | 47 ++++++++++++++++++ .../workspace/card/WorkspaceCardPage.tsx | 39 --------------- ...iew.tsx => WorkspaceCardVBANoECardView.js} | 48 +++++++++++------- ...w.tsx => WorkspaceCardVBAWithECardView.js} | 39 +++++++-------- 6 files changed, 145 insertions(+), 117 deletions(-) create mode 100644 src/pages/workspace/card/WorkspaceCardNoVBAView.js delete mode 100644 src/pages/workspace/card/WorkspaceCardNoVBAView.tsx create mode 100644 src/pages/workspace/card/WorkspaceCardPage.js delete mode 100644 src/pages/workspace/card/WorkspaceCardPage.tsx rename src/pages/workspace/card/{WorkspaceCardVBANoECardView.tsx => WorkspaceCardVBANoECardView.js} (53%) rename src/pages/workspace/card/{WorkspaceCardVBAWithECardView.tsx => WorkspaceCardVBAWithECardView.js} (67%) diff --git a/src/pages/workspace/card/WorkspaceCardNoVBAView.js b/src/pages/workspace/card/WorkspaceCardNoVBAView.js new file mode 100644 index 000000000000..3233f8ea7e23 --- /dev/null +++ b/src/pages/workspace/card/WorkspaceCardNoVBAView.js @@ -0,0 +1,49 @@ +import PropTypes from 'prop-types'; +import React from 'react'; +import {View} from 'react-native'; +import ConnectBankAccountButton from '@components/ConnectBankAccountButton'; +import * as Illustrations from '@components/Icon/Illustrations'; +import Section from '@components/Section'; +import Text from '@components/Text'; +import UnorderedList from '@components/UnorderedList'; +import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; + +const propTypes = { + /** The policy ID currently being configured */ + policyID: PropTypes.string.isRequired, + + ...withLocalizePropTypes, +}; + +function WorkspaceCardNoVBAView(props) { + const styles = useThemeStyles(); + return ( +
+ + {props.translate('workspace.card.noVBACopy')} + + + + +
+ ); +} + +WorkspaceCardNoVBAView.propTypes = propTypes; +WorkspaceCardNoVBAView.displayName = 'WorkspaceCardNoVBAView'; + +export default withLocalize(WorkspaceCardNoVBAView); diff --git a/src/pages/workspace/card/WorkspaceCardNoVBAView.tsx b/src/pages/workspace/card/WorkspaceCardNoVBAView.tsx deleted file mode 100644 index 322d433a8e62..000000000000 --- a/src/pages/workspace/card/WorkspaceCardNoVBAView.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import React from 'react'; -import {View} from 'react-native'; -import ConnectBankAccountButton from '@components/ConnectBankAccountButton'; -import * as Illustrations from '@components/Icon/Illustrations'; -import Section from '@components/Section'; -import Text from '@components/Text'; -import UnorderedList from '@components/UnorderedList'; -import useLocalize from '@hooks/useLocalize'; -import useThemeStyles from '@hooks/useThemeStyles'; - -type WorkspaceCardNoVBAViewProps = { - /** The policy ID currently being configured */ - policyID: string; -}; - -function WorkspaceCardNoVBAView({policyID}: WorkspaceCardNoVBAViewProps) { - const styles = useThemeStyles(); - const {translate} = useLocalize(); - - return ( -
- - {translate('workspace.card.noVBACopy')} - - - - -
- ); -} - -WorkspaceCardNoVBAView.displayName = 'WorkspaceCardNoVBAView'; - -export default WorkspaceCardNoVBAView; diff --git a/src/pages/workspace/card/WorkspaceCardPage.js b/src/pages/workspace/card/WorkspaceCardPage.js new file mode 100644 index 000000000000..55220b85ce63 --- /dev/null +++ b/src/pages/workspace/card/WorkspaceCardPage.js @@ -0,0 +1,47 @@ +import PropTypes from 'prop-types'; +import React from 'react'; +import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; +import WorkspacePageWithSections from '@pages/workspace/WorkspacePageWithSections'; +import CONST from '@src/CONST'; +import WorkspaceCardNoVBAView from './WorkspaceCardNoVBAView'; +import WorkspaceCardVBANoECardView from './WorkspaceCardVBANoECardView'; +import WorkspaceCardVBAWithECardView from './WorkspaceCardVBAWithECardView'; + +const propTypes = { + /** The route object passed to this page from the navigator */ + route: PropTypes.shape({ + /** Each parameter passed via the URL */ + params: PropTypes.shape({ + /** The policyID that is being configured */ + policyID: PropTypes.string.isRequired, + }).isRequired, + }).isRequired, + + ...withLocalizePropTypes, +}; + +function WorkspaceCardPage(props) { + return ( + + {(hasVBA, policyID, isUsingECard) => ( + <> + {!hasVBA && } + + {hasVBA && !isUsingECard && } + + {hasVBA && isUsingECard && } + + )} + + ); +} + +WorkspaceCardPage.propTypes = propTypes; +WorkspaceCardPage.displayName = 'WorkspaceCardPage'; + +export default withLocalize(WorkspaceCardPage); diff --git a/src/pages/workspace/card/WorkspaceCardPage.tsx b/src/pages/workspace/card/WorkspaceCardPage.tsx deleted file mode 100644 index f6e368db84ea..000000000000 --- a/src/pages/workspace/card/WorkspaceCardPage.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import type {StackScreenProps} from '@react-navigation/stack'; -import React from 'react'; -import useLocalize from '@hooks/useLocalize'; -import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; -import WorkspacePageWithSections from '@pages/workspace/WorkspacePageWithSections'; -import CONST from '@src/CONST'; -import type SCREENS from '@src/SCREENS'; -import WorkspaceCardNoVBAView from './WorkspaceCardNoVBAView'; -import WorkspaceCardVBANoECardView from './WorkspaceCardVBANoECardView'; -import WorkspaceCardVBAWithECardView from './WorkspaceCardVBAWithECardView'; - -type WorkspaceCardPageProps = StackScreenProps; - -function WorkspaceCardPage({route}: WorkspaceCardPageProps) { - const {translate} = useLocalize(); - - return ( - - {(hasVBA: boolean, policyID: string, isUsingECard: boolean) => ( - <> - {false && } - - {false && } - - {true && } - - )} - - ); -} - -WorkspaceCardPage.displayName = 'WorkspaceCardPage'; - -export default WorkspaceCardPage; diff --git a/src/pages/workspace/card/WorkspaceCardVBANoECardView.tsx b/src/pages/workspace/card/WorkspaceCardVBANoECardView.js similarity index 53% rename from src/pages/workspace/card/WorkspaceCardVBANoECardView.tsx rename to src/pages/workspace/card/WorkspaceCardVBANoECardView.js index 3c9b773d6994..970cd9105368 100644 --- a/src/pages/workspace/card/WorkspaceCardVBANoECardView.tsx +++ b/src/pages/workspace/card/WorkspaceCardVBANoECardView.js @@ -1,6 +1,5 @@ import React from 'react'; import {View} from 'react-native'; -import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; import Button from '@components/Button'; import * as Expensicons from '@components/Icon/Expensicons'; @@ -8,37 +7,45 @@ import * as Illustrations from '@components/Icon/Illustrations'; import Section from '@components/Section'; import Text from '@components/Text'; import UnorderedList from '@components/UnorderedList'; -import useLocalize from '@hooks/useLocalize'; +import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; +import compose from '@libs/compose'; +import userPropTypes from '@pages/settings/userPropTypes'; import * as Link from '@userActions/Link'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {User} from '@src/types/onyx'; -type WorkspaceCardVBANoECardViewOnyxProps = { +const propTypes = { /** Information about the logged in user's account */ - user: OnyxEntry; + user: userPropTypes, + + ...withLocalizePropTypes, }; -type WorkspaceCardVBANoECardViewProps = WorkspaceCardVBANoECardViewOnyxProps; +const defaultProps = { + user: {}, +}; -function WorkspaceCardVBANoECardView({user}: WorkspaceCardVBANoECardViewProps) { +function WorkspaceCardVBANoECardView(props) { const styles = useThemeStyles(); - const {translate} = useLocalize(); - return ( <>
- {Boolean(user?.isCheckingDomain) && {translate('workspace.card.checkingDomain')}} + {Boolean(props.user.isCheckingDomain) && {props.translate('workspace.card.checkingDomain')}} ); } +WorkspaceCardVBANoECardView.propTypes = propTypes; +WorkspaceCardVBANoECardView.defaultProps = defaultProps; WorkspaceCardVBANoECardView.displayName = 'WorkspaceCardVBANoECardView'; -export default withOnyx({ - user: { - key: ONYXKEYS.USER, - }, -})(WorkspaceCardVBANoECardView); +export default compose( + withLocalize, + withOnyx({ + user: { + key: ONYXKEYS.USER, + }, + }), +)(WorkspaceCardVBANoECardView); diff --git a/src/pages/workspace/card/WorkspaceCardVBAWithECardView.tsx b/src/pages/workspace/card/WorkspaceCardVBAWithECardView.js similarity index 67% rename from src/pages/workspace/card/WorkspaceCardVBAWithECardView.tsx rename to src/pages/workspace/card/WorkspaceCardVBAWithECardView.js index a53a44fa52cf..40ecd80b8e6e 100644 --- a/src/pages/workspace/card/WorkspaceCardVBAWithECardView.tsx +++ b/src/pages/workspace/card/WorkspaceCardVBAWithECardView.js @@ -2,35 +2,28 @@ import React from 'react'; import {View} from 'react-native'; import * as Expensicons from '@components/Icon/Expensicons'; import * as Illustrations from '@components/Icon/Illustrations'; -import type {MenuItemWithLink} from '@components/MenuItemList'; import Section from '@components/Section'; import Text from '@components/Text'; import UnorderedList from '@components/UnorderedList'; -import useLocalize from '@hooks/useLocalize'; +import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import * as Link from '@userActions/Link'; -type MenuLinks = { - ISSUE_AND_MANAGE_CARDS: string; - RECONCILE_CARDS: string; - SETTLEMENT_FREQUENCY: string; +const propTypes = { + ...withLocalizePropTypes, }; -type MenuItems = MenuItemWithLink[]; - -const MENU_LINKS: MenuLinks = { +const MENU_LINKS = { ISSUE_AND_MANAGE_CARDS: 'domain_companycards', RECONCILE_CARDS: encodeURI('domain_companycards?param={"section":"cardReconciliation"}'), SETTLEMENT_FREQUENCY: encodeURI('domain_companycards?param={"section":"configureSettings"}'), }; -function WorkspaceCardVBAWithECardView() { +function WorkspaceCardVBAWithECardView(props) { const styles = useThemeStyles(); - const {translate} = useLocalize(); - - const menuItems: MenuItems = [ + const menuItems = [ { - title: translate('workspace.common.issueAndManageCards'), + title: props.translate('workspace.common.issueAndManageCards'), onPress: () => Link.openOldDotLink(MENU_LINKS.ISSUE_AND_MANAGE_CARDS), icon: Expensicons.ExpensifyCard, shouldShowRightIcon: true, @@ -39,7 +32,7 @@ function WorkspaceCardVBAWithECardView() { link: () => Link.buildOldDotURL(MENU_LINKS.ISSUE_AND_MANAGE_CARDS), }, { - title: translate('workspace.common.reconcileCards'), + title: props.translate('workspace.common.reconcileCards'), onPress: () => Link.openOldDotLink(MENU_LINKS.RECONCILE_CARDS), icon: Expensicons.ReceiptSearch, shouldShowRightIcon: true, @@ -48,7 +41,7 @@ function WorkspaceCardVBAWithECardView() { link: () => Link.buildOldDotURL(MENU_LINKS.RECONCILE_CARDS), }, { - title: translate('workspace.common.settlementFrequency'), + title: props.translate('workspace.common.settlementFrequency'), onPress: () => Link.openOldDotLink(MENU_LINKS.SETTLEMENT_FREQUENCY), icon: Expensicons.Gear, shouldShowRightIcon: true, @@ -60,23 +53,29 @@ function WorkspaceCardVBAWithECardView() { return (
- {translate('workspace.card.VBAWithECardCopy')} + {props.translate('workspace.card.VBAWithECardCopy')}
); } +WorkspaceCardVBAWithECardView.propTypes = propTypes; WorkspaceCardVBAWithECardView.displayName = 'WorkspaceCardVBAWithECardView'; -export default WorkspaceCardVBAWithECardView; +export default withLocalize(WorkspaceCardVBAWithECardView); From 3a7ad1e1314b598cd5acdd012ac15ba4890a2112 Mon Sep 17 00:00:00 2001 From: ruben-rebelo Date: Thu, 1 Feb 2024 10:20:07 +0000 Subject: [PATCH 005/167] [TS migration][WorkspaceReimburse] Missing imports --- src/libs/actions/Policy.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index 48fd8944f196..c8f982a0c6c6 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -37,7 +37,7 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {PersonalDetailsList, Policy, PolicyMember, PolicyTags, RecentlyUsedCategories, RecentlyUsedTags, ReimbursementAccount, Report, ReportAction, Transaction} from '@src/types/onyx'; import type {Errors} from '@src/types/onyx/OnyxCommon'; -import type {CustomUnit} from '@src/types/onyx/Policy'; +import type {Attributes, CustomUnit, Rate} from '@src/types/onyx/Policy'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; type AnnounceRoomMembersOnyxData = { From d11c98a08b95289c93bf2824f9aff85ee9741c1d Mon Sep 17 00:00:00 2001 From: ruben-rebelo Date: Fri, 2 Feb 2024 09:39:11 +0000 Subject: [PATCH 006/167] [TS migration][WorkspaceReimburse] Code improvements --- .../parameters/UpdateWorkspaceCustomUnitAndRateParams.ts | 2 +- src/libs/PolicyUtils.ts | 2 +- src/libs/actions/Policy.ts | 2 +- .../workspace/reimburse/WorkspaceRateAndUnitPage.tsx | 8 ++++---- src/pages/workspace/reimburse/WorkspaceReimburseView.tsx | 1 - src/types/onyx/Form.ts | 2 +- 6 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/libs/API/parameters/UpdateWorkspaceCustomUnitAndRateParams.ts b/src/libs/API/parameters/UpdateWorkspaceCustomUnitAndRateParams.ts index 22bbd20c7308..02186cddd32a 100644 --- a/src/libs/API/parameters/UpdateWorkspaceCustomUnitAndRateParams.ts +++ b/src/libs/API/parameters/UpdateWorkspaceCustomUnitAndRateParams.ts @@ -1,6 +1,6 @@ type UpdateWorkspaceCustomUnitAndRateParams = { policyID: string; - lastModified: number; + lastModified?: number; customUnit: string; customUnitRate: string; }; diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index a812cab24402..b4270867f9b2 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -50,7 +50,7 @@ function hasCustomUnitsError(policy: OnyxEntry): boolean { return Object.keys(policy?.customUnits?.errors ?? {}).length > 0; } -function getNumericValue(value: number, toLocaleDigit: (arg: string) => string): number | string { +function getNumericValue(value: number | string, toLocaleDigit: (arg: string) => string): number | string { const numValue = parseFloat(value.toString().replace(toLocaleDigit('.'), '.')); if (Number.isNaN(numValue)) { return NaN; diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index c8f982a0c6c6..1c9295dc08bc 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -939,7 +939,7 @@ function hideWorkspaceAlertMessage(policyID: string) { Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {alertMessage: ''}); } -function updateWorkspaceCustomUnitAndRate(policyID: string, currentCustomUnit: CustomUnit, newCustomUnit: NewCustomUnit, lastModified: number) { +function updateWorkspaceCustomUnitAndRate(policyID: string, currentCustomUnit: CustomUnit, newCustomUnit: NewCustomUnit, lastModified?: string) { if (!currentCustomUnit.customUnitID || !newCustomUnit?.customUnitID || !newCustomUnit.rates?.customUnitRateID) { return; } diff --git a/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage.tsx b/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage.tsx index 9c24b6bde023..1f5d7d854046 100644 --- a/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage.tsx +++ b/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage.tsx @@ -48,7 +48,7 @@ function WorkspaceRateAndUnitPage({policy, route}: WorkspaceRateAndUnitPageProps {label: translate('common.miles'), value: CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES}, ]; - const saveUnitAndRate = (unit: Unit, rate: number) => { + const saveUnitAndRate = (unit: Unit, rate: string) => { const distanceCustomUnit = Object.values(policy?.customUnits ?? {}).find((customUnit) => customUnit.name === CONST.CUSTOM_UNITS.NAME_DISTANCE); if (!distanceCustomUnit) { return; @@ -68,7 +68,7 @@ function WorkspaceRateAndUnitPage({policy, route}: WorkspaceRateAndUnitPageProps }, }; - Policy.updateWorkspaceCustomUnitAndRate(policy?.id ?? '', distanceCustomUnit, newCustomUnit, parseInt(policy?.lastModified ?? '', 2)); + Policy.updateWorkspaceCustomUnitAndRate(policy?.id ?? '', distanceCustomUnit, newCustomUnit, policy?.lastModified); }; const submit = (values: OnyxFormValuesFields) => { @@ -83,9 +83,9 @@ function WorkspaceRateAndUnitPage({policy, route}: WorkspaceRateAndUnitPageProps const outputCurrency = policy?.outputCurrency ?? CONST.CURRENCY.USD; // Allow one more decimal place for accuracy const rateValueRegex = RegExp(String.raw`^-?\d{0,8}([${getPermittedDecimalSeparator(decimalSeparator)}]\d{1,${CurrencyUtils.getCurrencyDecimals(outputCurrency) + 1}})?$`, 'i'); - if (!rateValueRegex.test(values.rate.toString()) || values.rate.toString() === 'Nan') { + if (!rateValueRegex.test(values.rate) || values.rate === '') { errors.rate = 'workspace.reimburse.invalidRateError'; - } else if (NumberUtils.parseFloatAnyLocale(values.rate.toString()) <= 0) { + } else if (NumberUtils.parseFloatAnyLocale(values.rate) <= 0) { errors.rate = 'workspace.reimburse.lowRateError'; } return errors; diff --git a/src/pages/workspace/reimburse/WorkspaceReimburseView.tsx b/src/pages/workspace/reimburse/WorkspaceReimburseView.tsx index ea9ee9e8a421..375fa5b8d439 100644 --- a/src/pages/workspace/reimburse/WorkspaceReimburseView.tsx +++ b/src/pages/workspace/reimburse/WorkspaceReimburseView.tsx @@ -25,7 +25,6 @@ import type {Unit} from '@src/types/onyx/Policy'; import WorkspaceReimburseSection from './WorkspaceReimburseSection'; type WorkspaceReimburseViewOnyxProps = { - /** From Onyx */ /** Bank account attached to free plan */ reimbursementAccount: OnyxEntry; }; diff --git a/src/types/onyx/Form.ts b/src/types/onyx/Form.ts index 5c0bb096b42f..1b119460b344 100644 --- a/src/types/onyx/Form.ts +++ b/src/types/onyx/Form.ts @@ -62,7 +62,7 @@ type ReportFieldEditForm = Form>; type RateUnitForm = Form<{ unit: Unit; - rate: number; + rate: string; }>; export default Form; From 9ff4961a40c9eb4125207b5aa89852f68f9f2a81 Mon Sep 17 00:00:00 2001 From: Aswin S Date: Wed, 7 Feb 2024 07:55:32 +0530 Subject: [PATCH 007/167] fix: animated background getting cropped on android --- src/pages/home/report/AnimatedEmptyStateBackground.tsx | 2 +- src/pages/home/report/ReportActionItem.js | 4 ++-- src/styles/utils/index.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pages/home/report/AnimatedEmptyStateBackground.tsx b/src/pages/home/report/AnimatedEmptyStateBackground.tsx index 7e259b7473cf..5e91aada8464 100644 --- a/src/pages/home/report/AnimatedEmptyStateBackground.tsx +++ b/src/pages/home/report/AnimatedEmptyStateBackground.tsx @@ -13,7 +13,7 @@ function AnimatedEmptyStateBackground() { const StyleUtils = useStyleUtils(); const {windowWidth, isSmallScreenWidth} = useWindowDimensions(); const illustrations = useThemeIllustrations(); - const IMAGE_OFFSET_X = windowWidth / 2; + const IMAGE_OFFSET_X = 0; // If window width is greater than the max background width, repeat the background image const maxBackgroundWidth = variables.sideBarWidth + CONST.EMPTY_STATE_BACKGROUND.ASPECT_RATIO * CONST.EMPTY_STATE_BACKGROUND.WIDE_SCREEN.IMAGE_HEIGHT; diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index 435c086d913f..75e9e1ca8627 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -583,7 +583,7 @@ function ReportActionItem(props) { if (ReportUtils.isTaskReport(props.report)) { if (ReportUtils.isCanceledTaskReport(props.report, parentReportAction)) { return ( - <> + - + ); } return ( diff --git a/src/styles/utils/index.ts b/src/styles/utils/index.ts index b3b4924ebb19..8657594f0a10 100644 --- a/src/styles/utils/index.ts +++ b/src/styles/utils/index.ts @@ -701,7 +701,7 @@ function getReportWelcomeBackgroundImageStyle(isSmallScreenWidth: boolean, isMon if (isSmallScreenWidth) { return { height: emptyStateBackground.SMALL_SCREEN.IMAGE_HEIGHT, - width: '200%', + width: '100%', position: 'absolute', }; } From 34135a2c023764b456df2fe86a0c95af2cb26022 Mon Sep 17 00:00:00 2001 From: ruben-rebelo Date: Thu, 8 Feb 2024 15:54:07 +0000 Subject: [PATCH 008/167] [TS migration][WorkspaceReimburse] Code improvements --- .../UpdateWorkspaceCustomUnitAndRateParams.ts | 2 +- .../reimburse/WorkspaceRateAndUnitPage.tsx | 4 ++-- .../workspace/reimburse/WorkspaceReimburseView.tsx | 13 ------------- src/types/onyx/Form.ts | 2 +- 4 files changed, 4 insertions(+), 17 deletions(-) diff --git a/src/libs/API/parameters/UpdateWorkspaceCustomUnitAndRateParams.ts b/src/libs/API/parameters/UpdateWorkspaceCustomUnitAndRateParams.ts index a94089512a36..010bcaa1e60a 100644 --- a/src/libs/API/parameters/UpdateWorkspaceCustomUnitAndRateParams.ts +++ b/src/libs/API/parameters/UpdateWorkspaceCustomUnitAndRateParams.ts @@ -1,6 +1,6 @@ type UpdateWorkspaceCustomUnitAndRateParams = { policyID: string; - lastModified?: number | string; + lastModified?: string; customUnit: string; customUnitRate: string; }; diff --git a/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage.tsx b/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage.tsx index 1f5d7d854046..6011c303f535 100644 --- a/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage.tsx +++ b/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage.tsx @@ -56,7 +56,7 @@ function WorkspaceRateAndUnitPage({policy, route}: WorkspaceRateAndUnitPageProps const currentCustomUnitRate = Object.values(distanceCustomUnit?.rates ?? {}).find((r) => r.name === CONST.CUSTOM_UNITS.DEFAULT_RATE); const unitID = distanceCustomUnit.customUnitID ?? ''; const unitName = distanceCustomUnit.name ?? ''; - const rateNumValue = PolicyUtils.getNumericValue(rate, toLocaleDigit) as number; + const rateNumValue = PolicyUtils.getNumericValue(rate, toLocaleDigit); const newCustomUnit: Policy.NewCustomUnit = { customUnitID: unitID, @@ -64,7 +64,7 @@ function WorkspaceRateAndUnitPage({policy, route}: WorkspaceRateAndUnitPageProps attributes: {unit}, rates: { ...currentCustomUnitRate, - rate: rateNumValue * CONST.POLICY.CUSTOM_UNIT_RATE_BASE_OFFSET, + rate: Number(rateNumValue) * CONST.POLICY.CUSTOM_UNIT_RATE_BASE_OFFSET, }, }; diff --git a/src/pages/workspace/reimburse/WorkspaceReimburseView.tsx b/src/pages/workspace/reimburse/WorkspaceReimburseView.tsx index a61313b8b1e3..cb0b5bce7fc3 100644 --- a/src/pages/workspace/reimburse/WorkspaceReimburseView.tsx +++ b/src/pages/workspace/reimburse/WorkspaceReimburseView.tsx @@ -127,19 +127,6 @@ function WorkspaceReimburseView({policy, reimbursementAccount}: WorkspaceReimbur /> - - Navigation.navigate(ROUTES.WORKSPACE_RATE_AND_UNIT.getRoute(policy?.id ?? ''))} - wrapperStyle={[styles.mhn5, styles.wAuto]} - brickRoadIndicator={(distanceCustomUnit?.errors ?? distanceCustomRate?.errors) && CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR} - /> - Date: Fri, 9 Feb 2024 21:53:06 +0530 Subject: [PATCH 009/167] fix: action item taking extra space --- src/pages/home/report/ReportActionItem.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index f960b987427b..d96ae8e77872 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -627,7 +627,7 @@ function ReportActionItem(props) { if (ReportUtils.isTaskReport(props.report)) { if (ReportUtils.isCanceledTaskReport(props.report, parentReportAction)) { return ( - + + Date: Fri, 9 Feb 2024 21:57:04 +0530 Subject: [PATCH 010/167] fix: clean lint --- src/pages/home/report/ReportActionItem.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index d96ae8e77872..57f716adc4cd 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -627,7 +627,7 @@ function ReportActionItem(props) { if (ReportUtils.isTaskReport(props.report)) { if (ReportUtils.isCanceledTaskReport(props.report, parentReportAction)) { return ( - + Date: Mon, 12 Feb 2024 05:45:08 +0530 Subject: [PATCH 011/167] fix: background image horizontal shift on sensor value change --- src/pages/home/report/AnimatedEmptyStateBackground.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/home/report/AnimatedEmptyStateBackground.tsx b/src/pages/home/report/AnimatedEmptyStateBackground.tsx index bbc340f5afb0..bcc2275da037 100644 --- a/src/pages/home/report/AnimatedEmptyStateBackground.tsx +++ b/src/pages/home/report/AnimatedEmptyStateBackground.tsx @@ -12,7 +12,7 @@ function AnimatedEmptyStateBackground() { const StyleUtils = useStyleUtils(); const {windowWidth, isSmallScreenWidth} = useWindowDimensions(); const illustrations = useThemeIllustrations(); - const IMAGE_OFFSET_X = 0; + const IMAGE_OFFSET_X = windowWidth * 1.1; // If window width is greater than the max background width, repeat the background image const maxBackgroundWidth = variables.sideBarWidth + CONST.EMPTY_STATE_BACKGROUND.ASPECT_RATIO * CONST.EMPTY_STATE_BACKGROUND.WIDE_SCREEN.IMAGE_HEIGHT; @@ -37,7 +37,7 @@ function AnimatedEmptyStateBackground() { xOffset.value = clamp(xOffset.value + y * CONST.ANIMATION_GYROSCOPE_VALUE, -IMAGE_OFFSET_X, IMAGE_OFFSET_X); yOffset.value = clamp(yOffset.value - x * CONST.ANIMATION_GYROSCOPE_VALUE, -IMAGE_OFFSET_Y, IMAGE_OFFSET_Y); return { - transform: [{translateX: withSpring(-IMAGE_OFFSET_X - xOffset.value)}, {translateY: withSpring(yOffset.value)}], + transform: [{translateX: withSpring(xOffset.value)}, {translateY: withSpring(yOffset.value)}, {scale: 1.15}], }; }, [isReducedMotionEnabled]); From 977512b09fa0d41ebe06623e6394320ef942b349 Mon Sep 17 00:00:00 2001 From: Aswin S Date: Mon, 12 Feb 2024 07:30:55 +0530 Subject: [PATCH 012/167] refactor: move image offset values outside of component --- src/pages/home/report/AnimatedEmptyStateBackground.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/pages/home/report/AnimatedEmptyStateBackground.tsx b/src/pages/home/report/AnimatedEmptyStateBackground.tsx index bcc2275da037..3a920f4f8449 100644 --- a/src/pages/home/report/AnimatedEmptyStateBackground.tsx +++ b/src/pages/home/report/AnimatedEmptyStateBackground.tsx @@ -6,13 +6,14 @@ import useWindowDimensions from '@hooks/useWindowDimensions'; import variables from '@styles/variables'; import CONST from '@src/CONST'; -const IMAGE_OFFSET_Y = 75; +// Maximum horizontal and vertical shift in pixels on sensor value change +const IMAGE_OFFSET_X = 30; +const IMAGE_OFFSET_Y = 20; function AnimatedEmptyStateBackground() { const StyleUtils = useStyleUtils(); const {windowWidth, isSmallScreenWidth} = useWindowDimensions(); const illustrations = useThemeIllustrations(); - const IMAGE_OFFSET_X = windowWidth * 1.1; // If window width is greater than the max background width, repeat the background image const maxBackgroundWidth = variables.sideBarWidth + CONST.EMPTY_STATE_BACKGROUND.ASPECT_RATIO * CONST.EMPTY_STATE_BACKGROUND.WIDE_SCREEN.IMAGE_HEIGHT; @@ -37,6 +38,8 @@ function AnimatedEmptyStateBackground() { xOffset.value = clamp(xOffset.value + y * CONST.ANIMATION_GYROSCOPE_VALUE, -IMAGE_OFFSET_X, IMAGE_OFFSET_X); yOffset.value = clamp(yOffset.value - x * CONST.ANIMATION_GYROSCOPE_VALUE, -IMAGE_OFFSET_Y, IMAGE_OFFSET_Y); return { + // On Android, scroll view sub views gets clipped beyond container bounds. Set the top position so that image wouldn't get clipped + top: IMAGE_OFFSET_Y, transform: [{translateX: withSpring(xOffset.value)}, {translateY: withSpring(yOffset.value)}, {scale: 1.15}], }; }, [isReducedMotionEnabled]); From 150ac223e8fa53ce4a5e6d6aca63d7d370edf48e Mon Sep 17 00:00:00 2001 From: Someshwar Tripathi Date: Wed, 21 Feb 2024 01:42:52 +0530 Subject: [PATCH 013/167] Reorder Tooltip component in IconButton --- src/components/VideoPlayer/IconButton.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/components/VideoPlayer/IconButton.js b/src/components/VideoPlayer/IconButton.js index 71c1a2150692..7a9a93f5a626 100644 --- a/src/components/VideoPlayer/IconButton.js +++ b/src/components/VideoPlayer/IconButton.js @@ -40,12 +40,12 @@ const defaultProps = { function IconButton({src, fill, onPress, style, hoverStyle, tooltipText, small, shouldForceRenderingTooltipBelow}) { const styles = useThemeStyles(); return ( - - - {(isHovered) => ( + + {(isHovered) => ( + - )} - - + + )} + ); } From f278edc12c5d04148ae1539290ed7303ef68b8ad Mon Sep 17 00:00:00 2001 From: Someshwar Tripathi Date: Wed, 21 Feb 2024 02:16:10 +0530 Subject: [PATCH 014/167] Simplify component by using hoverStyle prop --- src/components/VideoPlayer/IconButton.js | 38 +++++++++++------------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/src/components/VideoPlayer/IconButton.js b/src/components/VideoPlayer/IconButton.js index 7a9a93f5a626..eaf7ec532fb2 100644 --- a/src/components/VideoPlayer/IconButton.js +++ b/src/components/VideoPlayer/IconButton.js @@ -1,6 +1,5 @@ import PropTypes from 'prop-types'; import React from 'react'; -import Hoverable from '@components/Hoverable'; import Icon from '@components/Icon'; import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback'; import Tooltip from '@components/Tooltip'; @@ -40,26 +39,23 @@ const defaultProps = { function IconButton({src, fill, onPress, style, hoverStyle, tooltipText, small, shouldForceRenderingTooltipBelow}) { const styles = useThemeStyles(); return ( - - {(isHovered) => ( - - - - - - )} - + + + + + ); } From 458be5e5bffd0891b4bfba8d947a455b1413bfe3 Mon Sep 17 00:00:00 2001 From: ruben-rebelo Date: Tue, 27 Feb 2024 09:57:32 +0000 Subject: [PATCH 015/167] [TS migration][WorkspaceReimburse] Ts issues fix --- src/libs/PolicyUtils.ts | 1 + .../workspace/reimburse/WorkspaceRateAndUnitPage.tsx | 11 ++++++----- .../workspace/reimburse/WorkspaceReimburseView.tsx | 1 + 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index 650f290ed065..b5b9e0eb5848 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -4,6 +4,7 @@ import type {ValueOf} from 'type-fest'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {PersonalDetailsList, Policy, PolicyMembers, PolicyTagList, PolicyTags} from '@src/types/onyx'; +import type {Rate} from '@src/types/onyx/Policy'; import type {EmptyObject} from '@src/types/utils/EmptyObject'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; diff --git a/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage.tsx b/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage.tsx index 6011c303f535..8fef5f4dc6f9 100644 --- a/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage.tsx +++ b/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage.tsx @@ -3,7 +3,7 @@ import React, {useEffect} from 'react'; import {Keyboard, View} from 'react-native'; import FormProvider from '@components/Form/FormProvider'; import InputWrapper from '@components/Form/InputWrapper'; -import type {OnyxFormValuesFields} from '@components/Form/types'; +import type {FormOnyxValues} from '@components/Form/types'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; import Picker from '@components/Picker'; import TextInput from '@components/TextInput'; @@ -21,6 +21,7 @@ import WorkspacePageWithSections from '@pages/workspace/WorkspacePageWithSection import * as BankAccounts from '@userActions/BankAccounts'; import * as Policy from '@userActions/Policy'; import CONST from '@src/CONST'; +import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; @@ -28,7 +29,7 @@ import type {Unit} from '@src/types/onyx/Policy'; type WorkspaceRateAndUnitPageProps = WithPolicyProps & StackScreenProps; -type ValidationError = {rate?: string}; +type ValidationError = {rate?: TranslationPaths | undefined}; function WorkspaceRateAndUnitPage({policy, route}: WorkspaceRateAndUnitPageProps) { const {translate, toLocaleDigit} = useLocalize(); @@ -71,13 +72,13 @@ function WorkspaceRateAndUnitPage({policy, route}: WorkspaceRateAndUnitPageProps Policy.updateWorkspaceCustomUnitAndRate(policy?.id ?? '', distanceCustomUnit, newCustomUnit, policy?.lastModified); }; - const submit = (values: OnyxFormValuesFields) => { - saveUnitAndRate(values.unit, values.rate); + const submit = (values: FormOnyxValues) => { + saveUnitAndRate(values.unit as Unit, values.rate); Keyboard.dismiss(); Navigation.goBack(ROUTES.WORKSPACE_REIMBURSE.getRoute(policy?.id ?? '')); }; - const validate = (values: OnyxFormValuesFields): ValidationError => { + const validate = (values: FormOnyxValues): ValidationError => { const errors: ValidationError = {}; const decimalSeparator = toLocaleDigit('.'); const outputCurrency = policy?.outputCurrency ?? CONST.CURRENCY.USD; diff --git a/src/pages/workspace/reimburse/WorkspaceReimburseView.tsx b/src/pages/workspace/reimburse/WorkspaceReimburseView.tsx index b1a0e50cdca7..050398d00235 100644 --- a/src/pages/workspace/reimburse/WorkspaceReimburseView.tsx +++ b/src/pages/workspace/reimburse/WorkspaceReimburseView.tsx @@ -143,6 +143,7 @@ function WorkspaceReimburseView({policy, reimbursementAccount}: WorkspaceReimbur WorkspaceReimburseView.displayName = 'WorkspaceReimburseView'; export default withOnyx({ + // @ts-expect-error: ONYXKEYS.REIMBURSEMENT_ACCOUNT is conflicting with ONYXKEYS.FORMS.REIMBURSEMENT_ACCOUNT_FORM reimbursementAccount: { key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, }, From aafd0e5b047113afaecaf5cc5c04e766c1f97e57 Mon Sep 17 00:00:00 2001 From: ruben-rebelo Date: Tue, 27 Feb 2024 12:40:29 +0000 Subject: [PATCH 016/167] [TS migration][WorkspaceReimburse] Lint --- src/pages/workspace/reimburse/WorkspaceReimburseView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/workspace/reimburse/WorkspaceReimburseView.tsx b/src/pages/workspace/reimburse/WorkspaceReimburseView.tsx index 050398d00235..9c7b1c493e8d 100644 --- a/src/pages/workspace/reimburse/WorkspaceReimburseView.tsx +++ b/src/pages/workspace/reimburse/WorkspaceReimburseView.tsx @@ -143,7 +143,7 @@ function WorkspaceReimburseView({policy, reimbursementAccount}: WorkspaceReimbur WorkspaceReimburseView.displayName = 'WorkspaceReimburseView'; export default withOnyx({ - // @ts-expect-error: ONYXKEYS.REIMBURSEMENT_ACCOUNT is conflicting with ONYXKEYS.FORMS.REIMBURSEMENT_ACCOUNT_FORM + // @ts-expect-error: ONYXKEYS.REIMBURSEMENT_ACCOUNT is conflicting with ONYXKEYS.FORMS.REIMBURSEMENT_ACCOUNT_FORM reimbursementAccount: { key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, }, From 691d12642545d1204a41e89f8e9880784845e15e Mon Sep 17 00:00:00 2001 From: Someshwar Tripathi Date: Mon, 4 Mar 2024 07:29:39 +0530 Subject: [PATCH 017/167] Update IconButton to use PressableWithFeedback --- src/components/VideoPlayer/IconButton.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/VideoPlayer/IconButton.js b/src/components/VideoPlayer/IconButton.js index eaf7ec532fb2..5af9bc87e66f 100644 --- a/src/components/VideoPlayer/IconButton.js +++ b/src/components/VideoPlayer/IconButton.js @@ -1,7 +1,7 @@ import PropTypes from 'prop-types'; import React from 'react'; import Icon from '@components/Icon'; -import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback'; +import PressableWithFeedback from '@components/Pressable/PressableWithFeedback'; import Tooltip from '@components/Tooltip'; import useThemeStyles from '@hooks/useThemeStyles'; import stylePropTypes from '@styles/stylePropTypes'; @@ -43,7 +43,7 @@ function IconButton({src, fill, onPress, style, hoverStyle, tooltipText, small, text={tooltipText} shouldForceRenderingBelow={shouldForceRenderingTooltipBelow} > - - + ); } From bcb4663fc1c601f948dcd369020ad3198d115710 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Fri, 8 Mar 2024 17:53:54 +0700 Subject: [PATCH 018/167] Fix deleted characters display briefly after saving edited parent message --- src/pages/home/report/ReportActionItemMessageEdit.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/pages/home/report/ReportActionItemMessageEdit.tsx b/src/pages/home/report/ReportActionItemMessageEdit.tsx index fbf2da69aa31..b5c18976aa86 100644 --- a/src/pages/home/report/ReportActionItemMessageEdit.tsx +++ b/src/pages/home/report/ReportActionItemMessageEdit.tsx @@ -17,6 +17,7 @@ import Tooltip from '@components/Tooltip'; import useHandleExceedMaxCommentLength from '@hooks/useHandleExceedMaxCommentLength'; import useKeyboardState from '@hooks/useKeyboardState'; import useLocalize from '@hooks/useLocalize'; +import usePrevious from '@hooks/usePrevious'; import useReportScrollManager from '@hooks/useReportScrollManager'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; @@ -79,6 +80,7 @@ function ReportActionItemMessageEdit( const {translate, preferredLocale} = useLocalize(); const {isKeyboardShown} = useKeyboardState(); const {isSmallScreenWidth} = useWindowDimensions(); + const prevDraftMessage = usePrevious(draftMessage); const getInitialDraft = () => { if (draftMessage === action?.message?.[0].html) { @@ -124,11 +126,11 @@ function ReportActionItemMessageEdit( const draftRef = useRef(draft); useEffect(() => { - if (ReportActionsUtils.isDeletedAction(action) || (action.message && draftMessage === action.message[0].html)) { + if (ReportActionsUtils.isDeletedAction(action) || Boolean(action.message && draftMessage === action.message[0].html) || Boolean(prevDraftMessage === draftMessage)) { return; } setDraft(Str.htmlDecode(draftMessage)); - }, [draftMessage, action]); + }, [draftMessage, action, prevDraftMessage]); useEffect(() => { // required for keeping last state of isFocused variable From ff0c54f349b6f790c2dbd4c537d531a1a2f8c3d8 Mon Sep 17 00:00:00 2001 From: ruben-rebelo Date: Mon, 11 Mar 2024 15:41:47 +0000 Subject: [PATCH 019/167] [TS migration] Migrate AddressSearch, Banner, Button and ButtonWithDropdownMenu stories to typescript --- src/components/AddressSearch/index.tsx | 1 + src/components/AddressSearch/types.ts | 2 +- src/components/Banner.tsx | 1 + src/components/Button/index.tsx | 1 + ...h.stories.js => AddressSearch.stories.tsx} | 20 ++++++++++++------- .../{Banner.stories.js => Banner.stories.tsx} | 12 +++++++---- .../{Button.stories.js => Button.stories.tsx} | 18 ++++++++++------- ....js => ButtonWithDropdownMenu.stories.tsx} | 16 +++++++++------ 8 files changed, 46 insertions(+), 25 deletions(-) rename src/stories/{AddressSearch.stories.js => AddressSearch.stories.tsx} (53%) rename src/stories/{Banner.stories.js => Banner.stories.tsx} (75%) rename src/stories/{Button.stories.js => Button.stories.tsx} (80%) rename src/stories/{ButtonWithDropdownMenu.stories.js => ButtonWithDropdownMenu.stories.tsx} (57%) diff --git a/src/components/AddressSearch/index.tsx b/src/components/AddressSearch/index.tsx index a2e3f5d9948e..1eebff7b5bfa 100644 --- a/src/components/AddressSearch/index.tsx +++ b/src/components/AddressSearch/index.tsx @@ -455,3 +455,4 @@ function AddressSearch( AddressSearch.displayName = 'AddressSearchWithRef'; export default forwardRef(AddressSearch); +export type {AddressSearchProps}; diff --git a/src/components/AddressSearch/types.ts b/src/components/AddressSearch/types.ts index 27e068cd1777..efbcc6374341 100644 --- a/src/components/AddressSearch/types.ts +++ b/src/components/AddressSearch/types.ts @@ -96,4 +96,4 @@ type AddressSearchProps = { type IsCurrentTargetInsideContainerType = (event: FocusEvent | NativeSyntheticEvent, containerRef: RefObject) => boolean; -export type {CurrentLocationButtonProps, AddressSearchProps, RenamedInputKeysProps, IsCurrentTargetInsideContainerType}; +export type {CurrentLocationButtonProps, AddressSearchProps, RenamedInputKeysProps, IsCurrentTargetInsideContainerType, StreetValue}; diff --git a/src/components/Banner.tsx b/src/components/Banner.tsx index 56fe7c4d0b42..b4bdfa8405fa 100644 --- a/src/components/Banner.tsx +++ b/src/components/Banner.tsx @@ -109,3 +109,4 @@ function Banner({text, onClose, onPress, containerStyles, textStyles, shouldRend Banner.displayName = 'Banner'; export default memo(Banner); +export type {BannerProps} diff --git a/src/components/Button/index.tsx b/src/components/Button/index.tsx index ca1bc391e800..b11e16e47f6e 100644 --- a/src/components/Button/index.tsx +++ b/src/components/Button/index.tsx @@ -348,3 +348,4 @@ function Button( Button.displayName = 'Button'; export default withNavigationFallback(React.forwardRef(Button)); +export type {ButtonProps}; diff --git a/src/stories/AddressSearch.stories.js b/src/stories/AddressSearch.stories.tsx similarity index 53% rename from src/stories/AddressSearch.stories.js rename to src/stories/AddressSearch.stories.tsx index 8b9223bc5ea2..a648a20c0bfa 100644 --- a/src/stories/AddressSearch.stories.js +++ b/src/stories/AddressSearch.stories.tsx @@ -1,12 +1,17 @@ +import type {ComponentMeta, ComponentStory} from '@storybook/react'; import React, {useState} from 'react'; +import type {AddressSearchProps} from '@components/AddressSearch'; import AddressSearch from '@components/AddressSearch'; +import type {RenamedInputKeysProps, StreetValue} from '@components/AddressSearch/types'; + +type AddressSearchStory = ComponentStory; /** * We use the Component Story Format for writing stories. Follow the docs here: * * https://storybook.js.org/docs/react/writing-stories/introduction#component-story-format */ -export default { +const story: ComponentMeta = { title: 'Components/AddressSearch', component: AddressSearch, args: { @@ -15,12 +20,12 @@ export default { }, }; -function Template(args) { - const [value, setValue] = useState(''); +function Template(args: AddressSearchProps) { + const [value, setValue] = useState(''); return ( setValue(street)} + value={value as string} + onInputChange={(inputValue) => setValue(inputValue)} // eslint-disable-next-line react/jsx-props-no-spreading {...args} /> @@ -29,11 +34,12 @@ function Template(args) { // Arguments can be passed to the component by binding // See: https://storybook.js.org/docs/react/writing-stories/introduction#using-args -const Default = Template.bind({}); +const Default: AddressSearchStory = Template.bind({}); -const ErrorStory = Template.bind({}); +const ErrorStory: AddressSearchStory = Template.bind({}); ErrorStory.args = { errorText: 'The street you are looking for does not exist', }; +export default story; export {Default, ErrorStory}; diff --git a/src/stories/Banner.stories.js b/src/stories/Banner.stories.tsx similarity index 75% rename from src/stories/Banner.stories.js rename to src/stories/Banner.stories.tsx index 3a6f454843d1..6e71979a88a8 100644 --- a/src/stories/Banner.stories.js +++ b/src/stories/Banner.stories.tsx @@ -1,6 +1,10 @@ +import type {ComponentStory} from '@storybook/react'; import React from 'react'; +import type {BannerProps} from '@components/Banner'; import Banner from '@components/Banner'; +type BannerStory = ComponentStory; + /** * We use the Component Story Format for writing stories. Follow the docs here: * @@ -11,25 +15,25 @@ const story = { component: Banner, }; -function Template(args) { +function Template(args: BannerProps) { // eslint-disable-next-line react/jsx-props-no-spreading return ; } // Arguments can be passed to the component by binding // See: https://storybook.js.org/docs/react/writing-stories/introduction#using-args -const InfoBanner = Template.bind({}); +const InfoBanner: BannerStory = Template.bind({}); InfoBanner.args = { text: 'This is an informational banner', }; -const HTMLBanner = Template.bind({}); +const HTMLBanner: BannerStory = Template.bind({}); HTMLBanner.args = { text: 'This is a informational banner containing HTML', shouldRenderHTML: true, }; -const BannerWithLink = Template.bind({}); +const BannerWithLink: BannerStory = Template.bind({}); BannerWithLink.args = { text: 'This is a informational banner containing internal Link and public link', shouldRenderHTML: true, diff --git a/src/stories/Button.stories.js b/src/stories/Button.stories.tsx similarity index 80% rename from src/stories/Button.stories.js rename to src/stories/Button.stories.tsx index 2bf254b9f382..0ad1fc57b8df 100644 --- a/src/stories/Button.stories.js +++ b/src/stories/Button.stories.tsx @@ -1,29 +1,33 @@ /* eslint-disable react/jsx-props-no-spreading */ +import type {ComponentMeta, ComponentStory} from '@storybook/react'; import React, {useCallback, useState} from 'react'; import {View} from 'react-native'; +import type {ButtonProps} from '@components/Button'; import Button from '@components/Button'; import Text from '@components/Text'; +type ButtonStory = ComponentStory; + /** * We use the Component Story Format for writing stories. Follow the docs here: * * https://storybook.js.org/docs/react/writing-stories/introduction#component-story-format */ -const story = { +const story: ComponentMeta = { title: 'Components/Button', component: Button, }; -function Template(args) { +function Template(args: ButtonProps) { // eslint-disable-next-line react/jsx-props-no-spreading return